Search code examples
cgcclinkerld

Linking an object file to the C standard library with ld


Say I'm trying to create an executable binary from:

// main.c

#include <stdio.h>

int main() {
    printf("Hello, world!");
    return 0;
}

I can just run gcc main.c and I get the executable binary a.out that prints `Hello, world!", as desired.

I've been told that gcc did two things here:\

  1. Compiled main.c to a object code, and
  2. Linked that code with the stardard library so that printf works.\

How can I manually accomplish these two steps manually, one-by-one?

I start compiling main.c to object code by running gcc -c main.c, creating the object file main.o. Now I'd like to do the linking step. It seems I can do this, also with gcc, by simply running gcc main.o, producing the executable binary a.out.

I've read that this second invocation of gcc is actually running the linker ld under the hood. How can I do this with ld directly, instead of with a second invocation of gcc? If I run ld main.o, I get a warning and an error:

ld: warning: cannot find entry symbol _start; defaulting to 00000000004000b0
ld: main.o: in function `main':
main.c:(.text+0x10): undefined reference to `printf'

The error seems to suggest that I need to tell it to link to the C standard library so I can use printf. I can with the -lc flag ld main.o -lc. This produces a.out, but I still get the warning:

ld: warning: cannot find entry symbol _start; defaulting to 00000000004002e0

And when I run a.out, it prints Hello, world! all over the screen, forever:

Hello, world!Hello, world!Hello, world!Hello, world!Hello, world!Hello, world!Hello, world!Hello, world!Hello, world!Hello, world!Hello, world!Hello, world!Hello, world!Hello, world!Hello, world!Hello,

What's the correct way to invoke ld here to eliminate the warning and get the correct executable?


Solution

  • What's the correct way to invoke ld here to eliminate the warning and get the correct executable?

    The only correct way to link a user-space C binary is by using the appropriate compiler front end: gcc main.o.

    Using ld directly is never correct.

    You can see what gcc actually does by invoking gcc -v main.o. Note however that the resulting ld (or collect2 command):

    • may differ depending on how your gcc was configured
    • may differ between different OS releases
    • may differ depending on which libc is being used

    TL;DR: you are welcome to learn how gcc invokes ld on your system, but you should not put the result into any Makefile or a build script.