如何在动态打开的共享库的条目上设置断点? [英] How to set breakpoint on entry of dynamically opened shared library?

查看:145
本文介绍了如何在动态打开的共享库的条目上设置断点?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

对于某些情况,我正在使用实验性事务性内存模型(使用g ++编译)检查一个简单的C ++程序.我想确切地知道register_tm_clones的调用位置(您可以通过转储一个简单的程序来查看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 ?? ()

在程序中的某个位置由ld-linux打开libgcc时调用.我确保我们已与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决定何时打开libgcc,以及因此在何处调用register_tm_clones?

已经看到了.

我认为您的困惑在于无法理解动态链接的流程运行时会发生什么.大致来说,这些步骤是:

  1. 内核创建一个新的进程"shell".并mmap将可执行文件放入其中.

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

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

  3. ld-linux开始运行时,它会先重新定位自身,然后mmap s all 直接与主要可执行文件链接的库.您可以通过readelf -d a.out | grep NEEDED看到这些库.

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

  4. 初始化库(通过调用其构造函数,该函数通常称为_init,但也可以具有不同的名称)< ==这是libgcc_s.so.1初始化的地方,其register_tm_clones被称为.

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

For some context, I'm inspecting a simple C++ program using the experimental transactional memory model, compiled with g++. I want to know exactly where register_tm_clones is called(you can see the fn by objdumping a simple program). This function will be called even in a program like int main() {}.

I want to know where in the whole scope of a general program where register_tm_clones is called. I set a breakpoint on it in GDB and I backtrace:

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. I make sure that we're linked with 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)? I know _start is the true entry of the C++ program. and we run __libc_csu_init, and then there's some steps and we get to 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?

解决方案

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?

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 mmaps the executable into it.

  2. The kernel observes that the executable has PT_INTERP segment, and mmaps the file referenced there into the process as well. 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).

    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.

  3. When ld-linux starts running, it first relocates itself, then mmaps all the libraries that the main executable directly linked against. You can see these libraries with 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.

  5. Once all libraries are loaded and initialized, ld-linux finally calls _start in the main executable, which will eventually call main.

这篇关于如何在动态打开的共享库的条目上设置断点?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆