Search code examples
linkercpu-registersarm64apple-silicon

Linker error using program counter on Apple Silicon


Following the HelloSilicon tutorial on ARM assembly for Apple Silicon I decided to play around with the general purpose registers and with the program counter.

When feeding the source to the linker with any of the registrers ("R1", "R2"... ) or "PC" ("R15") I got the following linker error:

% ld -o cicle cicle.o -lSystem -syslibroot `xcrun -sdk macosx --show-sdk-path` -e _start -arch arm64
Undefined symbols for architecture arm64:
  "PC", referenced from:
      _start in cicle.o
ld: symbol(s) not found for architecture arm64

The source is:

.global _start          // Provide program starting address to linker
.align 2            // Make sure everything is aligned properly

// Setup the parameters to print hello world
// and then call the Kernel to do it.
_start: 
label:
    mov X0, #1      // 1 = StdOut
    adr X1, helloworld  // string to print
    mov X2, #13         // length of our string
    mov X16, #4     // Unix write system call
    svc #0x80       // Call kernel to output the string
    b   PC          //Expecting endless loop
    //b PC-6        //Expecting endless "Hello World" loop

    
// Setup the parameters to exit the program
// and then call the kernel to do it.
    mov     X0, #0      // Use 0 return code
    mov     X16, #1     // System call number 1 terminates this program
    svc     #0x80       // Call kernel to terminate the program

helloworld:      .ascii  "Hello World!\n"

I understand that this problem can be solved when coding on XCode 12+ changing the Bundle to Mach-O on the build settings, however, appending the bundle argument -b sends a ld: warning: option -b is obsolete and being ignored warning.


Solution

  • When feeding the source to the linker with any of the registrers ("R1", "R2"... ) or "PC" ("R15") I got the following linker error:

    These registers don't exist, you're reading the manual for the wrong architecture. You need ARMv8, not ARMv7.

    As for this:

    b   PC          //Expecting endless loop
    //b PC-6        //Expecting endless "Hello World" loop
    

    PC is not a thing. It's just an identifier like any other, such as _start. If you want to refer to the current instruction, use a dot (.):

    b .    // infinite loop
    b .+8  // skip the next instruction
    

    Note that any arithmetic is unscaled. If you want to jump 6 instructions back, you'll have to do b .-(4*6). If you attempted to compile b .-6, you'd get an error because b is only able to encode offsets aligned to 4 bytes.