简体   繁体   English

如何在动态打开的共享库入口处设置断点?

[英]How to set breakpoint on entry of dynamically opened shared library?

For some context, I'm inspecting a simple C++ program using the experimental transactional memory model, compiled with g++.在某些情况下,我正在检查一个简单的 C++ 程序,该程序使用实验性事务 memory model,用 Z6264F87F5281EZEC7464 编译。 I want to know exactly where register_tm_clones is called(you can see the fn by objdumping a simple program).我想知道register_tm_clones的确切调用位置(您可以通过 objdumping 一个简单的程序来查看 fn)。 This function will be called even in a program like int main() {} .这个 function 即使在像int main() {}这样的程序中也会被调用。

I want to know where in the whole scope of a general program where register_tm_clones is called.我想知道调用register_tm_clones的通用程序的整个 scope 的哪个位置。 I set a breakpoint on it in GDB and I backtrace:我在 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 ?? ()

It's called when libgcc is opened by ld-linux at some point in the program.ld-linux在程序中的某个位置打开libgcc时调用它。 I make sure that we're linked with libgcc .我确保我们与libgcc相关联。 Yup:是的:

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

But... How do I know when this is being called (It's definitely not in main )?但是......我怎么知道什么时候被调用(它绝对不在main )? I know _start is the true entry of the C++ program.我知道_start是C++程序的真正入口。 and we run __libc_csu_init , and then there's some steps and we get to main.然后我们运行__libc_csu_init ,然后有一些步骤,我们进入 main。 How can I set breakpoints to see in the grand picture to see when ld decided to open libgcc , and consequently where register_tm_clones is called?我如何设置断点以查看大图以查看ld何时决定打开libgcc以及调用register_tm_clones的位置?

How can I set breakpoints to see in the grand picture to see when ld decided to open libgcc, and consequently where register_tm_clones is called?如何设置断点以查看大图以查看 ld 何时决定打开 libgcc,以及调用 register_tm_clones 的位置?

You already see that.已经看到了。

I think your confusion resides in not understanding what happens when a dynamically linked process runs.我认为您的困惑在于不了解动态链接进程运行时会发生什么。 Roughly, the steps are:大致来说,步骤是:

  1. The kernel creates a new process "shell" and mmap s the executable into it. kernel 创建一个新进程“shell”并将可执行文件mmap到其中。

  2. The kernel observes that the executable has PT_INTERP segment, and mmap s the file referenced there into the process as well. mmap观察到可执行文件具有PT_INTERP段,并且将那里引用的文件也映射到进程中。 Here, the contents of PT_INTERP is /lib64/ld-linux-x86-64.so.2 , aka dynamic loader, not to be confused with /usr/bin/ld (aka the static linker).在这里, PT_INTERP的内容是/lib64/ld-linux-x86-64.so.2 ,也就是动态加载程序,不要与/usr/bin/ld (又名 static 链接器)混淆。

    Further, because there is a program interpreter, the kernel transfers control to it (instead of calling _start in the main executable), because the main executable is not ready to run yet.此外,由于存在程序解释器,kernel 将控制权转移给(而不是在主可执行文件中调用_start ),因为主可执行文件尚未准备好运行。

  3. When ld-linux starts running, it first relocates itself, then mmap s all the libraries that the main executable directly linked against.ld-linux开始运行时,它首先重新定位自己,然后mmap主要可执行文件直接链接的所有库。 You can see these libraries with readelf -d a.out | grep NEEDED您可以使用readelf -d a.out | grep NEEDED查看这些库。 readelf -d a.out | grep NEEDED . readelf -d a.out | grep NEEDED

    Note: since each of these libraries may itself direcly depend on other libraries, this process is repeated recursively.注意:由于这些库中的每一个都可能直接依赖于其他库,因此递归地重复此过程。

  4. The libraries are initialized (by calling their constructor function, which is often called _init but can have different name as well) <== this is where libgcc_s.so.1 is initialized, and its register_tm_clones is called.库被初始化(通过调用它们的构造函数 function,它通常被称为_init但也可以有不同的名称)<== 这是libgcc_s.so.1被初始化的地方,它的register_tm_clones被调用。

  5. Once all libraries are loaded and initialized, ld-linux finally calls _start in the main executable, which will eventually call main .一旦所有库都加载并初始化, ld-linux最终会在主可执行文件中调用_start ,最终会调用main

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM