0

我正在尝试在这里学习一些组装,我需要专业人士的帮助!

测试.s:

.data
helloworld:
    .asciz "printf test! %i\n"
.text
.globl main
main:
    push $0x40
    push $helloworld
    call printf
    mov $0, %eax
    ret

测试.工作.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

编译.sh:

rm test
gcc test.s -o test -lc
chmod 777 test
./test

test.s 立即出现段错误。我通过使用 Eclipse 中的反汇编窗口制作了 test.working.s,并编写了一个小的 C 程序来使用 printf 打印一些东西。

所以,问题!

  1. 为什么test.s不起作用

  2. 在我的 C 程序中,main 被定义为 main(int argc, char ** argv) 不是吗?因此,pop如果我不需要这些论点,我不应该在开始时需要两次吗?

  3. 在 x86-64 中,我在某处读到 %rax 是 64 位寄存器,%eax 是 32 位寄存器,而 %ax 是 16 位寄存器。那么寄存器看起来像这样: XX XX EE EE RR RR RR RR(R = RAX 的 4 位,E = EAX 的 4 位,X = AX 的 4 位)在 little-endian 系统上(1 表示为 0x01000000,我认为......)?

  4. GCC 不会让我输入pop %eaxor push %eax。它只会让我输入 64 位版本或 16 位版本。那么如何将 RAX 的 32 个 EAX 位推入堆栈呢?如何仅弹出 32 位?

  5. test.working.s (我想这在 1 中得到了回答,但如果不是......)通过更改寄存器调用 printf,而不是通过将东西压入堆栈。我想这是因为它更快?在调用 c 函数时,您如何知道何时执行此操作以及按什么顺序执行此操作?

  6. 这也适用于 Windows x86-64 吗?我知道printf的操作可能会有所不同,但是如果我在printf之后清理并恢复寄存器,我应该可以吧?

  7. 你应该如何清理和恢复寄存器?根据http://www.cs.uaf.edu/2005/fall/cs301/support/x86/,它说我“必须保存 %esp, %ebp, %esi, %edi”。那是指当我编写一个函数时,这些寄存器必须以它们进入的方式返回,或者我应该在调用函数之前自己保存它们。可能是前者,因为 %esp,但只是检查!

  8. 很明显,我不需要 x86-64,尤其是因为我刚刚开始,那么我该如何更改 compile.sh 只为 x86 呢?

  9. .asciz仅仅意味着.ascii+吗"\0"

  10. 我可以返回驻留在 C 中的堆栈上的大型结构(>64 位)。这是如何在汇编中实现的?

为任何帮助干杯!

4

1 回答 1

3
  1. 因为在 64 位模式下,您应该在寄存器中而不是在堆栈中传递参数(请参阅此答案)。即使不是这种情况,您也没有大小说明符push $0x40,因此很可能您只推送 16 位值而不是 32 位。

  2. 堆栈的顶部将包含返回地址到调用main来自的任何地方(例如__libc_start_main)。在此之下,您会发现argcargv。你不需要弹出任何一个(你不应该弹出任何一个,因为你需要保留返回地址)。

  3. 32 位值 1 将被写为 0x00000001(左侧最重要的 nybble),并将(low address) 01 00 00 00 (high address)以小端配置存储。由于通常首先使用最高有效数字而不是根据它们的存储方式编写数字,因此将您的RAX描述写为是有意义的RR RR RR RR EE EE XX XX,如果不清楚顺序是什么,可能使用位索引标记。


  4. 同样,这是 64 位 x86 代码的调用约定,如本答案中所述。

  5. 并非没有一些变化,因为 Windows 使用的 64 位调用约定略有不同(用于传递参数的寄存器是RCX, RDX, R8, R9)。

  6. 例如,通过将它们保存在堆栈上。有被调用者保存的寄存器和调用者保存的寄存器。
    被调用者(被调用的函数)必须保存某些寄存器并在返回之前恢复它们,以符合调用约定。对于 Linux 类型系统上的 64 位程序,将是RBX, RBP, R12-R15(在 64 位 Windows 上,这还包括RSIRDI)。
    调用者(调用函数的代码)必须考虑某些寄存器是易失的(即可以被函数更改),如果在函数返回后需要它们的值,则应该保存和恢复它们。在 Linux 类型的系统上,这些将是RAX, RCX, RDX, RSI, RDI, R8-R11.

  7. GNU 汇编器应该支持一个-m32命令行选项来指定您正在汇编 32 位代码。

  8. 是的。

于 2013-05-14T06:13:02.917 回答