x64:为什么这段代码给我“地址边界错误"? [英] x64: Why does this piece of code give me "Address boundary error"
问题描述
为什么下面的x64程序集给我地址边界错误"?仅当我在call _print_string
之后添加代码时才会发生.我假设某些寄存器已被修改,但是一旦_print_string
函数返回,就不应该将它们还原吗?
Why does the following x64 assembly give me "Address boundary error"? It only happens when I add code after call _print_string
. I assume that some of the register have been modified but aren't they supposed to be reverted once the _print_string
function 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汇编器中-
用作立即操作数的文字常量必须以$
(美元符号)作为前缀,否则该常量将被视为内存操作数.
The issue with this code is pretty simple. In GNU Assembler using AT&T syntax -
literal constants that are used as an immediate operand need to be prefixed with a $
(dollar sign) otherwise the constant is treated as a memory operand.
这些行都有这个问题:
subq obj_size, %rsp
movq 1, %rax
[snip]
addq obj_size, %rsp
在这些情况下,因为要使用常量obj_size
和1
作为值(立即操作数)而不是内存引用.上面的说明应该是:
In these cases since you want to use the constants obj_size
and 1
as a value (immediate operand) and not a memory reference. The instructions above should have been:
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上的那些内存位置.
subq obj_size, %rsp
attempted to subtract the 64-bit value at memory address 0x8 from the value in RSP. movq 1, %rax
attempted to move the 64-bit value at memory address 0x1 into RAX. Your program faulted since those memory locations on OS/X can't be read from.
A good article on the difference between AT&T syntax and Intel syntax can be found on IBM's website. In particular they have this difference listed:
在AT& T语法中,立即数操作数前面带有$;在Intel语法中,立即数不是.例如:Intel:
push 4
,AT& T:pushl $4
In AT&T syntax, immediate operands are preceded by $; in Intel syntax, immediate operands are not. For example: Intel:
push 4
, AT&T:pushl $4
要缩小此类问题的范围,使用调试器通常是有益的.在OS/X上,如果您不使用Xcode,则可以从命令行使用调试器 LLDB . 使用 LLDB 的教程可能会有用.在这种情况下,您可以将 LLDB 作为lldb ./nameofprogram
运行,然后使用run
命令允许它继续运行直到失败.然后,调试器将向您显示崩溃发生的汇编指令.
To narrow down problems like these it is often beneficial to use a debugger. On OS/X if you are not using Xcode you can use the debugger LLDB from the command line. A tutorial on using LLDB may be useful. In this case you could have run LLDB as lldb ./nameofprogram
and then used the run
command to allow it to continue until it failed. The debugger would have then shown you what assembly instruction the crash occurred at.
If you want to know the calling convention used by 64-bit OS/X code Apple defines it this way:
OS X x86-64函数调用约定与System V应用程序二进制接口AMD64体系结构处理器增补中描述的函数调用约定相同.
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体系结构处理器补编此处.呼叫者和被呼叫者保存的寄存器列表可在图3.4:寄存器用法
You can find the System V Application Binary Interface AMD64 Architecture Processor Supplement here. The list of caller and callee saved registers can be found in Figure 3.4: Register Usage
这篇关于x64:为什么这段代码给我“地址边界错误"?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!