ページ 1 / 1
アセンブリが読めなくて困っています。
Posted: 2012年12月11日(火) 00:29
by 安藤
objdump -d (実行プログラム)
で実行プログラムのアセンブリを読み、このプログラムが何をしているか説明しなければいけないのですが、読めなくて困っています。
出題者からヒントを出されて、逆アセンブリの結果の一部(以下に記載)である<fff>, <main>のところを読めばわかるとのことなのですが
理解できません。
何かしらの値が1以下か2以上かで分岐している処理があるのはわかるのですが、このプログラムが何をしているかと言われると答えられません。
環境は、zenithで動き、zenithのOSは64bitのlinux(Redhat)です。
コード:
00000000004004d8 <fff>:
4004d8: 55 push %rbp
4004d9: 48 89 e5 mov %rsp,%rbp
4004dc: 53 push %rbx
4004dd: 48 83 ec 08 sub $0x8,%rsp
4004e1: 89 7d f4 mov %edi,0xfffffffffffffff4(%rbp)
4004e4: 83 7d f4 01 cmpl $0x1,0xfffffffffffffff4(%rbp)
4004e8: 74 06 je 4004f0 <fff+0x18>
4004ea: 83 7d f4 02 cmpl $0x2,0xfffffffffffffff4(%rbp)
4004ee: 75 09 jne 4004f9 <fff+0x21>
4004f0: c7 45 f0 01 00 00 00 movl $0x1,0xfffffffffffffff0(%rbp)
4004f7: eb 1d jmp 400516 <fff+0x3e>
4004f9: 8b 7d f4 mov 0xfffffffffffffff4(%rbp),%edi
4004fc: 83 ef 01 sub $0x1,%edi
4004ff: e8 d4 ff ff ff callq 4004d8 <fff>
400504: 89 c3 mov %eax,%ebx
400506: 8b 7d f4 mov 0xfffffffffffffff4(%rbp),%edi
400509: 83 ef 02 sub $0x2,%edi
40050c: e8 c7 ff ff ff callq 4004d8 <fff>
400511: 01 c3 add %eax,%ebx
400513: 89 5d f0 mov %ebx,0xfffffffffffffff0(%rbp)
400516: 8b 45 f0 mov 0xfffffffffffffff0(%rbp),%eax
400519: 48 83 c4 08 add $0x8,%rsp
40051d: 5b pop %rbx
40051e: c9 leaveq
40051f: c3 retq
0000000000400520 <main>:
400520: 55 push %rbp
400521: 48 89 e5 mov %rsp,%rbp
400524: 48 83 ec 10 sub $0x10,%rsp
400528: 48 8d 75 fc lea 0xfffffffffffffffc(%rbp),%rsi
40052c: bf 58 06 40 00 mov $0x400658,%edi
400531: b8 00 00 00 00 mov $0x0,%eax
400536: e8 b5 fe ff ff callq 4003f0 <scanf@plt>
40053b: 8b 7d fc mov 0xfffffffffffffffc(%rbp),%edi
40053e: e8 95 ff ff ff callq 4004d8 <fff>
400543: 89 c6 mov %eax,%esi
400545: bf 5b 06 40 00 mov $0x40065b,%edi
40054a: b8 00 00 00 00 mov $0x0,%eax
40054f: e8 7c fe ff ff callq 4003d0 <printf@plt>
400554: c9 leaveq
400555: c3 retq
400556: 90 nop
400557: 90 nop
400558: 90 nop
400559: 90 nop
40055a: 90 nop
40055b: 90 nop
40055c: 90 nop
40055d: 90 nop
40055e: 90 nop
40055f: 90 nop
Re: アセンブリが読めなくて困っています。
Posted: 2012年12月11日(火) 11:32
by softya(ソフト屋)
mainはscanfして得られた値でfff関数をコールして戻り値をprintfしているだけですね。
fff関数は0xfffffffffffffff4(%rbp)とかに借りの変数名を付けてやれば読みやすくなるんではないでしょうか?
あと一行ごとにコメントを書いて、フローチャートとか書くと良いかと思います。
それとfff関数でリカーシブルコールをしているのは分かりますか?
Re: アセンブリが読めなくて困っています。
Posted: 2012年12月11日(火) 15:16
by 安藤
softya(ソフト屋) さんが書きました:
それとfff関数でリカーシブルコールをしているのは分かりますか?
わかりませんでした。
15、19行目の行目のcallというのは呼び出された行、pushの処理だけを行うのかjmpと同じように以下の作業も繰り返すかイマイチわかっていません。
softya(ソフト屋) さんが書きました:あと一行ごとにコメントを書いて、フローチャートとか書くと良いかと思います。
書いてみた内容をまとめてみましたので質問したいと思います。
0xfffffffffffffff4(%rbp)、0xfffffffffffffff0(%rbp)などをrbp(4)、rbp(0)とすると、rbp(4)に入れたediの値が1以上2より小さいならループは終わり、何かしらを返すようにし、値が2より大きければその値から1ずつ引いていき処理を繰り返し、ループを終了させると考えたのですが、
この値が何を表し、返すであろう値のもとのeaxがいきなり出てきたことも理解できません。
Re: アセンブリが読めなくて困っています。
Posted: 2012年12月11日(火) 15:59
by softya(ソフト屋)
安藤 さんが書きました:softya(ソフト屋) さんが書きました:
それとfff関数でリカーシブルコールをしているのは分かりますか?
わかりませんでした。
15、19行目の行目のcallというのは呼び出された行、pushの処理だけを行うのかjmpと同じように以下の作業も繰り返すかイマイチわかっていません。
再帰呼び出しの仕組みは理解されていますか?
それとcallqは、全て同じ命令ですね。15,19と35,37,41も全て同じcallqです。
安藤 さんが書きました:
softya(ソフト屋) さんが書きました:あと一行ごとにコメントを書いて、フローチャートとか書くと良いかと思います。
書いてみた内容をまとめてみましたので質問したいと思います。
0xfffffffffffffff4(%rbp)、0xfffffffffffffff0(%rbp)などをrbp(4)、rbp(0)とすると、rbp(4)に入れたediの値が1以上2より小さいならループは終わり、何かしらを返すようにし、値が2より大きければその値から1ずつ引いていき処理を繰り返し、ループを終了させると考えたのですが、
この値が何を表し、返すであろう値のもとのeaxがいきなり出てきたことも理解できません。
eaxは戻り値として使用していると思います。
●参考に。 呼び出し規約のcdecl だと思います。
「呼出規約 - Wikipedia」
http://ja.wikipedia.org/wiki/%E5%91%BC% ... F%E7%B4%84
Re: アセンブリが読めなくて困っています。
Posted: 2012年12月12日(水) 11:19
by かずま
これなら読めますか?
コード:
#include <stdio.h>
// memory
int mem[4096];
// registers
int rax, eax; // accumulator
int rbx, ebx; // base index
int rsi, esi; // source index
int rdi, edi; // destination index
int *rbp, *ebp; // base pointer
int *rsp, *esp = mem + 4096; // stack pointer
// char *rip, *eip; // instruction pointer
void fff()
{
*--esp = rbp, *--esp = ebp; // push %rbp
rbp = rsp, ebp = esp; // mov %rsp,%rbp
*--esp = rbx, *--esp = ebx; // push %rbx
esp -= 2; // sub $0x8,%rsp
ebp[-3] = edi; // mov %edi,-12(&rbp)
if (ebp[-3] == 1) // cmpl $0x1,-12(%rbp)
goto fff_0x18; // je 4004f0 <fff+0x18>
if (ebp[-3] != 2) // cmpl $0x2,-12(%rbp)
goto fff_0x21; // jne 4004f9 <fff+0x21>
fff_0x18:
ebp[-4] = 1; // movl $0x1,-16(%rbp)
goto fff_0x3e; // jmp 400516 <fff+0x3e>
fff_0x21:
edi = ebp[-3]; // mov -12(%rbp),%edi
edi -= 1; // sub $0x1,%edi
fff(); // callq 4004d8 <fff>
ebx = eax; // mov %eax,%ebx
edi = ebp[-3]; // mov -12(%rbp),%edi
edi -= 2; // sub $0x2,%edi
fff(); // callq 4004d8 <fff>
ebx += eax; // add %eax,%ebx
ebp[-4] = ebx; // mov %ebx,-16(%rbp)
fff_0x3e:
eax = ebp[-4]; // mov -16(%rbp),%eax
esp += 2; // add $0x8,%rsp
ebx = *esp++, rbx = *esp++; // pop %rbx
ebp = *esp++, rbp = *esp++; // leaveq
//rip = *esp++, eip = *esp++; // retq
}
int main(void)
{
*--esp = rbp, *--esp = ebp; // push %rbp
rbp = rsp, ebp = esp; // mov %rsp,%rbp
esp -= 4; // sub $0x10,%rsp
esi = &ebp[-1]; // lea -4(%rbp),%rsi
edi = "%d"; // mov $0x400658,%edi
eax = scanf(edi, esi); // callq 4003f0 <scanf@plt>
edi = ebp[-1]; // mov -4(%rbp),%edi
fff(); // callq 4004d8 <fff>
esi = eax; // mov %eax,%esi
edi = "%d\n"; // mov $40065b,%edi
eax = 0; // mov $0x0,%eax
eax = printf(edi, esi); // callq 4003d0 <printf@plt>
ebp = *esp++, rbp = *esp++; // leaveq
//rip = *esp++, eip = *esp++; // retq
return eax;
}
Re: アセンブリが読めなくて困っています。
Posted: 2012年12月12日(水) 20:11
by 安藤
かずま さんが書きました:これなら読めますか?
私なりに読んでみました。C言語仕様に書き換えていただきありがとうございます。
しかし読みやすくはなりましたが具体的に何をしているかは理解できませんでした、すみません。
<fff>について
ebp[-3]の値の分岐によって、2以外であれば2になるまで値を引いていき、(31行目)
2になれば再帰(32行目)を終わり
その後ediが-2され0になり、またfffが呼び出され(?)、
fff_0x3eへ??
fff_0x3eの作業は何を意味しているかはわかりませんでした。
<main>について
mainはscanfで入力されたものを、fffである処理をしてその結果をprintf("%d", eax)で出力するものですよね。
よって、fffの内容さえ理解できれば問題無いと思うのですがそれが把握しきれていません。
Re: アセンブリが読めなくて困っています。
Posted: 2012年12月12日(水) 20:33
by softya(ソフト屋)
やっている内容は自分でC言語に書き換えてみたらどうでしょうか?
fff_0x3eの作業はスタックフレームの操作と戻り値とリターン処理です。
「C言語関数辞典 - C言語用語集 スタックフレーム (stack frame)」
http://www.c-tipsref.com/words/stackframe.html
Re: アセンブリが読めなくて困っています。
Posted: 2012年12月12日(水) 21:40
by 安藤
softya(ソフト屋) さんが書きました:やっている内容は自分でC言語に書き換えてみたらどうでしょうか?
fff_0x3eの作業はスタックフレームの操作と戻り値とリターン処理です。
回答ありがとうございます。4行にそれだけの処理が詰め込まれているのですね。
書ける自信はありませんがいろいろ調べて書いてみたいと思います。
それと、fffの解釈は上に書いたもので合っているでしょうか。
知識が少ないのでよろしければ回答のほど、詳しくお願いします。
Re: アセンブリが読めなくて困っています。
Posted: 2012年12月12日(水) 22:05
by softya(ソフト屋)
知識が無いのはだれでも最初は同じです。
アセンブラで一行毎にコメントを書き、C言語でどの命令にあたるか考えてみないと何時まで経っても理解できないと思います。
>ebp[-3]の値の分岐によって、2以外であれば2になるまで値を引いていき、(31行目)
>2になれば再帰(32行目)を終わり
>その後ediが-2され0になり、またfffが呼び出され(?)、
だいぶと解釈が違うと思いますので、上に書いたことをやってみてください。
if文、関数を呼び出す式と引数処理、return文などの処理が見受けられます。
Re: アセンブリが読めなくて困っています。
Posted: 2012年12月13日(木) 12:24
by かずま
これでもだめですか?
コード:
#include <stdio.h>
// registers
int eax; // 関数の返却値
int ebx; // 一時領域
int edi; // 関数の第1引数
int *ebp; // 関数のスタックフレームを指す
void fff(void)
{
int mem[5]; // スタックフレーム
mem[4] = ebp; // 関数呼び出し元の ebp を退避
ebp = mem + 4; // この関数のスタックフレームを設定
ebp[-2] = ebx; // ebx を退避 (この関数で ebx が使えるようになった)
ebp[-3] = edi; // edi を退避 (再帰呼出しで壊れるから)
if (ebp[-3] == 1 || ebp[-3] == 2) {
ebp[-4] = 1;
} else {
edi = ebp[-3];
edi -= 1;
fff(edi);
ebx = eax;
edi = ebp[-3];
edi -= 2;
eax = fff(edi);
ebx += eax;
ebp[-4] = ebx;
}
eax = ebp[-4]; // 返却値の設定
ebx = ebp[-2]; // ebx の復元
ebp = mem[4]; // ebp の復元
return eax;
}
int main(void)
{
int n;
scanf("%d", &n);
printf("%d\n", fff(n));
return 0;
}
レジスタをローカル変数にしたら、退避復元が不要になります
非表示エリア
この非表示エリアを表示するには、登録し、ログインする必要があります。
Re: アセンブリが読めなくて困っています。
Posted: 2012年12月13日(木) 12:30
by softya(ソフト屋)
かずまさんお気持はわかりますが性急に回答を出し過ぎだと私は思います。
もう少し安藤さんの回答を待っても良いのではないでしょうか?
一時的にhiddenとさしていただきました。かずまさんのコメントをお待ちしております。
Re: アセンブリが読めなくて困っています。
Posted: 2012年12月13日(木) 12:58
by かずま
softya(ソフト屋) さんが書きました:一時的にhiddenとさしていただきました。かずまさんのコメントをお待ちしております。
確かに焦りすぎました。すみません。
ところで、最初のプログラムですが、間違っていました。
void fff(void) で、引数なし、返却値なしなのに、
fff(edi) で呼び出したり、return eax; があったりしています。
訂正します。
コード:
#include <stdio.h>
// registers
int eax; // 関数の返却値
int ebx; // 一時領域
int edi; // 関数の第1引数
int *ebp; // 関数のスタックフレームを指す
void fff(void)
{
int mem[5]; // スタックフレーム
mem[4] = ebp; // 関数呼び出し元の ebp を退避
ebp = mem + 4; // この関数のスタックフレームを設定
ebp[-2] = ebx; // ebx を退避 (この関数で ebx が使えるようになった)
ebp[-3] = edi; // edi を退避 (再帰呼出しで壊れるから)
if (ebp[-3] == 1 || ebp[-3] == 2) {
ebp[-4] = 1;
} else {
edi = ebp[-3];
edi -= 1;
fff();
ebx = eax;
edi = ebp[-3];
edi -= 2;
fff();
ebx += eax;
ebp[-4] = ebx;
}
eax = ebp[-4]; // 返却値の設定
ebx = ebp[-2]; // ebx の復元
ebp = mem[4]; // ebp の復元
}
int main(void)
{
int n;
scanf("%d", &n);
edi = n;
fff();
printf("%d\n", eax);
return 0;
}
Re: アセンブリが読めなくて困っています。
Posted: 2012年12月13日(木) 13:04
by softya(ソフト屋)
ご本人が考えないと何にもならないので回答のバランスは難しいですね。
答えだけもらって終わりという人も多いので、この掲示板の趣旨としてはプログラミングを理解して欲しいと言うことですから、お手伝いの範囲で留めるべきなのでが出し渋ると諦める人が出るので難しいなと日頃から思っております。
Re: アセンブリが読めなくて困っています。
Posted: 2012年12月13日(木) 13:09
by 安藤
softya(ソフト屋)さん
fffにおいて、1行毎にコメントや質問をさせていただきます。
せっかくですので"かずま"さんに書いていただいたコードを参考にさせていただきます。
指導をお願いします。
コード:
#include <stdio.h>
// memory
int mem[4096];
// registers
int rax, eax; // accumulator
int rbx, ebx; // base index
int rsi, esi; // source index
int rdi, edi; // destination index
int *rbp, *ebp; // base pointer
int *rsp, *esp = mem + 4096; // stack pointer
// char *rip, *eip; // instruction pointer
void fff()
{
*--esp = rbp, *--esp = ebp; // push %rbp スタックespにrbp,ebpを順に格納
rbp = rsp, ebp = esp; // mov %rsp,%rbp rbpにrsp、ebpにespを格納
*--esp = rbx, *--esp = ebx; // push %rbx スタックespにrbx,ebxを順に格納
esp -= 2; // sub $0x8,%rsp rsp=rsp-$0x8(10進数の8)と思ったのですがなぜ左式になったかわかりません。
ebp[-3] = edi; // mov %edi,-12(&rbp) ediをrbp(-12)の中身にコピー(ここでのediはscanfで入力した値?)なぜebp[-3]?
if (ebp[-3] == 1) // cmpl $0x1,-12(%rbp) 1とrbpの中身を比較し、1の方が小さい
goto fff_0x18; // je 4004f0 <fff+0x18> 上の比較が正しければ4004f0へジャンプ
if (ebp[-3] != 2) // cmpl $0x2,-12(%rbp) 2とrbpの中身を比較し、2の方が低い
goto fff_0x21; // jne 4004f9 <fff+0x21> 上の比較があやまりならば4004f9へジャンプ
fff_0x18:
ebp[-4] = 1; // movl $0x1,-16(%rbp) 1をrbp(-16)の中身にコピー
goto fff_0x3e; // jmp 400516 <fff+0x3e> 400516へジャンプ
fff_0x21:
edi = ebp[-3]; // mov -12(%rbp),%edi rbp(-12)の中身をediにコピー
edi -= 1; // sub $0x1,%edi edi=edi-1
fff(); // callq 4004d8 <fff> fffの呼び出し(再帰)・・・これにより結果的にediの値を1ずつ引いていってる
ebx = eax; // mov %eax,%ebx eaxをebxにコピー
edi = ebp[-3]; // mov -12(%rbp),%edi rbp(-12)の中身をediにコピー
edi -= 2; // sub $0x2,%edi edi=edi-2
fff(); // callq 4004d8 <fff> fffの呼び出し(再帰)・・・これにより結果的にediの値を2ずつ引いていってる?また?
ebx += eax; // add %eax,%ebx ebx=ebx+eax
ebp[-4] = ebx; // mov %ebx,-16(%rbp) ebxをrbp(-16)の中身へコピー
fff_0x3e:
eax = ebp[-4]; // mov -16(%rbp),%eax rbp(-16)の中身をeaxへコピー
esp += 2; // add $0x8,%rsp rsp=rsp+8?
ebx = *esp++, rbx = *esp++; // pop %rbx rbxの中身を取り出す?
ebp = *esp++, rbp = *esp++; // leaveq 開放?
//rip = *esp++, eip = *esp++; // retq
}
int main(void)
{
*--esp = rbp, *--esp = ebp; // push %rbp
rbp = rsp, ebp = esp; // mov %rsp,%rbp
esp -= 4; // sub $0x10,%rsp
esi = &ebp[-1]; // lea -4(%rbp),%rsi
edi = "%d"; // mov $0x400658,%edi
eax = scanf(edi, esi); // callq 4003f0 <scanf@plt>
edi = ebp[-1]; // mov -4(%rbp),%edi
fff(); // callq 4004d8 <fff>
esi = eax; // mov %eax,%esi
edi = "%d\n"; // mov $40065b,%edi
eax = 0; // mov $0x0,%eax
eax = printf(edi, esi); // callq 4003d0 <printf@plt>
ebp = *esp++, rbp = *esp++; // leaveq
//rip = *esp++, eip = *esp++; // retq
return eax;
}
[/quote]
Re: アセンブリが読めなくて困っています。
Posted: 2012年12月13日(木) 13:32
by softya(ソフト屋)
>sub $0x8,%rsp rsp=rsp-$0x8(10進数の8)と思ったのですがなぜ左式になったかわかりません。
C言語のポインタ演算だからです。8バイトのアドレス変更でも4バイトのintが2つ分ですよね?
>ediをrbp(-12)の中身にコピー(ここでのediはscanfで入力した値?)なぜebp[-3]?
これも上と同様の考え方です。12バイトはint型3つ分です。
>cmpl $0x1,-12(%rbp) 1とrbpの中身を比較し、1の方が小さい
cmpl 命令の理解が間違っています。調べなおしましょう。
>fffの呼び出し(再帰)・・・これにより結果的にediの値を1ずつ引いていってる
この発想を止めたほうが良いかも知れませんね。
edi-1の値で呼び出す。と言う理解で留めるべきです。どっちかと言うと再帰の考え方を見直すべきかな。
【追記】
これは変数であるとか、C言語的に書くとこうなるというのもコメントに付け加えていってください。
あと数行の処理単位で何をしているか別にコメントを書いたほうが良いでしょう。
Re: アセンブリが読めなくて困っています。
Posted: 2012年12月13日(木) 15:25
by 安藤
お二方、回答ありがとうございます。
前回の私の投稿は12時台のお二人のお返事を見てませんでした。
知識があまりにも不足しているので
少し勉強してからまた投稿し、質問します。
すぐには質問内容をまとめて投稿できなさそうなので時間がかかると思いますがそのときまた質問させてください。
すぐには私の考えを出せそうにないのでその旨だけ伝えさせていただきます。
Re: アセンブリが読めなくて困っています。
Posted: 2012年12月18日(火) 15:55
by 安藤
考えがまとまったので投稿させていただきます。
このプログラムにおけるfffは入力した自然数値毎のフィボナッチ数を求めている。
入力値が1もしくは2の場合は1を出力するようになっており、それ以外の時はその入力値の1個前、2個前まで戻りその値のフィボナッチ数を
出し、最終的にその値を足して入力値のフィボナッチ数を出力している。
コード:
00000000004004d8 <fff>:
4004d8: 55 push %rbp
4004d9: 48 89 e5 mov %rsp,%rbp
4004dc: 53 push %rbx
4004dd: 48 83 ec 08 sub $0x8,%rsp
4004e1: 89 7d f4 mov %edi,0xfffffffffffffff4(%rbp) 入力から受け取ったediの中身をrbp4に。
4004e4: 83 7d f4 01 cmpl $0x1,0xfffffffffffffff4(%rbp) 1とrbp4を比較
4004e8: 74 06 je 4004f0 <fff+0x18> 1=rbp4ならジャンプ
4004ea: 83 7d f4 02 cmpl $0x2,0xfffffffffffffff4(%rbp) 2とrbp4を比較
4004ee: 75 09 jne 4004f9 <fff+0x21> 2≠rbp4ならジャンプ
4004f0: c7 45 f0 01 00 00 00 movl $0x1,0xfffffffffffffff0(%rbp) 1をrbp0に。
4004f7: eb 1d jmp 400516 <fff+0x3e> ジャンプ
4004f9: 8b 7d f4 mov 0xfffffffffffffff4(%rbp),%edi rbp4の中身をediに。
4004fc: 83 ef 01 sub $0x1,%edi ediから1ひく
4004ff: e8 d4 ff ff ff callq 4004d8 <fff> fffの再帰呼び出し
400504: 89 c3 mov %eax,%ebx eaxの中身をebxへ。
400506: 8b 7d f4 mov 0xfffffffffffffff4(%rbp),%edi rbp4の値をediへ。
400509: 83 ef 02 sub $0x2,%edi ediから2ひく
40050c: e8 c7 ff ff ff callq 4004d8 <fff> fffの再帰呼び出し
400511: 01 c3 add %eax,%ebx ebxにeaxを足す
400513: 89 5d f0 mov %ebx,0xfffffffffffffff0(%rbp) ebxの値をrbp0へ。
400516: 8b 45 f0 mov 0xfffffffffffffff0(%rbp),%eax rbp0の値をeaxへ。これが最終的に返す値
400519: 48 83 c4 08 add $0x8,%rsp
40051d: 5b pop %rbx
40051e: c9 leaveq
40051f: c3 retq
Re: アセンブリが読めなくて困っています。
Posted: 2012年12月18日(火) 16:05
by softya(ソフト屋)
合っていると思います。ご苦労様でした。
%eaxに関しては戻り値と表現したほうがより分かりやすいと思います。
あとC言語に逆変換できたり、一行ごとではなく機能単位にコメントが書けるぐらい自在になればよりベストです。
Re: アセンブリが読めなくて困っています。
Posted: 2012年12月18日(火) 16:29
by 安藤
何度にもわたって回答していただきありがとうございました。
ここでようやく少しだけアセンブリが読むことがわかったのでこれから徐々に逆変換等もできるようにしていきたいと思います。