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

Att*_*eld 16 c static gcc

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

在GCC中链接部分静态和部分动态

将动态库链接到链接到其他静态库的静态库

GCC:静态链接只有一些库

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)
Run Code Online (Sandbox Code Playgroud)

要仅将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)
Run Code Online (Sandbox Code Playgroud)

但是,尝试使用相同的过程来链接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
Run Code Online (Sandbox Code Playgroud)

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

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

$ 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
Run Code Online (Sandbox Code Playgroud)

请注意,最后一个已成功编译/链接.但是,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;
}
Run Code Online (Sandbox Code Playgroud)

编辑:

我也试过statifier和ermine,正如这个问题所示:

gcc中共享库函数的静态链接

两者都不起作用.

ams*_*ams 23

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

gcc test.c libc.a -lm
Run Code Online (Sandbox Code Playgroud)

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

gcc crt1.o test.c libc.a -lm -lc -lgcc -lc
Run Code Online (Sandbox Code Playgroud)

因此,这意味着,任何由两种称为libc函数crt1.otest.c将被从拉libc.a静态和链接,而称为任何功能仅仅libmlibgcc将动态链接(但如果调用的libm东西已经把它将会重用静态函数).

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

不幸的是,你似乎被可能存在的bug strcmp(或者更确切地说是包含的libc strcmp)所挫败:这STT_GNU_IFUNC是一个聪明的功能,它允许包含多个版本的函数,并且最优选的一个被选中在运行时,基于可用的硬件.我不确定,但看起来此功能仅在PIE(位置无关可执行文件)或共享库构建中可用.

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

gcc test.c mystrcmp.c libc.a -lm
Run Code Online (Sandbox Code Playgroud)

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

ar x libc.a
gcc test.c somefile.o -lm
Run Code Online (Sandbox Code Playgroud)

ar.a文件,tar.tar文件,但是命令用法变化了一下,所以这个例子中提取.o从文件中.a的文件,然后明确他们联系.


Z b*_*son 5

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

mystrcmp.c

int strcmp(const char *s1, const char *s2) {
}
Run Code Online (Sandbox Code Playgroud)

编译

gcc -c test.c
gcc -c mystrcmp.c
Run Code Online (Sandbox Code Playgroud)

设定档

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`
Run Code Online (Sandbox Code Playgroud)

链接

ld -m elf_x86_64 -o数学crt1.o crti.o test.o mystrcmp.o libc.a libgcc_eh.a libc.a libm.so-动态链接器/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)
Run Code Online (Sandbox Code Playgroud)

似乎动态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)
Run Code Online (Sandbox Code Playgroud)

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