x64:为什么这段代码会给我“地址边界错误”

时间:2017-04-10 17:49:49

标签: macos assembly 64-bit x86-64 att

为什么以下x64程序集会给我“地址边界错误”?只有在call _print_string之后添加代码时才会发生这种情况。我假设某些寄存器已经被修改但是在_print_string函数返回后它们不应该被还原吗?

我使用的是Mac OS X

obj_size = 8

.data
    hello_world: .asciz "hello world!"

.text
    .globl _main


_main:

    pushq %rbp
    movq %rsp, %rbp
    leaq hello_world(%rip), %rdi
    callq _print_string

    subq obj_size, %rsp
    movq 1, %rax
    movq %rax, obj_size(%rsp)

    addq obj_size, %rsp


    leave
    ret

C程序是:

void
print_string(char *str) 
{
    printf("%s\n", str);
}

1 个答案:

答案 0 :(得分:3)

此代码的问题非常简单。在使用AT& T语法的GNU汇编程序中 -  用作立即操作数的文字常量需要以$(美元符号)作为前缀,否则该常量将被视为内存操作数。

这些行都有这个问题:

subq obj_size, %rsp
movq 1, %rax
[snip]
addq obj_size, %rsp

在这些情况下,因为您希望使用常量obj_size1作为值(立即操作数)而不是内存引用。上面的说明应该是:

subq $obj_size, %rsp
movq $1, %rax
[snip]
addq $obj_size, %rsp

subq obj_size, %rsp尝试从 RSP 中的值中减去内存地址0x8处的64位值。 movq 1, %rax尝试将内存地址0x1的64位值移动到 RAX 。您的程序出现故障,因为OS / X上的内存位置无法读取。

关于AT& T语法和英特尔语法之间差异的好文章可以在IBM's website找到。特别是他们列出了这种差异:

  

在AT& T语法中,立即操作数前面是$;在Intel语法中,立即操作数不是。例如:英特尔:push 4,AT& T:pushl $4

要缩小像这样的问题,使用调试器通常是有益的。在OS / X上,如果您不使用Xcode,可以从命令行使用调试器 LLDB tutorial on using LLDB可能有用。在这种情况下,您可以将 LLDB 作为lldb ./nameofprogram运行,然后使用run命令允许它继续运行直至失败。然后调试器会向您显示崩溃发生的汇编指令。

如果您想知道64位OS / X代码Apple defines it this way使用的调用约定:

  

OS X x86-64函数调用约定与System V Application Binary Interface AMD64 Architecture Processor Supplement中描述的函数调用约定相同。

您可以找到System V Application Binary Interface AMD64 Architecture Processor Supplement here。可以在图3.4:寄存器使用

中找到调用者和被调用者保存寄存器的列表。