katsuhiro -> katsuhiro/refmon -> katsuhiro/refmon/stack_dump
ローカル変数を用いる場合、関数開始時の sp が ip に保存されてスタックに積まれる。
関数のプロローグ、およびエピローグコード(g++ 3.3.5 最適化なし)
mov ip, sp stmdb sp!, {fp, ip, lr, pc} ; これが終わったあとを sp_new とする sub fp, ip, #4 sub sp, sp, #8 ; これが終わったあとを sp_after_sub とする ... ldmia fp, {fp, sp, pc}
stmdb では DB が指定されているので、sp を sp-16 の値まで減らしてからリストの左側にあるレジスタから fp, ip, lr, pc の順にプッシュする。プッシュの方向はアドレスが増加する(絵で言うと下向き)方向である。
出口には ldmia がある。ここでは IA が指定されているので、fp <- 積んである元の fp、sp <- 元の ip、pc <- 元の lr と読み込んでから(「元の〜」はスタックに積んだ値です)、sp を sp+12 する。
接尾辞 | 意味 |
DA | Decrement After(メモリアクセス後にポインタを減らす) |
IA | Increment After(メモリアクセス後にポインタを増やす) |
DB | Decrement Before(メモリアクセス前にポインタを減らす) |
IB | Increment Before(メモリアクセス前にポインタを増やす) |
スタックの様子
0x00000000 | |______| __ sp_after_sub |_local| |_local| __ sp_new |_float|(浮動小数点のバックアップが |_float| 来るとしたら、この位置) |__fp__| |__ip__| |__lr__| __ fp |__pc__| __ sp, ip | 0xffffffff
ネストした呼び出しを行ったときの様子
0x00000000 | |______| __ sp_after_sub2 |_local| |_local| __ sp_new2 |__fp1_| |__ip2_| |__lr2_| __ fp2 |__pc2_| __ sp_after_sub1, ip2 |_local| |_local| __ sp_new1 |__fp0_| |__ip1_| |__lr1_| __ fp1 |__pc1_| __ sp, ip1 | 0xffffffff
関数の開始から終了まで sp は不変であるとみなす。
fp を使う関数との差はあまりないように思える。
ローカル変数も浮動小数点レジスタのバックアップも行われることがある。
0x00000000 | |______| __ sp |_local| |_local| |_float| |__r4__| |__r5__| |__fp__| |__lr__| __ sp_orig | 0xffffffffただし、ローカル変数領域を使わない、浮動小数点レジスタを積まない、整数レジスタ(fp, lr 以外のレジスタ)を積まない、fp を積まない、lr を積まない、といったさまざまなバリエーションがある。
レジスタを変更する場合、保存すべきレジスタは全て積まれる。このとき stmdb 命令が使われることが多い。
stmdb sp!, {r0, r1, ..., lr} (関数本体) ldmia sp!, {r0, r1, ..., lr}
大切なのは「lr や fp がどこにあるのか」だが、これは命令を解析して得るほかない。
stm 命令の下位 15ビットがストアするレジスタのリストになっている。リファレンス参照のこと。
str lr, [sp, #-4]! (関数本体) ldr pc, [sp], #4
//date(coreutils-5.2.1) の __dcgettext str lr, [sp, #-4]! mov ip, #0 sub sp, sp, #8 (関数本体) add sp, sp, #8 ldr pc, [sp], #4
//date(coreutils-5.2.1) の _IO_file_doallocate stmdb sp!, {r4, r5, r6, lr} ldr r3, [r0, #56] sub sp, sp, #104 (関数本体) add sp, sp, #104 ldmia sp!, {r4, r5, r6, pc}
//date(coreutils-5.2.1) の __mmap stmdb sp!, {r0, r1, r2, r3} mov r0, sp swi 0x0090005a add sp, sp, #16 ; 0x10 cmn r0, #4096 ; 0x1000 movcc pc, lr b 35c60 <__syscall_error>
以下の関数で使用されていた
//date(coreutils-5.2.1, static linked) の _dl_catch_error stmdb sp!, {r4, r5, r6, r7, r8, r9, sl, fp, lr} sfm f4, 4, [sp, #-48]! (...) sub sp, sp, #252 (関数本体) add sp, sp, #252 lfm f4, 4, [sp], #48 ldmia sp!, {r4, r5, r6, r7, r8, r9, sl, fp, pc}
ARM の命令セットリファレンスには、sfm という命令はない。
objdump では特殊な stc 命令を sfm という名前で表示するようだ。
objdump によって sfm f4, 4, [sp, #-48]! と翻訳される機械語 16進 e d 2 d 4 2 0c 2進 1110 1101 0010 1101 0100 0010 00001100 これを stc 命令のフィールドに当てはめると cond 110P UNWL Rn CRd cp# offset_8 =13 =4 =2 =12
00342f90 <__select>: 342f90: e59fc068 ldr ip, [pc, #104] ; 343000 <.text+0x33af30> 342f94: e59cc000 ldr ip, [ip] 342f98: e33c0000 teq ip, #0 ; 0x0 342f9c: 1a000006 bne 342fbc <__select+0x2c> 342fa0: e52d4004 str r4, [sp, #-4]! 342fa4: e59d4004 ldr r4, [sp, #4] 342fa8: ef90008e swi 0x0090008e