Compiling and Linking LLVM

I’m learning about LLVM and found the video Lauding a Language is Very Mid by Sheafification of G on YouTube, and I’m trying to reproduce initially the “Compiling our example” section.

This LLVM IR code is in square.ll:

define i64 @main(i64 %argc, ptr %argv) {
    %has_int = icmp eq i64 %argc, 2
    br i1 %has_int, label %compute_square, label %error

error:
    call i32 @puts(ptr @error.msg)
    ret i64 1

compute_square:
    ; char **x.offset = &argv[1]
    ; char * x.ptr = *x.offset
    %x.offset = getelementptr ptr, ptr %argv, i64 1
    %x.ptr = load ptr, ptr %x.offset
    %x = call i64 @strtoull(ptr %x.ptr, ptr null, i32 10)

    %x.sqr = mul i64 %x, %x
    call i32 (ptr, ...) @printf(ptr @print.i64, i64 %x.sqr)
    ret i64 0
}

@error.msg = private constant [21 x i8] c"Expected an integer.\00"
@print.i64 = private constant [6 x i8] c"%llu\0A\00"

declare i64 @strtoull(ptr, ptr, i32)
declare i32 @puts(ptr)
declare i32 @printf(ptr, ...)

I started with a temporary shell, in which I could get to the object code:

$ nix-shell -p llvm lld

[nix-shell]$ opt -O3 square.ll -S -o square.opt.ll

[nix-shell]$ llc -O3 square.opt.ll -filetype=obj -o square.o

[nix-shell]$ ls
square.ll  square.o  square.opt.ll

But then, G uses the linker and points to the c runtime:

gg@ubuntu:~$ ld.lld square.o /lib/x86_64-linux-gnu/crt1.o -L /lib/x86_64-linux-gnu/ -lc -o square.out

Of course, this file isn’t there in NixOs, so I created this shell.nix to find it:

$ cat shell.nix
with import <nixpkgs> {};
mkShell {
  NIX_LD_LIBRARY_PATH = lib.makeLibraryPath [
    stdenv.cc.cc
    openssl
  ];
  packages = with pkgs; [
    bat
    libllvm
    llvmPackages.bintools
  ];
  NIX_LD = lib.fileContents "${stdenv.cc}/nix-support/dynamic-linker";
  FOO_VAR = stdenv.cc;
}
$ nix-shell

[nix-shell:~]$ echo $NIX_LD
/nix/store/wb6rhpznjfczwlwx23zmdrrw74bayxw4-glibc-2.42-47/lib/ld-linux-x86-64.so.2

[nix-shell:~]$ ld.lld square.o /nix/store/wb6rhpznjfczwlwx23zmdrrw74bayxw4-glibc-2.42-47/lib/crt1.o -lc -o square.o

As you can see, I got it to work by copying the directory where $NIX_LD is located to point to crt1.o. Also -lc in this shell took care of the location of -L /lib/x86_64-linux-gnu/ by itself apparently.

How can I get the actual path to crt1.o into my shell.nix? Can this shell be further improved?

crt1.o is usually in ${stdenv.cc.libc}/lib so that should be trivial to get working.

Cool, I can add

LIBC_DIR = "${stdenv.cc.libc}/lib";

to my shell.nix and then link with

[nix-shell]$ ld.lld square.o $LIBC_DIR/crt1.o -lc -o square.out

However, I’d like to know how did you search for this key? (I know it’s a newbie’s question, that’s the point).

I’m not sure what you mean by “search” for the “key”. This is just how nixpkgs is designed. I personally know it from working with the stdenv and just exposure to nixpkgs. The manual for one references it in a few places. You can also see it in nix repl by doing:

nix-repl> :lf github:NixOS/nixpkgs

nix-repl> legacyPackages.${builtins.currentSystem}.stdenv.cc.libc

That will print the derivation itself, if you add :b to it then nix will fetch or build it. Then just copy the path for the out output and run ls or whatever tools in bash to look at it.