当程序启动(Linux ELF可执行文件)时,
在我的机器上,这些寄存器被清零,但当编写汇编程序时,在新进程中是否可以依靠这样的行为,我能否信赖?
eax
,ebx
等寄存器中是否有零,还是可以有其他值存在?在我的机器上,这些寄存器被清零,但当编写汇编程序时,在新进程中是否可以依靠这样的行为,我能否信赖?
eax
,ebx
等寄存器中是否有零,还是可以有其他值存在?这完全取决于每个平台的ABI。由于您提到了 eax
和 ebx
,让我们来看看对于 x86(截至 Linux v5.17.5)的情况。在 fs/binfmt_elf.c
中,load_elf_binary()
内核会检查ABI是否在程序加载时指定了任何寄存器值的要求:
/*
* The ABI may specify that certain registers be set up in special
* ways (on i386 %edx is the address of a DT_FINI function, for
* example. In addition, it may also specify (eg, PowerPC64 ELF)
* that the e_entry field is the address of the function descriptor
* for the startup routine, rather than the address of the startup
* routine itself. This macro performs whatever initialization to
* the regs structure is required as well as any relocations to the
* function descriptor entries when executing dynamically links apps.
*/
然后调用ELF_PLAT_INIT
,该宏在arch/xxx/include/elf.h
中为每种架构定义。对于x86架构,它执行以下操作:
#define ELF_PLAT_INIT(_r, load_addr) \
do { \
_r->bx = 0; _r->cx = 0; _r->dx = 0; \
_r->si = 0; _r->di = 0; _r->bp = 0; \
_r->ax = 0; \
} while (0)
当你的静态链接ELF二进制文件在Linux x86上加载时,可以指望所有寄存器值都等于零。但是这并不意味着你应该这样做。 :-)
请注意,执行动态链接的二进制文件实际上会在进程中运行动态链接器代码,然后才能到达你的_start
(ELF入口点)。正如ABI允许的那样,这可能会在寄存器中留下垃圾,除了堆栈指针ESP / RSP和atexit
hook EDX / RDX之外。
_start
之前运行,并且允许ABI在寄存器中留下垃圾。只有静态链接的可执行文件在执行到 _start
时才将其寄存器清零。 - Peter Cordesx86-64 System V ABI
3.4.1 "初始栈和寄存器状态" (Basile连接到此PDF版本):
%rsp
指向栈
堆栈指针保存最低地址的字节的地址,它是在进程入口时保证16字节对齐的
%rdx
是一个函数指针,如果非零应该由应用程序与 atexit 注册。
应用程序应该注册的函数指针。
%rbp
没有特定值,但用户空间应将其设置为基础帧。
该寄存器的内容在进程初始化时未指定,但用户代码应通过将帧指针设置为零来标记最深的堆栈帧。
其他一切都未定义。
因为LSB这样说,Linux遵循这个规范。
%rdx
允许为空,在当前Linux中,从静态链接的可执行文件启动新进程时,它为0
。但是,在执行动态链接的可执行文件时,动态链接器在_start
之前运行,因此它可以在%rdx
中设置一个值(根据gdb /bin/bash
)。我不确定_start
此时是否仍应将其视为函数指针,但可能是的,因为此时的其他所有内容都遵循ABI。 - Peter Cordes%rdx
将是一个指向运行动态链接库析构函数的函数指针。在动态可执行文件中,ld-linux.so将其传递给主可执行文件的_start
,以便它可以使用atexit
进行注册。ABI的进程入口保证在动态可执行文件中完全适用于_start
。(包括大多数寄存器实际上可以保存垃圾;这种情况与静态可执行文件不同,在静态可执行文件中,内核实际上会将寄存器清零。) - Peter Cordes