Debugging 函数步骤/跟踪步骤的lldb命令:继续,直到下一个函数调用或当前函数从
在LLDB中,如何实现函数步骤/跟踪步骤?也就是说,继续,直到调用函数或从返回当前函数为止。假设在之前没有可用的源代码来执行Debugging 函数步骤/跟踪步骤的lldb命令:继续,直到下一个函数调用或当前函数从,debugging,assembly,lldb,Debugging,Assembly,Lldb,在LLDB中,如何实现函数步骤/跟踪步骤?也就是说,继续,直到调用函数或从返回当前函数为止。假设在之前没有可用的源代码来执行 这相当于执行步骤inst,直到堆栈帧结构更改。不清楚是要执行步骤还是继续,直到函数退出。 对于后一种情况,如果您能够找出堆栈上的返回地址位置,则可以在其上放置一个读表。最终将离开当前函数的RET指令将需要读取该位置以查找返回地址 如果具有有效的帧指针,则可以自动查找返回地址位置。下面是一个使用gdb的示例: Breakpoint 1, 0x080483e6 in foo
这相当于执行步骤inst
,直到堆栈帧结构更改。不清楚是要执行步骤还是继续,直到函数退出。
对于后一种情况,如果您能够找出堆栈上的返回地址位置,则可以在其上放置一个读表。最终将离开当前函数的RET
指令将需要读取该位置以查找返回地址
如果具有有效的帧指针,则可以自动查找返回地址位置。下面是一个使用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.
foo()中的断点1,0x080483e6
(gdb)disas foo
函数foo的汇编程序代码转储:
0x080483e3:推送%ebp
0x080483e4:mov%esp,%ebp
=>0x080483e6:nop
0x080483e7:xor%eax,%eax
0x080483e9:mov%ebp,%esp
0x080483eb:弹出%ebp
0x080483ec:ret
0x080483ed:否
0x080483ee:nop
0x080483ef:nop
汇编程序转储结束。
(gdb)应付账款$ebp+4
$1=0xffffd9f8
(gdb)rwatch*(int*)0xffffd9f8
硬件读取观察点2:*(int*)0xffffd9f8
(gdb)c
持续的。
硬件读取观察点2:*(int*)0xffffd9f8
值=134513633
主管道中的0x080483e1()
(gdb)disas main
主功能的汇编程序代码转储:
0x080483dc:调用0x80483e3
=>0x080483e1:nop
0x080483e2:ret
汇编程序转储结束。
获得返回地址后,如果函数不可重入,还可以使用每日临时断点:
(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 ()
(gdb)x/a$ebp+4
0xffffd9f8:0x80483e1
(gdb)tbreak*0x80483e1
0x80483e1处的临时断点3
(gdb)c
持续的。
主()中的临时断点3,0x080483e1
如果没有帧指针,只需在函数开头找到返回地址即可。否则,您将需要执行一些反向工程,以查看自函数项之后堆栈指针是如何更改的
在LLDB中,如何单步执行,直到当前程序集级函数离开?(在之前没有可执行的源代码)。
我正在寻找一种自动执行步骤inst的方法,直到堆栈帧结构发生变化,即调用函数或从中返回当前函数
正如我所检查的,当前版本的LLVM没有这样的步进模式,它将在函数返回或任何函数调用时停止
在函数退出时有“完成”(“线程步出”)停止;还有“nexti”(“threadstep inst over”),用于单步执行而不访问调用的函数
LLDB的来源包括所有支持的模式列表:
-检查文件末尾的命令列表-在CommandObjectMultiwordThread::CommandObjectMultiwordThread
我认为在LLDB中实现所需的步进模式很容易,因为有一些组件可以实现步进直到返回(CommandObjectThreadStepWithTypeAndScope(…eStepTypeOut,eStepScopeSource)
=>QueueThreadPlanForStepOut
)和函数调用检测器(对于CommandObjectThreadStepWithTypeAndScope(…eStepTypeTraceOver,eStepScopeInstruction)
=>QueueThreadPlanForStepSingleInstruction
)。中的代码应该会有所帮助。这是一个以lldb为目标的python脚本,它添加了一个“step function”命令。只要调用堆栈结构发生更改,该命令就会停止
步进函数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__)
用法示例:
$ 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)
正如我所写:“我正在寻找一种自动执行步骤inst的方法,直到堆栈帧结构发生变化,即调用函数或从中返回当前函数。”。因此,调试器在这两种情况下都应该中断。建议的解决方案仅适用于后一种情况。Danra,gdb中是否存在相同的功能?gdb用于此的命令是什么?是命令finish
(据推测,gdb和LLDB都有该命令)您需要什么?@osgx-我不知道gdb命令用于此目的。@Iwillnotexistidnotexist否,因为当从当前函数中调用新函数时,finish
不会中断。希望找到一个不需要我实现的解决方案:)LLDB中不支持所需的ThreadPlan,所以,要么您应该在LLDB内部实现新的计划,要么您应该尝试围绕单个指令步进编写脚本,以检测ret
和call
指令。SBAPI相当于调试器停止时打印的函数SBThread::GetStatus(stream)。这将打印线程停止原因和带源的帧0输出(如果可用)(使用线程格式和帧格式设置)。谢谢Jim!将集成到解决方案中。