如何在动态打开的共享库的条目上设置断点? [英] How to set breakpoint on entry of dynamically opened shared library?
问题描述
对于某些情况,我正在使用实验性事务性内存模型(使用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?
您已经看到了.
我认为您的困惑在于无法理解动态链接的流程运行时会发生什么.大致来说,这些步骤是:
-
内核创建一个新的进程"shell".并
mmap
将可执行文件放入其中. -
内核观察到可执行文件具有
PT_INTERP
段,并且mmap
还将在其中引用的文件也输入到进程中.在这里,PT_INTERP
的内容是/lib64/ld-linux-x86-64.so.2
,也就是动态加载程序,不要与/usr/bin/ld
(又是静态链接器)相混淆.此外,由于有一个程序解释器,内核将控制权转移给了 it (而不是在主要可执行文件中调用
_start
),因为主要可执行文件还没有准备好运行. /p> -
当
ld-linux
开始运行时,它会先重新定位自身,然后mmap
s all 直接与主要可执行文件链接的库.您可以通过readelf -d a.out | grep NEEDED
看到这些库.注意:由于这些库中的每个库本身都可能直接依赖于其他库,因此将以递归方式重复此过程.
-
初始化库(通过调用其构造函数,该函数通常称为
_init
,但也可以具有不同的名称)< ==这是libgcc_s.so.1
初始化的地方,其register_tm_clones
被称为. -
加载并初始化所有库后,
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:
The kernel creates a new process "shell" and
mmap
s the executable into it.The kernel observes that the executable has
PT_INTERP
segment, andmmap
s the file referenced there into the process as well. Here, the contents ofPT_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.When
ld-linux
starts running, it first relocates itself, thenmmap
s all the libraries that the main executable directly linked against. You can see these libraries withreadelf -d a.out | grep NEEDED
.Note: since each of these libraries may itself direcly depend on other libraries, this process is repeated recursively.
The libraries are initialized (by calling their constructor function, which is often called
_init
but can have different name as well) <== this is wherelibgcc_s.so.1
is initialized, and itsregister_tm_clones
is called.Once all libraries are loaded and initialized,
ld-linux
finally calls_start
in the main executable, which will eventually callmain
.
这篇关于如何在动态打开的共享库的条目上设置断点?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!