2

我用汇编语言编写了一个简单的程序,试图在我的 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
4

1 回答 1

3

您的代码中有多个问题。

从我推断您正在尝试制作 64 位可执行文件64bit Ubuntupushq %rax

如果是这样的话,那么...

这里:

pushq %rax
pushq $output
call printf
addl $8, %esp

函数调用后您没有正确平衡堆栈。你还记得这是 64 位代码吗?您需要添加到rsp,而不是esp。此外,如果您推送 2 个 8 字节参数,则必须准确删除 2 个 8 字节参数,这意味着您必须添加 16 个,而不是 8 个。

但它甚至比这更糟糕。在 64 位模式下,参数的传递方式不同。第一个参数位于寄存器rdirsirdxrcxr8r9。所以,这给了我们:

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之前始终是 16 字节对齐的。call

这里:

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距离超过 2GB rip(事实:在 64 位模式下的大多数指令中,位移仅限于内存操作数中的 32 位有符号整数,并且它们中的大多数没有仅位移的内存操作数也可以在 64 位模式下编码,它们在那里使用rip-relative 寻址),您需要手动将 64 位地址values和索引添加到数组中乘以 4。确保执行 64 位加法,没有任何截断一路上。

必读:

  • System V 应用二进制接口 AMD64 架构处理器补充草案版本 0.99.6

  • 不同 C++ 编译器和操作系统的调用约定 作者:Agner Fog

于 2012-07-14T05:03:18.770 回答