31

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

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.cの強いシンボルによって上書きされることはありません。

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

4

2 に答える 2

53

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

スタティック ライブラリをリンクする場合、はデフォルトで弱い/強いシンボルを気にldしませ。未定義のシンボルを最初に検出されたシンボルに解決するだけです (したがって、コマンド ラインでのスタティック ライブラリの順序は重要です)。

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

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

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

$ ./a.out
bar

簡単に言えば、--whole-archiveリンカにすべてのシンボル (解決済みのものを含む) を強制的にスキャンさせます。弱いシンボルによって既に解決された強いシンボルがある場合 (この場合のように)、強いシンボルは弱いシンボルを無効にします。

また、Eli Bendersky による静的ライブラリとそのリンク プロセスに関するすばらしい投稿「静的リンクでのライブラリの順序」と、この SO の質問も参照してください。

于 2016-05-12T15:49:58.003 に答える
9

一般的に言えば、弱い実装を に入れなければ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;
}

私のメイクファイル:

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ライブラリに保持し、強い実装を別のライブラリに保持すると、弱い実装を上書きすることができます。 lib は の強力な実装を定義しますbar()

于 2012-10-27T13:06:41.800 に答える