RHEL 10 での C および C++ アプリケーションの開発
開発者用ワークステーションのセットアップ、および Red Hat Enterprise Linux 10 での C および C++ アプリケーションの開発とデバッグ
概要
第1章 開発ワークステーションの設定
Red Hat Enterprise Linux 10 は、カスタムアプリケーションの開発をサポートします。この章では、開発における最も一般的なユースケースと、インストールに必要なツールおよびユーティリティーについて説明します。
1.1. 前提条件
- グラフィカル環境を含む RHEL システムがインストールされ、サブスクライブされます。
1.2. デバッグおよびソースのリポジトリーの有効化
Red Hat Enterprise Linux の標準インストールでは、デバッグリポジトリーおよびソースリポジトリーが有効になっていません。このリポジトリーには、システムコンポーネントのデバッグとパフォーマンスの測定に必要な情報が含まれます。
手順
ソースおよびデバッグ情報パッケージチャネルを有効にします。
$(uname -i)
の部分は、システムのアーキテクチャーに一致する値に自動的に置き換えられます。アーキテクチャー名 値 64 ビット Intel および AMD
x86_64
64 ビット ARM
aarch64
IBM POWER
ppc64le
64 ビット IBM Z
s390x
1.3. アプリケーションのバージョンを管理するための設定
複数の開発者が関わるプロジェクトではすべて、効果的なバージョン管理が必須になります。Red Hat Enterprise Linux には、Git という名前の分散型バージョン管理システムが同梱されています。
手順
git パッケージをインストールします。
dnf install git
# dnf install git
Copy to Clipboard Copied! 任意で、Git コミットに関連付ける名前と、メールアドレスを設定します。
git config --global user.name "Full_Name" git config --global user.email "email@example.com"
$ git config --global user.name "Full_Name" $ git config --global user.email "email@example.com"
Copy to Clipboard Copied! Full Name と email@example.com は、自身の名前とメールアドレスに置き換えます。
オプション: Git によって起動されるデフォルトのテキストエディターを変更するには、
core.editor
設定オプションの値を設定します。git config --global core.editor command
$ git config --global core.editor command
Copy to Clipboard Copied! command は、選択のテキストエディターの起動に使用するコマンドに置き換えます。
1.4. C および C++ でアプリケーションを開発するための設定
Red Hat Enterprise Linux には、C および C++ のアプリケーションを作成するツールが同梱されています。
前提条件
- デバッグリポジトリーおよびソースリポジトリーが有効である。
手順
GNU Compiler Collection (GCC)、GNU Debugger (GDB) などの開発ツールが含まれる Development Tools パッケージグループをインストールします。
dnf group install "Development Tools"
# dnf group install "Development Tools"
Copy to Clipboard Copied! clang
コンパイラーとlld
リンカーを含む LLVM ベースのツールチェーンをインストールします。dnf install llvm-toolset
# dnf install llvm-toolset
Copy to Clipboard Copied! 必要に応じて、Fortran 依存関係用に、GNU Fortran コンパイラーをインストールします。
dnf install gcc-gfortran
# dnf install gcc-gfortran
Copy to Clipboard Copied!
1.5. アプリケーションをデバッグするための設定
Red Hat Enterprise Linux には、内部のアプリケーションの動作を分析してトラブルシューティングを行うためのデバッグおよび計測のツールが同梱されています。
前提条件
- デバッグリポジトリーおよびソースリポジトリーが有効である。
手順
デバッグ用のツールをインストールします。
dnf install gdb valgrind systemtap ltrace strace
# dnf install gdb valgrind systemtap ltrace strace
Copy to Clipboard Copied! debuginfo-install
ツールを使用するには、dnf-utils
パッケージをインストールします。dnf install dnf-utils
# dnf install dnf-utils
Copy to Clipboard Copied! 環境設定用の SystemTap ヘルパースクリプトを実行します。
stap-prep
# stap-prep
Copy to Clipboard Copied! stap-prep
は、現在 実行中 のカーネルに関連するパッケージをインストールすることに注意してください。これは、実際にインストールされているカーネルと異なる場合があります。stap-prep
が正しいkernel-debuginfo
およびkernel-headers
パッケージをインストールするには、uname -r
コマンドを使用して現在のカーネルバージョンを再度チェックし、必要に応じてシステムを再起動します。-
SELinux
ポリシーで、関連するアプリケーションを正常に実行できるだけでなく、デバッグ状況でも実行できるようになっていることを確認してください。詳細は、SELinux の使用 を参照してください。
1.6. アプリケーションのパフォーマンスを測定するための設定
Red Hat Enterprise Linux には、開発者がアプリケーションのパフォーマンス低下の原因を特定できるように支援するアプリケーションが同梱されています。
前提条件
- デバッグリポジトリーとソースリポジトリーが有効になっている。
手順
パフォーマンス測定用のツールをインストールします。
dnf install perf papi pcp-zeroconf valgrind strace sysstat systemtap
# dnf install perf papi pcp-zeroconf valgrind strace sysstat systemtap
Copy to Clipboard Copied! 環境設定用の SystemTap ヘルパースクリプトを実行します。
stap-prep
# stap-prep
Copy to Clipboard Copied! stap-prep は、現在 実行中 のカーネルに関連するパッケージをインストールすることに注意してください。これは、実際にインストールされているカーネルと異なる場合があります。stap-prep が正しい kernel-debuginfo および kernel-headers パッケージをインストールするには、
uname -r
コマンドを使用して現在のカーネルバージョンを再度チェックし、必要に応じてシステムを再起動します。Performance Co-Pilot (PCP) コレクターサービスを有効にして開始します。
systemctl enable pmcd && systemctl start pmcd
# systemctl enable pmcd && systemctl start pmcd
Copy to Clipboard Copied!
第2章 C または C++ のアプリケーションの作成
Red Hat は、C 言語および C++ 言語を使用してアプリケーションを作成するための各種のツールを提供しています。このセクションでは、最も一般的な開発タスクの一部を記載しています。
2.1. RHEL 10 の GCC
Red Hat Enterprise Linux 10 には、標準コンパイラーとして GCC 14 が同梱されています。
GCC 14 のデフォルトの言語標準設定は C++17 です。これは、コマンドラインオプション -std=gnu++17
を明示的に使用するのと同じです。
C++20 などの新しい言語標準、およびこれらの新しい言語標準で導入されたライブラリー機能は、まだ実験的なものと見なされています。
2.2. GCC でのビルドコード
ソースコードを実行可能コードに変換する必要がある状況を説明します。
2.2.1. コード形式間の関係
前提条件
- コンパイルとリンクの概念を理解している。
考えられるコード形式
C 言語および C++ 言語には、以下の 3 つのコード形式があります。
C 言語または C++ 言語で記述された ソースコード。プレーンテキストファイルとして表示されます。
このファイルは通常、
.c
、.cc
、.cpp
、.h
、.hpp
、.i
、.inc
などの拡張子を使用します。サポートされる拡張子およびその解釈のリストは、gcc の man ページを参照してください。man gcc
$ man gcc
Copy to Clipboard Copied! コンパイラー でソースコードを コンパイル して作成する オブジェクトコード。これは中間形式です。
オブジェクトコードファイルは、拡張子
.o
を使用します。リンカー でオブジェクトコードを リンク して作成する 実行可能なコード。
Linux アプリケーションの実行可能ファイルは、ファイル名の拡張子を使用しません。共有オブジェクト (ライブラリー) の実行可能ファイルは、
.so
のファイル名の拡張子を使用します。
静的リンク用のライブラリーアーカイブファイルも存在します。これは、ファイル名拡張子 .a
を使用するオブジェクトコードのバリアントです。静的リンクは推奨されません。「静的リンクおよび動的リンク」を参照してください。
GCC でのコード形式の処理
ソースコードから実行可能なコードを生成するには、2 つの手順を行います。必要となるアプリケーションまたはツールはそれぞれ異なります。GCC は、コンパイラーとリンカーのどちらにも、インテリジェントドライバーとして使用できます。これにより、必要なアクション (コンパイルおよびリンク) に gcc
コマンドを 1 つ使用できます。GCC は、アクションとそのシーケンスを自動的に選択します。
- ソースファイルを、オブジェクトファイルにコンパイルする
- オブジェクトファイルおよびライブラリーはリンクされます (以前にコンパイルしたソールも含む)。
GCC を実行して、1 つのステップでコンパイルのみ、リンクのみ、またはコンパイルとリンクの両方を実行できます。これは、入力タイプや必要とされる出力タイプにより決定されます。
大規模なプロジェクトには、アクションごとに個別に GCC を実行するビルドシステムが必要なため、GCC が両方同時に実行できる場合でも 2 つの異なるアクションとしてコンパイルとリンクを実行することを検討することが推奨されます。
2.2.2. オブジェクトコードへのソースファイルのコンパイル
オブジェクトコードファイルを、実行ファイルから直接作成するのではなく、ソースファイルから作成するには、GCC で、オブジェクトコードファイルのみを出力として作成するように必要があります。このアクションは、大規模なプロジェクトのビルドプロセスの基本操作となります。
前提条件
- C または C++ のソースコードファイルがある。
- GCC がシステムにインストールされている
手順
- ターミナルで、ソースコードファイルが含まれているディレクトリーを開きます。
-c
オプションを指定してgcc
を実行します。gcc -c source.c another_source.c
$ gcc -c source.c another_source.c
Copy to Clipboard Copied! オブジェクトファイルは、オリジナルのソースコードファイルを反映したファイル名を使用して作成されます。
source.c
はsource.o
になります。注記C++ ソースコードの場合は、標準 C++ ライブラリーの依存関係を処理しやすくするために、
gcc
コマンドをg++
に置き換えます。
2.2.3. GCC で C および C++ のアプリケーションのデバッグの有効化
デバッグ情報のサイズが大きい場合、その情報はデフォルトで実行可能ファイルに含まれません。GCC を使用した C および C++ のアプリケーションのデバッグを有効にするには、コンパイラーに対して、デバッグデータを作成するように、明示的に指示する必要があります。
コードのコンパイルおよびリンク時に、GCC でデバッグ情報の作成を有効にするには、-g
オプションを使用します。
gcc ... -g ...
$ gcc ... -g ...
-
コンパイラーとリンカーで最適化を実行すると、元のソースコードと関連付けることが難しい実行可能コードが生成される場合があります。変数が最適化されたり、ループがアンロールされたり、操作が周囲の操作にマージされたりする可能性があります。これにより、デバッグに負の影響が及ぶ可能性があります。デバッグエクスペリエンスを向上させるには、
-Og
オプションを指定して、最適化を設定することを検討してください。ただし、最適化レベルを変更すると、実行可能なコードが変更になり、バグを取り除くための動作が変更する可能性があります。 -
デバッグ情報にマクロ定義も追加するには、
-g
の代わりに-g3
オプションを使用します。 -
-fcompare-debug
GCC オプションでは、GCC でコンパイルしたコードを、デバッグ情報を使用した場合とデバッグ情報を使用しない場合でテストします。このテストでは、出力されたバイナリーファイルの 2 つが同一であれば合格します。このテストを行うことで、実行可能なコードがデバッグオプションによる影響は受けないようにするだけでなく、デバッグコードにバグが含まれないようにします。-fcompare-debug
オプションを使用するとコンパイルの時間が大幅に伸びることに留意してください。このオプションに関する詳細は、GCC の man ページを参照してください。
2.2.4. GCC でのコードの最適化
1 つのプログラムは、複数の機械語命令シーケンスに変換できます。コンパイル時にコードを分析するためにより多くのリソースを割り当てると、より最適な結果が得られます。
GCC では、-Olevel
オプションを使用して最適化レベルを設定できます。このオプションでは、level の部分に値を指定できます。
レベル | 説明 |
---|---|
| コンピレーション速度の最適化 - コードの最適化なし (デフォルト) |
| 最適化して、コード実行速度を向上させます (数値が大きいほど、速度は高くなります)。 |
| ファイルサイズを最適化します。 |
|
レベルを |
| デバッグ作業の最適化 |
リリースビルドの場合は、最適化オプション -O2
を使用します。
開発中は、-Og
オプションは、状況によってはプログラムまたはライブラリーをデバッグするのに役立ちます。バグによっては、特定の最適化レベルでのみ出現するため、リリースの最適化レベルでプログラムまたはライブラリーをテストしてください。
GCC では、個別の最適化を有効にするオプションが多数含まれています。詳細は、関連情報のセクションを参照してください。
2.2.5. GCC でコードを強化するオプション
コンパイラーで、ソースコードをオブジェクトコードに変換する場合には、さまざまなチェックを追加して、一般的に悪用される状況などを回避し、セキュリティーを強化できます。適切なコンパイラーオプションセットを選択すると、ソースコードを変更せずに、よりセキュアなプログラムやライブラリーを作成できます。
リリースバージョンのオプション
Red Hat Enterprise Linux を使用する開発者には、以下のオプションリストが推奨される最小限のオプションとなります。
gcc ... -O2 -g -Wall -Wl,-z,now,-z,relro -fstack-protector-strong -fstack-clash-protection -D_FORTIFY_SOURCE=3 ...
$ gcc ... -O2 -g -Wall -Wl,-z,now,-z,relro -fstack-protector-strong -fstack-clash-protection -D_FORTIFY_SOURCE=3 ...
-
プログラムの場合は、
-fPIE
および-pie
の位置独立実行可能オプションを追加します。 -
動的にリンクされたライブラリーの場合は、必須の
-fPIC
(位置独立コード) オプションを使用すると間接的にセキュリティーが強化されます。
開発オプション
開発時にセキュリティーの欠陥を検出する場合は、以下のオプションを使用します。これらのオプションは、リリースバージョンのオプションと併せて使用してください。
gcc ... -Walloc-zero -Walloca-larger-than -Wextra -Wformat-security -Wvla-larger-than ...
$ gcc ... -Walloc-zero -Walloca-larger-than -Wextra -Wformat-security -Wvla-larger-than ...
-fhardened
GCC 14 では新しいフラグ -fhardened
が提供され、これにより他の多くのフラグが有効になり、ABI に影響を与えずに生成されたコードのセキュリティーが向上します。
-fanalyzer
GCC には、セキュリティー関連の問題を含む、ソースコード内の潜在的な問題についての警告をトリガーするフラグ -fanalyzer
が用意されています。-fanalyzer
では誤検出や検出漏れが頻繁に発生するため、正式な解析ツールとしてではなく、詳細な調査が必要な潜在的なバグを見つけるための参考ツールとして使用する必要があります。このフラグにより、コンパイル中に使用される時間とメモリーが大幅に増加します。C コードでのみ使用してください。
関連情報
2.2.6. 実行ファイルを作成するコードのリンク
C または C++ のアプリケーション構築の最後の手順は、リンクです。リンクにより、オブジェクトファイルやライブラリーがすべて実行可能ファイルに統合されます。
前提条件
- オブジェクトファイルが 1 つまたは複数ある。
- GCC がシステムにインストールされている。
手順
- オブジェクトコードファイルを含むディレクトリーに移動します。
gcc
を実行します。gcc ... objfile.o another_object.o ... -o executable-file
$ gcc ... objfile.o another_object.o ... -o executable-file
Copy to Clipboard Copied! executable-file という名前の実行ファイルが、指定したオブジェクトファイルとライブラリーをベースに作成されます。
追加のライブラリーをリンクするには、オブジェクトファイルのリストの前に、必要なオプションを追加します。詳細は、「GCC でのライブラリーの使用」 を参照してください。
注記C++ ソースコードの場合は、標準 C++ ライブラリーの依存関係を処理しやすくするために、
gcc
コマンドをg++
に置き換えます。
2.2.7. 例: GCC で C プログラムの構築 (1 つの手順でコンパイルとリンク)
以下の例では、簡単な C++ のサンプルプログラムを構築する手順を説明します。
この例では、コードをコンパイルおよびリンクする方法を 1 つの手順で行います。
手順
hello-c
ディレクトリーを作成して、そのディレクトリーに移動します。mkdir hello-c cd hello-c
$ mkdir hello-c $ cd hello-c
Copy to Clipboard Copied! 以下の内容を含む
hello.c
ファイルを作成します。#include <stdio.h> int main() { printf("Hello, World!\n"); return 0; }
#include <stdio.h> int main() { printf("Hello, World!\n"); return 0; }
Copy to Clipboard Copied! GCC でコードをコンパイルし、リンクします。
gcc hello.c -o helloworld
$ gcc hello.c -o helloworld
Copy to Clipboard Copied! これにより、コードがコンパイルされ、オブジェクトファイル
hello.o
が作成され、オブジェクトファイルから実行ファイルhelloworld
がリンクされます。作成された実行可能ファイルを実行します。
./helloworld
$ ./helloworld Hello, World!
Copy to Clipboard Copied!
2.2.8. 例: GCC を使用した C プログラムの構築 (2 つの手順でコンパイルとリンク)
以下の例では、簡単な C++ のサンプルプログラムを構築する手順を説明します。
この例では、コードのコンパイルとリンクは、2 つの別個のステップです。
手順
hello-c
ディレクトリーを作成して、そのディレクトリーに移動します。mkdir hello-c cd hello-c
$ mkdir hello-c $ cd hello-c
Copy to Clipboard Copied! 以下の内容を含む
hello.c
ファイルを作成します。#include <stdio.h> int main() { printf("Hello, World!\n"); return 0; }
#include <stdio.h> int main() { printf("Hello, World!\n"); return 0; }
Copy to Clipboard Copied! GCC でコードをコンパイルします。
gcc -c hello.c
$ gcc -c hello.c
Copy to Clipboard Copied! オブジェクトファイル
hello.o
が作成されます。オブジェクトファイルから実行可能ファイル
helloworld
をリンクします。gcc hello.o -o helloworld
$ gcc hello.o -o helloworld
Copy to Clipboard Copied! 作成された実行可能ファイルを実行します。
./helloworld
$ ./helloworld Hello, World!
Copy to Clipboard Copied!
2.2.9. 例: GCC で C++ プログラムの構築 (1 つの手順でコンパイルとリンク)
以下の例では、最小限の C++ プログラムのサンプルを構築する手順を説明します。
この例では、コードをコンパイルおよびリンクする方法を 1 つの手順で行います。
手順
hello-cpp
ディレクトリーを作成して、そのディレクトリーに移動します。mkdir hello-cpp cd hello-cpp
$ mkdir hello-cpp $ cd hello-cpp
Copy to Clipboard Copied! 以下の内容を含む
hello.cpp
ファイルを作成します。#include <iostream> int main() { std::cout << "Hello, World!\n"; return 0; }
#include <iostream> int main() { std::cout << "Hello, World!\n"; return 0; }
Copy to Clipboard Copied! g++
でコードをコンパイルし、リンクします。g++ hello.cpp -o helloworld
$ g++ hello.cpp -o helloworld
Copy to Clipboard Copied! これにより、コードがコンパイルされ、オブジェクトファイル
hello.o
が作成され、オブジェクトファイルから実行ファイルhelloworld
がリンクされます。作成された実行可能ファイルを実行します。
./helloworld
$ ./helloworld Hello, World!
Copy to Clipboard Copied!
2.2.10. 例: GCC を使用した C++ プログラムの構築 (2 つの手順でコンパイルとリンク)
以下の例では、最小限の C++ プログラムのサンプルを構築する手順を説明します。
この例では、コードのコンパイルとリンクは、2 つの別個のステップです。
手順
hello-cpp
ディレクトリーを作成して、そのディレクトリーに移動します。mkdir hello-cpp cd hello-cpp
$ mkdir hello-cpp $ cd hello-cpp
Copy to Clipboard Copied! 以下の内容を含む
hello.cpp
ファイルを作成します。#include <iostream> int main() { std::cout << "Hello, World!\n"; return 0; }
#include <iostream> int main() { std::cout << "Hello, World!\n"; return 0; }
Copy to Clipboard Copied! g++
でコードをコンパイルします。g++ -c hello.cpp
$ g++ -c hello.cpp
Copy to Clipboard Copied! オブジェクトファイル
hello.o
が作成されます。オブジェクトファイルから実行可能ファイル
helloworld
をリンクします。g++ hello.o -o helloworld
$ g++ hello.o -o helloworld
Copy to Clipboard Copied! 作成された実行可能ファイルを実行します。
./helloworld
$ ./helloworld Hello, World!
Copy to Clipboard Copied!
2.3. GCC でのライブラリーの使用
コード内でのライブラリーの使用を説明します。
2.3.1. ライブラリーの命名規則
ライブラリーには特別なファイル名規則が使用されます。つまり、foo として知られるライブラリーは、libfoo.so
ファイルまたは libfoo.a
ファイルとして存在する必要があります。この規則は、リンクする GCC の入力オプションでは自動的に理解されますが、出力オプションでは理解されません。
ライブラリーにリンクする場合は、ライブラリーは
-l
オプションを使用して、-lfoo
のように名前 foo でのみ指定できます。gcc ... -lfoo ...
$ gcc ... -lfoo ...
Copy to Clipboard Copied! -
ライブラリーの作成時には、
libfoo.so
またはlibfoo.a
の完全なファイル名を指定する必要があります。
関連情報
2.3.2. 静的リンクおよび動的リンク
開発者は、完全にコンパイルされた言語でアプリケーションを構築する際に、静的リンクまたは動的リンクを使用できます。特に Red Hat Enterprise Linux で C および C++ 言語を使用するコンテキストでは、静的リンクと動的リンクの違いを理解することが重要です。Red Hat は、Red Hat Enterprise Linux のアプリケーションで静的リンクを使用することは推奨していません。
静的リンクおよび動的リンクの比較
静的リンクは、作成される実行可能ファイルのライブラリーの一部になります。動的リンクは、これらのライブラリーを別々のファイルとして保持します。
静的リンクには多くの欠点があり、特にアプリケーション全体や glibc および libstdc++ ライブラリーでは避ける必要があります。
- リソースの使用
静的リンクにより、より多くのコードが含まれるより大きな実行可能ファイルが生成されます。ライブラリーからのこの追加コードはシステムのプログラム間で共有できないため、ランタイム時にファイルシステムの使用量とメモリーの使用量が増加します。静的にリンクされた同じプログラムを実行している複数のプロセスは依然としてコードを共有します。
一方、静的アプリケーションは、必要なランタイムの再配置も少なくなるため、起動時間が短縮します。また、必要なプライベートの RSS (Resident 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()
関数に対する呼び出しの形式をサポートしません。
静的リンクの場合
以下のようなケースでは、静的リンクは妥当なオプションとして選択できます。
- 動的リンクが有効になっていないライブラリーを使用する場合。
-
空の chroot 環境またはコンテナーでコードを実行するために完全に静的なリンクが必要な場合。ただし、
glibc-static
パッケージを使用した静的リンクは、Red Hat ではサポートされません。
2.3.3. リンク時最適化
リンク時最適化 (LTO) には、次のような利点があります。
- LTO を使用すると、コンパイラーはリンク時に中間表現を使用して、プログラムのすべての変換ユニットでさまざまな最適化を実行できます。その結果、実行可能ファイルとライブラリーが小さくなり、実行速度が速くなります。
- LTO を使用すると、コンパイル時にパッケージのソースコードをより徹底的に分析できます。これにより、潜在的なコーディングエラーに対するさまざまな GCC 診断が向上します。
既知の問題
LTO には次の既知の問題があります。
単一定義規則 (ODR) に違反すると、
-Wodr
警告が生成されます未定義の動作をもたらす ODR の違反は、
-Wodr
警告を生成します。これは通常、プログラムのバグを示しています。-Wodr
警告はデフォルトで有効になっています。LTO はメモリー消費の増加を引き起こします
コンパイラーは、プログラムを構成する変換単位を処理するときに、より多くのメモリーを消費します。メモリーが制限されているシステムでは、プログラムをビルドするときに LTO を無効にするか、並列処理レベルを下げてください。
GCC は一見未使用の機能を削除します
GCC は、asm() ステートメントが参照するシンボルをコンパイラーが認識できないため、未使用とみなされる関数を削除する場合があります。その結果、コンパイルエラーが発生する場合があります。これを防ぐには、プログラムで使用するシンボルに
__attribute__((used))
を追加します。-fPIC
オプションを使用してコンパイルすると、エラーが発生しますGCC は asm() ステートメントの内容を解析しないため、
-fPIC
コマンドラインオプションを使用してコードをコンパイルすると、エラーが発生する可能性があります。これを防ぐには、変換ユニットをコンパイルするときに-fno-lto
オプションを使用します。
詳細は、LTO FAQ — Symbol usage from assembly language を参照してください。asm() ステートメントで
.symver
ディレクティブを使用してシンボルのバージョン管理を実装することは、LTO と互換性がありません。ただし、symver
属性を使用してシンボルのバージョン管理を実装することは可能です。以下に例を示します。__attribute__ ((_symver_ ("<symbol>@VERS_1"))) void <symbol>_v1 (void) { }
2.3.4. GCC でのライブラリーの使用
ライブラリーは、プログラムで再利用可能なコードのパッケージです。C または C++ のライブラリーは、以下の 2 つの部分で構成されます。
- ライブラリーコード
- ヘッダーファイル
ライブラリーを使用するコードのコンパイル
ヘッダーファイルでは、ライブラリーで提供する関数や変数など、ライブラリーのインターフェイスを記述します。コードをコンパイルする場合に、ヘッダーファイルの情報が必要です。
通常、ライブラリーのヘッダーファイルは、アプリケーションのコードとは別のディレクトリーに配置されます。ヘッダーファイルの場所を GCC に指示するには、-I
オプションを使用します。
gcc ... -Iinclude_path ...
$ gcc ... -Iinclude_path ...
include_path は、ヘッダーファイルのディレクトリーのパスに置き換えます。
-I
オプションは、複数回使用して、ヘッダーファイルを含むディレクトリーを複数追加できます。ヘッダーファイルを検索する場合は、-I
オプションで表示される順序で、これらのディレクトリーが検索されます。
ライブラリーを使用するコードのリンク
実行ファイルをリンクする場合には、アプリケーションのオブジェクトコードと、ライブラリーのバイナリーコードの両方が利用できる状態でなければなりません。静的ライブラリーおよび動的ライブラリーのコードは、形式が異なります。
-
静的なライブラリーは、アーカイブファイルとして利用できます。静的なライブラリーには、一連のオブジェクトファイルが含まれます。アーカイブファイルのファイル名の拡張子は
.a
になります。 -
動的なライブラリーは共有オブジェクトとして利用できます。実行ファイルの形式です。共有オブジェクトのファイル名の拡張子は
.so
になります。
ライブラリーのアーカイブファイルまたは共有オブジェクトファイルの場所を GCC に指示するには、-L
オプションを使用します。
gcc ... -Llibrary_path -lfoo ...
$ gcc ... -Llibrary_path -lfoo ...
library_path は、ライブラリーのディレクトリーのパスに置き換えます。
-L
オプションは複数回使用して、ディレクトリーを複数追加できます。ライブラリーを検索する場合は、-L
オプションで表示される順序で、これらのディレクトリーが検索されます。
オプションの指定順は重要です。対象のライブラリーがディレクトリーにリンクされていることが分からないと、GCC は、ライブラリー foo をリンクできません。そのため、-L
オプションを使用して先にライブラリーディレクトリーを指定してから、-l
オプションを使用してライブラリーをリンクするようにしてください。
1 つの手順でライブラリーを使用するコードをコンパイルおよびリンクする方法
1 つの gcc
コマンドでコードをコンパイルおよびリンクできる場合は、上記の両方の状況のオプションを同時に使用します。
2.3.5. GCC での静的ライブラリーの使用
静的なライブラリーは、オブジェクトファイルを含むアーカイブとして利用できます。リンクを行うと、作成された実行ファイルの一部となります。
Red Hat は、セキュリティー上の理由から、静的リンクを使用することは推奨していません。「静的リンクおよび動的リンク」を参照してください。静的リンクは、特に Red Hat が提供するライブラリーに対して、必要な場合に限り使用してください。
前提条件
- GCC がシステムにインストールされている。
- 静的リンクおよび動的リンクを理解している。
- 有効なプログラムを設定するソースまたはオブジェクトのファイルセット。静的ライブラリー foo だけが必要です。
-
foo ライブラリーは
libfoo.a
ファイルとして利用でき、動的リンクにlibfoo.so
ファイルは指定されていない。
Red Hat Enterprise Linux に含まれるライブラリーのほとんどは、動的リンク用としてのみサポートされています。次の手順は、動的リンクに 無効 のライブラリーに対してのみ有効です。「静的リンクおよび動的リンク」を参照してください。
手順
ソースとオブジェクトファイルからプログラムをリンクするには、静的にリンクされたライブラリー foo (libfoo.a
として検索可能) を追加します。
- コードが含まれるディレクトリーに移動します。
foo ライブラリーのヘッダーで、プログラムソースファイルをコンパイルします。
gcc ... -Iheader_path -c ...
$ gcc ... -Iheader_path -c ...
Copy to Clipboard Copied! header_path を、foo ライブラリーのヘッダーファイルを含むディレクトリーのパスに置き換えます。
プログラムを foo ライブラリーにリンクします。
gcc ... -Llibrary_path -lfoo ...
$ gcc ... -Llibrary_path -lfoo ...
Copy to Clipboard Copied! library_path は、
libfoo.a
ファイルを含むディレクトリーへのパスに置き換えます。あとでプログラムを実行するには、次のコマンドを実行します。
./program
$ ./program
Copy to Clipboard Copied!
静的リンクに関連付けられる GCC オプション -static
は、すべての動的リンクを禁止します。代わりに -Wl,-Bstatic
および -Wl,-Bdynamic
オプションを使用して、リンカーの動作をより正確に制御します。GCC での静的および動的ライブラリーの両方の使用
2.3.6. GCC での動的ライブラリーの使用
動的ライブラリーは、スタンドアロンの実行ファイルとして提供します。このファイルは、リンク時およびランタイム時に必要です。このファイルは、アプリケーションの実行可能ファイルからは独立しています。
前提条件
- GCC がシステムにインストールされている。
- 有効なプログラムを設定するソースまたはオブジェクトファイルセットがある。動的ライブラリー foo だけが必要になります。
- foo ライブラリーが libfoo.so ファイルとして利用できる。
プログラムの動的ライブラリーへのリンク
動的ライブラリー foo にプログラムをリンクするには、次のコマンドを実行します。
gcc ... -Llibrary_path -lfoo ...
$ gcc ... -Llibrary_path -lfoo ...
プログラムを動的ライブラリーにリンクすると、作成されるプログラムは常にランタイム時にライブラリーを読み込む必要があります。ライブラリーの場所を特定するオプションは 2 つあります。
-
実行可能ファイルそのものに保存された
run path
値を使用する方法 -
ランタイム時に
LD_LIBRARY_PATH
変数を使用する方法
実行可能ファイルに保存された run path
値を使用する方法
run path
は、リンク時に実行可能ファイルの一部として保存される特殊な値です。後に実行可能ファイルからプログラムがロードされる際に、ランタイムリンカーが run path
値を使用してライブラリーファイルの場所を特定します。
GCC とリンクしているときに、library_path パスを run path
として保存するには、以下を実行します。
gcc ... -Llibrary_path -lfoo -Wl,-run path=library_path ...
$ gcc ... -Llibrary_path -lfoo -Wl,-run path=library_path ...
library_path のパスは、libfoo.so ファイルを含むディレクトリーを参照する必要があります。
-Wl,-run path=
オプションのコンマの後にスペースを追加しないでください。
あとでプログラムを実行するには、次のコマンドを実行します。
./program
$ ./program
LD_LIBRARY_PATH
環境変数を使用する方法
ライブラリーを見つけるための検索パスを設定する別の方法は、LD_LIBRARY_PATH
環境変数を使用することです。この変数の値は、プログラムごとに変更する必要があります。この値は、共有ライブラリーオブジェクトが配置されているパスを表す必要があり、プログラム呼び出しごとに設定する必要があります。
library_path パスにあるライブラリーを使用してプログラムを実行するには、以下を使用します。
export LD_LIBRARY_PATH=library_path:$LD_LIBRARY_PATH ./program
$ export LD_LIBRARY_PATH=library_path:$LD_LIBRARY_PATH
$ ./program
実行パスと LD_LIBRARY_PATH
のインタラクション
Red Hat Enterprise Linux 10 では、リンク中にプログラムにエンコードされた run path
は、リンクされたライブラリーが LD_LIBRARY_PATH
に見つからない場合のみ使用されます。-Wl
、--disable-new-dtags
オプションを使用すると、LD_LIBRARY_PATH
の前に実行パスが検索される Red Hat Enterprise Linux 10 の古い動作を復元できます。
ライブラリーのデフォルトディレクトリーへの配置
ランタイムのリンカー設定では、複数のディレクトリーを動的ライブラリーファイルのデフォルトの場所として指定します。このデフォルトの動作を使用するには、ライブラリーを適切なディレクトリーにコピーします。
動的リンカーの動作に関する詳細な説明は、このドキュメントの対象外です。詳細は、以下の資料を参照してください。
動的リンカーの Linux man ページ:
man ld.so
$ man ld.so
Copy to Clipboard Copied! /etc/ld.so.conf
設定ファイルの内容:cat /etc/ld.so.conf
$ cat /etc/ld.so.conf
Copy to Clipboard Copied! 追加設定なしに動的リンカーにより認識されるライブラリーのレポート (ディレクトリーを含む):
ldconfig -v
$ ldconfig -v
Copy to Clipboard Copied!
2.3.7. 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 ...
$ gcc ... path/to/libfoo.a ...
ファイルの拡張子 .a
から、gcc は、このファイルがプログラムとリンクするためのライブラリーであることを理解します。ただし、ライブラリーファイルの完全パスを指定するのは柔軟な方法ではありません。
-Wl
オプションの使用
gcc オプションの -Wl
は、基盤のリンカーにオプションを渡す特別なオプションです。このオプションの構文は、他の gcc オプションとは異なります。-Wl
オプションの後に、リンカーのオプションをコンマ区切りのリストにして入力します。ただし、他の gcc オプションには、スペース区切りのオプションのリストが必要です。
gcc が使用する ld リンカーには、-Bstatic
と -Bdynamic
のオプションがあり、このオプションに続くライブラリーをそれぞれ静的にリンクするか動的にリンクするかを指定します。-Bstatic
とライブラリーをリンカーに渡した後、後続のライブラリーを -Bdynamic
オプションで動的にリンクするには、デフォルトの動的リンクの動作を手動で復元する必要があります。
プログラムをリンクするには、first ライブラリーを静的にリンク (libfirst.a
) して、second ライブラリーを動的にリンク (libsecond.so
) します。
gcc ... -Wl,-Bstatic -lfirst -Wl,-Bdynamic -lsecond ...
$ gcc ... -Wl,-Bstatic -lfirst -Wl,-Bdynamic -lsecond ...
gcc は、デフォルトの ld 以外のリンカーを使用するように設定できます。
2.4. GCC でのライブラリーの作成
ライブラリーを作成する手順と、Linux オペレーティングシステムでライブラリーに使用される必要な概念を説明します。また、ライブラリーに使用される特別なファイル名規則を学習する必要があります。「ライブラリーの命名規則」を参照してください。
2.4.1. soname のメカニズム
動的に読み込んだライブラリー (共有オブジェクト) は、soname と呼ばれるメカニズムを使用して、複数の互換性のあるライブラリーを管理します。
前提条件
- 動的リンクとライブラリーを理解している。
- ABI の互換性の概念を理解している。
- ライブラリーの命名規則を理解している。
- シンボリックリンクを理解している。
問題の概要
動的に読み込んだライブラリー (共有オブジェクト) は、独立した実行ファイルとして存在します。そのため、依存するアプリケーションを更新せずに、ライブラリーを更新できます。ただし、この概念では、以下の問題が発生します。
- 実際のライブラリーバージョンを特定
- 同じライブラリーに対して複数のバージョンが必要
- 複数のバージョンでそれぞれ ABI の互換性を示す
soname のメカニズム
この問題を解決するには、Linux では soname と呼ばれるメカニズムを使用します。
foo
ライブラリーの X.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
$ objdump -p somelibrary | grep SONAME
somelibrary は、検証するライブラリーのファイル名に置き換えます。
2.4.2. GCC での動的ライブラリーの作成
動的にリンクされたライブラリー (共有オブジェクト) では以下が可能です。
- コードを再利用してリソースを予約する
- ライブラリーコードの更新を容易化にしてセキュリティーを強化する
以下の手順に従って、ソースから動的ライブラリーを構築してインストールします。
前提条件
- soname メカニズムを理解している。
- GCC がシステムにインストールされている。
- ライブラリーのソースコードがある。
手順
- ライブラリーソースのディレクトリーに移動します。
位置独立コードオプション
-fPIC
でオブジェクトファイルに各ソースファイルをコンパイルします。gcc ... -c -fPIC some_file.c ...
$ gcc ... -c -fPIC some_file.c ...
Copy to Clipboard Copied! オブジェクトファイルは、オリジナルのソースコードファイルと同じファイル名ですが、拡張子が
.o
となります。オブジェクトファイルから共有ライブラリーをリンクします。
gcc -shared -o libfoo.so.x.y -Wl,-soname,libfoo.so.x some_file.o ...
$ gcc -shared -o libfoo.so.x.y -Wl,-soname,libfoo.so.x some_file.o ...
Copy to Clipboard Copied! 使用するメジャーバージョン番号は X で、マイナーバージョン番号は Y です。
libfoo.so.x.y
ファイルを、システムの動的リンカーが検索できる適切な場所にコピーします。Red Hat Enterprise Linux では、ライブラリーのディレクトリーは/usr/lib64
です。cp libfoo.so.x.y /usr/lib64
# cp libfoo.so.x.y /usr/lib64
Copy to Clipboard Copied! このディレクトリーにあるファイルを操作するには、root パーミッションが必要な点に注意してください。
soname メカニズムのシンボリックリンク構造を作成します。
ln -s libfoo.so.x.y libfoo.so.x ln -s libfoo.so.x libfoo.so
# ln -s libfoo.so.x.y libfoo.so.x # ln -s libfoo.so.x libfoo.so
Copy to Clipboard Copied!
関連情報
- Linux ドキュメントプロジェクト - Program Library HOWTO - 3.共有ライブラリー
2.4.3. GCC および ar での静的ライブラリーの作成
オブジェクトファイルを特別なアーカイブファイルに変換して、静的にリンクするライブラリーを作成できます。
Red Hat は、セキュリティー上の理由から、静的リンクの使用は推奨していません。静的リンクは、特に Red Hat が提供するライブラリーに対して、必要な場合にのみ使用してください。詳細は 「静的リンクおよび動的リンク」 を参照してください。
前提条件
- GCC と binutils がシステムにインストールされている。
- 静的リンクおよび動的リンクを理解している。
- ライブラリーとして共有している関数を含むソースファイルが利用できる。
手順
GCC で仲介となるオブジェクトファイルを作成します。
gcc -c source_file.c ...
$ gcc -c source_file.c ...
Copy to Clipboard Copied! 必要に応じて、さらにソースファイルを追加します。作成されるオブジェクトファイルはファイル名を共有しますが、拡張子は
.o
を使用します。binutils
パッケージのar
ツールを使用して、オブジェクトファイルを静的ライブラリー (アーカイブ) に変換します。ar rcs libfoo.a source_file.o ...
$ ar rcs libfoo.a source_file.o ...
Copy to Clipboard Copied! libfoo.a
ファイルが作成されます。nm
コマンドを使用して、作成されたアーカイブを検証します。nm libfoo.a
$ nm libfoo.a
Copy to Clipboard Copied! - 静的ライブラリーファイルを適切なディレクトリーにコピーします。
ライブラリーにリンクする場合、GCC は自動的に
.a
のファイル名の拡張子 (ライブラリーが静的リンクのアーカイブであること) を認識します。gcc ... -lfoo ...
$ gcc ... -lfoo ...
Copy to Clipboard Copied!
2.5. Make でのさらなるコードの管理
GNU make ユーティリティー (略称 make) は、ソースファイルからの実行可能ファイルの生成を管理するツールです。make は自動的に、複雑なプログラムのどの部分が変更されたかを判断し、再コンパイルする必要があります。make は、Makefiles と呼ばれる絵設定ファイルを使用して、プログラムを構築する方法を管理します。
2.5.1. GNU make
および Makefile
の概要
特定のプロジェクトのソースファイルから使用可能な形式 (通常は実行ファイル) を作成するには、必要な手順を完了します。後で繰り返し実行できるように、アクションとそのシーケンスを記録します。
Red Hat Enterprise Linux には、この目的に合わせて設計されたビルドシステムである、GNU make
が含まれています。
前提条件
- コンパイルとリンクの概念を理解している。
GNU make
GNU make
は、ビルドプロセスの命令が含まれる Makefile を読み込みます。Makefile には、特定のアクション (レシピ) で特定の条件 (ターゲット) を満たす方法を記述する複数の ルール が含まれています。ルールは、別のルールに階層的に依存できます。
オプションを指定せずに make
を実行すると、カレントディレクトリーで Makefile が検索され、デフォルトのターゲットに到達しようとします。実際の Makefile ファイル名は Makefile
、makefile
、および 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.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)
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 変数が定義されている行に追加します。
2.5.2. 例: Makefile を使用した C プログラムの構築
この例の手順に従い、Makefile を使用して C のサンプルプログラムを構築します。
手順
hellomake
ディレクトリーを作成して、そのディレクトリーに移動します。mkdir hellomake cd hellomake
$ mkdir hellomake $ cd hellomake
Copy to Clipboard Copied! 以下の内容で
hello.c
ファイルを作成します。#include <stdio.h> int main(int argc, char *argv[]) { printf("Hello, World!\n"); return 0; }
#include <stdio.h> int main(int argc, char *argv[]) { printf("Hello, World!\n"); return 0; }
Copy to Clipboard Copied! 以下の内容で
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)
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)
Copy to Clipboard Copied! 重要Makefile レシピの行は、Tab 文字で開始する必要があります。上記のテキストをドキュメントからコピーする際に、カットアンドペーストのプロセスでは、タブではなくスペースが貼り付けられる場合があります。この場合は、手動で修正してください。
make
を実行します。make
$ make gcc -c -Wall hello.c -o hello.o gcc hello.o -o hello
Copy to Clipboard Copied! このコマンドで、実行可能ファイル
hello
が作成されます。この実行可能ファイル
hello
を実行します。./hello
$ ./hello Hello, World!
Copy to Clipboard Copied! Makefile ターゲットの
clean
を実行して、作成されたファイルを削除します。make clean
$ make clean rm -rf hello.o hello
Copy to Clipboard Copied!
関連情報
2.5.3. make
のドキュメント
make
の詳細は、以下に記載のドキュメントを参照してください。
インストールされているドキュメント
man
ツールおよびinfo
ツールを使用して、お使いのシステムにインストールされている man ページと情報ページを表示します。man make info make
$ man make $ info make
Copy to Clipboard Copied!
オンラインドキュメント
- Free Software Foundation 提供の GNU Make Manual
第3章 アプリケーションのデバッグ
デバッグアプリケーションのトピックは非常に広範囲です。ここでは、開発者向けに複数の状況でデバッグを行うための最も一般的な手法を説明します。
3.1. デバッグ情報を使用したデバッグの有効化
アプリケーションおよびライブラリーをデバッグするには、デバッグ情報が必要です。次のセクションでは、この情報を取得する方法を説明します。
3.1.1. デバッグの情報
実行なコードをデバッグしている場合は、2 種類の情報により、ツール、さらにはプログラマーがバイナリーコードを理解できます。
- ソースコードテキスト
- ソースコードテキストがバイナリーコードにどのように関連しているのかの説明
このような情報はデバッグ情報と呼ばれます。
Red Hat Enterprise Linux は、実行可能なバイナリー、共有ライブラリー、または debuginfo
ファイルに ELF 形式を使用します。これらの ELF ファイル内では、DWARF 形式を使用してデバッグ情報が維持されます。
ELF ファイルに保存されている DWARF 情報を表示するには、readelf -w file
コマンドを実行します。
3.1.2. GCC で C および C++ のアプリケーションのデバッグの有効化
デバッグ情報のサイズが大きい場合、その情報はデフォルトで実行可能ファイルに含まれません。GCC を使用した C および C++ のアプリケーションのデバッグを有効にするには、コンパイラーに対して、デバッグデータを作成するように、明示的に指示する必要があります。
コードのコンパイルおよびリンク時に、GCC でデバッグ情報の作成を有効にするには、-g
オプションを使用します。
gcc ... -g ...
$ gcc ... -g ...
-
コンパイラーとリンカーで最適化を実行すると、元のソースコードと関連付けることが難しい実行可能コードが生成される場合があります。変数が最適化されたり、ループがアンロールされたり、操作が周囲の操作にマージされたりする可能性があります。これにより、デバッグに負の影響が及ぶ可能性があります。デバッグエクスペリエンスを向上させるには、
-Og
オプションを指定して、最適化を設定することを検討してください。ただし、最適化レベルを変更すると、実行可能なコードが変更になり、バグを取り除くための動作が変更する可能性があります。 -
デバッグ情報にマクロ定義も追加するには、
-g
の代わりに-g3
オプションを使用します。 -
-fcompare-debug
GCC オプションでは、GCC でコンパイルしたコードを、デバッグ情報を使用した場合とデバッグ情報を使用しない場合でテストします。このテストでは、出力されたバイナリーファイルの 2 つが同一であれば合格します。このテストを行うことで、実行可能なコードがデバッグオプションによる影響は受けないようにするだけでなく、デバッグコードにバグが含まれないようにします。-fcompare-debug
オプションを使用するとコンパイルの時間が大幅に伸びることに留意してください。このオプションに関する詳細は、GCC の man ページを参照してください。
3.1.3. debuginfo パッケージおよび debugsource パッケージ
debuginfo
パッケージおよび debugsource
パッケージには、プログラムおよびライブラリーのデバッグ情報と、デバッグソースコードが含まれます。Red Hat Enterprise Linux リポジトリーのパッケージにインストールされているアプリケーションやライブラリーの場合は、追加のチャンネルから個別の debuginfo
パッケージおよび debugsource
パッケージを取得できます。
デバッグの情報パッケージタイプ
デバッグに使用できるパッケージには、以下の 2 つのタイプがあります。
- debuginfo パッケージ
-
debuginfo
パッケージは、バイナリーコード機能用に人間が判読可能な名前を提供するために必要なデバッグ情報を提供します。これらのパッケージには、DWARF デバッグ情報を含む.debug
ファイルが含まれています。これらのファイルは、/usr/lib/debug
ディレクトリーにインストールされます。 - debugsource パッケージ
-
debugsource
パッケージには、バイナリーコードのコンパイルに使用されるソースファイルが含まれています。適切なdebuginfo
パッケージおよびdebugsource
パッケージの両方がインストールされている状態で、GDB、LLDB などのデバッガーは、バイナリーコードの実行をソースコードに関連付けることができます。ソースコードファイルは、/usr/src/debug
ディレクトリーにインストールされています。
3.1.4. GDB を使用したアプリケーションまたはライブラリーの debuginfo パッケージの取得
デバッグ情報は、コードをデバッグするために必要です。パッケージからインストールされるコードの場合、GNU デバッガー (GDB) は足りないデバッグ情報を自動的に認識し、パッケージ名を解決し、パッケージの取得方法に関する具体的なアドバイスを提供します。
前提条件
- デバッグするアプリケーションまたはライブラリーがシステムにインストールされている。
-
GDB と
debuginfo-install
ツールがシステムにインストールされている必要があります。 -
debuginfo
およびdebugsource
パッケージを提供するリポジトリーを設定し、システムで有効にしている。詳細は、デバッグおよびソースリポジトリーの有効化 を参照してください。
手順
デバッグするアプリケーションまたはライブラリーに割り当てられた GDB を起動します。GDB は、足りないデバッグ情報を自動的に認識し、実行するコマンドを提案します。
gdb -q /bin/ls
$ gdb -q /bin/ls Reading symbols from /bin/ls...Reading symbols from .gnu_debugdata for /usr/bin/ls...(no debugging symbols found)...done. (no debugging symbols found)...done. Missing separate debuginfos, use: dnf debuginfo-install coreutils-9.5-6.el10.x86_64 (gdb)
Copy to Clipboard Copied! GDB を終了します。q と入力して、Enter で確認します。
(gdb) q
(gdb) q
Copy to Clipboard Copied! GDB が提案するコマンドを実行して、必要な
debuginfo
パッケージをインストールします。dnf debuginfo-install coreutils-9.5-6.el10.x86_64
# dnf debuginfo-install coreutils-9.5-6.el10.x86_64
Copy to Clipboard Copied! dnf
パッケージ管理ツールは、変更の概要を提供し、確認を求め、確認後に必要なファイルをすべてダウンロードしてインストールします。-
GDB が
debuginfo
パッケージを提案できない場合は、以下の手順に従ってください。「手動でのアプリケーションまたはライブラリーの debuginfo パッケージの取得」。
3.1.5. 手動でのアプリケーションまたはライブラリーの debuginfo パッケージの取得
実行ファイルの場所を特定し、インストールするパッケージを見つけることで、インストールする debuginfo
パッケージを手動で判断できます。
Red Hat では 、インストールするパッケージを決定するために GDB を使用する ことを推奨しています。この手動の手順は、GDB がインストールするパッケージを提案できない場合にのみ使用してください。
前提条件
- アプリケーションまたはライブラリーをシステムにインストールしている。
- アプリケーションまたはライブラリーが、パッケージからインストールされている。
-
debuginfo-install
ツールがシステム上で使用可能である必要があります。 -
debuginfo
パッケージを提供するチャネルをシステム上で設定し、有効にする。
手順
アプリケーションまたはライブラリーの実行可能ファイルを検索します。
which
コマンドを使用して、アプリケーションファイルを検索します。which less
$ which less /usr/bin/less
Copy to Clipboard Copied! locate
コマンドを使用して、ライブラリーファイルを検索します。locate libz | grep so
$ locate libz | grep so /usr/lib64/libz.so.1 /usr/lib64/libz.so.1.2.11
Copy to Clipboard Copied! デバッグの元の理由にエラーメッセージが含まれる場合は、ライブラリーのファイル名にエラーメッセージに記載されている番号と同じ追加番号が含まれるものを選択します。疑わしい場合は、ライブラリーファイルの名前に追加の番号が含まれていないものを使用して、残りの手順を試してください。
注記locate
コマンドはmlocate
パッケージで提供されます。このパッケージをインストールして、その使用を有効にするには、次のコマンドを実行します。dnf install mlocate updatedb
# dnf install mlocate # updatedb
Copy to Clipboard Copied!
ファイルを提供するパッケージの名前およびバージョンを検索します。
rpm -qf /usr/lib64/libz.so.1.3.1.zlib-ng
$ rpm -qf /usr/lib64/libz.so.1.3.1.zlib-ng zlib-ng-compat-2.2.3-1.el10.x86_64
Copy to Clipboard Copied! この出力では、インストールされているパッケージの詳細が、name:epoch-version.release.architecture 形式で提供されます。
重要この手順では結果が生成されないので、どのパッケージがこのバイナリーファイルを提供しているかは判断できません。次のような状況が考えられます。
- このファイルは、現在 の設定でパッケージ管理ツールに認識されないパッケージからインストールされます。
-
このファイルは、ローカルにダウンロードして手動でインストールしたパッケージからインストールされます。この場合、適切な
debuginfo
パッケージを自動的に判断することはできません。 - パッケージ管理ツールの設定が正しく設定されていません。
-
このファイルは、どのパッケージからもインストールされません。そのような場合は、それぞれの
debuginfo
パッケージも存在しません。
これ以降の手順はこの手順によって異なるため、この状況を解決するか、この手順を中止する必要があります。正確なトラブルシューティング手順の説明は、この手順の範囲外です。
dnf debuginfo-install
ユーティリティーを使用してdebuginfo
パッケージをインストールします。このコマンドで、直前の手順で確認したパッケージ名およびその他の詳細情報を使用します。dnf debuginfo-install zlib-ng-compat-2.2.3-1.el10.x86_64
# dnf debuginfo-install zlib-ng-compat-2.2.3-1.el10.x86_64
Copy to Clipboard Copied!
3.2. GDB を使用したアプリケーションの内部状況の検証
アプリケーションが正しく機能しない理由を特定するには、実行を制御し、デバッガーで内部状態を検証します。このセクションでは、このタスクに GNU Debugger (GDB) を使用する方法を説明します。
3.2.1. GNU デバッガー (GDB)
Red Hat Enterprise Linux には GNU デバッガー (GDB) が含まれ、コマンドラインユーザーインターフェイスを使用して、プログラム内で何が起こっているかを調べることができます。
GDB 機能
1 つの GDB セッションで、以下のタイプのプログラムをデバッグできます。
- マルチスレッドプログラムおよびフォークプログラム
- 一度に複数のプログラム
-
リモートマシン上のプログラムや、コンテナー内で
gdbserver
ユーティリティーを使用し、TCP/IP ネットワーク接続経由で接続されたプログラム
デバッグの要件
実行コードをデバッグするには、GDB では、その特定のコードのデバッグ情報が必要です。
- ユーザーが開発したプログラムでは、コードの構築中にデバッグ情報を作成できます。
- パッケージからインストールしたシステムプログラムの場合は、debuginfo パッケージをインストールする必要があります。
3.2.2. プロセスへの GDB の割り当て
プロセスを検証するには、GDB がプロセスに 割り当てられている 必要があります。
GDB でのプログラムの起動
プログラムがプロセスとして実行していない場合は、GDB でプログラムを起動します。
gdb program
$ gdb program
program は、ファイル名またはプログラムへのパスに置き換えます。
GDB は、プログラムの実行を開始するように設定します。run
コマンドでプロセスの実行を開始する前に、ブレークポイントと gdb
環境をセットアップできます。
実行中のプロセスに GDB を割り当て
プロセスとして実行中のプログラムに GDB を割り当てるには、以下を行います。
ps
コマンドで、プロセス ID (pid) を検索します。ps -C program -o pid h
$ ps -C program -o pid h pid
Copy to Clipboard Copied! program は、ファイル名またはプログラムへのパスに置き換えます。
このプロセスに GDB を割り当てます。
gdb -p pid
$ gdb -p pid
Copy to Clipboard Copied! pid は、
ps
の出力からの実際のプロセス ID 番号に置き換えます。
実行中のプロセスに実行中の GDB を割り当てる
実行中のプロセスに実行中の GDB を割り当てるには、以下を行います。
GDB コマンド
shell
を使用してps
コマンドを実行し、プログラムのプロセス ID (pid) を検索します。(gdb) shell ps -C program -o pid h pid
(gdb) shell ps -C program -o pid h pid
Copy to Clipboard Copied! program は、ファイル名またはプログラムへのパスに置き換えます。
attach
コマンドを使用して、GDB をプログラムに割り当てます。(gdb) attach pid
(gdb) attach pid
Copy to Clipboard Copied! pid は、
ps
の出力からの実際のプロセス ID の番号に置き換えます。
場合によっては、GDB が適切な実行ファイルを検索できない可能性があります。file
コマンドを使用して、パスを指定します。
(gdb) file path/to/program
(gdb) file path/to/program
3.2.3. GDB を使用したプログラムコードのステップ実行
GDB デバッガーがプログラムに割り当てられたら、複数のコマンドを使用して、プログラムの実行を制御できます。
前提条件
必要なデバッグ情報を利用できる状態にしている。
- プログラムはコンパイルされ、デバッグ情報で構築されている。
- 関連する debuginfo パッケージがインストールされている。
- GDB はデバッグするプログラムに割り当てられている。
コードをステップ実行する GDB コマンド
r
(run)-
プログラムの実行を開始します。任意の引数を指定して
run
を実行すると、プログラムが通常起動したかのように、それらの引数が実行可能ファイルに渡されます。通常は、ブレークポイントの設定後にこのコマンドを実行します。 start
-
プログラムの実行を開始しますが、プログラムのメイン機能の開始時に停止します。
start
を任意の引数と共に実行すると、プログラムが通常起動したかのように、それらの引数が実行可能ファイルに渡されます。
c
(continue)現在の状態からプログラムの実行を継続します。プログラムの実行は、以下のいずれかが true になるまで継続します。
- ブレークポイントに到達した場合
- 指定の条件を満たした場合
- プログラムによりシグナルを受信する場合
- エラーが発生した場合
- プログラムが終了する場合
n
(next)現在のソースファイルでコードが次の行に到達するまで、現在の状態からプログラムの実行を続行します。プログラムの実行は、以下のいずれかが true になるまで継続します。
- ブレークポイントに到達した場合
- 指定の条件を満たした場合
- プログラムによりシグナルを受信する場合
- エラーが発生した場合
- プログラムが終了する場合
s
(step)-
また、
step
コマンドは、現在のソースファイル内の各コード行で実行を停止します。ただし、関数呼び出し を含むソース行で実行が現在停止すると、GDB は、関数呼び出しを入力した後 (実行後ではなく)、実行を停止します。 until
location- location オプションで指定したコードの場所に到達するまで、実行が継続されます。
fini
(finish)プログラムの実行を再開し、実行が関数から返されたときに停止します。プログラムの実行は、以下のいずれかが true になるまで継続します。
- ブレークポイントに到達した場合
- 指定の条件を満たした場合
- プログラムによりシグナルを受信する場合
- エラーが発生した場合
- プログラムが終了する場合
q
(quit)- 実行を終了し、GDB を終了します。
3.2.4. GDB でのプログラム内部値の表示
プログラムの内部変数の値を表示することは、プログラムの実行内容を理解する際に重要です。GDB は、内部変数の検査に使用できる複数のコマンドを提供します。これらのコマンドの中で最も有用なものは次のとおりです。
p
(print)指定された引数の値を表示します。通常、引数は単純な 1 つの値から構造まで、あらゆる複雑な変数の名前です。引数には、プログラム変数やライブラリー関数の使用、テストするプログラムに定義する関数など、現在の言語で有効な式も指定できます。
pretty-printer Python スクリプトまたは Guile スクリプトを使用して GDB を拡張し、
print
コマンドを使用して、(クラス、構造などの) データ構造をカスタマイズ表示することができます。bt
(backtrace)現在の実行ポイントに到達するために使用される関数呼び出しのチェーン、または実行が終了するまで使用される関数のチェーンを表示します。これは、深刻なバグ (セグメント障害など) を調査し、見つけるのが困難な原因に役に立ちます。
backtrace
コマンドにfull
オプションを追加すると、ローカル変数も表示されます。bt
コマンドおよびinfo frame
コマンドを使用して表示されるデータをカスタマイズして表示するために、frame filter Python スクリプトで GDB を拡張できます。フレーム という用語は、1 つの関数呼び出しに関連付けられたデータを指します。info
info
コマンドは、さまざまな項目に関する情報を提供する汎用コマンドです。これは、説明する項目を指定するオプションを取ります。-
info args
コマンドは、現在選択されているフレームの関数呼び出しのオプションを表示します。 -
info locals
コマンドは、現在選択されているフレームにローカル変数を表示します。
使用できる項目をリスト表示するには、GDB セッションで
help info
コマンドを実行します。(gdb) help info
(gdb) help info
Copy to Clipboard Copied! -
l
(list)- プログラムのソースコードを表示します。プログラムが開始されていて現在停止中の場合、このコマンドは、プログラムが停止している位置のソースコードと、数行のコンテキストを表示します。プログラムが起動される前に、メイン関数がリスト表示されます。list は、厳密には内部状態を表示するコマンドではありませんが、プログラムの実行における次の手順で、内部状態にどのような変更が発生するかをユーザーが理解するのに役立ちます。
3.2.5. GDB ブレークポイントを使用して、定義したコードの場所で実行を停止
多くの場合、コードの一部のみが検証されます。ブレークポイントは、コード内の特定の場所でプログラムの実行を停止するように GDB に指示を出すマーカーです。ブレークポイントは、ソースコードの行に関連付けられているのが最も一般的です。その場合、ブレークポイントを配置するには、ソースファイルと行番号を指定する必要があります。
ブレークポイントを配置する には、以下を行います。
ソースコード ファイル の名前と、そのファイルの 行 を指定します。
(gdb) br file:line
(gdb) br file:line
Copy to Clipboard Copied! ファイル が存在しない場合は、現在の実行ポイントにソースファイルの名前が使用されます。
(gdb) br line
(gdb) br line
Copy to Clipboard Copied! または、関数名を使用して、起動時にブレークポイントを配置します。
(gdb) br function_name
(gdb) br function_name
Copy to Clipboard Copied!
タスクを特定の回数反復すると、プログラムでエラーが発生する可能性があります。実行を停止するために追加の 条件 を指定するには、以下を実行します。
(gdb) br file:line if condition
(gdb) br file:line if condition
Copy to Clipboard Copied! condition を、C または C++ 言語の条件に置き換えます。file と line は、上記と同様に、ファイル名および行数に置き換えます。
全ブレークポイントおよびウォッチポイントの状態を 検査 する場合は、以下のコマンドを実行します。
(gdb) info br
(gdb) info br
Copy to Clipboard Copied! info br
の出力で表示された 番号 を使用してブレークポイントを 削除 するには、以下のコマンドを実行します。(gdb) delete number
(gdb) delete number
Copy to Clipboard Copied! 指定の場所のブレークポイントを 削除 するには、次のコマンドを実行します。
(gdb) clear file:line
(gdb) clear file:line
Copy to Clipboard Copied!
3.2.6. データへのアクセスや変更を停止するための GDB ウォッチポイントの使用
多くの場合、特定のデータが変更されたり、アクセスされるまでプログラムを実行させることには利点があります。次の例は、最も一般的な使用例です。
前提条件
- GDB の理解
GDB でのウォッチポイントの使用
ウォッチポイントは、プログラムの実行を停止するように GDB に指示を出すマーカーです。ウォッチポイントはデータに関連付けられています。ウォッチポイントを配置するには、変数、複数の変数、またはメモリーアドレスを記述する式を指定する必要があります。
データの 変更 (書き込み) を行うために、ウォッチポイントを 配置 するには、次を使用します。
(gdb) watch expression
(gdb) watch expression
Copy to Clipboard Copied! expression を、監視する内容を記述する式に置き換えます。変数の場合、式 は、変数の名前と同じです。
データ アクセス (読み込み) のためのウォッチポイントを 配置 するには、以下を実行します。
(gdb) rwatch expression
(gdb) rwatch expression
Copy to Clipboard Copied! 任意 のデータへのアクセス (読み取りおよび書き込みの両方) のためにウォッチポイントを 配置 するには、以下を実行します。
(gdb) awatch expression
(gdb) awatch expression
Copy to Clipboard Copied! 全ウォッチポイントおよびブレークポイントの状態を 検査 するには以下を実行します。
(gdb) info br
(gdb) info br
Copy to Clipboard Copied! ウォッチポイントを 削除 するには、以下を実行します。
(gdb) delete num
(gdb) delete num
Copy to Clipboard Copied! num オプションは、
info br
コマンドで報告される番号に置き換えます。
3.2.7. GDB でのフォークまたはスレッド化されたプログラムのデバッグ
プログラムによっては、フォークまたはスレッドを使用して、並行コード実行を実現します。複数の同時実行パスをデバッグするには、特別な留意点があります。
前提条件
- プロセスのフォークおよびスレッドの概念を理解している。
GDB でのフォークされたプログラムのデバッグ
フォークとは、プログラム (親) が独立したコピー (子) を作成する状況です。フォーク発生時の GDB の動作に影響を与えるには、以下の設定およびコマンドを使用します。
follow-fork-mode
設定で、フォークの後に GDB が親または子に従うかどうかを制御します。set follow-fork-mode parent
- フォークの後に、親プロセスのデバッグを実行します。これがデフォルトになります。
set follow-fork-mode child
- フォークの後に子のプロセスをデバッグします。
show follow-fork-mode
-
follow-fork-mode
の現在の設定を表示します。
set detach-on-fork
設定では、GDB が (フォローしていない) 他のプロセスを制御するか、そのまま実行させるかを制御します。set detach-on-fork on
-
フォローされないプロセス (
follow-fork-mode
の値によって異なる) は切り離され、独立して実行されます。これがデフォルトになります。 set detach-on-fork off
-
GDB は両方のプロセスの制御を維持します。フォローされるプロセス (
follow-fork-mode
の値によって異なる) は通常どおりデバッグされ、他のプロセスは中断されます。 show detach-on-fork
-
detach-on-fork
の現在の設定を表示します。
GDB でのスレッド化されたプログラムのデバッグ
GDB には、個別のスレッドをデバッグして、独立して操作し、検査する機能があります。GDB が検査したスレッドのみを停止するようにするには、set non-stop on
コマンドおよび set target-async on
コマンドを使用します。これらのコマンドは、.gdbinit
ファイルに追加できます。その機能が有効になると、GDB がスレッドのデバッグを実行する準備が整います。
GDB は、現在のスレッド の概念を使用します。デフォルトでは、コマンドは現在のスレッドのみに適用されます。
info threads
-
現在のスレッドを示す
id
番号およびgid
番号を使用してスレッドのリストを表示します。 thread id
-
指定した
id
を現在のスレッドとして設定します。 thread apply ids command
-
command
コマンドを、ids
でリスト表示されたすべてのスレッドに適用します。ids
オプションは、スペースで区切られたスレッド ID のリストです。特殊な値all
は、すべてのスレッドにコマンドを適用します。 break location thread id if condition
-
スレッド番号
id
に対してのみ特定のcondition
を持つ特定のlocation
にブレークポイントを設定します。 watch expression thread id
-
スレッド番号
id
に対してのみexpression
で定義されるウォッチポイントを設定します。 command&
-
command
コマンドを実行して、すぐに gdb プロンプト(gdb)
に戻りますが、バックグラウンドでコード実行が続行されます。 interrupt
- バックグラウンドでの実行が停止されます。
3.3. アプリケーションの対話の記録
アプリケーションの実行可能コードは、オペレーティングシステムや共有ライブラリーのコードと対話します。この相互作用のアクティビティーログを記録すると、実際のアプリケーションコードをデバッグしなくても、アプリケーションの動作を十分に把握できます。または、アプリケーションの相互作用を分析することで、バグが現れる条件を特定するのに役立ちます。
3.3.1. アプリケーションの相互作用の記録に役立つツール
Red Hat Enterprise Linux は、アプリケーションの相互作用を分析するための複数のツールを提供しています。
- strace
strace
ツールでは主に、アプリケーションが使用するシステムコール (カーネル関数) のロギングが可能になります。-
strace
がパラメーターを解釈し、基礎となるカーネルコードに関する知識が得られるため、strace
ツールでは呼び出しに関する詳細な出力が得られます。数値は、定数名、フラグリストにデプロイメントされたビット単位の結合フラグ、実際の文字列を提供するために逆参照された文字配列へのポインターなどにそれぞれ変換されます。最新のカーネル機能のサポートがない場合があります。 - トレースされた呼び出しをフィルタリングして、取得するデータ量を減らすことができます。
-
strace
を使用する場合、ログフィルターの設定以外に特別なセットアップは必要ありません。 -
strace
でアプリケーションコードを追跡すると、アプリケーションの実行速度が大幅に低下します。結果として、strace
は、多くの実稼働環境のデプロイメントには適していません。代替方法として、ltrace
または SystemTap の使用を検討してください。 -
{rhdts} で利用可能な
strace
のバージョンは、システムコールの改ざんも実行できます。この機能は、デバッグに役立ちます。
-
- ltrace
ltrace
ツールを使用すると、アプリケーションのユーザー空間呼び出しのログを共有オブジェクト (動的ライブラリー) に記録できます。-
ltrace
ツールを使用すると、任意のライブラリーへの呼び出しを追跡できます。 - トレースされた呼び出しをフィルタリングして、取得するデータ量を減らすことができます。
-
ltrace
を使用するために、ログフィルターのセットアップ以外に、特別なセットアップは必要ありません。 -
ltrace
ツールは軽量かつ高速で、strace
に代わる機能を提供します。strace
でカーネルの関数を追跡する代わりに、ltrace
でglibc
など、ライブラリー内の各インターフェイスを追跡できます。 -
ltrace
は、strace
などの既知の呼び出しセットを処理しないため、ライブラリー関数に渡される値を説明しません。ltrace
の出力には、raw の数値およびポインターのみが含まれます。ltrace
の出力の解釈には、出力にあるライブラリーの実際のインターフェイス宣言を確認する必要があります。
注記Red Hat Enterprise Linux 10 では、既知の問題により、
ltrace
がシステム実行可能ファイルをトレースできません。この制限は、ユーザーが構築する実行ファイルには適用されません。-
- SystemTap
SystemTap は、Linux システム上で実行中のプロセスおよびカーネルアクティビティーを調査するための有用なインストルメンテーションプラットフォームです。SystemTap は、独自のスクリプト言語を使用してカスタムイベントハンドラーをプログラミングします。
-
strace
とltrace
を使用する場合と比較すると、ロギングのスクリプト化は、初期セットアップ段階での作業が増えることを意味します。ただし、スクリプト機能は単にログを生成するだけでなく、SystemTap の有用性を高めます。 - SystemTap は、カーネルモジュールを作成し、挿入すると機能します。SystemTap は効率的に使用でき、システムまたはアプリケーションの実行速度が大幅に低下することはありません。
- SystemTap には一連の使用例が提供されます。
-
- GDB
GNU デバッガー (GDB) は主に、ロギングではなく、デバッグを目的としています。ただし、その機能の一部は、アプリケーションの相互作用が重要な主要なアクティビティーであるシナリオでも有用です。
- GDB を使用すると、相互作用イベントを取得して、後続の実行パスの即時デバッグを簡単に組み合わせることができます。
- GDB は、他のツールで問題のある状況を最初に特定した後、まれなイベントまたは特異なイベントへの応答を分析するのに最適です。イベントが頻繁に発生するシナリオで GDB を使用すると、効率が悪くなったり、不可能になったりします。
関連情報
3.3.2. strace でアプリケーションのシステムコールの監視
strace
ツールを使用すると、アプリケーションによって実行されるシステム (カーネル) 呼び出しを監視できます。
手順
- 監視するシステムコールを特定します。
strace
を起動して、プログラムに割り当てます。監視するプログラムが実行されていない場合は、
strace
を起動して、プログラム を指定します。strace -fvttTyy -s 256 -e trace=call program
$ strace -fvttTyy -s 256 -e trace=call program
Copy to Clipboard Copied! プログラムがすでに実行中の場合は、プロセス id (pid) を検索して、その id に
strace
を割り当てます。ps -C program strace -fvttTyy -s 256 -e trace=call -ppid
$ ps -C program (...) $ strace -fvttTyy -s 256 -e trace=call -ppid
Copy to Clipboard Copied! -
call は、表示するシステムコールに置き換えます。
-e trace=call
オプションは複数回使用できます。何も指定しない場合、strace
はすべてのシステムコールタイプを表示します。詳細は、man ページの strace(1) を参照してください。 -
フォークされたプロセスまたはスレッドを追跡しない場合は、
-f
オプションを指定しないでください。
strace
ツールは、アプリケーションによるシステムコールとその詳細を表示します。ほとんどの場合、システムコールのフィルターが設定されていないと、アプリケーションとそのライブラリーは多数の呼び出しを行い、
strace
出力がすぐに表示されます。プログラムが終了すると、
strace
ツールも終了します。追跡しているプログラムの終了前に監視を中断するには、
を押します。-
strace
でプログラムを起動すると、そのプログラムはstrace
と共に終了します。 -
実行中のプログラムに
strace
を割り当てると、そのプログラムはstrace
と共に終了します。
-
アプリケーションが実行したシステムコールのリストを分析します。
- リソースへのアクセスや可用性の問題は、エラーを返す呼び出しとしてログに表示されます。
- システムコールに渡される値とコールシーケンスのパターンは、アプリケーションの動作の原因に関する洞察を提供します。
- アプリケーションがクラッシュした場合、重要な情報はおそらくログの最後にあります。
- 出力には不要な情報が多く含まれています。ただし、目的のシステムコールに対してより正確なフィルターを作成し、この手順を繰り返すことができます。
出力を確認することにも、ファイルに保存することにも利点があります。これを実行するには、tee
コマンドを使用します。
strace ... |& tee your_log_file.log
$ strace ... |& tee your_log_file.log
3.3.3. ltrace でアプリケーションのライブラリー関数呼び出しの監視
ltrace
ツールを使用すると、ライブラリー (共有オブジェクト) で利用可能な関数へのアプリケーションの呼び出しを監視できます。
Red Hat Enterprise Linux 10 では、既知の問題により、ltrace
がシステム実行可能ファイルをトレースできません。この制限は、ユーザーが構築する実行ファイルには適用されません。
手順
- 可能であれば、対象のライブラリーおよび関数を特定します。
ltrace
を起動し、プログラムに割り当てます。監視するプログラムが実行されていない場合には、
ltrace
を起動して、プログラム を指定します。ltrace -f -l library -e function program
$ ltrace -f -l library -e function program
Copy to Clipboard Copied! -
プログラムがすでに実行中の場合は、プロセス id (pid) を検索して、その id に
ltrace
を割り当てます。 -e
オプション、-f
オプション、および-l
オプションを使用して、出力にフィルターを設定します。-
function として表示される関数の名前を指定します。
-e function
オプションは複数回使用できます。何も指定しないと、ltrace
はすべての関数への呼び出しを表示します。 -
関数を指定するのではなく、
-l library
オプションでライブラリー全体を指定することができます。このオプションは、-e function
オプションと同じように動作します。 -
フォークされたプロセスまたはスレッドを追跡しない場合は、
-f
オプションを指定しないでください。
詳細情報は、man ページの ltrace(1) を参照してください。
-
function として表示される関数の名前を指定します。
ltrace
は、アプリケーションによるライブラリー呼び出しを表示します。多くの場合は、フィルターが設定されていないと、アプリケーションは多数の呼び出しを行い、
ltrace
の出力がすぐに表示されます。ltrace
は、プログラムが終了すると終了します。追跡しているプログラムの終了前に監視を中断するには、
を押します。-
ltrace
でプログラムを起動した場合には、プログラムはltrace
と共に終了します。 -
実行中のプログラムに
ltrace
を割り当てると、プログラムはltrace
と共に終了します。
-
アプリケーションが実行したライブラリーコールのリストを分析します。
- アプリケーションがクラッシュした場合、重要な情報はおそらくログの最後にあります。
- 出力には不要な情報が多く含まれています。ただし、より正確なフィルターを作成して、手順を繰り返すことができます。
出力を確認することにも、ファイルに保存することにも利点があります。これを実行するには、tee
コマンドを使用します。
ltrace ... |& tee your_log_file.log
$ ltrace ... |& tee your_log_file.log
3.3.4. SystemTap を使用したアプリケーションのシステムコールの監視
SystemTap ツールでは、カーネルイベントにカスタムイベントハンドラーを登録できます。strace
ツールと比較すると、使いにくいですが、より効率的で、より複雑な処理ロジックを可能にします。strace.stp
と呼ばれる SystemTap スクリプトは、SystemTap と共にインストールされ、SystemTap を使用して strace
に類似の機能を提供します。
手順
監視するプロセスのプロセス ID (pid) を検索します。
ps -aux
$ ps -aux
Copy to Clipboard Copied! strace.stp
スクリプトで SystemTap を実行します。stap /usr/share/systemtap/examples/process/strace.stp -x pid
# stap /usr/share/systemtap/examples/process/strace.stp -x pid
Copy to Clipboard Copied! pid の値は、プロセス ID です。
スクリプトはカーネルモジュールにコンパイルされ、それが読み込まれます。これにより、コマンドの入力から出力の取得までにわずかな遅延が生じます。
- プロセスでシステムコールが実行されると、呼び出し名とパラメーターがターミナルに出力されます。
-
プロセスが終了した場合、または
Ctrl+C
を押した場合に、スクリプトは終了します。
3.3.5. GDB を使用したアプリケーションのシステムコールの傍受
GNU デバッガー (GDB) により、プログラムの実行中に発生するさまざまな状況で実行を停止できます。プログラムがシステムコールを実行するときに実行を停止するには、GDB の チェックポイント を使用します。
手順
キャッチポイントを設定します。
(gdb) catch syscall syscall-name
(gdb) catch syscall syscall-name
Copy to Clipboard Copied! catch syscall
コマンドは、プログラムがシステムコールを実行する際に実行を停止する特別なタイプのブレークポイントを設定します。syscall-name
オプショは、呼び出し名を指定します。さまざまなシステムコールに対して複数のキャッチポイントを指定できます。syscall-name
オプションに何も指定しない場合には、GDB はすべてのシステムコールで停止します。プログラムの実行を開始します。
プログラムにより、実行が開始していない場合は開始します。
(gdb) r
(gdb) r
Copy to Clipboard Copied! プログラムの実行が停止した場合は、再開します。
(gdb) c
(gdb) c
Copy to Clipboard Copied!
- GDB は、プログラムが指定のシステムコールを実行した後に実行を停止します。
関連情報
- 「GDB でのプログラム内部値の表示」
- 「GDB を使用したプログラムコードのステップ実行」
- GDB を使用したデバッグ - Setting Watchpoints
3.3.6. GDB を使用したアプリケーションによるシグナル処理のインターセプト
GNU デバッガー (GDB) により、プログラムの実行中に発生するさまざまな状況で実行を停止できます。プログラムがオペレーティングシステムからシグナルを受信するときに実行を停止するには、GDB の キャッチポイント を使用します。
手順
キャッチポイントを設定します。
(gdb) catch signal signal-type
(gdb) catch signal signal-type
Copy to Clipboard Copied! catch signal
コマンドは、プログラムがシグナルを受信したときに実行を停止する特別なタイプのブレークポイントを設定します。signal-type
オプションは、シグナルのタイプを指定します。すべてのシグナルを取得するには、特別な値'all'
を使用します。プログラムを実行します。
プログラムにより、実行が開始していない場合は開始します。
(gdb) r
(gdb) r
Copy to Clipboard Copied! プログラムの実行が停止した場合は、再開します。
(gdb) c
(gdb) c
Copy to Clipboard Copied!
- GDB は、プログラムが指定のシグナルを受けると実行を停止します。
3.4. クラッシュしたアプリケーションのデバッグ
アプリケーションを直接デバッグできない場合があります。このような状況では、アプリケーションの終了時にアプリケーションに関する情報を収集し、後で分析できます。
3.4.1. コアダンプ: その概要と使用方法
コアダンプは、アプリケーションの動作が停止した時点のアプリケーションのメモリーの一部のコピーで、ELF 形式で保存されます。コアダンプには、アプリケーションの内部変数、スタックすべてが含まれ、アプリケーションの最終的な状態を検査することができます。それぞれの実行可能ファイルおよびデバッグ情報を追加すると、実行中のプログラムを分析するのと同様に、デバッガーでコアダンプファイルを分析できます。
Linux オペレーティングシステムカーネルは、この機能が有効な場合に、コアダンプを自動的に記録できます。または、実行中のアプリケーションにシグナルを送信すると、実際の状態に関係なくコアダンプを生成できます。
一部の制限は、コアダンプを生成する機能に影響する場合があります。現在の制限を表示するには、次のコマンドを実行します。
ulimit -a
$ ulimit -a
3.4.2. コアダンプによるアプリケーションのクラッシュの記録
アプリケーションのクラッシュを記録するには、コアダンプの保存内容を設定し、システムに関する情報を追加します。
手順
コアダンプを有効にするには、
/etc/systemd/system.conf
ファイルに以下の行が含まれていることを確認します。DumpCore=yes DefaultLimitCORE=infinity
DumpCore=yes DefaultLimitCORE=infinity
Copy to Clipboard Copied! これらの設定が以前に存在したかどうか、以前の値が何であったかを説明するコメントを追加することもできます。これにより、必要に応じて、この変更を後で元に戻すことができます。コメントは、
#
文字で始まる行です。ファイルを変更するには、管理者レベルのアクセスが必要です。
新しい設定を適用します。
systemctl daemon-reexec
# systemctl daemon-reexec
Copy to Clipboard Copied! コアダンプサイズの制限を削除します。
ulimit -c unlimited
# ulimit -c unlimited
Copy to Clipboard Copied! この変更を元に戻すには、
unlimited
ではなく、0
を指定してコマンドを実行します。システム情報を収集する
sosreport
ユーティリティーを提供するsos
パッケージをインストールします。dnf install sos
# dnf install sos
Copy to Clipboard Copied! -
アプリケーションがクラッシュすると、コアダンプが生成され、
systemd-coredump
により処理されます。 SOS レポートを作成して、システムに関する追加情報を提供します。
sosreport
# sosreport
Copy to Clipboard Copied! これにより、設定ファイルのコピーなど、システムに関する情報が含まれる
.tar
アーカイブが作成されます。コアダンプを探してエクスポートします。
coredumpctl list executable-name coredumpctl dump executable-name > /path/to/file-for-export
$ coredumpctl list executable-name $ coredumpctl dump executable-name > /path/to/file-for-export
Copy to Clipboard Copied! アプリケーションが複数回クラッシュした場合、最初のコマンドの出力には、取得されたコアダンプがさらにリスト表示されます。その場合、2 番目のコマンドに対して、他の情報を使用してより正確なクエリーを作成します。詳細は、man ページ coredumpctl(1) を参照してください。
デバッグを行うコンピューターに、コアダンプと SOS レポートを移動します。既知の場合は、実行ファイルも転送します。
重要実行可能ファイルが不明な場合は、コアファイルのその後の分析で特定します。
- 必要に応じて、コアダンプと SOS レポートに移動後に削除して、ディスク領域を解放します。
3.4.3. コアダンプでアプリケーションのクラッシュ状態の検査
前提条件
- クラッシュが発生したシステムのコアダンプファイルと sosreport がある。
- GDB および elfutils がシステムにインストールされている。
手順
クラッシュが発生した実行可能ファイルを特定するには、コアダンプファイルを指定して
eu-unstrip
コマンドを実行します。eu-unstrip -n --core=./core.9814
$ eu-unstrip -n --core=./core.9814 0x400000+0x207000 2818b2009547f780a5639c904cded443e564973e@0x400284 /usr/bin/sleep /usr/lib/debug/bin/sleep.debug [exe] 0x7fff26fff000+0x1000 1e2a683b7d877576970e4275d41a6aaec280795e@0x7fff26fff340 . - linux-vdso.so.1 0x35e7e00000+0x3b6000 374add1ead31ccb449779bc7ee7877de3377e5ad@0x35e7e00280 /usr/lib64/libc-2.14.90.so /usr/lib/debug/lib64/libc-2.14.90.so.debug libc.so.6 0x35e7a00000+0x224000 3ed9e61c2b7e707ce244816335776afa2ad0307d@0x35e7a001d8 /usr/lib64/ld-2.14.90.so /usr/lib/debug/lib64/ld-2.14.90.so.debug ld-linux-x86-64.so.2
Copy to Clipboard Copied! 出力には、行ごとに各モジュールの詳細が、スペースで区切られます。情報は以下の順序でリスト表示されます。
- モジュールがマッピングされているメモリーアドレス。
- モジュールのビルド ID、およびメモリー内の場所。
-
モジュールの実行可能ファイル名。不明な場合は
-
、ファイルからモジュールがロードされていない場合は.
と表示されます。 -
デバッグ情報のソース。利用可能な場合はファイル名として、実行可能ファイル自体に含まれている場合は
.
として、まったく存在しない場合は-
として表示されます。 -
主要なモジュールの共有ライブラリー名 (soname) または
[exe]
この例では、重要な詳細は、ファイル名
/usr/bin/sleep
と、テキスト[exe]
を含む行のビルド ID2818b2009547f780a5639c904cded443e564973e
です。この情報を使用して、コアダンプの分析に必要な実行可能ファイルを特定できます。クラッシュした実行可能ファイルを取得します。
- 可能であれば、クラッシュが発生したシステムからコピーします。コアファイルから抽出したファイル名を使用します。
システムで同じ実行ファイルを使用することもできます。Red Hat Enterprise Linux にビルドされた実行ファイルはそれぞれ、固有の build-id 値を持つメモが含まれています。関連する、ローカルで利用可能な実行ファイルの build-id を特定します。
eu-readelf -n executable_file
$ eu-readelf -n executable_file
Copy to Clipboard Copied! この情報を使用して、リモートシステムの実行可能ファイルをローカルコピーと一致させます。ローカルファイルの build-id とコアダンプに記載されている build-id は一致する必要があります。
-
最後に、アプリケーションが RPM パッケージからインストールされている場合は、パッケージから実行ファイルを取得できます。
sosreport
出力を使用して、必要なパッケージの正確なバージョンを確認します。
- 実行可能ファイルで使用する共有ライブラリーを取得します。実行ファイルと同じ手順を使用します。
- アプリケーションがパッケージとして配布されている場合は、GDB で実行ファイルを読み込み、足りない debuginfo パッケージに関するヒントを表示します。詳細は、「GDB を使用したアプリケーションまたはライブラリーの debuginfo パッケージの取得」 を参照してください。
コアファイルを詳細に調べるには、GDB で実行ファイルとコアダンプファイルを読み込みます。
gdb -e executable_file -c core_file
$ gdb -e executable_file -c core_file
Copy to Clipboard Copied! 不足しているファイルとデバッグ情報に関する追加のメッセージは、デバッグセッションで不足しているものを特定するのに役に立ちます。必要に応じて直前の手順に戻ります。
アプリケーションのデバッグ情報がパッケージではなくファイルとして利用できる場合は、
symbol-file
コマンドを使用してこのファイルを GDB にロードします。(gdb) symbol-file program.debug
(gdb) symbol-file program.debug
Copy to Clipboard Copied! program.debug は、実際のファイル名に置き換えます。
注記コアダンプに含まれるすべての実行可能ファイルのデバッグ情報をインストールする必要はない場合があります。これらの実行可能ファイルのほとんどは、アプリケーションコードで使用されるライブラリーです。これらのライブラリーが分析中の問題の直接原因でない可能性があるので、ライブラリーのデバッグ情報を含める必要はありません。
GDB コマンドを使用して、クラッシュした時点のアプリケーションの状態を検査します。GDB を使用したアプリケーションの内部状況の検証 を参照してください。
注記コアファイルを分析する場合に、GDB が実行中のプロセスに割り当てられる訳ではありません。実行を制御するコマンドは影響を受けません。
関連情報
- GDB を使用したデバッグ - 2.1.1 Choosing Files
- GDB を使用したデバッグ - 18.1 Commands to Specify Files
- GDB を使用したデバッグ - 18.3 Debugging Information in Separate Files
3.4.4. coredumpctl を使用したコアダンプの作成およびアクセス
systemd
の coredumpctl
ツールは、クラッシュが発生したマシン上のコアダンプの処理を大幅に合理化できます。この手順では、応答しないプロセスのコアダンプを取得する方法を説明します。
前提条件
システムは、コアダンプの処理に
systemd-coredump
を使用するように設定している。true かどうか確認するには、次のコマンドを実行します。sysctl kernel.core_pattern
$ sysctl kernel.core_pattern
Copy to Clipboard Copied! 次の内容で出力が始まる場合は、設定が適切です。
kernel.core_pattern = |/usr/lib/systemd/systemd-coredump
kernel.core_pattern = |/usr/lib/systemd/systemd-coredump
Copy to Clipboard Copied!
手順
実行ファイル名の既知の部分に基づいて、ハングしたプロセスの PID を検索します。
pgrep -a executable-name-fragment
$ pgrep -a executable-name-fragment
Copy to Clipboard Copied! このコマンドは、フォームの行を出力します。
PID command-line
PID command-line
Copy to Clipboard Copied! command-line 値を使用して、PID が目的のプロセスに属することを確認します。
以下に例を示します。
pgrep -a bc
$ pgrep -a bc 5459 bc
Copy to Clipboard Copied! 中断シグナルをプロセスに送信します。
kill -ABRT PID
# kill -ABRT PID
Copy to Clipboard Copied! コアが
coredumpctl
で取得されていることを確認します。coredumpctl list PID
$ coredumpctl list PID
Copy to Clipboard Copied! 以下に例を示します。
coredumpctl list 5459
$ coredumpctl list 5459 TIME PID UID GID SIG COREFILE EXE Thu 2019-11-07 15:14:46 CET 5459 1000 1000 6 present /usr/bin/bc
Copy to Clipboard Copied! 必要に応じて、コアファイルをさらに検証または使用します。
PID と他の値でコアダンプを指定できます。詳細は、man ページの coredumpctl(1) を参照してください。
コアファイルの詳細を表示します。
coredumpctl info PID
$ coredumpctl info PID
Copy to Clipboard Copied! GDB デバッガーでコアファイルを読み込むには、次のコマンドを実行します。
coredumpctl debug PID
$ coredumpctl debug PID
Copy to Clipboard Copied! デバッグ情報の可用性によっては、GDB は次のようなコマンドを実行するコマンドを提案します。
Missing separate debuginfos, use: dnf debuginfo-install bc-1.07.1-23.el10.x86_64
Missing separate debuginfos, use: dnf debuginfo-install bc-1.07.1-23.el10.x86_64
Copy to Clipboard Copied! このプロセスの詳細は、「GDB を使用したアプリケーションまたはライブラリーの debuginfo パッケージの取得」。
その後の処理を別の場所でするためにコアファイルをエクスポートするには、次のコマンドを実行します。
coredumpctl dump PID > /path/to/file_for_export
$ coredumpctl dump PID > /path/to/file_for_export
Copy to Clipboard Copied! /path/to/file_for_export を、コアダンプを配置するファイルに置き換えます。
3.4.5. gcore
を使用したプロセスメモリーのダンプ
コアダンプのデバッグのワークフローでは、プログラムの状態をオフラインで分析できます。場合によっては、このプロセスの環境にアクセスするのが困難な場合など、実行中のプログラムでこのワークフローを使用できます。gcore
コマンドを使用すると、実行中のプロセスのメモリーをダンプできます。
手順
プロセス ID (pid) を検索します。
ps
、pgrep
、およびtop
などのツールを使用します。ps -C some-program
$ ps -C some-program
Copy to Clipboard Copied! このプロセスのメモリーをダンプします。
gcore -o filename pid
$ gcore -o filename pid
Copy to Clipboard Copied! これでファイル filename が作成され、その中にプロセスメモリーがダンプされます。メモリーをダンプしている間は、プロセスの実行は停止します。
- コアダンプが終了すると、プロセスは通常の実行を再開します。
SOS レポートを作成して、システムに関する追加情報を提供します。
sosreport
# sosreport
Copy to Clipboard Copied! これにより、設定ファイルのコピーなど、システムに関する情報が含まれる .tar アーカイブが作成されます。
- デバッグを行うコンピューターに、プログラムの実行ファイル、コアダンプ、および SOS レポートを移動します。
- 必要に応じて、コアダンプと SOS レポートに移動後に削除して、ディスク領域を解放します。
関連情報
- How to obtain a core file without restarting an application?(Red Hat ナレッジベース)
3.4.6. GDB での保護されたプロセスメモリーのダンプ
プロセスのメモリーをダンプしないようにマークできます。これにより、銀行、会計アプリケーション、または仮想マシン全体など、プロセスメモリーに機密データが含まれる場合は、リソースを節約し、セキュリティーを強化できます。カーネルのコアダンプ (kdump
) および手動のコアダンプ (gcore
、GDB) はどちらも、このようにマークされたメモリーをダンプしません。
場合によっては、これらの保護に関係なく、プロセスメモリーの内容全体をダンプする必要があります。この手順では、GDB デバッガーを使用してこれを行う方法を説明します。
手順
/proc/PID/coredump_filter
ファイルの設定を無視するように GDB を設定します。(gdb) set use-coredump-filter off
(gdb) set use-coredump-filter off
Copy to Clipboard Copied! メモリーページのフラグ
VM_DONTDUMP
を無視するように GDB を設定します。(gdb) set dump-excluded-mappings on
(gdb) set dump-excluded-mappings on
Copy to Clipboard Copied! メモリーをダンプします。
(gdb) gcore core-file
(gdb) gcore core-file
Copy to Clipboard Copied! core-file を、メモリーをダンプするファイルの名前に置き換えます。
3.5. コンテナー内のアプリケーションのデバッグ
トラブルシューティングのさまざまな側面に合わせてカスタマイズされたさまざまなコマンドラインツールを使用できます。以下に、一般的なコマンドラインツールとともにカテゴリーを示します。
これはコマンドラインツールの完全なリストではありません。コンテナーアプリケーションをデバッグするためのツールの選択は、コンテナーイメージとユースケースに大きく依存します。
たとえば、systemctl
、journalctl
、ip
、netstat
、ping
、traceroute
、perf
、iostat
ツールは、ネットワーク、systemd サービス、ハードウェアパフォーマンスカウンターなどのシステムレベルのリソースと対話するため、ルートアクセスが必要になる場合があります。これらのリソースは、セキュリティー上の理由から、ルートレスコンテナーでは制限されています。
ルートレスコンテナーは昇格された権限を必要とせずに動作し、ユーザー名前空間内で非ルートユーザーとして実行されるため、セキュリティーが向上し、ホストシステムから分離されます。ホストとのやり取りが制限され、攻撃対象領域が縮小され、権限昇格の脆弱性のリスクが軽減されるため、セキュリティーが強化されます。
ルートフルコンテナーは、通常はルートユーザーとして昇格された権限で実行され、システムリソースと機能への完全なアクセス権が付与されます。ルートフルコンテナーは柔軟性と制御性に優れていますが、権限昇格の可能性があり、ホストシステムが脆弱性にさらされる可能性があるため、セキュリティーリスクが生じます。
ルートフルコンテナーとルートレスコンテナーの詳細は、podman RHEL システムロールを使用してバインドマウントでルートレスコンテナーを作成する および ルートレスコンテナーに関する特別な考慮事項 を参照してください。
Systemd とプロセス管理ツール
systemctl
- コンテナー内の systemd サービスを制御し、開始、停止、有効化、無効化の操作を可能にします。
journalctl
- systemd サービスによって生成されたログを表示し、コンテナーの問題のトラブルシューティングに役立ちます。
ネットワークツール
ip
- コンテナー内のネットワークインターフェイス、ルーティング、およびアドレスを管理します。
netstat
- ネットワーク接続、ルーティングテーブル、およびインターフェイス統計を表示します。
ping
- コンテナーまたはホスト間のネットワーク接続を検証します。
traceroute
- パケットが宛先に到達するまでのパスを識別します。ネットワークの問題の診断に役立ちます。
プロセスとパフォーマンスツール
ps
- コンテナー内で現在実行中のプロセスをリスト表示します。
top
- コンテナー内のプロセスによるリソース使用状況に関するリアルタイムの分析情報を提供します。
htop
- リソース使用率を監視するためのインタラクティブなプロセスビューアー。
perf
- CPU パフォーマンスのプロファイリング、トレース、およびモニタリングにより、システムまたはアプリケーション内のパフォーマンスのボトルネックを正確に特定できます。
vmstat
- コンテナー内の仮想メモリーの統計を報告し、パフォーマンス分析に役立ちます。
iostat
- コンテナー内のブロックデバイスの入出力統計を監視します。
gdb
(GNU デバッガー)- ユーザーが実行を追跡および制御し、変数を検査し、実行時にメモリーとレジスターを分析できるようにすることで、プログラムの調査とデバッグを支援するコマンドラインデバッガー。詳細は、Red Hat OpenShift コンテナー内のアプリケーションのデバッグ を参照してください。
strace
- プログラムによって行われたシステムコールを傍受して記録し、プログラムとオペレーティングシステム間のやり取りを明らかにすることでトラブルシューティングに役立ちます。
セキュリティーとアクセス制御ツール
sudo
- 昇格された権限でコマンドを実行できるようにします。
chroot
- コマンドのルートディレクトリーを変更します。異なるルートディレクトリー内でのテストやトラブルシューティングに役立ちます。
Podman 固有のツール
podman logs
- 実行時に 1 つ以上のコンテナーに存在するログをバッチで取得します。
podman inspect
- 名前または ID で識別されるコンテナーとイメージの低レベル情報を表示します。
podman events
-
Podman で発生するイベントを監視して出力します。各イベントには、タイムスタンプ、タイプ、ステータス、名前 (該当する場合)、およびイメージ (該当する場合) が含まれます。デフォルトのロギングメカニズムは
journald
です。 podman run --health-cmd
- ヘルスチェックを使用して、コンテナー内で実行されているプロセスの状態または準備ができているかどうかを判断できます。
podman top
- コンテナーの実行中のプロセスを表示します。
podman exec
- 実行中のコンテナー内でコマンドを実行したり、実行中のコンテナーにアタッチしたりすることは、コンテナー内で何が起こっているかをよりよく理解するのに非常に役立ちます。
podman export
- コンテナーに障害が発生すると、何が起こったのかを知ることは基本的に不可能です。コンテナーからファイルシステム構造をエクスポートすると、マウントされたボリュームに存在しない可能性のある他のログファイルを確認できるようになります。
第4章 開発用の追加ツールセット
4.1. コンパイラーツールセット
RHEL 10 は、Application Streams として次のコンパイラーツールセットを提供します。
- LLVM ツールは、LLVM コンパイラーインフラストラクチャーフレームワーク、C 言語および C++ 言語用の Clang コンパイラー、LLDB デバッガー、コード解析の関連ツールを提供します。
-
Rust Toolset は、Rust プログラミング言語コンパイラー
rustc
、cargo
ビルドツールおよび依存マネージャー、cargo-vendor
プラグイン、および必要なライブラリーを提供します。 -
Go Toolset は、Go プログラミング言語ツールおよびライブラリーを提供します。Go は、
golang
としても知られています。
使用方法の詳細と情報は、Red Hat Developer Tools ページのコンパイラーツールセットのユーザーガイドを参照してください。
4.2. Annobin プロジェクト
Annobin プロジェクトは Watermark 仕様プロジェクトの実装です。Watermark 仕様プロジェクトは、マーカーを Executable and Linkable Format (ELF) オブジェクトに追加してそのプロパティーを判断するためのものです。Annobin プロジェクトは、annobin
プラグインと annockeck
プログラムで構成されます。
annobin
プラグインは、GNU コンパイラーコレクション (GCC) コマンドライン、コンパイル状態、およびコンパイルプロセスをスキャンし、ELF ノートを生成します。ELF ノートでは、バイナリーの構築方法を記録し、セキュリティー強化チェックを実行する annocheck
プログラムの情報を得ることができます。
セキュリティー強化チェッカーは annocheck
プログラムの一部で、デフォルトで有効になっています。バイナリーファイルをチェックして、必要なセキュリティー強化オプションでプログラムが構築されていて、正しくコンパイルされているかを判断します。annocheck
は、ELF オブジェクトファイルのディレクトリー、アーカイブ、および RPM パッケージを再帰的にスキャンできます。
ファイルは ELF 形式である必要があります。annocheck
は、他のバイナリーファイルタイプの処理に対応していません。
次のセクションでは、以下を行う方法を説明します。
-
annobin
プラグインの使用 -
annocheck
プログラムの使用 -
冗長な
annobin
ノートの削除
4.2.1. annobin プラグインの使用
次のセクションでは、以下を行う方法を説明します。
-
annobin
プラグインの有効化 -
annobin
プラグインにオプションを渡します。
4.2.1.1. annobin プラグインの有効化
次のセクションでは、gcc
および clang
を使用して annobin
プラグインを有効にする方法を説明します。
手順
gcc
でannobin
プラグインを有効にするには、以下を使用します。gcc -fplugin=annobin
$ gcc -fplugin=annobin
Copy to Clipboard Copied! gcc
でannobin
プラグインを見つることができない場合は、以下を使用します。gcc -iplugindir=/path/to/directory/containing/annobin/
$ gcc -iplugindir=/path/to/directory/containing/annobin/
Copy to Clipboard Copied! /path/to/directory/containing/annobin/ は、
annobin
を含むディレクトリーへの絶対パスに置き換えます。annobin
プラグインを含むディレクトリーを検索するには、以下を使用します。gcc --print-file-name=plugin
$ gcc --print-file-name=plugin
Copy to Clipboard Copied!
clang
でannobin
プラグインを有効にするには、以下を使用します。clang -fplugin=/path/to/directory/containing/annobin/
$ clang -fplugin=/path/to/directory/containing/annobin/
Copy to Clipboard Copied! /path/to/directory/containing/annobin/ は、
annobin
を含むディレクトリーへの絶対パスに置き換えます。
4.2.1.2. annobin プラグインへのオプションの指定
次のセクションでは、gcc
および clang
を使用して annobin
プラグインにオプションを渡す方法を説明します。
手順
gcc
を使用してannobin
プラグインにオプションを渡すには、以下を使用します。gcc -fplugin=annobin -fplugin-arg-annobin-option file-name
$ gcc -fplugin=annobin -fplugin-arg-annobin-option file-name
Copy to Clipboard Copied! option は
annobin
コマンドライン引数に、file-name はファイル名に置き換えます。例
実行する
annobin
に関する追加情報を表示するには、以下を使用します。gcc -fplugin=annobin -fplugin-arg-annobin-verbose file-name
$ gcc -fplugin=annobin -fplugin-arg-annobin-verbose file-name
Copy to Clipboard Copied! file-name は、ファイルの名前に置き換えます。
clang
を使用してannobin
プラグインにオプションを渡すには、以下を使用します。clang -fplugin=/path/to/directory/containing/annobin/ -Xclang -plugin-arg-annobin -Xclang option file-name
$ clang -fplugin=/path/to/directory/containing/annobin/ -Xclang -plugin-arg-annobin -Xclang option file-name
Copy to Clipboard Copied! option は
annobin
コマンドライン引数に、/path/to/directory/containing/annobin/ は、annobin
を含むディレクトリーへの絶対パスに置き換えます。例
実行する
annobin
に関する追加情報を表示するには、以下を使用します。clang -fplugin=/usr/lib64/clang/10/lib/annobin.so -Xclang -plugin-arg-annobin -Xclang verbose file-name
$ clang -fplugin=/usr/lib64/clang/10/lib/annobin.so -Xclang -plugin-arg-annobin -Xclang verbose file-name
Copy to Clipboard Copied! file-name は、ファイルの名前に置き換えます。
4.2.2. annocheck プログラムの使用
次のセクションでは、annocheck
を使用して検証する方法を説明します。
- ファイル
- ディレクトリー
- RPM パッケージ
-
annocheck
の追加ツール
annocheck
は、ELF オブジェクトファイルのディレクトリー、アーカイブ、および RPM パッケージを再帰的にスキャンします。ファイルは ELF 形式である必要があります。annocheck
は、他のバイナリーファイルタイプの処理に対応していません。
4.2.2.1. annocheck を使用したファイルの検証
次のセクションでは、annocheck
を使用して ELF ファイルを検証する方法を説明します。
手順
ファイルを検証するには、以下を使用します。
annocheck file-name
$ annocheck file-name
Copy to Clipboard Copied! file-name は、ファイルの名前に置き換えます。
ファイルは ELF 形式である必要があります。annocheck
は、他のバイナリーファイルタイプの処理に対応していません。annocheck
は、ELF オブジェクトファイルなどの静的ライブラリーを処理します。
関連情報
-
annocheck
および使用可能なコマンドラインオプションの詳細は、システムのannocheck
の man ページを参照してください。
4.2.2.2. annocheck を使用したディレクトリーの検証
次のセクションでは、annocheck
を使用してディレクトリー内の ELF ファイルを検証する方法を説明します。
手順
ディレクトリーをスキャンするには、以下を使用します。
annocheck directory-name
$ annocheck directory-name
Copy to Clipboard Copied! directory-name は、ディレクトリー名に置き換えます。
annocheck
は、ディレクトリー、ディレクトリー内のサブディレクトリー、アーカイブおよび RPM パッケージの内容を自動的に検査します。
annocheck
は ELF ファイルのみを検索します。他のファイルタイプは無視されます。
関連情報
-
annocheck
および使用可能なコマンドラインオプションの詳細は、システムのannocheck
の man ページを参照してください。
4.2.2.3. annocheck を使用した RPM パッケージの検証
次のセクションでは、annocheck
を使用して RPM パッケージの ELF ファイルを検証する方法を説明します。
手順
RPM パッケージをスキャンするには、以下を使用します。
annocheck rpm-package-name
$ annocheck rpm-package-name
Copy to Clipboard Copied! rpm-package-name は、RPM パッケージ名に置き換えます。
annocheck
は、RPM パッケージ内のすべての ELF ファイルを再帰的にスキャンします。
annocheck
は ELF ファイルのみを検索します。他のファイルタイプは無視されます。
debug info RPM が含まれる RPM パッケージをスキャンするには、以下を使用します。
annocheck rpm-package-name --debug-rpm debuginfo-rpm
$ annocheck rpm-package-name --debug-rpm debuginfo-rpm
Copy to Clipboard Copied! rpm-package-name は RPM パッケージの名前に、debuginfo-rpm はバイナリー RPM に関連付けられた debug info RPM の名前に置き換えます。
関連情報
-
annocheck
および使用可能なコマンドラインオプションの詳細は、システムのannocheck
の man ページを参照してください。
4.2.2.4. annocheck の追加ツールの使用
annocheck
には、バイナリーファイルを検証する複数のツールが含まれます。コマンドラインオプションを使用してこれらのツールを有効にできます。
次のセクションでは、以下を有効にする方法を説明します。
-
built-by
ツール -
notes
ツール -
section-size
ツール
複数のツールを同時に有効にできます。
強化チェッカーはデフォルトで有効になっています。
4.2.2.4.1. built-by
ツールの有効化
annocheck
built-by
ツールを使用して、バイナリーファイルを構築したコンパイラーの名前を検索できます。
手順
built-by
ツールを有効にするには以下を使用します。annocheck --enable-built-by
$ annocheck --enable-built-by
Copy to Clipboard Copied!
関連情報
-
built-by
ツールの詳細は、コマンドラインオプション--help
を参照してください。
4.2.2.4.2. notes
ツールの有効化
annocheck
notes
ツールを使用して、annobin
プラグインが作成したバイナリーファイルに保存されたノートを表示できます。
手順
notes
ツールを有効にするには、以下を使用します。annocheck --enable-notes
$ annocheck --enable-notes
Copy to Clipboard Copied! このノートは、アドレス範囲順に表示されます。
関連情報
-
notes
ツールの詳細は、コマンドラインオプション--help
を参照してください。
4.2.2.4.3. section-size
ツールの有効化
annocheck
section-size
ツールを使用すると、名前付きセクションのサイズを表示できます。
手順
section-size
ツールを有効にするには、以下を使用します。annocheck --section-size=name
$ annocheck --section-size=name
Copy to Clipboard Copied! name は、名前付きセクションの名前に置き換えます。出力は、特定のセクションに限定されます。累積結果は、最後に生成されます。
関連情報
-
section-size
ツールの詳細は、コマンドラインオプション--help
を参照してください。
4.2.2.4.4. 強化チェッカーの基本
強化チェッカーはデフォルトで有効になっています。コマンドラインオプション --disable-hardened
で、強化チェッカーを無効にできます。
4.2.2.4.4.1. 強化チェッカーのオプション
annocheck
プログラムは、以下のオプションをチェックします。
-
-z now
リンカーオプションを使用して遅延結合が無効になる。 - プログラムのメモリーの実行可能なリージョン内にスタックがない。
- GOT テーブルの再配置が読み取り専用に設定されている。
- プログラムセグメントには、読み取り、書き込み、および実行権限ビットセットの 3 つすべてがある。
- 実行コードに対する再配置がない。
- ランタイム時に共有ライブラリーを見つけるための runpath 情報には、/usr にルート指定されたディレクトリーのみが含まれている。
-
プログラムが
annobin
ノートを有効にしてコンパイルされている。 -
プログラムが
-fstack-protector-strong
オプションを有効にしてコンパイルされている。 -
プログラムが
-D_FORTIFY_SOURCE=2
でコンパイルされている。 -
プログラムが
-D_GLIBCXX_ASSERTIONS
でコンパイルされている。 -
プログラムが
-fexceptions
を有効にしてコンパイルされている。 -
プログラムが
-fstack-clash-protection
を有効にしてコンパイルされている。 -
プログラムが
-O2
以降でコンパイルされている。 - プログラムには書き込み可能な再配置がない。
- 動的実行可能ファイルには動的セグメントがある。
-
共有ライブラリーが
-fPIC
または-fPIE
でコンパイルされている。 -
動的実行可能ファイルは
-fPIE
でコンパイルされ、-pie
でリンクされている。 -
利用可能な場合は、
-fcf-protection=full
オプションが使用されている。 -
利用可能な場合は、
-mbranch-protection
オプションが使用されている。 -
利用可能な場合は、
-mstackrealign
オプションが使用されている。
4.2.2.4.4.2. 強化チェッカーの無効化
次のセクションでは、強化チェッカーを無効にする方法を説明します。
手順
強化チェッカーがないファイルでノートをスキャンするには、以下を使用します。
annocheck --enable-notes --disable-hardened file-name
$ annocheck --enable-notes --disable-hardened file-name
Copy to Clipboard Copied! file-name は、ファイルの名前に置き換えます。
4.2.3. 冗長する annobin ノートの削除
annobin
を使用すると、バイナリーのサイズが増えます。annobin
でコンパイルしたバイナリーのサイズを縮小するには、冗長な annobin
ノートを削除できます。冗長する annobin
ノートを削除するには、binutils
パッケージに含まれる objcopy
プログラムを使用します。
手順
冗長な
annobin
ノートを削除するには、以下を使用します。objcopy --merge-notes file-name
$ objcopy --merge-notes file-name
Copy to Clipboard Copied! file-name は、ファイルの名前に置き換えます。
第5章 RHEL 10 における主な変更点
以下は、RHEL 10 における重要な変更点です。
5.1. C++ における互換性に影響を与える変更
std::condition_variable::wait
がスレッドのキャンセルポイントになる
GCC 11 以前では、std::condition_variable::wait
関数は noexcept
であったため、スレッドのキャンセルと互換性がありませんでした。その結果、pthread_cancel
の呼び出しによって std::condition_variable::wait
でブロックされたスレッドがキャンセルされると、プロセスは終了します。GCC 12 以降では、std::condition_variable::wait
は pthread_cancel
関数の呼び出しによってキャンセルされる可能性があり、その際にはスタックがアンワインドされます。例外をスローしない wait
に依存するコードがある場合は、コードを確認して適切なアクションを実行してください。
非推奨のクラステンプレート
特定のクラステンプレートは C++ の新しいバージョンでは非推奨となり、GCC 12 以降では警告診断が生成されます。
次のクラステンプレートは C++11 以降では非推奨となりました。
-
std::unary_function
-
std::binary_function
-
-
std::iterator
クラステンプレートは、C++17 以降では非推奨となりました。
警告診断を防ぐには、次のいずれかのアクションを実行します。
コードに他の変更を加えたくない場合は、GCC の診断プラグマを使用して警告診断を無効にします。以下に例を示します。
#pragma GCC diagnostic push #pragma GCC diagnostic ignored “-Wdeprecated-declarations” class Functor : public std::unary_function<int, int> { /* … */ }; #pragma GCC diagnostic pop
#pragma GCC diagnostic push #pragma GCC diagnostic ignored “-Wdeprecated-declarations” class Functor : public std::unary_function<int, int> { /* … */ }; #pragma GCC diagnostic pop
Copy to Clipboard Copied! コードを C++ の新しいバージョンと互換性を持たせたい場合は、コード内のこれらのクラステンプレートをネストされた typedefs に置き換えます。たとえば、
std::unary_function
基本クラスをresult_type
およびargument_type
typedefs に置き換えることができます。class Functor { using result_type = int; using argument_type = int; /* … */ };
class Functor { using result_type = int; using argument_type = int; /* … */ };
Copy to Clipboard Copied!
5.2. glibc
の互換性に影響を与える変更
動的リンカーが共有オブジェクトを見つける方法における変更
次のリストは、動的リンカー検索アルゴリズムの変更点を示しています。
-
動的リンカーは、ライブラリー検索パス上の
tls
サブディレクトリーまたはAT_PLATFORM
システム名に対応するサブディレクトリーから共有オブジェクトをロードしなくなりました。 -
動的リンカーは、従来の
AT_HWCAP
検索メカニズムにちなんで名付けられたサブディレクトリーを検索しなくなりました。
RHEL 8.4 以降で利用可能な glibc-hwcaps
メカニズムにアプリケーションを移植します。
catchsegv
スクリプトと libSegFault.so
共有オブジェクトを削除
catchsegv
スクリプトと関連する libSegFault.so
共有オブジェクトは削除されました。コアダンプとバックトレースをインターセプトするには、systemd-coredump
や coredumpctl
などのプロセス外の代替手段を使用できます。
5.3. C23 サポート
glibc
での C2X サポート
GNU C ライブラリーには、C23 標準の一部であるいくつかの新機能が追加されました。詳細は、glibc
のマニュアルを参照してください。