ASM-x86
ARM64の方を先にやってしまった… と言うことでx86_64ですわ x86_64向けもRosetta 2でどうにかやるのでちょっと本物とは誤差があるかもしれないと言う部分だけ許してください。
#include <stdio.h>
// 引数が 8 個ある関数
int add_all(int a, int b, int c, int d, int e, int f, int g, int h) {
int sum = a + b + c + d + e + f + g + h;
return sum; // ここにブレークポイントを置く
}
int main(void) {
int result = add_all(1, 2, 3, 4, 5, 6, 7, 8);
printf("Result: %d\n", result);
return 0;
}
このコードを最適化無視して、デバッグ情報を付与しつつx86_64向けにコンパイル この方法だとAT&T記法になる。 ちなみにintel記法とAT&T記法がある。 書き方に少し違いがある。
$ clang -g -O0 -arch x86_64 demo.c -o demo
$ lldb demo
(lldb) b add_all
(lldb) r
(lldb) disassemble
ブレークポイントを設定して、走らせ、そこからdisassemble AT&T記法で以下の通り
demo`add_all:
0x100000470 <+0>: pushq %rbp
0x100000471 <+1>: movq %rsp, %rbp
0x100000474 <+4>: movl 0x18(%rbp), %eax
0x100000477 <+7>: movl 0x10(%rbp), %eax
0x10000047a <+10>: movl %edi, -0x4(%rbp)
0x10000047d <+13>: movl %esi, -0x8(%rbp)
0x100000480 <+16>: movl %edx, -0xc(%rbp)
0x100000483 <+19>: movl %ecx, -0x10(%rbp)
0x100000486 <+22>: movl %r8d, -0x14(%rbp)
0x10000048a <+26>: movl %r9d, -0x18(%rbp)
-> 0x10000048e <+30>: movl -0x4(%rbp), %eax
0x100000491 <+33>: addl -0x8(%rbp), %eax
0x100000494 <+36>: addl -0xc(%rbp), %eax
0x100000497 <+39>: addl -0x10(%rbp), %eax
0x10000049a <+42>: addl -0x14(%rbp), %eax
0x10000049d <+45>: addl -0x18(%rbp), %eax
0x1000004a0 <+48>: addl 0x10(%rbp), %eax
0x1000004a3 <+51>: addl 0x18(%rbp), %eax
0x1000004a6 <+54>: movl %eax, -0x1c(%rbp)
0x1000004a9 <+57>: movl -0x1c(%rbp), %eax
0x1000004ac <+60>: popq %rbp
0x1000004ad <+61>: retq
新しい基準
pushq %rbp
pushq(Push Quadword)はスタックにデーターを積みます。
最後のqは8B(64bit)を扱うという合図。
x86_64ではOSがアプリを立ち上げたタイミングで%rspに対してsp(スタックポインタ)の先頭アドレスを保存する。
なのでmain関数のプロセスのspの先頭アドレスはrspですが、add_all関数のスタック領域の計算のために新しく仮のspの先頭アドレスであるrbpを作る。
add_all関数のスタック領域はこれを基準にoffsetで計算を進める。
movq %rsp, %rbp
movq(Move Quadword)はデーターをコピー。 moveq [コピー元], [コピー先] rbpは領域の確保のタイミングで動かすので基準をrspにコピーさせておく。