x64:为什么这段代码给我 "Address boundary error"
x64: Why does this piece of code give me "Address boundary error"
为什么下面的 x64 程序集给我 "Address boundary error"?只有当我在 call _print_string
之后添加代码时才会发生这种情况。我假设某些寄存器已被修改,但一旦 _print_string
函数 returns?
,它们不应该被恢复吗?
我正在使用 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);
}
这段代码的问题很简单。在使用 AT&T 语法的 GNU 汇编程序中 -
用作立即操作数的文字常量需要以 $
(美元符号)为前缀,否则该常量将被视为内存操作数。
这些行都有这个问题:
subq obj_size, %rsp
movq 1, %rax
[snip]
addq obj_size, %rsp
在这些情况下,因为您想使用常量 obj_size
和 1
作为值(立即操作数)而不是内存引用。上面的说明应该是:
subq $obj_size, %rsp
movq , %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 语法之间区别的好文章。特别是他们列出了这种差异:
In AT&T syntax, immediate operands are preceded by $; in Intel syntax, immediate operands are not. For example: Intel: push 4
, AT&T: pushl
要缩小此类问题的范围,使用调试器通常是有益的。在 OS/X 上,如果您不使用 Xcode,则可以从命令行使用调试器 LLDB。 tutorial on using LLDB 可能会有用。在这种情况下,您可以将 运行 LLDB 设置为 lldb ./nameofprogram
,然后使用 run
命令使其继续运行直到失败。然后调试器会告诉你崩溃发生在什么汇编指令上。
如果你想知道64位OS/X代码使用的调用约定Apple defines it this way:
The OS X x86-64 function calling conventions are the same as the function calling conventions described in System V Application Binary Interface AMD64 Architecture Processor Supplement.
您可以找到 System V 应用程序二进制接口 AMD64 架构处理器补充 here。可以在图 3.4:寄存器使用
中找到调用者和被调用者保存的寄存器列表
为什么下面的 x64 程序集给我 "Address boundary error"?只有当我在 call _print_string
之后添加代码时才会发生这种情况。我假设某些寄存器已被修改,但一旦 _print_string
函数 returns?
我正在使用 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);
}
这段代码的问题很简单。在使用 AT&T 语法的 GNU 汇编程序中 -
用作立即操作数的文字常量需要以 $
(美元符号)为前缀,否则该常量将被视为内存操作数。
这些行都有这个问题:
subq obj_size, %rsp
movq 1, %rax
[snip]
addq obj_size, %rsp
在这些情况下,因为您想使用常量 obj_size
和 1
作为值(立即操作数)而不是内存引用。上面的说明应该是:
subq $obj_size, %rsp
movq , %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 语法之间区别的好文章。特别是他们列出了这种差异:
In AT&T syntax, immediate operands are preceded by $; in Intel syntax, immediate operands are not. For example: Intel:
push 4
, AT&T:pushl
要缩小此类问题的范围,使用调试器通常是有益的。在 OS/X 上,如果您不使用 Xcode,则可以从命令行使用调试器 LLDB。 tutorial on using LLDB 可能会有用。在这种情况下,您可以将 运行 LLDB 设置为 lldb ./nameofprogram
,然后使用 run
命令使其继续运行直到失败。然后调试器会告诉你崩溃发生在什么汇编指令上。
如果你想知道64位OS/X代码使用的调用约定Apple defines it this way:
The OS X x86-64 function calling conventions are the same as the function calling conventions described in System V Application Binary Interface AMD64 Architecture Processor Supplement.
您可以找到 System V 应用程序二进制接口 AMD64 架构处理器补充 here。可以在图 3.4:寄存器使用
中找到调用者和被调用者保存的寄存器列表