弱参照と定義について

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

弱参照

リンカでは、通常の非弱参照をリンクに含まれているシンボルに解決できない場合、ライブラリ内のシンボルを検索して解決を試みます。

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

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

C ソースファイル内の関数または変数の宣言は、__weak 修飾子を使用してマークできます。 extern と同様に、この修飾子は、関数または変数が別のソースファイル内で宣言されていることをコンパイラに通知します。 この関数または変数の定をコンパイラが使用できない可能性があるため、リンカによって解決される弱参照が作成されます。

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

  • シンボルがコード内の他の場所から厳密に参照されている。

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

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

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

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

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

  • 非演算命令 NOP

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

weak 定義

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

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

Show/hide弱参照の例

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

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

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

  • コンパイラコマンドラインオプション --split_sections を指定すると、各関数は独自のセクションに配置されます。 この例では、このオプションを指定してライブラリをコンパイルすると、foo()init_foo() は別々のセクションに配置されます。 そのため、foo() への呼び出しが原因で、init_foo() は自動的にはビルドに含まれません。

  • リンカフィードバックメカニズム --feedback を指定すると、リンク手順で init_foo() は未使用と記録されます。 そのため、その後のコンパイルで init_foo() は独自のセクションに配置され、削除できるようになります。

  • コンパイラディレクティブ #pragma arm section を指定した場合も、コンパイラによって、一部の関数に対して個別の 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
    ; 残りのコード
  END

Show/hideweak 定義の例

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

Show/hide関連項目

Copyright © 2010-2012 ARM. All rights reserved.ARM DUI 0474GJ
Non-ConfidentialID051612