Search code examples
cmacosassemblyarmclang

Statically loaded C to aarch64 Assembly, for learning purposes


I want to learn assembly, and I figured that compiling C code to assembly would be a good start. That is to say: cc main.c -S which compiles

#include <stdio.h>

int main() {
    printf("Hello, World!\n");
}

to

    .section    __TEXT,__text,regular,pure_instructions
    .build_version macos, 14, 0 sdk_version 14, 5
    .globl  _main                           ; -- Begin function main
    .p2align    2
_main:                                  ; @main
    .cfi_startproc
; %bb.0:
    stp x29, x30, [sp, #-16]!           ; 16-byte Folded Spill
    .cfi_def_cfa_offset 16
    mov x29, sp
    .cfi_def_cfa w29, 16
    .cfi_offset w30, -8
    .cfi_offset w29, -16
    adrp    x0, l_.str@PAGE
    add x0, x0, l_.str@PAGEOFF
    bl  _printf
    mov w0, #0
    ldp x29, x30, [sp], #16             ; 16-byte Folded Reload
    ret
    .cfi_endproc
                                        ; -- End function
    .section    __TEXT,__cstring,cstring_literals
l_.str:                                 ; @.str
    .asciz  "Hello, World!\n"

.subsections_via_symbols

... As you can see, I'm running on aarch64. But that is not the problem:

as main.s still no problem. But when I do ld a.out:

Undefined symbols for architecture arm64:
  "_printf", referenced from:
      _main in a.out
ld: symbol(s) not found for architecture arm64

Now, I already found a solution for this on some obscure, almost completely unrelated blog post, so:
ld a.out -L /Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/usr/lib/ -lSystem

and now it works.

$ ./a.out
Hello, World!

So here the question: how am I supposed to know how _printf is defined? I mean, I already know how to do a syscall and write to stdout, but for anything more complicated, this will become such a chore. The whole point of this is for me to learn Assembly, but dynamic linking is making this very difficult. If I could statically link it, it would make things so much easier. Is there a compiler that allows this? By the way, I'm using clang:

$ cc --version
Apple clang version 15.0.0 (clang-1500.3.9.4)
Target: arm64-apple-darwin23.5.0
Thread model: posix
InstalledDir: /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin

I tried as follows:

$ cc main.c -static
ld: library not found for -lcrt0.o
clang: error: linker command failed with exit code 1 (use -v to see invocation)

and

$ cc main.c -lstatic
ld: library 'static' not found
clang: error: linker command failed with exit code 1 (use -v to see invocation)

and

$ cc main.c -static -L /Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/usr/lib/
ld: library not found for -lcrt0.o
clang: error: linker command failed with exit code 1 (use -v to see invocation)

and when I do

$ cc main.c -static -S

-static is just being ignored.

I tried a lot of other things; some I don't don't remember, some I can't be bothered to write down. You get the gist of it. Please help.


Solution

  • You can disassemble a static object file by doing this:

    gcc -march=native -static main.c -o main.o 
    objdump -drwC -Mintel main.o > main.s