【问题标题】:How to step through a longjmp in gdb如何在 gdb 中单步执行 longjmp
【发布时间】:2016-11-03 18:14:45
【问题描述】:

我正在尝试修复其他人编写的代码中的错误,并且我正在尝试在 gdb 中逐步解决它以找出发生了什么。但是我打的其中一行是对 longjmp() 的调用,并且在该行上点击“next”之后,gdb 会继续常规执行,而不是在下一个正在执行的源代码行上中断。如果我在 longjmp() 行上尝试“step”,则会发生类似的继续。是否有任何 gdb 命令可以用来中断在 longjmp() 之后正在执行的下一个源代码行?

【问题讨论】:

  • 您可以执行break main,然后执行run 并通过程序保持stepping。然后它会在每一行提示输入,包括 longjmp() 之后的那一行。 (如果这回答了你的问题,我会把它放在答案中)
  • 如果您在该行点击“step”而不是“next”会发生什么?您可以做的一件事是在处理来自setjmp() 的非零返回值的代码上添加一个断点。
  • @MDXF,需要明确的是,您是否要使用step 命令而不是next 命令? OP说这对他不起作用。我看到您建议专门在main 上打破,但我不明白为什么这会与在longjmp() 之前的其他地方打破相比有所不同。

标签: c gdb setjmp


【解决方案1】:

值得注意的是,gdb 的行为是依赖于系统的。

在 Linux 上,nextlongjmpthrow 上的工作方式与您预期的大致相同:如果非本地跳转的目标位于或高于当前帧,则执行将停止在那里。

这是在longjmpthrow 的实现中使用调试钩子实现的。对于throw,这是通过辅助函数(旧方法)和所谓的 SystemTap 探针(又名“sdt 探针”)来完成的。对于longjmp,这只能通过 glibc 中的 sdt 探针完成。

为此,必须将探针支持编译到相关库中。您可以通过readelf -n 查询。至少 Fedora 正确地做到了这一点。

理论上可以在 gdb 中为其他平台实现对longjmp 的支持。但是,这可能并不简单。

您可以尝试解码jmp_buf,如其他 cmets 中所述。但是请注意,在某些系统上,例如 Linux,出于安全原因,jmp_buf 中的目标 PC 已被编码。所以你必须弄清楚如何解码它。

解决此问题的另一种方法是简单地单步通过longjmp 本身。在那里设置断点,比如break longjmp;使用disassemble查看程序集;然后si,直到您逐步执行将执行转移到目标的指令。

【讨论】:

    【解决方案2】:

    您需要在setjmp 的非零返回码之后的行设置断点。

    例如:

    #include <stdio.h>
    #include <setjmp.h>
    
    jmp_buf jb;
    
    void f1()
    {
        printf("jumping\n");
        longjmp(jb, 1);
        printf("what???\n");
    }
    
    int main()
    {
        if (!setjmp(jb)) {
            printf("calling f1\n");
            f1();
        } else {
            printf("jumped!!\n");    // line 19
        }
        return 0;
    }
    

    在调用longjmp 之后,下一行要执行的是第 19 行(见注释)。因此,在这种情况下,在 gdb 提示符下运行 break 19,您将在调用 longjmp 后停在该行。

    gdb 输出:

    (gdb) break 19
    Breakpoint 2 at 0x40056d: file /tmp/x1.c, line 19.
    (gdb) start
    Temporary breakpoint 3 at 0x400549: file /tmp/x1.c, line 15.
    Starting program: /tmp/x1
    warning: no loadable sections found in added symbol-file system-supplied DSO at 0x2aaaaaaab000
    
    Temporary breakpoint 3, main () at /tmp/x1.c:15
    15          if (!setjmp(jb)) {
    (gdb) step
    16              printf("calling f1\n");
    (gdb)
    calling f1
    17              f1();
    (gdb)
    f1 () at /tmp/x1.c:8
    8           printf("jumping\n");
    (gdb)
    jumping
    9           longjmp(jb, 1);
    (gdb)
    
    Breakpoint 2, main () at /tmp/x1.c:19
    19              printf("jumped!!\n");
    (gdb)
    jumped!!
    21          return 0;
    (gdb)
    22      }
    (gdb)
    0x0000003fa441d9f4 in __libc_start_main () from /lib64/libc.so.6
    (gdb)
    

    【讨论】:

    • 这基本上奏效了。花了一点时间来找出最后调用的 setjmp 是什么,但是在足够多的 setjmp 的非零返回之后放置断点后,我最终弄清楚了程序的去向。
    【解决方案3】:

    是否有任何 gdb 命令可以用来中断在 longjmp() 之后正在执行的下一个源代码行?

    据我所知,没有。您需要跟踪 jmp_buf 的来源,备份调用堆栈以找到填充它的 setjmp()。如果源代码中的setjmp() 调用很少,您可以考虑在每个调用之后设置一个断点。否则,您可能只是单步执行程序,在您通过的每个 setjmp() 之后设置一个断点。

    另请注意,当到达相应的longjmp() 时,您确实应该在调用堆栈中的某个函数中找到相关的setjmp(),否则jmp_buf 无效。

    您可以使用bt 命令获取调用堆栈,并使用frame 命令切换并检查堆栈上的任何帧。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-03-23
      • 2012-07-01
      • 1970-01-01
      • 1970-01-01
      • 2010-11-11
      相关资源
      最近更新 更多