アセンブリ言語の教科書 第3章 Hello, GAS world!!
サンプルに少し改良を加えました。
.text .global main main: push %ebp mov %esp,%ebp push $hello call printf mov $0, %eax leave ret .data hello: .string "Hello, GAS World!!"
一見何の変哲もないHello,Worldですが、かなり罠が隠れています。1行づつ罠を解いていきます。
復習ついでに、ベースポインタ入門をしてみる。
ボクノス的ベースポインタ入門
--------------------------------------------- | | | | | | | |呼び出し元のアドレス| --------------------------------------------- ^%esp ^%ebp
最初はこの状態。スタックは右から左へ積んでいきます。スタックには、呼び出し元のアドレスが入ってます。アドレスを間違えるととんでもない所に飛んでいくので注意しましょう。
push %ebpで、呼び出し元のベースポインタを積む。
---------------------------------------------------------------- | | | | | | |呼び出し元のベースポインタ|呼び出し元のアドレス| ----------------------------------------------------------------- ^%esp ^%ebp←コレを積む
ベースポインタ%ebpは、スタックの底を表す。関数呼び出し直後に定義し、元の関数へ確実に戻れる足がかりを作る。その他に、変数定義や、バックトレース、フレーム処理等にも使われる。
現在の%espを%ebpに代入する。
---------------------------------------------------------------- | | | | | | |呼び出し元のベースポインタ|呼び出し元のアドレス| ----------------------------------------------------------------- ^%esp^%ebp
今は、%esp == %ebpな状態。後はpushで左へ積んでいけばOK。
実はこのサンプルでは、Hello,Worldよりespとebpの関係の方が重要なのだ!!(と僕は思う)
pushしてpopしてない。
そう、コレがベースポインタの凄い所。pushしたら、popするのが流儀。しかし、popしなくても、ベースポインタでスタックの底を定義してるので、leaveで確実にスタックの底へたどり着ける!!
leave
leaveすると、最初の状態まで戻る。enterはあんまりよくわかってない。
まとめ
- 試しにpopしまくってみて、ちゃんと戻れる事を確認して欲しい。
- %epb->%epb->%epb->%epbと辿ることで、バックトレースを取ることが出来る。
- 4(%ebp)に呼出し元アドレスが入っているので、色々活用が効く。
- 前の関数の変数定義を覗き見することも出来る。
- スタック重要。
復習終了。
OS自作入門で散々ハマったからな・・・。
参考
- Binary hacs hack#63 Cでバックトレースを表示する
- ベースポインタ攻略用hack。色々やりかたがあるらしい。
- Schemeの実装におけるスタックフレーム(Draft)
- インテル・アーキテクチャ 最適化マニュアル
- 複雑な命令 (たとえば、enter、leave、loop)の使用を避け、単純な命令のシーケンスを使用する。
- leaveはpopで代用出来る。