Symbol relocation from one compiler version to another - gcc

I'm compiling a static library, let's call it static.a which is later linked by a shared library shared.so and by a final executable binary file (shared.so uses just a few functions from static.a maybe later this can be further splited). If I try to compile it suing gcc 7.4 I get this linker error:
/usr/bin/ld: ../../static.a(file.cpp.o): relocation R_X86_64_TPOFF32 against symbol `_ZGVZN6spdlog7details2os9thread_idEvE3tid' can not be used when making a shared object; recompile with -fPIC
I decided to try also gcc 9.1 and this error doesn't apear anymore.
should I always use -fpic when building a static library that will be used in a shared library? I know fpic adds some overhead.
how come a newer version of gcc can relocate the symbols of the static.a inside the shared library? Is this safe?
Thank you.

All code in shared library should be compiled with -fPIC so your static library should too. -fPIC does indeed introduce an overhead but to a large extent it can be mitigated with options like -fno-semantic-interposition and/or -fvisibility=hidden.
The error that you see is coming from the linker so it seems that the newer GCC does not use the problematic relocation. You can inspect the generated assembly to find out the difference in generated code.

Related

-fsanitize=address ,-static-libasan, could not see asan libray linked to final .so file

As mentioned using -fsanitize=address during compilation or .so file creation will automatically link libasan.so library right ?
I am facing issue :-
==13640==ASan runtime does not come first in initial library list; you should either link runtime to your application or manually preload it with LD_PRELOAD.
xrun: *E,ELBERR: Error during elaboration (status 1), exiting.
I found the same issue and fix for the same here :- https://github.com/google/sanitizers/issues/796
Firstly i try to use -fsanitize=address -static-libasan flags to my gcc compiler and linker to created .so files. The created library file 'libsynsv.so' itself don't show the 'asan' library as its dependency with ldd libsynsv.so output.
/folder/san/client/src/main/cvip/asan/Release/verilog/../lib/libviputil.so: undefined symbol: __asan_option_detect_stack_use_after_return.
Is there any issue with my GCC command? Why my library was not linked to asan though i ran with -fsanitize-address.
Is there any issue with my GCC command?
Yes: to properly work, address sanitizer must intercept every call to malloc. Thus you can not instrument a shared library with -fsanitize=address and load that library into main executable that is itself not instrumented.
The created library file 'libsynsv.so' itself don't show the 'asan' library as its dependency with ldd libsynsv.so output.
As #yugr said in comments, -static-libasan is ignored when linking a shared library.
Why my library was not linked to asan though i ran with -fsanitize-address.
Because linking asan runtime into a shared library is not sufficient to make address sanitizer work.

-fPIC error when linking static and dynamic libs with GCC

I have written a small code that I want to compile with a combination of static and dynamic libs. The code uses functions from hdf5 and exodusII (a specialist CAE lib) as well as math, and of course good-old stdio.
To make the binary highly portable, I wanted to link hdf5 and exodusII statically into the code, but leave math and libc as shared, so that the code is optimised on different platforms.
I cannot work out what the correct method is to compile something like this. I already have tried:
gcc -lm -lc -fPIC test1.c /usr/lib/libexodus.a /usr/lib/libhdf5.a -Wl,-pie
This gives the error:
/usr/lib64/crt1.o: relocation R_X86_64_32S against '__libc_csu_fini' can not be used when making a shared object; recompile with -fPIC
/usr/lib64/crt1.o: could not read symbols: Bad value
I have also tried:
gcc -c test1.c (WORKS!)
ld /usr/lib/libhdf5.a /usr/lib/libexodus.a -lm -lc test1.o
Which gives a warning of not being able to find an entry symbol _start followed by a whole lot of undefined reference errors to the libexodus functions in test1.c. (I have checked libexodus.a with nm, and the functions being reported do actually exist in the archive.
I would really appreciate a hand in this. I am not overly experienced in using static libs, but for this application, I think it is the best choice, so long as I can work out a reliable way of compiling and linking.
The error was in the linking order. I have now learned that the linking order works like babushka dolls where the library at the top of the dependency tree comes first, and the most general library comes last.
For future reference, in the end, the working build command was as follows:
gcc test1.c -lexodus -lnetcdf -lhdf5_hl -hdf5 -lcurl -ldl -lm
What I don't really understand is that when I had built the exodus library as a shared library, I only had to link against the shared library, and the dependency libraries (-lnetcdf -lhdf5_hl -lhdf5 -lcurl) were not specified; however, with the static compilation of the exact same library, I now need to link all the libraries explicitly.
If someone has an answer to this behaviour, it would be helpful for my understanding and very appreciated, but as I can continue coding with this current build method, it is not an urgent matter.

Why is the CMake solution for "R_X86_64_32 can not be used when making a shared object" specific to x86_64?

I just got this error when compiling some plugins for my new toy (on gcc/g++ on Linux):
relocation R_X86_64_32 can not be used when making a shared object; recompile with -fPIC
I basically understand why PIC is needed but, within the CMake system, the solution seems to be this:
IF (CMAKE_SYSTEM_PROCESSOR STREQUAL "x86_64")
SET_TARGET_PROPERTIES (${PLUGIN_BASE_LIB} PROPERTIES COMPILE_FLAGS "-fPIC")
ENDIF (CMAKE_SYSTEM_PROCESSOR STREQUAL "x86_64")
I don't understand why this solution is conditional.
The follow-up seems to suggest that -fPIC should be used basically everywhere except 32-bit Linux, which suggests that the above is not portable.
Should I always use -fPIC? Will there be any adverse effects?
${PLUGIN_BASE_LIB} needs to be statically linked to both the main executable, and statically the various shared libraries which are the plugins.
Ideally you want to build two versions of code: one for the main executable and one for the library. The first will need to be compiled with -fPIE (which is default in modern distros) and the second with -fPIC. As you point out this does not depend on target architecture.
You can compile only one version with -fPIC but then main executable will be suboptimal because -fPIC forces compiler to obey symbol interposition rules which significantly limits it's ability to optimize code e.g. inline and clone functions.

GCC link order changed?

I am trying to link a C++ module using GCC, essentially like this:
gcc -c hello.c
g++ -c world.cpp
gcc -ohello -lstdc++ hello.o world.o
Note that I use -lstdc++ to link the C++ module in, so that I can use gcc instead of g++. The problem is that I'm getting the error:
undefined reference to `operator new(unsigned long)'
(Assuming that world.cpp contains at least one call to new.)
This error is fixed if I put -lstdc++ at the end of the linker line, like this:
gcc -ohello hello.o world.o -lstdc++
I am aware that this question has been asked many times here, but I have a special requirement. I am not directly calling GCC. I am using a build system for a different programming language (Mercury) which is calling GCC on my behalf, and I can't easily modify the way it calls GCC (though I can specify additional libraries using the LDFLAGS environment variable). So I have two additional requirements:
I cannot use g++ to link (only gcc) -- that is why I am doing the -lstdc++ trick above rather than simply linking with g++).
I don't think that I can control the order of the linker commands -- Mercury will put the .o files on the command-line after any libraries.
I understand the basic reason why the order is important, but what is baffling me is why did this break now? I just updated to Ubuntu 11.10 / GCC 4.6.1. I have been successfully compiling this program for years using precisely the above technique (putting -lstdc++ first). Only now has this error come up. An unrelated program of mine links against OpenGL using -lgl and that too broke when I upgraded and I had to move -lgl to the end of the command-line. I'm probably going to discover that dozens of my programs no longer compile. Why did this change? Is there something wrong with my new system or is that the way it is now? Note that these are ordinary shared libraries, not statically linked.
Is there anything I can do to make GCC go back to the old way, where the order of libraries doesn't matter? Is there any other way I can convince GCC to link libstdc++ properly without moving it after the .o files on the command-line?
If Mercury puts object files after libraries, Mercury is broken. Libraries belong after object files - always. You may sometimes get away with the reverse order, but not reliably. (Static libraries must go after the object files that reference symbols in the static library. Sometimes, a linker will note the symbols defined by a shared library even when none of the symbols are used; sometimes, the linker will only note the shared library symbols if the shared library provides at least one symbol.)

How do you repackage the gnu gcc standard libraries stdc++, gcc, and gcc_eh?

Without modifying and recompiling the gnu gcc and stdc++ library builds, I need to be able to reproduce dynamic loading versions of those libraries with a different embedded soname.
I thought I would be clever and use the available static versions and repackage them with something like this:
ld -E -shared -static "-lstdc++" -lgcc -lgcc_eh -o librepackaged_standard.so
librepacked_standard.so is created, without warnings or errors, but ldd reports its not a dynamic library and readelf reports only these basic symbols:
Symbol table '.symtab' contains 4 entries:
Num: Value Size Type Bind Vis Ndx Name
0: 0000000000000000 0 NOTYPE LOCAL DEFAULT UND
1: 0000000000201000 0 NOTYPE GLOBAL DEFAULT ABS __bss_start
2: 0000000000201000 0 NOTYPE GLOBAL DEFAULT ABS _edata
3: 0000000000201000 0 NOTYPE GLOBAL DEFAULT ABS _end
I am unsure why ld isn't bringing in all of the symbols defined statically. I also don't know if there are any other special parameters I need to give it for this to work.
Another option is if there is a know cross platform way to simply change the soname embedded in the original elf libraries. I'm currently only concerned with elf formatted binaries. I am not interested in writting my own tool to change the .soname in existing binaries.
UPDATE:
The reason no symbols were getting compiled is because ld handles static binaries differently than .o files. By default it does not import any symbols from the .a file unless they are required by a another library on the link line. I fixed that by provided the --whole-archive option.
However that gives me another error, relocation R_X86_64_32S against_ZSt12_S_first_one' can not be used when making a shared object; recompile with -fPICand could not read symbols: Bad value` They are both from libstdc++.a in the bitset.o archive. So I can't just recompile the .a's into a dynamic library because the GNU GCC compile, by default, does not compile the object files used for the static libraries with the PIC option.
That leaves me with finding an elf tool or recompiling GNU GCC with modifications to its build.
As stated by one of the answers, licensing issues could be a concern with any of these approaches. My best answer is that we need to change to our requirements and find a different solution that doesn't involve changing or repackaging the GCC standard libraries in any fashion.
The reason no symbols were getting compiled into the shared library is because ld handles static binaries differently than .o files. By default it does not import any symbols from the .a file unless they are required by a another library on the link line. The answer to that particular issue is to use the --whole-archive option and linking the .a files directly mostly works.
However, for this to work the .o files included in the static archive need to have been compiled using the -fPIC option at compile time. However the object files used for the static libraries are not compiled with that option in the static libraries available.
So, the solution to changing the SONAME is to use an ELF binary utility or rebuilding GNU GCC modified to use different SONAMEs.
Since there are licensing concerns in this situation any of the solutions are not practical for the project because it is not open source and we do not want the requirement to redistribute a modified source version of GNU GCC for all our platforms.
The -static probably undid the -shared.
Classically, you'd extract the object files from the static libraries and then package those object files into the shared library - relying on the universal use of PIC (position-independent code) so that the objects in the static libraries can safely be converted into a shared library. You might be able to do without that extract step, but I doubt it.
You might want to think about whether you're meeting the licencing terms and conditions.

Resources