我用汇编语言编写了一个简单的程序,尝试在我的64位Ubuntu操作系统上运行它。但是,它因“分段故障(核心转储)错误”而失败。
这是我的代码:
.section .data
values :
.int 10, 15, 20, 25, 30 ,35, 40, 45, 50, 55, 60
output :
.asciz "The value is %d\n"
.section .text
.globl main
main :
nop
movl $0, %edi
loop :
movl values( , %edi, 4), %eax
pushq %rax
pushq $output
call printf
addl $8, %esp
inc %edi
cmpl $11, %edi
jne loop
movl $0, %ebx
movl $1, %eax
int $0x80
答案 0 :(得分:3)
您的代码中存在多个问题。
从64bit Ubuntu
和pushq %rax
我推断您正在尝试制作64位可执行文件。
如果是这样,那么......
下面:
pushq %rax
pushq $output
call printf
addl $8, %esp
您在函数调用后没有正确平衡堆栈。你还记得这是64位代码吗?您需要添加到rsp
,而不是esp
。此外,如果您按下2个8字节参数,则必须准确删除2个8字节参数,这意味着您必须添加16个而不是8个。
但情况甚至更糟。在64位模式下,参数的传递方式不同。第一个参数位于寄存器rdi
,rsi
,rdx
,rcx
,r8
和r9
中。所以,这给了我们:
movq %rax, %rsi
movq $output, %rdi
movq $0, %rax ; number of vector registers used for var-arg-function printf()
call printf
下面:
inc %edi
您刚刚通过调用销毁了rdi
的值,并使用此寄存器进行参数传递。您需要在通话前手动推送rdi
,然后将其弹出。或者您可以将其保存在全局变量中。如果您选择推送和弹出,请确保在任何rsp
指令之前,堆栈指针call
始终是16字节对齐。
下面:
movl $0, %ebx
movl $1, %eax
int $0x80
您正在使用32位系统调用接口。在64位程序中,您必须使用64位系统调用接口:
movq $60, %rax ; sys_exit
movq $0, rdi ; return 0 (success)
syscall
现在,我认为这个也可能有问题:
movl $0, %edi
loop :
movl values( , %edi, 4), %eax
通常,在64位代码中进行地址计算时,不应该使用32位寄存器和32位指令。我将其更改为:
movl $0, %rdi
loop :
movl values( , %rdi, 4), %eax
如果两者都不起作用,因为values
的地址距rip
的距离超过2GB(事实:在64-的大多数指令中,位移仅限于内存操作数中的32位有符号整数位模式,其中大部分都没有64位模式的仅位移内存操作数编码,它们使用rip
- 相对寻址,你需要手动添加{{1}的64位地址并将数组中的索引乘以4.确保在此过程中进行64位加法而没有任何截断。
必备读取:
System V应用程序二进制接口AMD64架构处理器补充草案版本0.99.6
调用不同C ++编译器和操作系统的约定作者:Agner Fog