静态链接仅某些库


108

与GCC链接时,如何仅将某些特定的库静态链接到我的二进制文件?

gcc ... -static ...试图静态链接所有链接的库,但是我还没有其中一些的静态版本(例如:libX11)。


Answers:


112

gcc -lsome_dynamic_lib code.c some_static_lib.a


5
在目标文件之后链接库,尤其是静态库。在古代和现代版本的链接环境中(我不确定2010年11月以前的旧版本是否为现状),在code.c文件前列出静态库可确保其中的符号将被忽略,除非碰巧存在一个main()库中的目标文件的一个功能。
乔纳森·莱夫勒

44
请您详细说明这是如何工作的?仅代码答案对初学者没有帮助。
jb。

8
@jb默认情况下,gcc动态链接。使用-lsome_dynamic_lib时,它会按预期动态链接。但是,当为gcc明确提供静态库时,它将始终尝试将其静态链接。但是,关于符号解析的顺序,有些棘手的细节。我不太确定这是怎么回事。我了解到,如果有疑问,请尝试重新排列库标志的顺序:-)
bchurchill

4
如果您静态链接例如GPL库
HiB,

1
@HiB GPL将相同的方法应用于静态和动态链接
osvein

50

您也可以使用ld选项-Bdynamic

gcc <objectfiles> -static -lstatic1 -lstatic2 -Wl,-Bdynamic -ldynamic1 -ldynamic2

后面的所有库(包括由gcc自动链接的系统库)都将动态链接。


19
-Wl,-Bdynamic需要GNU ld,因此该解决方案不适用于gcc使用系统ld的系统(例如Mac OS X)。

33
gcc objectfiles -o program -Wl,-Bstatic -ls1 -ls2 -Wl,-Bdynamic -ld1 -ld2

您还可以使用:-static-libgcc -static-libstdc++gcc库的标志

请记住,如果libs1.solibs1.a都存在,则链接器将选择libs1.so之前-Wl,-Bstatic还是之后-Wl,-Bdynamic-L/libs1-library-location/打电话前不要忘记通过-ls1


1
至少,此解决方案适用于针对libgomp的静态链接!
杰罗姆

这对我来说效果很好,而-static在命令中的某处使用失败(我假设它尝试静态链接的对象比仅我想要的库更多)。
nh2

4
注意 的顺序-Wl,-Bstatic-Wl,-Bdynamic是很重要的。
帕维尔·弗拉索夫

27

ld(不适用于gcc)的联机帮助页中,参考以下--static选项:

您可以在命令行上多次使用此选项:它会影响库搜索其后的-l选项。

一种解决方案是将动态依赖--static项放在命令行中的选项之前。

另一种可能性是不使用--static,而是提供静态对象文件的完整文件名/路径(即,不使用-l选项)以静态链接特定库。例:

# echo "int main() {}" > test.cpp
# c++ test.cpp /usr/lib/libX11.a
# ldd a.out
linux-vdso.so.1 =>  (0x00007fff385cc000)
libstdc++.so.6 => /usr/lib/libstdc++.so.6 (0x00007f9a5b233000)
libm.so.6 => /lib/libm.so.6 (0x00007f9a5afb0000)
libgcc_s.so.1 => /lib/libgcc_s.so.1 (0x00007f9a5ad99000)
libc.so.6 => /lib/libc.so.6 (0x00007f9a5aa46000)
/lib64/ld-linux-x86-64.so.2 (0x00007f9a5b53f000)

正如您在示例中看到的那样,libX11它不是静态链接的,因此不在动态链接库的列表中。

注意:.so即使使用完整的文件名/路径指定文件,文件也始终是动态链接的。


libX11.a和输出的关系是ldd a.out什么?
拉菲·哈查杜安

1
知道了 ldd输出所需的共享库,而libX11没有出现在该列表中。
拉菲·哈查杜安

这还不清楚。您说“这个选项”和“那个选项”。有什么选择?
章鱼,

19

据我了解,问题如下。您有几个库,一些是静态的,一些是动态的,有些是静态和动态的。 gcc的默认行为是链接“大多是动态的”。也就是说,gcc在可能的情况下链接到动态库,否则会退回到静态库。当您使用-static选项来gcc时,行为是仅链接静态库,如果找不到静态库,即使存在适当的动态库,也会出错退出。

另一种选择,我有好几次想GCC有,就是我所说的-mostly静态的和基本上是相反-dynamic(默认值)。 -如果是静态的,则大多数静态文件都倾向于链接到静态库,但会回落到动态库。

该选项不存在,但是可以使用以下算法进行仿真:

  1. 构造不带-static的链接命令行。

  2. 遍历动态链接选项。

  3. 累积库路径,即变量<lib_path>中形式为-L <lib_dir>的那些选项

  4. 对于每个动态链接选项,即形式为-l <lib_name>的那些链接,请运行命令gcc <lib_path> -print-file-name = lib <lib_name> .a并捕获输出。

  5. 如果该命令输出的不是您传递的内容,它将是静态库的完整路径。将动态库选项替换为静态库的完整路径。

冲洗并重复操作,直到处理完整个链接命令行为止。脚本也可以选择从静态链接中排除的库名称列表。

以下bash脚本似乎可以解决问题:

#!/bin/bash

if [ $# -eq 0 ]; then
    echo "Usage: $0 [--exclude <lib_name>]. . . <link_command>"
fi

exclude=()
lib_path=()

while [ $# -ne 0 ]; do
    case "$1" in
        -L*)
            if [ "$1" == -L ]; then
                shift
                LPATH="-L$1"
            else
                LPATH="$1"
            fi

            lib_path+=("$LPATH")
            echo -n "\"$LPATH\" "
            ;;

        -l*)
            NAME="$(echo $1 | sed 's/-l\(.*\)/\1/')"

            if echo "${exclude[@]}" | grep " $NAME " >/dev/null; then
                echo -n "$1 "
            else
                LIB="$(gcc $lib_path -print-file-name=lib"$NAME".a)"
                if [ "$LIB" == lib"$NAME".a ]; then
                    echo -n "$1 "
                else
                    echo -n "\"$LIB\" "
                fi
            fi
            ;;

        --exclude)
            shift
            exclude+=(" $1 ")
            ;;

        *) echo -n "$1 "
    esac

    shift
done

echo

例如:

mostlyStatic gcc -o test test.c -ldl -lpthread

在我的系统上返回:

gcc -o test test.c "/usr/lib/gcc/x86_64-linux-gnu/4.7/../../../x86_64-linux-gnu/libdl.a" "/usr/lib/gcc/x86_64-linux-gnu/4.7/../../../x86_64-linux-gnu/libpthread.a"

或排除:

mostlyStatic --exclude dl gcc -o test test.c -ldl -lpthread

然后我得到:

gcc -o test test.c -ldl "/usr/lib/gcc/x86_64-linux-gnu/4.7/../../../x86_64-linux-gnu/libpthread.a"

7

还有-l:libstatic1.a哪些可以用来链接静态库(感谢gcc的-l选项的(负升结肠)变种https://stackoverflow.com/a/20728782)。是否有文件记录?不在gcc的官方文档中(这也不完全适用于共享库):https : //gcc.gnu.org/onlinedocs/gcc/Link-Options.html

-llibrary
-l library 

链接时搜索名为library的库。(使用库作为单独参数的第二个替代方法仅是为了符合POSIX,并且不建议这样做。)...使用-l选项和指定文件名之间的唯一区别是-l将库用'lib'和' '.a'并搜索几个目录。

binutils ld文档对其进行了描述。该-lname选项将进行搜索,libname.so然后libname.a添加lib前缀和.so(如果当前启用)或.a后缀。但是-l:nameoption仅会精确搜索指定的名称:https : //sourceware.org/binutils/docs/ld/Options.html

-l namespec
--library=namespec

将指定的归档文件或目标文件添加namespec到要链接的文件列表中。此选项可以使用多次。如果 namespec为格式:filename,则ld将在库路径中搜索名为的文件filename,否则将在库路径中搜索名为的文件libnamespec.a

在支持共享库的系统上,ld可能还会搜索以外的文件libnamespec.a。具体来说,在ELF和SunOS系统上,ld将在目录中libnamespec.so搜索一个名为的库,然后再搜索一个名为的库 libnamespec.a。(按照惯例,.so扩展名表示共享库。)请注意,此行为不适用于:filename,它始终指定一个名为的文件filename

链接器将在命令行上指定的位置仅搜索一次存档。如果档案库定义的符号在命令行中档案之前出现的某个对象中未定义,则链接器将包含档案中的相应文件。但是,稍后出现在命令行中的对象中未定义的符号将不会导致链接程序再次搜索档案。

请参阅-(选项,以强制链接程序多次搜索档案。

您可以在命令行上多次列出同一档案。

这种类型的档案搜索是Unix链接器的标准配置。但是,如果在AIX上使用ld,请注意它与AIX链接器的行为不同。

-l:namespec自binutils 2.18版本(2007)起记录了该变体:https : //sourceware.org/binutils/docs-2.18/ld/Options.html


该选项似乎在其他所有失败的地方都可以使用。我们偶然发现了需要静态链接libjsoncpp.a的情况,因为我们的构建机器会生成与libjsocpp.so.0链接的二进制文件,而目标OS仅提供libjsoncpp.so.1。在我们消除这种差异之前,这是在我们的案例中唯一能产生适当结果的解决方案。
Tomasz W

4

一些加载程序(链接器)提供用于打开和关闭动态加载的开关。如果GCC在这样的系统(Solaris以及其他系统)上运行,则可以使用相关选项。

如果知道要静态链接的库,则只需在链接行中按完整路径指定静态库文件。


6
即使已接受此答案,也不能完全解决问题。正如@peoro解释的那样,他要解决的问题是他没有所有库的静态版本,这意味着他希望尽可能多地静态链接库。看我的答案。
jcoffland

2

一个行内链接的动态和静态库,你必须把静态库动态库文件和目标文件,如下所示:

gcc -lssl main.o -lFooLib -o main

否则,它将无法正常工作。我花了一些时间才弄清楚。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.