Today, I want to show you how to link an object file produces with any compiled language into an executable using the LLVM linker lld.

I will limit myself to a unix operating system and linking using the musl C standard library. Steps for other systems will be similar.

Introduction to Linking

Many compilers such as GCC or Clang for C or rustc for Rust, parse the code and convert it to machine instructions, storing the result in object files. These files contain the function and structure definitions.

After this step is done, the compiler invokes the linker. The job of the linker is to take these object files, other object files coming from the libraries your code is using (for instance libc) and “links” the functions together, for instance ensuring that when your code calls a library function, the correct code is called.

The output of this operation is the final executable file. What I just described is plain static linking. I won’t discuss anything else here.

The linking step is usually performed by the compiler calling the linker, so it’s typically transparent to the user. For this reason, there isn’t so much information online on how to do it manually. But if you are developing your own compiled language, you might have to do this step manually, or at least understand how it’s done.

The linking step is also crucial to add small pieces of assembly that ensure the program can be started correctly and returns the right result to the invoking process.

Context & Prerequisites

In particular, I had this problem when writing my own compiled language using LLVM. I used llc to compile LLVM IR code into object files and I wanted to link this with muslc to be able to create my own standalone executables.

I will assume you already have some object files (ending with *.o) which contain a main function (the entrypoint to our program) and a copy of muslc compiled. If you want to get your own copy of the libc, just download the tarball from the project website and build it.

The compilation will generate certain files. The minimum we need is:

  • libc.a - The file containing all functionality such as prinf.
  • crt1.o, crti.o, crtn.o - These files contain some special assembly code that is called before the main function to initialize the C standard library and to pass input and output correctly between our program and the invoking process. You can find more information in the crt man page.

All these files are of course platform specific.

Manual Linking

To link your program run:

ld.lld <paths/to/objects_files> <path/to/library_files>
  -o <path/to/executable>

(of course replacing the placeholders within <>).

That is actually all you need to do!

It took me a while to figure this out, by checking how clang does it and trying to reduce the number of options to ld.lld to a minimum.