web-dev-qa-db-ja.com

アセンブリ-.data、.code、およびレジスタ...?

それで今朝私はアセンブリについて混乱した質問を投稿し、私は本当に感謝しています。

そして今、私はアセンブリに入り始め、それがどのように機能するかを理解し始めています。

私が大丈夫だと私が理解していると思うことには、スタック、割り込み、バイナリ/ 16進数、および一般的な基本操作のほとんど(jmp、Push、movなど)が含まれます。

私が理解するのに苦労していて助けを求めている概念を以下に示します。次のいずれかに対処できれば、非常に役立ちます。

  1. 正確に.dataセクションで何が起こっていますか?宣言している変数はありますか?
  2. もしそうなら、後でコードセクションで変数を宣言できますか?そうでない場合、なぜでしょうか?もしそうなら、どのように、そしてなぜデータセクションを使用するのですか?
  3. レジスターとは?変数とどのように比較しますか?つまり、これは小さな情報を格納する場所だということですが、私には変数のように聞こえます。
  4. 配列を作成するにはどうすればよいですか?私はこれが一種のランダムに見えることを知っていますが、私がこのようなことをどうやってやるのかについて興味があります。
  5. 各レジスタの使用目的に関する一般的な慣行のリストはありますか?まだ完全には取得できませんが、たとえば、プロシージャからの「戻り値」を格納するために特定のレジスタを使用する必要があると言っている人がいることに気づきました。そのようなプラクティスの包括的または少なくとも有益なリストはありますか?
  6. 私がアセンブリを学ぶ理由の1つは、高レベルのコードの背後で何が行われているのかをよりよく理解することです。それを念頭に置いて-私がc ++でプログラミングしているとき、私はしばしばスタックとヒープについて考えています。アセンブリでは、スタックが何であるか知っています-「ヒープ」はどこにありますか?

いくつかの情報:WinAsmをIDEとして使用してmasm32を使用しており、Windows 7で作業しています。c++/Javaなどの高水準言語でのプログラミング経験は豊富です。


編集:いつも助けてくれてありがとう、いつものように非常に有益です!素晴らしいもの!最後にもう1つ-スタックポインターとベースポインターの違い、またはESPとEBPの違いは何ですか。誰かが私を手伝ってくれませんか?

編集:私は今それを手に入れていると思います... ESPは常にスタックの一番上を指します。しかし、EBPを好きなところに向けることができます。ESP =は自動的に処理されますが、EBPで好きなことができます。次に例を示します。

Push 6
Push 5
Push 4
mov EBP, ESP
Push 3
Push 2

このシナリオでは、EBPは4を保持するアドレスを指すようになりましたが、ESPは2を保持するアドレスを指すようになりました。

実際のアプリケーションでは、6、5、および4が関数の引数である可能性がありますが、3および2はその関数内のローカル変数である可能性があります。

28
Cam

順番に答えてみよう!

  1. データセクションには、システムがプログラムのエントリポイントを呼び出す前に、システムによって自動的に初期化されるものが含まれます。あなたが正しい、通常グローバル変数はここに終わります。理由はないため、通常、ゼロで初期化されたデータは実行可能ファイルに含まれていません。プログラムローダーに対するいくつかのディレクティブで、そのスペースを生成する必要があります。プログラムが実行を開始すると、ZI領域とデータ領域は通常交換可能です。 Wikipedia にはもっと多くの情報があります。

  2. 変数は、アセンブリプログラミング時には実際には存在しません。少なくとも、Cコードを記述しているときには存在しません。あなたが持っているのはあなたの記憶をどのようにレイアウトするかについてあなたがした決定です。変数は、スタック内、メモリ内のどこかに存在するか、またはレジスタ内にのみ存在します。

  3. レジスタは、プロセッサの内部データストレージです。一般に、プロセッサレジスタの値に対してのみ操作を実行できます。内容をメモリに読み込んだり、メモリから保存したりできます。これは、コンピュータの基本的な動作方法です。簡単な例を示します。このCコード:

    _int a = 5;
    int b = 6;
    int *d = (int *)0x12345678; // assume 0x12345678 is a valid memory pointer
    *d = a + b;
    _

    以下の行に沿って、いくつかの(簡略化された)アセンブリに翻訳される場合があります。

    _load  r1, 5
    load  r2, 6
    load  r4, 0x1234568
    add   r3, r1, r2
    store r4, r3
    _

    この場合、レジスターを変数と見なすことができますが、一般に、1つの変数が常に同じレジスターにとどまる必要はありません。ルーチンがどれほど複雑かによっては、それさえ不可能かもしれません。一部のデータをスタックにプッシュしたり、他のデータをポップオフしたりする必要があります。 「変数」とは、その論理的なデータの断片であり、メモリやレジスタなどに存在する場所ではありません。

  4. 配列は、メモリの連続したブロックです。ローカル配列の場合、スタックポインタを適切にデクリメントできます。グローバル配列の場合、データセクションでそのブロックを宣言できます。

  5. レジスターに関する規則はたくさんあります。それらを正しく使用する方法の詳細については、プラットフォームのABIまたは呼び出し規則のドキュメントを確認してください。アセンブラのドキュメントにも情報が含まれている場合があります。 wikipediaのABI記事 を確認してください。

  6. アセンブリプログラムは、任意のCプログラムと同じシステムコールを作成できるため、malloc()を呼び出すだけで、ヒープからメモリを取得できます。

32
Carl Norum

これに追加したいのですが。コンピュータ上のプログラムは、他にもありますが、通常3つのセクションに分かれています。

コードセグメント-.code、.text: http://en.wikipedia.org/wiki/Code_segment

コンピューティングでは、テキストセグメントまたは単にテキストとしても知られるコードセグメントは、実行可能命令を含むメモリまたはオブジェクトファイルの一部を参照するために使用されるフレーズです。サイズは固定されており、通常は読み取り専用です。テキストセクションが読み取り専用でない場合、特定のアーキテクチャでは自己変更コードが許可されます。読み取り専用コードは、複数のプロセスで同時に実行できる場合、再入可能です。メモリ領域として、コードセグメントは、ヒープおよびスタックオーバーフローによる上書きを防ぐために、メモリの下部または最下部に配置されます。

データセグメント-.data: http://en.wikipedia.org/wiki/Data_segment

データセグメントは、オブジェクトファイルまたはメモリ内のプログラムのセクションの1つであり、プログラマーによって初期化されるグローバル変数と静的変数が含まれます。このセクションのすべてのデータは、プログラムがロードされる前にプログラマーによって設定されるため、固定サイズです。ただし、変数の値は実行時に変更できるため、読み取り専用ではありません。これは、Rodata(定数、読み取り専用データ)セクション、およびコードセグメント(テキストセグメントとも呼ばれます)とは対照的です。

BSS: http://en.wikipedia.org/wiki/.bss

コンピュータープログラミングでは、.bssまたはbss(元々はシンボルによって開始されたブロック)が、ゼロ値のデータのみで満たされた静的変数とグローバル変数を含むデータセグメントの一部の名前として、多くのコンパイラーとリンカーによって使用されています最初に(つまり、実行が開始されたとき)。多くの場合、「bssセクション」または「bssセグメント」と呼ばれます。プログラムローダーは、プログラムを読み込むときに、bssセクションに割り当てられたメモリを初期化します。

レジスタは、他の人が説明しているように、データまたはメモリアドレスを格納するためのCPUの機能です。操作は、add eax, ebxなどのレジスタに対して実行され、アセンブリの方言によって異なります。この場合、これはebxのコンテンツをeaxに追加してeaxに格納する(NASM構文)と解釈されます。 GNU AS(AT&T))で同等のものはmovl $ebx, $eaxです。アセンブリの異なる方言には異なるルールと演算子があります。この理由で、私はMASMのファンではありません-非常にNASM、YASM、およびGNU ASの両方とは異なります。

Cとの一般的な相互作用は実際にはありません。ABIはこれがどのように発生するかを指定します。たとえば、x86(unix)ではスタックにプッシュされたメソッドの引数が見つかりますが、Unixではx86-64では最初のいくつかの引数がレジスターに配置されます。どちらのABIも、関数の結果がeax/raxレジスタに格納されることを期待しています。

以下は、WindowsとLinuxの両方でアセンブルされる32ビットの追加ルーチンです。

_Add
    Push    ebp             ; create stack frame
    mov     ebp, esp
    mov     eax, [ebp+8]    ; grab the first argument
    mov     ecx, [ebp+12]   ; grab the second argument
    add     eax, ecx        ; sum the arguments
    pop     ebp             ; restore the base pointer
    ret

ここで、私の意味を確認できます。 「戻り値」はeaxにあります。対照的に、x64バージョンは次のようになります。

_Add
    Push    rbp             ; create stack frame
    mov     rbp, rsp
    mov     eax, edi        ; grab the first argument
    mov     ecx, esi        ; grab the second argument
    add     eax, ecx        ; sum the arguments
    pop     rbp             ; restore the base pointer
    ret

この種のものを定義するドキュメントがあります。 UNIX x64 ABIは次のとおりです: http://www.x86-64.org/documentation/abi-0.99.pdf 。おそらく、必要なプロセッサ、プラットフォームなどのABIを見つけることができると思います。

アセンブリでアレイをどのように操作しますか?ポインター演算。 eaxにベースアドレスが指定されている場合、整数のサイズが4バイトの場合、次に格納される整数は[eax+4]になります。このスペースは、malloc/callocまでの呼び出しを使用して作成するか、システムにあるものは何でも、メモリ割り当てシステムコールを呼び出すことができます。

「ヒープ」とは何ですか?再びウィキペディアによると、これは動的メモリ割り当て用に予約されたメモリ領域です。 calloc、malloc、またはメモリ割り当てシステムコールを呼び出すまで、アセンブリプログラムには表示されませんが、存在します。

エッセイでごめんなさい。

18
user257111