3.22 弱参照と定義

弱参照と定義を使用することで、リンカはより柔軟な方法でビルドにさまざまな関数や変数を含めることができます。

弱参照と定義は通常、ライブラリ関数への参照です。

弱参照

リンカでは、通常の非弱参照をこれまでにロードされた内容からシンボルに解決できない場合、ライブラリ内のシンボルを検索して解決を試みます。

  • そうした参照を見つけることができなかった場合、リンカによってエラーが報告されます。

  • そうした参照が解決されると、少なくとも 1 つの非弱参照によってエントリポイントから到達できるセクションは、使用済みとマークされます。こうすることで、このセクションが未使用のセクションとしてリンカによって削除されることがなくなります。各非弱参照は、1 つの定義によってのみ解決される必要があります。複数の定義が存在する場合、リンカによってエラーが報告されます。

弱参照を解決するために、リンカによってライブラリからオブジェクトがロードされることはありません。弱参照は、定義が他の理由でイメージに含まれている場合にのみ解決できます。弱参照によって、リンカが定義を含むセクションを使用済みとマークすることはありません。そのため、未使用のセクションとしてリンカによって削除される可能性があります。以下のさまざまな理由で、定義がイメージ内に既に存在している場合があります。

  • シンボルが、コード内の他の場所からの非弱参照である。

  • シンボル定義が、これらのいずれかの理由で含まれているシンボル定義と同じ ELF セクション内に存在している。

  • シンボル定義が、 --keep を使用して指定されているセクション、または ENTRY ポイントを含むセクション内に存在している。

  • シンボル定義がリンクに含まれているオブジェクトファイル内に存在し、 --no_remove オプションが使用されている。ライブラリ内のオブジェクトファイルが明示的にリンカコマンドラインに含まれている場合を除き、オブジェクトファイルはライブラリから参照されません。

要約すると、弱参照は定義がイメージに既に含まれている場合に解決されますが、その定義が含まれているかどうかの確認は行いません。

未解決の weak 関数の呼び出しは、以下のいずれかに置き換えられます。

  • 非演算命令 NOP

  • リンク命令 BL を使用した、後続の命令への分岐。(つまり、関数呼び出しは行われません)

weak 定義

関数定義、つまりアセンブラのエクスポートされたラベルは、変数の定義と同じように、weak としてマークすることもできます。この場合、weak シンボル定義がオブジェクトファイル内に作成されます。

通常の定義と同様の方法で、weak 定義を使用して、そのシンボルへの参照を解決できます。ただし、そのシンボルの non-weak 定義がビルド内に存在する場合、リンカでは weak 定義の代わりに、その定義を使用します。複数回定義されたシンボルが原因で、エラーは生成されません。

弱参照の例

ライブラリには、関数 foo() が含まれています。この関数は、アプリケーションの一部のビルドで呼び出され、その他のビルドでは呼び出されません。この関数を使用する場合は、init_foo() を最初に呼び出す必要があります。弱参照を使用して、init_foo() への呼び出しを自動化できます。

ライブラリでは、init_foo()foo() を同じ ELF セクション内に定義できます。アプリケーションの初期化コードでは、init_foo() を weak で呼び出す必要があります。何らかの理由でアプリケーションに foo() が含まれている場合は、 init_foo() も含まれており、それが初期化コードから呼び出されます。foo() が含まれていないビルドでは、init_foo() への呼び出しはリンカによって削除されます。

通常、1 つのソースファイル内で定義されている複数の関数のコードは、コンパイラによって、1 つの ELF セクションに配置されます。ただし、特定のビルドオプションによってこの動作が変更される場合があります。そのため、ビルドが ELF セクションへのファイルのグループ化に依存している場合は、注意して使用する必要があります。

この例では、foo() を含むビルドと、foo() を含まないビルトとで初期化コードをビルドし直す必要はありません。また、init_foo() を呼び出さないバージョンの初期化コードと、アプリケーションの他の部分で foo() を呼び出すアプリケーションが誤ってビルドされる可能性もありません。

通常、ライブラリにビルドされる foo.c ソースコードの例を以下に示します。

void init_foo() {     // 初期化コード } void foo() {      // 一部のビルドに含まれ      // init_foo() を最初に呼び出す必要がある関数。}

init.c の例を以下に示します。

__weak void init_foo(void); int main(void) {     init_foo();     //  foo() を直接または間接的に呼び出したりする残りのコード。}

アセンブラによって生成される弱参照の例を以下に示します。

init.s:IMPORT init_foo WEAK   AREA init, CODE, readonly     BL init_foo     ;Rest of code   END

weak 定義の例

関数の単純な実装またはダミーの実装を weak 定義として使用できます。これらを使用すると、関数の完全な実装を含めることなく動作が定義されたソフトウェアをビルドできます。また、必要に応じて、一部のビルドに完全な実装を含めることもできます。

関連する概念
3.23 リンカによるライブラリの検索、選択、およびスキャンの実行方法
3.26 リンカによる参照の解決方法
関連する参考文書
12.57 --feedback=filename
12.78 --keep=section_id
12.118 --remove、--no_remove
関連情報
--split_sections コンパイラオプション
__weak
__attribute__((weak)) 関数属性
__attribute__((weak)) 変数属性
EXPORT、GLOBAL
IMPORT、EXTERN
__attribute__((weakref("target"))) 変数属性
#pragma arm section [section_type_list]
NOP
B
ENTRY
非機密扱いPDF file icon PDF 版ARM DUI0474JJ
Copyright © 2010-2013 ARM.All rights reserved.