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

3

38

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.

Takamatsu answered 9/10, 2014 at 11:29 Comment(11)
What's the problem you're trying to solve? I mean at a higher level, not "I want to link statically." Perhaps we can solve that problem.Canny
libm links to libc - that may be part of problem (see ldd /usr/lib64/libm.so).Petua
@John Zwinck, It is not trivial, I'm trying to show that an ROP based exploit is possible against Intel MPX under some special circumstances, but it requires that libmpx (which only exists in a dynamically linked form, no source code available, as far as I know of) is linked in dynamically and libc be linked in statically. However if this is not possible it would be interesting to know why not in any case, and I would look for a different approach.Takamatsu
@el.pescado, yes that is most likely at least part of the problem, it was pointed out briefly in the comment section before the comment disappeared in the other question.Takamatsu
Do you require the whole of libc linked statically, or just a few specific functions?Insulator
BTW, any attempt to fix this by putting libc.a or -lc before test.c will not work because link-order matters.Insulator
@ams, ideally the whole libc, but some functions might do.Takamatsu
@AttributedTensorField, I updated my answer here again. libm.so depends on libc.so so it's impossible to link it dynamically without also linking in libc.so unless you get the source for libm and mange to build it without the dependency on libc.Brigandine
That's true, but shouldn't be a problem because libm.so, on any given machine, will only have dependencies in libc.so that work on that same machine.Insulator
@ams, I think for the OP this was just an exercise to try and link something static and something dynamic. The main point I have learned is that many other libraries depend on the dynamic libc as well so it's difficult to link without dynamic libc. This explains why I can't link libgomp and libpthread dynamic without dynamic libc.Brigandine
possible duplicate of Link glibc statically but some other library dinamically with GCCInfinity
I
39

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.

Insulator answered 9/10, 2014 at 14:30 Comment(16)
Fantastic! Exactly the answer I was looking/hoping for. I actually (while googling) came across posts about this being related to a bug, but I dismissed it as the posts were a couple years old.Takamatsu
As an aside (the question was very well answered), unfortunately I get "a.out: error while loading shared libraries: RTLD_NEXT used in code not dynamically loaded" when I do this with the MPX library (libmpx). However I will look more into it.Takamatsu
I think it's safe to say that this is not a well tested arrangement. There may well be many bugs. Maybe even a show-stopper.Insulator
@AttributedTensorField, I'm happy to see you followed up with this question. The reason I tried to answer your question is I was building a shared library and I wanted no dependencies in the library itself. I found when I used the library on an older Linux machine that it crashed because of these dependencies. It was easy to make this shared library with no dependencies with MSVC but with GCC it's been a major pain. On linux systems I recompile the library for each system (which is not always easy without root access). One gets the feeling that GCC does not want you to be making things static.Brigandine
@Zboson That's pretty accurate; static binaries have issues, and there are certain features that a modern Glibc refuses to make static (i.e. the libc.a version will dlopen a library). FWIW, the common solution to the portability problem is to build using libraries from an old system -- they usually work on newer systems, but not the other way around. RHEL5 is a good option, right now.Insulator
If I make a hello world (hello.c) and do gcc hello.c libc.a I get the STT_GNU_IFUNC error. However, if I do gcc -static hello.c it compiles fine and is static. How come this works?Brigandine
How can I have gcc tell me exactly what it does when I use -static? There must be some verbose mode to show everything it does.Brigandine
-v sets verbose mode, but it can be tricky to read as both commands and output are printed. -### does a "dry run" and just prints the command it would run (but be careful to quote the ### or your shell will think you have a comment).Insulator
I'm not sure why the STT_GNU_IFUNC stuff works with -static, but I'd imagine it would have to do with a different linker script being selected.Insulator
Let me restate this. How can I compile my hello.c statically without using -static. Both gcc hello.c libc.a and gcc -static-gcc hello.c libc.a don't work.Brigandine
I don't know how you can. The workaround I gave in my answer should side-step the problem.Insulator
This worked gcc -static-libgcc hello.c libc.a -Wl,-Bstatic -lc.Brigandine
This compiles gcc -static-libgcc test.c mystrcmp.c libc.a -lm -Wl,-Bstatic -lc. However, ldd shows that it still depends on on libc.Brigandine
Add -Wl,-t and the linker will list what it pulls from where. Then you can reference those symbols early (just take their address, or something), and hopefully that'll fix it?Insulator
I resubmitted my answer based on your answer. libm.so depeonds on libc.so so it's impossible to link libm.so without libc.so without recompiling the source code and removing the dependency on libc.soBrigandine
gcc: error: crt1.o: No such file or directory gcc: error: libc.a: No such file or directoryPosset
B
8

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.

Brigandine answered 10/10, 2014 at 9:26 Comment(1)
I wonder if you actually pulled in all the required .o files before libc.so was requested.Helios
P
-1

just use

gcc sample_uart.c -static  -static-libgcc -static-libstdc++

# lddtree a.out
a.out => ./a.out (interpreter => none)


#################### test ############################
ar x /usr/lib/arm-linux-gnueabihf/libc.a



-rw-r--r--  1 root root   792 Oct 28 00:38 wmemset.o
-rw-r--r--  1 root root  2456 Oct 28 00:38 wmemstream.o
-rw-r--r--  1 root root  1616 Oct 28 00:38 wordcopy.o
-rw-r--r--  1 root root 18544 Oct 28 00:38 wordexp.o
-rw-r--r--  1 root root  1384 Oct 28 00:38 wprintf_chk.o
-rw-r--r--  1 root root  1088 Oct 28 00:38 wprintf.o
-rw-r--r--  1 root root   884 Oct 28 00:38 write_nocancel.o
-rw-r--r--  1 root root  1368 Oct 28 00:38 write.o
-rw-r--r--  1 root root  1340 Oct 28 00:38 writev.o
-rw-r--r--  1 root root  1084 Oct 28 00:38 wscanf.o
-rw-r--r--  1 root root  3924 Oct 28 00:38 wstrops.o
-rw-r--r--  1 root root  2112 Oct 28 00:38 xcrypt.o
-rw-r--r--  1 root root  1504 Oct 28 00:38 xdr_array.o
-rw-r--r--  1 root root   836 Oct 28 00:38 xdr_float.o
-rw-r--r--  1 root root  2344 Oct 28 00:38 xdr_intXX_t.o
-rw-r--r--  1 root root  1740 Oct 28 00:38 xdr_mem.o
-rw-r--r--  1 root root  4696 Oct 28 00:38 xdr.o
-rw-r--r--  1 root root  3880 Oct 28 00:38 xdr_rec.o
-rw-r--r--  1 root root  1640 Oct 28 00:38 xdr_ref.o
-rw-r--r--  1 root root  1556 Oct 28 00:38 xdr_sizeof.o
-rw-r--r--  1 root root  2464 Oct 28 00:38 xdr_stdio.o
-rw-r--r--  1 root root  1688 Oct 28 00:38 xlocale.o
-rw-r--r--  1 root root   936 Oct 28 00:38 xmknodat.o
-rw-r--r--  1 root root   944 Oct 28 00:38 xmknod.o
-rw-r--r--  1 root root  1052 Oct 28 00:38 xpg_basename.o
-rw-r--r--  1 root root  1640 Oct 28 00:38 xpg-strerror.o
-rw-r--r--  1 root root   900 Oct 28 00:38 xstat64.o
-rw-r--r--  1 root root  1184 Oct 28 00:38 xstatconv.o
-rw-r--r--  1 root root  1196 Oct 28 00:38 xstat.o
root@hi3798mv100:~/sample/uart#
root@hi3798mv100:~/sample/uart#
root@hi3798mv100:~/sample/uart# gcc sample_uart.c -lm
root@hi3798mv100:~/sample/uart# gcc sample_uart.c *.o -lm
/usr/bin/ld: dso_handle.o:(.data.rel.ro.local+0x0): multiple definition of `__dso_handle'; /usr/lib/gcc/arm-linux-gnueabihf/9/crtbeginS.o:(.data.rel.local+0x0): first defined here
/usr/bin/ld: rcmd.o: in function `__validuser2_sa':
(.text+0x418): warning: Using 'getaddrinfo' in statically linked applications requires at runtime the shared libraries from the glibc version used for linking
collect2: error: ld returned 1 exit status


Posset answered 27/10, 2022 at 16:41 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.