2

上学期我在系统级编程课程中学习了 MIPS 汇编,现在一直在研究 Intel 和 AMD 架构。

我尝试在 GAS 中编写一个简单的 x86_64 程序调用 printf 并打印 argc 和 argv[0-4] 时遇到了麻烦。为了帮助我理解如何正确执行,我使用“gcc -S”查看 C 源文件“test.c”的汇编程序:

#include <stdio.h>
int main (int argc, char * argv[]) {
    printf("%d,%s,%s,%s,%s,%s\n", argc, argv[0], argv[1], argv[2], argv[3], argv[4]);
return 0;
}

“gcc -S -masm=intel test.c”的输出是:

    .file   "test.c"
    .intel_syntax noprefix
    .section    .rodata
.LC0:
    .string "%d,%s,%s,%s,%s,%s\n"
    .text
    .globl  main
    .type   main, @function
main:
.LFB0:
    .cfi_startproc
    push    rbp
    .cfi_def_cfa_offset 16
    .cfi_offset 6, -16
    mov rbp, rsp
    .cfi_def_cfa_register 6
    sub rsp, 32
    mov DWORD PTR [rbp-4], edi
    mov QWORD PTR [rbp-16], rsi
    mov rax, QWORD PTR [rbp-16]
    add rax, 32
    mov rsi, QWORD PTR [rax]
    mov rax, QWORD PTR [rbp-16]
    add rax, 24
    mov r8, QWORD PTR [rax]
    mov rax, QWORD PTR [rbp-16]
    add rax, 16
    mov rdi, QWORD PTR [rax]
    mov rax, QWORD PTR [rbp-16]
    add rax, 8
    mov rcx, QWORD PTR [rax]
    mov rax, QWORD PTR [rbp-16]
    mov rdx, QWORD PTR [rax]
    mov eax, DWORD PTR [rbp-4]
    mov QWORD PTR [rsp], rsi
    mov r9, r8
    mov r8, rdi
    mov esi, eax
    mov edi, OFFSET FLAT:.LC0
    mov eax, 0
    call    printf
    mov eax, 0
    leave
    .cfi_def_cfa 7, 8
    ret
    .cfi_endproc
.LFE0:
    .size   main, .-main
    .ident  "GCC: (GNU) 4.7.2 20121109 (Red Hat 4.7.2-8)"
    .section    .note.GNU-stack,"",@progbits

老实说,我不认为我完全理解第 18-38 行发生了什么。对我来说,看起来 gcc 在 [rbp-4] 和 [rbp-16] 处存储了指向 argc 和 argv[0] 的指针,然后将 [rbp-16] 作为基点加载到 rax(指向 argv[0] 的指针) ),并添加 8,16,24,... 以使 rax 指向 argv[1,2,3,...],然后将该地址加载到适当的寄存器中以传递给 printf。

通过这种解释,我能够充分理解命令行参数是如何传递给 main() 的,以便能够将我的 GAS 代码修复为:

.intel_syntax noprefix
.globl  main

.data
fmt:    .asciz  "%d,%s,%s,%s,%s,%s\n"

.text
main:
    push    rbp
    mov     rbp, rsp

    mov     rdx, QWORD PTR [rsi]
    mov     rcx, QWORD PTR [rsi+8]
    mov     r8, QWORD PTR [rsi+16]
    mov     r9, QWORD PTR [rsi+24]
    push    [rsi+32]
    mov     rsi, rdi
    mov     rdi, offset fmt
    xor     rax, rax
    call    printf

return:
    mov     rsp, rbp
    pop     rbp
    xor     rax, rax
    ret

这会产生与 test.c 以及 gcc 生成的 test.s 相同的输出。所以我的问题是......我这样做的方式有什么问题吗?如果不是,为什么 gcc 会生成如此复杂的方法来做如此简单的事情?也许这只是编译器解释数组使用的方式?

我想我的方法在技术上是正确的,因为它产生相同的输出,但我想确保它是一种“可接受的”方法。

4

0 回答 0