経験上、こういう低レベル(?)な分野を押さえておくことは結構大事だったりします。そうは言っても、具体的に深く掴むのは結構難しいものですね(と、先にエクスキューズしておく笑)。
この時のmain()のアドレスはどこのレジスタに入っているのですか?
main()関数のアドレスはraxレジスタに入っています。
main()のアドレスは0x400f8eになっていました。
print/x *(int *)($rax)
とやると、$4 = 0xe5894855 全く関係のないアドレスが出現します。
print/x $rax として下さい。0x400f8eが表示されるはずです。
なお、「0xe5894855」は、main()関数冒頭の4バイトを数値として表示した結果です。後述の操作をご覧下さい。
mov 0x18(%rsp),%rax # この命令でraxにmain()のアドレスが代入され
callq *%rax # main()を呼び出す
%rax と、「」があるので、C言語のポインタの参照演算子と紛らわしい感じがしますが、
ともかく、このcall命令はraxレジスタの値が飛び先番地です。
__libc_start_mainは、main()を呼ぶのだから、「Cスタートアップ・ルーチン」と呼ばれるコードです。OSとCコンパイラの橋渡しと言えます。従って、この部分はgcc、OS(とディストリビューション)のバージョンによって微妙に違う可能性がありますし、コンパイル時のオプションも現象に関係ありそうです。こうした情報や手順が質問に示されていないと、回答する側も現象を特定できない・再現できないなど、回答が難しくなります。私がUbuntu 16.04で試した手順を以下に示します。
sh
1$ cc -g tera49052.c ← -gオプション、gdbを使う
2$ objdump -D a.out | less ← まずobjdumpで逆アセンブルし、"main"を探す
3// 途中省略
400000000004005da <main>:
5 4005da: 55 push %rbp
6 4005db: 48 89 e5 mov %rsp,%rbp
7 4005de: 48 83 ec 10 sub $0x10,%rsp
8 4005e2: 89 7d fc mov %edi,-0x4(%rbp)
9 4005e5: 48 89 75 f0 mov %rsi,-0x10(%rbp)
10 4005e9: bf 24 07 40 00 mov $0x400724,%edi
冒頭の2命令(push %rbp, mov %rsp,%rbp)の機械語が 0x55, 0x48, 0x89, 0xe5 であり、x86はリトル・エンディアンだから
print/x *(int *)($rax)
とやると、$4 = 0xe5894855
となったのです。決して無関係なものが表示されたのではないのですが、表示しようとしてるものが、アドレスなのか、メモリの値なのか、メモリの値は符号付きか無符号か、整数か実数か、等の条件に注意を払う必要があるわけです。
続いてgdbも使ってみました(貴方と、表示の様子が少し違うことをお断りします)。
sh
1$ gdb a.out
2// 省略
3(gdb) b main ← main()関数にブレークポイントをかけておき
4Breakpoint 1 at 0x4005e9: file tera49052.c, line 25.
5(gdb) r ← 実行開始
6Starting program: /home/rubato/Documents/a.out
7
8Breakpoint 1, main (argc=1, argv=0x7fffffffde28) at tera49052.c:25
925 func("* print addresses *"); ← main()の一行目を表示して停止
10(gdb) info reg ← レジスタの値を表示してみる
11rax 0x4005da 4195802 ← raxは、まだmain()のアドレスを保持している
12rbx 0x0 0
13rcx 0x0 0
14rdx 0x7fffffffde38 140737488346680
15// 省略
16(gdb) print/x *(int *)($rax) ← main() 冒頭の4命令が表示される
17$1 = 0xe5894855
18(gdb) print/x $rax ← main() のアドレスが表示される
19$2 = 0x4005da
20(gdb) info frame ← この時点のスタックフレームを表示する
21Stack level 0, frame at 0x7fffffffdd50:
22 rip = 0x4005e9 in main (tera49052.c:25); saved rip = 0x7ffff7a2e830
23 source language c.
24// 以下省略
ここに表示された「saved rip = 0x7ffff7a2e830」は「callq *%rax」命令でスタックに積まれた戻り番地です。なので、
sh
1(gdb) disas 0x7ffff7a2e830
2Dump of assembler code for function __libc_start_main:
3 0x00007ffff7a2e740 <+0>: push %r14
4 // 途中省略
5 0x00007ffff7a2e822 <+226>: mov 0x14(%rsp),%edi
6 0x00007ffff7a2e826 <+230>: mov (%rax),%rdx
7 0x00007ffff7a2e829 <+233>: mov 0x18(%rsp),%rax ← main()のアドレスをraxに
8 0x00007ffff7a2e82e <+238>: callq *%rax ← main()を呼び出す
9 0x00007ffff7a2e830 <+240>: mov %eax,%edi ← このアドレスがスタックに積まれた
10 0x00007ffff7a2e832 <+242>: callq 0x7ffff7a48020 <__GI_exit> ← exit(status);
こんな遠くからrdiをわざわざ、設定している理由が全くわからない
__libc_start_mainのソースコードを調べるべきではないでしょうか。ここもC言語で記述されているはずで、アセンブリコードだけで考えるのは的外れな感じがします。が、私にはソースコードを探すのは難儀なのでパスします。
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
2016/09/29 13:23