【问题标题】:static linking only some libraries仅静态链接一些库
【发布时间】:2011-05-08 13:11:28
【问题描述】:

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

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

【问题讨论】:

标签: gcc linker static-libraries


【解决方案1】:

gcc -lsome_dynamic_lib code.c some_static_lib.a

【讨论】:

  • 在目标文件之后链接库——尤其是静态库。在链接环境的古代和现代版本中(我不确定截至 2010 年 11 月的适度旧版本的现状),在 code.c 文件之前列出静态库可保证其中的符号将被忽略,除非存在恰好是库对象文件之一中的 main() 函数。
  • 能否请您详细说明这是如何工作的?仅代码答案对初学者没有帮助。
  • @jb 默认情况下,gcc 链接是动态的。当您使用 -lsome_dynamic_lib 时,它会按预期动态链接。但是,当 gcc 被显式地提供一个静态库时,它总是会尝试静态链接它。然而,关于符号被解析的顺序有一些棘手的细节。我不太确定它是如何工作的。我了解到,如有疑问,请尝试重新排列库标志的顺序 :-)
  • 如果您静态链接例如 GPL library,则会出现 lincense 问题
  • @HiB GPL 以同样的方式应用于静态和动态链接
【解决方案2】:

您也可以使用ld 选项-Bdynamic

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

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

【讨论】:

  • -Wl,-Bdynamic 需要 GNU ld,因此此解决方案不适用于 gcc 使用系统 ld 的系统(例如 Mac OS X)。
【解决方案3】:
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 之后。不要忘记在调用-ls1之前传递-L/libs1-library-location/

【讨论】:

  • 至少,此解决方案适用于针对 libgomp 的静态链接!
  • 这对我来说效果很好,而在命令中的某处使用-static 会失败(我假设它试图静态链接更多的东西,而不仅仅是我想要的库)。
  • 注意。 -Wl,-Bstatic-Wl,-Bdynamic 的顺序很重要。
【解决方案4】:

来自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 的输出有什么关系?
  • 啊,我明白了。 ldd 输出所需的共享库,而 libX11 未出现在该列表中。
  • 这不清楚。你说“这个选项”和“那个选项”。什么选择?
【解决方案5】:

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

另一个选项,我曾多次希望 gcc 有,我称之为 -mostly-static,本质上与 -dynamic(默认)。 -mostly-static(如果存在)会更喜欢链接静态库,但会退回到动态库。

此选项不存在,但可以使用以下算法进行模拟:

  1. 在不包含 -static 的情况下构造链接命令行。

  2. 遍历动态链接选项。

  3. 累积库路径,即变量

  4. 中的-L形式的选项
  5. 对于每个动态链接选项,即 -l 形式的选项,运行命令 gcc -print-file-name=lib.a 并捕获输出。

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

冲洗并重复,直到处理完整个链接命令行。可选地,该脚本还可以获取要从静态链接中排除的库名称列表。

以下 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"

【讨论】:

    【解决方案6】:

    gcc 中还有 -l 选项的 -l:libstatic1.a(减号冒号)变体,可用于链接静态库(感谢 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:name 选项只会精确搜索指定的名称: 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 链接器的行为。

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

    【讨论】:

    • 这个选项似乎在其他一切都失败的情况下工作。我们偶然发现了一个需要静态链接 libjsoncpp.a 的案例,因为我们的构建机器会生成链接到 libjsocpp.so.0 的二进制文件,而目标操作系统只提供 libjsoncpp.so.1。在我们能够消除这种差异之前,这是在我们的案例中产生正确结果的唯一解决方案。
    【解决方案7】:

    一些加载器(链接器)提供用于打开和关闭动态加载的开关。如果 GCC 在这样的系统上运行(Solaris - 可能还有其他系统),那么您可以使用相关选项。

    如果您知道要静态链接哪些库,您可以简单地在链接行中指定静态库文件 - 通过完整路径。

    【讨论】:

    • 即使这个答案被接受,它也不能完全解决问题。正如@peoro 解释的那样,他试图解决的问题是他没有所有库的静态版本,这意味着他希望静态链接尽可能多的库。看我的回答。
    【解决方案8】:

    要将动态库和静态库链接在一行内,必须将静态库放在动态库和目标文件之后,如下所示:

    gcc -lssl main.o -lFooLib -o main

    否则,它将无法正常工作。我确实需要一些时间才能弄清楚。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2012-10-22
      • 2012-07-29
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多