Search code examples
cmacosassemblyarm64libc

Using Standard C Library function on ARM64 macOS assembly doesn't work after linking


I'm on MacOS (Apple Silicon M4 processor, so, ARM64), and when trying to use puts from the standard C library:

    .global main                                                        // 1 
main:                                                                   // 2 
    stp     x21, x30, [sp, -16]!    // push onto stack                  // 3 
    mov     x21, x1                 // argc -> x0, argv -> x1           // 4 
                                                                        // 5 
top:                                                                    // 6 
    ldr     x0, [x21], 8            // argv++, old value in x0          // 7 
    cbz     x0, bottom              // if *argv == NULL goto bottom     // 8 
    bl      puts                    // puts(*argv)                      // 9 
    b       top                     // goto top                         // 10 
                                                                        // 11 
bottom:                                                                 // 12 
    ldp     x21, x30, [sp], 16      // pop from stack                   // 13 
    mov     x0, xzr                 // return 0                         // 14 
    ret                                                                 // 15 
                                                                        // 16 
    .end                       

It fails, and returns:

~/ clang -lc -arch arm64 test2.s -e main                                                                                               

Undefined symbols for architecture arm64:
  "puts", referenced from:
      main in test2-8965bb.o
ld: symbol(s) not found for architecture arm64
clang: error: linker command failed with exit code 1 (use -v to see invocation)

I have tried with the addition of the -lSystem flag, doesn't work, instead just returns a ld: warning: ignoring duplicate libraries: '-lSystem' warning.

Not sure what I did wrong, making it that I can't use libc (just in case, I have tried this:

#include <stdio.h>

int main(){
  puts("Hello, World!");
  return 0;
}

Whos assembly equivalent is:

    .section    __TEXT,__text,regular,pure_instructions
    .build_version macos, 15, 0 sdk_version 15, 2
    .globl  _main                           ; -- Begin function main
    .p2align    2
_main:                                  ; @main
    .cfi_startproc
; %bb.0:
    sub sp, sp, #32
    stp x29, x30, [sp, #16]             ; 16-byte Folded Spill
    add x29, sp, #16
    .cfi_def_cfa w29, 16
    .cfi_offset w30, -8
    .cfi_offset w29, -16
    mov w8, #0                          ; =0x0
    str w8, [sp, #8]                    ; 4-byte Folded Spill
    stur    wzr, [x29, #-4]
    adrp    x0, l_.str@PAGE
    add x0, x0, l_.str@PAGEOFF
    bl  _puts
    ldr w0, [sp, #8]                    ; 4-byte Folded Reload
    ldp x29, x30, [sp, #16]             ; 16-byte Folded Reload
    add sp, sp, #32
    ret
    .cfi_endproc
                                        ; -- End function
    .section    __TEXT,__cstring,cstring_literals
l_.str:                                 ; @.str
    .asciz  "Hello, World!"

.subsections_via_symbols

Which works just fine!)

Some help would be appreciated!


Solution

  • As @Shawn and @fuz mentioned, adding a prefixing _ is required for all C code built for the macOS ABI.

    Therefore, the corrected code would become:

        .global main                                                        // 1 
    main:                                                                   // 2 
        stp     x21, x30, [sp, -16]!    // push onto stack                  // 3 
        mov     x21, x1                 // argc -> x0, argv -> x1           // 4 
                                                                            // 5 
    top:                                                                    // 6 
        ldr     x0, [x21], 8            // argv++, old value in x0          // 7 
        cbz     x0, bottom              // if *argv == NULL goto bottom     // 8 
        bl      _puts                   // note that there is a `_`         // 9   
        b       top                     // goto top                         // 10 
                                                                            // 11 
    bottom:                                                                 // 12 
        ldp     x21, x30, [sp], 16      // pop from stack                   // 13 
        mov     x0, xzr                 // return 0                         // 14 
        ret                                                                 // 15 
                                                                            // 16 
        .end         
    

    Fixed part:

    bl    _puts  // with the addition of the leading `_`