Search code examples
macosassemblyarm64

How to subtract two label values in ARM64 assembly using clang's as(1)?


I found an ARM64/MacOS "Hello, World" program that looks like this:

.global _main
.align 2 

_main: mov X0, #1
       adr X1, helloworld
       mov X2, #13
       mov X16, #4
       svc 0

       mov X0, #0 
       mov X16, #1
       svc 0

helloworld:      .ascii  "Hello World!\n"

What I'd like to do is eliminate the hard-coded string-length (mov X2, #13). But I can't figure out how to get as to subtract two label values for use in an expression.

I tried adding helloend: after the string and doing

mov X2, #helloend - helloworld

Which got me this message:

hello.s:6:9: error: unknown AArch64 fixup kind!
    mov X2, #helloend - helloworld

I also tried doing a runtime subtraction, but trying to use =label as a parameter to the sub instruction gave me the same "error: unknown AArch64 fixup kind!" message.

I'm using the as that's part of clang 14.0.3.

What's the solution?


Solution

  • You can create a new symbol with the length, then mov it into a register.

    len = helloend - helloworld
    mov x2, #len
    

    Or .equ len, helloend - helloworld works too. You can shorten it a bit by defining it right after the string:

    helloworld:      .ascii  "Hello World!\n"
    len = . - helloworld
    

    I think the issue is that with mov x2, #(end - begin), the assembler tries to emit fixups to have the linker do the subtraction and encode the result into the instruction. This isn't supported. But len = end - begin forces the subtraction to be done within the assembler, and then it can encode the immediate directly.