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

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

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

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

C

C言語は、1972年にAT&Tベル研究所の、デニス・リッチーが主体となって作成したプログラミング言語です。 B言語の後継言語として開発されたことからC言語と命名。そのため、表記法などはB言語やALGOLに近いとされています。 Cの拡張版であるC++言語とともに、現在世界中でもっとも普及されているプログラミング言語です。

Q&A

解決済

5回答

1840閲覧

アセンブラのポインタ・メモリの流れについて詳しく解説お願いします

YutaNaito

総合スコア7

アセンブリ言語

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

C

C言語は、1972年にAT&Tベル研究所の、デニス・リッチーが主体となって作成したプログラミング言語です。 B言語の後継言語として開発されたことからC言語と命名。そのため、表記法などはB言語やALGOLに近いとされています。 Cの拡張版であるC++言語とともに、現在世界中でもっとも普及されているプログラミング言語です。

0グッド

0クリップ

投稿2019/08/01 12:11

前提・実現したいこと

現在、コンピュータサイエンスの勉強のために書籍でアセンブラを勉強しています。
アセンブラの勉強を通してCPUやメモリの仕組みを理解したいと考えてます。
今までC, C++, C#, Java, Python, htmlなどプログラミング言語を触りましたが、
大学時代情報系ではなかったので全て独学で、コンピュータサイエンスについての知見は浅いです。

発生している問題・エラーメッセージ

書籍で理解できない部分があるのでどなたか詳しい人に教えていただきたいです。
以下のCのプログラムをアセンブルした結果について不明な点があります。

c

1/* アセンブル元のC言語のプログラム */ 2#include <string.h> 3 char *evil(char *a){ 4 char s[128]; 5 strcpy(s, a); 6 return s;

assembra

1/* アセンブル結果 */ 2push ebp 3mov ebp, esp 4sub esp, 136 //1 5sub esp, 8 // 6push dword [ebp+8] 7lea eax, [ebp-136] 8push eax 9call <strcpy> 10add esp, 16 11leave 12ret

不明な点を以下に示します

・sub esp, 136の136は値なのか、アドレスなのか、また、

・sub esp, 136はespに136を入れてるイメージでいいのか

・以下の部分では何をしているのか、なぜ136と8を分けて足してるのか、レジスタの中で何が起きているのか
sub esp, 136 //1
sub esp, 8 //
push dword [ebp+8]
lea eax, [ebp-136]

・call <strcpy>は関数strcpyのアドレスをどこに保存しているのか?

・最後らへんでなぜ"add esp, 16"をしているのか?

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

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

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

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

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

guest

回答5

0

sub esp, 136はespに136を入れてるイメージでいいのか

各命令の意味を知らずにアセンブラプログラムを読もうというのは無謀です。
sub は引き算です。

また、CPUのレジスタ構成を理解していますか?
スタックポインタ、ベースポインタの意味が分かりますか?
分からないとすると、そこから説明するのはこういう場では無理です。

どこまで理解したいのかにも依りますが、CPUアーキテクチャを学習した上で、コンパイラの作り方みたいな本を読んだ方がいいかと思います。

あと、

Cのプログラムをアセンブルした結果

「Cのプログラムをコンパイルした結果」ですね。

#追記

どこまで理解したいのかにも依りますが、

言葉だけで説明すると、

1.関数の入り口でスタック上にローカル変数(仮引数も含む)のエリアを確保します。つまりスタックポインタをそのエリアサイズだけずらします。引数のサイズはポインタ1つで8バイト、ローカル変数128バイトで合計136バイト
2.scrcpyを呼びます。Cの関数を呼び出す手順が決まっていて(コンパイラにも依りますが)、2つの引数をスタックに積んで呼び出し、戻ってきたら、積んだ引数を捨てます(スタックポンタを引数サイズここではポインタ2つで16バイトずらす)。
3.1でずらしたスタックポインタを戻します(LEAVE命令)。
4.呼び出し元に戻る(RET命令)。
ベースポンタの説明は省略。

136を引いたあとに、さらに8引いている8の意味は調べないと分からない。CじゃなくてJavaScriptのように関数の中で関数を定義できる言語だと、スタックフレームのリンク用だと思うのですが。

なぜ136と8を分けて足してるのか、

は、最適化すれば多分1つになるでしょう。

投稿2019/08/01 12:34

編集2019/08/01 13:07
otn

総合スコア84423

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

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

otn

2019/08/01 13:16

> 引数のサイズはポインタ1つで8バイト、ローカル変数128バイトで合計136バイト ここちょっと違う気がしてきました。引数は、ここの外ですね。 この8バイトと、追記に書いた8バイトは、保留ということで、正しいことを知りたければ自分で調べてみてください。
YutaNaito

2019/08/01 22:54

ありがとうございます! おっしゃる通り勉強不足でした。 しっかりアセンブラやCPUを勉強してみようと思います アセンブラを勉強するのにオススメの教材等あれば教えていただけないでしょうか?
otn

2019/08/02 01:33

80486時代の本ですが、http://www.amazon.co.jp/dp/4756102131 これの現代版があれば良いと思いますが、x86アーキテクチャを知らない人が読むには現在でも勉強になるかと思います。図書館で借りてください。
guest

0

・sub esp, 136の136は値なのか、アドレスなのか、また、

ローカル変数、ワーク用エリアの確保サイズですな

・sub esp, 136はespに136を入れてるイメージでいいのか

ダメです。sub というのは引き算ですぜ
espから136を引いています

・call <strcpy>は関数strcpyのアドレスをどこに保存しているのか?

リンク時にアドレスが割り当てられます。
保存してあるわけではないです

・最後らへんでなぜ"add esp, 16"をしているのか?

冒頭でsub しているのでその分addしてますな
pushしている分をこれで戻してますね

それぞれの命令がどういう動作をするのかを理解しましょう。

投稿2019/08/01 12:34

編集2019/08/01 13:04
y_waiwai

総合スコア87719

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

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

YutaNaito

2019/08/01 22:55

ありがとうございます! しっかり各命令の動作を確認してみます!
guest

0

ちょっと怪しいところが有るのですが・・・

asm

1push ebp ; bp退避 2mov ebp, esp ; bpをspに 3sub esp, 136 //1 ; 文字列(s)の領域確保 4sub esp, 8 // ;はじめは復帰値かと思ったんですが・・不明? 5push dword [ebp+8] ; 文字列のポインタ(*a)をpush 6lea eax, [ebp-136] ; 文字列のアドレスをaxに 7push eax ; axをpush 8call <strcpy> 9add esp, 16 ; 引数(スタック)を戻す 10leave 11ret

で、clangだと

asm

1 .cfi_startproc 2# %bb.0: 3 pushq %rbx 4 .cfi_def_cfa_offset 16 5 subq $128, %rsp 6 .cfi_def_cfa_offset 144 7 .cfi_offset %rbx, -16 8 movq %rdi, %rsi 9 movq %rsp, %rbx 10 movq %rbx, %rdi 11 callq strcpy 12 movq %rbx, %rax 13 addq $128, %rsp 14 .cfi_def_cfa_offset 16 15 popq %rbx 16 .cfi_def_cfa_offset 8 17 retq

こうなります(全部レジスタ渡ですね)
・・
他の方も言われてますがアセンブラを勉強しましょうd^^
で、コンパイラも色々癖が有りますから、展開のされ方も習得されたほうがいいと思います。

投稿2019/08/01 14:20

編集2019/08/01 14:23
cateye

総合スコア6851

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

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

rubato6809

2019/08/01 15:33 編集

質問者のコンパイラは 32bit 版です。32bit版CPUは自由に使えるレジスタが少ないので、引数は基本スタック渡しになるようです。 一方、cateyeさんが提示されたclangのコードは明らかに64bit版です。64bit版CPUはレジスタがぐんと増えるので(確か引数6個まで)レジスタ渡しが基本のようです。
YutaNaito

2019/08/01 22:36

ありがとうございます! おっしゃる通り勉強不足でした アセンブラをしっかり勉強してみようと思います
guest

0

ベストアンサー

sub esp, 136の136は値なのか、アドレスなのか

値です。

sub esp, 136はespに136を入れてるイメージでいいのか

違います。代入ではありません。
sub は subtract 引き算という意味です。
sub esp, 136 は esp = esp - 136 という引き算です。
esp はスタックポインタです。esp はメモリのアドレスを値として持っています。esp に対して引き算や足し算をすると、スタック領域の中のポイントするメモリが変わります。それはスタック領域のトップ位置が変化することになる…と言ってわかるかな?図示できると良いのだけど今は…。

以下の部分では何をしているのか、なぜ136と8を分けて足してるのか、レジスタの中で何が起きているのか

繰り返しますが、足し算ではなく、引き算です。

asm

1sub esp, 136 // esp = esp - 136, s[128] を割り当てた 2sub esp, 8 // esp = esp - 8 3push dword [ebp+8] // この関数の引数 a の値を strcpy に渡そうとしている 4lea eax, [ebp-136] // eax に s[] の先頭アドレスを代入し、 5push eax // eax をスタックトップにpushして strcpy に渡す
  • 136 は 128 + 8 です。主な目的は s[128] となるメモリ領域を確保(割当て)すること。加えて8バイトの領域(用途は今、不明)もスタック領域に確保すること。
  • sub esp, 8 で、さらに8バイトの領域をスタックに確保した、ということ(たぶん、関数呼出しをする際の「作法」のようなもの、後述)。

136バイトの領域と8バイトの領域は、使う目的が違う=出処が違うので、別の命令が生成されたのです。でも、2つの連続した sub esp 命令は、次の一命令に置き換えることができます。
sub esp, 144 // esp = esp - 144

人間がプログラムするなら、容易に一命令にまとめられますが、おそらくコンパイラの最適化レベルが低い(最適化オプションを指定していない?)ので、まとめられなかったのでしょう。

call <strcpy>は関数strcpyのアドレスをどこに保存しているのか?

これをアセンブルする時点で strcpy のアドレスは不明です。アセンブラは、アドレスはわからないけど、call命令の機械語命令とバイト数は当然わかってる…
具体的に言うと call strcpy は 0xE8 0x?? 0x?? 0x?? 0x?? という5バイトの機械語命令にアセンブルされると思われ、4バイトの 0x?? は~~ strcpy のアドレス~~が書かれる所なのだけど、この時点で strcpy のアドレスは決まらないので、この4バイトを 0x?? のままにしている…アドレスに相当する部分の機械語コードを空欄にしてアセンブルしておくのです。
「保存」ではなく、後でアドレスを埋められるように「空欄」を作っておくということ。

コンパイル処理の中で、アセンブルの次(というか、最後)の処理はリンクです。
strcpy 関数の機械語コードはライブラリファイルの中にあります。それをこのプログラムと結合(linkage)します。結合した時点で strcpy のアドレスが確定するので、空欄を埋めることができる…先ほど空欄にしておいた 4バイトの 0x?? に確定したアドレスを書き込むことができて、実行可能なファイルができあがる、というわけです。

※上記、打ち消し線を引いた2ヵ所を「strcpy への距離」と訂正します。この距離は call 命令の、次の命令の先頭アドレスが基準です。それは今回 add 命令ですから

  • 距離 = (strcpy のアドレス)-(add 命令の先頭アドレス)

と計算します。この値が4バイトの 0x?? に書かれます。以上、昨夜はアドレスか距離か、どちらか確信ないまま回答しましたが、いずれにしても、 strcpy のアドレス(と add 命令のアドレス)はリンク時に確定し、全ての機械語が確定する、という意味では大勢に違いありません(と言い訳するw)。

最後らへんでなぜ"add esp, 16"をしているのか?

call命令の直前にpush命令が2つ、それと上記の sub esp, 8 命令があります。

asm

1sub esp, 8 // esp = esp - 8 2push dword [ebp+8] // esp = esp - 4 3lea eax, [ebp-136] 4push eax // esp = esp - 4 5call <strcpy> 6add esp, 16 // esp = esp + 16

二つのpush命令は strcpy(s, a) の実引数 s と a をスタックに書いて strcpy() に渡しています。
ひとつのpush命令は esp の値を 4 減らします(esp = esp - 4)。上にコメントしたように、8, 4, 4 が esp から引かれているので、これらの分をまとめて元に戻すのが add esp, 16 です。
スタックの値を元に戻したということから、ここの sub esp, 8 から add esp, 16 までの6命令が strcpy(s, a); という関数呼出しをコンパイルした部分として、ひとまとまりのコードと見ることができます。

不明点はまだあるでしょうから、コメントもしくは新たな質問で聞いてください。

投稿2019/08/01 13:40

編集2019/08/02 11:03
rubato6809

総合スコア1380

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

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

YutaNaito

2019/08/01 22:51

詳しく丁寧に説明していただきありがとうございます! しっかり回答見て不明な点あれば質問させていただきます
guest

0

既に他の方の回答で尽くされている気もしますが、

書籍で理解できない部分があるので

どんな本で勉強したのでしょうか?
(アセンブラの解説があれば、subなんて必ず、説明がある筈)
また、アセンブラと言うと、CPUに依存しますが、CPUは何? (予想できますが)
コンパイル環境等はどうなっているか?

こんなのが明確で無いと、正確な答えは出ません。

[追記]
一応、大体の説明 (厳密にはちょっと自信無いが)
sub esp, 136 // esp レジスタの値を 136だけ減じる。 esp = esp - 136 (128 + 8(引数))
sub esp, 8 // esp = esp - 8 (8 => 64bitアドレス分か?)
push dword [ebp+8] // 多分、s の先頭アドレスを スタックに入れる
lea eax, [ebp-136] // 引数 aの値をスタックにいれる。

call <strcpy> // <strcpy> はリンク時に実際の値が設定される。
// さて、リンクとは? の問題が発生するが、こちらは分かりますか?
add esp, 16 // スタック(esp)に 16を加算 esp = esp + 16、
// esp, ebp はレジスタですが、その役割は?
// esp, ebpレジスタとか、アセンブラ命令から、Intel系 CPUでしょうか。
// あちこちに 8と言う数字が出ることから、 64bit CPUでしょうか?
// 関数呼び出し時の引数の指定方法は、CPUとコンパイラ依存です。

あ、

strcpy(s, a);
return s;

ここで、s を戻していますが、この char s[128] はスタック上に領域を確保しているで、元の Cプログラムは正しく動かないと思います。

投稿2019/08/01 13:17

編集2019/08/01 14:27
pepperleaf

総合スコア6383

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

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

rubato6809

2019/08/01 15:38

> 元の Cプログラムは正しく動かない それは質問者も承知の上じゃないかな?だって関数名が evil だから
YutaNaito

2019/08/01 22:40

説明不足で申し訳ございませんでした。 教材に載ってたものをそのまま書いて しまいました。 アセンブラしっかり勉強してみようと思います
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.50%

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

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

質問する

関連した質問