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

Posted

技术标签:

【中文标题】x64:为啥这段代码给我“地址边界错误”【英文标题】:x64: Why does this piece of code give me "Address boundary error"x64:为什么这段代码给我“地址边界错误” 【发布时间】:2017-04-10 17:49:49 【问题描述】:

为什么下面的 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);

【问题讨论】:

哪个操作系统?有些寄存器没有保留,而保留的寄存器因操作系统而异。 @RudyVelthuis 我正在使用 Mac OS X 【参考方案1】:

这段代码的问题很简单。在使用 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 上的这些内存位置。

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

在 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 应用程序二进制接口 AMD64 架构处理器补充here。调用者和被调用者保存的寄存器列表可以在图 3.4:寄存器使用

中找到

【讨论】:

以上是关于x64:为啥这段代码给我“地址边界错误”的主要内容,如果未能解决你的问题,请参考以下文章

为啥我的球拍程序没有给我任何输出?

为啥我在此代码中收到 SIGABRT 错误 [关闭]

为啥释放内存会导致分段错误?

shmget 如何分配内存?无法使用线性寻址访问(地址边界错误)

“import tensorflow”由信号 SIGSEGV 终止(地址边界错误)

为啥数据给我null?