Lldb to function / trace step: continue until the next function call or until the current function is returned from

In LLDB, how can I implement a function / trace step? That is, continue until the function is called, or the current function is returned. Suppose there is no source code to execute until .

This is equivalent to executing step-inst until the structure of the stack frame has changed.

+6
source share
3 answers

Here lldb is for a python script that adds the step-function command. The command stops whenever the structure of the call stack changes.

step_func.py

 import lldb def step_func(debugger, command, result, internal_dict): thread = debugger.GetSelectedTarget().GetProcess().GetSelectedThread() start_num_frames = thread.GetNumFrames() if start_num_frames == 0: return while True: thread.StepInstruction(0) if thread.GetNumFrames() != start_num_frames: stream = lldb.SBStream() thread.GetStatus(stream) description = stream.GetData() print >>result, "Call stack depth changed %d -> %d" % (start_num_frames, thread.GetNumFrames()) print >>result, description, break def __lldb_init_module (debugger, dict): debugger.HandleCommand('command script add -f %s.step_func sf' % __name__) 

Usage example:

 $ lldb /bin/ls Current executable set to '/bin/ls' (x86_64). (lldb) command script import step_func (lldb) process launch --stop-at-entry Process 12944 launched: '/bin/ls' (x86_64) Process 12944 stopped * thread #1: tid = 0x438b0, 0x00007fff5fc01028 dyld`_dyld_start, stop reason = signal SIGSTOP frame #0: 0x00007fff5fc01028 dyld`_dyld_start dyld`_dyld_start: -> 0x7fff5fc01028: popq %rdi 0x7fff5fc01029: pushq $0 0x7fff5fc0102b: movq %rsp, %rbp 0x7fff5fc0102e: andq $-16, %rsp (lldb) sf Call stack depth changed 1 -> 2 * thread #1: tid = 0x438b0, 0x00007fff5fc0109e dyld`dyldbootstrap::start(macho_header const*, int, char const**, long, macho_header const*, unsigned long*), stop reason = instruction step into frame #0: 0x00007fff5fc0109e dyld`dyldbootstrap::start(macho_header const*, int, char const**, long, macho_header const*, unsigned long*) dyld`dyldbootstrap::start(macho_header const*, int, char const**, long, macho_header const*, unsigned long*): -> 0x7fff5fc0109e: pushq %rbp 0x7fff5fc0109f: movq %rsp, %rbp 0x7fff5fc010a2: pushq %r15 0x7fff5fc010a4: pushq %r14 (lldb) Call stack depth changed 2 -> 3 * thread #1: tid = 0x438b0, 0x00007fff5fc22f9b dyld`mach_init, stop reason = instruction step into frame #0: 0x00007fff5fc22f9b dyld`mach_init dyld`mach_init: -> 0x7fff5fc22f9b: pushq %rbp 0x7fff5fc22f9c: movq %rsp, %rbp 0x7fff5fc22f9f: movb 326075(%rip), %al ; mach_init.mach_init_inited 0x7fff5fc22fa5: testb %al, %al (lldb) Call stack depth changed 3 -> 4 * thread #1: tid = 0x438b0, 0x00007fff5fc22fb9 dyld`mach_init_doit, stop reason = instruction step into frame #0: 0x00007fff5fc22fb9 dyld`mach_init_doit dyld`mach_init_doit: -> 0x7fff5fc22fb9: pushq %rbp 0x7fff5fc22fba: movq %rsp, %rbp 0x7fff5fc22fbd: callq 0x7fff5fc23210 ; task_self_trap 0x7fff5fc22fc2: movl %eax, 69740(%rip) ; mach_task_self_ (lldb) Call stack depth changed 4 -> 5 * thread #1: tid = 0x438b0, 0x00007fff5fc23210 dyld`task_self_trap, stop reason = instruction step into frame #0: 0x00007fff5fc23210 dyld`task_self_trap dyld`task_self_trap: -> 0x7fff5fc23210: movq %rcx, %r10 0x7fff5fc23213: movl $16777244, %eax 0x7fff5fc23218: syscall 0x7fff5fc2321a: ret (lldb) Call stack depth changed 5 -> 4 * thread #1: tid = 0x438b0, 0x00007fff5fc22fc2 dyld`mach_init_doit + 9, stop reason = instruction step into frame #0: 0x00007fff5fc22fc2 dyld`mach_init_doit + 9 dyld`mach_init_doit + 9: -> 0x7fff5fc22fc2: movl %eax, 69740(%rip) ; mach_task_self_ 0x7fff5fc22fc8: callq 0x7fff5fc231f8 ; mach_reply_port 0x7fff5fc22fcd: leaq 69724(%rip), %rcx ; _task_reply_port 0x7fff5fc22fd4: movl %eax, (%rcx) (lldb) 
+6
source

In LLDB, how can I proceed until the current build level function remains? (Source code is not available for execution). I am looking for an automated way to execute step-inst until the structure of the stack frame has changed, i.e. the function is called or the current one is returned.

As I noticed, the current version of LLVM does not have a step mode that stops when a function returns or when any function is called.

There is a "finish" ("turn off the stream") to stop when exiting a function; there is also "nexti" ("thread step-inst-over") for a single step without visiting function calls.

There are LLDB sources with a list of all supported modes: http://llvm.org/viewvc/llvm-project/lldb/trunk/source/Commands/CommandObjectThread.cpp?revision=194531&view=markup - check the very end of the file for the list of commands - in CommandObjectMultiwordThread::CommandObjectMultiwordThread

I think it’s easy to implement the required step mode in LLDB, because there are components for performing both steps before returning ( CommandObjectThreadStepWithTypeAndScope (... eStepTypeOut, eStepScopeSource) => QueueThreadPlanForStepOut ) and function detector (for CommandObjectThreadStepWithTypeAndScope (...eStepTypeTraceOver,eStepScopeInstruction) => QueueThreadPlanForStepSingleInstruction ). The code Target / ThreadPlanStepInstruction.cpp should help.

+3
source

It is unclear whether you want to step or just continue until the function exits. In the latter case, if you can determine the location of the return address on the stack, you can put the watch clock on it. The RET command, which ultimately leaves the current function, will need to read this location to find the return address.

Finding the location of the return address can be automatic if you have a valid frame pointer. The following is an example of using gdb :

 Breakpoint 1, 0x080483e6 in foo () (gdb) disas foo Dump of assembler code for function foo: 0x080483e3 <+0>: push %ebp 0x080483e4 <+1>: mov %esp,%ebp => 0x080483e6 <+3>: nop 0x080483e7 <+4>: xor %eax,%eax 0x080483e9 <+6>: mov %ebp,%esp 0x080483eb <+8>: pop %ebp 0x080483ec <+9>: ret 0x080483ed <+10>: nop 0x080483ee <+11>: nop 0x080483ef <+12>: nop End of assembler dump. (gdb) p/a $ebp+4 $1 = 0xffffd9f8 (gdb) rwatch *(int*)0xffffd9f8 Hardware read watchpoint 2: *(int*)0xffffd9f8 (gdb) c Continuing. Hardware read watchpoint 2: *(int*)0xffffd9f8 Value = 134513633 0x080483e1 in main () (gdb) disas main Dump of assembler code for function main: 0x080483dc <+0>: call 0x80483e3 <foo> => 0x080483e1 <+5>: nop 0x080483e2 <+6>: ret End of assembler dump. 

Once you have a return address, you can also use a daily temporary breakpoint if your function is not reentrant:

 (gdb) x/a $ebp+4 0xffffd9f8: 0x80483e1 <main+5> (gdb) tbreak *0x80483e1 Temporary breakpoint 3 at 0x80483e1 (gdb) c Continuing. Temporary breakpoint 3, 0x080483e1 in main () 

Without a frame pointer, it is easy to find the return address at the beginning of the function. Otherwise, you will need to do reverse engineering to see how the stack pointer has changed since the function was entered.

+1
source

All Articles