reference, declarationdefinition
definition → references, declarations, derived classes, virtual overrides
reference to multiple definitions → definitions
unreferenced
    1
    2
    3
    4
    5
    6
    7
    8
    9
   10
   11
   12
   13
   14
   15
   16
   17
   18
   19
   20
   21
   22
   23
   24
   25
   26
   27
   28
   29
   30
   31
   32
   33
   34
   35
   36
   37
   38
   39
   40
   41
   42
   43
   44
   45
   46
   47
   48
   49
   50
   51
   52
   53
   54
   55
   56
   57
   58
   59
   60
   61
   62
   63
   64
   65
   66
   67
   68
   69
   70
   71
   72
   73
   74
   75
   76
   77
   78
   79
   80
   81
   82
   83
   84
   85
   86
   87
   88
   89
   90
   91
   92
   93
   94
   95
   96
   97
   98
   99
  100
  101
  102
  103
  104
  105
  106
  107
  108
  109
  110
  111
  112
  113
  114
  115
  116
  117
  118
  119
  120
  121
  122
  123
  124
  125
  126
  127
  128
  129
  130
  131
  132
  133
  134
  135
  136
  137
  138
  139
  140
  141
  142
  143
  144
  145
  146
  147
  148
  149
  150
  151
  152
  153
  154
  155
  156
  157
  158
  159
  160
  161
  162
  163
  164
  165
  166
  167
  168
  169
  170
  171
  172
  173
  174
  175
  176
  177
  178
  179
  180
  181
  182
  183
  184
  185
# This implements the "diagnose-nsstring" command, usually installed in the debug session like
#   command script import lldb.diagnose
# it is used when NSString summary formatter fails to replicate the logic that went into LLDB making the
# decisions it did and  providing some useful context information that can
# be used for improving the formatter

from __future__ import print_function

import lldb


def read_memory(process, location, size):
    data = ""
    error = lldb.SBError()
    for x in range(0, size - 1):
        byte = process.ReadUnsignedFromMemory(x + location, 1, error)
        if error.fail:
            data = data + "err%s" % "" if x == size - 2 else ":"
        else:
            try:
                data = data + "0x%x" % byte
                if byte == 0:
                    data = data + "(\\0)"
                elif byte == 0xa:
                    data = data + "(\\a)"
                elif byte == 0xb:
                    data = data + "(\\b)"
                elif byte == 0xc:
                    data = data + "(\\c)"
                elif byte == '\n':
                    data = data + "(\\n)"
                else:
                    data = data + "(%s)" % chr(byte)
                if x < size - 2:
                    data = data + ":"
            except Exception as e:
                print(e)
    return data


def diagnose_nsstring_Command_Impl(debugger, command, result, internal_dict):
    """
    A command to diagnose the LLDB NSString data formatter
    invoke as
    (lldb) diagnose-nsstring <expr returning NSString>
    e.g.
    (lldb) diagnose-nsstring @"Hello world"
    """
    target = debugger.GetSelectedTarget()
    process = target.GetProcess()
    thread = process.GetSelectedThread()
    frame = thread.GetSelectedFrame()
    if not target.IsValid() or not process.IsValid():
        return "unable to get target/process - cannot proceed"
    options = lldb.SBExpressionOptions()
    options.SetFetchDynamicValue()
    error = lldb.SBError()
    if frame.IsValid():
        nsstring = frame.EvaluateExpression(command, options)
    else:
        nsstring = target.EvaluateExpression(command, options)
    print(str(nsstring), file=result)
    nsstring_address = nsstring.GetValueAsUnsigned(0)
    if nsstring_address == 0:
        return "unable to obtain the string - cannot proceed"
    expression = "\
struct $__lldb__notInlineMutable {\
    char* buffer;\
    signed long length;\
    signed long capacity;\
    unsigned int hasGap:1;\
    unsigned int isFixedCapacity:1;\
    unsigned int isExternalMutable:1;\
    unsigned int capacityProvidedExternally:1;\n\
#if __LP64__\n\
    unsigned long desiredCapacity:60;\n\
#else\n\
    unsigned long desiredCapacity:28;\n\
#endif\n\
    void* contentsAllocator;\
};\
\
struct $__lldb__CFString {\
    void* _cfisa;\
    uint8_t _cfinfo[4];\
    uint32_t _rc;\
    union {\
        struct __inline1 {\
            signed long length;\
        } inline1;\
        struct __notInlineImmutable1 {\
            char* buffer;\
            signed long length;\
            void* contentsDeallocator;\
        } notInlineImmutable1;\
        struct __notInlineImmutable2 {\
            char* buffer;\
            void* contentsDeallocator;\
        } notInlineImmutable2;\
        struct $__lldb__notInlineMutable notInlineMutable;\
    } variants;\
};\
"

    expression = expression + "*(($__lldb__CFString*) %d)" % nsstring_address
    # print expression
    dumped = target.EvaluateExpression(expression, options)
    print(str(dumped), file=result)

    little_endian = (target.byte_order == lldb.eByteOrderLittle)
    ptr_size = target.addr_size

    info_bits = dumped.GetChildMemberWithName("_cfinfo").GetChildAtIndex(
        0 if little_endian else 3).GetValueAsUnsigned(0)
    is_mutable = (info_bits & 1) == 1
    is_inline = (info_bits & 0x60) == 0
    has_explicit_length = (info_bits & (1 | 4)) != 4
    is_unicode = (info_bits & 0x10) == 0x10
    is_special = (
        nsstring.GetDynamicValue(
            lldb.eDynamicCanRunTarget).GetTypeName() == "NSPathStore2")
    has_null = (info_bits & 8) == 8

    print("\nInfo=%d\nMutable=%s\nInline=%s\nExplicit=%s\nUnicode=%s\nSpecial=%s\nNull=%s\n" % \
        (info_bits, "yes" if is_mutable else "no", "yes" if is_inline else "no", "yes" if has_explicit_length else "no", "yes" if is_unicode else "no", "yes" if is_special else "no", "yes" if has_null else "no"), file=result)

    explicit_length_offset = 0
    if not has_null and has_explicit_length and not is_special:
        explicit_length_offset = 2 * ptr_size
        if is_mutable and not is_inline:
            explicit_length_offset = explicit_length_offset + ptr_size
        elif is_inline:
            pass
        elif not is_inline and not is_mutable:
            explicit_length_offset = explicit_length_offset + ptr_size
        else:
            explicit_length_offset = 0

    if explicit_length_offset == 0:
        print("There is no explicit length marker - skipping this step\n", file=result)
    else:
        explicit_length_offset = nsstring_address + explicit_length_offset
        explicit_length = process.ReadUnsignedFromMemory(
            explicit_length_offset, 4, error)
        print("Explicit length location is at 0x%x - read value is %d\n" % (
            explicit_length_offset, explicit_length), file=result)

    if is_mutable:
        location = 2 * ptr_size + nsstring_address
        location = process.ReadPointerFromMemory(location, error)
    elif is_inline and has_explicit_length and not is_unicode and not is_special and not is_mutable:
        location = 3 * ptr_size + nsstring_address
    elif is_unicode:
        location = 2 * ptr_size + nsstring_address
        if is_inline:
            if not has_explicit_length:
                print("Unicode & Inline & !Explicit is a new combo - no formula for it", file=result)
            else:
                location += ptr_size
        else:
            location = process.ReadPointerFromMemory(location, error)
    elif is_special:
        location = nsstring_address + ptr_size + 4
    elif is_inline:
        location = 2 * ptr_size + nsstring_address
        if not has_explicit_length:
            location += 1
    else:
        location = 2 * ptr_size + nsstring_address
        location = process.ReadPointerFromMemory(location, error)
    print("Expected data location: 0x%x\n" % (location), file=result)
    print("1K of data around location: %s\n" % read_memory(
        process, location, 1024), file=result)
    print("5K of data around string pointer: %s\n" % read_memory(
        process, nsstring_address, 1024 * 5), file=result)


def __lldb_init_module(debugger, internal_dict):
    debugger.HandleCommand(
        "command script add -f %s.diagnose_nsstring_Command_Impl diagnose-nsstring" %
        __name__)
    print('The "diagnose-nsstring" command has been installed, type "help diagnose-nsstring" for detailed help.')

__lldb_init_module(lldb.debugger, None)
__lldb_init_module = None