使用 ELF 文件的“.init_array”部分

Using ".init_array" section of ELF file

程序启动时(Linux上需要运行一段代码时,如何正确使用.init_section可执行文件(ELF32- i386)?我有以下代码(GNU 汇编器),它有 ctor 初始化函数,这个函数的地址放在 .init_array 部分:

.intel_syntax noprefix
.data
s1: .asciz "Init code\n"
s2: .asciz "Main code\n"
.global _start
.global ctor
.text
ctor:
    mov     eax, 4           # sys_write()
    mov     ebx, 1           # stdout
    mov     ecx, offset s1 
    mov     edx, 10
    int     0x80
    ret

.section .init_array
    .long ctor

.text
_start:
    mov     eax, 4
    mov     ebx, 1
    mov     ecx, offset s2
    mov     edx, 10
    int     0x80

    mov     eax, 1
    mov     ebx, 0
    int     0x80

此代码由以下代码汇编而成:

as -o init.o init.asm
ld -o init init.o

当生成的可执行文件为 运行 时,仅打印 "Main code" 字符串。如何正确使用 .init_array 部分?

EDIT1:我想使用 .init_array 因为有多个源文件都有自己的初始化代码。可以在启动时调用所有这些代码 'manually' 并在每次将源文件添加到项目或从项目中删除时修改它,但是 .init_array 似乎就是为这种情况设计的:

Before transferring control to an application, the runtime linker processes any initialization sections found in the application and any loaded dependencies. The initialization sections .preinit_array, .init_array, and .init are created by the link-editor when a dynamic object is built.

The runtime linker executes functions whose addresses are contained in the .preinit_array and .init_array sections. These functions are executed in the same order in which their addresses appear in the array.

如果在没有 gcc 的情况下创建可执行文件,链接器似乎不执行启动代码。我尝试编写自己的标准 init 例程,它读取 .init_array 部分中的函数指针并调用它们。它适用于一个文件,其中可以标记该部分的结尾,例如,用零标记。但是对于多个文件,这个零可以重新定位在部分的中间。如何正确判断由多个源文件组合而成的一节的大小?

如果你按照你现在的方式静态linked裸可执行文件,在_start入口点使用你自己的代码,你的代码只是从那一点开始运行。如果你想让某事发生,你的代码必须让它发生。没有魔法。

使用节可以将来自多个源文件的启动代码组合在一起,因此所有启动代码都是冷的并且可能被调出页面,或者至少不需要 TLB 条目。

所以你 "properly use" 部分通过将函数放在那里并从在 _start.

之后运行的代码调用它们

在您的代码示例中,.init_array 看起来像是一个函数指针列表。我假设标准 CRT 启动文件读取 ELF 文件并找到该部分的长度,然后通过它间接调用这些函数。由于您正在编写自定义代码,因此调用一个执行所有操作的 init 函数会更快。


动态 linking:

"runtime linker" 是动态二进制文件的 ELF 解释器。它在 _start 之前在您的进程中运行代码,所以是的,显然它确实处理了 ELF 部分并让奇迹发生了。


因此,为了响应您的编辑,您的选择是:自己实施 .init_array 的此处理,或创建动态可执行文件。我很确定这个过程已经包含在其他问题中,我没有时间为仍然没有 link libc 的动态可执行文件研究正确的命令行。 (尽管您可能只想使用 gcc -nostartfiles 或其他东西。)

如果遇到困难,请发表评论。无论如何,我可能会在以后有更多时间时更新它,或者随时在工作命令中进行编辑。

对于普通的 C 程序,.init_array 由在调用 main 之前从 _start 调用的函数遍历。 this 网站上有很好的描述。

所以我看到了两种方法:您可以简单地 link 针对 glibc 启动代码。或者你必须自己找出另一种机制来解决这个问题。