真正的入口点是动态加载程序中的 Linux 程序吗?如何在调用堆栈中显示它?

Is the real entry point a Linux program in the dynamic loader and how to show it in the callstack?

我认为 Linux 使用共享库(未使用 -static 编译)的程序的启动过程是:

(1) 在bash

中输入可执行文件的名称

(2) Bash 读取可执行文件,发现它包含一个“.interp”部分,其中包含动态加载程序的名称。

(3) Bash fork一个新进程,在子进程中执行动态加载器,将可执行文件的name/path作为参数传入动态加载器。但我不确定入口点叫什么,动态加载器中没有“_start”,它是 /lib64/ld-linux-x86-64.so.2

(4)exec return从Linux内核到用户态后,用户态执行的第一条指令应该是动态加载器的入口点.

(5) 动态加载器加载可执行文件(其name/path作为参数传入)及其所有依赖项,动态加载器将调用“.init_array”部分的方法每个加载的模块。特别是,主可执行文件中“.init_array”部分中的方法也应由动态加载程序执行。从动态加载器的角度来看,主可执行文件和共享库之间确实没有太大区别。

(6) 动态加载程序调用主可执行文件的入口点,即_start。这意味着,如果动态加载程序的入口点也称为_start,则从此处开始的调用堆栈中应该有两个_start。

以上理解有没有问题?

但问题是,在用gdb调试程序时使用

set backtrace past-main
start
bt

它只显示到 _start 的回溯,但是动态加载器中的调用栈呢?也试过:

set backtrace past-entry
start
bt

现在它在 main 之前什么都不显示...

Is there any problem in the above understanding?

是的:大部分都是错误的。

bash 并不看二进制文件“内部”,它只是 fork()s 和 exec()s 它。 kernel映射二进制,发现有PT_INTERPsegment(不关心sections 在运行时)在其中,映射解释器并将控制权传递给它。

After exec return from Linux kernel to user mode, the first instruction executed in user mode should be the entry point of the dynamic loader.

正确。

The dynamic loader load the executable

这又是部分错误:内核已经映射了主要的可执行文件。

The dynamic loader call the entry point of the main executable, which is _start. This means, if the entry point of the dynamic loader is also called _start, there should be two _start in the callstack from this point on.

动态加载器将控制权转移到主要的可执行入口点。控制转移不必是 CALL,它可以是 JMP,在这种情况下,您不应该期望找到 a.out:_entry.[=25= 的任何调用者]

x86_64 上,这发生在 _dl_start_user 汇编例程中(在 sysdeps/x86_64/dl-machine.h 文件中),它看起来像这样:

_dl_start_user:
    # Save the user entry point address in %r12.
    movq %rax, %r12
...
    # Jump to the user's entry point.
    jmp *%r12