web-dev-qa-db-ja.com

静的ライブラリでgccリンクの強いシンボルを作成して弱いシンボルを上書きするにはどうすればよいですか?

私の問題は次のように要約できます。

bar.c

#include <stdio.h>

void bar() {
    printf("bar\n");
}

main.c

#include <stdio.h>

void __attribute__((weak)) bar() {
    printf("foo\n");
}

int main() {
    bar();
    return 0;
}

Makefile

all:
    gcc -c bar.c
    ar -rc libbar.a bar.o
    gcc main.c -L. -lbar

出力

$ ./a.out
foo

したがって、bar.cが静的ライブラリlibbar.aのmain.cにリンクされているため、main.cの弱いシンボルbarがbar.cの強いシンボルによって上書きされることはありません。

Libbar.aの強いシンボルをmain.cの弱いシンボルに上書きするようにgccに指示するにはどうすればよいですか?

25
user1777342

一般的に言えば、弱い実装をmainに入れないと、リンカーは実行時についにそれを解決します。ただし、_main.c_で実装した場合、この静的をリンクするときにのみ、強い境界(_bar.c_)でオーバーライドできます。

読んでください http://www.bottomupcs.com/libraries_and_the_linker.html -このトピックに関する興味深いものがたくさん含まれています。

私は自分でテストを行いました:

bar.c

_#include <stdio.h>

void bar()
{
        puts("bar.c: i'm the strong bar()");
}
_

baz.c

_#include <stdio.h>

void __attribute__((weak)) bar() 
{
        puts("baz.c: i'm the weak bar()");
}
_

main.c

_#include <stdio.h>

#ifdef V2
        void __attribute__((weak)) bar()
        {
                puts("main: i'm the build in weak bar()");
        }
#else
        void __attribute__((weak)) bar();
#endif

int main()
{
    bar();
    return 0;
}
_

私のMakefile:

_all:
    gcc -c -o bar.o bar.c
    gcc -shared -fPIC -o libbar.so bar.o
    gcc -c -o baz.o baz.c
    gcc -shared -fPIC -o libbaz.so baz.o
    gcc -o main1 main.c -L. -lbar -lbaz
    gcc -o main2 main.c -L. -lbaz -lbar
    LD_LIBRARY_PATH=. ./main1                                   # => bar.c
    LD_LIBRARY_PATH=. ./main2                                   # => baz.c
    LD_LIBRARY_PATH=. LD_PRELOAD=libbaz.so ./main1              # => baz.c (!!)
    LD_LIBRARY_PATH=. LD_PRELOAD=libbaz.so ./main2              # => baz.c
    gcc -o main3 main.c bar.o baz.o
    gcc -o main4 main.c baz.o bar.o
    ./main3                                                     # => bar.c
    ./main4                                                     # => bar.c
    gcc -DV2 -o main5 main.c -L. -lbar -lbaz
    gcc -DV2 -o main6 main.c -L. -lbaz -lbar
    LD_LIBRARY_PATH=. ./main5                                   # => main's implementation
    LD_LIBRARY_PATH=. ./main6                                   # => main's implementation
    gcc -DV2 -o main7 main.c -L. -lbar -lbaz
    gcc -DV2 -o main8 main.c -L. -lbaz -lbar
    LD_LIBRARY_PATH=. LD_PRELOAD=libbaz.so ./main7              # => main's implementation
    LD_LIBRARY_PATH=. LD_PRELOAD=libbaz.so ./main8              # => main's implementation
    gcc -DV2 -o main9  main.c -L. -lbar -lbaz
    gcc -DV2 -o main10 main.c -L. -lbaz -lbar
    LD_LIBRARY_PATH=. LD_PRELOAD=libbar.so ./main9              # => main's implementation
    LD_LIBRARY_PATH=. LD_PRELOAD=libbar.so ./main10             # => main's implementation
    gcc -c bar.c
    gcc -c baz.c
    gcc -o main11 main.c bar.o baz.o
    gcc -o main12 main.c baz.o bar.o
    ./main11                                                    # => bar.c
    ./main12                                                    # => bar.c
    gcc -o main13 -DV2 main.c bar.o baz.o
    gcc -o main14 -DV2 main.c baz.o bar.o
    ./main13                                                    # => bar.c
    ./main14                                                    # => bar.c
_

Main1 && main2を見てください...弱い実装を_main.c_に入れずに、弱い実装をライブラリに、強い実装を別のライブラリに保持すると、オーバーライドできます。強いライブラリがbar()の強力な実装を定義している場合は弱いもの。

7
max.haredoom

私はmax.haredoomによって与えられた答えに戸惑っています(そしてそれが受け入れられたこと)。答えは共有ライブラリと動的リンクを扱っていますが、質問は明らかに静的ライブラリを使用した静的リンクの動作に関するものでした。これは誤解を招くと思います。

静的ライブラリをリンクするとき、ldnot弱い/強いシンボルを気にしますデフォルト:それは単に未定義のシンボルを最初に遭遇したシンボルに解決します(コマンドラインでの静的ライブラリの順序は重要です)。

ただし、このデフォルトの動作は、--whole-archiveオプションを使用して変更できます。 Makefileの最後のステップを次のように書き直す場合:

gcc main.c -L. -Wl,--whole-archive -lbar -Wl,--no-whole-archive

次に、次のように表示されます。

$ ./a.out
bar

簡単に言うと、--whole-archiveは、リンカーにすべてのシンボル(既に解決されているシンボルを含む)をスキャンするように強制します。 (私たちの場合のように)弱いシンボルによってすでに解決された強いシンボルがある場合、強いシンボルは弱いシンボルを無効にします。

静的ライブラリとそのリンクプロセスに関するすばらしい投稿も参照してください "静的リンクのライブラリ順序" by Eli Bendersky および this SO question

35