2017-04-10 91 views
1

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

我使用的是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

哪些操作系统?一些寄存器不被保留,并且保存哪些不同于OS到OS。 –

+0

@RudyVelthuis我使用的是Mac OS X –

回答

3

问题与此代码非常简单。在GNU汇编程序中使用AT & T语法 - 用作立即数操作数的立即数常量需要以$(美元符号)作为前缀,否则常量将被视为内存操作数。因为你要使用的常量obj_size1作为一个值(立即数),而不是内存引用

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

在这些情况下:

这些线路都有这个问题。上述说明应该是:

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语法和Intel语法之间差异的好文章可以在IBM's website上找到。特别是他们有以下差异:

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


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


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

OS X的x86-64的函数调用约定是相同的调用约定的函数描述System V应用程序二进制接口AMD64体系结构处理器补充。

您可以找到系统V应用程序二进制接口AMD64架构处理器补充here。主叫方和被叫方的列表保存的寄存器可以图3.4中找到:注册使用