31

それで今朝、私は組み立てについて混乱した質問を投稿しました、そして私はいくつかの素晴らしい本物の助けを受け取りました、それは本当に感謝しています。

そして今、私は組み立てを始め、それがどのように機能するかを理解し始めています。

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

私が理解するのに苦労していて助けを求めている概念は以下のとおりです-あなたが以下のいずれかに取り組むことができればそれは大きな助けになるでしょう:

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

いくつかの情報:私はIDEとしてWinAsmでmasm32を使用しており、Windows7で作業しています。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はその関数内のローカル変数である可能性があります。

4

2 に答える 2

33

順番に答えてみよう!

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

  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または呼び出し規約のドキュメントを確認してください。アセンブラのドキュメントにも情報が含まれている場合があります。ウィキペディアのABIの記事を確認してください。

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

于 2010-03-01T01:00:02.240 に答える
18

これに付け加えたいと思います。コンピュータ上のプログラムは通常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(元々はBlock Started by Symbolの略)は、静的変数とゼロ値データのみで埋められるグローバル変数を含むデータセグメントの一部の名前として、多くのコンパイラーとリンカーによって使用されます。最初に(つまり、実行が開始されたとき)。これは、「bssセクション」または「bssセグメント」と呼ばれることがよくあります。プログラムローダーは、プログラムをロードするときに、bssセクションに割り当てられたメモリを初期化します。

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

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

ここで、私が何を意味するかを見ることができます。「return」値は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次に格納される整数のベースアドレスが与えられると[eax+4]、整数のサイズが4バイトの場合になります。このスペースは、malloc / callocまでの呼び出しを使用して作成することも、システム上にあるものに関係なく、メモリ割り当てシステムコールを呼び出すこともできます。

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

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

于 2010-03-01T01:58:00.030 に答える