【问题标题】:lldb command to function-step / trace-step: continue until next function call or until current function is returned fromlldb 命令到 function-step / trace-step:继续直到下一个函数调用或直到当前函数从返回
【发布时间】:2014-03-27 10:01:04
【问题描述】:

在 LLDB 中,如何实现功能步骤/跟踪步骤?也就是说,继续直到调用一个函数,或者从当前函数返回。假设没有可用于执行until 的源代码。

这相当于执行step-inst,直到堆栈帧结构发生变化。

【问题讨论】:

  • Danra,gdb 中是否存在相同的功能?什么是 gdb 命令?
  • 命令finish(据说GDB和LLDB都有)是你需要的吗?
  • @osgx - 我不知道用于此的 gdb 命令。
  • @IwillnotexistIdonotexist 否,因为finish 不会在从当前函数中调用新函数的情况下中断。

标签: debugging assembly lldb


【解决方案1】:

在函数退出之前,不清楚您是要单步执行还是继续执行。 对于后一种情况,如果你能找出栈上的返回地址位置,你可以在上面放一个读监视。最终离开当前函数的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.

获得返回地址后,如果您的函数不可重入,您还可以使用日常临时断点:

(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 ()

没有帧指针,只有在函数的开头很容易找到返回地址。否则,您将需要进行一些逆向工程,以查看自函数进入以来堆栈指针是如何更改的。

【讨论】:

  • 正如我所写:“我正在寻找一种自动化的方式来执行步进,直到堆栈框架结构发生变化,即调用一个函数或返回当前函数。”。所以调试器应该在这些情况下中断。建议的解决方案仅适用于后一种情况。
【解决方案2】:

在 LLDB 中,我如何才能逐步退出当前的汇编级函数? (直到没有源代码可以执行)。 我正在寻找一种自动化的方式来执行步进,直到堆栈框架结构发生变化,即调用一个函数或返回当前函数。

正如我所检查的,当前版本的 LLVM 没有这种步进模式,它将在函数返回或任何函数调用时停止。

函数退出时有“finish”(“thread step-out”)停止;还有“nexti”(“thread step-inst-over”)单步,无需访问被调用函数。

LLDB 的来源包含所有支持的模式列表: http://llvm.org/viewvc/llvm-project/lldb/trunk/source/Commands/CommandObjectThread.cpp?revision=194531&view=markup - 检查文件末尾的命令列表 - 在CommandObjectMultiwordThread::CommandObjectMultiwordThread

我认为在 LLDB 中实现所需的步进模式很容易,因为有组件可以实现步进直到返回 (CommandObjectThreadStepWithTypeAndScope (... eStepTypeOut, eStepScopeSource) => QueueThreadPlanForStepOut) 和函数调用检测器 (CommandObjectThreadStepWithTypeAndScope (...eStepTypeTraceOver,eStepScopeInstruction) => QueueThreadPlanForStepSingleInstruction)。 Target/ThreadPlanStepInstruction.cpp 中的代码应该会有所帮助。

【讨论】:

  • 希望有一个不需要我实施的解决方案 :)
  • LLDB 中不支持所需的 ThreadPlan,因此您应该在 LLDB 中实施新计划,或者您应该尝试围绕单指令步进执行一些脚本以检测 retcall 指令.
【解决方案3】:

这是一个以 lldb 为目标的 python 脚本,它添加了一个“step-function”命令。只要调用堆栈结构发生变化,该命令就会停止。

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__)

使用示例:

$ 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) 

【讨论】:

  • 与调试器停止时打印的 SB API 等效的是函数 SBThread::GetStatus(stream)。如果可用,这将打印线程停止原因和带有源的第 0 帧输出(使用线程格式和帧格式设置。)
  • 谢谢吉姆!将集成到解决方案中。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2022-01-17
  • 1970-01-01
  • 1970-01-01
  • 2016-11-05
  • 1970-01-01
  • 2014-09-18
  • 2019-12-23
相关资源
最近更新 更多