为什么此代码会出现分段错误? (它由Intel Assemble语法编码)
结果是这样的。
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
答案 0 :(得分:3)
如果删除代码' mov ebx,4',则删除了分段错误错误。 (但结果不符合我的意图)
EBX
寄存器几乎是所有x86调用约定中的callee-save。这意味着叶子函数不能破坏其值。如果需要使用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
还是序言......你的结局。