首页 > 解决方案 > 在例程中间调用地址

问题描述

我正在wireshark-2.6.10使用Pin. 在初始化期间的几个点,我可以看到一些调用,例如:

00000000004e9400 <__libc_csu_init@@Base>:
  ...
  4e9449:       41 ff 14 dc             callq  *(%r12,%rbx,8)
  ...

此调用的目标是0x197db0,如下所示:

0000000000197cb0 <_start@@Base>:
  ...
  197db0:       55                      push   %rbp
  197db1:       48 89 e5                mov    %rsp,%rbp
  197db4:       5d                      pop    %rbp
  197db5:       e9 66 ff ff ff          jmpq   197d20 <_start@@Base+0x70>
  197dba:       66 0f 1f 44 00 00       nopw   0x0(%rax,%rax,1)
  ...

Pin表示这是在包含例程的中间,即_start@@Base. 但是,当我使用 达到这个目标时gdb,我看到以下输出:

>│0x5555556ebdb0 <frame_dummy>                                    push   %rbp
 │0x5555556ebdb1 <frame_dummy+1>                                  mov    %rsp,%rbp
 │0x5555556ebdb4 <frame_dummy+4>                                  pop    %rbp
 │0x5555556ebdb5 <frame_dummy+5>                                  jmpq   0x5555556ebd20 <register_tm_clones>
 │0x5555556ebdba <frame_dummy+10>                                 nopw   0x0(%rax,%rax,1)
 │0x5555556ebdc0 <main_window_update()>                           xor    %edi,%edi

请注意,如果我减去偏差值,则运行时目标地址将与编译时值一致(即,0x5555556ebdb0 - 0x555555554000 = 0x197db0)。似乎存在一个pseudo-routine被调用的frame_dummyinside _start@@Base。这怎么可能?如何pseudo-routines事先(即在执行之前)提取这些的地址?

更新:

对函数中间的这些类型的调用不存在于GIMPand中Anjuta(它们几乎完全是在C源代码中编写和构建的)。但是存在于Inkscapeand Wireshark(写在 中C++,虽然我不认为语言是原因。这两个是从包中安装的。)。

起初,这种情况似乎只发生在初始化期间和调用main()函数之前。但是,至少在wireshark-2.6.10这至少发生在一个地方main()开始之后。在这里,我们有wireshark-qt.cpp: Lines 522-524(它是 的一部分main())。

/* Get the compile-time version information string */
comp_info_str = get_compiled_version_info(get_wireshark_qt_compiled_info,
                      get_gui_compiled_info);

这是对get_compiled_version_info(). 在汇编中,函数在地址处被调用0x5555556e74c2 (0x1934c2 without bias),如下所示:

>│0x5555556e74c2 <main(int, char**)+178>  callq  0x5555556f5870 <get_compiled_version_info>
 │0x5555556e74c7 <main(int, char**)+183>  lea    0x4972(%rip),%rdi        # 0x5555556ebe40 <get_wireshark_runtime_info(_GString*)>
 │0x5555556e74ce <main(int, char**)+190>  mov    %rax,%r13

同样,目标位于另一个函数的中间_ZN7QStringD1Ev@@Base

00000000001980f0 <_ZN7QStringD1Ev@@Base>:
...
1a1870:       41 54                   push   %r12
...

这是gdb ( 0x5555556f5870 - 0x555555554000 = 0x1a1870) 的输出:

>│0x5555556f5870 <get_compiled_version_info>      push   %r12
 │0x5555556f5872 <get_compiled_version_info+2>    mov    %rdi,%r12
 │0x5555556f5875 <get_compiled_version_info+5>    push   %rbp
 │0x5555556f5876 <get_compiled_version_info+6>    lea    0x349445(%rip),%rdi        # 0x555555a3ecc2

可以看出,调试器识别出这个地址是 的起始地址get_compiled_version_info()。这是因为它可以访问debug_info. 在我发现的所有情况下,这些符号pseudo-routines都已从原始二进制文件中删除(因为.symtab已从二进制文件中删除)。但奇怪的是,它位于里面_ZN7QStringD1Ev@@Base。因此,Pin认为get_compiled_version_info()是在里面_ZN7QStringD1Ev@@Base

标签: debugginggdbfunction-callbacktraceintel-pin

解决方案


这怎么可能?

frame_dummy一个真正的 C 函数。如果Pin认为它在中间_start,那可能是因为:

  1. _start是一个汇编函数,并且
  2. .st_size在符号表中设置不正确。

您可以通过查看来确认这一点readelf -Ws a.out | egrep ' (_start|frame_dummy)'

您可能正在使用与相当旧的 GLIBC 链接的二进制文件。

GLIBC 用于生成 C 运行时启动文件(从何_start而来),方法是使用gcc -S从 C 源代码创建程序集,然后使用sed. 指令.size错误是该方法的一个问题,并且从 2012 年开始不再使用x86_64commit)。

如何事先(即在执行之前)提取这些伪例程的地址?

Pin 不会神奇地创建这些伪程序,它们必须在readelf -Ws原始二进制文件的输出中可见。


推荐阅读