第15章 GCC でのビルドコード

本章では、ソースコードを実行可能なコードに変換する必要のある状況を扱います。

15.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 は自動的に必要なアクション (コンパイルおよびリンク) とそのシーケンスを選択します。

  1. ソースファイルは、オブジェクトファイルにコンパイルされます。
  2. オブジェクトファイルおよびライブラリーはリンクされます (以前にコンパイルしたソールも含む)。

ステップ 1 だけ、ステップ 2 だけ、ステップ 1 と 2 両方というように、GCC を実行することができます。これは、入力タイプや要求する出力タイプにより決定されます。

大規模なプロジェクトには、アクション毎に個別に GCC を実行するビルドシステムが必要なため、GCC が両方同時に実行できる場合でも 2 つの異なるアクションとしてコンパイルとリンクを実行するように検討するほうが良いでしょう。

関連資料

15.2. ソースファイルのオブジェクトコードへのコンパイル

実行可能ファイルから直接作成するのではなく、ソースファイルからオブジェクトコードファイルを作成するには、 GCC にオブジェクトコードファイルのみを出力として作成するように指示する必要があります。このアクションは、大規模なプロジェクトのビルドプロセスの基本操作となります。

前提条件

ステップ

  1. ソースコードファイルが含まれるディレクトリーに移動します。
  2. -c オプションを指定して gcc を実行します。

    $ gcc -c source.c another_source.c

    オブジェクトファイルは、オリジナルのソースコードファイルを反映したファイル名を使用して作成されます。 source.csource.o になります。

    注記

    C++ ソースコードの場合は、標準 C++ ライブラリーの依存関係を都合よく処理するために、gcc コマンドを g++ に置き換えてください。

関連資料

15.3. GCC を使用した C および C++ アプリケーションのデバッグの有効化

デバッグの情報が大きい場合には、デフォルトでは実行可能ファイルは含まれません。GCC を使用した C および C++ アプリケーションのデバッグを有効化するには、コンパイラーに対して、ファイルを作成するように、明示的に指示する必要があります。

GCC を使用したデバッグ情報の作成の有効化

コードのコンパイルおよびリンク時には GCC でデバッグ情報の作成を有効化するには、-g オプションを使用します。

$ gcc ... -g ...
  • コンパイラーとリンカーで最適化を行うと、実行可能なコードを、元のソースコードと関連付けることが難しくなります。変数の最適化、ループのアンロール、周りの操作へのマージなどが行われる可能性があります。デバッグの体験を向上するには、-Og オプションを指定して、最適化を設定することを考慮してください。ただし、最適化レベルを変更すると、実行可能なコードが変更され、バグを取り除くための実際の動作が変更される可能性があります。
  • -fcompare-debug GCC オプションでは、GCC でコンパイルしたコードを、デバッグ情報を使用して (または、デバッグ情報を使用せずに) テストします。このテストでは、出力されたバイナリーファイル 2 つが同一であれば合格します。このテストを行うことで、実行可能なコードはデバッグオプションによる影響は受けないようにするだけでなく、デバッグコードにバグが含まれないようにします。-fcompare-debug オプションを使用するとコンピレーションの時間が大幅に伸びます。このオプションに関する詳細情報は、GCC の man ページを参照してください。

関連資料

15.4. GCC でのコードの最適化

1 つのプログラムは、複数の機械語命令シーケンスに変換可能です。コンパイル時にコード分析用のリソースがより多く割り当てられると、より最適な結果が得られます。

GCC でのコードの最適化

GCC では、-Olevel オプションを使用して最適化レベルを設定できます。このオプションでは、level の部分に各種値を指定することができます。

レベル説明

0

コンピレーション速度の最適化: コードの最適化なし (デフォルト)

123

コード実行速度を高めるための最適化の作業量増加

s

作成されるファイルサイズの最適化

fast

レベル 3 以上は、厳密な標準準拠を無視して追加の最適化を可能にします

g

デバッグ作業の最適化

リリースビルドの場合の最適化オプションは、-O2 を推奨します。

開発中は、場合によってはプログラムやライブラリーのデバッグを行えるように -Og オプションのほうが便利です。バグによっては、特定の最適化レベルでのみ出現するので、リリースの最適化レベルでプログラムまたはライブラリーをテストするようにしてください。

GCC では、個別の最適化を有効にするオプションが多数含まれています。詳細情報は、以下の追加リソースを参照してください。

関連資料

15.5. GCC でのコードのハード化

コンパイラーで、ソースコードをオブジェクトコードに変換する場合には、さまざまなチェックを追加して、一般的に悪用される状況などを回避し、セキュリティーを強化することができます。適切なコンパイラーオプションセットを選択して、ソースコードを変更せずに、よりセキュアなプログラムやライブラリーを制作することができます。

リリースバージョンのオプション

Red Hat Enterprise Linux を使用する開発者には、以下のオプション一覧が最小限必要なオプションとなります。

$ gcc ... -O2 -g -Wall -Wl,-z,now,-z,relro -fstack-protector-strong -D_FORTIFY_SOURCE=2 ...
  • プログラムには、-fPIE および -pie の位置独立コードオプションを追加します。
  • 動的にリンクされたライブラリーには、必須の -fPIC (位置独立コード) オプションを使用すると間接的にセキュリティーが強化されます。

開発オプション

開発時にセキュリティーの欠陥を検出する場合には、以下のオプションを推奨します。これらのオプションは、リリースバージョンのオプションと合わせて使用してください。

$ gcc ... -Walloc-zero -Walloca-larger-than -Wextra -Wformat-security -Wvla-larger-than ...

関連資料

15.6. 実行可能なファイルを作成するためのコードのリンク

C または C++ アプリケーション構築の最後の手順は、リンクです。リンクをすることで、オブジェクトファイルやライブラリーをすべて実行可能なファイルに統合します。

前提条件

ステップ

  1. オブジェクトコードファイルを含むディレクトリーに移動します。
  2. gcc を実行します。

    $ gcc ... objfile.o another_object.o ... -o executable-file

    executable-file という名前の実行可能なファイルが、指定したオブジェクトファイルとライブラリーをベースに作成されます。

    追加のライブラリーををリンクするには、オブジェクトファイルの一覧の前に、必要なオプションを追加します。「16章GCC でのライブラリーの使用」を参照してください。

    注記

    C++ ソースコードの場合は、標準 C++ ライブラリーの依存関係を都合よく処理するために、gcc コマンドを g++ に置き換えてください。

関連資料

15.7. 各種 Red Hat 製品との C++ の互換性

Red Hat エコシステムには、Red Hat Enterprise Linux および Red Hat Developer Toolset で提供される、GCC コンパイラーおよびリンカーのバージョンが複数含まれます。これらのバージョン間の C++ ABI の互換性は以下のとおりです。

  • GCC 4.8 がベースになり、Red Hat Enterprise Linux 7 の一部として直接提供されている システムコンパイラー は、C++98 標準仕様 (C++03 としても知られています)、およびそのバリアント (GNU 拡張あり) へのコンパイルおよびリンクのみをサポートします。
  • -std=C++98 または -std=gnu++98 オプションで明示的に構築された C++98 準拠のバイナリーまたはライブラリーは、使用するコンパイラーのバージョンにかかわらず、自由に組み合わせることができます。
  • Red Hat Developer Toolset では、同じメジャーバージョンの GCC を使用して構築された適切なフラグで、すべての C++ オブジェクトがコンパイルされている場合に限り、C++11 および C++14 言語の使用および併用がサポートされます。
  • Red Hat Developer Toolset および Red Hat Enterprise Linux ツールチェーンで構築された C++ ファイルをリンクする場合には、Red Hat Developer Toolset バージョンのコンパイラーとリンカーが推奨されます。
  • Red Hat Enterprise Linux 6 および 7、そして Red Hat Developer Toolset バージョン 4.1 までのコンパイラーにおけるデフォルト設定は -std=gnu++98 です。つまり、GNU 拡張がある C++98 です。
  • Red Hat Developer Toolset 6、6.1、7、および 7.1 のコンパイラーにおけるデフォルト設定は -std=gnu++14 です。つまり、GNU 拡張がある C++14 です。

関連資料

15.8. 例: GCC での C プログラムの構築

以下の例では、最小限の C プログラムのサンプルを構築する手順を説明します。

前提条件

  • GCC の使用方法を理解していること

ステップ

  1. hello-c ディレクトリーを作成して、そのディレクトリーに移動します。

    $ mkdir hello-c
    $ cd hello-c
  2. 以下の内容を含む hello.c ファイルを作成します。

    #include <stdio.h>
    
    int main() {
      printf("Hello, World!\n");
      return 0;
    }
  3. GCC でコードをコンパイルします。

    $ gcc -c hello.c

    オブジェクトファイル hello.o が作成されます。

  4. オブジェクトファイルから作成した、実行可能なファイル helloworld をリンクします。

    $ gcc hello.o -o helloworld
  5. 作成された実行可能なファイルを実行します。

    $ ./helloworld
    Hello, World!

関連資料

15.9. 例: GCC での C++ プログラムの構築

以下の例では、最小限の C++ プログラムのサンプルを構築する手順を説明します。

前提条件

  • GCC の使用方法を理解していること
  • gccg++ の相違点を理解していること

ステップ

  1. hello-cpp ディレクトリーを作成して、そのディレクトリーに移動します。

    $ mkdir hello-cpp
    $ cd hello-cpp
  2. 以下の内容を含む hello.cpp ファイルを作成します。

    #include <iostream>
    
    int main() {
      std::cout << "Hello, World!\n";
      return 0;
    }
  3. g++ でコードをコンパイルします。

    $ g++ -c hello.cpp

    オブジェクトファイル hello.o が作成されます。

  4. オブジェクトファイルから作成した、実行可能なファイル helloworld をリンクします。

    $ g++ hello.o -o helloworld
  5. 作成された実行可能なファイルを実行します。

    $ ./helloworld
    Hello, World!