I use an M1 MacBook Pro.
I am trying the load a value I put in the .data segment in my assembly code, but when I look at the value of the register using LLDB, it shows me 0x0.
Here's the code:
.global _main
.extern print
.extern printInt
.extern exit
.align 4
.text
_main:
ldr x0, =num
b exit
.data
num: .word 5
Don't worry about the exit
label I branch to, I have seperately defined that in another
assembly file.
And here's the register read just before executing the LDR instruction:
General Purpose Registers:
x0 = 0x0000000000000001
x1 = 0x000000016fdff2d0
And here's the register read just after executing the LDR instruction:
General Purpose Registers:
x0 = 0x0000000000000000
x1 = 0x000000016fdff2d0
I am also really struggling with learning Assembly on this machine, so if you have any helpful tools I can learn using, it would be really helpful if you share them.
I have tried searching up what the LDR instruction does, but the ARM's website is really obscure and difficult to process and other tools aren't helpful. I have no idea what's going on half the time.
This is the fault of Apple's new linker.
For a long time, Apple had been using their ld64 linker for all of their platforms. And in this answer, I explain why using ldr xN, =...
is broken on macOS, and what to use instead.
Now, I do think that some choices made about the internal workings of the Mach-O LLVM pipeline (specifically about segment relocations) make it really hard to properly support this "emit-a-pointer-and-load-it-by-pc-relative-label" for Darwin targets. But the specific way in which this failure manifests depends on choices made by the linker. Ideally the linker would identify that this was gonna be a problem and error out at link-time. But ld64
seems to be blissfully unaware of runtime restrictions and simply emits a pointer in a segment where trying to rebase it will crash the process.
But that was last year.
In Xcode 15, Apple has deprecated ld64 and replaced it with a new default that I've been calling dyld-ld
for lack of a better name.
Running what
on this new ld
or invoking it with -v
used to identify it as part of the dyld
source tree back in Xcode 15.0:
PROGRAM:ld PROJECT:dyld-1015.7
Though it seems to have since been broken out into its own project:
PROGRAM:ld PROJECT:ld-1115.7.2
Unfortunately it's closed source, so we can't just go and examine it. But from the behaviour it exhibits, I'm pretty sure it's not a fork of ld64
, but a rewrite from scratch.
Now, ld64
is still shipped as ld-classic
and can be selected on the clang command line with -ld_classic
. If you compile your code with that and then run dyld_info -fixups
on your binary, you will see the rebase that is supposed to get applied (which will make the binary crash on launch).
If you link with dyld-ld
(which can be explicitly selected with -ld_new
, though that is the default anyway), then dyld_info -fixups
will just not show a rebase for that pointer at all. And if you examine it in a disassembler, you will find that it simply has the value 0
. This means that such binaries will now launch... but then very likely just crash on some NULL
pointers. And that's what you're seeing in lldb
.
So yeah, different symptom, same broken feature. You should still use adrp
+add
as I explain in my other answer.