弱いシンボルは、実行可能およびリンク可能形式(ELF)のオブジェクト ファイルのリンク中に特別に注釈が付けられたシンボルを示します。デフォルトでは、注釈がない場合、オブジェクト ファイル内のシンボルは強いシンボルです。リンク中、強いシンボルは同じ名前の弱いシンボルをオーバーライドできます。対照的に、同じ名前の強いシンボルが 2 つ存在する場合、リンカーは最初に見つかったシンボルを優先してシンボルを解決します。この動作により、実行可能ファイルはmalloc (3) などの標準ライブラリ関数をオーバーライドできます。バイナリ実行可能ファイルをリンクする場合、弱く宣言されたシンボルには定義は必要ありません。比較すると、(デフォルトでは) 定義のない強いシンボルが宣言されると、未定義のシンボル リンク エラーが発生します。
弱いシンボルはCやC++言語標準では言及されていないため、コードに挿入しても移植性はあまり高くありません。2つのプラットフォームがシンボルを弱いシンボルとしてマークするための同じまたは類似の構文をサポートしている場合でも、微妙な点で意味が異なることがあります。たとえば、実行時の動的リンク中に弱いシンボルが意味を失うかどうかなどです。[1]
構文
GNUコンパイラコレクションとSolaris Studio Cコンパイラは、シンボルを弱いものとして注釈するための同じ構文、つまり特別な#pragma、、#pragma weakおよび代わりに関数と変数の属性を共有しています__attribute__((weak))。[2] [3] [4] [5] [6] [7]
プラグマ
// 関数宣言
#pragma weakness power2
intべき乗2 ( int x );
属性
// 関数宣言
int __attribute__ ((弱い))のべき乗( int x );
// または
int power2 ( int x ) __attribute__ ((弱い));
// 変数宣言;
extern int __attribute__ (( weak )) global_var ;
ツールサポート
nmコマンドは、オブジェクト ファイル、ライブラリ、および実行可能ファイル内の弱いシンボルを識別します。Linux では、弱い関数シンボルは、弱いデフォルト定義が使用可能な場合は「W」でマークされ、そうでない場合は「w」でマークされます。弱く定義された変数シンボルは、「V」および「v」でマークされます。Solaris では、「nm」は弱いシンボルに対して「GLOB」ではなく「WEAK」を出力します。
例
次の例は、GCC と Solaris Studio を搭載した LinuxおよびSolarisで動作します。
静的な例
main.c :
#include <stdio.h> #include <stdlib.h>
#include "power_slow.h"
int main ( int argc , char ** argv ) { fprintf ( stderr , "power3() = %d \n " , power3 ( atoi ( argv [ 1 ]))); 0を返す; }
パワースロー.h:
#ifndef POWER2_SLOW_H
#define POWER2_SLOW_H
// 代替構文
// #pragma weakness power2
int
__attribute__ (( weak )) power2 ( int x ) // 代わりにシンボルの後// __attribute__((weak)) ;
int のべき乗3 ( int x );
#終了
パワースロー.c :
#include <stdio.h> #include "power_slow.h"
int power2 ( int x ) { fprintf ( stderr , "slow power2() \n " ); return x * x ; }
int power3 ( int x ) {戻り値 power2 ( x ) * x ; }
パワー.c :
#include <stdio.h> int power2 ( int x ) { fprintf ( stderr , "fast power2() \n " ); return x * x ; }
ビルドコマンド:
cc -g -c -o main.o main.c
cc -g -c -o power_slow.o power_slow.c
cc -g -c -o パワー.o パワー.c
cc main.o power_slow.o -o遅い
cc main.o power_slow.o power.o -o fast
出力:
$ ./slow 3遅い power2() power3() = 27 $ ./fast 3速い power2() power3() = 27
弱い属性を削除してビルド コマンドを再実行すると、最後のコマンドが失敗し、次のエラー メッセージが表示されます (Linux の場合)。
`power2' の複数の定義
最後から 2 番目のものも成功し、./slow同じ出力が得られます。
弱いシンボル関数の定義が、デフォルトの実装として、またはリンクされたオブジェクト ファイルのいずれかに別の弱いシンボル関数定義や強いシンボル関数定義として存在しない場合、その弱いシンボルに対して未定義のシンボル エラーが発生することなくリンクは正常に実行されますが、実行するとランタイム クラッシュが発生する可能性があります。
共有例
前の例の main.c に以下を追加します。
#ifndef NO_USER_HOOK
void user_hook ( void ) { fprintf ( stderr , "main: user_hook() \n " ); } #endif
power_slow.c を次のように置き換えます:
#include <stdio.h> #include "power_slow.h"
void __attribute__ (( weak )) user_hook ( void ); #ifdef ENABLE_DEF void user_hook ( void ) { fprintf ( stderr , "power_slow: user_hook() \n " ); } #endif
int power2 ( int x ) { if ( user_hook ) // 必要となるのは ifndef ENABLE_DEF user_hook (); return x * x ; }
int power3 ( int x ) {戻り値 power2 ( x ) * x ; }
ビルドコマンド:
cc -g -c -o main.o main.c
cc -g -fpic -c -o power_slow.po power_slow.c
cc -shared -fpic -o libpowerslow.so power_slow.po
cc main.o -L ` pwd ` -Wl,-R ` pwd ` -lpowerslow -o main
cc -g -DENABLE_DEF -fpic -c -o power_slow.po power_slow.c
cc -shared -fpic -o libpowerslow.so power_slow.po
cc main.o -L ` pwd ` -Wl,-R ` pwd ` -lpowerslow -o main2
cc -g -DNO_USER_HOOK -c -o main.o main.c
cc -g -fpic -c -o power_slow.po power_slow.c
cc -shared -fpic -o libpowerslow.so power_slow.po
cc main.o -L ` pwd ` -Wl,-R ` pwd ` -lpowerslow -o main3
cc -g -DNO_USER_HOOK -c -o main.o main.c
cc -g -DENABLE_DEF -fpic -c -o power_slow.po power_slow.c
cc -shared -fpic -o libpowerslow.so power_slow.po
cc main.o -L ` pwd ` -Wl,-R ` pwd ` -lpowerslow -o main4
出力:
$ ./main 3 main: user_hook() power3() = 27 $ ./main2 3 main: user_hook() power3() = 27 $ ./main3 3 power3() = 27 $ ./main4 3 power_slow: user_hook() power3() = 27
弱い属性を削除してビルド コマンドを再実行すると、ビルド エラーは発生せず、mainとに対して同じ出力 (Linux の場合) が表示されますmain2。 のビルド コマンドを実行すると、main3次の警告およびエラー メッセージが表示されます (Linux の場合)。
警告: 'user_hook' のアドレスは常に 'true' として評価されます libpowerslow.so: `user_hook' への未定義の参照
警告は、式に ELF ジャンプ テーブル エントリが含まれているため、式が常に true に評価されることを静的に判断できるため、コンパイラによって発行されます。エラー メッセージはリンカーによって発行されます。のビルドには同じ警告が含まれますが、リンク エラーは含まれません。
if (user_hook)user_hookmain4
ユースケース
弱いシンボルは、リンク時に、より特化された(最適化されたなど)関数に置き換えることができる関数のデフォルト実装を提供するメカニズムとして使用できます。デフォルトの実装は弱いとして宣言され、特定のターゲットでは、強く宣言されたシンボルを含むオブジェクト ファイルがリンカー コマンド ラインに追加されます。
ライブラリがシンボルを弱いシンボルとして定義した場合、そのライブラリにリンクするプログラムは、カスタマイズなどの目的で強いシンボルを自由に提供できます。
弱いシンボルのもう 1 つの使用例は、バイナリの下位互換性の維持です。
制限事項
UNIX System V系のシステムでは、プログラム実行時に動的リンカーが弱いシンボル定義を強いシンボル定義と同様に解決します。たとえば、バイナリがライブラリ libfoo.so と libbar.so に対して動的にリンクされているとします。libfoo はシンボルを定義しf、それを弱いシンボルとして宣言します。libbar もfそれを強いシンボルとして定義し、宣言します。リンク コマンドラインでのライブラリの順序 (つまり-lfoo -lbar) に応じて、動的リンカーは libfoo.so の弱いシンボル f を使用しますが、実行時には強いシンボルが使用できます。GNU は、動的リンカーに弱いセマンティクスを提供するld環境変数を提供しています。 [1] [8]LD_DYNAMIC_WEAK
次のような構文を使用する場合
#pragma 弱い関数
void関数();
void bar () { if ( func ) func (); }
、コンパイラと使用される最適化レベルに応じて、コンパイラは条件を常に真と解釈する場合があります(func標準の観点からは未定義と見なされるため)。[7]上記の構成の代替として、システムAPIを使用してがfunc定義されているかどうかを確認する方法があります(例: を含むdlsym RTLD_DEFAULT)。 上記のチェックは、funcにelfジャンプテーブルエントリが含まれている場合など、他の理由で失敗することもあります。[9]
静的ライブラリで弱いシンボルを使用する場合、共有ライブラリとは異なる意味を持ちます。つまり、静的ライブラリでは、シンボルの検索は最初のシンボルで停止します。たとえそれが弱いシンボルであっても、強いシンボルを持つオブジェクトファイルもライブラリアーカイブに含まれます。Linuxでは、リンカーオプションによって--whole-archiveその動作が変わります。[10]
弱い関数属性は関数宣言で使用することを想定しています。関数定義で使用すると、コンパイラと最適化レベルによっては予期しない結果が生じる可能性があります。[11]
Solaris では、カーネル内でも弱いシンボルが使用されます。カーネルの汎用部分 ( と呼ばれるgenunix)はunix、仮想メモリ ルーチンなどのカーネルのプラットフォーム固有の部分 ( と呼ばれる) でオーバーライドされる弱い関数を指定します。カーネル ランタイム リンカーは、ブート中にカーネルがメモリに結合されるときに、これらの関数のアドレスを設定します。ただし、これはカーネルがロード可能なモジュールでは機能しません。カーネル内の弱いシンボルは、モジュールがロードされるときにカーネル モジュール シンボルに置き換えられません。
関連する方法
C プリプロセッサ(CPP) 条件構文を使用して、シンボルの異なるバージョンを切り替えることもできます。弱いシンボルとの違いは、弱いシンボルはリンカーによって解釈されることです。CPP は、C コンパイラの前に各翻訳単位のコンパイル中に実行されます。
ビルド プロセス (例: make) は、ターゲットに応じて異なるバージョンのシンボルのみを作成したり、異なる (特殊な) ライブラリが使用およびリンクされるように条件付きで実装できます。
参照
参考文献
- ^ ab Drepper, Ulrich (2000-06-07). 「弱い処理」.
- ^ 「GCC マニュアル、6.58.9 弱いプラグマ」。
- ^ 「GCC マニュアル、6.30 関数の属性の宣言」。GNU。2013年 5 月 29 日閲覧。
- ^ 「GCCマニュアル、6.36 変数の属性の指定」。
- ^ 「Oracle Solaris Studio 12.3: C ユーザーズ ガイド、2.11.27 弱」。
- ^ 「Oracle Solaris Studio 12.3: C ユーザーズ ガイド、2.9 サポートされている属性」。
- ^ ab 「Oracle Solaris 11 Express 11/10 リンカーおよびライブラリ ガイド、2.11 弱いシンボル」。
- ^ Drepper, Ulrich (2011 年 10 月)。「共有ライブラリの書き方 (バージョン 4.1.2)、1.5.2 シンボルの再配置、6 ページ」(PDF)。
- ^ 「弱いリンクと Linux 共有ライブラリ」。
- ^ 「GNU LD マニュアルページ」。
- ^ Kiszka, Jan (2006-05-23). 「Re: 4.1 での弱属性の過剰最適化」
