Linking C function lib to x86 assembly program in modern 64bit Linux - c

I'm going through a book focusing on x86 programming (Professional Assembly Language, WROX 2005). I had some problems last night and I was hoping to sort this out before returning home today so I can get a running-start and continue the text. My machine runs x64 Ubuntu (11.04 if I'm not mistaken) so the text focusing on 32bit x86 is slightly 'outdated' (I have to add --32 when assembling etc).
I am trying to dynamically link C-library functions with my assembly program but I am unsuccesfull (below commands are from memory).
ld -dynamic-linking /lib/ld-linux.so.2 -o complex -lc complex.o -m elf_i386
Running the above command in Linux gives me the message that it can't understand -lc. Okay, so I removed it.
ld -dynamic-linking /lib/ld-linux.so.2 -o complex complex.o -m elf_i386
I then get the notification that 'printf' is not recognised. The hopes was for the dynamic linker to link to the library but it does not seem to do so. Going to \lib\ I could not locate ld-linux.so.2 (strangely it didn't give me an error on this) but I did locate ld-linux-86-64.so.2. My code is 32bit but I thought what the heck, let's try this:
ld -dynamic-linking /lib/ld-linux-86-64.so.2 -o complex complex.o -m elf_i386
Still it gave the same error that 'call printf' was not recognized.
Need help dynamically linking C library functions with my 32bit Assembly program using 64bit Linux and standard GNU tools.

Sounds like you need to install the 32-bit C-runtime. Under Fedora this is:
yum install glibc-devel.i686
But I don't know the name of the equivalent Ubunutu package; perhaps:
apt-get install libc6-dev-i386

It is almost always a bad idea to try to construct a ld command line yourself. Let GCC do it for you; it automatically handles all sorts of subtleties that you don't want to have to worry about. For a 32-bit program, you do need one special command line switch, -m32:
gcc -m32 -o complex complex.o
If you have more .o files, just stack them up at the end. If you need to link against any system libraries other than libc, put appropriate -lwhatever options after all the object files.
trojanfoe is also correct; the 32-bit toolchain is an optional component. But you need more than just the 32-bit C library. Try this first:
apt-get install gcc-multilib
it should pull in most of what you need.

Try the following order please(suppose your code fil is try.s):
as --32 -g -o try.o try.s
ld -m elf_i386 -dynamic-linker /lib/ld-linux.so.2 -lc -o try try.o
For x86-64 format executable fileļ¼š
as -g -o try.o try.s
ld -dynamic-linker /lib64/ld-linux-x86-64.so.2 -lc -o try try.o

Related

make an object file(*.o) compiled by gcc to be a binary file(*.bin)

I am learning the os tutorial.
https://github.com/cfenollosa/os-tutorial
After I using gcc convert my basic.c to basic.o,
gcc -ffreestanding -c basic.c -o basic.o
As describe in the toturial, I get a file named basic.o which is an object file.
According to the next step, I need to convert basic.o to basic.bin.
ld -o basic.bin -Ttext 0x0 --oformat binary basic.o
The question is that my platform is Macbook Pro(M1) which os version is 12.4.
It seems that there are differences between macos ld and windows ld.
If I input ld -o basic.bin -Ttext 0x0 --oformat binary basic.o, I will get feedback as below.
ld: unknown option: -Ttext
What command about ld I need to convert basic.o to basic.bin on my platform?
Yes they are different, in fact gcc will be just a symlink to clang unless explicitly installed and told otherwise.
As for the -T option, I'm not too sure if you know what it means(if you don't, it will basically tell the linker to put the text section at address 0x0).
You might want to invest the time and look for how to build a linkerScript because you will probably going to use that later anyway.
How to build a linkerScript
If the link is dead at some point, you can just google "linkerScript ld" and it will show some tutorials.

Split generation of a Shared Object library (.so) file into two pass instead of one

I am trying to build a shared library lib_test.so from 'test.c' & test.exp files. This lib_test.so file will be used as a extension to another application.
The application doc specifies generation of tle lib_test.so file directly in a single pass by the following command:
`gcc -q64 -o lib_test.so test.c -bM:Sre -bE:test.exp -bnoentry`
But my requirement is to build the library in two passes:
Compile to generate test.o file using gcc command.
Link to generate the library lib_test.so using ld command.
I tried this as follows:
Executed compile step as follows: gcc -q64 -c -o test.o test.c.
Create lib_test.so as follows: ld -bM:Sre -bE:test.exp -bnoentry -o lib_test.so test.o
But it is not generating a proper lib_test.so file.
I am using Ubuntu 16.04 LTS 64-Bit with latest GCC
Can you please suggest the correct way to split the process into two passes...
Thanks & Regards.
You rarely ever want to use ld to perform the linking. The gcc frontend does the better job of setting the right flags etc. So, use gcc.
i.e. Instead of
ld -bM:Sre -bE:test.exp -bnoentry -o lib_test.so test.o
do
gcc -bM:Sre -bE:test.exp -bnoentry -o lib_test.so test.o
in your second step.
The big difference between linking with the GCC frontend program gcc and with the actual linker ld is that the GCC frontend adds a few libraries to be linked with. Most notably the GCC runtime library (-lgcc_s or -lgcc) and the actual standard C library (-lc).
When you invoke ld directly you do not tell it to link with those libraries.
There might also be other libraries and flags the GCC frontend passes to ld without your knowledge. For the "one pass" build, pass the flag -v to gcc for verbose output and see what arguments, flags and libraries it uses.

How to install 32 bit glibc on 64 bit ubuntu

I am trying to learn the C Calling conventions in assembly language. To do so, I made a simple program using the puts function from the C standard library.
I assembled and linked the program with the following commands :-
nasm -f elf file.asm
gcc -m32 file.asm -o file
The nasm produces the right object file but when running the gcc to link the object files, I am getting error.
Looking at the error I have figured it out that I don't have the 32 bit version of glibc on my system. How can I install it. I already have installed this package installed.
I have 64 bit ubuntu 12.04 as my OS.
EDIT :- I have installed the following packages, but the problem is still not solved :-
1)ia32-libs
2) libc6-i386
This command will install the 32bit glibc libraries on 64 bit Ubuntu:
sudo apt-get install gcc-multilib
This is the proper syntax for linking assembly object code into an executable using gcc:
gcc -m32 objectfile.o -o executablefile
(nasm -felf32 already creates objectfile.o; the .asm file should not appear on GCC's command line. GCC can assemble+link a .S file in one step using GAS syntax, but NASM is a separate package.)
I assembled and linked the program with the following commands :-
nasm -f elf file.asm
gcc -m32 file.asm -o file
This is wrong. Your first nasm command is probably creating a file.o file (and you should check that, e.g. with ls -l file.o). The second gcc command does not do what you wish.
But gcc does not know about *.asm file extensions (it knows about .S for preprocessable GNU assembler syntax, and .s for assembler code, but probably handle unknown extensions like .asm as ELF object files by default, however file.asm is not an ELF object file). You should try linking with
gcc -Wall -v -m32 file.o -o file
Notice that you give to GCC an object file in ELF (for the linker invoked by gcc) which you previously produced with nasm.
(you might later remove the -v option to gcc)
Alternatively, use the GNU as assembler syntax (not the nasm one), name your file file.S (if you want it to be preprocessed) or file.s (without preprocessing) and use gcc -v -Wall -m32 file.s -o myprog to compile it.
BTW, to understand more about calling conventions, read the x86-64 ABI spec (and the similar one for 32 bits x86 ...), make a small C example file some-example.c, then run gcc -S -fverbose-asm -O some-example.c and look into the produced some-example.s with an editor or pager.
Learn also more about ELF then use readelf (& objdump) appropriately.
You want to install a package called 'ia32-libs'

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

Resources