ランタイムライブラリ
ランタイムライブラリ(英: run-time library)は、標準Cライブラリなど、コンピュータプログラムの実行時(ランタイム)にメインプログラムと常に同時に存在して利用される前提のライブラリである。
概要
[編集]C言語ではmain
関数を実行した直後、<stdio.h>に定義されたstdout
を参照すると標準出力のファイルポインタを得ることができる。また、main
関数の引数を参照すればコマンドライン引数の文字列を参照することができる。これらはオペレーティングシステム (OS) やコンパイラが初期化しているものではなく、main
関数を呼び出している関数が更に存在しその中に「スタートアップルーチン」とよばれる初期化処理と解放処理が存在しているからである[1][2][3]。また、puts
といった標準ライブラリの関数はランタイムライブラリの中に実装されているため、別途明示的なリンク指定をすることなくヘッダーの#include
をするだけで使うことができる。これらのスタートアップルーチンや標準関数といった、言語に必要な標準的機能を提供するライブラリがランタイムライブラリである。
CランタイムライブラリはシステムコンポーネントとしてOSに標準搭載されていることもある。GNU CライブラリからフォークしたLinux libc、AndroidのBionic libc[4]、Microsoft Windows 10のUniversal CRTなどがある。
処理系との関係
[編集]ランタイムライブラリは一般的なライブラリと異なり、言語機能を支える側面から処理系の一部という性格が強い。特にC++では、例外処理や、new
、delete
、ユーザー定義のコンストラクタを持つ型の静的変数といった、コンパイル時のコード生成では冗長でコードが肥大化してしまう言語機能が存在し、それらの機能は(共通化されたサブルーチンとしての)ランタイムライブラリの関数に依存している。他の言語でもっと大きなものの例としてはガベージコレクションの機能などがある[要出典]。このため一般的なライブラリと異なり、ランタイムライブラリは明示的にリンク指定をしなくても処理系のリンカによって自動でリンクされる。また、ランタイムライブラリはデバッグ用とリリース用など、用途に応じて一つの環境に複数存在するが、処理系のリンカはコンパイルオプションやリンクオプションに応じて適切なランタイムライブラリを選択する[2]。
必然的にリンクされるものであり、インタフェース的にも密結合であることから、(動的リンクがサポートされている環境でも)静的リンクされることも多い。同様に、ダイナミックリンクライブラリで提供されていて動的リンクする場合でも、必要に応じて遅延ロードするのではなく、プロセスの起動時に一括で早期ロードされることもある。
ランタイムライブラリの除去
[編集]CおよびC++には言語仕様によってOSに依存しないフリースタンディング環境が定められている。Cにおけるフリースタンディング環境では標準ライブラリとして関数が一切存在しない。また、エントリーポイントもmain
である必要がなく、グローバル変数等の初期化もいらない。Cにおけるフリースタンディング環境ではランタイムライブラリを必要としない。ただしC++についてはOSなしの環境でも最低限の言語機能を実現するためランタイムライブラリを必要とする。
ランタイムライブラリを除去する方法はフリースタンディング環境だけでなく、処理系のオプションによって強制的に実現することもできる。gccでは-nodefaultlibs
[5]を指定し、Microsoft Visual C++では/NODEFAULTLIB
[6]を指定する。標準のランタイムライブラリを除去することにより実行ファイルを軽量化したり、TinyCRTのような第三者提供のランタイムライブラリ[7]を使用することができるようになる。特に軽量化はメガデモとよばれるデモシーンにおいて必須の技術となっている。
再配布時の注意点
[編集]多くのリンカはランタイムライブラリを静的リンクするオプションを明示的に指定しない限り、容量削減と脆弱性対策の観点から既定で動的リンク形式のランタイムライブラリを選択する[8]。このため、アプリケーションプログラムを実行するコンピュータには、そのアプリケーションプログラムのモジュールとは別に動的リンク形式のランタイムライブラリのモジュールが必要となる。もし再配布先のコンピュータ(エンドユーザー環境)にランタイムライブラリが存在しなければ、プログラムは実行直後に異常終了してしまう。異常終了に至る流れはランタイムライブラリに限らず動的リンクを使う場合全般に言えることであるが、ランタイムライブラリについては暗黙にリンクしてしまうため見逃しやすく、特に問題が発生しやすい。また、もしランタイムライブラリがアプリケーションごとにプライベートでなく、システム全体で共有される場合、バージョン互換性の問題も抱えることになる(DLL地獄)。
再配布先にランタイムライブラリが存在しない状況としては、
- 再配布先のコンピュータにインストールされている共有ランタイムライブラリのバージョンが一致しない。
- 再配布したプログラムが開発用の(デバッグ情報や性能測定用情報を含んだ)ランタイムライブラリとリンクしている。
といったものがある。
ランタイムライブラリは、同じシリーズのコンパイラでも異なるバージョン間でバイナリ互換性がないこともあり[9]、動的リンク形式のモジュールに関してはバージョンに応じた名前を付けるなどして、side-by-sideで管理されていることがある。その場合はプログラムのビルドに使用したコンパイラに対応するバージョンの共有ランタイムライブラリが必要になる。また、バイナリ互換性がない場合にモジュール境界を越えてオブジェクトをやりとりすると未定義の問題が生じる[10]。
再配布先のコンピュータにインストールされている共有ランタイムライブラリのバージョンが一致しない場合の対策としては、ランタイムライブラリを静的リンクする方法や、プライベートモジュールとしてアプリケーションのパッケージに同梱する方法がある。確実な方法ではあるが、ランタイムライブラリにセキュリティホール(脆弱性)が見つかった場合、共有ランタイムライブラリであればOSのセキュリティパッチ(脆弱性対策)によって更新される一方、ランタイムライブラリを静的リンクしたりプライベートモジュールとして再配布したりしてしまうと、その恩恵を受けられないという欠点もある。
通例、共有ランタイムライブラリは再配布可能 (redistributable) パッケージ[11]またはOSの更新プログラム[12]として再配布先のコンピュータに導入することが可能であるが、このパッケージや更新プログラムによって導入できるランタイムライブラリはエンドユーザー向け(リリースビルド用)のランタイムライブラリだけであり、開発者向け(デバッグビルド用)のランタイムライブラリは含まれない[2]。このため誤ってデバッグ用のランタイムライブラリとリンクしたプログラムを配布すると異常終了を発生させることになる。
特に問題が発生しやすくなる要因としては下記のものがある。
- 開発用のコンピュータに、コンパイラをはじめとする開発ツールをインストールすると、暗黙のうちにデバッグ用の共有ランタイムライブラリがインストールされるため、デバッグ用のランタイムライブラリの存在を自覚しづらい。
- デバッグオプションの有無だけでリリース用とデバッグ用が切り替わるため、どちらのライブラリを使っているか自覚しづらい。
- リリース用のランタイムライブラリをリンクしたプログラムと、デバッグ用のランタイムライブラリをリンクしたプログラムの間に、目に見える大きな差がない。
対策としては、開発ツールがインストールされていないコンピュータ上で必ず動作確認することが挙げられる。
脚注
[編集]- ^ /ENTRY (Entry-Point Symbol) | Microsoft Docs
- ^ a b c CRT Library Features | Microsoft Docs
- ^ 典型的なスタートアップルーチンとしては
crt0
がある。詳細は crt0 を参照。 - ^ ネイティブ API | Android NDK | Android Developers
- ^ https://gcc.gnu.org/onlinedocs/gcc/Link-Options.html
- ^ /NODEFAULTLIB (Ignore Libraries) | Microsoft Docs
- ^ https://github.com/larryhe/tinyCRT
- ^ Upgrade your code to the Universal CRT | Microsoft Docs
- ^ C++ binary compatibility 2015-2019 | Microsoft Docs
- ^ Potential Errors Passing CRT Objects Across DLL Boundaries | Microsoft Docs
- ^ Redistributing Visual C++ Files | Microsoft Docs
- ^ Windows での汎用の C ランタイムの更新プログラム