如果需要在程序启动时运行一段代码(在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中的函数指针,并调用它们。它适用于一个文件,其中一个文件可以标记该部分的结尾,例如,为零。但是对于多个文件,这个零可以在该部分的中间重新定位。如何正确确定从多个源文件组合的部分的大小?
答案 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 启动代码。或者你必须自己寻找其他机制来解决这个问题。