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

Reply via email to