83

I want to compile my C-code without the (g)libc. How can I deactivate it and which functions depend on it?

I tried -nostdlib but it doesn't help: The code is compilable and runs, but I can still find the name of the libc in the hexdump of my executable.

u149796
  • 1,788
  • 4
  • 14
  • 19
  • 2
    `-nostdlib` should do it, what platform/compiler version are you using? – Carl Norum Mar 30 '10 at 20:36
  • "doesn't help" as in that didn't disable the library, or you couldn't compile anything with that flag? – Josh Lee Mar 30 '10 at 20:47
  • 4
    You probably also want -nostartupfiles. –  Mar 30 '10 at 22:03
  • 8
    There is no -nostartupfiles option. You probably mean -nostartfiles, which is already implied by -nostdlib. – ataylor Mar 31 '10 at 18:24
  • 1
    Here is an excellent article on optimizing elf binaries which covers stripping stdlib off executables: [A Whirlwind Tutorial on Creating Really Teensy ELF Executables for Linux ](http://www.muppetlabs.com/~breadbox/software/tiny/teensy.html) – Michał Trybus Apr 04 '10 at 15:12
  • 4
    http://blog.ksplice.com/2010/03/libc-free-world/ has a very good description of precisely controlling the gcc's programatic output. Edit: They (ksplice) just put out part 2 of the above tutorial/guide. See it here: http://blog.ksplice.com/2010/04/libc-free-world-2/ This mostly deals with linker settings to remove unnecessary fluff from files. – Adam Shiemke Mar 31 '10 at 01:18

2 Answers2

102

If you compile your code with -nostdlib, you won't be able to call any C library functions (of course), but you also don't get the regular C bootstrap code. In particular, the real entry point of a program on Linux is not main(), but rather a function called _start(). The standard libraries normally provide a version of this that runs some initialization code, then calls main().

Try compiling this with gcc -nostdlib -m32:

// Tell the compiler incoming stack alignment is not RSP%16==8 or ESP%16==12
__attribute__((force_align_arg_pointer))
void _start() {

    /* main body of program: call main(), etc */

    /* exit system call */
    asm("movl $1,%eax;"
        "xorl %ebx,%ebx;"
        "int  $0x80"
    );
    __builtin_unreachable();  // tell the compiler to make sure side effects are done before the asm statement
}

The _start() function should always end with a call to exit (or other non-returning system call such as exec). The above example invokes the system call directly with inline assembly since the usual exit() is not available.

Peter Cordes
  • 328,167
  • 45
  • 605
  • 847
ataylor
  • 64,891
  • 24
  • 161
  • 189
  • 14
    For 64 bit, the assembly code has to look like this: `asm("mov rax,60; mov rdi,0; syscall")`. – sigalor May 03 '16 at 18:03
  • 10
    Adding to @sigalor's comment, to compile with `gcc` you will need to use AT&T syntax so this should look like the following: `asm(mov $60,%rax; mov $0,%rdi; syscall)` – lanoxx May 11 '16 at 11:59
  • 3
    @ataylor: why _start() function should always end with call to exit() ? What if I don't write exit() in start() function ? – Destructor May 25 '17 at 05:52
  • 4
    @Destructor on my system, it hangs for a few seconds, then segfaults. – Moonchild Aug 15 '17 at 23:18
  • ```$ gcc -nostdlib main.c Undefined symbols for architecture x86_64: "_main", referenced from: implicit entry/start for main executable ld: symbol(s) not found for architecture x86_64 clang: error: linker command failed with exit code 1 (use -v to see invocation) ``` – Mike Bell May 02 '18 at 23:59
  • You have to combine -nostdlib with -nostartfiles (now?), in my experience, to get the magic stripped-down no-main experience. – SilverbackNet Aug 01 '18 at 02:34
  • @Destructor, from the disassembly code by `objdump -dS exe_file`, `080480d8 <_start>: .globl _start _start:call main 80480d8: e8 00 00 00 00 call 80480dd
    080480dd
    : int main() { 80480dd: 55 push %ebp ..... 80480ef: c9 leave 80480f0: c3 ret ` . After the `call`, the next op is 80480dd. If you don't want `exit()`, make another idea to skip `80480dd~80480f0`. `_exit()` does
    – gfan Oct 02 '18 at 13:30
  • To call this function anything other than `_start`, use `-e` linker option to set entry point: `gcc -nostdlib -Wl,-efunction1` – Ivan Feb 14 '20 at 07:51
  • 2
    Note that the stack is aligned differently on entry to `_start`: no return address on the stack so it's still 16-byte aligned. But GCC will assume that it's a normal function, so it will violate the ABI when it calls other functions. Use `-mincoming-stack-boundary=2` for that file [(docs)](https://gcc.gnu.org/onlinedocs/gcc/x86-Options.html) to tell GCC that the stack might only be 4-byte aligned on function entry, or maybe an `__attribte__((target("something")))` just on `_start` – Peter Cordes Jun 02 '20 at 12:59
  • Update: [How Get arguments value using inline assembly in C without Glibc?](https://stackoverflow.com/a/50283880) shows how to define a `_start` in C if you really want to abuse the compiler this way, using `__attribute__((force_align_arg_pointer))`. – Peter Cordes May 15 '22 at 22:38
  • I had to also specify "-no-pie" to get a proper executable binary, where `file` says "a.out: ELF 64-bit LSB executable, x86-64" instead of "a.out: ELF 64-bit LSB shared object, x86-64, version 1 (SYSV), dynamically linked" – masterxilo Apr 30 '23 at 00:58
13

The simplest way to is compile the C code to object files (gcc -c to get some *.o files) and then link them directly with the linker (ld). You will have to link your object files with a few extra object files such as /usr/lib/crt1.o in order to get a working executable (between the entry point, as seen by the kernel, and the main() function, there is a bit of work to do). To know what to link with, try linking with the glibc, using gcc -v: this should show you what normally comes into the executable.

You will find that gcc generates code which may have some dependencies to a few hidden functions. Most of them are in libgcc.a. There may also be hidden calls to memcpy(), memmove(), memset() and memcmp(), which are in the libc, so you may have to provide your own versions (which is not hard, at least as long as you are not too picky about performance).

Things might get clearer at times if you look at the produced assembly (use the -S flag).

Thomas Pornin
  • 72,986
  • 14
  • 147
  • 189
  • I have to use _start instead of main, but when I try to call a libc function gcc doesn't complain. Does the libc-link disappear if I remove all libc-calls? – u149796 Apr 01 '10 at 07:50
  • 3
    Not directly. If you try `gcc -v`, you will see that `gcc` gives some object files to the linker (the `*.o`). The linker includes all object files it is given. "Disappearance" occurs only with libraries (`*.a`) because they are repositories of object files that the linker is free to use or not to use. – Thomas Pornin Apr 01 '10 at 13:37