Red Hat Training

A Red Hat training course is available for RHEL 8

RHEL 8 でのアプリケーション開発

Red Hat Enterprise Linux 8

Red Hat Enterprise Linux 8 のアプリケーション開発ツールの概要

Red Hat Customer Content Services

概要

本書は、アプリケーション開発に最適なエンタープライズプラットフォームとして、Red Hat Enterprise Linux 8 を活用するさまざまな機能とユーティリティーを説明します。

Red Hat ドキュメントへのフィードバック

ご意見ご要望をお聞かせください。ドキュメントの改善点ございませんか。改善点を報告する場合は、以下のように行います。

  • 特定の文章に簡単なコメントを記入する場合は、ドキュメントが Multi-page HTML 形式になっているのを確認してください。コメントを追加する部分を強調表示し、そのテキストの下に表示される Add Feedback ポップアップをクリックし、表示された手順に従ってください。
  • より詳細なフィードバックを行う場合は、Bugzilla のチケットを作成します。

    1. Bugzilla の Web サイトにアクセスします。
    2. Component で Documentation を選択します。
    3. Description フィールドに、ドキュメントの改善に関するご意見を記入してください。ドキュメントの該当部分へのリンクも記入してください。
    4. Submit Bug をクリックします。

第1章 開発ワークステーションの設定

Red Hat Enterprise Linux 8 は、カスタムアプリケーションの開発に対応します。開発者がカスタムアプリケーションを開発できるように、必要なツールやユーティリティーを使用して、システムを設定する必要があります。本章では、開発で最も一般的なユースケースと、インストールする項目を紹介します。

1.1. 前提条件

  • グラフィカル環境のシステムがインストールされ、サブスクライブされている。

1.2. デバッグおよびソースのリポジトリーの有効化

Red Hat Enterprise Linux の標準インストールでは、デバッグリポジトリーおよびソースリポジトリーが有効になっていません。このリポジトリーには、システムコンポーネントのデバッグとパフォーマンスの測定に必要な情報が含まれます。

手順

  • ソースおよびデバッグの情報パッケージチャンネルを有効にします。

    # subscription-manager repos --enable rhel-8-for-$(uname -i)-baseos-debug-rpms
    # subscription-manager repos --enable rhel-8-for-$(uname -i)-baseos-source-rpms
    # subscription-manager repos --enable rhel-8-for-$(uname -i)-appstream-debug-rpms
    # subscription-manager repos --enable rhel-8-for-$(uname -i)-appstream-source-rpms

    $(uname -i) の部分は、システムのアーキテクチャーで一致する値に自動的に置き換えられます。

    アーキテクチャー名

    64 ビット Intel および AMD

    x86_64

    64 ビット ARM

    aarch64

    IBM POWER

    ppc64le

    IBM Z

    s390x

1.3. アプリケーションのバージョンを管理するための設定

複数の開発者が関わるプロジェクトではすべて、効果的なバージョン管理が必須になります。Red Hat Enterprise Linux には、Git という名前の分散型バージョン管理システムが同梱されています。

手順

  1. git パッケージをインストールします。

    # yum install git
  2. 任意で、Git コミットに関連付ける名前と、メールアドレスを設定します。

    $ git config --global user.name "Full Name"
    $ git config --global user.email "email@example.com"

    Full Nameemail@example.com を、お客様の名前とメールアドレスに置き換えます。

  3. 任意で、Git で開始するデフォルトのテキストエディターを変更するには、core.editor 設定オプションの値を設定します。

    $ git config --global core.editor command

    command を、テキストエディターを起動するのに使用するコマンドに置き換えます。

関連資料

  • Git およびチュートリアルの Linux の man ページ:

    $ man git
    $ man gittutorial
    $ man gittutorial-2

    多くの Git コマンドには、独自の man ページがあります。例は、git-commit(1) を参照してください。

  • Git ユーザーマニュアル - Git の HTML ドキュメントは /usr/share/doc/git/user-manual.html にあります。
  • Pro Git - オンライン版の Pro Git ブックでは、Git、概念、用途が詳細に説明されています。
  • Reference - オンライン版の Git の Linux man ページ

1.4. C および C++ でアプリケーションを開発するための設定

Red Hat Enterprise Linux には、C および C++ のアプリケーションを作成するツールが同梱されています。

前提条件

  • デバッグリポジトリーおよびソースリポジトリーが有効である。

手順

  1. GNU Compiler Collection (GCC)、GNU Debugger (GDB) などの開発ツールが含まれる Development Tools パッケージグループをインストールします。

    # yum group install "Development Tools"
  2. clang コンパイラー、lldb デバッガーなどの LLVM ベースのツールチェインをインストールします。

    # yum install llvm-toolset
  3. 必要に応じて、Fortran 依存関係用に、GNU Fortran コンパイラーをインストールします。

    # yum install gcc-gfortran

1.5. アプリケーションをデバッグするための設定

Red Hat Enterprise Linux には、内部のアプリケーションの動作を分析してトラブルシューティングを行うためのデバッグおよび計測のツールが同梱されています。

前提条件

  • デバッグリポジトリーおよびソースリポジトリーが有効である。

手順

  1. デバッグに役立つツールをインストールします。

    # yum install gdb valgrind systemtap ltrace strace
  2. debuginfo-install ツールを使用するには、yum-utils パッケージをインストールします。

    # yum install yum-utils
  3. 環境設定用の SystemTap ヘルパースクリプトを実行します。

    # stap-prep

    このスクリプトを実行すると、カーネルの debuginfo パッケージがインストールされます。このパッケージのサイズは 2 GiB を超えています。

  4. SELinux ポリシーで、関連するアプリケーションを正常に実行できるだけでなく、デバッグ状況でも実行できるようになっていることを確認してください。詳細は「SELinux の使用」を参照してください。

1.6. アプリケーションのパフォーマンスを測定するための設定

Red Hat Enterprise Linux には、開発者がアプリケーションのパフォーマンス低下の原因を特定できるように支援するアプリケーションが同梱されています。

前提条件

  • デバッグリポジトリーおよびソースリポジトリーが有効である。

手順

  1. パフォーマンス測定用のツールをインストールします。

    # yum install perf papi pcp-zeroconf valgrind strace sysstat systemtap
  2. 環境設定用の SystemTap ヘルパースクリプトを実行します。

    # stap-prep

    このスクリプトを実行すると、カーネルの debuginfo パッケージがインストールされます。このパッケージのサイズは 2 GiB を超えています。

  3. Performance Co-Pilot (PCP) コレクターサービスを有効にして開始します。

    # systemctl enable pmcd && systemctl start pmcd

第2章 RHEL 8 で、RHEL 6 または RHEL 7 のアプリケーションを実行する方法

Red Hat Enterprise Linux 8 で Red Hat Enterprise Linux 6 または 7 のアプリケーションを実行する場合は、さまざまな選択肢があります。システム管理者には、アプリケーション開発者の詳細なガイダンスが必要です。以下は、Red Hat が提供するオプション、検討事項、およびリソースの概要になります。

RHEL バージョンが一致するゲスト OS の仮想マシンでアプリケーションを実行する
このオプションではリソースコストが高まりますが、環境はアプリケーションの要件にほぼ一致しており、このアプローチでは検討事項がそれほど必要ありません。これは、現在推奨されるオプションです。
各 RHEL バージョンをベースにしたコンテナーでアプリケーションを実行する
リソースのコストは上記の場合よりも低くなりますが、設定要件はより厳しくなります。コンテナーホストとゲストのユーザー領域の関係の詳細は「Red Hat Enterprise Linux Container Compatibility Matrix」を参照してください。
RHEL 8 でアプリケーションをネイティブに実行する

このオプションは、リソースのコストが一番低くなりますが、要件が最も厳しくなります。アプリケーション開発者が、RHEL 8 システムの正しい設定を決定する必要があります。以下の資料は、開発者がこのタスクを行う際に役に立ちます。

上記は、アプリケーションの互換性を判断するのに必要な資料を網羅しているわけではありません。既知の非互換の変更や、互換性に関する Red Hat ポリシーに関する資料であり、出発点にしかなりません。

「kABI (Kernel Application Binary Interface) とは何ですか?」 も併せて参照してください。 ナレッジベースのアーティクルには、カーネルおよび互換性に関連する情報が記載されています。

パート I. C または C++ のアプリケーションの作成

Red Hat は、C 言語および C++ 言語を使用してアプリケーションを構築するツールを提供しています。本パートでは、最も一般的な開発タスクを一部記載します。

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

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

3.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. オブジェクトファイルおよびライブラリーをリンクする (以前にコンパイルしたソールも含む)。

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

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

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

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

前提条件

  • C または C++ のソースコードファイルがある。
  • GCC をシステムにインストールしておく。

手順

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

    $ gcc -c source.c another_source.c

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

    注記

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

3.3. GCC で C および C++ のアプリケーションのデバッグを有効にする

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

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

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

関連資料

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

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

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

レベル説明

0

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

123

最適化して、コード実行速度を向上させます (数値が大きいほど、速度は高くなります)。

s

ファイルサイズを最適化します。

fast

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

g

デバッグ作業の最適化

リリースビルドの最適化オプションは -O2 です。

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

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

関連資料

3.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 ...

関連資料

3.6. 実行ファイルを作成するコードのリンク

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

前提条件

  • オブジェクトファイルが 1 つまたは複数ある。
  • GCC がシステムにインストールされている。

手順

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

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

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

    追加のライブラリーをリンクするには、オブジェクトファイルの一覧の前に、必要なオプションを追加します。

    注記

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

3.7. 例: 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!

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

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

前提条件

  • 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!

第4章 GCC でのライブラリーの使用

この章では、コード内でのライブラリーの使用を説明します。

4.1. ライブラリーの命名規則

特別なファイルの命名規則をライブラリーに使用します。foo として知られるライブラリーは、libfoo.so ファイルまたは libfoo.a ファイルとして存在する必要があります。この規則は、リンクする GCC の入力オプションでは自動的に理解されますが、出力オプションでは理解されません。

  • ライブラリーにリンクする場合は、-lfoo のように、-l オプションと foo の名前でしか、ライブラリーを指定することができません。

    $ gcc ... -lfoo ...
  • ライブラリーの作成時には、libfoo.solibfoo.a など、完全なファイル名を指定する必要があります。

4.2. 静的リンクおよび動的リンク

開発者は、完全にコンパイルされた言語でアプリケーションを構築する際に、静的リンクまたは動的リンクを使用できます。本セクションでは、特に Red Hat Enterprise Linux で C 言語および C++ 言語を使用している場合のコンテキストの相違点を説明します。つまり、Red Hat は、Red Hat Enterprise Linux のアプリケーションで静的リンクを使用することは推奨していません。

静的リンクおよび動的リンクの比較

静的リンクは、作成される実行ファイルのライブラリーの一部になります。動的リンクは、このライブラリーを別のファイルとして維持します。

動的リンクおよび静的リンクは、いくつかの点で異なります。

リソースの使用

静的リンクは、より多くのコードを含むため、実行ファイルが大きくなります。ライブラリーからのこの追加コードは、システムのプログラム間で共有できないため、ランタイム時にファイルシステムの使用量とメモリー使用量が増加します。静的にリンクされた同じプログラムを実行している複数のプロセスがコードを共有します。

一方、静的アプリケーションは、必要なランタイムの再配置も少なくなるため、起動時間が短縮します。また、必要なプライベートの RSS (homeal Set Size) メモリーも少なくなります。静的リンク用に生成されたコードは、PIC (位置独立コード) により発生するオーバーヘッドにより、動的リンクよりも効率が良くなります。

セキュリティー
ABI 互換性を提供する、動的にリンクされたライブラリーは、そのライブラリーに依存する実行ファイルを変更せずに更新できます。これは、Red Hat Enterprise Linux の一部として Red Hat が提供するライブラリー (セキュリティー更新が提供される場所) で特に重要です。このようなライブラリーには、静的リンクを使用しないことが強く推奨されます。
互換性

静的リンクは、オペレーティングシステムが提供するライブラリーのバージョンに依存しない実行ファイルを提供しているように見えます。ただし、ほとんどのライブラリーは、その他のライブラリーに依存しています。静的リンクを使用すると、依存関係に柔軟性がなくなるため、前方互換性と後方互換性が失われます。静的リンクは、実行ファイルが構築されたシステムでのみ機能します。

警告

GNU C ライブラリー (glibc) から静的ライブラリーをリンクするアプリケーションでは、引き続き glibc が動的ライブラリーとしてシステムに存在する必要があります。さらに、アプリケーションのランタイム時に利用できる glibc の動的ライブラリーバリアントは、アプリケーションのリンク時に表示されるものとビット単位で同じバージョンである必要があります。したがって、静的リンクは、実行ファイルが構築されたシステムでのみ機能することが保証されます。

サポート範囲
Red Hat が提供するほとんどの静的ライブラリーは CodeReady Linux Builder チャンネルにあり、Red Hat ではサポートされていません。
機能

いくつかのライブラリー (特に GNU C ライブラリー (glibc)) は、静的にリンクすると提供する機能が少なくなります。

たとえば、静的にリンクすると、glibc は、スレッドと、同じプログラム内の dlopen() 関数への呼び出しの形式をサポートしません。

上述のデメリットにより、静的リンクは、特にアプリケーション全体、glibc ライブラリー、および libstdc++ ライブラリーに対しては、使用しないようにしてください。

静的リンクの場合

静的リンクは、次のようないくつかのケースでは合理的な選択が可能です。

  • 動的リンクが使用できないライブラリーを使用している
  • 空の chroot 環境またはコンテナーでコードを実行するには、完全に静的なリンクが必要です。ただし、glibc-static パッケージを使用した静的リンクは、Red Hat ではサポートされません。

関連資料

4.3. GCC でのライブラリーの使用

ライブラリーは、プログラムで再利用可能なコードのパッケージです。C または C++ のライブラリーは、以下の 2 つの部分で構成されます。

  • ライブラリーコード
  • ヘッダーファイル

ライブラリーを使用するコードのコンパイル

ヘッダーファイルでは、ライブラリーで提供する関数や変数など、ライブラリーのインターフェースを記述します。コードをコンパイルする場合に、ヘッダーファイルの情報が必要です。

通常、ライブラリーのヘッダーファイルは、アプリケーションのコードとは別のディレクトリーに配置されます。ヘッダーファイルの場所を GCC に指示するには、-I オプションを使用します。

$ gcc ... -Iinclude_path ...

include_path は、ヘッダーファイルのディレクトリーのパスに置き換えます。

-I オプションは、複数回使用して、ヘッダーファイルを含むディレクトリーを複数追加できます。ヘッダーファイルを検索する場合は、-I オプションで表示順に、これらのディレクトリーが検索されます。

ライブラリーを使用するコードのリンク

実行ファイルをリンクする場合には、アプリケーションのオブジェクトコードと、ライブラリーのバイナリーコードの両方が利用できる状態でなければなりません。静的ライブラリーおよび動的ライブラリーのコードは、形式が異なります。

  • 静的なライブラリーは、アーカイブファイルとして利用できます。静的なライブラリーには、一連のオブジェクトファイルが含まれます。アーカイブファイルのファイル名の拡張子は .a になります。
  • 動的なライブラリーは共有オブジェクトとして利用できます。実行ファイルの形式です。共有オブジェクトのファイル名の拡張子は、.so になります。

アーカイブファイルまたは共有オブジェクトファイルの場所を GCC に渡すには、-L オプションを使用します。

$ gcc ... -Llibrary_path -lfoo ...

library_path は、ライブラリーのディレクトリーのパスに置き換えます。

-I オプションは、複数回使用して、ディレクトリーを複数追加できます。ライブラリーを検索する場合は、-L オプションで表示順に、このディレクトリーが検索されます。

オプションの指定順は重要です。対象のライブラリーがディレクトリーにリンクされていることが分からないと、GCC は、ライブラリー foo をリンクできません。そのため、-L オプションを使用して先にライブラリーディレクトリーを指定してから、-l オプションでライブラリーをリンクするようにしてください。

1 つの手順でライブラリーを使用するコードをコンパイルおよびリンクする方法

1 つの gcc コマンドでコードをコンパイルおよびリンクできる場合は、上記のオプションを一度に使用します。

関連資料

4.4. GCC での静的ライブラリーの使用

静的なライブラリーは、オブジェクトファイルを含むアーカイブとして利用できます。リンクを行うと、作成された実行ファイルの一部となります。

注記

Red Hat は、セキュリティー上の理由から、静的リンクを使用することは推奨していません。「静的リンクおよび動的リンク」を参照してください。静的リンクは、特に Red Hat が提供するライブラリーに対して、必要な場合に限り使用してください。

前提条件

  • GCC がシステムにインストールされている。
  • 静的リンクおよび動的リンクを理解している。
  • 有効なプログラムを構成するソースまたはオブジェクトのファイルセット。静的ライブラリー foo だけが必要です。
  • foo ライブラリーは libfoo.aファイルとして利用でき、動的リンクには libfoo.so ファイルがありません。
注記

Red Hat Enterprise Linux に含まれるライブラリーの多くは、動的リンク用にのみ対応しています。次の手順は、動的リンクに 無効の ライブラリーに対してのみ有効です。

手順

ソースとオブジェクトファイルからプログラムをリンクするには、静的にリンクされたライブラリー foo (libfoo.a として検索可能) を追加します。

  1. コードが含まれるディレクトリーに移動します。
  2. foo ライブラリーのヘッダーで、プログラムソースファイルをコンパイルします。

    $ gcc ... -Iheader_path -c ...

    header_path を、foo ライブラリーのヘッダーファイルを含むディレクトリーのパスに置き換えます。

  3. プログラムを foo ライブラリーにリンクします。

    $ gcc ... -Llibrary_path -lfoo ...

    library_path を、libfoo.a ファイルを含むディレクトリーのパスに置き換えます。

  4. あとでプログラムを実行するには、以下のコマンドを実行します。

    $ ./program
注意

静的リンクに関連する GCC オプション -static は、すべての動的リンクを禁止します。代わりに -Wl,-Bstatic オプションおよび -Wl,-Bdynamic オプションを使用して、リンカーの動作をより正確に制御します。「GCC で静的ライブラリーおよび動的ライブラリーの両方を使用」を参照してください。

4.5. GCC での動的ライブラリーの使用

動的ライブラリーは、スタンドアロンの実行ファイルとして提供します。このファイルは、リンク時およびランタイム時に必要です。このファイルは、アプリケーションの実行ファイルからは独立しています。

前提条件

  • GCC がシステムにインストールされている。
  • 有効なプログラムを構成するソースまたはオブジェクトファイルセットがある。動的ライブラリー foo だけが必要になります。
  • foo ライブラリーが libfoo.so ファイルとして利用できる。

プログラムの動的ライブラリーへのリンク

動的ライブラリー foo にプログラムをリンクするには、以下のコマンドを実行します。

$ gcc ... -Llibrary_path -lfoo ...

プログラムを動的ライブラリーにリンクすると、作成されるプログラムは常にランタイム時にライブラリーを読み込む必要があります。ライブラリーの場所を特定するオプションは 2 つあります。

  • 実行ファイルに保存された rpath の値を使用する方法
  • ランタイム時に LD_LIBRARY_PATH 変数を使用する方法

実行ファイルに保存された rpath の値を使用する方法

rpath は、リンク時に実行ファイルの一部として保存される特別な値です。その後、実行ファイルからプログラムを読み込む時に、ランタイムリンカーが rpath の値を使用してライブラリーファイルの場所を特定します。

GCC とリンクし、library_path のパスを rpath として保存します。

$ gcc ... -Llibrary_path -lfoo -Wl,-rpath=library_path ...

library_path のパスは、libfoo.so ファイルを含むディレクトリーを参照する必要があります。

注意

-Wl,-rpath= オプションのコンマの後にスペースがあるので注意してください。

あとでプログラムを実行するには、以下のコマンドを実行します。

$ ./program

LD_LIBRARY_PATH 環境変数を使用する方法

プログラムの実行ファイルに rpath がない場合、ランタイムリンカーは LD_LIBRARY_PATH の環境変数を使用します。この変数の値は、共有ライブラリーのオブジェクトがあるパスに合わせて、プログラムごとに変更する必要があります。

rpath セットがなく、ライブラリーが library_path パスにある状態で、プログラムを実行します。

$ export LD_LIBRARY_PATH=library_path:$LD_LIBRARY_PATH
$ ./program

rpath の値を空白にすると柔軟性がありますが、プログラムを実行するたびに LD_LIBRARY_PATH 変数を設定する必要があります。

ライブラリーの、デフォルトのディレクトリーへの配置

ランタイムのリンカー設定では、複数のディレクトリーを動的ライブラリーファイルのデフォルトの場所として指定します。このデフォルトの動作を使用するには、ライブラリーを適切なディレクトリーにコピーします。

動的リンカーの動作に関する詳細な説明は、本書の対象外です。詳しい情報は、以下の資料を参照してください。

  • 動的リンカーの Linux man ページ:

    $ man ld.so
  • /etc/ld.so.conf 設定ファイルの内容:

    $ cat /etc/ld.so.conf
  • 追加設定なしに動的リンカーにより認識されるライブラリーのレポート (ディレクトリーを含む):

    $ ldconfig -v

4.6. GCC で静的ライブラリーおよび動的ライブラリーの両方を使用

場合によっては、静的ライブラリーと動的ライブラリーの両方をリンクする必要があります。このような場合には、いくつかの課題があります。

前提条件

  • 静的リンクおよび動的リンクの理解

はじめに

GCC は、動的ライブラリーと静的ライブラリーの両方を認識します。-lfoo オプションがあると、gcc はまず、動的にリンクされた foo ライブラリーを含む共有オブジェクト (.so ファイル) を検索し、静的ライブラリーを含むアーカイブファイル (.a) を検索します。したがって、この検索により、以下の状況が発生する可能性があります。

  • 共有オブジェクトのみが見つかり、gcc がそのオブジェクトに動的にリンクする
  • アーカイブファイルのみが見つかり、gcc がそのファイルに静的にリンクする
  • 共有オブジェクトとアーカイブファイルの両方が見つかり、gcc はデフォルト設定のとおりに共有オブジェクトに動的にリンクする
  • 共有オブジェクトもアーカイブファイルも見つからず、リンクに失敗する

このようなルールがあるため、リンクするために、静的ライブラリーまたは動的ライブラリーを選択する場合は、gcc が検索可能なバージョンのみを指定するようにします。これにより、-Lpath オプションで指定する場合に、静的ライブラリーまたは動的ライブラリーを含むディレクトリーを追加するか、追加しないかで、ある程度制御が可能になります。

また、動的リンクがデフォルトの設定であるため、明示的にリンクを指定する必要があるのは、静的と動的の両方を静的にリンクする必要がある場合のみです。考えられる方法は以下の 2 つです。

  • -l オプションではなく、ファイルパスで静的ライブラリーを指定する
  • -Wl オプションを使用して、リンカーにオプションを渡す

ファイルで静的ライブラリーを指定する方法

通常、gcc は、-lfoo オプションで、foo ライブラリーにリンクするように指示されます。ただし、代わりに、ライブラリーを含む libfoo.a ファイルの完全パスは指定できます。

$ gcc ... path/to/libfoo.a ...

ファイルの拡張子 .a から、gcc は、このファイルがプログラムとリンクするためのライブラリーであることを理解します。ただし、ライブラリーファイルの完全パスを指定するのは柔軟な方法ではありません。

-Wl オプションの使用

gcc オプションの -Wl は、基盤のリンカーにオプションを渡す特別なオプションです。このオプションの構文は、他の gcc オプションとは異なります。このオプションの後に、リンカーのオプションのコンマ区切りの一覧を指定して、スペースで区切った gcc オプションと混同されないようにします。

gcc が使用する ld リンカーには、-Bstatic-Bdynamic のオプションがあり、このオプションの後に来るライブラリーが静的または動的にリンクすべきかどうかを指定します。-Bstatic とライブラリーをリカーに渡した後、以降のライブラリーを -Bdynamic オプションで動的にリンクするには、デフォルトの動的リンクの動作を手動で復元する必要があります。

プログラムをリンクするには、まず 静的に (libfirst.a) ライブラリーをリンクして、次に 動的に (libsecond.so) リンクします。

$ gcc ... -Wl,-Bstatic -lfirst -Wl,-Bdynamic -lsecond ...
注記

gcc は、デフォルトの ld 以外のリンカーを使用するように設定できます。

関連資料

第5章 GCC でのライブラリーの作成

本章では、ライブラリーの作成手順と、Linux オペレーティングシステムで使用するために必要なライブラリーの概念を説明します。

5.1. ライブラリーの命名規則

特別なファイルの命名規則をライブラリーに使用します。foo として知られるライブラリーは、libfoo.so ファイルまたは libfoo.a ファイルとして存在する必要があります。この規則は、リンクする GCC の入力オプションでは自動的に理解されますが、出力オプションでは理解されません。

  • ライブラリーにリンクする場合は、-lfoo のように、-l オプションと foo の名前でしか、ライブラリーを指定することができません。

    $ gcc ... -lfoo ...
  • ライブラリーの作成時には、libfoo.solibfoo.a など、完全なファイル名を指定する必要があります。

5.2. soname のメカニズム

動的に読み込んだライブラリー (共有オブジェクト) は、soname と呼ばれるメカニズムを使用して、複数の互換性のあるライブラリーを管理します。

前提条件

  • 動的リンクとライブラリーを理解している。
  • ABI の互換性の概念を理解している。
  • ライブラリーの命名規則を理解している。
  • シンボリックリンクを理解している。

問題の概要

動的に読み込んだライブラリー (共有オブジェクト) は、独立した実行ファイルとして存在します。そのため、依存するアプリケーションを更新せずに、ライブラリーを更新できます。ただし、この概念では、以下の問題が発生します。

  • 実際のライブラリーバージョンを特定
  • 同じライブラリーに対して複数のバージョンが必要
  • 複数のバージョンでそれぞれ ABI の互換性を示す

soname のメカニズム

この問題を解決するには、Linux では soname と呼ばれるメカニズムを使用します。

ライブラリー fooX.Y バージョンは、バージョン番号 (X) が同じ値でマイナーバージョンが異なるバージョンと、ABI の互換性があります。互換性を確保してマイナーな変更を加えると、Y の数字が増えます。互換性がなくなるような、メジャーな変更を加えると、X の数字が増えます。

foo ライブラリーバージョン X.Y は、libfoo.so.x.y ファイルとして存在します。ライブラリーファイルの中に、soname が libfoo.so.x の値として記録され、互換性を指定します。

アプリケーションを構築すると、リンカーが libfoo.so ファイルを検索して、ライブラリーを特定します。この名前のシンボリックリンクが存在し、実際のライブラリーファイルを参照する必要があります。次にリンカーは、ライブラリーファイルから soname を読み込み、アプリケーションの実行ファイルに記録します。最後に、リンカーにより、ファイル名でなく、soname を使用してライブラリーで依存関係を宣言するように、アプリケーションが作成されます。

ランタイムの動的リンカーが実行前にアプリケーションをリンクすると、soname がアプリケーションの実行ファイルから読み込まれます。この soname は libfoo.so.x と呼ばれます。この名前のシンボリックリンクが存在し、実際のライブラリーファイルを参照する必要があります。soname が変更しないため、これにより、バージョンの Y コンポーネントに関係なく、ライブラリーを読み込むことができます。

注記

バージョン番号の Y の部分は、1 つの数字である必要はありません。また、ライブラリーによっては、名前がバージョンに組み込まれているものもあります。

ファイルからの soname の読み込み

somelibrary ライブラリーファイルの soname を表示します。

$ objdump -p somelibrary | grep SONAME

somelibrary は、検証するライブラリーのファイル名に置き換えます。

5.3. GCC での動的ライブラリーの作成

動的にリンクされたライブラリー (共有オブジェクト) は、ライブラリーコードの更新を容易にすることで、コードが再利用され、セキュリティーが強化されるため、リソースが保護されます。以下の手順に従って、ソースから動的ライブラリーを構築してインストールします。

前提条件

  • soname メカニズムを理解している。
  • GCC がシステムにインストールされている。
  • ライブラリーのソースコードがある。

手順

  1. ライブラリーソースのディレクトリーに移動します。
  2. 位置独立コードオプション -fPIC でオブジェクトファイルに各ソースファイルをコンパイルします。

    $ gcc ... -c -fPIC some_file.c ...

    オブジェクトファイルは、オリジナルのソースコードファイルと同じファイル名ですが、拡張子が .o となります。

  3. オブジェクトファイルから共有ライブラリーをリンクします。

    $ gcc -shared -o libfoo.so.x.y -Wl,-soname,libfoo.so.x some_file.o ...

    使用するメジャーバージョン番号は X で、マイナーバージョン番号は Y です。

  4. libfoo.so.x.y ファイルを、システムの動的リンカーが検索できる適切な場所にコピーします。Red Hat Enterprise Linux では、ライブラリーのディレクトリーは /usr/lib64 となります。

    # cp libfoo.so.x.y /usr/lib64

    このディレクトリーにあるファイルを操作するには、root パーミッションが必要な点に注意してください。

  5. soname メカニズムのシンボリックリンク構造を作成します。

    # ln -s libfoo.so.x.y libfoo.so.x
    # ln -s libfoo.so.x libfoo.so

関連資料

5.4. GCC および ar での静的ライブラリーの作成

オブジェクトファイルを特別なアーカイブファイルに変換して、静的にリンクするライブラリーを作成できます。

注記

Red Hat は、セキュリティー上の理由から、静的リンクを使用することは推奨していません。静的リンクは、特に Red Hat が提供するライブラリーに対して、必要な場合にのみ使用してください。詳細は「静的リンクおよび動的リンク」を参照してください。

前提条件

  • GCC と binutils がシステムにインストールされている。
  • 静的リンクおよび動的リンクを理解している。
  • 関数を含むソースファイルをライブラリーとして共有している。

手順

  1. GCC で仲介となるオブジェクトファイルを作成します。

    $ gcc -c source_file.c ...

    必要に応じて、さらにソースファイルを追加します。作成されるオブジェクトファイルはファイル名を共有しますが、拡張子は .o を使用します。

  2. binutils パッケージの ar ツールを使用して、オブジェクトファイルを静的ライブラリー (アーカイブ) に変換します。

    $ ar rcs libfoo.a source_file.o ...

    libfoo.a ファイルが作成されます。

  3. nm コマンドを使用して、作成されたアーカイブを検証します。

    $ nm libfoo.a
  4. 静的ライブラリーファイルを適切なディレクトリーにコピーします。
  5. ライブラリーにリンクする場合、GCC は自動的に .a のファイル名の拡張子 (ライブラリーが静的リンクのアーカイブであること) を認識します。

    $ gcc ... -lfoo ...

関連資料

  • Linux の man ページ ar(1):

    $ man ar

第6章 Make でのさらなるコードの管理

GNU の make ユーティリティー (通称 make) は、ソースファイルから実行ファイルの生成を制御するツールです。make は自動的に、複雑なプログラムのどの部分が変更され、再度コンパイルする必要があるのかを判断します。make は Makefile と呼ばれる設定ファイルを使用して、プログラムを構築する方法を制御します。

6.1. GNU make および Makefile の概要

特定のプロジェクトのソースファイルから使用可能な形式 (通常は実行ファイル) を作成するには、必要な手順を完了します。後で反復できるように、アクションとそのシーケンスを記録します。

Red Hat Enterprise Linux には、この目的用に設計されたビルドシステムである、GNU make が含まれています。

前提条件

  • コンパイルとリンクの概念を理解している。

GNU make

GNU make はビルドプロセスの命令が含まれる Makefile を読み込みます。Makefile には、特定のアクション (レシピ) で特定の条件 (ターゲット) を満たす方法を記述する複数の ルール が含まれています。ルールは、別のルールに階層的に依存できます。

オプションを指定せずに make を実行すると、現在のディレクトリーで Makefile を検索し、デフォルトのターゲットに到達しようと試みます。実際の Makefile ファイル名は Makefilemakefile、および GNUmakefile です。デフォルトのターゲットは、Makefile の内容で決まります。

Makefile の詳細

Makefile は比較的単純な構文を使用して 変数ルール を定義します。Makefile は ターゲットレシピ で構成されます。ターゲットでは、ルールが実行された場合にどのような出力が表示されるのかを指定します。レシピの行は、TAB 文字で開始する必要があります。

通常、Makefile は、ソースファイルをコンパイルするルール、作成されるオブジェクトファイルをリンクするルール、および階層上部のエントリーポイントとしての役割を果たすターゲットで構成されます。

1 つのファイル (hello.c) で構成される C プログラムを構築する場合は、以下の Makefile を参照してください。

all: hello

hello: hello.o
        gcc hello.o -o hello

hello.o: hello.c
        gcc -c hello.c -o hello.o

上記の例では、ターゲット all に到達するには、ファイル hello が必要です。hello を取得するには、hello.o (gcc でリンク) が必要で、hello.c (gcc でコンパイル) を基に作成します。

ターゲットの all は、ピリオド (.) で開始されない最初のターゲットであるため、デフォルトのターゲットとなっています。この Makefile が現在のディレクトリーに含まれている場合に、引数なしで make を実行するのは、make all を実行するのと同じです。

一般的な Makefile

より一般的な Makefile は、この手順を正規化する変数を使用し、ターゲット「clean」を追加して、ソースファイル以外をすべて削除します。

CC=gcc
CFLAGS=-c -Wall
SOURCE=hello.c
OBJ=$(SOURCE:.c=.o)
EXE=hello

all: $(SOURCE) $(EXE)

$(EXE): $(OBJ)
        $(CC) $(OBJ) -o $@

%.o: %.c
        $(CC) $(CFLAGS) $< -o $@

clean:
        rm -rf $(OBJ) $(EXE)

このような Makefile にソースファイルを追加する場合は、SOURCE 変数が定義されている行に追加します。

関連資料

6.2. 例: Makefile を使用した C プログラムの構築

以下の手順に従い、Makefile を使用して C のサンプルプログラムを構築します。

前提条件

  • Makefile と make の概念を理解している。

手順

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

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

    #include <stdio.h>
    
    int main(int argc, char *argv[]) {
      printf("Hello, World!\n");
      return 0;
    }
  3. 以下の内容で Makefile ファイルを作成します。

    CC=gcc
    CFLAGS=-c -Wall
    SOURCE=hello.c
    OBJ=$(SOURCE:.c=.o)
    EXE=hello
    
    all: $(SOURCE) $(EXE)
    
    $(EXE): $(OBJ)
            $(CC) $(OBJ) -o $@
    
    %.o: %.c
            $(CC) $(CFLAGS) $< -o $@
    
    clean:
            rm -rf $(OBJ) $(EXE)
    注意

    Makefile レシピの行は、Tab 文字で開始する必要があります。上記のテキストをドキュメントからコピーする際に、カットアンドペーストのプロセスでは、タブではなくスペースが貼り付けられる場合があります。この場合は、手動で修正してください。

  4. make を実行します。

    $ make
    gcc -c -Wall hello.c -o hello.o
    gcc hello.o -o hello

    このコマンドで、実行ファイルが hello 作成されます。

  5. この実行ファイル hello を実行します。

    $ ./hello
    Hello, World!
  6. Makefile のターゲット clean を実行して、作成されたファイルを削除します。

    $ make clean
    rm -rf hello.o hello

6.3. make のドキュメント

make の詳細は、以下に記載のドキュメントを参照してください。

インストールされているドキュメント

  • man ツールおよび info ツールで、お使いのシステムにインストールされている man ページと情報ページを表示します。

    $ man make
    $ info make

オンラインドキュメント

付録A RHEL 8 の開発ツールの相違点

以下のセクションでは、Red Hat Enterprise Linux 8 と 7 の主な相違点を説明します。

A.1. RHEL 7 以降の toolchain の変更点

以下のシナリオでは、Red Hat Enterprise Linux 7 で説明されているコンポーネントのリリース以降のツールチェインにおける変更を記載します。『Red Hat Enterprise Linux 8.0 リリースノート』も併せて参照してください。

A.1.1. RHEL 8 の GCC における変更点

Red Hat Enterprise Linux 8 では、GCC ツールチェーンは GCC 8.2 リリースシリーズに基づいています。以下は、Red Hat Enterprise Linux 7 からの主な変更点です。

  • エイリアス解析、ベクトル化機能の改善、同一コードの折りたたみ、プロシージャー間解析、ストアマージの最適化パスなど、一般的な最適化が多数追加されました。
  • Address Sanitizer が改善されました。
  • メモリリークを検出するために、Leak Sanitizer が追加されました。
  • 未定義の挙動を検出するために、Undefined Behavior Sanitizer が追加されました。
  • デバッグ情報が DWARF5 形式で生成できるようになりました。この機能は実験的なものです。
  • ソースコードカバレッジ解析ツールの GCOV が、さまざまな改良とともに拡張されました。
  • OpenMP 4.5 仕様のサポートが追加されました。また、OpenMP 4.0 仕様のオフロード機能は、C、C++、および Fortran のコンパイラーが対応します。
  • 特定の、起こりうるプログラムエラーを静的に検出するために、新しい警告と改善された診断が追加されました。
  • ソースの場所は、その場所よりも広い範囲を追跡するため、診断する内容が濃くなりました。コンパイラーは、「fix-it」ヒントを提供し、可能なコードの修正を提案します。代替名とタイポの検出を簡単にするために、スペルチェックが追加されました。

セキュリティー

GCC が、生成したコードをさらに強化するツールを提供するように拡張されました。セキュリティーに関する改善点には以下が含まれます。

  • オーバーフローチェックを含む算術計算のための組み込み関数 __builtin_add_overflow__builtin_sub_overflow、および __builtin_mul_overflow が追加されました。
  • スタッククラッシュに対して追加のコード保護を生成するために、-fstack-clash-protection オプションが追加されました。
  • 増加したプログラムセキュリティーの制御フロー命令のターゲットアドレスを確認するために、-fcf-protection オプションが導入されました。
  • 新しい -Wstringop-truncation 警告オプションは、コピーした文字列を切り捨てるか、目的が変更しない strncatstrncpystpncpy などのバインドされた文字列操作関数への呼び出しを一覧表示します。
  • -Warray-bounds 警告オプションが改善され、範囲外の配列のインデックスおよびポインターのオフセットの検出が改善されるようになりました。
  • memcpyrealloc などの生のメモリーアクセス機能により、重要なクラスタイプのオブジェクトで潜在的に危険な操作を警告するために、-Wclass-memaccess 警告オプションが追加されました。

アーキテクチャーおよびプロセッサーのサポート

アーキテクチャーおよびプロセッサーサポートの改善点は次のとおりです。

  • Intel AVX-512 アーキテクチャー、その多数のマイクロアーキテクチャー、および Intel Software Guard Extensions (SGX) にアーキテクチャー固有の新しいオプションが複数追加されました。
  • コード生成は、現在、64 ビットの ARM アーキテクチャー LSE 拡張、ARMv8.2-A 16 ビット浮動小数点拡張 (FPE)、およびアーキテクチャーのバージョン ARMv8.2-A、ARMv8.3-A、および ARMv8.4-A を対象にできるようになりました。
  • ARM および 64 ビット ARM アーキテクチャーで -march=native オプションの処理が修正されました。
  • IBM Z アーキテクチャーのプロセッサー z13 および z14 に対応するようになりました。

言語および標準

以下は、言語と標準規格に関連した主な変更点です。

  • C 言語でコンパイルする際に使用されるデフォルトの標準規格が、GNU 拡張機能が含まれる C17 に変更になりました。
  • C++ 言語でコードをコンパイルする際に使用されるデフォルトの標準規格が、GNU 拡張機能が含まれる C++14 に変更になりました。
  • C++ ランタイムライブラリーが、C++11 および C++14 の標準規格に対応するようになりました。
  • C++ コンパイラーは、新しい機能を多数持つ C++14 標準仕様を実装するようになりました。たとえば、変数テンプレート、非静的データメンバーイニシャライザーを持つ統合、拡張した constexpr 指定子、標準サイズの割り当て解除関数、汎用ラムダ、可変長の配列、桁区切り記号などになります。
  • C 言語の標準 C11 のサポートが改善しました。ISO C11 アトミック、一般的な選択、およびスレッドローカルストレージが利用可能になりました。
  • 新しい __auto_type の GNU C 拡張機能が、C 言語の C++11 の auto キーワード機能のサブセットを提供します。
  • ISO/IEC TS 18661-3:2015 標準規格が指定する型名 _FloatN および _FloatNx が、C フロントエンドで認識されるようになりました。
  • C 言語でコンパイルする際に使用されるデフォルトの標準規格が、GNU 拡張機能が含まれる C17 に変更になりました。これは、--std=gnu17 オプションを使用するのと同じ効果があります。以前は、デフォルトは、GNU 拡張を持つ C89 です。
  • GCC は、C++17 言語標準規格と、C++20 標準規格の一部の機能を使用してコンパイルできるようになりました。
  • 空のクラスを引数として渡すと、プラットフォーム ABI で要求される、Intel 64 アーキテクチャーおよび AMD64 アーキテクチャーで領域を使用しません。削除したコピーまたは移動のコンストラクターだけを持つクラスを渡すか返すと、重要なコピーまたは移動のコンストラクターを持つクラスと同じ規則を使用します。
  • C++11 の alignof 演算子により返される値は、C の _Alignof 演算子と一致し、最小の配置を返すように修正されました。適切な配置を見つけるには、GNU 拡張機能 __alignof__ を使用します。
  • Fortran 言語コード用の libgfortran ライブラリーのメインバージョンが 5 に変更になりました。
  • Ada (GNAT)、GCC Go、および Objective C/C++ 言語のサポートが削除されました。Go コード開発には Go Toolset を使用してください。

関連資料

A.1.2. RHEL 8 の GCC へのセキュリティー強化

本セクションは、Red Hat Enterprise Linux 7.0 のリリース以降に追加されたセキュリティーに関連する GCC の変更の詳細を説明します。

新しい警告

以下のような警告オプションが追加されました。

オプション警告の表示理由

-Wstringop-truncation

コピーした文字列を切り捨てるか、目的が変更しない strncatstrncpystpncpy などのバインドした文字列操作を読み出します。

-Wclass-memaccess

memcpyrealloc のような、生のメモリー機能により、潜在的に危険な方法で操作される重要なクラスタイプのオブジェクトです。

警告は、ユーザー定義のコンストラクターやコピー代入演算子、破損した仮想テーブルポインター、const 修飾型または参照、またはメンバーポインターのデータメンバーを回避する呼び出しを検出します。この警告は、データメンバーへのアクセス制御を回避する呼び出しも検出します。

-Wmisleading-indentation

コードのインデントにより、コードのブロック構造について誤解を与える場所。

-Walloc-size-larger-than=size

割り当てるメモリーの量が size を超えた場合にメモリー割り当て関数を呼び出します。2 つのパラメーターを乗じることで割り当てが指定される関数や、alloc_size 属性が付けられた関数とも連携します。

-Walloc-zero

メモリー量を割り当てないようにするメモリー割り当て関数を呼び出します。2 つのパラメーターを乗じることで割り当てが指定される関数や、alloc_size 属性が付けられた関数とも連携します。

-Walloca

alloca 関数へのすべての読み出し。

-Walloca-larger-than=size

size 以上のメモリーが必要になると、alloca 関数が呼び出されます。

-Wvla-larger-than=size

指定のサイズを超えたか、そのバウンドが十分に拘束されるか不明な可変長配列 (VLA) の定義。

-Wformat-overflow=level

フォーマットされた出力関数の sprintf ファミリーへの呼び出しで、特定の好ましいバッファーオーバーフロー。level 値の詳細と説明は、man ページの gcc(1) を参照してください。

-Wformat-truncation=level

フォーマットされた出力関数の snprintf ファミリーへの呼び出しで、特定の好ましい出力の切り替え。level 値の詳細と説明は、man ページの gcc(1) を参照してください。

-Wstringop-overflow=type

memcpystrcpy などの文字列処理関数への呼び出しのバッファーオーバーフロー。level 値の詳細と説明は、man ページの gcc(1) を参照してください。

警告の改良

次の GCC の警告が修正されました。

  • -Warray-bounds オプションが改善され、範囲外の配列インデックスおよびポインターオフセットの複数インスタンスを検出するようになりました。たとえば、フレキシブル配列メンバーと文字列リテラルに負または過剰なポインターが検出されます。
  • GCC 7 で導入された -Wrestrict オプションは、標準メモリーと、memcpystrcpy などの文字列操作関数への制限引数を介してオブジェクトへのアクセスをオーバーラップする、より多くのインスタンスを検出するように強化されました。
  • -Wnonnull オプションは、null 以外の引数 (nonnull 属性が付いている) を期待する関数に null ポインターを渡す広範囲なケースセットを検出するように強化されました。

新しい UndefinedBehaviorSanitizer

UndefinedBehaviorSanitizer と呼ばれる未定義の動作を検出する新しいランタイムサニタイザーが追加されました。主な機能は以下のようになります。

オプションチェック

-fsanitize=float-divide-by-zero

ゼロによる浮動小数点除算を検出します。

-fsanitize=float-cast-overflow

浮動小数点型から整数の変換がオーバーフローしていないことを確認します。

-fsanitize=bounds

配列境界の計測を有効にして、範囲外のアクセスを検出します。

-fsanitize=alignment

アラインメントチェックを有効にし、アラインが適切でないさまざまなオブジェクトを検出します。

-fsanitize=object-size

オブジェクトサイズのチェックを有効にして、様々な範囲外のアクセスを検出します。

-fsanitize=vptr

C++ メンバー関数呼び出し、メンバーアクセス、および基本クラスおよび派生クラスへのポインター間の会話のチェックを有効にします。また、参照されるオブジェクトに正しい動的タイプがない場合は検出します。

-fsanitize=bounds-strict

配列境界の厳密なチェックを有効にします。これにより、柔軟なメンバー状の配列の計測と -fsanitize=bounds を有効にします。

-fsanitize=signed-integer-overflow

汎用ベクトルを持つ算術演算でも、算術オーバーフローが診断されます。

-fsanitize=builtin

事前定義されたビルトインの __builtin_clz または __builtin_ctz への無効な引数をランタイム時に診断します。-fsanitize=undefined からのチェックが含まれます。

-fsanitize=pointer-overflow

ポインターのラッピングに簡易ランタイムテストを実行します。-fsanitize=undefined からのチェックが含まれます。

AddressSanitizer の新規オプション

以下のオプションが AddressSanitizer に追加されました。

オプションチェック

-fsanitize=pointer-compare

異なるメモリーオブジェクトを指定するポインターの比較を警告します。

-fsanitize=pointer-subtract

異なるメモリーオブジェクトを指すポインターの減算を警告します。

-fsanitize-address-use-after-scope

その変数が定義されている範囲後に取得され使用されているアドレスの変数をサニタイズします。

その他のサニタイザーおよび計測

  • プローブを挿入するために、-fstack-clash-protection オプションが追加されました。このプローブは、スタック領域が静的または動的に割り当てられた場合に、スタックオーバーフローが確実に検出され、オペレーティングシステムが提供するスタックガードページを超えることに依存する攻撃ベクトルを軽減する際に挿入されます。
  • 制御フロー転送のターゲットアドレス命令 (間接的な関数呼び出し、関数の戻り値、間接ジャンプなど) のターゲットアドレスが有効であることを確認することで、コード計測を実行して、プログラムセキュリティーを高める新しいオプション -fcf-protection=[full|branch|return|none] が追加されました。

関連資料

  • 上述のオプションの一部に提供された値の詳細および説明は、man ページの gcc(1) を参照してください。

    $ man gcc

A.2. コンパイラーツールセット

RHEL 8.0 は、以下のコンパイラーツールセットを Application Streams として提供します。

  • LLVM コンパイラーインフラストラクチャーフレームワークを提供する Clang および LLVM Toolset 7.0.1 は、LLVM コンパイラーインフラストラクチャーフレームワーク、C 言語および C++ 言語用の Clang コンパイラー、LLDB デバッガー、コード解析の関連ツールを提供します。『Using Clang and LLVM Toolset』を参照してください。
  • Rust Toolset 1.31 は、Rust プログラミング言語コンパイラー rustccargo ビルドツールおよび依存マネージャー、cargo-vendor プラグイン、および必要なライブラリーを提供します。『Using Rust Toolset』を参照してください。
  • Go Toolset 1.11.5 は、Go プログラミング言語ツールおよびライブラリーを提供します。Go は、golang としても知られています。『Using Go Toolset』を参照してください。

A.3. GDB で互換性に影響を与える変更

Red Hat Enterprise Linux 8 で提供される GDB のバージョンは、特に GDB の出力が端末から直接読み込まれる場合に、互換性に影響を与える変更が多数含まれています。次のセクションは、この変更の詳細を提供します。

GDB の出力の解析は推奨されません。Python GDB API または GDB Machine Interface (MI) を使用するスクリプトが推奨されます。

GDBserver がシェルで inferior を開始

inferior コマンドライン引数で拡張や変数置換を有効にするために、GDBserver では、GDB と同じように、シェルで inferior を開始するようになりました。

シェルを使用して無効にするには、以下を行います。

  • GDB コマンド target extended-remote を使用する場合は、set startup-with-shell off コマンドでシェルが無効になります。
  • GDB コマンド target remote を使用する場合は、GDBserver の --no-startup-with-shell オプションでシェルが無効になります。

例A.1 リモートの GDB inferior へのシェル拡張例

この例は、GDBserver から /bin/echo /* コマンドを実行する方法が Red Hat Enterprise Linux versions 7 および 8 でどのように異なるかを示します。

  • RHEL 7 の場合:

    $ gdbserver --multi :1234
    $ gdb -batch -ex 'target extended-remote :1234' -ex 'set remote exec-file /bin/echo' -ex 'file /bin/echo' -ex 'run /*'
    /*
  • RHEL 8 の場合:

    $ gdbserver --multi :1234
    $ gdb -batch -ex 'target extended-remote :1234' -ex 'set remote exec-file /bin/echo' -ex 'file /bin/echo' -ex 'run /*'
    /bin /boot (...) /tmp /usr /var

gcj サポートが削除される

Java 用 (gcj) の GNU Compiler でコンパイルされた Java プログラムをデバッグするサポートが削除されました。

シンボルのダンプのメンテナンスコマンドの新しい構文

シンボルのダンプのメンテナンスコマンド構文に、ファイル名の前にオプションが追加されました。これにより、RHEL 7 の GDB で機能するコマンドが、RHEL 8 では機能しなくなりました。

例として、次のコマンドはファイルにシンボルを格納しませんが、エラーメッセージを生成します。

(gdb) maintenance print symbols /tmp/out main.c

シンボルのダンプのメンテナンスコマンドの新しい構文は、以下のようになります。

maint print symbols [-pc address] [--] [filename]
maint print symbols [-objfile objfile] [-source source] [--] [filename]
maint print psymbols [-objfile objfile] [-pc address] [--] [filename]
maint print psymbols [-objfile objfile] [-source source] [--] [filename]
maint print msymbols [-objfile objfile] [--] [filename]

スレッド番号がグローバルではなくなる

GDB は、グローバルのスレッド番号設定のみを使用していました。番号設定は、inferior_num.thread_num の形式 (2.1 など) で、inferior ごとに表示されるように拡張されました。そのため、利便性に関する変数 $_thread と、Python 属性 InferiorThread.num のスレッド番号が、inferior の間で固有ではならなくなりました。

GDB は、スレッドごとに、グローバルスレッド ID と呼ばれる 2 番目のスレッド ID を格納します。これは、以前のリリースのスレッド番号と同等の、新規のものになります。グローバルスレッド番号にアクセスするには、利便性に関する変数 $_gthread および Python 属性 InferiorThread.global_num を使用します。

後方互換性の場合は、Machine Interface (MI) のスレッド ID に、常にグローバル ID が含まれます。

例A.2 GDB スレッド番号変更の例

Red Hat Enterprise Linux 7 の場合:

# debuginfo-install coreutils
$ gdb -batch -ex 'file echo' -ex start -ex 'add-inferior' -ex 'inferior 2' -ex 'file echo' -ex start -ex 'info threads' -ex 'pring $_thread' -ex 'inferior 1' -ex 'pring $_thread'
(...)
  Id   Target Id         Frame
* 2    process 203923 "echo" main (argc=1, argv=0x7fffffffdb88) at src/echo.c:109
  1    process 203914 "echo" main (argc=1, argv=0x7fffffffdb88) at src/echo.c:109
$1 = 2
(...)
$2 = 1

Red Hat Enterprise Linux 8 の場合:

# dnf debuginfo-install coreutils
$ gdb -batch -ex 'file echo' -ex start -ex 'add-inferior' -ex 'inferior 2' -ex 'file echo' -ex start -ex 'info threads' -ex 'pring $_thread' -ex 'inferior 1' -ex 'pring $_thread'
(...)
  Id   Target Id         Frame
  1.1  process 4106488 "echo" main (argc=1, argv=0x7fffffffce58) at ../src/echo.c:109
* 2.1  process 4106494 "echo" main (argc=1, argv=0x7fffffffce58) at ../src/echo.c:109
$1 = 1
(...)
$2 = 1

値の中身に対するメモリーが制限される

GDB は、以前は、値のコンテンツに割り当てられるメモリー量に制限を課していませんでした。その結果、誤ったプログラムをデバッグすると、GDB が割り当てるメモリー量が多くなりすぎていました。割り当てたメモリーの量を制限できるように、max-value-size 設定が追加されました。この制限のデフォルト値は 64 KiB です。これにより、Red Hat Enterprise Linux 8 の GDB では、表示される値が大きくなりすぎることはありませんが、その値が大きすぎることが報告されます。

たとえば、char s[128*1024]; と定義された値を出力すると、異なる結果が生成されます。

  • Red Hat Enterprise Linux 7 では、$1 = 'A' <repeats 131072 times> となります。
  • Red Hat Enterprise Linux 8 では、value requires 131072 bytes, which is more than max-value-size (値には 131072 バイトが必要ですが、この値は max-value-size を超えています) と表示されます。

スタブ形式の Sun のバージョンがサポート対象外になる

Sun バージョンの stabs デバッグファイルフォーマットのサポートが削除されました。RHEL で gcc -gstabs オプションを使用して GCC が生成した stabs フォーマットは、GDB でも引き続きサポートされます。

Sysroot 処理変更

set sysroot path コマンドは、デバッグに必要なファイルを検索する際にシステムルートを指定します。このコマンドに適用したディレクトリー名は、文字列 target: のプレフィックスになり、GDB が、(ローカルおよびリモートの) ターゲットシステムの共有ライブラリーを読み込みます。以前は利用できた remote: プレフィックスは、target: として扱われるようになりました。さらに、デフォルトのシステム root 値は、後方互換性として、空の文字列から target: に変更になりました。

GDB がリモートのプロセスを開始したり、すでに実行しているプロセス (ローカルおよびリモートの両方) に接続する際に、指定したシステムの root が、主な実行ファイルのファイル名の先頭に追加されます。これは、プロセスがリモートの場合に、デフォルト値 target: が、GDB がリモートシステムからデバッグ情報を読み込もうとすることを示しています。これが発生しないようにするには、target remote コマンドの前に set sysroot コマンドを実行して、ローカルのシンボルファイルが、リモートのファイルが見つかるよりも早く見つかるようにします。

HISTSIZE が GDB コマンドの履歴サイズを制御しなくなる

HISTSIZE 環境変数に使用されている GDB は、コマンド履歴がどのぐらい保存されるかを指定していました。代わりに GDBHISTSIZE 環境変数が使用されるように変更になりました。この変数は、GDB に固有になります。可能な値とその効果は次のとおりです。

  • 正の数 - このサイズのコマンド履歴を使用
  • -1 または空の文字列 - コマンド履歴をすべて保持
  • 数値以外の値 - 無視

完了制限が追加される

set max-completions コマンドを使用して、完了時に検討される候補の最大値が制限されるようになりました。現在の制限を表示するには、show max-completions コマンドを実行します。デフォルト値は 200 です。この制限により、GDB が、生成する完了リストが大きすぎて、応答しなくならないようにします。

たとえば、p <tab><tab> の入力後の出力は、以下のようになります。

  • RHEL 7 の場合 - Display all 29863 possibilities? (y or n)
  • RHEL 8 の場合 - Display all 200 possibilities? (y or n)

HP-UX XDB 互換性モードが削除される

HP-UX XDB 互換性モードの -xdb オプションが GDB から削除されています。

スレッドのシグナル処理

GDB は、シグナルが実際に送信されるスレッドの代わりに、現在のスレッドへシグナルを配信していました。このバグは修正され、実行を再開する際に GDB が現在のスレッドへ、シグナルを常に渡すようになりました。

また、signal コマンドは、現在のスレッドに、必要なシグナルを常に正しく配信するようになりました。シグナルに対してプログラムが停止したり、ユーザーがスレッドを切り替えた場合は、GDB により確認が求められます。

ブレークポイントモードが常に挿入され、自動的にマージされる

breakpoint always-inserted 設定が変更しました。auto 値と対応する動作が削除されました。デフォルト値は off です。off の場合は、すべてのスレッドが停止するまで、GDB がターゲットからブレークポイントを削除しないようになります。

remotebaud コマンドがサポート対象外に

set remotebaud コマンドおよび show remotebaud コマンドがサポートされなくなりました。代わりに set serial baud コマンドおよび show serial baud コマンドを使用してください。

A.4. コンパイラーおよび開発ツールにおける互換性に影響を与える変更

std::string および std::list における C++ ABI の変更

RHEL 7 (GCC 4.8) と RHEL 8 (GCC 8) との間で変更した libstdc++ ライブラリーの std::string クラスおよび std::list クラスの Application Binary Interface (ABI) は、C++11 標準に従います。libstdc++ ライブラリーは、古い ABI および新しい ABI の両方をサポートしますが、その他の C++ システムライブラリーはサポートしません。そのため、このライブラリーに動的にリンクするアプリケーションを再構築する必要があります。これは、C++98 を含むすべての C++ 標準モードに影響します。RHEL 7 で Red Hat Developer Toolset コンパイラーを使用して構築したアプリケーションにも影響します。このコンパイラーは、古い ABI を維持して、システムライブラリーとの互換性を維持します。

librtkaio が削除される

この更新では、librtkaio ライブラリーが削除されました。このライブラリーは、ファイルへの高パフォーマンスのリアルタイム非同期 I/O アクセスを提供していました。これは、Linux の KAIO (kernel Asynchronous I/O) サポートに基づいています。

削除の結果は以下のようになります。

  • librtkaio を読み込む LD_PRELOAD メソッドを使用するアプリケーションは、不明なライブラリーに関する警告を表示し、代わりに librt ライブラリーを読み込み、適切に実行します。
  • librtkaio を読み込む LD_LIBRARY_PATH メソッドを使用するアプリケーションは、代わりに librt ライブラリーを読み込んで適切に実行し、警告は表示されません。
  • dlopen() システムコールを使用するアプリケーションでは、代わりに librtkaiolibrt ライブラリーを直接読み込みます。

librtkaio のユーザーには以下のオプションがあります。

  • 自身のアプリケーションを変更せずに、上記のフォールバックメカニズムを使用してください。
  • librt ライブラリーを使用するようにアプリケーションのコードを変更します。互換性のある POSIX 準拠 API を提供します。
  • 互換性のある API を提供する libaio ライブラリーを使用するようにアプリケーションのコードを変更します。

特定の条件では、librtlibaio の両方が、同じ機能および性能を提供します。

Red Hat 互換性レベルは、libaio パッケージが 2 になります。 librtk と削除された librtkaio の場合は 1 です。

詳細は、「Changes/GLIBC223 librtkaio removal」を参照してください。

Sun RPC インターフェースおよび NIS インターフェースが glibc から削除される

glibc ライブラリーは、新しいアプリケーションに Sun RPC および NIS のインターフェースを提供しなくなりました。このインターフェースは、レガシーアプリケーションを実行する場合にのみ利用できるようになりました。開発者は、Sun RPC の代わりに libtirpc ライブラリー、そして NIS の代わりに libnsl2 ライブラリーを使用するようにアプリケーションを変更する必要があります。アプリケーションは、置換ライブラリーの IPv6 サポートを利用します。

MPI デバッグサポート用 Valgrind ライブラリーが削除される

valgrind-openmpi パッケージが提供する Valgrindlibmpiwrap.so ラッパーライブラリーが削除されました。このライブラリーにより、MPI (Message Passing Interface) を使用して、Valgrind がプログラムをデバッグできるようになりました。このライブラリーは、以前のバージョンの Red Hat Enterprise Linux の Open MPI 実装バージョンに固有です。

libmpiwrap.so を使用する場合は、MPI 実装およびバージョンに固有のアップストリームソースから独自のバージョンを構築することが推奨されます。LD_PRELOAD 技術を使用して、カスタムビルドのライブラリーを Valgrind に提供します。

開発用ヘッダーおよび静的ライブラリーが valgrind-devel から削除される

valgrind-devel サブパッケージは、カスタムの valgrind ツールを開発する開発ファイルを追加するために使用されていました。このファイルには保証された API がないため、この更新によりこのファイルが削除され、静的なリンクが必要となり、サポート対象外となります。valgrind-devel パッケージには、valgrind が有効なプログラムや、valgrind.hcallgrind.hdrd.hhelgrind.hmemcheck.h などのヘッダーファイルに対する開発ファイルが含まれます。 このファイルは安定しており、サポートも適切です。

32 ビット Xen の nosegneg ライブラリーが削除される

glibc i686 パッケージは、以前は代替の glibc ビルドに含まれており、負のオフセット (nosegneg) を使用して、スレッド記述子セグメントレジスターの使用を回避していました。この代替ビルドは、ハードウェアの仮想化サポートを使用せずに、フル準仮想化のコストを削除するための最適化として、32 ビットバージョンの Xen Project ハイパーバイザーでのみ使用されます。この代替ビルドはこれ以上使用されず、削除されます。

GCC が、Ada、Go、および Objective C/C++ コードを構築しなくなる

GCC コンパイラーから、Ada (GNAT)、GCC Go、および Objective C/C++ の言語でコードを構築する機能が削除されました。

Go コードを構築する場合は、代わりに Go Toolset を使用します。

make の新しい演算子 != を使用すると、一部の makefile の既存構文で解釈が異なる

BSD makefile との互換性を高める $(shell …​) 関数の代わりに、シェル代入演算子 != が GNU の make に追加されました。これにより、variable!=value のように、感嘆符で終わり、その後に代入が続く名前の変数は、新しいシェル割り当てとして解釈されるようになりました。以前の動作に戻すには、variable! =value のように、感嘆符の後に空白を追加します。

演算子と関数の詳細と相違点は、GNU の make マニュアルを参照してください。

法律上の通知

Copyright © 2019 Red Hat, Inc.
The text of and illustrations in this document are licensed by Red Hat under a Creative Commons Attribution–Share Alike 3.0 Unported license ("CC-BY-SA"). An explanation of CC-BY-SA is available at http://creativecommons.org/licenses/by-sa/3.0/. In accordance with CC-BY-SA, if you distribute this document or an adaptation of it, you must provide the URL for the original version.
Red Hat, as the licensor of this document, waives the right to enforce, and agrees not to assert, Section 4d of CC-BY-SA to the fullest extent permitted by applicable law.
Red Hat, Red Hat Enterprise Linux, the Shadowman logo, the Red Hat logo, JBoss, OpenShift, Fedora, the Infinity logo, and RHCE are trademarks of Red Hat, Inc., registered in the United States and other countries.
Linux® is the registered trademark of Linus Torvalds in the United States and other countries.
Java® is a registered trademark of Oracle and/or its affiliates.
XFS® is a trademark of Silicon Graphics International Corp. or its subsidiaries in the United States and/or other countries.
MySQL® is a registered trademark of MySQL AB in the United States, the European Union and other countries.
Node.js® is an official trademark of Joyent. Red Hat is not formally related to or endorsed by the official Joyent Node.js open source or commercial project.
The OpenStack® Word Mark and OpenStack logo are either registered trademarks/service marks or trademarks/service marks of the OpenStack Foundation, in the United States and other countries and are used with the OpenStack Foundation's permission. We are not affiliated with, endorsed or sponsored by the OpenStack Foundation, or the OpenStack community.
All other trademarks are the property of their respective owners.