How to statically link libc while keeping dynamic loading? - c

I want to build a statically linked application that could use dynamically loadable plugins.
The problem is that I can't get rid of the libc.so dependency.
I'm using musl libc and compiling like
/opt/cross/x86_64-linux-musl/bin/x86_64-linux-musl-gcc -Wl,-E -fPIC -I... -static-libgcc -Wl,-Bstatic -ldl -lc -lgcc source.c -o output_bin foo.a bar.a -Wl,-Bdynamic
readelf -d shows that executable depends on libc.so, so executable doesn't work on other machines without musl libc.
Is it possible to include libc symbols into elf executable and link all external plugins to elf itself, without external .so dependencies? How to achieve this?

Maybe you should try compiling it without the stdlib using -nostdlib parameter.

Related

Static link SDL2 on Linux

I am trying to statically link SDL2 on Linux, with the goal of creating a binary that doesn't require any libraries to be required on the system. I understand this will require statically linking more than just SDL2, such as SDL2's dependencies and things like libc, so help on that front would be appreciated as well. But right now I can't get SDL2 to statically link at all.
I am using GCC, and SDL 2.0.16 that I compiled and installed myself with the default configuration, which includes static libraries. I already had SDL2 installed through my package manager, so my installation went to /usr/local/include/SDL2 and /usr/local/lib.
Running /usr/local/bin/sdl2-config --cflags --static-libs gives:
-I/usr/local/include/SDL2 -D_REENTRANT
-L/usr/local/lib -lSDL2 -lm -ldl -lpthread -lrt
No amount of messing around with these flags and -static have been able to produce a binary that doesn't dynamically link to SDL2. How can I do it?
Other flags I am using for other reasons are -std=c89 -Wall -Wno-unknown-pragmas -DNDEBUG -Os -g0 -s
Being able to cross compile and do this would be great, but I understand that's a lot more complex. I've been trying to compile with zig cc as that would allow cross-compilation later, but couldn't get it to work. I was able to get a build that didn't dynamically link to SDL2, but it would segfault.
In response to comments:
Running pkg-config --static --cflags --libs /usr/local/lib/pkgconfig/sdl2.pc gives:
-I/usr/local/include/SDL2 -D_REENTRANT -L/usr/local/lib -Wl,-rpath,/usr/local/lib -Wl,--enable-new-dtags -lSDL2 -lm -ldl -lpthread -lrt
Using that creates a dynamically-linked executable, so not what I want. If I add -static I get the error:
/usr/bin/ld: /usr/local/lib/libSDL2.a(SDL_dynapi.o): in function `get_sdlapi_entry':
/home/makeworld/Software/SDL2-2.0.16/src/dynapi/SDL_dynapi.c:237: warning: Using 'dlopen' in statically linked applications requires at runtime the shared libraries from the glibc version used for linking
Thanks to #HolyBlackCat and #keltar, I have been able to statically link SDL2.
I basically just used the output of pkg-config as provided in my question, but with the addition of -Wl,-Bstatic before -lSDL2 and -Wl,-Bdynamic after it. This statically links SDL2, but dynamically links all the other libraries.
The final command is:
gcc your_code.c -o your_executable -I/usr/local/include/SDL2 -D_REENTRANT \
-L/usr/local/lib -Wl,-rpath,/usr/local/lib -Wl,--enable-new-dtags \
-Wl,-Bstatic -lSDL2 -Wl,-Bdynamic -lm -ldl -lpthread -lrt
If your build of SDL2 is installed elsewhere, just change the /usr/local/include/SDL2 and /usr/local/lib part of the command to point to where the header files and .a files are respectively.
If I figure out how to cross-compile this setup, I will update this answer.

How can I "register" my library libfoo.so to link it with `-lfoo`?

How can I "register" my library foo.c, compiled to libfoo.so, to link it with -lfoo? Is it by appending its path to LD_LIBRARY_PATH? Is it by running sudo ldconfig?
For curiosity, who do I "register" it with? That is, which application "needs to know" what -lfoo means in order for gcc bar.c -lfoo to work? Is it the bash environment? Is it gcc? Is it the kernel?
Is any of this different for static libraries (eg. libfoo.a)?
If your library is not in a "standard" library directory such as (eg. /usr/local/lib or /usr/lib) then you'll need to tell the compiler it's location -L when you link it -l:
$ gcc -L/home/username/foo -Wall -o test main.c -lfoo
| |
|__ location of libfoo.so or libfoo.a |__ link libfoo.so or libfoo.a
GCC assumes that all libraries start with ‘lib’ and end with .so or .a
(.so is for shared object or shared libraries, and .a is for archive,
or statically linked libraries).
-L is the location to search for libraries linked to your executable
-l links the library to the executable (-lfoo can be translated as "link library libfoo.so")
When using shared libraries sometimes simply linking them is not enough (eg. if the library is not installed in a standard location such as the example shown above). This is where using LD_LIBRARY_PATH, rpath or ldconfig comes into play. Static library paths won't need to be set like shared libraries since they are compiled with the executable.
LD_LIBRARY_PATH
$ export LD_LIBRARY_PATH=/home/username/foo:$LD_LIBRARY_PATH
Exporting this variable informs the run-time linker to look in the specified location for the libfoo.so library that's associated with the executable. LD_LIBRARY_PATH is more or less a temporary, or convenient way of setting a library's location.
rpath
$ gcc -L/home/username/foo -Wl,-rpath=/home/username/foo -Wall -o test main.c -lfoo
rpath works similarly to LD_LIBRARY_PATH (as libraries can reside almost anywhere in userland), however, rpath is linked to the executable at compile time, and sets a "fixed" library location.
ldconfig
Ultimately what might be the best solution is to use ldconfig, which creates the necessary links and cache to the most recent shared libraries found in the standard library locations and ones you can specify. It does require elevated permissions (eg. sudo) to set paths up this way, and should be used with a certain degree of caution.
$ sudo cp /home/username/foo/libfoo.so /usr/local/lib
$ sudo chmod 0755 /usr/local/lib/libfoo.so
This copies the library into /usr/local/lib with permissions set to be readable by everyone.
$ sudo ldconfig
$ ldconfig -p | grep foo
libfoo.so (libc6) => /usr/local/lib/libfoo.so
Then we update the loader cache, and verify the path is set correctly.
$ gcc -Wall -o test main.c -lfoo
By using ldconfig, the -L option, LD_LIBRARY_PATH or rpath now shouldn't be needed.
Additional Information
↳ Shared Libraries With GCC on Linux

Linking libcurl with static libc

I am trying to compile a C program while linking dynamically libcurl and statically libc.
My Makefile looks like:
SRC=myprogram.c
LDFLAGS+= -static libc.a -static-libgcc -Wl,-static -lc
LDFLAGS+= -linfluxdb -lcurl -lm -ljson-c
I need to link libc dynamically because libc version is not the same between centos7 and centos6, so I will include the chosen one in the binary.
I could have linking everything statically but it's not working for libcurl; I am having errors in the linker resolving several libcurl functions.
I tried to add "dynamic" flag for libcurl
LDFLAGS+= -Wl,-Bdynamic -lcurl
But I still have a dynamic reference to libc. Ldd outputs:
libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007ff3811b2000)
Through this question (Static linking libcurl using c) I knew that libcurl depends on libc. Is it a way to force the usage of static libc everywhere ?

Something about "-Wl,-rpath=."

I have build a shared library libC.so, and it depends on libA.so and libB.so.
And then I build test.c which using libC.so via the command:
gcc test.c -o test -fPIC -I./ -L./ -lC
It will output error, could not find some symbols which are in libA.so and libB.so.
I know, tt can be build successufully when I add the flags "-lA -lB".
However I could not understood, why can it build successfully via the following command:
gcc test.c -o test -fPIC -I./ -L./ -lC -Wl,-rpath=.
I think your libA.so and libB.so are present in the current directory.
Your providing the linking option -Wl -rpath as your current directory.
So your linker takes the libraries in the current directory and link the symbols.
It is not giving any errors because of the linker options you have specified.
The next doubt you might get is,
I have only specified the directories to search but not specified libraries.
How is the linker is taking libA.so and libB.so libraries also?
But the linker is intelligent and it thinks that you have forgot to include these interdependent libraries (You might not know about interdependency)but you have specified the path to search for all the dependent libraries. So it picks the interdependent libraries.
I think it only works for Interdependency only. All direct libraries should be specified with -l option i think.

Static linking libcurl using c

I am using Libcurl in my application with C and GNU compiler on a linux machine.
Application also uses OpenSSL libraries and some other libraries. I am trying to statically link the libraries, except for lcurl linking with other libraries works fine.
I am trying to compile as below.
gcc -static -I. -o test test.c -lz -lssl -lcrypto -lcurl
Am I doing anything wrong?
Is there a way to force static linking of some librairies (libc for exemple) ?
Libcurl is itself linked against a ton of other libraries, most of which aren't included in your compile command line. For instance, mine (on Debian Squeeze) links against:
libc
libcom_err
libcrypto
libdl
libgcrypt
libgnutls
libgpg-error
libgssapi_krb5
libidn
libk5crypto
libkeyutils
libkrb5
libkrb5support
liblber-2.4
libldap_r-2.4
libpthread
libresolv
librt
libsasl2
libssh2
libssl
libtasn1
libz
(You can get a similar list for yourself by running ldd on the library on Linux, or otool -L on Darwin.)

Resources