我想知道除了EBP之外,保存公共寄存器(EAX,EBX等)的最佳方法是什么。 这是我的示例代码。它juste调用我的write函数,在参数中传递字符串地址和长度(参数放在堆栈中)。 在write函数中,我保存EBP,给它新值,取回参数并调用write syscall。
.section .data
ask_number_str:
.ascii "Hello world"
anstr_end:
.set ANSTR_SIZE, anstr_end - ask_number_str
.section .bss
.section .text
.globl main
main:
movl $42, %eax
movl $123, %ebx
movl $456, %ecx
movl $789, %edx
pushl $ANSTR_SIZE
pushl $ask_number_str
call write
add $8, %esp
exit:
movl $1, %eax
movl $0, %ebx
int $0x80
write:
pushl %ebp
movl %esp, %ebp
movl $4, %eax
movl $1, %ebx
movl 8(%ebp), %ecx
movl 12(%ebp), %edx
int $0x80
popl %ebp
ret
我想在函数调用中添加EAX,EBX,ECX和EDX的保存。我看到了三种保存公共寄存器的方法:
首先,在“mov%esp,%ebp”之前(在为ebp分配新地址之前)将它们保存在写入功能中。 =>问题:ESP,因此EBP将在公共寄存器值之后指向堆栈,因此我将不得不在堆栈中更深入地找到参数(我将不得不在之前通过EAX,EBX,ECX,EDX的值)找到参数)
其次,在“mov%esp,%ebp”之后将它们保存在write函数中。 =>问题:ESP,所以EBP将在公共寄存器值之前指向堆栈,因此我将不得不在堆栈中继续使用局部变量(如果我保存EAX,EBX,我将不得不传递4个值) ECX和EDX,我的本地变量的开头地址)。
第三,在函数调用之前(以及在推送参数之前)将它们保存在调用函数(这里是main)中。没有堆栈的问题,但我觉得这不是很优雅,我需要为每个呼叫添加8行(在通话前4次按下,4次弹出后)。
那么,什么是最好的方法?当然是另一种我没想到的方法:)
感谢您的帮助!
答案 0 :(得分:2)
如果您决定使用帧指针,通常要做的是在设置ebp
后保存它们。您保存的寄存器与任何本地变量没有区别,如果您愿意,您甚至可以使用mov
指令而不是push
/ pop
来访问它们,如果您愿意,可以将它们置于其他变量之下不想使用更大的抵消。
请注意,通常不需要帧指针,因为您可以相对于esp
进行寻址。此外,常见的通话约定通常允许修改eax
,ecx
和edx
,因此如果您坚持这一点,则需要保存ebx
。
答案 1 :(得分:0)
如果您不确定寄存器'情况,这些说明可以轻松地节省一天。
PUSHA/PUSHAD -- Push all General Registers
POPA/POPAD -- Pop all General Registers
这些指令按一定顺序推送和弹出通用和SI / ESI,DI / EDI寄存器。
PUSHA / PUSHAD指令的顺序如下。
Opcode Instruction Clocks Description
60 PUSHA 18 Push AX, CX, DX, BX, original SP, BP, SI, and DI
60 PUSHAD 18 Push EAX, ECX, EDX, EBX, original ESP, EBP ESI, and EDI
POPA / POPAD指令的顺序如下。 (按相反顺序)
Opcode Instruction Clocks Description
61 POPA 24 Pop DI, SI, BP, SP, BX, DX, CX, and AX
61 POPAD 24 Pop EDI, ESI, EBP, ESP(***),EBX, EDX, ECX, and EAX
*** ESP值被丢弃而不是加载到ESP中。