GCCの強い記号と弱い記号と強い参照と弱い参照

5048 ワード

1.強い記号と弱い記号


1.1 u-bootとkernelの_weak指令

u-bootおよびkernelは、関数を定義するために__weakを比較的一般的に使用する.include\linux\compiler-gcc.hにおいて__weakは、以下のように定義される.
#define __weak              __attribute__((weak))
GCC__attribute__((weak))命令で定義した関数または変数を弱符号(Weak Symbol)と呼ぶが、実際にはこの命令の大部分は関数を定義するために使用され、変数を定義するために使用されることは少ない.
コンパイラのデフォルト関数と初期化されたグローバル変数は強いシンボル(Strong Symbol)、初期化されていないグローバル変数は弱いシンボル(Weak Symbol)です.

1.2強い記号と弱い記号のリンク規則


<>第3章では,この強弱記号のリンク規則をよくまとめた.
強弱シンボルの概念に対して、リンクは次のルールに従って、複数回定義されたグローバルシンボルを処理し、選択します.
  • ルール1:強い記号が複数回定義されることは許されない(すなわち、異なるターゲットファイルに同名の強い記号が存在してはならない).複数の強いシンボル定義がある場合、リンク・レポート・シンボルは定義エラーを繰り返します.
  • ルール2:1つのシンボルがターゲットファイルに強いシンボルであり、他のファイルに弱いシンボルがある場合は、強いシンボルを選択します.
  • ルール3:1つのシンボルがすべてのターゲットファイルで弱いシンボルである場合、占有スペースが最大の1つを選択します.例えば、ターゲットファイルAはグローバル変数globalをint型と定義し、4バイトを占める.ターゲットファイルBはglobalをdoulbe型と定義し、8バイトを占めると、ターゲットファイルAとBがリンクされた後、シンボルglobalが8バイトを占める(できるだけ複数の異なるタイプの弱いシンボルを使用しないでください.そうしないと、発見しにくいプログラムエラーを招きやすい).
  • ルール1は、プログラムで初期化されたグローバル変数(int a=0など)が複数回定義されている場合、コンパイラはmultiple definitionのエラーを報告し、比較的一般的です.

  • 複数回定義されたグローバル関数も、関数名もシンボル変数であるため、その後定義された関数体を指すだけでなく、この変数は読み取り専用であり、変更できません.これも、関数名を変数に割り当てることができますが、関数名に値を割り当てることができない理由です.
  • ルール2はu-bootkernelの中で最もよく使われるテクニックです.

  • まず、__weaku-bootのファイルヘッダなど、プラットフォームに関係のないコードにboard_f.cの属性の関数を定義します.
    __weak void coloured_LED_init(void) {}
    __weak void red_led_on(void) {}
    __weak void red_led_off(void) {}
    __weak void green_led_on(void) {}
    __weak void green_led_off(void) {}
    __weak void yellow_led_on(void) {}
    __weak void yellow_led_off(void) {}
    __weak void blue_led_on(void) {}
    __weak void blue_led_off(void) {} 

    これらのledに関連する機能が必要であれば、関連するファイルでこれらの関数を再定義して実装すればよい.例えば、ファイルledでマクロgpio_led.cが開いたときにCONFIG_GPIO_LED_STUBSを呼び出してこれらの関数を再実装する.
  • 規則3は、同名の弱い記号の複数の定義が、主にタイプを使用しない変数に対して、占有空間が最大であることを示す.関数の場合、対応するシンボルは読み取り専用ポインタ変数に相当し、ポインタタイプのサイズは固定されており、この問題はありません.

  • 2.強引用と弱引用


    <>第三章では、強引用と弱引用についても説明した.
    弱参照と強参照現在私たちが見ている外部ターゲットファイルに対するシンボル参照は、ターゲットファイルが最終的に実行可能ファイルにリンクされている場合、それらは正しく決定されなければならない.このシンボルの定義が見つからないと、リンクはシンボルが定義されていないエラーを報告し、これを強適用(Strong Reference)と呼ぶ.これに対応して、弱い参照を処理する際に、その記号に定義がある場合、リンクはその記号の参照を決定する弱い参照(Weak Reference)がある.シンボルが定義されていない場合、リンクは参照に対してエラーを報告しません.リンクが強い参照と弱い参照を処理するプロセスはほとんど同じですが、定義されていない弱い参照では、リンクはエラーとは思いません.一般に、未定義の弱い参照の場合、リンクはデフォルトで0、またはプログラムコードが認識できるように特別な値です.__led_setでは、GCC拡張キーワードを使用して、外部関数への参照を弱い参照として宣言することができます.たとえば、次のコードがあります.
    __attribute__ ((weakref)) void foo();
    int main()
    {
        foo();
    }
    __attribute__((weakref))はリンクエラーを報告しない実行可能ファイルにコンパイルできます.しかし、この実行可能ファイルを実行すると、実行エラーが発生します.GCC関数がmain関数を使用しようとすると、foo関数のアドレスが0であるため、不正なアドレスアクセスエラーが発生する.1つの改善例は、
    __attribute__ ((weakref)) void foo();
    int main()
    {
      if (foo) foo();
    }

    この弱い記号と弱い参照は、ライブラリで定義された弱い記号がユーザー定義の強い記号で上書きされ、プログラムがカスタムバージョンのライブラリ関数を使用できるようにするなど、ライブラリにとって非常に有用である.あるいは、プログラムはいくつかの拡張機能モジュールの参照を弱い参照として定義することができ、拡張モジュールをプログラムに接続すると、機能モジュールは正常に使用することができます.いくつかの機能モジュールを削除すると、プログラムも正常にリンクできますが、対応する機能が欠けているだけで、プログラムの機能がより簡単に切り取られ、組み合わせられます.foou-boot v2016.09のコードを専門にチェックしたが,弱いインデックスを用いて関数を定義する場合は見つからず,あまり一般的ではないことが分かった.

    4.その他


    弱符号(Weak Symbol)および弱参照については、linux v3.3-3.8ツールチェーンGNUGCC言語文法の拡張のみであり、C自体の言語特性ではない.マイクロソフトのCといえば、弱い記号や弱い参照特性はサポートされていません.