Linking error: selective static linking of libm.a in GCC - c

I want to selectively link libm.a statically, all the other libraries (libc.so included) dinamically. But if I use the math functions from math.h, it almost always fails to link correctly. Why?
And why does it work sometimes? (For example if I only use sqrt, fabs or, strangely, tanh, it seems to link correctly)
myscript.sh:
#!/bin/bash
for i in sqrt tanh sin tan
do
echo "-----$i----------"
sed "s/ciao/$i/" prova.c >provat.c
gcc provat.c -fno-builtin -l:libm.a
[[ $? -eq 0 ]] && { echo -n "$i(2.0)="; ./a.out; echo " OK!"; }
echo
done
prova.c:
#include <stdio.h>
#include <math.h>
int main()
{
printf("%f", ciao(2.0));
return 0;
}
If I run myscript.sh, I can see that sqrt and tanh give no problems. sin and tan, instead, fail to link:
$./myscript.sh
-----sqrt----------
sqrt(2.0)=1.414214 OK!
-----tanh----------
tanh(2.0)=0.964028 OK!
-----sin----------
/usr/lib/x86_64-linux-gnu/libm-2.27.a(s_sin.o): In function `__sin_ifunc':
(.text+0x4d42): undefined reference to `_dl_x86_cpu_features'
/usr/lib/x86_64-linux-gnu/libm-2.27.a(s_sin.o): In function `__cos_ifunc':
(.text+0x4da2): undefined reference to `_dl_x86_cpu_features'
collect2: error: ld returned 1 exit status
-----tan----------
/usr/lib/x86_64-linux-gnu/libm-2.27.a(s_tan.o): In function `__tan_ifunc':
(.text+0x5782): undefined reference to `_dl_x86_cpu_features'
collect2: error: ld returned 1 exit status
I don't understand these error messages. Can somebody explain what happens?
Why can't I link libm.a statically (and the rest dinamically)? And why does it work sometimes?
Note: I use the -fno-builtin flag to GCC, so that GCC doesn't use any of its builtin functions. So the problem is not there.

It's not very clear (to me) why the restriction (libm.a + libc.so) is necessary, so it smells like an XY Problem.
According to [RedHat.Bugzilla]: Bug 1433347 - glibc: Selective static linking of libm.a fails due to unresolved _dl_x86_cpu_features symbol (pointed out by #KamilCuk):
This is not supported.
You would be mixing a static libm.a with future libc.so.6 and ld.so and that breaks the interdependencies between the core libraries which form "the implemetnation of the C runtime."
Either the entire implementation of the runtime is statically linked or none of it is statically linked. You can't choose to link parts of it statically and not others because each part depends on the other to form a complete implementation. The math library is not a thin you can link against statically, it happens we have a libm.a, but that's an implementation detail.
Please use '-static' for the entire application.
it seems like it's an unsupported configuration. That makes sense, but it's also a little bit confusing: even if libc and libm are 2 separate files on disk (for each configuration: static, shared), they are part of the same library (glibc), so:
It's not OK to use half of a library statically built, and the other half as a shared object (some things that come into my mind are: gcc's -fPIC flag, and also library's initialization)
Of course, most of the times, a library consists of a single file, so the above bullet wouldn't apply (there's where the confusion comes from)
From the beginning, I suspected that it's some code that (detects and) uses (if present) some CPU capabilities (most likely to improve speed or accuracy), that:
Is only used by some of the (trigonometric) functions (like sin, cos, but not tanh) - I'm just guessing here: based on how the function values are computed (e.g. Taylor series)
It only happens when libc (libm) are in sync
I used this simple program.
main.c:
#include <stdio.h>
#include <math.h>
#if !defined(FUNC)
# define FUNC sqrt
#endif
int main() {
double val = 3.141592;
printf("func(%.06lf): %.06lf\n", val, FUNC(val));
return 0;
}
Below it's a series of steps that I followed when looking into the problem:
Environment:
[cfati#cfati-ubtu16x64-0:~/Work/Dev/StackOverflow/q056415996]> ~/sopr.sh
*** Set shorter prompt to better fit when pasted in StackOverflow (or other) pages ***
[prompt]> uname -a
Linux cfati-ubtu16x64-0 4.15.0-51-generic #55~16.04.1-Ubuntu SMP Thu May 16 09:24:37 UTC 2019 x86_64 x86_64 x86_64 GNU/Linux
[prompt]> gcc --version
gcc (Ubuntu 5.4.0-6ubuntu1~16.04.11) 5.4.0 20160609
Copyright (C) 2015 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.
[prompt]> ldd --version
ldd (Ubuntu GLIBC 2.23-0ubuntu11) 2.23
Copyright (C) 2016 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.
Written by Roland McGrath and Ulrich Drepper.
[prompt]> ls
main.c
The case when the 2 library parts (libc and libm) are in sync:
[prompt]> gcc -fPIC main.c -DFUNC=sin -o sin_static.out -static -lm
[prompt]> ll sin_static.out
-rwxrwxr-x 1 cfati cfati 1007744 Jun 13 20:08 sin_static.out
[prompt]> ldd sin_static.out
not a dynamic executable
[prompt]> ./sin_static.out
func(3.141592): 0.000001
[prompt]>
[prompt]> gcc -fPIC main.c -DFUNC=sin -o sin_mso.out -l:libm.so
[prompt]> ll sin_mso.out
-rwxrwxr-x 1 cfati cfati 8656 Jun 13 20:09 sin_mso.out
[prompt]> ldd sin_mso.out
linux-vdso.so.1 => (0x00007ffc80ddd000)
libm.so.6 => /lib/x86_64-linux-gnu/libm.so.6 (0x00007f999636b000)
libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f9995fa1000)
/lib64/ld-linux-x86-64.so.2 (0x00007f9996674000)
[prompt]> ./sin_mso.out
func(3.141592): 0.000001
Everything works fine in both cases.
Switch to libm.a (for sin and tanh):
[prompt]> gcc -fPIC main.c -DFUNC=sin -o sin_ma.out -l:libm.a
/usr/lib/gcc/x86_64-linux-gnu/5/../../../x86_64-linux-gnu/libm.a(s_sin.o): In function `__cos':
(.text+0x3542): undefined reference to `_dl_x86_cpu_features'
/usr/lib/gcc/x86_64-linux-gnu/5/../../../x86_64-linux-gnu/libm.a(s_sin.o): In function `__sin':
(.text+0x3572): undefined reference to `_dl_x86_cpu_features'
collect2: error: ld returned 1 exit status
[prompt]> ll sin_ma.out
ls: cannot access 'sin_ma.out': No such file or directory
[prompt]>
[prompt]> gcc -fPIC main.c -DFUNC=tanh -o tanh_ma.out -l:libm.a
[prompt]> ll tanh_ma.out
-rwxrwxr-x 1 cfati cfati 12856 Jun 13 20:10 tanh_ma.out
[prompt]> ldd tanh_ma.out
linux-vdso.so.1 => (0x00007ffcfa531000)
libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f124625c000)
/lib64/ld-linux-x86-64.so.2 (0x00007f1246626000)
[prompt]> ./tanh_ma.out
func(3.141592): 0.996272
As seen:
For sin, the linker complained (even without -fno-builtin)
For tanh, things went fine
From now on, I'm going to focus on the case that doesn't work.
Play a bit with the linker flags (man ld ([die.linux]: ld(1) - Linux man page)):
[prompt]> gcc -fPIC main.c -DFUNC=sin -o sin_ma_undefined.out -l:libm.a -Wl,--warn-unresolved-symbols
/usr/lib/gcc/x86_64-linux-gnu/5/../../../x86_64-linux-gnu/libm.a(s_sin.o): In function `__cos':
(.text+0x3542): warning: undefined reference to `_dl_x86_cpu_features'
/usr/lib/gcc/x86_64-linux-gnu/5/../../../x86_64-linux-gnu/libm.a(s_sin.o): In function `__sin':
(.text+0x3572): warning: undefined reference to `_dl_x86_cpu_features'
[prompt]> ll sin_ma_undefined.out
-rwxrwxr-x 1 cfati cfati 104088 Jun 13 20:10 sin_ma_undefined.out
[prompt]> ldd sin_ma_undefined.out
linux-vdso.so.1 => (0x00007fff984b0000)
libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f274ad00000)
/lib64/ld-linux-x86-64.so.2 (0x00007f274b0ca000)
[prompt]> ./sin_ma_undefined.out
Segmentation fault (core dumped)
It passed the link phase, but segfaulted at runtime (which was kind of expected).
Came across [Amper.Git]: open-source/glibc - Add _dl_x86_cpu_features to rtld_global (note that none of that is in the official glibc: [GNU]: Index of /gnu/libc). It's some pretty heavy stuff there. I "borrowed" some and saved it.
_dl_x86_cpu_features.c:
#if defined(_DL_X86_CPU_FEATURES__WORKAROUND)
# define FEATURE_INDEX_MAX 1
enum {
COMMON_CPUID_INDEX_1 = 0,
COMMON_CPUID_INDEX_7,
COMMON_CPUID_INDEX_80000001, // for AMD
// Keep the following line at the end.
COMMON_CPUID_INDEX_MAX
};
struct cpu_features {
enum cpu_features_kind {
arch_kind_unknown = 0,
arch_kind_intel,
arch_kind_amd,
arch_kind_other
} kind;
int max_cpuid;
struct cpuid_registers {
unsigned int eax;
unsigned int ebx;
unsigned int ecx;
unsigned int edx;
} cpuid[COMMON_CPUID_INDEX_MAX];
unsigned int family;
unsigned int model;
unsigned int feature[FEATURE_INDEX_MAX];
};
struct cpu_features _dl_x86_cpu_features;
#endif
#include "_dl_x86_cpu_features.c" also needs to be added in main.c:
[prompt]> gcc -fPIC main.c -DFUNC=sin -D_DL_X86_CPU_FEATURES__WORKAROUND -o sin_ma_workaround.out -l:libm.a
[prompt]> ll sin_ma_workaround.out
-rwxrwxr-x 1 cfati cfati 104088 Jun 13 20:13 sin_ma_workaround.out
[prompt]> ldd sin_ma_workaround.out
linux-vdso.so.1 => (0x00007fff17b6c000)
libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f5a992e5000)
/lib64/ld-linux-x86-64.so.2 (0x00007f5a996af000)
[prompt]> ./sin_ma_workaround.out
func(3.141592): 0.000001
Apparently, it works (at least in my environment)!!!
Although it did the trick for me (and probably it will be the same in your case), I still consider it a workaround (gainarie), and I'm not aware of the full implications. So, my advice is to go with (either one of) the recommended options (from step #2.). But, it would be interesting to see how other compilers behave.

Related

Error compiling against libopcodes

I'm following some code from here which is as follows:
The OP says that you can link with -lbfd -lopcodes.
However, for OpenSuSE I've found that for libbfd I need -lbfd -liberty -lz -ldl
When trying to compile, here is my results:
make
gcc ./main.c -lbfd -liberty -lz -ldl -lopcodes -g -o bfd_se
/usr/lib64/gcc/x86_64-suse-linux/4.8/../../../../lib64/libopcodes.a(arm-dis.o): In function `print_insn_neon':
/home/abuild/rpmbuild/BUILD/binutils-2.24/build-dir/opcodes/../../opcodes/arm-dis.c:2927: undefined reference to `floatformat_ieee_single_little'
/home/abuild/rpmbuild/BUILD/binutils-2.24/build-dir/opcodes/../../opcodes/arm-dis.c:2927: undefined reference to `floatformat_to_double'
/usr/lib64/gcc/x86_64-suse-linux/4.8/../../../../lib64/libopcodes.a(m68k-dis.o): In function `print_insn_arg':
/home/abuild/rpmbuild/BUILD/binutils-2.24/build-dir/opcodes/../../opcodes/m68k-dis.c:1103: undefined reference to `floatformat_m68881_ext'
/home/abuild/rpmbuild/BUILD/binutils-2.24/build-dir/opcodes/../../opcodes/m68k-dis.c:1103: undefined reference to `floatformat_to_double'
/home/abuild/rpmbuild/BUILD/binutils-2.24/build-dir/opcodes/../../opcodes/m68k-dis.c:1099: undefined reference to `floatformat_ieee_double_big'
/home/abuild/rpmbuild/BUILD/binutils-2.24/build-dir/opcodes/../../opcodes/m68k-dis.c:1099: undefined reference to `floatformat_to_double'
/home/abuild/rpmbuild/BUILD/binutils-2.24/build-dir/opcodes/../../opcodes/m68k-dis.c:1095: undefined reference to `floatformat_ieee_single_big'
/home/abuild/rpmbuild/BUILD/binutils-2.24/build-dir/opcodes/../../opcodes/m68k-dis.c:1095: undefined reference to `floatformat_to_double'
collect2: error: ld returned 1 exit status
Makefile:4: recipe for target 'bfd_se' failed
make: *** [bfd_se] Error 1
What other flags do I need to add to have this program compile? I see the error is related to different architectures, but I'm not trying to cross compile.
Thanks!
useful information:
uname -a
Linux node 4.0.5-THS_on #1 SMP PREEMPT Thu Jun 18 16:37:06 CDT 2015 x86_64 x86_64 x86_64 GNU/Linux
gcc --version
gcc (SUSE Linux) 4.8.3 20140627 [gcc-4_8-branch revision 212064]
Copyright (C) 2013 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.
objdump --version (it uses libbfd and libopcodes)
GNU objdump (GNU Binutils; openSUSE 13.2) 2.24.0.20140403-6.1
Copyright 2013 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) any later version.
This program has absolutely no warranty.
Most of the time libbfd and libopcodes does not come as shared libraries in standard distributions (because they are not meant to be linked to). You must either link to the static libraries (libbfd.a and libopcodes.a) or recompile your distribution package and include the shared libraries in it (.so files).
So, to make it clear, try:
gcc ./main.c -liberty -lz -ldl -g -o bfd_se /usr/lib64/gcc/x86_64-suse-linux/4.8/../../../../lib64/libopcodes.a /usr/lib64/gcc/x86_64-suse-linux/4.8/../../../../lib64/libbfd.a

`bash: ./a.out: No such file or directory` on running executable produced by `ld`

Here is a Hello World code in C:
// a.c
#include <stdio.h>
int main() {
printf("Hello world\n");
return 0;
}
I compile it as gcc a.c, which produces a.out as expected and ./a.out prints Hello world... as expected.
Now if I do the compile and link separately:
gcc -c a.c; ld -lc a.o, it run the a.out produced as ./a.out I get the message:
bash: ./a.out: No such file or directory
I Googled that error and it seems that happens when the executable produced is a 32-bit ELF and the machine architecture is 64-bit.
I'm running a 64-bit machine and running file a.out gives:
a.out: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked (uses shared libs), not stripped
Why does this happen?
EDIT:
Output of uname -m
$ uname -m
x86_64
Output of ldd a.out
$ ldd a.out
linux-vdso.so.1 => (0x00007ffeeedfb000)
libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007fa13a7b8000)
/lib64/ld-linux-x86-64.so.2 (0x00007fa13abab000)
gcc a.c produces a.out which runs correctly.
ld -lc a.o
There are several things wrong with this command line:
In general, user-level code should never use ld directly, and always use appropriate compiler front end (gcc here) to perform the link.
As you have discovered, the link command line that gcc constructs is quite complicated, and the command line that you've accepted in Joan Esteban's answer is wrong.
If you want to see the actual link command, examine output from gcc -v a.o.
Also note that link command changes significantly when you change gcc command only slightly (e.g. some OSes require different crt1.o depending on whether you are linking multi-threaded executable or not), and the command line is always OS-specific (which is one more reason to never use ld directly).
Libraries should follow object files on command line. So ld -lc a.o is never correct, and should always be (a variant of) ld a.o -lc. Explanation.
Link dynamic executables with gcc foo.o (to use the right paths for CRT and libc, and the dynamic linker / ELF interpreter ld-linux-x86-64.so.2).
Or gcc -nostartfiles foo.o for libc but not CRT _start, if you have a hand-written _start
(For static executables without libc or CRT, you can use ld directly or gcc -nostdlib -static.)
gcc -v foo.o will show you the actual paths GCC used on your system.
The other answers only address how to avoid this1, not the actual question of what happened.
The gcc -c a.c; ld -lc a.o commands you gave produce a pretty obvious warning:
ld: warning: cannot find entry symbol _start; defaulting to 0000000000400260
So even if this file could be executed, it will probably crash right away. See #EmployedRussian's answer for an explanation of what you should have done.
The question of why it can't even be executed is still interesting:
$ strace ./a.out
execve("./a.out", ["./a.out"], [/* 72 vars */]) = -1 ENOENT (No such file or directory)
execve(2) returns ENOENT because it can't find the interpreter (which I figured out from file and so on, see below). You'd get the same error from trying to run a file that started with
#!/usr/non-existant-path/bin/bash
As you discovered, the usual reason for this error message is when running an ELF binary on a system without the right dynamic linker and dynamic libraries installed (e.g. a 64bit system without 32bit support installed). In your case, it's because you used a bad link command and made a dynamic executable with a bad interpreter path.
I'm on Ubuntu 15.10, where GNU file version 5.22 reports:
a.out: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib/ld64.so.1, not stripped
There is no /lib/ld64.so.1 on my system. ldd output is confusing, because ldd uses its default ELF interpreter, not the one specified by the binary.
$ ldd a.out
linux-vdso.so.1 => (0x00007ffc18d2b000)
libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f0e0a79f000)
/lib/ld64.so.1 => /lib64/ld-linux-x86-64.so.2 (0x0000559dbc9d2000)
So it assumes that the runtime interpreter in the binary resolved to the one ldd used itself, I guess.
Your ldd output is probably from an old version too, since it just shows /lib64/ld-linux-x86-64.so.2 for that line. Not taking a bad guess is probably better behaviour, for a weird case like this, but doesn't help you see that your binary has a weird interpreter path.
readelf -l a.out
will decode the ELF headers for you, including the interpreter path. (Thanks to #EmployedRussian's comment for pointing this out.)
Use that:
ld -o a.out -dynamic-linker /lib/ld-linux.so.2 /usr/lib/crt1.o /usr/lib/crti.o -lc c.o /usr/lib/crtn.o

How to make a linux shared object (library) runnable on its own?

Noticing that gcc -shared creates an executable file, I just got the weird idea to check what happens when I try to run it ... well the result was a segfault for my own lib. So, being curious about that, I tried to "run" the glibc (/lib/x86_64-linux-gnu/libc.so.6 on my system). Sure enough, it didn't crash but provided me some output:
GNU C Library (Debian GLIBC 2.19-18) stable release version 2.19, by Roland McGrath et al.
Copyright (C) 2014 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.
Compiled by GNU CC version 4.8.4.
Compiled on a Linux 3.16.7 system on 2015-04-14.
Available extensions:
crypt add-on version 2.1 by Michael Glad and others
GNU Libidn by Simon Josefsson
Native POSIX Threads Library by Ulrich Drepper et al
BIND-8.2.3-T5B
libc ABIs: UNIQUE IFUNC
For bug reporting instructions, please see:
<http://www.debian.org/Bugs/>.
So my question here is: what is the magic behind this? I can't just define a main symbol in a library -- or can I?
I wrote a blog post on this subject where I go more in depth because I found it intriguing. You can find my original answer below.
You can specify a custom entry point to the linker with the -Wl,-e,entry_point option to gcc, where entry_point is the name of the library's "main" function.
void entry_point()
{
printf("Hello, world!\n");
}
The linker doesn't expect something linked with -shared to be run as an executable, and must be given some more information for the program to be runnable. If you try to run the library now, you will encounter a segmentation fault.
The .interp section is a part of the resulting binary that is needed by the OS to run the application. It's set automatically by the linker if -shared is not used. You must set this section manually in the C code if building a shared library that you want to execute by itself. See this question.
The interpreter's job is to find and load the shared libraries needed by a program, prepare the program to run, and then run it. For the ELF format (ubiquitous for modern *nix) on Linux, the ld-linux.so program is used. See it's man page for more info.
The line below puts a string in the .interp section using GCC attributes. Put this in the global scope of your library to explicitly tell the linker that you want to include a dynamic linker path in your binary.
const char interp_section[] __attribute__((section(".interp"))) = "/path/to/ld-linux";
The easiest way to find the path to ld-linux.so is to run ldd on any normal application. Sample output from my system:
jacwah#jacob-mint17 ~ $ ldd $(which gcc)
linux-vdso.so.1 => (0x00007fff259fe000)
libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007faec5939000)
/lib64/ld-linux-x86-64.so.2 (0x00007faec5d23000)
Once you've specified the interpreter your library should be executable! There's just one slight flaw: it will segfault when entry_point returns.
When you compile a program with main, it's not the first function to be called when executing it. main is actually called by another function called _start. This function is responsible for setting up argv and argc and other initialisation. It then calls main. When main returns, _start calls exit with the return value of main.
There's no return address on stack in _start as it's the first function to be called. If it tries to return, an invalid read occurs (ultimately causing a segmentation fault). This is exactly what is happening in our entry point function. Add a call to exit as the last line of your entry function to properly clean up and not crash.
example.c
#include <stdio.h>
#include <stdlib.h>
const char interp_section[] __attribute__((section(".interp"))) = "/path/to/ld-linux";
void entry_point()
{
printf("Hello, world!\n");
exit(0);
}
Compile with gcc example.c -shared -fPIC -Wl,-e,entry_point.
While linking with -shared gcc strips start files, and some objects (like cout) will not be initialized. So, std::cout << "Abc" << std::endl will cause SEGFAULT.
Approach 1
(simplest way to create executable library)
To fix it change linker options. The simplest way - run gcc to build executable with -v option (verbose) and see the linker command line. In this command line you should remove -z now, -pie (if present) and add -shared. The sources must be anyway compiled with -fPIC (not -fPIE).
Let's try. For example we have the following x.cpp:
#include <iostream>
// The next line is required, while building executable gcc will
// anyway include full path to ld-linux-x86-64.so.2:
extern "C" const char interp_section[] __attribute__((section(".interp"))) = "/lib64/ld-linux-x86-64.so.2";
// some "library" function
extern "C" __attribute__((visibility("default"))) int aaa() {
std::cout << "AAA" << std::endl;
return 1234;
}
// use main in a common way
int main() {
std::cout << "Abc" << std::endl;
}
Firstly compile this file via g++ -c x.cpp -fPIC. Then will link it dumping command-line via g++ x.o -o x -v.
We will get correct executable, which can't be dynamically loaded as a shared library. Check this by python script check_x.py:
import ctypes
d = ctypes.cdll.LoadLibrary('./x')
print(d.aaa())
Running $ ./x will be successful. Running $ python check_x.py will fail with OSError: ./x: cannot dynamically load position-independent executable.
While linking g++ calls collect2 linker wraper which calls ld. You can see command-line for collect2 in the output of last g++ command like this:
/usr/lib/gcc/x86_64-linux-gnu/11/collect2 -plugin /usr/lib/gcc/x86_64-linux-gnu/11/liblto_plugin.so -plugin-opt=/usr/lib/gcc/x86_64-linux-gnu/11/lto-wrapper -plugin-opt=-fresolution=/tmp/ccqDN9Df.res -plugin-opt=-pass-through=-lgcc_s -plugin-opt=-pass-through=-lgcc -plugin-opt=-pass-through=-lc -plugin-opt=-pass-through=-lgcc_s -plugin-opt=-pass-through=-lgcc --build-id --eh-frame-hdr -m elf_x86_64 --hash-style=gnu --as-needed -dynamic-linker /lib64/ld-linux-x86-64.so.2 -pie -z now -z relro -o x /usr/lib/gcc/x86_64-linux-gnu/11/../../../x86_64-linux-gnu/Scrt1.o /usr/lib/gcc/x86_64-linux-gnu/11/../../../x86_64-linux-gnu/crti.o /usr/lib/gcc/x86_64-linux-gnu/11/crtbeginS.o -L/usr/lib/gcc/x86_64-linux-gnu/11 -L/usr/lib/gcc/x86_64-linux-gnu/11/../../../x86_64-linux-gnu -L/usr/lib/gcc/x86_64-linux-gnu/11/../../../../lib -L/lib/x86_64-linux-gnu -L/lib/../lib -L/usr/lib/x86_64-linux-gnu -L/usr/lib/../lib -L/usr/lib/gcc/x86_64-linux-gnu/11/../../.. x.o -lstdc++ -lm -lgcc_s -lgcc -lc -lgcc_s -lgcc /usr/lib/gcc/x86_64-linux-gnu/11/crtendS.o /usr/lib/gcc/x86_64-linux-gnu/11/../../../x86_64-linux-gnu/crtn.o
Find there -pie -z now and replace with -shared. After running this command you will get new x executable, which will wonderfully work as an executable and as a shared library:
$ ./x
Abc
$ python3 check_x.py
AAA
1234
This approach has disadvantages: it is hard to do replacement automatically. Also before calling collect2 GCC will create a temporary file for LTO plugin (link-time optimization). This temporary file will be missing while you running the command manually.
Approach 2
(applicable way to create executable library)
The idea is to change linker for GCC to own wrapper which will correct arguments for collect2. We will use the following Python script collect3.py as linker:
#!/usr/bin/python3
import subprocess, sys, os
marker = '--_wrapper_make_runnable_so'
def sublist_index(haystack, needle):
for i in range(len(haystack) - len(needle)):
if haystack[i:i+len(needle)] == needle: return i
def remove_sublist(haystack, needle):
idx = sublist_index(haystack, needle)
if idx is None: return haystack
return haystack[:idx] + haystack[idx+len(needle):]
def fix_args(args):
#print("!!BEFORE REPLACE ", *args)
if marker not in args:
return args
args = remove_sublist(args, [marker])
args = remove_sublist(args, ['-z', 'now'])
args = remove_sublist(args, ['-pie'])
args.append('-shared')
#print("!!AFTER REPLACE ", *args)
return args
# get search paths for linker directly from gcc
def findPaths(prefix = "programs: ="):
for line in subprocess.run(['gcc', '-print-search-dirs'], stdout=subprocess.PIPE).stdout.decode('utf-8').split('\n'):
if line.startswith(prefix): return line[len(prefix):].split(':')
# get search paths for linker directly from gcc
def findLinker(linker_name = 'collect2'):
for p in findPaths():
candidate = os.path.join(p, linker_name)
#print("!!CHECKING LINKER ", candidate)
if os.path.exists(candidate) : return candidate
if __name__=='__main__':
args = sys.argv[1:]
args = fix_args(args)
exit(subprocess.call([findLinker(), *args]))
This script will replace arguments and call true linker. To switch linker we will create the file specs.txt with the following content:
*linker:
<full path to>/collect3.py
To tell our fake linker that we want to correct arguments we will use the additional argument --_wrapper_make_runnable_so. So, the complete command line will be the following:
g++ -specs=specs.txt -Wl,--_wrapper_make_runnable_so x.o -o x
(we suppose that you want to link existing x.o).
After this you can both run the target x and use it as dynamic library.

Undefined reference to printf when using GCC cross compiler

I'm trying to get the following simple 'Hello World' program to compile using a cross compiler (GCC 4.9.2) targeting mips:
#include <stdio.h>
int main()
{
int x = 5;
printf("x = %d\n", x);
}
The x variable is there to stop GCC changing printf to puts, which it seems to do automatically for a simple newline-terminated string.
I've built a cross compiler under ${HOME}/xc and am executing it using the following command:
${HOME}/xc/bin/mips-gcc -v hello.c
However, I'm getting the following error:
/tmp/ccW5mHJu.o: In function `main':
(.text+0x24): undefined reference to `printf'
collect2: error: ld returned 1 exit status
I'm assuming this is a problem with the linker, as I'd expect the process to fail earlier if for example stdio.h couldn't be found on the search path. I can compile a simpler program which simply returns zero, so it's not the case that the entire toolchain is broken, presumably just the standard library linking (I'm using newlib 2.2.0-1).
I get the same error regardless of whether I run the cross compiler under Linux (Ubuntu 14.10) or Cygwin (Windows 8).
The full output from GCC is:
Using built-in specs.
COLLECT_GCC=/home/paul/xc/bin/mips-gcc
COLLECT_LTO_WRAPPER=/home/paul/xc/libexec/gcc/mips/4.9.2/lto-wrapper
Target: mips
Configured with: /home/paul/xc/mips/tmp/gcc-4.9.2/configure --prefix=/home/paul/xc --target=mips --enable-languages=c --with-newlib --without-isl --without-cloogs --disable-threads --disable-libssp --disable-libgomp --disable-libmudflap
Thread model: single
gcc version 4.9.2 (GCC)
COLLECT_GCC_OPTIONS='-v'
/home/paul/xc/libexec/gcc/mips/4.9.2/cc1 -quiet -v hello.c -quiet -dumpbase hello.c -auxbase hello -version -o /tmp/ccCpAajQ.s
GNU C (GCC) version 4.9.2 (mips)
compiled by GNU C version 4.9.1, GMP version 6.0.0, MPFR version 3.1.2, MPC version 1.0.3
GGC heuristics: --param ggc-min-expand=100 --param ggc-min-heapsize=131072
ignoring nonexistent directory "/home/paul/xc/lib/gcc/mips/4.9.2/../../../../mips/sys-include"
#include "..." search starts here:
#include <...> search starts here:
/home/paul/xc/lib/gcc/mips/4.9.2/include
/home/paul/xc/lib/gcc/mips/4.9.2/include-fixed
/home/paul/xc/lib/gcc/mips/4.9.2/../../../../mips/include
End of search list.
GNU C (GCC) version 4.9.2 (mips)
compiled by GNU C version 4.9.1, GMP version 6.0.0, MPFR version 3.1.2, MPC version 1.0.3
GGC heuristics: --param ggc-min-expand=100 --param ggc-min-heapsize=131072
Compiler executable checksum: cffaaedf0b24662e67a5d97387fc5b17
COLLECT_GCC_OPTIONS='-v'
/home/paul/xc/lib/gcc/mips/4.9.2/../../../../mips/bin/as -EB -O1 -no-mdebug -mabi=32 -o /tmp/ccW5mHJu.o /tmp/ccCpAajQ.s
COMPILER_PATH=/home/paul/xc/libexec/gcc/mips/4.9.2/:/home/paul/xc/libexec/gcc/mips/4.9.2/:/home/paul/xc/libexec/gcc/mips/:/home/paul/xc/lib/gcc/mips/4.9.2/:/home/paul/xc/lib/gcc/mips/:/home/paul/xc/lib/gcc/mips/4.9.2/../../../../mips/bin/
LIBRARY_PATH=/home/paul/xc/lib/gcc/mips/4.9.2/:/home/paul/xc/lib/gcc/mips/4.9.2/../../../../mips/lib/
COLLECT_GCC_OPTIONS='-v'
/home/paul/xc/libexec/gcc/mips/4.9.2/collect2 -plugin /home/paul/xc/libexec/gcc/mips/4.9.2/liblto_plugin.so -plugin-opt=/home/paul/xc/libexec/gcc/mips/4.9.2/lto-wrapper -plugin-opt=-fresolution=/tmp/cc8TAJb9.res -plugin-opt=-pass-through=-lgcc -plugin-opt=-pass-through=-lgcc -EB /home/paul/xc/lib/gcc/mips/4.9.2/crti.o /home/paul/xc/lib/gcc/mips/4.9.2/crtbegin.o -L/home/paul/xc/lib/gcc/mips/4.9.2 -L/home/paul/xc/lib/gcc/mips/4.9.2/../../../../mips/lib /tmp/ccW5mHJu.o -lgcc -lgcc /home/paul/xc/lib/gcc/mips/4.9.2/crtend.o /home/paul/xc/lib/gcc/mips/4.9.2/crtn.o
/home/paul/xc/lib/gcc/mips/4.9.2/../../../../mips/bin/ld: warning: cannot find entry symbol _start; defaulting to 0000000000400050
/tmp/ccW5mHJu.o: In function `main':
(.text+0x24): undefined reference to `printf'
collect2: error: ld returned 1 exit status
The build script I'm using is here (I wrote it based on half a dozen tutorials which all suggested slightly different things):
https://github.com/UoMCS/mips-cross-compile
Basically it does the following steps:
Build binutils.
Build GCC (stage 1).
Build newlib.
Build GCC (stage 2).
I'm aware that there are other tools such as crosstool-ng and builtroot, however the person I'm building this toolchain for wants to edit parts of binutils before setting off the build process, and the toolchain also has to work under Cygwin (crosstool-ng won't for various reasons, including case-sensitive file paths).
I think this is probably going to be something obvious, but I've been messing around with this for a week and can't see what it could be. Any help would be greatly appreciated!
It is necessary to build libraries to go with your cross compiler. In particular, you need to have a cross-compiled version of glibc or some other implementation of the standard library, to get a version of printf().
Have a look at this link for an example of the type of things you need to consider to get all the things you need - the cross-compiler, the headers, and libraries.
Try linking the library on the command line:
${HOME}/xc/bin/mips-gcc -v hello.c -lib
Including the std libraries (lib and io)header links the implementations by default (libc.so or .a). However, you are using a 'user-defined' implementation and may not be linking the proper one.
I suggest explicit linkage on the command line. I'm not certain of the syntax.
EDIT:
Or better Still, use a makefile to compile with the following lines, and specifying other include directories in the INCLUDES place holder:
CC = gcc
CXX = g++
INCLUDES =
CFLAGS = -g -Wall $(INCLUDES)
CXXFLAGS = -g -Wall $(INCLUDES)
LDFLAGS = -g
hello: hello.o newlib.o
hello.o: hello.c newlib.h
newlib.o: newlib.c newlib.h
newlib.h is the header file you'll include in newlib.c (implementation/definition) source file (that declares the functions) and hello.c. It may be named differently from stdio.h.
Check this out, it may help:
Why do you have to link the math library in C?
and this too:
http://www.tldp.org/HOWTO/Glibc2-HOWTO-6.html
A custom specs file could work:
cd /home/paul/xc/lib/gcc/mips/4.9.2/
${HOME}/xc/bin/mips-gcc -dumpspecs > specs
Add to the specs file:
*lib:
-lc
Note that there must be empty lines before *lib: and after -lc. Perhaps you have to change the library name to the name of your newlib-c-library. Perhaps more must be added than only -lc, e.g. the *lib:-section on my Linux looks more complex.
UPDATE: The builtin specs lib for the default libraries are configured here:
In file gcc-4.9.2/gcc/gcc.c lines 527-530:
/* config.h can define LIB_SPEC to override the default libraries. */
#ifndef LIB_SPEC
#define LIB_SPEC "%{!shared:%{g*:-lg} %{!p:%{!pg:-lc}}%{p:-lc_p}%{pg:-lc_p}}"
#endif
In file gcc-4.9.2/gcc/config/mips/elf.h lines 40-42:
/* Leave the linker script to choose the appropriate libraries. */
#undef LIB_SPEC
#define LIB_SPEC ""
Perhaps the default LIB_SPEC in gcc.c works for you by commenting out lines 40-42 in elf.h.
Perhaps you need to edit elf.h and replace the empty LIB_SPEC with "-lc" or something similar.
UPDATE: When you configured gcc you gave --target=mips. In gcc-4.9.2\gcc\config.gcc there are other mips-targets which are more specific, e.g.mips*-*-linux*, perhaps selecting the appropriate one gives the right LIB_SPEC and linking will be successful.
UPDATE: big endian Linux target: mips-unknown-linux-gnu little endian Linux target: mipsel-unknown-linux-gnu source
UPDATE: Using your build script I was able to link your sample program with following modifications:
In your config.sh:
export ISL_VERSION="0.12.2"
In file gcc-4.9.2/gcc/config/mips/elf.h lines 40-42:
/* Leave the linker script to choose the appropriate libraries. */
#undef LIB_SPEC
#define LIB_SPEC "-lc -lcfe -lc"
If you don't want the modification in elf.h the libraries must be given when invoking mips-gcc.
UPDATE:
newlib doesn't work at all, GCC fails in the second stage with an error about not being able to find crti.o etc.
Strange, using your build script crti.o was created:
[osboxes#osboxes 4.9.2]$ pwd
/home/osboxes/xc/lib/gcc/mips/4.9.2
[osboxes#osboxes 4.9.2]$ ll
total 6240
-rw-r--r--. 1 osboxes osboxes 3248 May 16 19:49 crtbegin.o
-rw-r--r--. 1 osboxes osboxes 1924 May 16 19:49 crtend.o
-rw-r--r--. 1 osboxes osboxes 1040 May 16 19:49 crti.o
-rw-r--r--. 1 osboxes osboxes 1056 May 16 19:49 crtn.o
drwxrwxr-x. 3 osboxes osboxes 4096 May 16 19:49 include
drwxrwxr-x. 2 osboxes osboxes 4096 May 16 19:45 include-fixed
drwxrwxr-x. 3 osboxes osboxes 4096 May 16 19:49 install-tools
-rw-r--r--. 1 osboxes osboxes 6289352 May 16 19:49 libgcc.a
-rw-r--r--. 1 osboxes osboxes 56844 May 16 19:49 libgcov.a
drwxrwxr-x. 3 osboxes osboxes 4096 May 16 19:49 plugin
-rw-rw-r--. 1 osboxes osboxes 6215 May 18 18:45 specs
printf() was implemented in libc,
Please check your c lib, such as glibc, oh, yours is newlib.
try #4566976 's way
use readelf -s check is there a printf section exists in libc.so libc.a
( i'm not sure the lib filename in newlib, mine is glibc )
The most convenient way to achieve this is to use putchar in place of printf. May be you have to change some of your code, or may be you have to add macros/functions that may run like printf.

C - Compile static file

I want to compile C code into one monolithic executable file (include every dependencies in this file) using GCC.
Usually I can compile the code with this command:
gcc -o server ex-serv-x509.c -lgnutls
But when I try to compile it with this -static argument I get this error:
[root#localhost test]# gcc -static -o server ex-serv-x509.c -lgnutls
/usr/bin/ld: cannot find -lc
collect2: ld returned 1 exit status
[root#localhost test]#
How I can solve the problem?
Best wishes
Try using the ldd command to see what it's linking in without the -static option. Here's what I get for a silly program I have.
~$ gcc so.o -lm -o so
~$ ldd so
linux-gate.so.1 => (0x00db7000)
libm.so.6 => /lib/libm.so.6 (0x00c7f000)
libc.so.6 => /lib/libc.so.6 (0x0037f000)
/lib/ld-linux.so.2 (0x002da000)
~$ gcc so.o -static -lm -o so
~$ ldd so
not a dynamic executable
So without the static I automagically get the shared version of libc, which surprised me even though it should not have. I imagine you have the shared version but not the static, so you'll need to get the static library from somewhere if you have decided that 1986 is the year for you :-).
To make sure you can do: gcc -print-search-dirs and search through them and make sure that libc.a is not to be found.

Resources