GCC动态链接libc static和其他一些库,重新访问?

时间:2014-10-09 11:29:23

标签: c gcc static

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

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版本,因为它将需要动态库(例如动态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 static和libm dynamic似乎不起作用:

$ 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

都没有效果。

2 个答案:

答案 0 :(得分:22)

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

gcc test.c libc.a -lm

在gcc添加隐式库之后,它(在概念上)看起来像这样:

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

这意味着crt1.otest.c调用的任何libc函数都将从libc.a引入并静态链接,而任何名为的函数libmlibgcc将动态链接(但如果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个文件,虽然命令用法略有不同,所以此示例提取.o来自.a文件的文件,然后显式链接它们。

答案 1 :(得分:4)

根据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)

因此,除非你设法编译libm而不依赖于libc,否则无法链接libc.so链接到libm.so是不可能的。