【问题标题】:Determine address of _dl_start确定_dl_start的地址
【发布时间】:2026-01-03 11:30:01
【问题描述】:

我想获取函数_dl_start(动态链接器的入口点)的地址。我可以使用 gdb 设置断点。我希望使用 readelf 找到符号,但我没有。如何获取地址/gdb 如何解析_dl_start?

使用gdb设置断点的示例源(main.cpp)是

int main( int argc, char** argv, char** envp )
{

  return 0;
}

我用它编译过

 g++ main.cpp -o teststart 

运行程序时的gdb输出是

(gdb) b _dl_start
Function "_dl_start" not defined.
Make breakpoint pending on future shared library load? (y or [n]) y
Breakpoint 1 (_dl_start) pending.
(gdb) r
Starting program: /tmp/teststart 

Breakpoint 1, 0x00007fa7ee8c4fc4 in _dl_start () from /lib64/ld-linux-x86-64.so.2

【问题讨论】:

    标签: gdb elf


    【解决方案1】:

    _dl_start 符号在 ld-linux-x86-64.so.2(动态加载器)中,并且该符号对于 ld-linux 是私有。这意味着从程序内部找到它的唯一方法是执行与 GDB 相同的操作:读取 ld-linux 的符号表,并在其中搜索“_dl_start”函数(按名称)。直接链接到它(正如 Martin 建议的那样)不能也不会起作用(正如您已经发现的那样)。

    读取 ELF 符号表并不是很复杂——您只需找到 .symtab.strtab 部分,并将 .symtab 读取为 Elf64_Sym 条目的表。或者使用libelf(启动here)。

    另一个复杂的问题是ld-linux 可以被剥离(它不需要符号表来工作)。如果被拒绝,GDB 和您的程序都无法找到_dl_start

    最后,您查找_dl_start 的尝试很可能是毫无意义的:您确实意识到在程序的第一条指令之前调用了该函数被执行。当你点击main 时,_dl_start 早就打完了,再也不会被调用了。

    更新:

    我还是想知道gdb是如何获取ld-linux中_dl_start的地址的(被剥离了)

    如果ld-linux 被剥离,GDB 将 无法在其中找到_dl_start。既然你 GDB 确实找到它,要么

    1. 您的 ld-linux 实际上并未被剥离,或者
    2. 您已为 glibc 安装了“单独的调试信息”package

    要验证ld-linux 是否真的被完全剥离,请运行nm /lib64/ld-linux-x86-64.so.2 | grep _dl_startreadelf -S /lib64/ld-linux-x86-64.so.2 | grep symtab。这两个命令都不应产生任何输出。

    要查看 GDB 从哪里加载符号,您可以使用 set print symbol-loading on 命令(在运行可执行文件之前)。

    我想调用 _dl_start(在准备堆栈和调整辅助向量之后)来创建已存储在内存中的程序的可执行映像(文件表示)...

    我不明白这可能是如何工作的。 _dl_start 在被调用之前期望某些状态(例如,它的全局变量被清零),所以第二次调用它非常很可能导致断言失败,即使你不调整辅助向量。如果您确实以某种非平凡的方式调整辅助向量,则断言更有可能,这(显然)是您的目标。

    【讨论】:

    • 谢谢。我想调用_dl_start(在准备堆栈和调整辅助向量之后)来创建已经存储在内存中的程序的可执行映像(文件表示)......我仍然想知道gdb如何在ld-linux中获取_dl_start的地址(它被剥离)
    • 安装额外的调试包是线索
    【解决方案2】:

    _dl_start 不是程序本身的一部分,它包含在运行时加载程序中(从输出“..._dl_start () from /lib64/ld-linux-x86-64.so.2”中可以看到)。 GDB 最初无法设置断点,因为它不包含在您的可执行文件中。 我有点不清楚,如果您想从程序内部或外部知道 _dl_start 的地址?从内部,您应该能够简单地分配它,例如像这样的 void* 变量:

    void* address = dl_start;
    

    【讨论】:

    • 谢谢马丁。我需要程序中的地址/函数指针。您提出的简单分配不起作用(在添加了一些必要的演员之后)。包含 dl_start 的对象是动态加载的,因此这样做会出现未解析的符号(链接)错误。