Custom compile of binutils/ld doesn't find symbols in archives - arm

I'm currently trying to compile Clang/LLVM for a bare metal aarch64 target. Compiling Clang was straightforward - in fact I have compiled to target multiple architectures including arm and aarch64. For the backend I'm using binutils. Since binutils can only target one architecture I've built both aarch64 and arm versions of this. Again building seemed to be straightforward. I built binutils for aarch64 using:
mkdir build
cd build
../configure --target=aarch64-none-elf
make
from an unpacked source package of binutils 2.24.
The problem I'm having is that I can't get my custom build of ld to handle archive files properly. It seems to find and open archive files without a problem but fails to search it for undefined symbols when compiling the final binary. Here's some more concrete details via an example:
Create a simple main.s file by compiling:
int foo();
int main() { return foo(); }
with Clang and using --target=aarch64 -S, i.e. to emit an architecture specific assembly file.
Do the same to create foo.s by compiling:
int foo() { return 0; }
Assemble both .s files to .o files using custom gas build from binutils.
Create an archive libfoo.a by running custom ar and ranlib builds from binutils using ar cr foo.a foo.s and ranlib libfoo.a.
Try to link the two files together using ld -L. -lfoo -o main.elf main.o.
The output shows an undefined reference to foo.
bar.cpp:(.text+0x14): undefined reference to `foo()'
I can run readelf on foo.a and it seems perfectly well formed. In particular I can see the public symbol for foo. If I run ld with --verbose then I see
attempt to open ./libfoo.a succeeded
but no indication that it's found any symbols.
If I link directly with the object files, i.e. using ld -o main.elf foo.o main.o then I get a well formed elf file. Furthermore, if I extract foo.o back out of libfoo.a then I can also link succesfully with the extracted foo.o.
Does anyone have any ideas why this might be happening? I'm pretty sure my clang build is okay as the emitted assembly files effectively decouple the problem from clang. I'd guess also that the assembled .o files are fine. So this only leaves ar/ranlib or ld as the culprit.
To try to eliminate ar/ranlib as the culprits I rebuilt binutils for arm (same steps but using --target=arm-none-eabi). If I integrate the built ar/ranlib binaries into a known good arm-eabi-none GCC toolchain then they seem to work correctly. My suspicions thus point to ld at the moment. Note that I get the same problem with the main/foo example as above if I use my Clang build with the arm build of binutils.
I also tried integrating the arm version of ld into the existing known good GCC toolchain, but got:
this linker was not configured to use sysroots
Need to figure that out. That's still a bit cryptic for me right now. It prevents me from sanity checking the arm ld build.
It might well be that I'm doing something obviously wrong, but right now I don't see it.
Notes:
I had to hack the make script for binutils to add a couple of -Wno-xxx flags. I need to do this the "correct" way, but I don't think this hack should affect the output.
I'm building/hosting all tools on OSX 10.9.4 64-bit.
UPDATE:
The error about sysroots is simple. The pre-existing toolchain was configured with flags:
--with-prefix $SOME_INSTALL_DIR
--with-sysroot $SOME_INSTALL_DIR/arm-none-eabi
If I rebuild with these then I can slot in my ld version without a problem and it works. I got excited thinking that this might have been my mistake. Previously I wasn't running make install as I didn't really care about installing at this stage. I thought perhaps my new build of ld was referencing OSX system libs/exes somehow (although I'm not sure exactly what it could reference, other than perhaps ar, i.e. if it runs ar to handle archives). However I still have the same problem with both the arm and arrach64 versions of binutils even when configured like this.

Looks like I was dramatically overthinking the problem and also missing something I'd never quite realised about ld. The issue was with the line:
ld -L. -lfoo -o main.elf main.o
I hadn't realise that ld is sensitive to object file/library ordering. The undefined reference to foo() was in main.o. I specified the library libfoo.a as input before main.o is parsed. This means that the linker doesn't bother searching libfoo.a for the symbol as it's already processed this library. main.o must be specified before, e.g.
ld -L. main.o -lfoo -o main.elf
Even wrapping -lfoo with --start-group ... --end-group doesn't fix this.
I'm still feeling somewhat surprised by this. And I've yet to convince myself it's a good feature of ld. Seems like the only use case I can think of right now is to sneakily allow duplicate symbols.
Time for a cup of tea!

Related

gcc: Not able to create .so from object files

I am trying to create .so dynamic library from *.o files and facing below issue.
LOG:
[nptemp-static]$ gcc -shared *.o -o libexample.so
/usr/bin/ld: bindings_hubbub_parser.o: relocation R_X86_64_32 against `.rodata.str1.8' can not be used when making a shared object; recompile with -fPIC
bindings_hubbub_parser.o: could not read symbols: Bad value
collect2: ld returned 1 exit status
Any idea? Do I need to recompile my whole source code with the option specified?
Actually, I am not aware of the source code which I compiled because all the source code is open source which I downloaded and compiled by following instructions in README.
I am trying to create .so dynamic library from *.o files and facing below issue.
This is not that simple. In practice, you should compile specifically when making a shared library, at least on Linux.
(Perhaps you might need to edit your Makefile or configure somehow your build automation if it was not designed for building a shared library; if building some free software library, you might ask help from its authors or community)
Shared libraries want to have position independent code. So you need to compile their source code with the -fPIC flag passed to g++ or gcc (see this). You could also want to explicit the rpath.
Read Drepper's paper: How To Write Shared Libraries.

Cannot link linker.ld

I am currently following the Bare Bones tutorial from OSDev and i have set up everything and compiled the kernel and bootloader into object files, and i have the .ld file. However, when i try to link the files and produce it into a bin using i686-elf-gcc -T linker.ld -o myos.bin -ffreestanding -O2 -nostdlib boot.o kernel.o -lgcc, i get this error:
.../i686/bin/../lib/gcc/i686-elf/4.8.2/../../../../i686-elf/bin/ld.exe: cannot open linker script file linker.ld
collect2.exe: error: ld returned 1 exit status
I am using GCC and using the compiled version of i686-elf-gcc which includes many more compilers and i have added 4 dlls as it was missing (also not related to linker), but i installed MinGW and added them. I am on Windows 10 x64. Many of the solutions or workarounds i find just brings up more errors or does absolutely nothing to the errors (-T doesn't do anything, -noixemul is not a parameter.). I removed multiboot content since i am gonna use a sector-long bootloader which loads the object files.
If i'm not mistaken, your problem is really, really, really simple:
You don't have access to linker.ld. Try running as superuser or changing the permissions.
If this doesn't solve it, you have an actual problem. Try not using the -T flag.
EDIT: Dear god, I didn't expect this to be right. Thanks!

GCC have include but not library

I'm writing my own kernel for fun, and in doing so I've needed to install glibc to use the standard C libraries. However, after installing the library to the desired directory, my kernel.c program includes the stdio.h header and attempts to use fopen, however I come across this error:
kernel.c:(.text+0x238): undefined reference tofopen'`
After looking around I noticed that I don't have any actual code to all of the header files, just the header files themselves. So I went and added the -L flag to GCC to add the lib folder that was created during the compilation of glibc and what I've found out is that the lib folder has nothing of what I need.
I poked around and found that the build directory I used when compiling glibc has the .o files I'm looking for (e.g it has iofopen.o for the fopen method).
So what's going on?
If needed, the commands I am using to compile my kernel are:
#!/bin/bash
nasm -felf32 boot.asm -o boot.o
/home/noah/opt/cross/bin/i686-elf-gcc -I/home/noah/Documents/NoahOS/include/ -L/home/noah/Documents/glibc/build -c *.c -std=gnu99 -ffreestanding -Wall -Wextra
/home/noah/opt/cross/bin/i686-elf-gcc -I/home/noah/Documents/NoahOS/include/ -L/home/noah/Documents/glibc/build -T linker.ld -o noahos.bin -ffreestanding -O2 -nostdlib *.o -lgcc
First line builds the boot file, which is assembly.
Second line runs gcc on all of the C language .c files and creates their object files.
Third line links all of the files together with linker.ld and outputs the final kernel to noahos.bin which is a runnable kernel using
qemu-system-i386 -kernel noahos.bin
If needed more information can be provided. Please ask.
You are correctly compiling your kernel using -nostdlib because the kernel can't use the standard library. Why not? Because it doesn't make sense: the standard library is the interface between user programs and the kernel, so that application developers don't need to know the system call specification for your kernel, all that is required is a port of the C library.
Oh, there's the answer. You need a port of the C library to use your own system calls. Starting with glibc might not be the easiest to port (it comes with the kitchen sink).

gcc and liboauth - linker can't find oauth.h

I'm trying to use liboauth with a C program, using gcc as my compiler, and no matter what I've tried I keep getting the error "ld: library not found for -loauth" and "clang: error: linker command failed with exit code 1".
I'm including the header via "#include <oauth.h>", and my most-recent call to gcc looked like this:
gcc -Wall -lcurl -loauth -I /usr/local/include -v -o api api.c
Now, oauth.h does exist in /usr/local/include, and there are a handful of liboauth files (including liboauth.a) located in /usr/local/bin, which I'm assuming were placed there when I ran the install. I will admit that I'm not very familiar with gcc and compiling non-trivial C programs, but I was able to get libcurl working on a fresh download in just a few minutes. I just can't figure out what's going on with liboauth.
Thanks in advance
If you are sure liboauth's located in /usr/local/bin use
gcc -Wall -L/usr/local/bin -I /usr/local/include -v -o api api.c -lcurl -loauth
It'd also be better to place libraries in the end of the command as there is some important stuff with them (they may depend on each other, etc).
By the way, it's pretty strange your libraries are in /usr/local/bin as libraries are almost always stored in some path like /usr/*/lib.

How to configure a non-standard linker for an autotooled build?

I wanted to configure an autotooled project to invoke a non-standard
linker (the gold linker),
using the stock autotools of Linux Mint 16/Ubuntu 13.10
I believed I would achieve this by:
libtoolize-ing the project
Running ./configure LD=/path/to/my/linker ... etc.
However this has been ineffective. libtoolize has been successful. After
a standard ./configure; make I now see that libtool is doing the
linking:
/bin/bash ./libtool --tag=CXX --mode=link g++ -g -O2 -o helloworld helloworld.o
But passing LD=/path/to/my/linker to configure makes no difference. Experimentally,
I even ran:
./configure LD=/does/not/exist
expecting to provoke an error, but I didn't. The output contains:
checking if the linker (/does/not/exist -m elf_x86_64) is GNU ld... no
checking whether the g++ linker (/does/not/exist -m elf_x86_64) supports shared libraries... yes
And thereafter a make continues to link, successfully, invoking g++ exactly as before.
What is the right way to configure a non-standard linker?
But passing LD=/path/to/my/linker to configure makes no difference
This is because LD is almost never and should almost never be used to link any user-space program. Correct links are performed by using the appropriate compiler driver (gcc, g++, etc) instead.
What is the right way to configure a non-standard linker?
If you have /some/path/ld and you want gcc to use that ld, pass -B/some/path flag to gcc.
It then follows that you likely want:
./configure CC='gcc -B/some/path' CXX='g++ -B/some/path' ...
I landed on this via a Google search, though my scenario is a bit different from yours; there was no libtool involved. An old open source program's Makefile was hard-coding ld to create an object file with a symbol from binary data.
This is what I ended up doing to work around the lack of $(LD) being recognized when passed to configure:
https://github.com/turboencabulator/tuxnes/commit/bab2747b175ee7f2fc3d9afb28d69d82db054b5e
Basically I added to configure.ac:
AC_CHECK_TOOL([LD], [ld])
Leaving this answer here for if someone else lands via a google search.

Resources