assemblyarmarm64

How to load data by label on Apple Silicon (ARM64)?


LDR doesn't work correctly due Apple's thoughts about security, but how I can load, for example this:

.data
.align 4
.mynumber: .quad   0x123456789ABCDEF0

I know about ADRP, but I'm not sure that I understand how it works.

I have already done this(ADRP, @PAGE, @PAGEOFF was taken from other sources):

.global _start

.text
_start: 
    ADRP    X2, arr1@PAGE
    ADD X2, X2, arr1@PAGEOFF

    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

.data
.align 4
arr1:   .FILL   10, 4, 0
mynumber:   .quad   0x123456789ABCDEF0
myoctaword: .octa 0x12345678876543211234567887654321

Solution

  • Loading "data by label" works perfectly fine:

    ldr x0, label
    
    label:
        .8byte 0x0123456789abcdef
    

    What doesn't work on Darwin is ldr x0, =..., because that's syntactic sugar for this:

    ldr x0, addrof_label
    
    label:
        .8byte 0x0123456789abcdef
    addrof_label:
        .8byte label
    

    And since label is a statically encoded address, it would need to be rebased by the dynamic linker, which crashes the process when attempted, since the __TEXT segment is both mapped as r-x and codesigned.
    (Update: since Xcode 15.0, Apple is using a new linker whose behaviour is different, but still wrong. See this answer for more details. The solution below still applies.)

    For getting the address of some labels, you can use adr:

    adr x0, label
    
    label:
        .8byte 0x0123456789abcdef
    

    But this will only work in a ±1MiB range, and additionally the toolchain will only let you do this within the same segment, i.e. __TEXT. For the general case, adrp+add is the way to go:

    adrp x0, label@PAGE
    add x0, x0, label@PAGEOFF
    
    .data
    label:
        .8byte 0x0123456789abcdef
    

    But you can use .loh AdrpAdd ... directives to have the linker fix this back up into adr+nop if the resulting offset is in range:

    L1:
    adrp x0, label@PAGE
    L2:
    add x0, x0, label@PAGEOFF
    .loh AdrpAdd L1, L2
    
    .data
    label:
        .8byte 0x0123456789abcdef
    

    The same can be done with adrp+ldr if necessary, and likewise .loh AdrpLdr ... can turn that into just a PC-relative ldr if in range.