Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

GCC linking libc static and some other library dynamically, revisited?

Tags:

c

static

gcc

The following questions are relevant but do not answer my question:

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

I asked a very similar question earlier, but since the previous question started by me got somewhat cluttered in the comment section and not fully answered (but I flagged it as answered since it was a good effort and did at least partially answer it) I will ask a new question. The question is specifically how to link libc as static, while linking some other library (e.g. libm) dynamically. This was suggested that cannot be done in the first question, is that true? If so it would be very interesting to know why not.

Is it even possible to do this? Someone made a comment (which was removed for some reason, maybe it was incorrect?) that it is possible, but there must then also exist a dynamically linked version of libc, since it will be required by the dynamic library (e.g. dynamic libm will require dynamic libc (?)).

This is fine for me, but it is not obvious to me how to tell GCC to do this, i.e. link in libc as both static and dynamic. How do I do this (I made a couple attempts, some are shown later in the question)? Or is there some other way to do what I want?

We first see that by simply running gcc test.c -lm, everything is linked in dynamically, as follows:

$ 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) 

To link only libm as static, while allowing libc to remain dynamic, we can do (as Z boson pointed out in one of the aforementioned questions):

$ 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) 

However, attempting the same procedure to link libc static and libm dynamic, does not seem to work:

$ 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 

What does this error message mean?

Some other attempts (most were also included in my first question):

$ 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 

Note that the last one compiled/linked successfully. However libc has not been linked in statically, only dynamically, so it is another failed attempt.

The test program is simply the following:

$ 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; } 

Edit:

I've also tried statifier and ermine, as suggested in this question:

Static link of shared library function in gcc

Neither works.

like image 947
AttributedTensorField Avatar asked Oct 09 '14 11:10

AttributedTensorField


People also ask

Is libc statically linked or dynamically linked?

However libc has not been linked in statically, only dynamically, so it is another failed attempt.

Can you statically link libc?

Statically linking libc is it's own minefield. It can and is done but even if you statically link everything else you should almost always dynamically link against your platform's libc. Statically linking libc is harder than dynamically linking it, but certainly easier than rewriting it.

Is libc always linked?

libc is always searched automatically.

What libc contains?

The C standard library or libc is the standard library for the C programming language, as specified in the ISO C standard. Starting from the original ANSI C standard, it was developed at the same time as the C library POSIX specification, which is a superset of it.


2 Answers

Basically, your first approach is the correct way to do this:

gcc test.c libc.a -lm 

After gcc adds the implicit libraries it'll look (conceptually) like this:

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

So that means that any libc functions called by either crt1.o or test.c will be pulled in from libc.a and linked statically, whereas any functions called solely from libm or libgcc will be linked dynamically (but it will reuse the static functions if libm calls something already pulled in).

The linker always starts at the left-most file/library, and works rightwards; it never goes back. .c and .o files are linked in unconditionally, but .a files and -l options are only used to find functions that are already referenced but not yet defined. Therefore, a library at the left is pointless (and -lc must appear twice because -lc depends on -lgcc, and -lgcc depends on -lc). Link order is important!

Unfortunately, you appear to have been foiled by what might be a bug in strcmp (or rather in the libc that contains strcmp): the STT_GNU_IFUNC thing is a clever feature that allows multiple versions of a function to be included, and the most optimal one to be selected at runtime, based on what hardware is available. I'm not sure, but it looks like this feature is only available in a PIE (Position Independent Executable) or shared library build.

Why that would be in a static libc.a is a mystery to me, but there's an easy workaround: implement your own strcmp (a basic, slow implementation is only a few lines of C), and link it in before libc.a.

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

Alternatively, you can extract the functions from libc.a that you really want, and link only those in statically:

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

ar is to .a files, as tar is to .tar files, although the command usage varies a bit, so this example extracts the .o files from the .a file, and then links them explicitly.

like image 98
ams Avatar answered Oct 22 '22 15:10

ams


Based on the answer by ams I did the follow

mystrcmp.c

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

Compile

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

Setup files

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` 

Link

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

This links and runs correctly. However, ldd shows

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) 

It appears that dynamic libm requires dynamic libc. Actually, that's easy to show

ldd libm.so reports

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) 

So it's impossible to link to libm.so without linking libc.so as well unless you manage to compile libm without the dependency on libc.

like image 33
Z boson Avatar answered Oct 22 '22 16:10

Z boson