質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.50%
アセンブリ言語

アセンブリ言語とは、機械語を人間にわかりやすい形で記述した低水準言語です。

Linux

Linuxは、Unixをベースにして開発されたオペレーティングシステムです。日本では「リナックス」と呼ばれています。 主にWebサーバやDNSサーバ、イントラネットなどのサーバ用OSとして利用されています。 上位500のスーパーコンピュータの90%以上はLinuxを使用しています。 携帯端末用のプラットフォームAndroidは、Linuxカーネル上に構築されています。

Q&A

解決済

2回答

2952閲覧

main()前のアセンブリ言語が読めません。

strike1217

総合スコア651

アセンブリ言語

アセンブリ言語とは、機械語を人間にわかりやすい形で記述した低水準言語です。

Linux

Linuxは、Unixをベースにして開発されたオペレーティングシステムです。日本では「リナックス」と呼ばれています。 主にWebサーバやDNSサーバ、イントラネットなどのサーバ用OSとして利用されています。 上位500のスーパーコンピュータの90%以上はLinuxを使用しています。 携帯端末用のプラットフォームAndroidは、Linuxカーネル上に構築されています。

0グッド

1クリップ

投稿2016/09/27 03:41

編集2016/09/27 04:44
0x401160 <__libc_start_main+416> mov %rax,%fs:0x300 │ │0x401169 <__libc_start_main+425> mov 0x2b56b8(%rip),%rdx # 0x6b6828 <environ> │ │0x401170 <__libc_start_main+432> mov 0x10(%rsp),%rsi │ │0x401175 <__libc_start_main+437> mov 0xc(%rsp),%edi │ B+ │0x401179 <__libc_start_main+441> mov 0x18(%rsp),%rax │ >│0x40117e <__libc_start_main+446> callq *%rax │ │0x401180 <__libc_start_main+448> mov %eax,%edi

call *%rax → main()に入っているのですが・・・

この時のmain()のアドレスはどこのレジスタに入っているのですか?
print/x *(int *)($rax)
とやると、$4 = 0xe5894855 全く関係のないアドレスが出現します。

main()のアドレスは0x400f8eになっていました。

GDBで解析しています。
どなたか教えてください。

> 「追記 」

__libc_start_main()が呼び出される前からmain()の設定をしている箇所を見つけました。
こんな遠くからrdiをわざわざ、設定している理由が全くわからないのですが・・・
main()呼び出し直前にprint/x で rdiの中を覗いてみたのですが、何も入っていませんでした。
何が起きているのか・・・さっぱりです。

│0x400e8b <_start+29> mov $0x400f8e,%rdi │ │0x400e92 <_start+36> callq 0x400fc0 <__libc_start_main>

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答2

0

ベストアンサー

経験上、こういう低レベル(?)な分野を押さえておくことは結構大事だったりします。そうは言っても、具体的に深く掴むのは結構難しいものですね(と、先にエクスキューズしておく笑)。

この時の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 12:41

rubato6809

総合スコア1380

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

strike1217

2016/09/29 13:23

あああ・・・ありがとうございます。 ソースコードを探すのはglibcをビルドして、やってみました。 うまくコードを見ることができました。 gdbの使い方についてもう少し学習する必要がありそうです。 ありがとうございました。
guest

0

main()呼び出し直前にprint/x で rdiの中を覗いてみたのですが、何も入っていませんでした。

この「main()呼び出し」というのはcallq *%raxのことでしょうか?
このraxは一つ上の行でスタックから拾っているので、そこまでの間にrdiをスタックに積んでいるんじゃないでしょうか?(さらに一つ上の行でedi使ってますし)

投稿2016/09/27 05:05

fuzzball

総合スコア16731

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

strike1217

2016/09/29 13:18

遅くなってすいません。 「「main()呼び出し」というのはcallq *%raxのこと」でございます。 ありがとうございます。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.50%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問