On Wed, 4 Sep 2024, Evgeny Karpov wrote:
Monday, September 4, 2024
Martin Storsjö <mar...@martin.st> wrote:
compilation time
adrp x0, symbol + 256
90000000 adrp x0, 0
As the symbol offset is 256, you will need to encode the offset "256" in
the instruction immediate field. Not "256 >> 12". This is the somewhat
non-obvious part here, but this is the only way symbol offsets can work.
This is how MS tools handle immediates in IMAGE_REL_ARM64_PAGEBASE_REL21,
and LLVM has replicated this bit.
This approach does not allow to address 4GB, instead it can address only 1MB.
This issue has been fixed in this patch series.
https://sourceware.org/pipermail/binutils/2024-August/136481.html
But this is not something you can redefine yourself! The relocation format
and their behaviour is defined by Microsof (your employer?), you can't
just change it within the scope of GNU tools because you disagree with it!
Yes, the immediate offset can be maximum 1 MB. But this doesn't mean that
you can't address anywhere in the 4 GB address space of a PE image. It
just means that an IMAGE_REL_ARM64_PAGEBASE_REL21 can point anywhere up to
1 MB before/after a symbol. You can't have one single symbol at the start
of an image and try to add a fixed 4 GB offset on top of that - that's not
what any regular object file would do anyway.
Yes, it is possible to hit cases where you want an offset slightly larger
than 1 MB - if you happen to have a very large object file. It's very rare
though, but it can happen. In LLVM we fixed this by injecting extra label
symbols with 1 MB intervals if it turns out that an individual section
ends up larger than this, like this:
https://github.com/llvm/llvm-project/commit/06d0d449d8555ae5f1ac33e8d4bb4ae40eb080d3
armasm produces the same opcode
for: adrp x0, symbol + 256
it will be: 90000000 adrp x0, 0
It seems like armasm64 doesn't handle this case correctly, and/or is
inconsistent.
But let's see what MSVC cl.exe does, if you don't trust my other
references.
$ cat adrp.c
extern char array[];
char *getPtr(void) {
return &array[256];
}
$ cl -c -O2 adrp.c -Fa
Microsoft (R) C/C++ Optimizing Compiler Version 19.41.34120 for ARM64
Copyright (C) Microsoft Corporation. All rights reserved.
adrp.c
$ dumpbin -nologo -disasm adrp.obj
Dump of file adrp.obj
File Type: COFF OBJECT
getPtr:
0000000000000000: 90000808 adrp x8,array+#0x100
0000000000000004: 91040100 add x0,x8,array+#0x100
0000000000000008: D65F03C0 ret
$ cat adrp.asm
[...]
AREA |.text$mn|, CODE, ARM64
|getPtr| PROC
adrp x8,array+#0x100
add x0,x8,array+#0x100
ret
ENDP ; |getPtr|
END
Unfortunately, it seems like armasm64 doesn't actually manage to assemble
the output of MSVC in this case. If the # chars are removed, it can
assemble it, but the offsets simply aren't encoded at all - neither for
the adrp nor for the add. So it simply seems that armasm64 doesn't support
immediates for symbol offsets at all.
Nevertheless, the object file format supports it just fine, MSVC cl.exe
uses it, and link.exe handles it exactly like I've described.
// Martin