I have this simple piece of code which should print hi, and it works fine.
hi.s
section .text
hi:
db "hi", 0
global sayHi
align 16
sayHi:
lea rax, [rel hi]
ret
start.c
extern int puts();
extern const char *sayHi();
void start() {
puts(sayHi());
}
However, when I change lea rax, [rel hi] to lea rax, [hi], I get an error message from ld, relocation truncated to fit: IMAGE_REL_AMD64_ADDR32.
I checked at what address the program is being loaded, and the debugger prints this. This is when using [rel hi]. I'm not sure whether the address is physical or virtual, but it's sure it goes over 32 bits, so obviously any static data won't fit in a 32-bit absolute displacement.
Entry point: 0x140001000
0x0000000140001000 - 0x0000000140001060 is .text
0x0000000140002000 - 0x0000000140002030 is .rdata
I wrote this C code to see how the compiler loads static data.
volatile char a[10];
void f(int i) {
a[i];
}
gcc for Windows emits this.
f:
lea rax, a[rip]
movsx rcx, ecx
movzx eax, BYTE PTR [rax+rcx]
ret
I also checked MSVC from godbolt.
f:
movsxd rax, ecx
lea rcx, OFFSET FLAT:a
movzx eax, BYTE PTR [rax+rcx]
ret 0
Apart from MSVC not using relative addressing where it obviously should, both compilers think static data can always be loaded over 32-bit addresses, so it's not possible to put it in a 32-bit absolute displacement.
In Linux, this was not the case because the linker had no problem handling hand-written assembly code using 32-bit displacement, and also the compilers chose to do that instead of an additional lea. The same code compiled by a Linux gcc is this.
f:
movsx rdi, edi
movzx eax, BYTE PTR a[rdi]
ret