【问题标题】:How to set breakpoint on entry of dynamically opened shared library?如何在动态打开的共享库入口处设置断点?
【发布时间】:2020-05-14 05:21:31
【问题描述】:

在某些情况下,我正在检查一个使用实验性事务内存模型的简单 C++ 程序,该模型是用 g++ 编译的。我想知道 register_tm_clones 的确切调用位置(您可以通过 objdumping 一个简单的程序来查看 fn)。即使在像int main() {} 这样的程序中也会调用这个函数。

我想知道调用register_tm_clones 的通用程序的整个范围内的哪个位置。我在 GDB 中对其设置断点并回溯:

Breakpoint 1, 0x00007ffff7c5e6e0 in register_tm_clones () from /usr/lib/libgcc_s.so.1
(gdb) bt
#0  0x00007ffff7c5e6e0 in register_tm_clones () from /usr/lib/libgcc_s.so.1
#1  0x00007ffff7fe209a in call_init.part () from /lib64/ld-linux-x86-64.so.2
#2  0x00007ffff7fe21a1 in _dl_init () from /lib64/ld-linux-x86-64.so.2
#3  0x00007ffff7fd313a in _dl_start_user () from /lib64/ld-linux-x86-64.so.2
#4  0x0000000000000001 in ?? ()
#5  0x00007fffffffe390 in ?? ()
#6  0x0000000000000000 in ?? ()

libgcc 在程序中的某个点被ld-linux 打开时调用它。我确保我们与libgcc 相关联。是的:

❯ ldd main
    linux-vdso.so.1 (0x00007fff985e4000)
    libstdc++.so.6 => /usr/lib/libstdc++.so.6 (0x00007f7eb82dc000)
    libm.so.6 => /usr/lib/libm.so.6 (0x00007f7eb8196000)
    libgcc_s.so.1 => /usr/lib/libgcc_s.so.1 (0x00007f7eb817c000)
    libc.so.6 => /usr/lib/libc.so.6 (0x00007f7eb7fb6000)
    /lib64/ld-linux-x86-64.so.2 => /usr/lib64/ld-linux-x86-64.so.2 (0x00007f7eb84ec000)

但是...我怎么知道它何时被调用(肯定不在main 中)?我知道_start是C++程序的真正入口。然后我们运行__libc_csu_init,然后有一些步骤,我们进入main。如何设置断点以查看大图以查看 ld 何时决定打开 libgcc 以及因此调用 register_tm_clones 的位置?

【问题讨论】:

  • 您的问题不清楚:您已经看到确切在哪里调用它。另外,ld != ld-linux,请不要混淆,以免混淆大家。
  • 抱歉,我应该更清楚我的要求。我的意思是我想确切地知道,从_start 到程序结束,在哪里调用了register_tm_clones。另外,我想我不熟悉ldld-linux 之间的区别。你能向我解释一下区别,以便我可以编辑问题陈述吗?我已经更新了问题,使我要问的问题更清楚。

标签: c++ linux gdb ld


【解决方案1】:

我如何设置断点以查看 ld 何时决定打开 libgcc 以及如何调用 register_tm_clones?

已经看到了。

我认为您的困惑在于不了解动态链接进程运行时会发生什么。大致步骤如下:

  1. 内核创建一个新进程“shell”并将mmaps 可执行文件放入其中。

  2. 内核观察到可执行文件有PT_INTERP 段,并且mmaps 文件也被引用到进程中。在这里,PT_INTERP 的内容是/lib64/ld-linux-x86-64.so.2,又名动态加载器,不要与/usr/bin/ld(又名静态链接器)混淆。

    此外,由于有程序解释器,内核将控制权转移给(而不是在主可执行文件中调用_start),因为主可执行文件还没有准备好运行。

  3. ld-linux 开始运行时,它首先重新定位自己,然后mmaps 所有主可执行文件直接链接的库。您可以使用readelf -d a.out | grep NEEDED 查看这些库。

    注意:由于这些库中的每一个本身都可能直接依赖于其他库,因此递归地重复此过程。

  4. 这些库被初始化(通过调用它们的构造函数,通常称为_init,但也可以有不同的名称)libgcc_s.so.1 的地方,它的register_tm_clones 被调用。

  5. 加载并初始化所有库后,ld-linux 最终会在主可执行文件中调用_start,最终将调用main

【讨论】:

  • 我明白了。感谢您的详尽解释!我不确定动态链接器何时执行整个映射。我一直认为_start 是程序的真正开始。因此,如果我没记错的话(只是总结一下):动态链接器映射所有需要的共享库,运行它们的 _init(或其他任何名称),然后在主可执行文件中调用 _start,是对吗?
  • @OneRaynyDay 是的,没错。 _start 程序的实际开始,但ld-linux 会运行一段时间(1000 条指令中的 10 条),然后才到达主可执行文件中的 _start。更令人困惑的是,ld-linux 也有它自己的_start,这是第一个用户空间指令执行的地方。有点相关:stackoverflow.com/a/22491581/50617
  • 我明白了。非常感谢!这有助于澄清很多事情,远远超出了这个问题的范围。感谢您的帮助:)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2016-11-26
  • 2014-03-29
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-05-28
相关资源
最近更新 更多