为什么这段代码会给出分段错误? (它由英特尔汇编语法编码(
结果是这样的。
return value : 80
[Dump]
eax : 0x00000012
ebx : 0x00000004
ecx : 0x00000400
edx : 0x4010a980
Segmentation fault (core dumped)
我想我有足够的代码来防止分段错误。 每个函数都有序幕和尾声来维护堆栈内存。
但是,它会发生分段错误错误。
[补充说明] 如果我删除代码"mov ebx,4",则分段错误已删除。(但结果不符合我的意图(
extern printf
segment .data
dumpmsg db 10,10,10,10,10,10,'[Dump]',10,'eax : 0x%0.8x',10,'ebx : 0x%0.8x',10,'ecx : 0x%0.8x',10,'edx : 0x%0.8x',10,00
msg db 'return value : %d', 10, 00
segment .bss
segment .text
global main
main:
push ebp
mov ebp, esp
push 20
call pig
add esp, 4
push eax
push msg
call printf
call print_x
leave
ret
pig:
push ebp
mov ebp, esp
mov eax, [ebp+8]
mov ebx, 4
mul ebx
leave
ret
print_x:
push ebp
mov ebp, esp
push edx
push ecx
push ebx
push eax
push dumpmsg
call printf
leave
ret
如果我删除代码"mov ebx,4",则分段错误错误已被删除。(但结果不符合我的意图(
EBX
寄存器在几乎所有 x86 调用约定中都是被调用方保存的。这意味着叶函数不能破坏其值。如果需要使用EBX
,则必须将其原始值保存在函数的顶部,并在末尾恢复。这通常通过PUSH
+POP
指令来完成,以将寄存器的值保存在堆栈上。
您的pig
函数正在破坏EBX
与mov ebx, 4
指令,但它没有注意保存和恢复其原始值。这违反了调用约定,这很重要,因为您正在与 C 代码进行互操作。
修复很简单:
pig:
push ebp
mov ebp, esp
push ebx
mov eax, [ebp+12]
mov ebx, 4
mul ebx
pop ebx
leave
ret
或者,只需使用ECX
寄存器来保存除数,因为这个是调用者保存的(因此可以自由删除(:
pig:
push ebp
mov ebp, esp
mov eax, [ebp+12]
mov ecx, 4
mul ecx
leave
ret
每个函数都有序幕和尾声来维护堆栈内存。
这真的没有必要。pig
函数除了检索参数外根本不使用堆栈,因此您可以简单地将其编写为:
pig:
mov eax, [esp+4]
mov ecx, 4
mul ecx
ret
基指针(EBP
(的使用也可以从其他函数中省略,即使是那些使用堆栈的函数,也可以直接使用堆栈指针(ESP
(。这与编译器将进行的优化相同,以释放EBP
作为附加寄存器并保存序言/尾声膨胀。但是,如果您更愿意这样做,那么除了性能之外,它不会受到任何伤害。
重要的是您遵循调用约定。EAX
、EDX
和ECX
寄存器可以在叶函数内自由切换;其余所有内容都需要显式保存并恢复到其原始值。在堆栈上分配空间时,需要确保释放它。你这样做的方式是灵活的,无论是sub esp, xx
...add esp, xx
还是序幕...尾声你有。