When using the GCC driver, what makes a static lib "incompatible"? - c

So what I am trying to do is on Ubuntu 14.04 (x86_64) I want to set up musl-libc based on the latest released 1.1.11 version which is available at this moment.
What I did was to:
Install multilib support for GCC: sudo apt-get --no-install-recommends install gcc-multilib
Configure the libraries for 32-bit and 64-bit respectively and install them into separate folders:
CFLAGS=-m32 ./configure --prefix=$HOME/bin/musl-32-bit --disable-shared --target=i386-linux-gnu && make && make install
CFLAGS=-m64 ./configure --prefix=$HOME/bin/musl-64-bit --disable-shared --target=x86_64-linux-gnu
Then in order to build a statically linked premake4, I invoke GNU make like this on the Makefile generated by premake4:
make -j 8 CC=$HOME/bin/musl-32-bit/bin/musl-gcc ARCH=-m32 LDFLAGS="-v -static" verbose=1
This appears to work up to the linking step, which bombs with:
Linking Premake4
$HOME/bin/musl-32-bit/bin/musl-gcc -o bin/release/premake4 intermediate/gmake__/premake.o intermediate/gmake__/os_uuid.o intermediate/gmake__/os_pathsearch.o intermediate/gmake__/os_match.o intermediate/gmake__/os_chdir.o intermediate/gmake__/os_mkdir.o intermediate/gmake__/os_stat.o intermediate/gmake__/os_getversion.o intermediate/gmake__/premake_main.o intermediate/gmake__/os_isdir.o intermediate/gmake__/string_endswith.o intermediate/gmake__/os_isfile.o intermediate/gmake__/scripts.o intermediate/gmake__/path_isabsolute.o intermediate/gmake__/os_rmdir.o intermediate/gmake__/os_getcwd.o intermediate/gmake__/os_is64bit.o intermediate/gmake__/os_copyfile.o intermediate/gmake__/lstate.o intermediate/gmake__/ltable.o intermediate/gmake__/lgc.o intermediate/gmake__/lobject.o intermediate/gmake__/lcode.o intermediate/gmake__/lmathlib.o intermediate/gmake__/lbaselib.o intermediate/gmake__/lmem.o intermediate/gmake__/lfunc.o intermediate/gmake__/lparser.o intermediate/gmake__/ldblib.o intermediate/gmake__/lzio.o intermediate/gmake__/lstrlib.o intermediate/gmake__/lvm.o intermediate/gmake__/lauxlib.o intermediate/gmake__/llex.o intermediate/gmake__/lstring.o intermediate/gmake__/ldump.o intermediate/gmake__/ldebug.o intermediate/gmake__/loadlib.o intermediate/gmake__/lopcodes.o intermediate/gmake__/linit.o intermediate/gmake__/ldo.o intermediate/gmake__/lapi.o intermediate/gmake__/liolib.o intermediate/gmake__/loslib.o intermediate/gmake__/lundump.o intermediate/gmake__/ltm.o intermediate/gmake__/ltablib.o -v -static -L. -s -rdynamic -lm -ldl
Using built-in specs.
Reading specs from $HOME/bin/musl-32-bit/lib/musl-gcc.specs
rename spec cpp_options to old_cpp_options
COLLECT_GCC=gcc
COLLECT_LTO_WRAPPER=/usr/lib/gcc/x86_64-linux-gnu/4.8/lto-wrapper
Target: x86_64-linux-gnu
Configured with: ../src/configure -v --with-pkgversion='Ubuntu 4.8.4-2ubuntu1~14.04' --with-bugurl=file:///usr/share/doc/gcc-4.8/README.Bugs --enable-languages=c,c++,java,go,d,fortran,objc,obj-c++ --prefix=/usr --program-suffix=-4.8 --enable-shared --enable-linker-build-id --libexecdir=/usr/lib --without-included-gettext --enable-threads=posix --with-gxx-include-dir=/usr/include/c++/4.8 --libdir=/usr/lib --enable-nls --with-sysroot=/ --enable-clocale=gnu --enable-libstdcxx-debug --enable-libstdcxx-time=yes --enable-gnu-unique-object --disable-libmudflap --enable-plugin --with-system-zlib --disable-browser-plugin --enable-java-awt=gtk --enable-gtk-cairo --with-java-home=/usr/lib/jvm/java-1.5.0-gcj-4.8-amd64/jre --enable-java-home --with-jvm-root-dir=/usr/lib/jvm/java-1.5.0-gcj-4.8-amd64 --with-jvm-jar-dir=/usr/lib/jvm-exports/java-1.5.0-gcj-4.8-amd64 --with-arch-directory=amd64 --with-ecj-jar=/usr/share/java/eclipse-ecj.jar --enable-objc-gc --enable-multiarch --disable-werror --with-arch-32=i686 --with-abi=m64 --with-multilib-list=m32,m64,mx32 --with-tune=generic --enable-checking=release --build=x86_64-linux-gnu --host=x86_64-linux-gnu --target=x86_64-linux-gnu
Thread model: posix
gcc version 4.8.4 (Ubuntu 4.8.4-2ubuntu1~14.04)
COMPILER_PATH=/usr/lib/gcc/x86_64-linux-gnu/4.8/:/usr/lib/gcc/x86_64-linux-gnu/4.8/:/usr/lib/gcc/x86_64-linux-gnu/:/usr/lib/gcc/x86_64-linux-gnu/4.8/:/usr/lib/gcc/x86_64-linux-gnu/
LIBRARY_PATH=/usr/lib/gcc/x86_64-linux-gnu/4.8/:/usr/lib/gcc/x86_64-linux-gnu/4.8/../../../../lib32/:/lib/../lib32/:/usr/lib/../lib32/:/usr/lib/gcc/x86_64-linux-gnu/4.8/../../../:/lib/:/usr/lib/
COLLECT_GCC_OPTIONS='-m32' '-o' 'bin/release/premake4' '-v' '-static' '-L.' '-s' '-rdynamic' '-specs=$HOME/bin/musl-32-bit/lib/musl-gcc.specs' '-mtune=generic' '-march=i686'
/usr/lib/gcc/x86_64-linux-gnu/4.8/collect2 -dynamic-linker /lib/ld-musl-i386.so.1 -nostdlib -static -export-dynamic -z relro -o bin/release/premake4 -s $HOME/bin/musl-32-bit/lib/crt1.o $HOME/bin/musl-32-bit/lib/crti.o /usr/lib/gcc/x86_64-linux-gnu/4.8/crtbegin.o -L. -L$HOME/bin/musl-32-bit/lib -L /usr/lib/gcc/x86_64-linux-gnu/4.8/. intermediate/gmake__/premake.o intermediate/gmake__/os_uuid.o intermediate/gmake__/os_pathsearch.o intermediate/gmake__/os_match.o intermediate/gmake__/os_chdir.o intermediate/gmake__/os_mkdir.o intermediate/gmake__/os_stat.o intermediate/gmake__/os_getversion.o intermediate/gmake__/premake_main.o intermediate/gmake__/os_isdir.o intermediate/gmake__/string_endswith.o intermediate/gmake__/os_isfile.o intermediate/gmake__/scripts.o intermediate/gmake__/path_isabsolute.o intermediate/gmake__/os_rmdir.o intermediate/gmake__/os_getcwd.o intermediate/gmake__/os_is64bit.o intermediate/gmake__/os_copyfile.o intermediate/gmake__/lstate.o intermediate/gmake__/ltable.o intermediate/gmake__/lgc.o intermediate/gmake__/lobject.o intermediate/gmake__/lcode.o intermediate/gmake__/lmathlib.o intermediate/gmake__/lbaselib.o intermediate/gmake__/lmem.o intermediate/gmake__/lfunc.o intermediate/gmake__/lparser.o intermediate/gmake__/ldblib.o intermediate/gmake__/lzio.o intermediate/gmake__/lstrlib.o intermediate/gmake__/lvm.o intermediate/gmake__/lauxlib.o intermediate/gmake__/llex.o intermediate/gmake__/lstring.o intermediate/gmake__/ldump.o intermediate/gmake__/ldebug.o intermediate/gmake__/loadlib.o intermediate/gmake__/lopcodes.o intermediate/gmake__/linit.o intermediate/gmake__/ldo.o intermediate/gmake__/lapi.o intermediate/gmake__/liolib.o intermediate/gmake__/loslib.o intermediate/gmake__/lundump.o intermediate/gmake__/ltm.o intermediate/gmake__/ltablib.o -lm -ldl --start-group /usr/lib/gcc/x86_64-linux-gnu/4.8/libgcc.a /usr/lib/gcc/x86_64-linux-gnu/4.8/libgcc_eh.a -lc --end-group /usr/lib/gcc/x86_64-linux-gnu/4.8/crtend.o $HOME/bin/musl-32-bit/lib/crtn.o
/usr/bin/ld: skipping incompatible $HOME/bin/musl-32-bit/lib/libc.a when searching for -lc
/usr/bin/ld: cannot find -lc
collect2: error: ld returned 1 exit status
make[1]: *** [bin/release/premake4] Error 1
make: *** [Premake4] Error 2
The relevant line is:
/usr/bin/ld: skipping incompatible $HOME/bin/musl-32-bit/lib/libc.a when searching for -lc
Now the part I don't understand about this is, that when I ar x the libc.a (into a folder $HOME/bin/musl-32-bit/lib/libc) generated during the build step of musl-libc (see above), it proves that all of the objects included seem to be of the correct target architecture (all show ELF 32-bit LSB relocatable, Intel 80386, version 1 (SYSV), not stripped) as I can prove from coming up empty when issuing the following command:
find $HOME/bin/musl-32-bit/lib -name '*.o' -exec file {} +|grep -v 'ELF 32-bit LSB relocatable, Intel 80386, version 1 (SYSV), not stripped'
And in fact this gives no output. Similarly when looking inside the build directory using the same method, I cannot find any object file that doesn't match my expectation.
For good measure I decided to also task objdump to tell me more about the libc.a in question and came up with the same result:
objdump -a $HOME/bin/musl-32-bit/lib/libc.a|grep 'file format'|grep -v 'file format elf32-i386'
So my question is twofold:
what disqualifies a static library as "incompatible" when GCC is asked to link it?
what could be the particular issue I am seeing?
The first is what I am really interested in, but with the second I am asking to share your experience with trouble-shooting like this. Which verification steps have I missed, for example?
Please note that the "native" premake4 builds just fine with:
make -j 8 CC=$HOME/bin/musl-64-bit/bin/musl-gcc ARCH=-m64 LDFLAGS=-static verbose=1
From the output when adding the -v flag to LDFLAGS it appears as if the target always stays at x86_64-linux-gnu. I have yet to come up with a method to fix this, though.

In short, the musl-gcc wrapper script setup is not well-suited to use with -m32. I think what's happening is that the actual compiler is getting invoked in the default (64-bit) mode by musl-gcc, then the resulting object files are not compatible with the (intended, 32-bit) libc.
It may work if you put -m32 in the generated wrapper script. This will happen automatically with recent versions if you put the -m32 in $CC (i.e. CC="gcc -m32") rather than putting it in $CFLAGS.
Update: As noted in the discussion that was moved to chat, adding -Wl,-melf_i386 is probably also needed (due to flaws in the spec file used by the musl-gcc wrapper that don't account for -m32 support) but still does not seem to be sufficient.

The solution
It turns out the solution is rather simple.
We need to tell both the linker and the driver to use -m32 ... I was that far before. However, it turns out that the missing piece was to pass the linker option to the driver via CFLAGS like this -Wl,-melf_i386.
I am finally able to build and link the 32-bit executable on a multilib-enabled 64-bit host.
NB: Below information is left in place for those who want to learn how I investigated the issue.
Alright, so I investigated the issue a little further and the output gets more enlightening once you extract the object files. To reproduce what I am doing you may have to use Bash or a similar shell that allows for $(...) or you need to adjust the command lines accordingly.
First off it's important to have gcc-multilib and friends installed in order to target -m32 (i386-linux-gnu which happens to be an alias for i686-linux-gnu here).
The code and the make file
I had the following make file:
CC?=$(HOME)/bin/musl/bin64/musl-gcc
BLDARCH?=-m64
CFLAGS+=-v $(BLDARCH)
LDFLAGS+=-v -static $(BLDARCH)
all: helloworld
helloworld: helloworld.c
clean:
rm -f helloworld
rebuild: clean all
.PHONY: clean rebuild
.NOTPARALLEL: rebuild
and the following small helloworld.c:
#include <stdio.h>
int main(int argc, char** argv)
{
printf("Hello world!\n");
return 0;
}
and my 32-bit musl-libc was installed to $HOME/bin/musl/{bin,include,lib}32 respectively. The 64-bit one was installed to $HOME/bin/musl/{bin,include,lib}64 respectively.
Attempting to build with:
make CC=$HOME/bin/musl/bin32/musl-gcc BLDARCH=-m32 rebuild
always failed with the same meaningless lines:
/usr/bin/ld: skipping incompatible ~/bin/musl/lib32/libc.a when searching for -lc
/usr/bin/ld: cannot find -lc
collect2: error: ld returned 1 exit status
make: *** [helloworld] Error 1
Digging into the details
So after some contemplation I decided to re-do the steps done by the gcc driver manually.
This meant to run roughly (I replaced all occurrences of my home folder with a ~):
Compile: /usr/lib/gcc/x86_64-linux-gnu/4.8/cc1 -quiet -imultilib 32 -imultiarch i386-linux-gnu helloworld.c -nostdinc -isystem ~/bin/musl/include32 -isystem /usr/lib/gcc/x86_64-linux-gnu/4.8/include -quiet -dumpbase helloworld.c -m32 -mtune=generic -march=i686 -auxbase helloworld -version -fstack-protector -Wformat -Wformat-security -o /tmp/ccGmMuR1.s
Assemble: as -v -v --32 -o /tmp/ccgRGlqf.o /tmp/ccGmMuR1.s
Link: env COMPILER_PATH=/usr/lib/gcc/x86_64-linux-gnu/4.8/:/usr/lib/gcc/x86_64-linux-gnu/4.8/:/usr/lib/gcc/x86_64-linux-gnu/:/usr/lib/gcc/x86_64-linux-gnu/4.8/:/usr/lib/gcc/x86_64-linux-gnu/ LIBRARY_PATH=/usr/lib/gcc/x86_64-linux-gnu/4.8/32/:/usr/lib/gcc/x86_64-linux-gnu/4.8/../../../../lib32/:/lib/../lib32/:/usr/lib/../lib32/:/usr/lib/gcc/x86_64-linux-gnu/4.8/:/usr/lib/gcc/x86_64-linux-gnu/4.8/../../../:/lib/:/usr/lib/ COLLECT_GCC_OPTIONS="-v -v -static -m32 -o helloworld -specs=~/bin/musl/lib32/musl-gcc.specs -mtune=generic -march=i686" /usr/lib/gcc/x86_64-linux-gnu/4.8/collect2 -dynamic-linker /lib/ld-musl-i386.so.1 -nostdlib -static -z relro -o helloworld ~/bin/musl/lib32/crt1.o ~/bin/musl/lib32/crti.o /usr/lib/gcc/x86_64-linux-gnu/4.8/32/crtbegin.o -L~/bin/musl/lib32 -L /usr/lib/gcc/x86_64-linux-gnu/4.8/32/. /tmp/ccpL09mJ.o --start-group /usr/lib/gcc/x86_64-linux-gnu/4.8/32/libgcc.a /usr/lib/gcc/x86_64-linux-gnu/4.8/32/libgcc_eh.a -lc --end-group /usr/lib/gcc/x86_64-linux-gnu/4.8/32/crtend.o ~/bin/musl/lib32/crtn.o
Obviously this didn't change the error message a bit just yet:
/usr/bin/ld: skipping incompatible ~/bin/musl/lib32/libc.a when searching for -lc
/usr/bin/ld: cannot find -lc
collect2: error: ld returned 1 exit status
So I took out the -lc from the command line for collect2 and created a folder ~/bin/musl/lib32/archive into which I extracted the whole libc.a generated by my musl-libc build attempt. I then instructed collect2 where to find the object files like this:
Link: env COMPILER_PATH=/usr/lib/gcc/x86_64-linux-gnu/4.8/:/usr/lib/gcc/x86_64-linux-gnu/4.8/:/usr/lib/gcc/x86_64-linux-gnu/:/usr/lib/gcc/x86_64-linux-gnu/4.8/:/usr/lib/gcc/x86_64-linux-gnu/ LIBRARY_PATH=/usr/lib/gc
c/x86_64-linux-gnu/4.8/32/:/usr/lib/gcc/x86_64-linux-gnu/4.8/../../../../lib32/:/lib/../lib32/:/usr/lib/../lib32/:/usr/lib/gcc/x86_64-linux-gnu/4.8/:/usr/lib/gcc/x86_64-linux-gnu/4.8/../../../:/lib/:/usr/lib/ COLLECT_GCC_OPTIONS="-v -v -
static -m32 -o helloworld -specs=~/bin/musl/lib32/musl-gcc.specs -mtune=generic -march=i686" /usr/lib/gcc/x86_64-linux-gnu/4.8/collect2 -dynamic-linker /lib/ld-musl-i386.so.1 -nostdlib -static -z relro -o helloworld $HOME/bin/musl/lib32/crt1.o ~/bin/musl/lib32/crti.o /usr/lib/gcc/x86_64-linux-gnu/4.8/32/crtbegin.o -L~/bin/musl/lib32 -L /usr/lib/gcc/x86_64-linux-gnu/4.8/32/. /tmp/ccpL09mJ.o --start-group /usr/lib/gcc/x86_64-linux-g
nu/4.8/32/libgcc.a /usr/lib/gcc/x86_64-linux-gnu/4.8/32/libgcc_eh.a --end-group /usr/lib/gcc/x86_64-linux-gnu/4.8/32/crtend.o ~/bin/musl/lib32/crtn.o $(find ~/bin/musl/lib32/archive -type f -name '*.o')
This gist is taking out -lc and appending $(find ~/bin/musl/lib32/archive -type f -name '*.o').
Which gave me a whole bunch of new, but more meaningful errors similar to the following ones:
/usr/bin/ld: Warning: size of symbol `__init_ssp' changed from 1 in ~/bin/musl/lib32/archive/__libc_start_main.o to 65 in ~/bin/musl/lib32/archive/__stack_chk_fail.o
/usr/bin/ld: Warning: size of symbol `__funcs_on_exit' changed from 126 in ~/bin/musl/lib32/archive/atexit.o to 1 in ~/bin/musl/lib32/archive/exit.o
# more of those
/usr/bin/ld: i386 architecture of input file `~/bin/musl/lib32/crt1.o' is incompatible with i386:x86-64 output
/usr/bin/ld: i386 architecture of input file `~/bin/musl/lib32/crti.o' is incompatible with i386:x86-64 output
# more of those
The plot thickens. Apparently the collect2 command gets the wrong idea about what to build. Which is odd considering the output for the governing environment variables:
COMPILER_PATH=/usr/lib/gcc/x86_64-linux-gnu/4.8/:/usr/lib/gcc/x86_64-linux-gnu/4.8/:/usr/lib/gcc/x86_64-linux-gnu/:/usr/lib/gcc/x86_64-linux-gnu/4.8/:/usr/lib/gcc/x86_64-linux-gnu/
LIBRARY_PATH=/usr/lib/gcc/x86_64-linux-gnu/4.8/32/:/usr/lib/gcc/x86_64-linux-gnu/4.8/../../../../lib32/:/lib/../lib32/:/usr/lib/../lib32/:/usr/lib/gcc/x86_64-linux-gnu/4.8/:/usr/lib/gcc/x86_64-linux-gnu/4.8/../../../:/lib/:/usr/lib/
COLLECT_GCC_OPTIONS='-v' '-v' '-static' '-m32' '-o' 'helloworld' '-specs=~/bin/musl/lib32/musl-gcc.specs' '-mtune=generic' '-march=i686'
... which I passed using env in my attempt to reproduce the conditions encountered by the collect2 wrapper when linking the libc.a.
In order to find out more about the internals of the GNU compilers one needs to read https://gcc.gnu.org/onlinedocs/gccint/
Unfortunately the part about collect2 doesn't help us here. But the lengthy output of /usr/lib/gcc/x86_64-linux-gnu/4.8/collect2 --help looks promising.
Just how could we smuggle our command line option to collect2?
For now I was going to settle for whatever I could run manually. So I attempted to tell the linker which output format I expected. Based on the list of supported targets I was interested in elf_i386.
Passing -melf_386 at the end of the previous line gave an interesting error output. Numerous referenced functions such as __vsyscall, __moddi3 and __divdi3 were undefined. That indicated they simply didn't exist in the object files from the static lib or in any of the startup .o files for that matter:
~/bin/musl/lib32/archive/aio.o: In function `cleanup':
aio.c:(.text+0x5ad): undefined reference to `__vsyscall'
aio.c:(.text+0x5bb): undefined reference to `__vsyscall'
aio.c:(.text+0x5e8): undefined reference to `__vsyscall'
aio.c:(.text+0x5f6): undefined reference to `__vsyscall'
aio.c:(.text+0x61d): undefined reference to `__vsyscall'
~/bin/musl/lib32/archive/aio.o:aio.c:(.text+0x62b): more undefined references to `__vsyscall' follow
~/bin/musl/lib32/archive/cpow.o: In function `cpow':
cpow.c:(.text+0x4f): undefined reference to `__mulxc3'
~/bin/musl/lib32/archive/cpowf.o: In function `cpowf':
cpowf.c:(.text+0x47): undefined reference to `__mulxc3'
~/bin/musl/lib32/archive/cpowl.o: In function `cpowl':
cpowl.c:(.text+0x4c): undefined reference to `__mulxc3'
~/bin/musl/lib32/archive/sysconf.o: In function `sysconf':
sysconf.c:(.text+0xcc): undefined reference to `__vsyscall'
~/bin/musl/lib32/archive/__getdents.o: In function `__getdents':
__getdents.c:(.text+0x13): undefined reference to `__vsyscall'
~/bin/musl/lib32/archive/opendir.o: In function `opendir':
opendir.c:(.text+0x37): undefined reference to `__vsyscall'
~/bin/musl/lib32/archive/readdir.o: In function `readdir':
readdir.c:(.text+0x1f): undefined reference to `__vsyscall'
~/bin/musl/lib32/archive/__init_tls.o: In function `__init_tls':
__init_tls.c:(.text+0x136): undefined reference to `__vsyscall6'
__init_tls.c:(.text+0x16e): undefined reference to `__vsyscall'
As I had already clarified in my question, the object files from the archive all stated that they were elf32-i386.
The functions __vsyscall and __vsyscall6 should end up in a file called syscall.o given the source file for i386 in musl-libc: src/internal/i386/syscall.s. Let's verify that first. Since there is also a file src/misc/syscall.c the name might be different though. Four files have syscall in the file name:
__syscall_cp.o
syscall_cp.o
syscall.o
syscall_ret.o
Querying those files using nm gave:
$ nm -s $(ls |grep syscall)
__syscall_cp.o:
00000000 t sccp
U __syscall
00000005 T __syscall_cp
00000000 W __syscall_cp_c
syscall_cp.o:
U __cancel
00000008 T __cp_begin
00000035 T __cp_cancel
00000030 T __cp_end
00000000 T __syscall_cp_asm
syscall.o:
00000000 T syscall
U __syscall_ret
U __vsyscall6
syscall_ret.o:
U __errno_location
00000000 T __syscall_ret
Symbols with the symbol type U are undefined and therefore expected by the linker to come from outside (external to each object file).
A final $ nm --defined-only *.o ../*.o|grep vsyscall was what was needed to verify that those symbols were indeed missing from the libc.a.
So the cross-built libc.a what was faulty after all. Back to the drawing board.
I hope this description helps others to figure out similar issues and look behind the scenes in GCC.
The saga continues
I was really surprised to see:
$ nm --defined-only ../libc.a |grep -B 2 vsyscall
syscall.o:
0000004b T __syscall
00000000 T __vsyscall
00000031 T __vsyscall6
but for the extracted object files the corresponding command (nm --defined-only *.o ../*.o|grep -B 2 vsyscall) would yield no output.
So inside the libc.a somehow nm sees the two symbols, but after extracting them they disappear? Odd.
Let's look for syscall.o in the libc.a:
$ nm ../libc.a |grep ^syscall
syscall.o:
syscall_ret.o:
syscall.o:
syscall_cp.o:
Whoa? So syscall.o exists twice inside the static library? Well that looks like it may just be the error cause we're looking for. And it certainly does explain why the symbols disappear. Likely the latter syscall.o overwrites the one first extracted when running ar x ....
Confirming:
$ nm ../libc.a |grep -A 4 ^syscall\.o
syscall.o:
0000004b T __syscall
U __sysinfo
00000000 T __vsyscall
00000031 T __vsyscall6
--
syscall.o:
00000000 T syscall
U __syscall_ret
U __vsyscall6
and looking into the musl-libc source tree after doing the 32-bit build:
$ find . -type f -name 'syscall.o' -exec nm {} +
./src/internal/syscall.o:
0000004b T __syscall
U __sysinfo
00000000 T __vsyscall
00000031 T __vsyscall6
./src/misc/syscall.o:
00000000 T syscall
U __syscall_ret
U __vsyscall6
Copying the former into the lib32/archive directory under a name that doesn't collide with existing names gives more errors on other functions, suggesting other object files may also exist as duplicates inside the generated libc.a.
Which ones are duplicates?
$ diff <(nm libc.a|grep ':$'|cut -f 1 -d :|sort) <(nm libc.a|grep ':$'|cut -f 1 -d :|sort -u)
--- /dev/fd/63 2015-10-05 23:58:53.683804823 +0000
+++ /dev/fd/62 2015-10-05 23:58:53.683804823 +0000
## -131,7 +131,6 ##
clogl.o
clog.o
clone.o
-clone.o
closedir.o
close.o
cnd_broadcast.o
## -1115,7 +1114,6 ##
__syscall_cp.o
syscall_cp.o
syscall.o
-syscall.o
syscall_ret.o
sysconf.o
sysinfo.o
This way we see clone.o and syscall.o are affected as duplicates. Which indicates that some object files are missing altogether from the libc.a given undefined references to the following symbols:
__divdi3
__moddi3
__mulxc3
__tls_get_new
__udivdi3
__umoddi3
These names happen to coincide with the ones from the Integer library routines listed for GCC. Which makes me think I am missing one library provided by GCC which to link. Like libgcc?! ...
I have package lib32gcc-4.8-dev which means I should have the required file:
$ apt-file list lib32gcc-4.8-dev|grep -E 'libgcc.*\.a'
lib32gcc-4.8-dev: /usr/lib/gcc/x86_64-linux-gnu/4.8/32/libgcc.a
lib32gcc-4.8-dev: /usr/lib/gcc/x86_64-linux-gnu/4.8/32/libgcc_eh.a
After the previous step I decided to set up a x86_32 version on Ubuntu 14.04 that was essentially at the same patch level.
I then compared the libgcc.a from the x86_32 machine with that from the x86_64 machine. They turned out to be almost identical except for the "symbol value" (in nm lingo) of a handful of functions.
Also, since the symbols were in the static library I attempted to link against the static library again. This worked only with -melf_i386 on the linker (collect2) command line.
After trying to use LDFLAGS and noticing that those were also passed to cc1, I set appended -Wl,-melf_i386 to the CFLAGS and now it worked. Brilliant.
As a side-note: I also swapped the /usr/lib/gcc/x86_64-linux-gnu/4.8/collect2 in the command line for ld of which collect2 is supposed to be a wrapper. The error output was identical.

Related

Compilation error: /usr/bin/ld: cannot find -lclntsh

While trying to compile a C program using make file, I am facing the following linking error described below.
I know this kind of issues are already discussed in many other posts, but I tried with solutions suggested in them but they all did not work.
/usr/bin/ld: cannot find -lclntsh
libclntsh.so and libclntsh.so.10.1 are present in oracle path /u01/app/oracle/product/10.2.0/lib and that is being given as -L option in make file. Still I am facing the issue:-
Linking yieldrpt ...
cc -v -g -D_HPUX_SOURCE -DPARETO -Wl,-aarchive -L/u01/app/oracle/product/10.2.0/lib/ yieldrpt.o -lclntsh `cat /u01/app/oracle/product/10.2.0/lib/ldflags` -lmalloc -ldl -lm \
-lmalloc -o yieldrpt
Using built-in specs.
Target: i386-redhat-linux
Configured with: ../configure --prefix=/usr --mandir=/usr/share/man --infodir=/usr/share/info --enable-shared --enable-threads=posix --enable-checking=release --with-system-zlib --enable-__cxa_atexit --disable-libunwind-exceptions --enable-libgcj-multifile --enable-languages=c,c++,objc,obj-c++,java,fortran,ada --enable-java-awt=gtk --disable-dssi --disable-plugin --with-java-home=/usr/lib/jvm/java-1.4.2-gcj-1.4.2.0/jre --with-cpu=generic --host=i386-redhat-linux
Thread model: posix
gcc version 4.1.2 20080704 (Red Hat 4.1.2-54)
/usr/libexec/gcc/i386-redhat-linux/4.1.2/collect2 --eh-frame-hdr -m elf_i386 --hash-style=gnu -dynamic-linker /lib/ld-linux.so.2 -o yieldrpt /usr/lib/gcc/i386-redhat-linux/4.1.2/../../../crt1.o /usr/lib/gcc/i386-redhat-linux/4.1.2/../../../crti.o /usr/lib/gcc/i386-redhat-linux/4.1.2/crtbegin.o -L/u01/app/oracle/product/10.2.0/lib/ -L/usr/lib/gcc/i386-redhat-linux/4.1.2 -L/usr/lib/gcc/i386-redhat-linux/4.1.2 -L/usr/lib/gcc/i386-redhat-linux/4.1.2/../../.. -aarchive yieldrpt.o -lclntsh -lnbeq10 -lnhost10 -lnus10 -lnldap10 -lldapclnt10 -lnsslb10 -lntcp10 -lntcps10 -lnsslb10 -lntcp10 -lntns10 -lmalloc -ldl -lm -lmalloc -lgcc --as-needed -lgcc_s --no-as-needed -lc -lgcc --as-needed -lgcc_s --no-as-needed /usr/lib/gcc/i386-redhat-linux/4.1.2/crtend.o /usr/lib/gcc/i386-redhat-linux/4.1.2/../../../crtn.o
/usr/bin/ld: cannot find -lclntsh
collect2: ld returned 1 exit status
make: *** [yieldrpt] Error 1
Please let me know if any other info is needed.
Thank you
You are passing -aarchive to the linker. This is an HP/UX compatibility option
that is synonymous with -Bstatic, which directs the linker not to link dynamic
libraries. Hence your libclntsh.so is ignored. See the documentation
If you don't know why this option is there you may well remove it and try again. If you
know it is necessary then you will have to find static libraries, including libclntsh.a to
link against.
If you have libclntsh.so.12.1 or similar is already installed you may try symlinking the library to libclntsh.so with the following command: ln -s /usr/lib/oracle/12.1/client64/lib/libclntsh.so.12.1 /usr/lib/oracle/12.1/client64/lib/libclntsh.so
For me it was /usr/bin/ld: cannot find -lclntshcore. For some reason, the following symlink is missing.
libclntshcore.so -> /home/ubuntu/ora2pg/instantclient_12_2/libclntshcore.so.12.1
[oracle#em12c bin]$ pwd
/u01/em12c/middleware/oms/bin
[oracle#em12c bin]$ export ORACLE_HOME=/u01/em12c/middleware/oms
[oracle#em12c bin]$ ./genclntsh
[oracle#em12c bin]$ ls -l /u01/em12c/middleware/oms/lib32//libclntsh.so
lrwxrwxrwx. 1 oracle oinstall 17 Oct 23 22:16 /u01/em12c/middleware/oms/lib32//libclntsh.so -> libclntsh.so.11.1

Clang link-time optimization doesn't work properly on Fedora 18

I'm a newcomer to clang, so it's likely I'm doing something silly. But I've spent several hours looking for solutions, including searching here, where I haven't found questions addressing -flto with distro-provided packages. The detail of this description are specific to Fedora 18, but I'm having similar problems on Ubuntu 13.04, so the problem isn't specific to Fedora. It's either me or clang.
Problem: I'm trying to compile a simple hello-world program using clang++ -flto to get the benefits of link-time-optimization. Without -flto it works fine. With -flto it fails to link. Invoking as clang -flto -o hello hello.o -v to see the full linker command line, I get:
$ clang++ -flto -o hello hello.o -v
clang version 3.2 (tags/RELEASE_32/final)
Target: x86_64-redhat-linux-gnu
Thread model: posix
"/usr/bin/ld" --eh-frame-hdr -m elf_x86_64 -dynamic-linker /lib64/ld-linux-x86-64.so.2 -o hello /usr/lib/gcc/x86_64-redhat-linux/4.7.2/../../../../lib64/crt1.o /usr/lib/gcc/x86_64-redhat-linux/4.7.2/../../../../lib64/crti.o /usr/lib/gcc/x86_64-redhat-linux/4.7.2/crtbegin.o -L/usr/lib/gcc/x86_64-redhat-linux/4.7.2 -L/usr/lib/gcc/x86_64-redhat-linux/4.7.2/../../../../lib64 -L/lib/../lib64 -L/usr/lib/../lib64 -L/usr/lib/gcc/x86_64-redhat-linux/4.7.2/../../.. -L/lib -L/usr/lib -plugin /usr/bin/../lib/LLVMgold.so hello.o -lstdc++ -lm -lgcc_s -lgcc -lc -lgcc_s -lgcc /usr/lib/gcc/x86_64-redhat-linux/4.7.2/crtend.o /usr/lib/gcc/x86_64-redhat-linux/4.7.2/../../../../lib64/crtn.o
/usr/bin/ld: /usr/bin/../lib/LLVMgold.so: error loading plugin
/usr/bin/ld: /usr/bin/../lib/LLVMgold.so: error in plugin cleanup (ignored)
clang: error: linker command failed with exit code 1 (use -v to see invocation)
There seem to be two problems:
clang++ invokes the linker as /usr/bin/ld, and that's not the gold linker. Fedora18 installs gold as /usr/bin/ld.gold. I've tried creating a symlink from /usr/local/bin/ld to /usr/bin/ld.gold, verified that which ld says /usr/local/bin/ld, but clang++ doesn't use that. It seems to be hardwired to /usr/bin/ld.
clang++ invoked the linker with -plugin /usr/bin/../lib/LLVMgold.so. That's wrong, as the Fedora distribution of clang places it at /usr/lib64/llvm/LLVMgold.so.
I have tried manually invoking that linker line above with the following tweaks:
Replace -plugin /usr/bin/../lib/LLVMgold.so with -plugin /usr/lib64/llvm/LLVMgold.so. This yields the error message hello.o: file not recognized: File format not recognized. So the non-gold linker seems to know about plugins but wont take the .o's which contain LLVM bitcode.
Replace /usr/bin/ld with /usr/bin/ld.gold. This works, generates an executable that runs as expected.
Both of the above with --plugin instead of -plugin. This change makes no difference.
So what's the best way for somebody who prefers to stick to the system-provided packages to use clang -flto? I'm hoping there is a config file, or undocumented options or environment variables that will let me override these. Or better, that I'm missing a package and a "yum install ..." will fix it.
I would prefer not to invoke the linker directly, as then my makefiles need to know system objects and libraries that they should be ignorant of (e.g. crt1.o, crtbegin.o, crtend.o). I could also build clang myself, but I'm not seeing anything in its configure script that lets me configure the path of the linker and plugin.
I'm running Fedora 18. The only non-distro packages on the computer are google chrome and VMware Tools (it's a guest inside VMWare Fusion). Versions of relevant Fedora packages (the whole computer is "yum updated" as of today, 29-Apr-2013):
$ yum list --noplugins installed binutils* clang* llvm* gcc*
Installed Packages
binutils.x86_64 2.23.51.0.1-6.fc18 #updates
binutils-devel.x86_64 2.23.51.0.1-6.fc18 #updates
clang.x86_64 3.2-2.fc18 #updates
clang-devel.x86_64 3.2-2.fc18 #updates
clang-doc.noarch 3.2-2.fc18 #updates
gcc.x86_64 4.7.2-8.fc18 #fedora
gcc-c++.x86_64 4.7.2-8.fc18 #fedora
llvm.x86_64 3.2-2.fc18 #updates
llvm-libs.x86_64 3.2-2.fc18 #updates
There is an utility alternatives in Fedora - it allows to subtitute one linker with another on system level:
$ sudo alternatives --display ld
ld - status is auto.
link currently points to /usr/bin/ld.bfd
/usr/bin/ld.bfd - priority 50
/usr/bin/ld.gold - priority 30
Current `best' version is /usr/bin/ld.bfd.
$ sudo alternatives --set ld /usr/bin/ld.gold
About LLVMgold.so location you can only report a bug in Fedora Bugzilla, since the path is built-in in clang sources:
lib/Driver/Tools.cpp: std::string Plugin = ToolChain.getDriver().Dir + "/../lib/LLVMgold.so";
Fedora guys may apply a patch to the Clang's source package, or create a symlink to LLVMgold.so.
There is no changes even in Fedora 20 yet.

Linking a C program directly with ld fails with undefined reference to `__libc_csu_fini`

I'm trying to compile a C program under Linux. However, out of curiosity, I'm trying to execute some steps by hand: I use:
the gcc frontend to produce assembler code
then run the GNU assembler to get an object file
and then link it with the C runtime to get a working executable.
Now I'm stuck with the linking part.
The program is a very basic "Hello world":
#include <stdio.h>
int main() {
printf("Hello\n");
return 0;
}
I use the following command to produce the assembly code:
gcc hello.c -S -masm=intel
I'm telling gcc to quit after compiling and dump the assembly code with Intel syntax.
Then I use th GNU assembler to produce the object file:
as -o hello.o hello.s
Then I try using ld to produce the final executable:
ld hello.o /usr/lib/libc.so /usr/lib/crt1.o -o hello
But I keep getting the following error message:
/usr/lib/crt1.o: In function `_start':
(.text+0xc): undefined reference to `__libc_csu_fini'
/usr/lib/crt1.o: In function `_start':
(.text+0x11): undefined reference to `__libc_csu_init'
The symbols __libc_csu_fini/init seem to be a part of glibc, but I can't find them anywhere! I tried linking against libc statically (against /usr/lib/libc.a) with the same result.
What could the problem be?
/usr/lib/libc.so is a linker script which tells the linker to pull in the shared library /lib/libc.so.6, and a non-shared portion, /usr/lib/libc_nonshared.a.
__libc_csu_init and __libc_csu_fini come from /usr/lib/libc_nonshared.a. They're not being found because references to symbols in non-shared libraries need to appear before the archive that defines them on the linker line. In your case, /usr/lib/crt1.o (which references them) appears after /usr/lib/libc.so (which pulls them in), so it doesn't work.
Fixing the order on the link line will get you a bit further, but then you'll probably get a new problem, where __libc_csu_init and __libc_csu_fini (which are now found) can't find _init and _fini. In order to call C library functions, you should also link /usr/lib/crti.o (after crt1.o but before the C library) and /usr/lib/crtn.o (after the C library), which contain initialisation and finalisation code.
Adding those should give you a successfully linked executable. It still won't work, because it uses the dynamically linked C library without specifying what the dynamic linker is. You'll need to tell the linker that as well, with something like -dynamic-linker /lib/ld-linux.so.2 (for 32-bit x86 at least; the name of the standard dynamic linker varies across platforms).
If you do all that (essentially as per Rob's answer), you'll get something that works in simple cases. But you may come across further problems with more complex code, as GCC provides some of its own library routines which may be needed if your code uses certain features. These will be buried somewhere deep inside the GCC installation directories...
You can see what gcc is doing by running it with either the -v option (which will show you the commands it invokes as it runs), or the -### option (which just prints the commands it would run, with all of the arguments quotes, but doesn't actually run anything). The output will be confusing unless you know that it usually invokes ld indirectly via one of its own components, collect2 (which is used to glue in C++ constructor calls at the right point).
I found another post which contained a clue: -dynamic-linker /lib/ld-linux.so.2.
Try this:
$ gcc hello.c -S -masm=intel
$ as -o hello.o hello.s
$ ld -o hello -dynamic-linker /lib/ld-linux.so.2 /usr/lib/crt1.o /usr/lib/crti.o hello.o -lc /usr/lib/crtn.o
$ ./hello
hello, world
$
Assuming that a normal invocation of gcc -o hello hello.c produces a working build, run this command:
gcc --verbose -o hello hello.c
and gcc will tell you how it's linking things. That should give you a good idea of everything that you might need to account for in your link step.
In Ubuntu 14.04 (GCC 4.8), the minimal linking command is:
ld -dynamic-linker /lib64/ld-linux-x86-64.so.2 \
/usr/lib/x86_64-linux-gnu/crt1.o \
/usr/lib/x86_64-linux-gnu/crti.o \
-L/usr/lib/gcc/x86_64-linux-gnu/4.8/ \
-lc -lgcc -lgcc_s \
hello.o \
/usr/lib/x86_64-linux-gnu/crtn.o
Although they may not be necessary, you should also link to -lgcc and -lgcc_s, since GCC may emit calls to functions present in those libraries for operations which your hardware does not implement natively, e.g. long long int operations on 32-bit. See also: Do I really need libgcc?
I had to add:
-L/usr/lib/gcc/x86_64-linux-gnu/4.8/ \
because the default linker script does not include that directory, and that is where libgcc.a was located.
As mentioned by Michael Burr, you can find the paths with gcc -v. More precisely, you need:
gcc -v hello_world.c |& grep 'collect2' | tr ' ' '\n'
This is how I fixed it on ubuntu 11.10:
apt-get remove libc-dev
Say yes to remove all the packages but copy the list to reinstall after.
apt-get install libc-dev
If you're running a 64-bit OS, your glibc(-devel) may be broken. By looking at this and this you can find these 3 possible solutions:
add lib64 to LD_LIBRARY_PATH
use lc_noshared
reinstall glibc-devel
Since you are doing the link process by hand, you are forgetting to link the C run time initializer, or whatever it is called.
To not get into the specifics of where and what you should link for you platform, after getting your intel asm file, use gcc to generate (compile and link) your executable.
simply doing gcc hello.c -o hello should work.
Take it:
$ echo 'main(){puts("ok");}' > hello.c
$ gcc -c hello.c -o hello.o
$ ld hello.o -o hello.exe /usr/lib/crt1.o /usr/lib/crti.o /usr/lib/crtn.o \
-dynamic-linker /lib/ld-linux.so.2 -lc
$ ./hello.exe
ok
Path to /usr/lib/crt*.o will when glibc configured with --prefix=/usr

Manually compile using gcc -v

Is it possible to compile a simple Hello World program that only uses provided gcc/glibc files rather than using the default ones provided by the OS ? (Therefore, when executed, the program will only use the provided files rather than the ones the OS provides.) I've looked everywhere on the net but cannot get any to work:
I tried to manually do what this does gcc -v simple.c but I cannot reproduce it myself.
This is what I tried: (all provided files are on the Desktop)
/home/myuser/Desktop/cc1 -quiet -v simple.c -quiet -dumpbase simple.c -mtune=generic -auxbase simple -version -o /tmp/temp1.s
How can the below paths be changed to custom ones rather than default ones ?
ignoring nonexistent directory "/usr/local/include/x86_64-linux-gnu"
ignoring nonexistent directory "/usr/lib/gcc/x86_64-linux-gnu/4.4.5/../../../../x86_64-linux-gnu/include"
#include "..." search starts here:
#include search starts here:
/usr/local/include
/usr/lib/gcc/x86_64-linux-gnu/4.4.5/include
/usr/lib/gcc/x86_64-linux-gnu/4.4.5/include-fixed
/usr/include/x86_64-linux-gnu
/usr/include
End of search list.
GNU C (Debian 4.4.5-8) version 4.4.5 (x86_64-linux-gnu)
compiled by GNU C version 4.4.5, GMP version 4.3.2, MPFR version 3.0.0-p3.
GGC heuristics: --param ggc-min-expand=100 --param ggc-min-heapsize=131072
Compiler executable checksum: dac4d891d068d1bed01868869b00bd17
as -V -Qy -o /tmp/temp2.o /tmp/temp1.s
GNU assembler version 2.20.1 (x86_64-linux-gnu) using BFD version (GNU Binutils for Debian) 2.20.1-system.20100303
/home/myuser/Desktop/collect2 --build-id --eh-frame-hdr -m elf_x86_64 --hash-style=both -dynamic-linker ld-2.11.2.so crt1.o crti.o crtbegin.o /tmp/temp2.o libgcc.a --as-needed libgcc_s.so.1 --no-as-needed libc.a libgcc_s.so.1 --as-needed libgcc.a --no-as-needed crtend.o crtn.o
Why is the below /usr/bin/ld used instead of the provided ld-2.11.2.so ?
/usr/bin/ld: dynamic STT_GNU_IFUNC symbol `strcmp' with pointer equality in `libc.a(strcmp.o)' can not be used when making an executable; recompile with -fPIE and relink with -pie
collect2: ld returned 1 exit status
Can anybody modify it to work ?
-v shows you the actions performed by the compiler driver. It does not affect what standard libraries you get.
To run with all custom libraries, use -nostdlib.
How can the below paths be changed to
custom ones rather than default ones ?
Simply add -I path to the compiler call. This path will be added before the internal paths.
Why is the below /usr/bin/ld used
instead of the provided ld-2.11.2.so ?
The first one is an executable binary, the second one is a shared object. You can't use a shared object in a place of a binary.

How do I properly reference the GNU readline library to scan terminal input?

I am attempting to compile C code that utilizes the following within GNU readline.
#include <readline/readline.h>;
#include <readline/history.h>;
I've tried changing the <> to "" and compiling both with and without the -lreadline options. Nothing seems to work. When compiling without -lreadline under gcc results in the following being generated while compiling (verbose):
Reading specs from /software/gcc-3.4.6-0/pkg/lib/gcc/i386-unknown-freebsd6.1/3.4.6/specs
Configured with: ../gcc-3.4.6/configure --prefix=/software/gcc-3.4.6-0/pkg --disable-dependency-tracking --localstatedir=/var --disable-nls --program-suffix=34 --enable-shared --enable-version-specific-runtime-libs
Thread model: posix
gcc version 3.4.6
/software/gcc-3.4.6-0/pkg/libexec/gcc/i386-unknown-freebsd6.1/3.4.6/cc1 -quiet -v myshell.c -quiet -dumpbase myshell.c -auxbase myshell -version -o /var/tmp//ccVSq3jQ.s
ignoring nonexistent directory "/software/gcc-3.4.6-0/pkg/lib/gcc/i386-unknown-freebsd6.1/3.4.6/../../../../i386-unknown-freebsd6.1/include"
#include "..." search starts here:
#include <...> search starts here:
/usr/local/include
/software/gcc-3.4.6-0/pkg/include
/software/gcc-3.4.6-0/pkg/lib/gcc/i386-unknown-freebsd6.1/3.4.6/include
/usr/include
End of search list.
GNU C version 3.4.6 (i386-unknown-freebsd6.1)
compiled by GNU C version 3.4.6.
GGC heuristics: --param ggc-min-expand=99 --param ggc-min-heapsize=129976
as -o /var/tmp//ccl1Jaqk.o /var/tmp//ccVSq3jQ.s
/software/gcc-3.4.6-0/pkg/libexec/gcc/i386-unknown-freebsd6.1/3.4.6/collect2 -V -dynamic-linker /libexec/ld-elf.so.1 -L/software/gcc-3.4.6-0/pkg/lib/gcc/i386-unknown-freebsd6.1 -o a /usr/lib/crt1.o /usr/lib/crti.o /software/gcc-3.4.6-0/pkg/lib/gcc/i386-unknown-freebsd6.1/3.4.6/crtbegin.o -L/software/gcc-3.4.6-0/pkg/lib/gcc/i386-unknown-freebsd6.1/3.4.6 -L/software/gcc-3.4.6-0/pkg/lib/gcc/i386-unknown-freebsd6.1/3.4.6/../../.. /var/tmp//ccl1Jaqk.o -lreadline -lgcc -lgcc_eh -lc -lgcc -lgcc_eh /software/gcc-3.4.6-0/pkg/lib/gcc/i386-unknown-freebsd6.1/3.4.6/crtend.o /usr/lib/crtn.o
/usr/bin/ld: cannot find -lreadline
GNU ld version 2.15 [FreeBSD] 2004-05-23
Supported emulations:
elf_i386_fbsd
collect2: ld returned 1 exit status
This is the output when compiling without the -lreadline option.
/var/tmp//ccNnucSC.o(.text+0x4f): In function `main':
: undefined reference to `readline'
collect2: ld returned 1 exit status
I have been unable to resolve this error up to this point. What am I missing in either my code or while invoking gcc?
While compiling under UNIX I found the following is necessary to properly reference the GNU readline library:
gcc code.c -L/usr/local/lib -I/usr/local/include -lreadline
This ensures that the compiler finds the readline directories and files during compilation and linking.
It sounds like you don't have the libreadline development libraries installed. On Debian (including the FreeBSD port) the library is located in /lib and the development libraries (that you would link to) are in /usr/lib.
Additionally you have semi-colons after your includes, which you shouldn't have.

Resources