如果需要在程序启动时运行一段代码(在Linux上),如何正确使用可执行文件(ELF32-i386)的.init_section?我有以下代码(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似乎只是针对这种情况设计的:
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中的函数指针,并调用它们.它适用于一个文件,其中一个文件可以标记该部分的结尾,例如,为零.但是对于多个文件,这个零可以在该部分的中间重新定位.如何正确确定从多个源文件组装的部分的大小?
解决方法:
如果你按照自己的方式创建一个静态链接的裸可执行文件,在_start入口点使用你自己的代码,你的代码就会从那一点开始运行.如果您想要实现某些目标,您的代码必须实现.没有魔力.
使用部分可以将来自多个源文件的启动代码组合在一起,因此所有启动代码都很冷,可能会被分页,或者至少不需要TLB条目.
所以你通过在那里放置函数并在_start之后的某个时间运行的代码中调用它来“正确使用”部分.
在您的代码示例中,它看起来像.init_array是一个函数指针列表.我假设标准的CRT启动文件读取ELF文件并找到该部分的长度,然后遍历它,间接调用这些函数.由于您正在制作自定义代码,因此调用执行所有操作的init函数会更快.
动态链接:
“运行时链接程序”是动态二进制文件的ELF解释器.它在_start之前在你的进程中运行代码,所以是的,显然它确实处理了ELF部分并使魔术发生.
因此,为了响应您的编辑,您的选择是:自己实现.init_array的此处理,或创建动态可执行文件.我很确定这个过程已经包含在其他问题中了,我没有时间研究一个仍然没有链接libc的动态可执行文件的正确命令行. (虽然你可能只想使用gcc -nostartfiles,或者其他东西.)
如果你遇到困难,请发表评论.我可以稍后在有更多时间时更新它,或者随意编辑工作命令.