【问题标题】:GCC linking libc static and some other library dynamically, revisited?GCC 动态链接 libc static 和其他一些库,重新访问?
【发布时间】:2014-12-04 07:35:51
【问题描述】:

以下问题是相关的,但不回答我的问题:

Linking partially static and partially dynamic in GCC

Linking a dynamic library to a static library that links to other static libraries

GCC: static linking only some libraries

Static link of shared library function in gcc

我之前问了一个非常相似的问题,但是由于我提出的上一个问题在评论部分有些混乱并且没有完全回答(但我将其标记为已回答,因为这是一个很好的努力并且至少部分回答了它) 我会问一个新问题。问题具体是如何将 libc 链接为静态,同时动态链接其他一些库(例如 libm)。有人建议在第一个问题中无法做到这一点,这是真的吗?如果是这样,知道为什么不这样做会很有趣。

甚至有可能做到这一点吗?有人发表了评论(由于某种原因被删除,也许是不正确的?)这是可能的,但是必须存在动态链接版本的 libc,因为它将被动态库(例如动态库需要动态库(?))。

这对我来说很好,但对我来说如何告诉 GCC 这样做并不明显,即在 libc 中链接为静态和动态。我该怎么做(我做了几次尝试,一些在问题后面显示)?还是有其他方法可以做我想做的事?

我们首先看到,通过简单的运行 gcc test.c -lm,一切都是动态链接的,如下:

$ gcc test.c -lm
$ ldd a.out 
        linux-vdso.so.1 (0x00007fffb37d1000)
        libm.so.6 => /lib64/libm.so.6 (0x00007f3b0eeb6000)
        libc.so.6 => /lib64/libc.so.6 (0x00007f3b0eb10000)
        /lib64/ld-linux-x86-64.so.2 (0x00007f3b0f1b0000)

要仅将 libm 链接为静态,同时允许 libc 保持动态,我们可以这样做(正如 Z boson 在上述问题之一中指出的那样):

$ gcc test.c /usr/lib/gcc/x86_64-pc-linux-gnu/4.7.3/../../../../lib64/libm.a

$ ldd a.out 
        linux-vdso.so.1 (0x00007fff747ff000)
        libc.so.6 => /lib64/libc.so.6 (0x00007f09aaa0c000)
        /lib64/ld-linux-x86-64.so.2 (0x00007f09aadb2000)

但是,尝试相同的过程来链接 libc 静态和 libm 动态,似乎不起作用:

$ gcc test.c /usr/lib/gcc/x86_64-pc-linux-gnu/4.7.3/../../../../lib64/libc.a -lm
/usr/lib/gcc/x86_64-pc-linux-gnu/4.7.3/../../../../x86_64-pc-linux-gnu/bin/ld: dynamic STT_GNU_IFUNC symbol `strcmp' with pointer equality in `/usr/lib/gcc/x86_64-pc-linux-gnu/4.7.3/../../../../lib64/libc.a(strcmp.o)' can not be used when making an executable; recompile with -fPIE and relink with -pie
collect2: error: ld returned 1 exit status

这个错误信息是什么意思?

其他一些尝试(大部分也包含在我的第一个问题中):

$ gcc test.c /usr/lib64/libc.a
linux-gnu/4.7.3/../../../../x86_64-pc-linux-gnu/bin/ld: dynamic STT_GNU_IFUNC symbol `strcmp' with pointer equality in `/usr/lib64/libc.a(strcmp.o)' can not be used when making an executable; recompile with -fPIE and relink with -pie
urned 1 exit status
$ gcc test.c -Wl,-Bdynamic -lm -Wl,-Bstatic -lc
/usr/lib/gcc/x86_64-pc-linux-gnu/4.7.3/../../../../x86_64-pc-linux-gnu/bin/ld: cannot find -lgcc_s
/usr/lib/gcc/x86_64-pc-linux-gnu/4.7.3/../../../../x86_64-pc-linux-gnu/bin/ld: cannot find -lgcc_s
collect2: error: ld returned 1 exit status
$ gcc -Wl,-Bdynamic -lm -Wl,-Bstatic -lc test.c
/usr/lib/gcc/x86_64-pc-linux-gnu/4.7.3/../../../../x86_64-pc-linux-gnu/bin/ld: cannot find -lgcc_s
/usr/lib/gcc/x86_64-pc-linux-gnu/4.7.3/../../../../x86_64-pc-linux-gnu/bin/ld: cannot find -lgcc_s
collect2: error: ld returned 1 exit status
$ gcc -Wl,-Bstatic -lc -Wl,-Bdynamic -lm test.c
/usr/lib/gcc/x86_64-pc-linux-gnu/4.7.3/../../../../x86_64-pc-linux-gnu/bin/ld: dynamic STT_GNU_IFUNC symbol `strcmp' with pointer equality in `/usr/lib/gcc/x86_64-pc-linux-gnu/4.7.3/../../../../lib64/libc.a(strcmp.o)' can not be used when making an executable; recompile with -fPIE and relink with -pie
collect2: error: ld returned 1 exit status
$ gcc test.c /usr/lib/gcc/x86_64-pc-linux-gnu/4.7.3/../../../../lib64/libc.a /usr/lib/gcc/x86_64-pc-linux-gnu/4.7.3/../../../../lib64/libc.so -lm
/usr/lib/gcc/x86_64-pc-linux-gnu/4.7.3/../../../../x86_64-pc-linux-gnu/bin/ld: dynamic STT_GNU_IFUNC symbol `strcmp' with pointer equality in `/usr/lib/gcc/x86_64-pc-linux-gnu/4.7.3/../../../../lib64/libc.a(strcmp.o)' can not be used when making an executable; recompile with -fPIE and relink with -pie
collect2: error: ld returned 1 exit status
$ gcc test.c /usr/lib/gcc/x86_64-pc-linux-gnu/4.7.3/../../../../lib64/libc.so /usr/lib/gcc/x86_64-pc-linux-gnu/4.7.3/../../../../lib64/libc.a -lm

注意最后一个编译/链接成功。但是 libc 并没有静态链接,只是动态链接,所以又是一次失败的尝试。

测试程序简单如下:

$ cat test.c 
#include <stdio.h>
#include <math.h>

int main(int argc, char **argv)
{
        int i;
        int result;

        for(i = 0; i < 65535; i++) {
                result = sin(i);
        }

        return 0;
}

编辑:

我也尝试过 statifier 和 ermine,正如这个问题所建议的那样:

Static link of shared library function in gcc

都不行。

【问题讨论】:

  • 您要解决的问题是什么?我的意思是在更高的层次上,而不是“我想静态链接”。也许我们可以解决这个问题。
  • libm 链接到libc - 这可能是问题的一部分(请参阅ldd /usr/lib64/libm.so)。
  • @John Zwinck,这不是微不足道的,我试图证明在某些特殊情况下可以针对英特尔 MPX 进行基于 ROP 的漏洞利用,但它需要 libmpx(它只存在于动态链接形式,据我所知,没有可用的源代码)是动态链接的,而 libc 是静态链接的。但是,如果这是不可能的,那么知道为什么不这样做会很有趣,我会寻找一种不同的方法。
  • @el.pescado,是的,这很可能至少是问题的一部分,在其他问题中的评论消失之前,评论部分已简要指出。
  • 您需要静态链接的整个 libc,还是只需要几个特定函数?

标签: c gcc static


【解决方案1】:

基本上,您的第一种方法是正确的方法:

gcc test.c libc.a -lm

gcc 添加隐式库后(概念上)如下所示:

gcc crt1.o test.c libc.a -lm -lc -lgcc -lc

这意味着任何由crt1.otest.c 调用的libc 函数都将从libc.a 拉入并静态链接,而任何从libm 或@ 调用的函数单独 987654327@ 将被动态链接(但如果 libm 调用已拉入的内容,它将重用静态函数)。

链接器总是从最左边的文件/库开始,并向右工作;它永远不会回去。 .c.o 文件无条件链接,但 .a 文件和 -l 选项仅用于查找已引用但尚未定义的函数。因此,左边的库是没有意义的(-lc 必须出现两次,因为-lc 依赖于-lgcc,而-lgcc 依赖于-lc)。 链接顺序很重要!

不幸的是,你似乎被strcmp(或者更确切地说是包含strcmp的libc)中的一个错误所挫败:STT_GNU_IFUNC 是一个聪明的功能,它允许一个函数的多个版本被包括在内,并在运行时根据可用的硬件选择最佳的一个。我不确定,但看起来此功能仅在 PIE(位置独立可执行文件)或共享库构建中可用。

为什么会出现在静态 libc.a 中对我来说是个谜,但有一个简单的解决方法:实现自己的 strcmp(基本的、缓慢的实现只有几行 C),并将其链接到之前 libc.a.

gcc test.c mystrcmp.c libc.a -lm

或者,您可以从libc.a 中提取您真正想要的函数,并仅静态链接:

ar x libc.a
gcc test.c somefile.o -lm

ar.a 文件,tar.tar 文件,虽然命令用法略有不同,所以本例从.a 文件中提取.o 文件,然后明确链接它们。

【讨论】:

  • 太棒了!正是我正在寻找/希望的答案。实际上,我(在谷歌搜索时)遇到了有关这与错误有关的帖子,但我将其驳回,因为这些帖子已有几年历史了。
  • 顺便说一句(这个问题得到了很好的回答),不幸的是,当我使用 MPX 库执行此操作时,我得到“a.out:加载共享库时出错:RTLD_NEXT 用于未动态加载的代码” (libmpx)。不过我会更深入地研究它。
  • 我认为可以肯定地说这不是一个经过良好测试的安排。很可能有很多错误。甚至可能是一个表演者。
  • @AttributedTensorField,我很高兴看到你跟进这个问题。我试图回答您的问题的原因是我正在构建一个共享库,并且我不希望库本身存在依赖关系。当我在一台较旧的 Linux 机器上使用该库时,我发现它由于这些依赖关系而崩溃。很容易使这个共享库不依赖于 MSVC,但对于 GCC,这是一个主要的痛苦。在 linux 系统上,我为每个系统重新编译库(如果没有 root 访问权限,这并不总是那么容易)。人们会觉得 GCC 不希望你让事情变得一成不变。
  • @Zboson 这很准确;静态二进制文件存在问题,现代 Glibc 拒绝将某些功能设为静态(即 libc.a 版本将 dlopen 库)。 FWIW,可移植性问题的常见解决方案是使用旧系统中的库进行构建——它们通常在较新的系统上工作,但反之则不行。现在,RHEL5 是一个不错的选择。
【解决方案2】:

根据 ams 的回答,我做了以下操作

mystrcmp.c

int strcmp(const char *s1, const char *s2) {
}

编译

gcc -c test.c
gcc -c mystrcmp.c

设置文件

ln -s `gcc -print-file-name=crt1.o`
ln -s `gcc -print-file-name=crti.o`
ln -s `gcc -print-file-name=crtn.o`
ln -s `gcc -print-file-name=libgcc_eh.a`
ln -s `gcc -print-file-name=libc.a`
ln -s `gcc -print-file-name=libm.so`

链接

ld -m elf_x86_64 -o math crt1.o crti.o test.o mystrcmp.o libc.a libgcc_eh.a libc.a libm.so -dynamic-linker /lib64/ld-linux-x86-64.so .2 crtn.o

此链接并正确运行。但是,ldd 显示

linux-vdso.so.1 =>  (0x00007fff51911000)
libm.so.6 => /lib/x86_64-linux-gnu/libm.so.6 (0x00007f8182470000)
libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f81820a9000)
/lib64/ld-linux-x86-64.so.2 (0x00007f8182793000)

动态libm 似乎需要动态libc。其实这很容易展示

ldd libm.so 报告

linux-vdso.so.1 =>  (0x00007fff20dfe000)
libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007fcaf74fe000)
/lib64/ld-linux-x86-64.so.2 (0x00007fcaf7bed000)

因此,除非您设法在不依赖 libc 的情况下编译 libm,否则无法在不链接 libc.so 的情况下链接到 libm.so。

【讨论】:

  • 我想知道您是否真的在请求 libc.so 之前提取了所有必需的 .o 文件。
猜你喜欢
  • 2012-10-22
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-02-25
  • 1970-01-01
  • 2014-01-06
  • 2020-06-17
相关资源
最近更新 更多