第2章 C または C++ アプリケーションの作成
2.1. GCC でのビルドコード
ソースコードを実行可能コードに変換する必要がある状況について説明します。
2.1.1. コード形式間の関係
前提条件
- コンパイルとリンクの概念を理解している。
考えられるコード形式
C 言語および C++ 言語には、以下の 3 つのコード形式があります。
C 言語または C++ 言語で記述された ソースコード。プレーンテキストファイルとして表示されます。
このファイルは通常、
.c
、.cc
、.cpp
、.h
、.hpp
、.i
、.inc
などの拡張子を使用します。サポートされる拡張子およびその解釈のリストは、gcc の man ページを参照してください。$ man gcc
コンパイラー でソースコードを コンパイルして作成する オブジェクトコード。これは中間形式です。
オブジェクトコードファイルは、拡張子
.o
を使用します。リンカー でオブジェクトコードをリンク して作成する 実行可能なコード。
Linux アプリケーションの実行可能ファイルは、ファイル名の拡張子を使用しません。共有オブジェクト (ライブラリー) の実行可能ファイルは、
.so
のファイル名の拡張子を使用します。
静的リンク用のライブラリーアーカイブファイルも存在します。これは、ファイル名拡張子 .a
を使用するオブジェクトコードのバリアントです。静的リンクは推奨されません。「静的リンクおよび動的リンク」 を参照してください。
GCC でのコード形式の処理
ソースコードから実行可能なコードを生成するには、2 つの手順を行います。必要となるアプリケーションまたはツールはそれぞれ異なります。GCC は、コンパイラーとリンカーのどちらにも、インテリジェントドライバーとして使用できます。これにより、必要なアクション (コンパイルおよびリンク) に gcc
コマンドを 1 つ使用できます。GCC は、アクションとそのシーケンスを自動的に選択します。
- ソースファイルを、オブジェクトファイルにコンパイルする
- オブジェクトファイルおよびライブラリーはリンクされます (以前にコンパイルしたソールも含む)。
GCC を実行して、1 つのステップでコンパイルのみ、リンクのみ、またはコンパイルとリンクの両方を実行できます。これは、入力タイプや必要とされる出力タイプにより決定します。
大規模なプロジェクトには、アクションごとに個別に GCC を実行するビルドシステムが必要なため、GCC が両方同時に実行できる場合でも 2 つの異なるアクションとしてコンパイルとリンクを実行することを検討することが推奨されます。
2.1.2. オブジェクトコードへのソースファイルのコンパイル
オブジェクトコードファイルを、実行ファイルから直接作成するのではなく、ソースファイルから作成するには、GCC で、オブジェクトコードファイルのみを出力として作成するように必要があります。このアクションは、大規模なプロジェクトのビルドプロセスの基本操作となります。
前提条件
- C または C++ のソースコードファイルがある
- GCC をシステムにインストールしておく。
手順
- ソースコードファイルが含まれるディレクトリーに移動します。
-c
オプションを指定してgcc
を実行します。$ gcc -c source.c another_source.c
オブジェクトファイルは、オリジナルのソースコードファイルを反映したファイル名を使用して作成されます。
source.c
はsource.o
になります。注記C++ ソースコードの場合は、標準 C++ ライブラリーの依存関係を処理しやすくするために、
gcc
コマンドをg++
に置き換えます。
2.1.3. GCC で C および C++ のアプリケーションのデバッグの有効化
デバッグの情報が大きいと、デフォルトでは実行ファイルが含まれません。GCC を使用した C および C++ のアプリケーションのデバッグを有効にするには、ファイルを作成するように、コンパイラーに明示的に指定する必要があります。
コードのコンパイルおよびリンク時に、GCC でデバッグ情報の作成を有効にするには、-g
オプションを使用します。
$ gcc ... -g ...
-
コンパイラーとリンカーで最適化を実行すると、元のソースコードと関連付けることが難しい実行可能コードが生成される場合があります。変数が最適化されたり、ループがアンロールされたり、操作が周囲の操作にマージされたりする可能性があります。これにより、デバッグに負の影響が及ぶ可能性があります。デバッグの体験を向上するには、
-Og
オプションを指定して、最適化を設定することを考慮してください。ただし、最適化レベルを変更すると、実行可能なコードが変更になり、バグを取り除くための動作が変更する可能性があります。 -
デバッグ情報にマクロ定義も追加するには、
-g
の代わりに-g3
オプションを使用します。 -
GCC オプション
-fcompare-debug
では、GCC でコンパイルしたコードを、デバッグ情報を使用して (または、デバッグ情報を使用せずに) テストします。このテストでは、出力されたバイナリーファイルの 2 つが同一であれば合格します。このテストを行うことで、実行可能なコードがデバッグオプションによる影響は受けないようにするだけでなく、デバッグコードにバグが含まれないようにします。-fcompare-debug
オプションを使用するとコンパイルの時間が大幅に伸びることに留意してください。このオプションに関する詳細は、GCC の man ページを参照してください。
関連情報
- 「デバッグ情報を使用したデバッグの有効化」
- GNU コンパイラーコレクション (GCC) の使用 - Options for Debugging Your Program
- GDB を使用したデバッグ - Debugging Information in Separate Files
GCC の man ページ:
$ man gcc
2.1.4. GCC でのコードの最適化
1 つのプログラムは、複数の機械語命令シーケンスに変換できます。コンパイル時にコードを分析するためにより多くのリソースを割り当てると、より最適な結果が得られます。
GCC では、-Olevel
オプションを使用して最適化レベルを設定できます。このオプションでは、level の部分に値を指定できます。
レベル | 説明 |
---|---|
| コンピレーション速度の最適化 - コードの最適化なし (デフォルト) |
| 最適化して、コード実行速度を向上させます (数値が大きいほど、速度は高くなります)。 |
| ファイルサイズを最適化します。 |
|
レベルを |
| デバッグ作業の最適化 |
リリースビルドの最適化オプションは -O2
です。
開発中は、場合によってはプログラムやライブラリーのデバッグを行えるように、-Og
オプションが便利です。バグによっては、特定の最適化レベルでのみ出現するため、リリースの最適化レベルでプログラムまたはライブラリーをテストしてください。
GCC では、個別の最適化を有効にするオプションが多数含まれています。詳細情報は、以下の関連資料を参照してください。
関連情報
- GNU コンパイラーコレクションの使用: Options That Control Optimization
GCC の Linux man ページ:
$ man gcc
2.1.5. GCC でコードを強化するオプション
コンパイラーで、ソースコードをオブジェクトコードに変換する場合には、さまざまなチェックを追加して、一般的に悪用される状況などを回避し、セキュリティーを強化できます。適切なコンパイラーオプションセットを選択すると、ソースコードを変更せずに、よりセキュアなプログラムやライブラリーを作成できます。
リリースバージョンのオプション
Red Hat Enterprise Linux を使用する開発者には、以下のオプションリストが推奨される最小限のオプションとなります。
$ gcc ... -O2 -g -Wall -Wl,-z,now,-z,relro -fstack-protector-strong -fstack-clash-protection -D_FORTIFY_SOURCE=2 ...
-
プログラムには、
-fPIE
および-pie
の位置独立実行形式オプションを追加します。 -
動的にリンクされたライブラリーには、必須の
-fPIC
(位置独立コード) オプションを使用すると間接的にセキュリティーが強化されます。
開発オプション
開発時にセキュリティーの欠陥を検出する場合は、以下のオプションを使用します。このオプションは、リリースバージョンのオプションと合わせて使用してください。
$ gcc ... -Walloc-zero -Walloca-larger-than -Wextra -Wformat-security -Wvla-larger-than ...
関連情報
- Defensive Coding Guide
- Memory Error Detection Using GCC - Red Hat 開発者のブログ投稿
2.1.6. 実行ファイルを作成するコードのリンク
C または C++ のアプリケーション構築の最後の手順は、リンクです。リンクにより、オブジェクトファイルやライブラリーがすべて実行可能ファイルに統合されます。
前提条件
- オブジェクトファイルが 1 つまたは複数ある。
- GCC がシステムにインストールされている。
手順
- オブジェクトコードファイルを含むディレクトリーに移動します。
gcc
を実行します。$ gcc ... objfile.o another_object.o ... -o executable-file
executable-file という名前の実行ファイルが、指定したオブジェクトファイルとライブラリーをベースに作成されます。
追加のライブラリーをリンクするには、オブジェクトファイルのリストの前に、必要なオプションを追加します。詳細は、「GCC でのライブラリーの使用」 を参照してください。
注記C++ ソースコードの場合は、標準 C++ ライブラリーの依存関係を処理しやすくするために、
gcc
コマンドをg++
に置き換えます。
2.1.7. 例: GCC で C プログラムの構築 (1 つの手順でコンパイルとリンク)
以下の例では、簡単な C++ のサンプルプログラムを構築する手順を説明します。
この例では、コードをコンパイルおよびリンクする方法を 1 つの手順で行います。
前提条件
- GCC の使用方法を理解している。
手順
hello-c
ディレクトリーを作成して、そのディレクトリーに移動します。$ mkdir hello-c $ cd hello-c
以下の内容を含む
hello.c
ファイルを作成します。#include <stdio.h> int main() { printf("Hello, World!\n"); return 0; }
GCC でコードをコンパイルし、リンクします。
$ gcc hello.c -o helloworld
これにより、コードがコンパイルされ、オブジェクトファイル
hello.o
が作成され、オブジェクトファイルから実行ファイルhelloworld
がリンクされます。作成された実行可能ファイルを実行します。
$ ./helloworld Hello, World!
2.1.8. 例: GCC を使用した C プログラムの構築 (2 つの手順でコンパイルとリンク)
以下の例では、簡単な C++ のサンプルプログラムを構築する手順を説明します。
この例では、コードのコンパイルとリンクは、2 つの別個のステップです。
前提条件
- GCC の使用方法を理解している。
手順
hello-c
ディレクトリーを作成して、そのディレクトリーに移動します。$ mkdir hello-c $ cd hello-c
以下の内容を含む
hello.c
ファイルを作成します。#include <stdio.h> int main() { printf("Hello, World!\n"); return 0; }
GCC でコードをコンパイルします。
$ gcc -c hello.c
オブジェクトファイル
hello.o
が作成されます。オブジェクトファイルから作成した実行可能ファイル
helloworld
をリンクします。$ gcc hello.o -o helloworld
作成された実行ファイルを実行します。
$ ./helloworld Hello, World!
2.1.9. 例: GCC で C++ プログラムの構築 (1 つの手順でコンパイルとリンク)
以下の例では、最小限の C++ プログラムのサンプルを構築する手順を説明します。
この例では、コードをコンパイルおよびリンクする方法を 1 つの手順で行います。
前提条件
-
gcc
とg++
の相違点を理解している。
手順
hello-cpp
ディレクトリーを作成して、そのディレクトリーに移動します。$ mkdir hello-cpp $ cd hello-cpp
以下の内容を含む
hello.cpp
ファイルを作成します。#include <iostream> int main() { std::cout << "Hello, World!\n"; return 0; }
g++
でコードをコンパイルし、リンクします。$ g++ hello.cpp -o helloworld
これにより、コードがコンパイルされ、オブジェクトファイル
hello.o
が作成され、オブジェクトファイルから実行ファイルhelloworld
がリンクされます。作成された実行可能ファイルを実行します。
$ ./helloworld Hello, World!
2.1.10. 例: GCC を使用した C++ プログラムの構築 (2 つの手順でコンパイルとリンク)
以下の例では、最小限の C++ プログラムのサンプルを構築する手順を説明します。
この例では、コードのコンパイルとリンクは、2 つの別個のステップです。
前提条件
-
gcc
とg++
の相違点を理解している。
手順
hello-cpp
ディレクトリーを作成して、そのディレクトリーに移動します。$ mkdir hello-cpp $ cd hello-cpp
以下の内容を含む
hello.cpp
ファイルを作成します。#include <iostream> int main() { std::cout << "Hello, World!\n"; return 0; }
g++
でコードをコンパイルします。$ g++ -c hello.cpp
オブジェクトファイル
hello.o
が作成されます。オブジェクトファイルから作成した実行可能ファイル
helloworld
をリンクします。$ g++ hello.o -o helloworld
作成された実行可能ファイルを実行します。
$ ./helloworld Hello, World!