llvm / llvm-project

The LLVM Project is a collection of modular and reusable compiler and toolchain technologies.
http://llvm.org
Other
27.96k stars 11.54k forks source link

lld COFF X64: out of range relocations are truncated without reporting errors #107124

Open v01dXYZ opened 1 week ago

v01dXYZ commented 1 week ago

In this related issue (https://github.com/llvm/llvm-project/issues/106319), some object code has been generated with code-model=small relocation-model=static and uses 32-bit absolute relocations. As the 64-bit base address is greater than 1 << 32, ldd produces wrong relocated addresses.

When looking at the code, it seems we narrow/truncate then compute instead of doing the way round. For comparison, ELF uses checkUInt/checkInt/checkIntUInt after 64-bit address computations.

llvmbot commented 1 week ago

@llvm/issue-subscribers-lld-coff

Author: None (v01dXYZ)

In this related issue (https://github.com/llvm/llvm-project/issues/106319), some object code has been generated with `code-model=small relocation-model=static` and uses 32-bit absolute relocations. As the 64-bit base address is greater than `1 << 32`, `ldd` produces wrong relocated addresses. When looking at the code, it seems we narrow/truncate then compute instead of doing the way round. For comparison, ELF uses `checkUInt/checkInt/checkIntUInt` after 64-bit address computations.
v01dXYZ commented 1 week ago

FYI in binutils/bfd, the important function that implements the overflow check is bfd/reloc.c _bfd_relocate_contents. Another interesting file is coff-x86_64.c, especially the howto_table array where is defined if the overflow is sign/unsigned/bitfield. I've got to find the exact part of the COFF specs which define that.

v01dXYZ commented 1 week ago

For comparison sake, I tried to use link.exe from MSVC toolchain directly (downloaded by following est31's tutorial: gist.github.comarchive).

link.exe simply refuses to link when there is a 32-bit absolute ADDR32 relocation with /largeaddressaware (which is enabled by default on x86_64). for /largeaddressaware cf MS's docsarchive.

When /largeaddressaware:no, only 32-bit base addresses are supported. Furthermore the 32-bit base address should not set the MSB and some LSBs (base_addr & 0x80_00_ff_ff) != 0.

I wrote down some test cases in assembly to see if overflows are detected by link.exe (depending on relocation type, base address, symbol address, etc.) . I tested also the case for symbol+offset: when the address at relocation is not set to 0 in the object code.

After those experiments, I observed that link.exe ensures:

Conclusion: The solution to the problem by Windows is to simply put restrictions on its executable format instead of relying on shared conf b/w code generation (code-model=small) and linker.

Which path should we take to solve the problem ?


Tested with two versions of linker.exe:

v01dXYZ commented 1 week ago

@MaskRay I've read your posts about relocation overflow for ELF. Could you advise me on which way is best fitted to current lld ? I prefer to ask before writing some code. I'm surprised this issue was not found earlier because default image base address for x86_64 is already beyond 4GB.

MaskRay commented 2 days ago

@MaskRay I've read your posts about relocation overflow for ELF. Could you advise me on which way is best fitted to current lld ? I prefer to ask before writing some code. I'm surprised this issue was not found earlier because default image base address for x86_64 is already beyond 4GB.

It'll be nice to have the errors (https://maskray.me/blog/2023-05-14-relocation-overflow-and-code-models). If the compiler does not generate ELF-like medium/large code model code sequences, it's generally not feasible to entirely rely on the linker to fix all limited relocations. Linkers support certain range extension thunks for function symbols, but it cannot alleviate issues for other symbols.