web-dev-qa-db-ja.com

Cとアセンブラは実際に何にコンパイルしますか?

だから私はC(++)プログラムが実際にはプレーンな「バイナリ」にコンパイルされないことを知りました(私はここで何か間違ったことをしているかもしれません、その場合私は申し訳ありません:D)がさまざまなこと(シンボルテーブル) 、os関連のもの、...)しかし...

  • アセンブラは純粋なバイナリに「コンパイル」しますか?これは、事前定義された文字列などのリソース以外に余分なものがないことを意味します。

  • Cがプレーンバイナリ以外のものにコンパイルする場合、その小さなアセンブラブートローダーはどのようにしてHDDからメモリに命令をコピーして実行するのでしょうか。おそらくCで書かれているOSカーネルがプレーンバイナリとは異なるものにコンパイルされる場合、ブートローダーはそれをどのように処理しますか?

編集:私はアセンブラが「コンパイル」しないことを知っています。これは、マシンの命令セットしか持っていないためです。ある場合は、ここにコメントとして残してください。変更します。

47
lamas

Cは通常、アセンブラーにコンパイルされます。これは、貧弱なコンパイラー作成者にとって簡単なことです。

アセンブリコードは常に再配置可能なオブジェクトコードにアセンブルします( "コンパイル"ではありません)。これは、バイナリマシンコードとバイナリデータと考えることができますが、装飾とメタデータがたくさん含まれています。重要な部分は次のとおりです。

  • コードとデータは名前付きの「セクション」に表示されます。

  • 再配置可能オブジェクトファイルには、セクション内の場所を参照するlabelsの定義が含まれている場合があります。

  • 再配置可能なオブジェクトファイルには、他の場所で定義されたラベルの値で埋められる「穴」が含まれる場合があります。そのような穴の正式名称はrelocation entryです。

たとえば、このプログラムをコンパイルしてアセンブルする(ただしリンクしない)場合

int main () { printf("Hello, world\n"); }

再配置可能なオブジェクトファイルを使用して

  • textのマシンコードを含むmainセクション

  • テキストセクションの先頭を指すmainのラベル定義

  • 文字列リテラル"Hello, world\n"のバイトを含むrodata(読み取り専用データ)セクション

  • printfに依存し、テキストセクションの中央にある呼び出し命令の「穴」を指す再配置エントリ。

Unixシステムを使用している場合、再配置可能なオブジェクトファイルはhello.oのように一般に.oファイルと呼ばれ、nmと呼ばれるシンプルなツールでラベルの定義と使用を調べることができます。 objdumpと呼ばれるやや複雑なツールからより詳細な情報を取得できます。

私はこれらのトピックをカバーするクラスを教えており、学生にアセンブラーとリンカーを作成させています。これには数週間かかりますが、彼らがそれを終えると、それらのほとんどは再配置可能なオブジェクトコードをかなりうまく処理します。それはそんなに簡単なことではありません。

46
Norman Ramsey

Cプログラムを取ってみましょう。

Cプログラムでgccclang、または 'cl'を実行すると、次の段階を経ます。

  1. プリプロセッサー(#include、#ifdef、trigraph分析、エンコーディング変換、コメント管理、マクロ...)プリプロセッサートークンへの字句解析を含み、最終的には適切なコンパイラーへの入力用のフラットテキストになります。
  2. 字句解析(トークンと字句エラーの生成)。
  3. 構文解析(解析ツリーと構文エラーの生成)。
  4. セマンティック分析(シンボルテーブルの作成、スコープ情報、スコープ/タイプエラー)データフロー。プログラムロジックを、オプティマイザが使用できる「中間表現」に変換します。 (しばしば [〜#〜] ssa [〜#〜] )。 clang/LLVMはLLVM-IRを使用し、gccはGIMPLE、次にRTLを使用します。
  5. 定数伝播、インライン化、ループからの不変量のホイスト、自動ベクトル化など、プログラムロジックの最適化。 (広く使用されている最新のコンパイラのコードの大部分は最適化パスです。)中間表現による変換は、一部のコンパイラの動作の一部にすぎないため、 (すべての最適化を無効にすることは不可能/無意味)
  6. アセンブリソースへの出力(または 。NET ILバイトコード などの別の中間形式)
  7. アセンブリをいくつかのバイナリオブジェクト形式に組み立てます。
  8. 必要な静的ライブラリへのアセンブリのリンク、および必要に応じて再配置。
  9. Elf、PE/coff、MachO64、またはその他の形式での最終的な実行可能ファイルの出力

実際には、これらの手順の一部は同時に実行できますが、これは論理的な順序です。ほとんどのコンパイラには、GCCなどのオープンソースコンパイラの最適化パス間で内部表現をダンプするなど、特定のステップ(プリプロセスやasmなど)の後に停止するオプションがあります。 (-ftree-dump-...

実際の実行可能バイナリの周りには、DOS .com実行可能ファイルでない限り、elfまたはcoff形式の「コンテナ」があることに注意してください。

あなたはコンパイラに関する本(私は ドラゴン 本、フィールドの標準的な紹介本をお勧めします)はall必要な情報など。

マルコがコメントしたように、リンクとロードは大きな領域であり、ドラゴンブックは実行可能バイナリの出力で多かれ少なかれ停止します。実際にそこからオペレーティングシステムで実行するのは、かなり複雑なプロセスです。Levineの Linkers and Loaders で説明しています。

私はこの答えをwikiして、人々がエラーを微調整したり情報を追加したりできるようにしました。

37
Paul Nathan

C++をバイナリ実行可能ファイルに変換するには、さまざまなフェーズがあります。言語仕様は、翻訳フェーズを明示的に述べていません。ただし、一般的な翻訳フェーズについて説明します。

ソースC++からアセンブリまたは反復言語へ

一部のコンパイラは、実際にはC++コードをアセンブリ言語または中間言語に変換します。これは必須のフェーズではありませんが、デバッグと最適化に役立ちます。

オブジェクトコードへのアセンブリ

次の一般的な手順は、アセンブリ言語をオブジェクトコードに変換することです。オブジェクトコードには、相対アドレスを持つアセンブリコードと、外部サブルーチン(メソッドまたは関数)へのオープン参照が含まれています。一般に、トランスレータはできるだけ多くの情報をオブジェクトファイルに入れます。それ以外はすべてnresolvedです。

オブジェクトコードのリンク

リンクフェーズでは、1つ以上のオブジェクトコードを組み合わせ、参照を解決し、重複するサブルーチンを排除します。最終出力は実行可能ファイルです。このファイルには、オペレーティングシステムと相対アドレスの情報が含まれています。

バイナリファイルの実行

オペレーティングシステムは、通常はハードドライブから実行可能ファイルをロードし、メモリに配置します。 OSは相対アドレスを物理的な場所に変換する場合があります。 OSは、実行可能ファイル(実行可能ファイルに記述されている場合があります)に必要なリソース(DLLやGUIウィジェットなど)も準備します。

バイナリに直接コンパイルするEmbedded Systemsで使用されているものなど、一部のコンパイラには、C++から直接実行可能なバイナリコードにコンパイルする機能があります。このコードは、相対アドレスではなく物理アドレスを持ち、OSのロードを必要としません。

メリット

これらのフェーズの利点の1つは、C++プログラムを分割して個別にコンパイルし、後でリンクできることです。他の開発者(別名ライブラリ)の一部とリンクすることもできます。これにより、開発者は開発中のコンパイラ部分のみをリンクし、すでに検証済みの部分でリンクできます。一般に、C++からオブジェクトへの変換は、プロセスの時間のかかる部分です。また、ソースコードにエラーがある場合、すべてのフェーズが完了するのを待ちたくありません。

オープンマインドを保ち、常にThird Alternative(Option)を期待します。

18
Thomas Matthews

プロセッサ、プラットフォーム、アセンブラ、Cコンパイラが異なるため、これは主観的なものであることに注意してください。この場合は、Intel x86プラットフォームについて説明します。

  1. アセンブラは純粋なバイナリにコンパイルされません。これらは生のマシンコードであり、データ、テキスト、bssなどのセグメントで定義されていますが、これはオブジェクトコードと呼ばれます。リンカーは、ステップインしてセグメントを調整し、実行可能、つまり実行の準備をします。ちなみに、gccを使用してコンパイルするときのデフォルトの出力は「a.out」です。これは、アセンブラー出力の省略形です。
  2. ブートローダーには特別なディレクティブが定義されており、DOSの時代には、.Org 100hなどのディレクティブを見つけるのが一般的です。人気。また、MSDOSに付属の古いdebug.exeを使用して.COMファイルを生成するためにアセンブラーを用意する必要はなく、小さな単純なプログラムのトリックを実行しました。COMファイルはリンカーを必要とせず、すぐに使用できました。実行バイナリ形式。 DEBUGを使用した簡単なセッションを次に示します。
 1:* a 0100 
 2:* mov AH、07 
 3:* int 21 
 4:* cmp AL、00 
 5 :* jnz 010c 
 6:* mov AH、07 
 7:* int 21 
 8:* mov AH、4C 
 9:* int 21 
 10:* 
 11:* r CX 
 12:* 10 
 13:* n respond.com 
 14:* w 
 15:* q 

これにより、キーストロークを待ち、画面にエコーしない「respond.com」と呼ばれる実行可能な.COMプログラムが生成されます。最初に、命令ポインタが.COMの機能である100hから始まることを示す「a 100h」の使用に注意してください。この古いスクリプトは主に、応答を待機してそれをエコーし​​ないバッチファイルで使用されていました。元のスクリプトは here にあります。

繰り返しになりますが、ブートローダーの場合、バイナリ形式に変換されます。DOSに付属していた EXE2BIN というプログラムがありました。これは、生のオブジェクトコードを、起動用の起動可能なディスクにコピーできる形式に変換する作業でした。リンカはランタイム環境用であり、実行可能および実行可能にするようにコードを設定するため、アセンブルされたコードに対してリンカが実行されることはありません。

起動時のBIOSは、コードがセグメント:オフセット、0x7c00にあると想定しています。メモリが正しく機能している場合、コード(EXE2BINが実行された後)が実行を開始し、ブートローダーがメモリ内で下に移動してロードを続行します。ディスクから読み取るためにint 0x13を発行し、A20ゲートをオンにし、DMAを有効にし、BIOSが16ビットモードのときにプロテクトモードに切り替えます。その後、ディスクから読み取られたデータがメモリに読み込まれ、ブートローダーがファージャンプを発行します。データコード(Cで記述される可能性が高い)。それは本質的にシステムが起動する方法です。

さて、前の段落は抽象的で単純に聞こえますが、見落としているかもしれませんが、それは一言で言えばそうです。

よろしくお願いします、トム。

3
t0mm13b

ここでは、2つの要素を組み合わせることができます。通常、次の2つのトピックがあります。

後者は、アセンブリの過程で前者にコンパイルされる場合があります。一部の中間形式はアセンブルされず、仮想マシンによって実行されます。 C++の場合、CILにコンパイルできます。これは.NETアセンブリにアセンブルされるため、混乱が生じます。

しかし、一般的にCとC++は通常、バイナリに、つまり実行可能ファイル形式にコンパイルされます。

1

読み聞かせるべき答えはたくさんありますが、私はこの簡潔さを保つことができると思います。

「バイナリコード」とは、マイクロプロセッサの回路を通過するビットを指します。マイクロプロセッサは、メモリから各命令を順番にロードし、彼らが言うことを何でもします。プロセッサフ​​ァミリが異なれば、命令の形式も異なります(x86、ARM、PowerPCなど)。プロセッサに、メモリ内の命令のアドレスを指定することにより、必要な命令を指定します。その後、プログラムの残りの部分でプロセッサを陽気に動かします。

プログラムをプロセッサにロードする場合は、最初にバイナリコードをメモリ内でアクセスできるようにして、最初にアドレスを設定する必要があります。 Cコンパイラは、ファイルシステムにファイルを出力します。このファイルは、新しい仮想アドレス空間にロードする必要があります。したがって、バイナリコードに加えて、そのファイルにはhasバイナリコードであるという情報と、そのアドレススペースがどのように見えるかを含める必要があります。

ブートローダーにはさまざまな要件があるため、ファイル形式は異なる場合があります。ただし、考え方は同じです。バイナリコードは常に、より大きなファイル形式のペイロードであり、正しい命令セットで記述されていることを確認するための最低限のチェックが含まれています。

Cコンパイラとアセンブラは通常、静的ライブラリファイルを生成するように構成されています。組み込みアプリケーションの場合、アドレス0から始まる命令を含む生のメモリイメージのようなものを生成するコンパイラを見つける可能性が高くなります。それ以外の場合は、Cコンパイラの出力を他の必要なものに変換するリンカーを作成できます。

1
Potatoswatter

これらは、ヘッダーとセグメントで構成される特定の形式(WindowsのCOFFなど)でファイルにコンパイルされます。これらの一部には、「プレーンバイナリ」のopコードがあります。アセンブラーとコンパイラー(Cなど)は、同じ種類の出力を作成します。古い* .COMファイルなどの一部の形式にはヘッダーがありませんでしたが、特定の前提条件がありました(メモリ内のどこに読み込まれるか、どれくらいの大きさになるかなど)。

Windowsマシンでは、OSのboostrapperはBIOSによってロードされたディスクセクターにあり、どちらも「プレーン」です。 OSがローダーをロードすると、ヘッダーとセグメントを持つファイルを読み取ることができます。

それは役に立ちますか?

1
Steven Sudit

質問のアセンブリの部分に答えるために、私が理解しているように、アセンブリはバイナリにコンパイルされません。アセンブリ===バイナリ。直接翻訳します。各アセンブリ操作には、直接一致するバイナリ文字列があります。各操作にはバイナリコードがあり、各レジ​​スタ変数にはバイナリアドレスがあります。

つまり、アセンブラ!=アセンブリと私があなたの質問を誤解していない限り。

1
Daniel Bingham

私が理解しているように、チップセット(CPUなど)には、データを格納するための一連のレジスタがあり、これらのレジスタを操作するための一連の命令を理解しています。指示は、「この値をこのレジスタに保存する」、「この値を移動する」、または「これら2つの値を比較する」のようなものになります。これらの命令は、人間が入力可能な短いアルファベットコード(アセンブリ言語、またはアセンブラ)で表現されることが多く、チップセットが理解できる数値にマッピングされます。これらの数値はバイナリ(マシンコード)でチップに提示されます。

これらのコードは、ソフトウェアが取得する最低レベルです。それよりも深く行くと、実際のチップのアーキテクチャに入りますが、これは私が関与していないものです。

0
Laizer

上記の答えはたくさんありますが、私はこれらのリソースを追加して、どうなるかを理解できるようにしたいと思いました。基本的に、WindowsおよびLinuxでは、誰かが可能な限り小さな実行可能ファイルを作成しようとしました。 Linux、ELF、Windows、PE。

どちらも削除された内容と理由を実行し、アセンブラーを使用して-felfのようなオプションを使用せずにELFファイルを構築します。

お役に立てば幸いです。

編集-また、truecrypt http://www.truecrypt.org のようなブートローダーのアセンブリを確認したり、grubの「stage1」(実際に書き込まれるビット) MDR)。

0
user257111

PEローダーがメモリにないため、実行可能ファイル(WindowsではPE形式)を使用してコンピューターを起動することはできません。

ブートストラップが機能する方法は、ディスク上のマスターブートレコードに数百バイトのコードのブロブが含まれていることです。コンピュータのBIOS(ROM内))は、このBLOBをメモリにロードし、CPU命令ポインタをこのブートコードの先頭に設定します。

次に、ブートコードは、WindowsでNTLDR(拡張子なし)と呼ばれる「第2ステージ」ローダーをルートディレクトリからロードします。これは、MBRローダーと同様に、コールドメモリにロードされて実行される生のマシンコードです。

NTLDRには、DLLやドライバーを含むPEファイルをロードするための完全な機能があります。

0
Tyler Durden