In LLDB, how can I implement a function-step / trace-step? That is, continue until either a function is called, or the current function is returned from. Assume no source code is available to perform until
.
This would be equivalent to perform step-inst
until the stack frame structure changes.
Here's an lldb targeted python script which adds a "step-function" command. The command stops whenever the call stack structure 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)
In LLDB, how can I step until the current assembly-level function is left? (No source code is available to perform until). I'm looking for an automated way to perform step-inst until the stack frame structure changes, that is a function is called or the current one is returned from.
As I checked, current current version of LLVM have no such stepping mode, which will stop at function return or at any function call.
There is "finish" ("thread step-out") to stop at exit of function; there is also "nexti" ("thread step-inst-over") to single stepping without visiting called functions.
There are sources of LLDB with 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 file for command list - in CommandObjectMultiwordThread::CommandObjectMultiwordThread
I think it can be easy to implement the needed stepping mode in the LLDB, because there are components to implement both stepping until return (CommandObjectThreadStepWithTypeAndScope (... eStepTypeOut, eStepScopeSource)
=> QueueThreadPlanForStepOut
) and function call detector (for CommandObjectThreadStepWithTypeAndScope (...eStepTypeTraceOver,eStepScopeInstruction)
=> QueueThreadPlanForStepSingleInstruction
). The code in Target/ThreadPlanStepInstruction.cpp should help.
Not clear whether you want to step or just continue until the function exits.
For the latter case, if you can figure out the return address location on the stack, you can put a read watch on it. The RET
instruction that will eventually leave the current function will need to read that location to find the return address.
Finding the return address location can be automated if you have a valid frame pointer. Here is an example 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 the return address, you can also use everyday 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's only easy to find the return address at the beginning of the function. Otherwise you will need to do some reverse engineering, to see how the stack pointer has been changed since the function entry.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With