2009-05-10 16 views
37

私は主にgccのような一般的に広く使われているコンパイラに興味があります。しかし、コンパイラごとに異なることがある場合は、そのことも知りたいと思います。プログラミング言語コンパイラは、最初にアセンブリに変換するか、直接機械語に変換しますか?

gccを例にとると、Cで書かれた短いプログラムをマシンコードに直接コンパイルするのですか、それとも人間が読めるアセンブリに変換してから組み込みの組み込みアセンブラ組み立てプログラムをバイナリに翻訳するには、マシンコード - 一連の命令をCPUに送りますか?

バイナリ実行可能ファイルを作成するためにアセンブリコードを使用していますが、かなり高価な操作ですか?それとも、それは比較的簡単で迅速なことですか?

は(我々は、プロセッサの唯一のx86ファミリーを扱っていると仮定しましょう、そしてすべてのプログラムは、Linuxのために書かれている。)

が、私は任意の助けのために非常に感謝されるだろうと問題に思いました。ありがとうございました!

答えて

40

gccは実際にアセンブラを生成し、アセンブラとして使用してアセンブルします。すべてのコンパイラがこれを行うわけではありません - MSコンパイラはオブジェクトコードを直接生成しますが、アセンブラ出力を生成することができます。アセンブラをオブジェクトコードに変換することは、少なくともコンパイルと比較してかなり簡単なプロセスです。 cfrontなどの他の高水準言語コードを生成するコンパイラもあります。最初のC++コンパイラは、出力としてCを生成し、それをCコンパイラでコンパイルしました。

ダイレクトコンパイルまたはアセンブリのどちらも実際には実行可能ファイルを生成しません。これは、リンカによって行われます。これは、compilation/assemblyによって生成されたさまざまなオブジェクトコードファイルを取り込み、それらに含まれるすべての名前を解決し、最終実行可能バイナリを生成します。

+3

直接実行可能ファイルを生成するために使用されるいくつかの歴史的なコンパイラ。コンパイル中に実行可能な.COMファイルを1回のパスで書き込むこともできます。各プロシージャのコードに続いて、コンパイラはそのプロシージャ内のパッチ・ポイントのリストを前のプロシージャのパッチ・ポイント・リストのアドレスとともに出力できます。スタートアップコードは、コードがロードされたときに必要なすべてのパッチを作ることができます]。これにより、フロッピーディスクを使用する場合でも、非常に小さなメモリフットプリントで迅速なコンパイルが可能になりました。 – supercat

6

一般に、コンパイラは、ソースコードを抽象構文木(AST)に構文解析し、中間言語に変換します。そのときだけ、通常はいくつかの最適化を行った後、ターゲット言語を出力します。

gccについては、さまざまなターゲットにコンパイルできます。私はx86用に最初にアセンブリをコンパイルするのかどうかはわかりませんが、コンパイラについていくつかの洞察を与えてくれました。

1

Visual C++にはアセンブリコードを出力するためにswitchがあるので、マシンコードを出力する前にアセンブリコードを生成すると思います。

6

、(マイク・ペリーとNasko Oskovによって)Introduction to Reverse Engineering Softwarechapter 2によると、両方のgccとcl.exeの(MSVCのためのバックエンド・コンパイラが++)を使用すると、出力に各コンパイラが生成するアセンブリを使用することができます-Sスイッチを持っています。

冗長モード(gcc -v)でgccを実行して、実行中のコマンドのリストを取得して、舞台裏で何が行われているかを確認することもできます。

1

おそらく、このポッドキャストを聞くことに興味があると思い:最もmulti-pass compilersアセンブリ言語でInternals of GCC

+1

更新リンク:http://www.se-radio.net/2007/07/episode-61-internals-of-gcc/ –

1

は、コード生成工程中に生成されます。これにより、レクサー、構文およびセマンティックフェーズを一度書き込んだ後、単一のアセンブラバックエンドを使用して実行可能コードを生成することができます。これは、さまざまなCPUの範囲を生成するCコンパイラなどのクロスコンパイラで多く使用されています。

ちょうどすべてのコンパイラには、この暗黙的または明示的なステップのいくつかの形式があります。

5

GCCがアセンブラにコンパイルします。他のコンパイラではそうではありません。たとえば、LLVM-GCCはLLVMアセンブリまたはLLVMバイトコードにコンパイルされ、その後マシンコードにコンパイルされます。ほとんどすべてのコンパイラにはある種の内部表現があり、LLVM-GCCはLLVMを使用し、IIRC、GCCはGIMPLEと呼ばれるものを使用します。

0

JavaコンパイラはJavaバイトコード(バイナリ形式)にコンパイルし、仮想マシン(jvm)を使用してこれを実行します。

これは遅く見えるかもしれませんが、JVMが後でCPU命令と新しい最適化を利用できるため、速度が遅くなる可能性があります。 C++コンパイラはこれをしません。コンパイル時に命令セットをターゲットにする必要があります。

14

gccを含むほぼすべてのコンパイラは、コンパイラの生成とデバッグの両方が容易なため、アセンブリコードを生成します。主な例外は、通常ジャストインタイムコンパイラまたはインタラクティブコンパイラです。その作者は、パフォーマンスオーバヘッドや、プロセス全体をフォークしてアセンブラを実行したくないという混乱を避けます。いくつかの興味深い例は、対話的に実行され、その場ですべての式をコンパイル

  • Standard ML of New Jerseyを、含まれています。

  • tinycc compilerは、Cスクリプトをコンパイル、ロード、実行するのに十分速く、100ミリ秒未満で実行できるように設計されているため、アセンブラとリンカを呼び出すオーバーヘッドは不要です。

これらのケースで共通しているのは、「瞬間的な」応答が望まれることです。アセンブラとリンカは十分に高速ですが、対話的な応答には十分ではありません。それでも。

Smalltalk、Java、Luaなどの大規模なファミリもありますが、アセンブラコードではなくバイトコードにコンパイルされますが、その実装ではあとでアセンブラを使用せずにバイトコードを機械コードに直接変換する可能性があります。

(脚注:1990年代初頭には、メアリー・フェルナンデスと私はcodeは、コンパイラの作家は、標準的なアセンブラとリンカをバイパスするために使用できるC  ライブラリを生成する、オンラインであるため、New Jersey Machine Code Toolkitを書いたメアリーは大体にそれを使用。 a.outを生成するときに彼女の最適化リンカーの速度を倍にします。ディスクに書き込まないと、スピードアップはさらに大きくなります...)

1

コンパイルのフェーズは数多くあります。要約すると、ソースコードを読み取ってトークンに分解し、最終的に解析木にするフロントエンドがあります。翻訳、それを最適化すると

reg1 = y + z 
x = reg1 + w 

コード:

x = y + z + w 

バックエンドは、最初の3つのアドレスコードなどのようなシーケンシャルコードを生成するための責任があります最後に機械語に変換します。必要なときにすべてのコンパイラは、中間レベルのコードにソースコードを変換していないものの、そのうちの一つが

0

を交換することができるように、すべての手順は慎重に積層されているが、いくつかのコンパイラでマシンレベルのコードにソースコードを取るの橋があります回答の

2

なしアセンブラがBINARY CODEや機種依存SYMBOLIC CODE間に抽象化の第一層であることを明確にしていません。コンパイラは、機械依存シンボルコードと機械独立シンボルコードの間の第2の抽象レイヤーです。

コンパイラは直接定義により、バイナリコードにコードを変換した場合、それはアセンブラではなく、コンパイラと呼ばれます。

コンパイラが、またはアセンブリ言語などであってもなくてもよい中間コードを使用すると言うことがより適切ですJavaはバイトコードを中間コードとして使用し、バイトコードはJava仮想マシン(JVM)のアセンブラです。

EDIT:アセンブラは、常にマシン依存のコードを生成し、なぜコンパイラは、マシンに依存しないコードを生成することが可能であるなぜあなたは不思議に思うかもしれません。答えは非常に簡単です。アセンブラはマシンコードを直接マッピングするため、生成するアセンブリ言語は常にマシンに依存します。それどころか、異なるマシンに複数のバージョンのコンパイラを書くことができます。したがって、マシンとは独立してコードを実行するには、同じコードをコンパイルする必要がありますが、そのマシン用に作成されたコンパイラバージョンでコンパイルする必要があります。

関連する問題