url: https://www.gaio.co.jp/gaioclub/compiler_blog10/
title: "【第10回】スタックフレーム | ガイオ・テクノロジー ソフト検証ツール/モデルベース/エンジニアリングサービス プロバイダー"
description: "ガイオ・テクノロジー株式会社は、自動車開発の新しいトレンドに対応するソフトウェア開発・検証を徹底支援します。"
host: www.gaio.co.jp
favicon: https://www.gaio.co.jp/favicon.ico
ARM64 (AArch64)
https://www.gaio.co.jp/gaioclub/compiler_blog10/ の図をお借りして考えます。 図のように画像の上部が低アドレス 0x00000000, 画像の下部が高アドレス 0xFFFFFFFF と仮定します。
func1 関数内部から func2 関数を呼び出したとき、 新たに func2 のスタックフレームが確保されます。 具体的には func2 のスタックフレームのサイズを としたとき、 という操作がおこなわれます。
そして、下記のような領域が用意されます。
stack-protector カナリアはオプショナルです。
0x00000000(低アドレス)
||||----------||||
------------------ ← (sp: stack pointer)
(Margin: スタックフレームを 16byte 単位にするための余白)
------------------
**ローカル領域**
- ローカル変数
- 引数の一時的な保存
------------------ ← (x29: frame pointer の基準点)
StackCanary (Optional)
- バッファオーバーフローでメモリを書き換えられたか?をチェックする用
------------------
x29 (フレームポインタ)
-----------------
x30 (リターンアドレス)
-----------------
(Func1 の StackFrame)
-----------------
||||----------||||
0xFFFFFFFF(高アドレス)
example
(lldb) dis
main`add:
0x100000510 <+0>: sub sp, sp, #0x20
0x100000514 <+4>: stp x29, x30, [sp, #0x10]
0x100000518 <+8>: add x29, sp, #0x10
0x10000051c <+12>: stur w0, [x29, #-0x4]
0x100000520 <+16>: str w1, [sp, #0x8]
0x100000524 <+20>: adrp x8, 8
0x100000528 <+24>: add x8, x8, #0x0 ; globalW
0x10000052c <+28>: ldr w8, [x8]
0x100000530 <+32>: str w8, [sp, #0x4]
0x100000534 <+36>: ldur w0, [x29, #-0x4]
0x100000538 <+40>: bl 0x1000004f8 ; doubled at add.c:5
-> 0x10000053c <+44>: ldr w8, [sp, #0x4]
0x100000540 <+48>: add w8, w8, w0
0x100000544 <+52>: ldr w9, [sp, #0x8]
0x100000548 <+56>: add w0, w8, w9
0x10000054c <+60>: ldp x29, x30, [sp, #0x10]
0x100000550 <+64>: add sp, sp, #0x20
0x100000554 <+68>: ret
上記のようなアセンブリコードを考えます。 ここでは 0x20 = 32 byte のスタックフレームが確保されます。
このとき、以下のようなスタックフレームの構成になります。
(sp + 0x00) : 余白
(sp + 0x04) : 4 bytes -> globalW の値 を一時的に格納した場所 (str w8, [sp,#0x4])
(sp + 0x08) : 4 bytes -> w1 (y) を一時的に格納した場所 (str w1, [sp,#0x8])
(sp + 0x0c) : 4 bytes -> w0 (x) を一時的に格納した場所 (stur w0, [x29,#-0x4] => x29-4 = sp+0x0c)
(sp + 0x10) : 8 bytes -> saved x29 (old FP) (stp x29,x30,[sp,#0x10])
(sp + 0x18) : 8 bytes -> saved x30 (old LR / return address)
sp + 0x00 から sp + 0x03 が未使用になっています。
これは AArch64 では $sp が 16 バイトの倍数になる = 末尾 0 になることを期待しています。
今回のスタックフレーム必要サイズが 4 * 3 + 8 * 2 = 28 = 0x1c
ですが、 16 バイトの倍数でないため 0x20
になるよう 4byte 余白をスタックフレームに追加しているようです。
x86-64
x86-64 は AArch64 とはまったく構成や仕組みが異なります。