2

私は前学期にシステムレベルのプログラミングコースで MIPS アセンブリを学び、現在 Intel と AMD のアーキテクチャを調べています。

printf を呼び出して argc と argv[0-4] を出力する単純な x86_64 プログラムを GAS で作成しようとすると、問題が発生しました。正しく行う方法を理解するために、「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 が argc と argv[0] へのポインターを [rbp-4] と [rbp-16] に格納し、[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

これにより、gcc で生成された test.s と同様に、test.c と同じ出力が生成されます。だから私の質問はこれです....私がやった方法に何か問題はありますか? そうでない場合、なぜ gcc はこれほど単純なことを行うのにこれほど複雑な方法を生成するのでしょうか? たぶん、コンパイラが配列の使用を解釈する方法にすぎないのでしょうか?

同じ出力が生成されるため、私の方法は技術的に正しいと思いますが、それが「受け入れられる」方法であることを確認したいと思います。

4

0 に答える 0