Compile static shared library with GCC - c

I want to create shared library (.so) that is statically linked, so it will not have dependencies on other libraries and can work on any version of linux.
My example.h
#ifndef example_h__
#define example_h__
#include <stdint.h>
extern void example_1(void);
#endif // example_h__
My example.c:
#include <stdio.h>
void example_1(void) {
puts("Hello, I'm a shared library");
}
My Makefile:
all :
gcc -c -Wall -Werror -fpic example.c
gcc -shared -Wl,-Bstatic -static-libstdc++ -static-libgcc -L/opt/musl/lib -I/opt/musl/include -o libexample.so example.o
readelf -d libexample.so
I already compiled musl lib from source with -fPIC option and still getting this error:
# make
gcc -c -Wall -Werror -fpic example.c
gcc -shared -Wl,-Bstatic -static-libstdc++ -static-libgcc -L/opt/musl/lib -I/opt/musl/include -o libexample.so example.o
/usr/lib/gcc/x86_64-alpine-linux-musl/6.2.1/../../../../x86_64-alpine-linux-musl/bin/ld: /opt/musl/lib/libc.a(__stdout_write.lo): relocation R_X86_64_PC32 against protected symbol `__stdio_write' can not be used when making a shared object
/usr/lib/gcc/x86_64-alpine-linux-musl/6.2.1/../../../../x86_64-alpine-linux-musl/bin/ld: final link failed: Bad value
collect2: error: ld returned 1 exit status
How to solve it?

Related

gcc, how to force the final executable link a unused shared library?

I have an executable shared_main , a shared library libbar.so and a dynamic load shared library libfoo.so (load in shared_main via dlopen).
shared_main doesn't use any symbols from libbar.so but libfoo.so does.
So gcc -g -Wall -o shared_main shared_main.c libbar.so -ldl doesn't link libbar.so to shared_main.
Checked via ldd shared_main.
How to let gcc force shared_main link libbar.so?
P.S. I know I can link libfoo.so with libbar.so. But I want to try if I can force shared_main to link libbar.so here.
shared_main.c
#include <stdio.h>
#include <dlfcn.h>
#include <stdlib.h>
int main(){
void* libHandle = dlopen("./libfoo.so", RTLD_LAZY);
if(libHandle == NULL){
printf("dlopen:%s", dlerror());
exit(1);
}
int(*xyz)(int);
xyz = (int(*)(int)) dlsym(libHandle, "xyz");
if(xyz == NULL){
printf("dlsym:%s", dlerror());
exit(1);
}
int b = xyz(3);
printf("xyz(3): %d\n", b);
}
foo.c (libfoo.so)
void func_in_bar();
int xyz(int b){
func_in_bar();
return b + 10;
}
bar.c (libbar.so)
//mimic python share library runtime
#include <stdio.h>
void func_in_bar(){
printf("I am a Function in bar\n");
}
void another_func_in_bar(){
printf("I am another function in bar\n");
}
makefile
shared_main:
gcc -g -Wall -o shared_main shared_main.c libbar.so -ldl
shared:
gcc -g -Wall -fPIC -shared -o libfoo.so foo.c
gcc -g -Wall -fPIC -shared -o libbar.so bar.c
You have an XY-problem, where X is: libfoo has unresolved symbols, but the linker doesn't warn about it
So use the -z defs option linkage-time, and when you get the linker error about the unresolved symbol add -lfoo to the linkage command.
That's still not enough, you will have to use a -L and a -Wl,-rpath option too. Here is a complete Makefile:
# Makefile
# LIBDIR should be the final place of the shared libraries
# such as /usr/local/lib or ~/libexec/myproject
LIBDIR := ${PWD}
TARGETS := shared_main libbar.so libfoo.so
all: ${TARGETS}
clean:
rm -f ${TARGETS} 2>/dev/null || true
shared_main: shared_main.c
gcc -g -Wall -o shared_main shared_main.c -ldl
libbar.so: bar.c
gcc -g -Wall -fPIC -shared -o libbar.so bar.c
libfoo.so: foo.c libbar.so
gcc -g -Wall -fPIC -shared -z defs -o libfoo.so foo.c \
-L${LIBDIR} -Wl,-rpath,${LIBDIR} -lbar
Edit: nonetheless, here is a hackish solution for the original question: use option -Wl,--no-as-needed
shared_main:
gcc -g -Wall -o shared_main shared_main.c \
-Wl,--no-as-needed -Wl,-rpath,${PWD} libbar.so -ldl
Everything works just fine for me, with unmodified files from OP.
$ make shared
gcc -g -Wall -fPIC -shared -o libfoo.so foo.c
gcc -g -Wall -fPIC -shared -o libbar.so bar.c
$ make shared_main
gcc -g -Wall -o shared_main shared_main.c libbar.so -ldl
$ LD_LIBRARY_PATH=. ldd shared_main
linux-vdso.so.1 (0x00007ffccb5f2000)
libbar.so => ./libbar.so (0x00007f78f6ce0000)
libdl.so.2 => /lib64/libdl.so.2 (0x00007f78f6cd1000)
libc.so.6 => /lib64/libc.so.6 (0x00007f78f6b06000)
/lib64/ld-linux-x86-64.so.2 (0x00007f78f6ce7000)
$ LD_LIBRARY_PATH=. ./shared_main
I am a Function in bar
xyz(3): 13
I only needed to help the library loader out a bit using LD_LIBRARY_PATH.
$ gcc --version
gcc (GCC) 11.3.1 20220421 (Red Hat 11.3.1-2)
Copyright (C) 2021 Free Software Foundation, Inc.
This is free software; see the source for copying conditions. There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
$ ld --version
GNU ld version 2.37-17.fc35
Copyright (C) 2021 Free Software Foundation, Inc.
This program is free software; you may redistribute it under the terms of
the GNU General Public License version 3 or (at your option) a later version.
This program has absolutely no warranty.
The answer is -Wl,--no-as-needed.
For my example, the full command is:
gcc -g -Wall -o shared_main shared_main.c -Wl,--no-as-needed libbar.so -ldl
From https://man7.org/linux/man-pages/man1/ld.1.html :
--as-needed
--no-as-needed
This option affects ELF DT_NEEDED tags for dynamic libraries
mentioned on the command line after the --as-needed option.
Normally the linker will add a DT_NEEDED tag for each dynamic
library mentioned on the command line, regardless of whether
the library is actually needed or not. --as-needed causes a
DT_NEEDED tag to only be emitted for a library that at that
point in the link satisfies a non-weak undefined symbol
reference from a regular object file or, if the library is
not found in the DT_NEEDED lists of other needed libraries, a
non-weak undefined symbol reference from another needed
dynamic library. Object files or libraries appearing on the
command line after the library in question do not affect
whether the library is seen as needed. This is similar to
the rules for extraction of object files from archives.
--no-as-needed restores the default behaviour.

Linkin two shared library with same symbol name but different symbol type

I want to link two shared libraries that both two libraries have same symbol name but different symbol type.
For example, there are liba.so and libb.so libraries that consist of two files a.c/b.c and b_wrapper.c/a_wrapper.c
a.c
__thread int var;
a_wrapper.cpp
extern __thread int var;
liba.so
$ gcc -fPIC -c a.c -o a.o
$ g++ -fPIC -c a_wrapper.cpp -o a_wrapper.o -std=c++11
$ g++ -fPIC -shared -o liba.so a.o a_wrapper.o -std=c++11
b.c
int var;
b_wrapper.c
extern int var;
libb.so
$ gcc -fPIC -c b.c -o b.o
$ g++ -fPIC -c b_wrapper.cpp -o b_wrapper.o -std=c++11
$ g++ -fPIC -shared -o libb.so b.o b_wrapper.o -std=c++11
then link liba.so and libb.so to top.c file, I get some error message
$ g++ top.cpp -ldl -o exe ./liba.so ./libb.so -std=c++11
/usr/bin/ld: var: TLS definition in ./liba.so section .tdata mismatches non-
TLS definition in ./libb.so section .bss
./libb.so: error adding symbols: Bad value
collect2: error: ld returned 1 exit status
I think the problem is because var in a.c is defined in thread local storage bss(tbss), but var in b.c is defined in normal bss.
I have read this post Linking two shared libraries with some of the same symbols
and try -Bsymbolic, --version-script and -fvisibility=hidden, but all these methods can not achieve my goal.
-Bsymbolic : I used it when creating liba.so and libb.so, but when I linked liba.so and libb.so I still got same error message described as above
--version-script and -fvisibility=hidden : I can't let var variable become local to one file(a.c) since other files(a_wrapper.cpp) will extern and use that variable
Is there any way that I can link two shared libraries that has some same symbol name but different symbol type?

using C and cuda create shared library got error at link stage

I was really struggled with this error when I try to build a shared library. My code utilize the Lapacke library and also CUDA. when I compile them, there are no errors(I compile them as)
gcc -m64 -Wall -fPIC -c xxx.c -o xxx.o $(INC)
where INC includes all directories
INC=-I. -I${JAVA_HOME}/include/ -I${JAVA_HOME}/include/linux/ I$/home/sniu/lapack-3.5.0/lapacke/include/ -I${CUDA_INSTALL_PATH}/include/ -I/home/sniu/CBLAS/include/
for cuda part, I wrote it as:
nvcc -m64 -arch=sm_20 -Xcompiler -fPIC $(INC) -c xxx.cu -o xxx.o
but I got the error message at the link stage:
gcc -m64 -D_REENTRANT -Wall -fPIC -g -shared -o libjniWrapper.so jniWrapper.o cholesky_inv.o wls_acc.o utils.o -L/home/sniu/lapack-3.5.0 -L/opt/cuda-toolkit/5.5.22/lib64 -lm -llapacke -llapack -lblas -lgfortran -lrt -lcudart -lcublas -ldl
/usr/bin/ld: /home/sniu/lapack-3.5.0/liblapacke.a(lapacke_dpotrf.o): relocation R_X86_64_32 against `.rodata.str1.1' can not be used when making a shared object; recompile with -fPIC
/home/sniu/lapack-3.5.0/liblapacke.a: could not read symbols: Bad value
collect2: error: ld returned 1 exit status
I am very sure libraries are there, I really confused why I got this error.
Any suggestions are appreciated, Thank you so much!

Building a shared library using gcc on Linux and MinGW on Windows

I'm having trouble with generating a build setup that allows shared libraries to be built in both Linux and Windows using gcc and MinGW, respectively. In Linux, a shared library doesn't have to resolve all dependencies at compile time; whereas, this appears to the case in Windows. Here is the problem setup:
$ cat foo.h
#ifndef FOO_H
#define FOO_H
void printme();
#endif
$ cat foo.c
#include "foo.h"
#include <stdio.h>
void printme() {
printf("Hello World!\n");
}
$ cat bar.h
#ifndef BAR_H
#define BAR_H
void printme2();
#endif
$ cat bar.c
#include "bar.h"
#include "foo.h"
void printme2() {
printme();
printme();
}
$ cat main.c
#include "bar.h"
int main(){
printme2();
}
$ cat Makefile
.c.o:
gcc -fPIC -c $<
all: foo.o bar.o main.o
gcc -shared foo.o -o libfoo.so
gcc -shared bar.o -o libbar.so
gcc main.o -Wl,-rpath=. -L . -lbar -lfoo -o main
Now, in Linux, this compiles and runs just fine:
$ make
gcc -fPIC -c foo.c
gcc -fPIC -c bar.c
gcc -fPIC -c main.c
gcc -shared foo.o -o libfoo.so
gcc -shared bar.o -o libbar.so
gcc main.o -Wl,-rpath=. -L . -lbar -lfoo -o main
$ ./main
Hello World!
Hello World!
In Windows, we need to change so to dll, which is minor and fine:
$ cat Makefile
.c.o:
gcc -fPIC -c $<
all: foo.o bar.o main.o
gcc -shared foo.o -o libfoo.dll
gcc -shared bar.o -o libbar.dll
gcc main.o -Wl,-rpath=. -L . -lbar -lfoo -o main
However, when we try to build, we get the following error:
$ make
gcc -fPIC -c foo.c
foo.c:1:0: warning: -fPIC ignored for target (all code is position independent) [enabled by default]
gcc -fPIC -c bar.c
bar.c:1:0: warning: -fPIC ignored for target (all code is position independent) [enabled by default]
gcc -fPIC -c main.c
main.c:1:0: warning: -fPIC ignored for target (all code is position independent) [enabled by default]
gcc -shared foo.o -o libfoo.dll
gcc -shared bar.o -o libbar.dll
bar.o:bar.c:(.text+0x7): undefined reference to `printme'
bar.o:bar.c:(.text+0xc): undefined reference to `printme'
collect2.exe: error: ld returned 1 exit status
make: *** [all] Error 1
Now, we can fix the error by simply including the objects from foo.o into libbar.dll:
$ cat Makefile
.c.o:
gcc -fPIC -c $<
all: foo.o bar.o main.o
gcc -shared foo.o -o libfoo.dll
gcc -shared bar.o foo.o -o libbar.dll
gcc main.o -Wl,-rpath=. -L . -lbar -lfoo -o main
$ make
gcc -fPIC -c foo.c
foo.c:1:0: warning: -fPIC ignored for target (all code is position independent) [enabled by default]
gcc -fPIC -c bar.c
bar.c:1:0: warning: -fPIC ignored for target (all code is position independent) [enabled by default]
gcc -fPIC -c main.c
main.c:1:0: warning: -fPIC ignored for target (all code is position independent) [enabled by default]
gcc -shared foo.o -o libfoo.dll
gcc -shared bar.o foo.o -o libbar.dll
gcc main.o -Wl,-rpath=. -L . -lbar -lfoo -o main
$ ./main
Hello World!
Hello World!
However, I don't like this approach since libbar.dll now contains symbols for both foo and bar. In Linux, it only contains symbols for bar. This separation is important for situations where a library depends on some standard numerical library like BLAS. I'd like to be able to deploy the shared library and have it depend on the optimized version of the numerical library on the user's machine and not my own.
In any case, what's the proper procedure to create a shared library where not all of the symbols are present at compile time?
In case it matters, I compiled these examples with gcc 4.6.3 on Linux and mingw-get-inst-20120426.exe with gcc 4.7.2 on Windows.
On Windows, you need to create an import library for the DLL. An import library looks like a static library, in that it defines all of the needed symbols, but it doesn't have the actual function implementations, it just has stubs. The import library will resolve the "undefined reference" errors while avoiding static linking.
To create an import library with MinGW, follow the instructions here. The key is that when building the DLL, you must pass the option -Wl,--out-implib,libexample_dll.a to the linker to generate the import library libexample_dll.a.
Then, when you compile your main executable, you use the -lexample_dll option (along with -L.) to link against the import library. So with your code, I think this should work:
all: foo.o bar.o main.o
gcc -shared foo.o -o libfoo.dll -Wl,--out-implib,libfoo.a
gcc -shared bar.o foo.o -o libbar.dll -Wl,--out-implib,libbar.a
gcc main.o -Wl,-rpath=. -L. -lbar -lfoo -o main
Also, note that on Windows, the calling convention for exported functions in DLL is almost always __stdcall, not the default __cdecl, so if you want your DLLs to be usable by other software, I'd recommend making them __cdecl. But that's not strictly requires, as long as both the code in the DLL and the header files agree on what the calling convention is.

why cant link 64bit static libgcc on ubuntu

I have problem link libgcc into a static linked .so
it only happens when linking 64bit module with -m64
Ubuntu 64bit 12.10 gcc 4.7
also failed on Ubuntu 64bit 12.04 gcc 4.6
32bit no problem
$gcc -fPIC -c -o hello.o hello.c -m32
$gcc -shared -m32 hello.o -o libhello.so -static-libgcc -Wl,-Bstatic -lc
$ ldd libhello.so
statically linked
64bit failed
$ make
gcc -fPIC -c -o hello.o hello.c
gcc -shared -m64 hello.o -o libhello.so -static-libgcc -Wl,-Bstatic -lc
/usr/bin/ld: /usr/lib/gcc/x86_64-linux-gnu/4.7/../../../x86_64-linux-gnu/libc.a(iofclose.o): relocation R_X86_64_32 against `__gcc_personality_v0' can not be used when making a shared object; recompile with -fPIC
/usr/lib/gcc/x86_64-linux-gnu/4.7/../../../x86_64-linux-gnu/libc.a: could not read symbols: Bad value
collect2: error: ld returned 1 exit status
make: *** [libhello.so] Error 1
hello.c
#include <stdio.h>
int f(){
FILE *out = fopen("/tmp/x.log", "wb");
fclose(out);
return 1;
}
Makefile
all: libhello.so
libhello.so: hello.o
gcc -shared -m64 hello.o -o libhello.so -static-libgcc -Wl,-Bstatic -lc
hello.o: hello.c
gcc -fPIC -c -o hello.o hello.c
clean:
rm -f hello.o libhello.so
The answer is basically "you can't do that." You're trying to link non-PIC code into a shared library, which is simply impossible on the x86_64 (amd64) architecture. You would need a static but PIC version of libgcc, and I suspect that would be only the start of your problems.
One of the reasons why libgcc is normally shared is that a given running executable has to have one and only one copy of some of the key data structures that libgcc maintains. Static linking makes sense for a final executable, since that one and only one copy will be the one statically linked into the executable, but the whole point of a dynamic object is to be loaded into another executable (which in turn will have its own copy of libgcc, either shared or static).

Resources