我正在尝试在这里学习一些装配,我需要专业人士的帮助!
test.s:
.data
helloworld:
.asciz "printf test! %i\n"
.text
.globl main
main:
push $0x40
push $helloworld
call printf
mov $0, %eax
ret
test.working.s:
.data
helloworld:
.asciz "printf test! %i\n"
.text
.globl main
main:
mov $0x40, %esi
mov $printf_test, %edi
mov $0x0, %eax
call printf
mov $0, %eax
ret
compile.sh:
rm test
gcc test.s -o test -lc
chmod 777 test
./test
test.s立即segfaults。我使用eclipse中的反汇编窗口编写了test.working.s,只是编写了一个小的C程序来使用printf打印一些东西。
所以,问题!
为什么test.s
不起作用
在我的C程序中,main被定义为main(int argc,char ** argv)不是吗?因此,如果我不需要这些论点,我不应该在开始时pop
两次吗?
在x86-64中,我读到%rax是64位寄存器,%eax是32位寄存器,%ax是16位寄存器。寄存器看起来像这样:
XX XX EE EE RR RR RR RR
(R = 4位RAX,E = 4位EAX,X = 4位AX)
在小端系统上(1表示为0x01000000,我认为......)?
GCC不会让我输入pop %eax
或push %eax
。它只允许我键入64位版本或16位版本。如何将RAX的32个EAX位推送到堆栈呢?我如何只弹出32位?
test.working.s(我想这会在1中回答,但如果不是......)通过更改寄存器调用printf,而不是通过将内容推送到堆栈。我认为这是因为它更快?你怎么知道何时这样做以及在调用c函数时的顺序是什么?
这也适用于Windows x86-64吗?我理解printf的操作可能有所不同,但是如果我在printf之后清理并恢复寄存器,我应该没问题呢?
您应该如何清理和恢复寄存器?根据{{3}},它说我必须保存%esp,%ebp,%esi,%edi“。这是指当我编写一个函数时,这些寄存器必须以它们进入的方式返回,或者我应该在调用函数之前自己保存它们。它可能是前者,因为%esp,但只是检查!
很明显我不需要x86-64,特别是因为我刚刚开始,所以如何改变compile.sh只需x86?
.asciz
只是表示.ascii
+ "\0"
吗?
我可以返回驻留在C堆栈中的大型结构(> 64位)。如何在汇编中实现?
为任何帮助干杯!
答案 0 :(得分:3)
因为在64位模式下,您应该在寄存器中而不是在堆栈中传递参数(请参阅this answer)。即使不是这种情况,你也没有push $0x40
的大小说明符,所以你很可能只是推动一个16位而不是32位。
堆栈顶部将包含发送给main
的地方的返回地址(例如__libc_start_main
)。您可以在下方找到argc
和argv
。你不需要弹出任何一个(你不应该弹出任何一个,因为你需要保留返回地址)。
32位值1将写为0x00000001(左侧最重要的nybble),并以小端配置存储为(low address) 01 00 00 00 (high address)
。由于通常首先编写具有最高有效数字的数字而不是根据它们的存储方式编写数字,因此将RAX
描述编写为RR RR RR RR EE EE XX XX
是有意义的,如果不清楚则可能使用位索引标记订单是什么。
同样,这是64位x86代码的调用约定,如this answer中所述。
不是没有一些更改,因为Windows is slightly different使用的64位调用约定(用于传递参数的寄存器是RCX, RDX, R8, R9
)。
例如,将它们保存在堆栈中。有被调用者保存的寄存器和调用者保存的寄存器。
被调用者(被调用的函数)必须保存某些寄存器并在返回之前恢复它们以符合呼召惯例。对于Linux类型系统上的64位程序RBX, RBP, R12-R15
(在64位Windows上,这还包括RSI
和RDI
)。
调用者(调用函数的代码)必须考虑某些易失性寄存器(即可以通过函数改变),并且如果在函数返回后需要它们的值,则应该保存和恢复它们。在Linux类型的系统上,这些将是RAX, RCX, RDX, RSI, RDI, R8-R11
。
GNU汇编器应支持-m32
命令行选项,以指定您正在组装32位代码。
是