私は前学期にシステムレベルのプログラミングコースで 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 はこれほど単純なことを行うのにこれほど複雑な方法を生成するのでしょうか? たぶん、コンパイラが配列の使用を解釈する方法にすぎないのでしょうか?
同じ出力が生成されるため、私の方法は技術的に正しいと思いますが、それが「受け入れられる」方法であることを確認したいと思います。