使用" .init_array" ELF文件的一部分

时间:2016-03-06 13:12:20

标签: linux assembly elf gas

如果需要在程序启动时运行一段代码(在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

运行生成的可执行文件时,只有"主代码"字符串打印。如何正确使用.init_array部分?

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

  

在将控制转移到应用程序之前,运行时链接程序   处理在应用程序中找到的任何初始化部分   加载的依赖项。初始化部分.preinit_array,   .init_array和.init是动态时由链接编辑器创建的   对象已建成。

     

运行时链接程序执行其地址包含在其中的函数   .preinit_array和.init_array部分。这些功能是   按照其地址出现在的相同顺序执行   阵列。

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

2 个答案:

答案 0 :(得分:2)

如果您按照自己的代码在_start入口点制作静态链接裸可执行文件,那么您的代码就会从该点开始运行。如果您想要实现某些目标,您的代码必须实现。没有魔力。

使用部分可以将来自多个源文件的启动代码组合在一起,因此所有启动代码都很冷,可能会被分页,或者至少不需要TLB条目。

所以你通过在那里放置函数并在_start之后的某个时间运行的代码中调用它来“正确使用”部分。

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

动态链接:

“运行时链接程序”是动态二进制文件的ELF解释器。它在_start之前在你的进程中运行代码,所以是的,显然它确实处理了ELF部分并使魔术发生。

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

如果你遇到困难,请发表评论。我可以稍后在有更多时间时更新,或者随意编辑工作命令。

答案 1 :(得分:1)

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

所以我看到两种方法:您可以简单地链接到 glibc 启动代码。或者你必须自己寻找其他机制来解决这个问题。