web-dev-qa-db-ja.com

弱いリンクをGCCで機能させるにはどうすればよいですか?

シンボルを弱リンクするようにGCCに指示する方法は3つあるようです。

  • __attribute__((weak_import))
  • __attribute__((weak))
  • #pragma weak symbol_name

これらのどれも私のために働きません:

#pragma weak asdf
extern void asdf(void) __attribute__((weak_import, weak));
...
{
    if(asdf != NULL) asdf();
}

私はいつもこのようなリンクエラーを受け取ります:

未定義のシンボル:
 "_ asdf"、参照元:
 _asdf $ non_lazy_ptr in ccFA05kN.o 
 ld:シンボルが見つかりません
 collect2:ldが1を返しました終了ステータス

OS X10.5.5でGCC4.0.1を使用しています。私は何が間違っているのですか?

23
Ben

私はこれを調べただけで、他の何人かが私の調査結果に興味があるのではないかと思いました。

Weak_importとの弱いリンクは、実際にはダイナミックライブラリでのみうまく機能します。静的リンクを使用して(上記のように-undefined dynamic_lookupを指定することで)動作させることができますが、これはそれほどホットなアイデアではありません。これは、実行時まで未定義のシンボルが検出されないことを意味します。これは、個人的には本番コードでは避けたいものです。

これは、それを機能させる方法を示すMac OSXターミナルセッションです。

ここにf.cがあります

int f(int n)
{
    return n * 7;
}

これがwhatnof.cです

#include <stdio.h>
#include <stdlib.h>

extern int f (int) __attribute__((weak_import));

int main() {
    if(f == NULL)
        printf("what, no f?\n");
    else
        printf("f(8) is %d\n", f(8));
    exit(0);
}

F.cからダイナミックライブラリを作成します。

$ cc -dynamiclib -o f.dylib f.c

動的ライブラリーをコンパイルしてリンクし、動的ライブラリーをリストします。

$ cc -o whatnof whatnof.c f.dylib
$ otool -L whatnof
whatnof:
       f.dylib (compatibility version 0.0.0, current version 0.0.0)
       /usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 125.2.0)

Whatnofを実行して、何が起こるかを確認します。

$ whatnof
f(8) is 56

次に、f.dylibを空のライブラリー(シンボルなし)に置き換えます。

$ mv f.dylib f.dylib.real
$ touch null.c
$ cc -dynamiclib -o f.dylib null.c

同じwhatnofを実行して、何が起こるかを確認します。

$ whatnof
what, no f?

Weak_importの基本的な考え方(または「ユースケース」)は、動的(共有)ライブラリのセットに対してリンクできるが、同じライブラリの以前のバージョンに対して同じコードを実行できるようにすることです。関数をNULLと照合して、コードが現在実行されている特定のダイナミックライブラリでサポートされているかどうかを確認できます。これは、Xcodeでサポートされている基本的な開発モデルの一部のようです。この例がお役に立てば幸いです。これは、Xcodeデザインのこの部分について私の心を安心させるのに役立ちました。

29

追加 -Wl,-flat_namespace,-undefined,dynamic_lookup最終リンクを実行するために使用するgccコンパイラ行へ。

7
Kyle

MACOSX_DEPLOYMENT_TARGET変数を10.2以降に設定する必要があります。弱いリンクについては、 Appleのドキュメント およびそれらの technote を参照してください。

4

Gcc docマニュアルから:

弱い

弱い属性により、宣言はグローバルではなく弱いシンボルとして発行されます。これは主に、ユーザーコードでオーバーライドできるライブラリ関数を定義するのに役立ちますが、関数以外の宣言でも使用できます。 ELFターゲット、およびGNUアセンブラとリンカーを使用する場合のa.outターゲットでも、弱いシンボルがサポートされます。

これは、リンク時にエラーが発生することなく、オブジェクトが弱いシンボル(別のオブジェクト/ライブラリで定義されている)を上書きすることが正当化されることを意味します。ライブラリがweak記号でリンクされているかどうかが不明確です。シンボルを定義しておらず、ライブラリが適切にリンクされていないようです。

1
Nicola Bonelli

実行可能な最小限のLinuxの例

main.c

#include <stdio.h>

int my_weak_var __attribute__((weak)) = 1;

int main(void) {
    printf("%d\n", my_weak_var);
}

notmain.c

int my_weak_var = 2;

両方のオブジェクトでコンパイルして実行します。

gcc -c -std=c99 -Wall -Wextra -pedantic -o main.o main.c
gcc -c -std=c99 -Wall -Wextra -pedantic -o notmain.o notmain.c
gcc -std=c99 -Wall -Wextra -pedantic -o main.out main.o notmain.o
./main.out

出力:

2

notmain.oなしでコンパイルして実行します:

gcc -std=c99 -Wall -Wextra -pedantic -o main.out main.o
./main.out

出力:

1

GitHubアップストリーム

したがって、notmain.oで指定された場合、弱くないシンボルが期待どおりに優先されることがわかります。

ELFオブジェクトファイル シンボルを次のように分析できます。

nm main.o notmain.o

それは与える:

main.o:
                 U _GLOBAL_OFFSET_TABLE_
0000000000000000 T main
0000000000000000 V my_weak_var
                 U printf

notmain.o:
0000000000000000 D my_weak_var

その後:

man nm

含む:

シンボルタイプ。少なくとも次のタイプが使用されます。他のオブジェクトも、オブジェクトファイル形式によって異なります。小文字の場合、記号は通常ローカルです。大文字の場合、シンボルはグローバル(外部)です。ただし、特別なグローバル記号( "u"、 "v"、および "w")に対して表示される小文字の記号がいくつかあります。

「D」
"d"シンボルは初期化されたデータセクションにあります。

「V」
"v"シンボルは弱いオブジェクトです。弱い定義済みシンボルが通常定義済みシンボルとリンクされている場合、通常定義済みシンボルがエラーなしで使用されます。弱い未定義のシンボルがリンクされていて、シンボルが定義されていない場合、弱いシンボルの値はエラーなしでゼロになります。一部のシステムでは、大文字はデフォルト値が指定されていることを示します。

ただし、.a静的ライブラリを処理する場合は、次の説明に従って-Wl,--whole-archiveを使用する必要がある場合があります。 静的ライブラリでgccリンクの強いシンボルを作成して弱いシンボルを上書きする方法

弱いシンボルも未定義のままにすることができます。これは、Binutilsでは「プラットフォーム固有の動作」につながります。以下を参照してください。 未解決の弱い関数のGCC動作

Ubuntu 18.10、GCC 8.2.0でテスト済み。