12

私は最近、独自のプログラミング言語を作成するという最終的な目標を持って、アセンブリ プログラミングの世界に没頭しようとしています。私の最初の実際のプロジェクトは、x86 マシン言語のごく一部をアセンブルして Windows 実行可能ファイルを作成できる、C で記述された単純なアセンブラーにしたいと考えています。マクロもリンカーもありません。組み立てだけ。

紙の上では、それは十分に単純に思えます。アセンブリ コードが入って、マシン コードが出てきます。

しかし、細かいことを考えていると、急に気が遠くなるような気がします。オペレーティング システムが要求する規則は何ですか? データを整列させてジャンプを計算するにはどうすればよいですか? 実行可能ファイルの内部はどのように見えますか?

途方に暮れています。これに関するチュートリアルはなく、よく使われているアセンブラのソース コードを見るのも刺激的ではありませんでした (ただし、もう一度試してみたいと思います)。

ここからどこへ行けばいいですか?どうやってそれをしたでしょうか?このトピックに関する優れたチュートリアルや文献はありますか?

4

3 に答える 3

16

私自身 (アセンブラと逆アセンブラ) をいくつか書きましたが、x86 から始めるつもりはありません。x86またはその他の命令セットを知っている場合は、別の命令セットの構文をすぐに(夕方/午後)ピックアップして学習できます。少なくともライオンズはそれを共有します. アセンブラー (または逆アセンブラー) を作成する行為は、確実に命令セットを迅速に教えてくれます。また、そのレベルでマイクロコードを調べたことのない、その命令セットの多くの経験豊富なアセンブリ プログラマーよりも、その命令セットについてよく理解できます。msp430、pdp11、thumb (thumb2 拡張機能ではない) (または mips または openrisc) はすべて、開始するのに適した場所であり、多くの指示がなく、過度に複雑ではないなどです。

最初に逆アセンブラをお勧めします。それには、arm、thumb、mips、openrisc などの固定長の命令セットを使用します。そうでない場合は、少なくとも逆アセンブラを使用します (間違いなく、既にアセンブラ、リンカ、および逆アセンブラー) と鉛筆と紙を使用して、機械コードとアセンブリ、特に分岐の関係を理解すると、オフセットが追加されたときにプログラムカウンターが命令の 1 つまたは 2 つ先にあるなどの癖が通常 1 つまたは複数あり、別のビットを得ることができます。バイトではなく命令全体で測定する場合があります。

Cプログラムでテキストを力ずくで解析して命令を読むのはとても簡単です。より難しい作業ですが、おそらく教育として、bison/flex を使用し、そのプログラミング言語を学習して、これらのツールが (さらに極端なブルート フォース) パーサーを作成できるようにすることです。パーサーは、コードにインターフェイスして、どこで何が見つかったかを伝えます。

アセンブラ自体は非常に単純です。ASCII を読み取り、マシン コードにビットを設定するだけです。ブランチやその他の PC 相対命令は、完全に解決するためにソース/テーブルを複数回通過する可能性があるため、少し面倒です。

  mov r0,r1
  mov r2 ,#1

アセンブラは行 (キャリッジ リターン 0xD またはライン フィード 0xA に続くバイトとして定義されます) のテキストの解析を開始し、空白以外の何かに到達するまで空白 (スペースとタブ) を破棄し、それから strncmp を既知のニーモニック。ヒットした場合は、その命令の可能な組み合わせを解析します。上記の単純なケースでは、mov が空白を非空白にスキップした後、おそらく最初に見つけたのはレジスタ、次にオプションの空白、次にコンマ。空白とコンマを削除し、それを文字列のテーブルと比較するか、単に解析します。そのレジスターが完了したら、コンマが見つかった場所を通り過ぎて、それが別のレジスターまたは即時のいずれかであるとしましょう。即時の場合は # 記号が必要であり、レジスタの場合は小文字または大文字で始まる必要があります ' r'. そのレジスタまたは即時を解析した後、その行にあるべきではない行に他に何もないことを確認してください。この命令のマシン コードをビルドするか、少なくともできるだけ多くのマシン コードをビルドし、次の行に進みます。面倒かもしれませんが、ASCII を解析するのは難しくありません...

少なくとも、作成時にマシンコード/データを蓄積するテーブル/配列に加えて、命令が未完了であるとマークするための何らかの方法、PC 相対命令が将来のパスで完了する必要があります。また、見つけたラベルを収集するテーブル/配列と、見つかったマシンコードテーブルのアドレス/オフセットも必要になります。宛先/ソースとして命令で使用されるラベルと、部分的に完了した命令を保持するテーブル/配列内のオフセットと同様に、それらが使用されます。最初のパスの後、ラベル定義のアドレス/オフセットを使用して問題の命令までの距離を計算し、作成を完了するまで、すべてのラベル定義をソースまたは宛先として使用されるラベルと一致させるまで、これらのテーブルに戻ります。その命令のマシンコード。

次のステップは、許可したい場合は、複数のソース ファイルを許可することです。アセンブラーによって解決されないラベルを用意する必要があるため、出力にプレースホルダーを残し、最長のジャンプ/分岐命令のフレーバーを作成する必要があります。次に、作成/使用するために選択した出力ファイル形式があります。リンカがあります。これはほとんど単純ですが、最終的な PC 相対命令のマシンコードを記入することを覚えておく必要があります。アセンブラの場合よりも難しくありません。自体。

アセンブラを書くことは、必ずしもプログラミング言語を作成してからそのためのコンパイラを書くことに関連しているとは限らないことに注意してください。別のことであり、別の問題です。実際、新しいプログラミング言語を作成したい場合は、既存の命令セットに既存のアセンブラーを使用するだけです。もちろん必須ではありませんが、ほとんどの教育やチュートリアルでは、プログラミング言語に bison/flex アプローチを使用します。また、コンパイラ クラスを開始するための大学のコースの講義ノートやリソースが数多くあります。言語の機能を追加するスクリプト。ミドルエンドとバックエンドは、フロントエンドよりも大きな課題です。このトピックに関する書籍は多数あり、オンライン リソースも多数あります。

于 2013-04-13T04:50:08.163 に答える
4

あなたが探しているのは、チュートリアルやソース コードではなく、仕様です。http://msdn.microsoft.com/en-us/library/windows/hardware/gg463119.aspxを参照してください。

実行可能ファイルの仕様を理解したら、それを生成するプログラムを作成します。ビルドする実行可能ファイルは、できるだけ単純にする必要があります。それをマスターしたら、命令名と数値引数を読み取り、exe にプラグインするコードのブロックを生成する単純な行指向のパーサーを作成できます。後で、シンボル、ブランチ、セクションなど、必要なものを追加できます。そこにhttp://www.davidsalomon.name/assem.advertis/asl.pdfのようなものが入ります。

PS Carl Norum は、上記のコメントに良い点があります。あなたの目標が独自のプログラミング言語を作成することである場合、アセンブラーを書くことを学ぶことは無関係であり、(あなたが作成したい言語がアセンブリ言語でない限り) 開始するための正しい方法ではありません. アセンブラー ソースから実行可能ファイルを生成するアセンブラーが既に存在するため、コンパイラはアセンブラー ソースを生成でき、アセンブラーを再作成する作業を回避できます。または、LLVM のようなものを使用することもできます。これにより、コンパイラの構築に関する他の多くの困難な問題が解決されます。実際に独自のプログラミング言語を作成する可能性は非常に低いですが、ゼロから始めてその必要がなければ、その可能性ははるかに低くなります。目標を決定し、それを達成するために利用できる最適なツールを使用してください。

于 2013-04-13T04:23:00.637 に答える
4

LLVM を見る必要があります。llvm はモジュラー コンパイラ バックエンドです。最も人気のあるフロント エンドは、C/C++/Objective-C をコンパイルするための Clang です。LLVM の良いところは、関心のあるコンパイラ チェーンの部分を選択し、その部分だけに集中して、他のすべてを無視できることです。独自の言語を作成し、LLVM 内部表現コードを生成するパーサーを作成し、中間層のターゲットに依存しない最適化と多くの異なるターゲットへのコンパイルをすべて無料で取得します。いくつかのエキゾチックな CPU のコンパイラに興味があります。LLVM 中間コードを受け取り、アセンブルを生成するコンパイラ バックエンドを作成します。最適化技術、おそらく自動スレッド化についていくつかのアイデアを持ち、LLVM 中間コードを処理する中間層を作成します。LLVM は、GCC のようなスタンドアロンのバイナリではなく、ライブラリのコレクションです。

于 2013-04-13T04:45:53.400 に答える