Search code examples
pythonclldb

How to print a char ** var from python LLDB


I'm trying to print out a char ** using the python LLDB lib. To be clear it the char **argv from the main function in a C program. It should have 3 strings in the array from the input.

When launching the C program and stopping right after main(int argc, char **argv) in xcode i can just do p argv[0] and p argv[1] and I get the correct arg strings to print.

In python I have a function to print the first 2 argv strings

def print_argv(argv: lldb.SBValue, target:SBTarget):
    pointer = argv.Dereference()
    summary = pointer.GetSummary().strip('\"')
    print(summary)
    str_len = len(summary)
    next_str_address:int = pointer.GetLoadAddress() + str_len + 1
    pointer_type = pointer.GetType()
    addr = SBAddress(next_str_address, target)
    next_value = target.CreateValueFromAddress("argv", addr, pointer_type)
    summary = next_value.GetSummary().strip('\"')

But the second input isn't there.

I've tried a number of different ways but I can't seen to get the second string. I tried using the argv.GetChildAtIndex and that works for 0 but not 1 or 2. I noticed that the address are offset by the string length, so I've tried to update the pointers, again no luck. I check that the inputs are really there by printing out from the C program.

Edit: Here is how to set up the call to the print function

debugger = lldb.SBDebugger.Create()
debugger.SetAsync(False)

target = debugger.CreateTargetWithFileAndArch (str(binary_path), lldb.LLDB_ARCH_DEFAULT)
assert target, "Failed to create target"

target.BreakpointCreateByName ("main", target.GetExecutable().GetFilename())

launch_info = lldb.SBLaunchInfo(str_args)
launch_info.SetWorkingDirectory(os.getcwd())
error = lldb.SBError()
process = target.Launch(launch_info, error)

assert process, "Failed to launch process"
for _ in range(1000):# Putting an upper limit on the number of functions to trace
    state = process.GetState()

    if state == lldb.eStateStopped:
        for thread in process:
            function_name = frame.GetFunctionName()             
            frame = thread.frames[0]
            if function_name in function_names:
                for arg in frame.arguments:
                    if arg.name == "argv":
                        print_argv(arg, 3, target)

    process.Continue()

Solution: Turns out you need to use can_create_synthetic==True.

child = argv.GetChildAtIndex(1, lldb.eNoDynamicValues, True)


Solution

  • The main problem is that GetChildAtIndex needs to have can_create_synthetic set to True, then the values show up.

    This seems to be true of stack allocated arrays too. I tried this C code:

    void bar(char **blargh, int len){
        for (int i = 0; i < len; i++){
            printf("%s\n", blargh[i]);
        }
    }
    
    int main(int argc, char **argv) {
        char *foo[] = {"one", "two", "three"};
        bar(foo, 3);
    }
    

    and it has the same result that can_create_synthetic is required to get at values other than the first. An array of simple types like ints doesn't change this either.

    The docs don't really explain much, other that to say that you may need it for indexes after 0.

    https://lldb.llvm.org/python_api/lldb.SBValue.html#lldb.SBValue.GetChildAtIndex

    Pointers differ depending on what they point to. If the pointer points to a simple type, the child at index zero is the only child value available, unless synthetic_allowed is true, in which case the pointer will be used as an array and can create ‘synthetic’ child values using positive or negative indexes.