【问题标题】:nasm segmentation fault while using arg使用 arg 时出现 nasm 分段错误
【发布时间】:2016-11-16 10:14:22
【问题描述】:
extern puts
global main
section .text
main:
mov rax, rdi
label:
test rax, rax
je exit
push rsi
mov rdi, [rsi]
call puts
pop rsi
dec rax
add rsi, 8
jmp label
exit:
pop rsi
ret
我写了这样的 nasm 代码。然而,分段错误发生在最后。我不明白为什么会发生分段错误。
【问题讨论】:
标签:
linux
gcc
assembly
nasm
x86-64
【解决方案1】:
rax 不能保证在函数调用中保留,因为它用于从函数返回整数结果(在puts 的情况下“成功时为非负数,错误时为 EOF”) 你需要在调用puts 之前保存rax 的值,就像你对rsi 所做的那样,然后再恢复它。
【解决方案2】:
显然,您希望在 64 位 Linux 上的 GCC 环境中获取命令行参数,它们是根据遵循 Linux 调用约定“System V AMD64 ABI”的 GCC 调用约定传递的。
让我们把程序逻辑翻译成C:
#include <stdio.h>
int main ( int argc, char** argv )
{
if (argc != 0)
{
do
{
puts (*argv);
argc--;
argv++;
} while (argc);
}
return;
}
asm 程序不返回退出代码。当函数返回时,该退出代码应该在RAX 中。顺便说一句:argc 始终 >0,因为argv 的第一个字符串包含程序名称。
main 函数既是“调用者”(调用 puts)又是“被调用者”(返回 GCC 环境)。作为调用者,它必须在调用puts 之前保留RAX 和RSI,并在需要时恢复它们。不使用被调用者保存的寄存器。不要忘记将堆栈对齐 16。
这行得通:
extern puts
global main
section .text
main: ; RDI: argc, RSI: argv, stack is unaligned by 8
mov rax, rdi
label:
test rax, rax
je exit
push rbx ; Push 8 bytes to align the stack before the call
push rax ; Save it (caller-saved)
push rsi ; Save it (caller-saved)
mov rdi, [rsi] ; Argument for puts
call puts
pop rsi ; Restore it
pop rax ; Restore it
pop rbx ; "Unalign" the stack
dec rax
add rsi, 8
jmp label
exit:
; pop rsi ; Once too much
xor eax, eax ; RAX = 0 (return 0)
ret ; RAX: return value