Loadable Bash Builtin - c

I'm writing a strcmp bash builtin. It compiles fine, but when I try to enable it, I get:
$ enable -f ./strcmp strcmp
bash: enable: cannot open shared object ./strcmp: ./strcmp: only ET_DYN and ET_EXEC can be loaded
The big parts of my builtin:
strcmp_builtin (list)
WORD_LIST *list;
char *strcmp_doc[] = {
(char *)NULL
};
struct builtin strcmp_struct = {
"strcmp", /* builtin name */
strcmp_builtin, /* function implementing the builtin */
BUILTIN_ENABLED, /* initial flags for builtin */
strcmp_doc, /* array of long documentation strings. */
"strcmp 'string 1' 'string 2'", /* usage synopsis; becomes short_doc */
0 /* reserved for internal use */
};
The compile line (from the expanded make file):
~/bash-4.2/examples/loadables $ gcc -fPIC -DHAVE_CONFIG_H -DSHELL \
-g -O2 -I. -I.. -I../.. -I../../lib -I../../builtins -I../../include \
-I~/bash-4.2 -I~/bash-4.2/lib -I~/bash-4.2/builtins -c \
-o strcmp strcmp.c
I have googled ET_DYN and ET_EXEC, and only found links to questions like this.

Did you notice the -c flag? That's keeping it from linking. Replace that with -shared, as #shr mentioned

A little follow up on an answer to my specific question (ET_DYN and ET_EXEC), as well as an expansion on WHY #stanparker's answer is correct.
ET_DYN and ET_EXEC are ELF Executable types. In the ELF header, there is a field Elf32_Half e_type or Elf64_Half e_type with several possibly ET_* enums. I'm guessing ET is "Executable Type", EXEC executable and DYN Dynamic. This should all be enough to show that the emitted artifact was not, in fact, an executable object of any kind, and should have encouraged looking closer at the GCC flags. (For more on the ELF headers, http://www.sco.com/developers/gabi/1998-04-29/ch4.eheader.html)
Now that we see we aren't linking, let's remove the -c flag. Then, we'll get a second error (this time somewhere in the compile),
$ gcc [...] -o strcmp.o strcmp.c
/usr/lib/gcc/i686-redhat-linux/4.6.0/../../../crt1.o: In function `_start':
(.text+0x18): undefined reference to `main'
/tmp/ccaiMtdc.o: In function `strcmp_builtin':
~/bash-4.2/examples/loadables/strcmp.c:32: undefined reference to `make_builtin_argv'
collect2: ld returned 1 exit status
There are actually two errors here. The first is "undefined reference to `main'", the second is "undefined reference to `make_builtin_argv'" (a bash internal function). The last line is enough to show us GCC is dying during linking. The function _start is a common entry point defined by glibc, which itself actually calls main in the program. At this point, duh, we aren't making an executable, but a shared lib. Adding -shared to the command line gets us compiling perfectly.
So, why wasn't make giving me the "Correct" command line? Makefile.in doesn't dynamically test for source files, so I should have added the .c and .o targets by hand, then rerunning ./configure. After doing so, we get
$ make strcmp
gcc [...] -c -o strcmp.o strcmp.c
gcc -shared -Wl,-soname,strcmp -L./lib/termcap -o strcmp strcmp.o
Does it work?
$ enable -f ./strcmp strcmp
$ strcmp "hi" "ho"
$ echo $?
2
$ strcmp "hi" "ha"
$ echo $?
1
$ strcmp "hi" "hi"
$ echo $?
0
That's what I expect it to do, so it seems it does work.
Anyway, the point of me writing this up is that this is not the first time I've personally gotten messed up on GCC and C compiling in general. This is not the first time I've seen someone have these problems in general. There is a tremendous amount of work that goes into getting a successful C compile, and each phase is important. So, I'm writing this up to remind myself of exactly what GCC (cc, ld, and the elf libs) were doing, why the one little character here and there is important, and the overall discovery process. I haven't seen it typed up anywhere else, so here is what I have.
PS. For those interested in this builtin, it will be on my site, http://davidsouther.com/2011/08/bash-strcmp-builtin/

I'm not sure, but... Ever tried gcc -shared ?

Related

OCaml: Issues linking C and OCaml

I am able to wrap C code and access it from the OCaml interpreter, but cannot build a binary! I'm 98% sure it is some linking problem, but can't find the tools to explore the linkage.
Getting even to this point was a chore, (endless quantities of Error: The external function is not available messages) so I'll document everything I did.
A 'system' file stuff.c
#include <stdio.h>
int fun(int z) // Emulate a "real" subroutine
{
printf("duuude whoa z=%d\n", z);
return 42;
}
Compile above as
cc -fPIC -c stuff.c
ld -shared -o libstuff.so stuff.o
An OCaml wrapper around above, in ocmstuff.c:
#include <caml/mlvalues.h>
CAMLprim value yofun(value z) {
return Val_long(fun(Long_val(z)));
}
Build above as
cc -fPIC -c ocmstuff.c
ld -shared -o dllostuff.so ocmstuff.o -L . -lstuff -lc -rpath .
Yes, the rpath really is needed, else the next steps suffer. (Edit: If you don't use rpath, you'll need to use LD_LIBRARY_PATH=. instead. For the final 'production' version, you'd change the rpath to the actual library path, or do ld.so.conf trickery or install into 'standard' locations, or tell your users about LD_LIBRARY_PATH. This is just like what you'd do for any other system. The rpath solution seems to be the most stable and reliable solution.)
Next, a module declaration, stored in fapi.mli
module Fapi : sig
external ofun : int -> int = "yofun" ;;
end
Build above as:
ocamlc -a -o fapi.cma -intf fapi.mli -dllib -lostuff
Does it work? Yes it does:
$ rlwrap ocaml fapi.cma
OCaml version 4.11.1
open Fapi ;;
Fapi.ofun 33 ;;
duuude whoa z=33
- : int = 42
#
So the wrapper works fine. Now lets compile with it. Here's myprog.ml:
open Fapi ;;
Fapi.ofun 33 ;;
Compile it:
ocamlc -c myprog.ml
ocamlc -o myprog myprog.cmo fapi.cma
The very last command spews:
File "_none_", line 1:
Error: Required module `Fapi' is unavailable
I am 98% sure the above error is due to some silly linking error, but I cannot track it down. Why do I think this? Well, here's a related problem that provides a hint.
$ rlwrap ocaml
open Fapi ;;
# Fapi.ofun 33 ;;
Error: The external function `yofun' is not available
#
Well, that's odd. It clearly must have found fapi.cma because that is the only way it can know about yofun. But somehow, it doesn't know it needs to dig into dllostuff.so for that. Or possibly dllostuff.so is failing to correctly link/load libstuff.so ? Or maybe libc.so to get printf ? I'm pretty sure its one of these last few, but I just can't get it to work, and don't have the tools to debug it. (nm and ldd -r look healthy. Are there some similar tools for the assorted cma,cmo,cmi,cmx files?)
Interfacing with C is much easier if you use dune. You don't need to know the low-level details it is all handled for you.
Now, back to your example. This is definitely not how OCaml users are interfacing with C, but if you really want to learn about it here are a few notes.
The reason why you have the error is that:
you specified modules in an incorrect order, it should be topological, not reverse topological order, i.e., the dependency comes before dependent
you do not have the .ml file (the -intf option means very different)
The reason why the last snippet doesn't work is because you're not loading the library. The ocaml binary obviously doesn't have any fapi units linked into it, so you have to explicitly load it using either #load directive or by passing it in the command line.
Also the following line is not necessary,
ld -shared -o dllostuff.so ocstuff.o -L . -lstuff -lc -rpath .
First of all, there is no need to link a stub file into a shared library. It is counterproductive and doesn't really bring you anything. Second, passing -rpath . will render the end executable unusable, unless the shared objects are stored in the same folder as the executable. Just remove this.
Just to complete your exercise, here is how it could be built and run. First, let's fix the stub file. We need the ml file and we also need to remove an extra module definition,
$ cat fapi.{ml,mli}
external ofun : int -> int = "yofun" ;;
external ofun : int -> int = "yofun" ;;
Yes, they are the same. The mli file is not really needed here, but let's keep it for the sake of completeness.
The way how you build the pure C part is fine, as long as you get a relocatable .so file it works.
Now to build the ocstuff.c (which we conventionally call stubs) you just need to do,
ocamlc -c ocstuff.c
Don't turn it into a shared library, don't do anything else with it. Now let's build the fapi library,
ocamlc -c fapi.mli
ocamlc -c fapi.ml
Now let's build the library that contains both OCaml and C code,
ocamlmklib -o fapi fapi.cmo ocstuff.o -lstuff -L.
Now we can finally build the executable,
ocamlc -c myprog.ml
LD_LIBRARY_PATH=. ocamlc -o myprog fapi.cma myprog.cmo
and run it,
LD_LIBRARY_PATH=. ./myprog
duuude whoa z=33
Notice that we have to use the LD_LIBRARY_PATH to tell the system dynamic loader where to look for the external dependency libstuff.so. You can, of course, use rpath to specify its location (pass it to ocamlmklib via -ccopt) but in general it is assumed that the external library is installed at some location that the system loader knows.
Again, unless you're developing your own build system, please use dune or oasis for building OCaml programs. These systems will handle all low-level details in the best possible way.
P.S. It is also worth mentioning that you're not building a binary, but a bytecode executable. For binaries, you will have to use the ocamlopt compiler. And this would be a completely different story. Again, dune is the solution.
There is a lot to take in here, but these lines are suspicious:
ocamlc -c myprog.ml
ocamlc -o myprog myprog.cmo fapi.cma
OCaml expects modules in topologically sorted order, with a module appearing on the command line before the modules that refer to it.
So it would seem the last line should be this:
ocamlc -o myprog fapi.cma myprog.cmo
I hope this helps, it's just a quick response.
The answer provided by ivg works. It also provides enough hints to retrofit the original question to get the correct behavior. The changes to the original recipe are:
Create fapi.mli and fapi.ml which both have the same content: external ofun : int -> int = "yofun" ;;
Compile both the above with ocaml -c. The mli must be compiled first: it yields an interface file cmi which is needed before the ml file can be compiled into it's object file cmo.
The name dllostuff.so was wrong: it must be dllfapi.so to maintain naming consistency.
Build the cma archive/library as ocamlc -a -o fapi.cma fapi.cmo -dllib -lfapi
That's it! Other than these, the original instructions work. The answer from ivg suggests using
ocamlmklib -o fapi fapi.cmo ostuff.o -L. -lstuff
instead of
ld -shared -o dllfapi.so ostuff.o -L. -lstuff
Either of these work. The primary difference is that ocamlmklib also creates a static-linked library libfapi.a. Other than that, it creates the dllfapi.so as before. (That version also contains a motley assortment of typical gcc symbols, for handling exceptions, library ctors, etc. It's not clear why these are needed here, since they'll show up sooner or later anyway.)

dietlibc, lowfat, opentracker - compiling against alternative libc

I'm attempting to build opentracker. My system has the following:
| package | library | headers |
| lowfat | /usr/lib/libowfat.a | /usr/include/libowfat |
| dietlibc | /opt/diet/lib-x86_64/*.a | /usr/diet/include |
| glibc | /usr/lib/*.{a,so} | /usr/include |
Looking at the Makefile for opentracker, I see (essentially) the following:
PREFIX?=..
LIBOWFAT_HEADERS=$(PREFIX)/libowfat
LIBOWFAT_LIBRARY=$(PREFIX)/libowfat
CFLAGS+=-I$(LIBOWFAT_HEADERS) -Wall -pipe -Wextra
LDFLAGS+=-L$(LIBOWFAT_LIBRARY) -lowfat -pthread -lpthread -lz
opentrackers: $(OBJECTS) $(HEADERS)
cc -o $# $(OBJECTS) $(LDFLAGS)
I've not compiled against an alternative libc before, so I'm including this information in case I've done this part wrong. When I invoke make, I need to point it at where my system has dietlibc and lowfat live. I'm doing it like this:
$ LDFLAGS=-L/opt/diet/lib-x86_64 make PREFIX=/opt/diet LIBOWFAT_HEADERS=/usr/include/libowfat LIBOWFAT_LIBRARY=/usr/lib
...
...
cc -o opentracker opentracker.o trackerlogic.o scan_urlencoded_query.o ot_mutex.o ot_stats.o ot_vector.o ot_clean.o ot_udp.o ot_iovec.o ot_fullscrape.o ot_accesslist.o ot_http.o ot_livesync.o ot_rijndael.o -L/opt/diet/lib-x86_64 -L/usr/lib -lowfat -pthread -lpthread -lz
/usr/bin/ld: /usr/lib/libowfat.a(io_fd.o):(.bss+0xb0): multiple definition of `first_deferred'; /usr/lib/libowfat.a(io_close.o):(.data+0x0): first defined here
...
... lots of warnings ...
/usr/bin/ld: opentracker.o: undefined reference to symbol '__ctype_b_loc##GLIBC_2.3'
/usr/bin/ld: /usr/lib/libc.so.6: error adding symbols: DSO missing from command line
Looks like there's two issues going on in there.
Multiple definitions of first_deferred
I see references to first_deferred in both io_close and io_fd, but they are in different sections.
$ objdump -t /usr/lib/libowfat.a | egrep '^[^:]+.o:|first_deferred' | grep -B1 first_deferred
io_close.o: file format elf64-x86-64
0000000000000000 g O .data 0000000000000008 first_deferred
--
io_fd.o: file format elf64-x86-64
00000000000000b0 g O .bss 0000000000000008 first_deferred
--
io_waituntil2.o: file format elf64-x86-64
0000000000000000 *UND* 0000000000000000 first_deferred
In io/io_fd.c, there's an #include io_internal.h and in that header there's an extern long first_deferred;. In io/io_close.c it's defined as long first_deferred=-1. So it doesn't look like it's double defined in the libowfat code itself. Did I compile lowfat wrong?
DSO missing from command line / symbol '__ctype_b_loc##GLIBC_2.3'
Since the Makefile is trying to compile against dietlibc, I'm a bit surprised that there's a reference to glibc (but, to be honest, also not surprised at all).
Here's the recipe for opentracker.o:
cc -c -o opentracker.o -march=x86-64 -mtune=generic -O2 -pipe -fno-plt -I/usr/include/libowfat -Wall -pipe -Wextra -O3 -DWANT_FULLSCRAPE opentracker.c
This doesn't appear to have the -L/opt/diet/lib-x86_64 argument from LDFLAGS that is used for the main executable. Should it? I don't think so as that's a linker argument so it would not make sense to add it to the compile command. I don't see any references to glibc in the object file:
$ objdump -t ./src/opentracker/opentracker.o | grep -c 'glib'
0
DSO missing from command line / symbol '__ctype_b_loc##GLIBC_2.3'
I found two permutations to solve this issue. Option one is to make sure the very first -L argument is the location of dietlibc's lib directory, so that all symbols are resolved from there first.
The other permutation was to invoke make via the /opt/diet/bin/diet wrapper program. From the dietlibc FAQ
Q: How do I install it? make install?
A: Yep. It will then install itself to /opt/diet, with the wrapper in
/opt/diet/bin/diet. Or you don't install it at all.
The diet libc comes with a wrapper called "diet", which can be found
in bin-$(ARCH)/diet, i.e. bin-i386/diet for most of us. Copy this
wrapper somewhere in your path (for example ~/bin) and then just
compile stuff by prepending diet to the command line, e.g. "diet gcc
-pipe -g -o t t.c".
Q: How do I compile programs using autoconf with the diet libc?
A: Set CC in the environment properly. For Bourne Shells:
$ CC="diet gcc -nostdinc" ./configure --disable-nls
That should be enough, but you might also want to set
--disable-shared and --enable-static for packages using libtool.
It's not explained anywhere on the website, as far as I can tell, what the wrapper program does. The code is annoying to read due to all the architecture specific #ifdefs, but the file comment indicates it just modifies the gcc command line in an architecture specific way. A quick scan suggests relevant args modifications include: -I/opt/diet/include when compiling, -nostdlib when linking, and possibly -Os.
Multiple definitions of first_deferred
I'm not happy with my workaround here. The symbol is defined in io_internal.h:
#ifndef my_extern
#define my_extern extern
#endif
my_extern long first_deferred;
Why is there a funny redefinition of the extern keyword? Read on. The initialization of this variable is in io_close.c:
#include "io_internal.h"
long first_deferred=-1;
And here's the interesting bit. In io_fd.c:
#define my_extern
#include "io_internal.h"
#undef my_extern
Why? Who knows. The author believes they are clever I guess and saved themselves some keystrokes? The effect of this is that my_extern is defined as an empty string, so when my_extern long first_deferred; is transcluded from the header, it appears as long first_deferred;. This is what leads there to be two locations for the symbol in the archive, as there are two files that reserve space for that symbol.
I'm not happy with my "solution", which was to remove the static initialization from io_close.c. Technically, that means the variable starts with random heap memory. A quick look at how it gets used suggests this is maybe not safe, but is probably safe enough. The variable is used as an index into an array. Thankfully iarray_get does a bounds check, so it's very likely that if(e) will be false and the variable will get set to -1 as it should be.
if (first_deferred!=-1) {
while (first_deferred!=-1) {
io_entry* e=iarray_get(&io_fds,first_deferred);
if (e) {
if (e->closed) {
e->closed=0;
close(first_deferred);
}
first_deferred=e->next_defer;
} else
first_deferred=-1; // can't happen
}
}
I can't provide a good explanation for those errors, but your post helped me to get it to compile so I figured I'd mention what I did.
The "first_deferred" error seems to come from using a newer version of libowfat, I got past that by using 0.31 instead.
I didn't come across the second error, but I was getting "__you_tried_to_link_a_dietlibc_object_against_glibc" errors which I got past by uninstalling dietlibc and compiling libowfat with glibc instead.
I compiled them the same way as the AUR packages:
https://aur.archlinux.org/packages/opentracker/
https://aur.archlinux.org/packages/libowfat/
Although, instead of installing libowfat, I just put it in the src directory and skipped fetching libowfat from CVS.

gcc ld linker issues [Need Help] [duplicate]

I'm trying to understand C compilation in a little more depth, and so I'm compiling and linking "manually". Here is my code
int main()
{
return 0;
}
And here is what I'm putting into my console (Windows):
gcc -S main.c
as main.s -o main.o
ld main.o
And when trying to link, I get:
main.o:main.c:(text+0x7): undefined reference to `__main'
You didn't link any of the necessary support libraries. C global objects like stdin, stdout, stderr don't just appear from nowhere. Command arguments and environment variables are pulled from the operating system. And on exit all those atexit() functions get called and the return code from main is passed to exit(return_code). Etc.
Check out the commands gcc -dumpspecs, gcc -print-libgcc-file-name. Look at all of the other libraries in that directory. You'll find a lot of those libraries and object files referenced in the output of dumpspecs. I don't know exactly when or how those spec rules are interpreted but you can probably get the idea. And I think the GCC info pages info gcc explain it in detail if you dig in far enough.
info gcc and then press 'g' and then enter 'Spec Files'
And as Jonathan Leffler said, the shortcut is to run gcc with the verbose option: gcc -v and just see what commands it used.

gcc without optimization gives error (but I have to omit -o to enable gdb functions)

I want to compile my c code without -o optimization (like $ gcc -g test test.c),I don't think the code is causing the problem because I tested simple code like the following and it won't work either:
#include<stdio.h>
main()
{
printf("hello\n");
}
and I got a whole bunch of errs like:
test: In function `_start':
(.text+0x0): multiple definition of `_start'
/usr/lib/i386-linux-gnu/gcc/i686-linux-gnu/4.5.2/../../../crt1.o:(.text+0x0): first d efined here
test:(.rodata+0x0): multiple definition of `_fp_hw'
/usr/lib/i386-linux-gnu/gcc/i686-linux-gnu/4.5.2/../../../crt1.o:(.rodata+0x0): first defined here
test: In function `_fini':
(.fini+0x0): multiple definition of `_fini'
/usr/lib/i386-linux-gnu/gcc/i686-linux-gnu/4.5.2/../../../crti.o:(.fini+0x0): first defined here
test:(.rodata+0x4): multiple definition of `_IO_stdin_used'
/usr/lib/i386-linux-gnu/gcc/i686-linux-gnu/4.5.2/../../../crt1.o:(.rodata.cst4+0x0): first defined here
test: In function `__data_start':
(.data+0x0): multiple definition of `__data_start'
/usr/lib/i386-linux-gnu/gcc/i686-linux-gnu/4.5.2/../../../crt1.o:(.data+0x0): first defined here
I googled and found that is because I didn't put -o there. But I have to, otherwise commands like "print var" wouldn't work on gdb. Is there anyway I can get around with this?
Thank you!
What you did is the following:
gcc -o test test.c
Which creates an executable test (-o means the output goes to the argument, in this case test). For optimisations you use -O (a uppercase O).
Now you do:
gcc -g test test.c
Which tells gcc to compile using the -g switch (enable debug symbols) and to use the files test and test.c for compilation. So your error messages arise by gcc trying to compile the executable file test, which obviously is no C source file. You would get a similar result just by typing:
gcc test
Instead run
gcc -g -o test test.c
as already suggested in the comments.
You're thinking of the -O flags, (capital letter "O"), which control optimization. The -o flag just gives your executable a name. If you don't specify that flag, your executable automatically gets named a.out
I am thinking you might have accidentally typed something like
gcc -g test.c test.c
which would cause duplicate symbol errors like the ones you showed above. The correct gcc invocation would be:
gcc -g -o test test.c
If you are looking to get maximum debugging information try -ggdb3 instead of -g
the default optimization level is -O1 if you want to disable optimizations entirely,
you must also pass -O0 to gcc, that is the letter capital O and the number zero.

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