クリーム色の背景、色付き表示、高コントラスト、大きな文字のほうが読みやすいですか?配色と文字を選ぶと、すべてのページに一度で反映されます。

無料レッスン

ソースコードが動くプログラムになるまで(gcc)

このレッスンのあとで: コンパイラがプログラムを通す段階の名前を言える。

50分のレッスンコマンド8件コンパイラとアーキテクチャのラボ:gcc、flex、bison、RISC-V を1台の VM で

最後の一歩で止まったビルド

勤怠管理のプログラムをビルドすると、undefined reference to printf と出ました。同僚は C のコードを1行ずつ読み始めます。

でも、コードに誤りはありません。このエラーはビルドの最後の段階から出ています。その段階は、C のコードを読みません。段階を知っていれば、1時間を無駄にせずに済みました。

このレッスンのゴール

メモ

コンパイラがプログラムを通す段階の名前を言える。

重要な言葉

ことばいみれい
ドライバビルド用のほかのプログラムを、順に動かすプログラムgcc
プリプロセッサファイルを貼り込み、マクロを置き換える段階
アセンブラアセンブリを機械語に変えるプログラムas
オブジェクトファイルまだ1つのプログラムになっていない機械語hello.o
シンボルリンカが探す名前。関数の名前など
リンカオブジェクトファイルとライブラリをつなぐプログラムld

学生が入力するセルは、このプレビューでは空白です。

gcc はほかのプログラムを動かす

gcc hello.c と打っても、gcc 自身はほとんど何もしません。gcc はドライバです。ほかのプログラムを順に動かし、前のプログラムが書いたファイルを次に渡します。

オプション -### を付けると、gcc は動かすはずのコマンドを表示するだけで、何も動かしません。下の2つの Run で、ラボのフォルダに入り、各コマンドが起動するプログラムの名前だけを取り出します。

コンパイラのフォルダに入る

コマンド
cd ~/compiler

ここでは表示のみで、実行されません。無料アカウントを作成すると、実行ボタンで自分のラボのターミナルに入力できます。

gcc が動かすはずのプログラム

コマンド
gcc -### -o hello hello.c 2>&1 | grep "^ " | cut -d " " -f 2

ここでは表示のみで、実行されません。無料アカウントを作成すると、実行ボタンで自分のラボのターミナルに入力できます。

例: 3つのプログラムと4つの段階

プログラム受け持つ段階書くもの
cc1前処理とコンパイルアセンブリ(.s ファイル)
asアセンブルオブジェクトファイル(.o)
collect2リンク(中で ld を動かす)実行できるプログラム

学生が入力するセルは、このプレビューでは空白です。

プリプロセッサはマクロを置き換える

hello.c には #define GREETING の行があります。これはマクロで、名前を文字列に置き換える約束です。

gcc -E は最初の段階で止まり、結果を画面に出します。#include の1行が数百行に増えるので、全部は読みません。grep で、squared を含む行だけを取り出します。

前処理のあとの printf の行

コマンド
gcc -E hello.c | grep squared

ここでは表示のみで、実行されません。無料アカウントを作成すると、実行ボタンで自分のラボのターミナルに入力できます。

すべての段階のファイルを残す

ふつう、段階の間のファイルは一時フォルダに置かれ、終わると消されます。-save-temps を付けると、ソースの隣に残ります。

1回のビルドで4つのファイルができます。前処理のあとの hello.i、コンパイルのあとの hello.s、アセンブルのあとの hello.o、リンクのあとの hello です。file はファイルの先頭を読んで、種類を答えます。

1回ビルドして、全部残す

コマンド
gcc -save-temps -o hello hello.c

ここでは表示のみで、実行されません。無料アカウントを作成すると、実行ボタンで自分のラボのターミナルに入力できます。

それぞれ何のファイルか

コマンド
file hello.i hello.s hello.o hello

ここでは表示のみで、実行されません。無料アカウントを作成すると、実行ボタンで自分のラボのターミナルに入力できます。

例: file の答え

ファイル作った段階file の答え(共通の部分)
hello.iプリプロセッサC source, ASCII text
hello.sコンパイラassembler source, ASCII text
hello.oアセンブラELF 64-bit LSB relocatable
helloリンカELF 64-bit LSB pie executable

学生が入力するセルは、このプレビューでは空白です。

オブジェクトファイルには穴がある

hello.o には、square と main の機械語が入っています。でも main が呼ぶ printf は、このファイルにありません。C のライブラリにあります。

nm は、オブジェクトファイルのシンボルを並べます。T は中身がここにあるという印です。U は未定義で、リンカが埋める穴です。

次に、C のライブラリを渡さずに、リンカの ld を自分で動かします。何と言うかを読みましょう。

hello.o のシンボル

コマンド
nm hello.o

ここでは表示のみで、実行されません。無料アカウントを作成すると、実行ボタンで自分のラボのターミナルに入力できます。

ライブラリなしでリンクする

コマンド
ld hello.o -o broken

ここでは表示のみで、実行されません。無料アカウントを作成すると、実行ボタンで自分のラボのターミナルに入力できます。

よくある間違い

注意

ビルドのエラーは、どれも自分のコードの誤りだと思いがちです。undefined reference はリンカから出ます。コードのコンパイルは、もう済んでいます。シンボルの中身が、最後の段階に渡されていないのです。C の行ではなく、何をリンクしたかを調べます。

printf はどこから来るか

コマンド
ldd hello

ここでは表示のみで、実行されません。無料アカウントを作成すると、実行ボタンで自分のラボのターミナルに入力できます。

さらに挑戦: 2つの段階のエラー

自分で打ちましょう。sed "s/square(7)/cube(7)/" hello.c > cube.c で、どこにもない関数 cube を呼ぶコピーを作ります。gcc -c cube.c を実行し、どの段階が答えたかを言いましょう。

次に、main の上に int cube(int n); の1行を足して、もう一度 gcc -c cube.c を実行します。nm cube.o を読み、最後に gcc cube.c を実行します。2つのエラーが違う段階から出る理由を説明しましょう。

このラボとソースについて: コンパイラとアーキテクチャのラボ:gcc、flex、bison、RISC-V を1台の VM で

授業で使いますか?このモジュールを学生に割り当てると、教員用ガイド、自動採点つきの課題、学生ごとのラボを使えます。