| Issue |
183123
|
| Summary |
[RuntimeDyld][COFF] 32-bit relocation addend not sign-extended in RuntimeDyldCOFFX86_64
|
| Labels |
new issue
|
| Assignees |
|
| Reporter |
sumit2089
|
## Summary
In `RuntimeDyldCOFFX86_64::processRelocationRef`, the 32-bit implicit addend for `IMAGE_REL_AMD64_REL32*` and `IMAGE_REL_AMD64_ADDR32NB` relocations is not sign-extended when read into a 64-bit variable, causing spurious relocation overflow asserts.
## Details
The addend is read as:
```cpp
Addend = readBytesUnaligned(Displacement, 4);
```
`readBytesUnaligned` returns `uint64_t`, so a negative 32-bit addend such as `0xFFFFFFFC` (-4) is zero-extended to `0x00000000FFFFFFFC` (4294967292) instead of being sign-extended to `0xFFFFFFFFFFFFFFFC` (-4).
This causes the assert in `resolveRelocation` to fire spuriously:
```cpp
uint64_t Result = Value + RE.Addend;
assert(((int64_t)Result <= INT32_MAX) && "Relocation overflow");
```
The computed `Result` overflows because `RE.Addend` is ~4 billion instead of -4.
**File:** `llvm/lib/ExecutionEngine/RuntimeDyld/Targets/RuntimeDyldCOFFX86_64.h`, line 254
## Proposed Fix
```diff
- Addend = readBytesUnaligned(Displacement, 4);
+ Addend = static_cast<int64_t>(static_cast<int32_t>(readBytesUnaligned(Displacement, 4)));
```
## Steps to Reproduce
### Environment
- **OS:** Windows 10/11 x64
- **Compiler:** MSVC (Visual Studio 2022)
- **LLVM version:** 21.1.8 (obtained via vcpkg from the official GitHub release `llvmorg-21.1.8`)
### Minimal reproducer
```cpp
#include <llvm/ExecutionEngine/Orc/Core.h>
#include <llvm/ExecutionEngine/Orc/RTDyldObjectLinkingLayer.h>
#include <llvm/ExecutionEngine/Orc/IRCompileLayer.h>
#include <llvm/ExecutionEngine/Orc/CompileUtils.h>
#include <llvm/ExecutionEngine/Orc/AbsoluteSymbols.h>
#include <llvm/ExecutionEngine/Orc/SelfExecutorProcessControl.h>
#include <llvm/ExecutionEngine/Orc/ThreadSafeModule.h>
#include <llvm/ExecutionEngine/SectionMemoryManager.h>
#include <llvm/IR/IRBuilder.h>
#include <llvm/IR/LLVMContext.h>
#include <llvm/IR/Module.h>
#include <llvm/IR/Verifier.h>
#include <llvm/Support/TargetSelect.h>
#include <llvm/Target/TargetMachine.h>
#include <llvm/MC/TargetRegistry.h>
#include <llvm/TargetParser/Host.h>
#include <cmath>
#include <cstdio>
// External function the JIT'd code will call
extern "C" float my_external_func(float x) { return x * 2.0f; }
int main() {
llvm::InitializeNativeTarget();
llvm::InitializeNativeTargetAsmPrinter();
// Create execution session
auto EPC = llvm::orc::SelfExecutorProcessControl::Create();
if (!EPC) {
fprintf(stderr, "Failed to create EPC\n");
return 1;
}
auto ES = std::make_unique<llvm::orc::ExecutionSession>(std::move(*EPC));
// Create object linking layer with default SectionMemoryManager
llvm::orc::RTDyldObjectLinkingLayer ObjLayer(
*ES,
[](const llvm::MemoryBuffer&) {
return std::make_unique<llvm::SectionMemoryManager>();
});
// Create target machine with CodeModel::Small
std::string ErrStr;
auto Triple = llvm::sys::getDefaultTargetTriple();
auto* Target = llvm::TargetRegistry::lookupTarget(Triple, ErrStr);
llvm::TargetOptions Opts;
auto* TM = Target->createTargetMachine(
Triple, "generic", "", Opts,
llvm::Reloc::PIC_,
llvm::CodeModel::Small,
llvm::CodeGenOptLevel::Default);
// Create IR compile layer
llvm::orc::IRCompileLayer CompileLayer(
*ES, ObjLayer, std::make_unique<llvm::orc::SimpleCompiler>(*TM));
// Create JITDylib and add external symbol
auto& JD = ES->createBareJITDylib("main");
llvm::orc::SymbolMap ExternalSymbols;
auto& Intern = ES->getExecutorSymbolStringPool();
ExternalSymbols[ES->intern("my_external_func")] = {
llvm::orc::ExecutorAddr::fromPtr(&my_external_func),
llvm::JITSymbolFlags::Exported | llvm::JITSymbolFlags::Callable
};
if (auto Err = JD.define(llvm::orc::absoluteSymbols(std::move(ExternalSymbols)))) {
fprintf(stderr, "Failed to define symbols\n");
return 1;
}
// Build a simple IR module that calls the external function
auto Ctx = std::make_unique<llvm::LLVMContext>();
auto Mod = std::make_unique<llvm::Module>("test", *Ctx);
Mod->setDataLayout(TM->createDataLayout());
Mod->setTargetTriple(llvm::Triple(Triple));
auto* FT = llvm::FunctionType::get(
llvm::Type::getFloatTy(*Ctx),
{llvm::Type::getFloatTy(*Ctx)},
false);
auto* F = llvm::Function::Create(FT, llvm::Function::ExternalLinkage, "test_func", *Mod);
auto* BB = llvm::BasicBlock::Create(*Ctx, "entry", F);
llvm::IRBuilder<> Builder(BB);
// Declare external function
auto* ExtFT = llvm::FunctionType::get(
llvm::Type::getFloatTy(*Ctx),
{llvm::Type::getFloatTy(*Ctx)},
false);
auto* ExtF = llvm::Function::Create(ExtFT, llvm::Function::ExternalLinkage,
"my_external_func", *Mod);
// Call it — this generates IMAGE_REL_AMD64_REL32 relocations in the COFF object
auto* CallResult = Builder.CreateCall(ExtF, {F->getArg(0)});
Builder.CreateRet(CallResult);
llvm::verifyFunction(*F);
// JIT compile — this triggers the assert in resolveRelocation
// when the implicit addend at the call site is negative
llvm::orc::ThreadSafeModule TSM(std::move(Mod), std::move(Ctx));
if (auto Err = CompileLayer.add(JD, std::move(TSM))) {
fprintf(stderr, "Failed to add module\n");
return 1;
}
auto Sym = ES->lookup({&JD}, "test_func");
if (!Sym) {
fprintf(stderr, "Lookup failed\n");
return 1;
}
printf("JIT compilation succeeded at %p\n",
reinterpret_cast<void*>(Sym->getAddress().getValue()));
if (auto Err = ES->endSession())
llvm::consumeError(std::move(Err));
return 0;
}
```
### How to trigger
The crash occurs when:
1. The COFF object emitted by LLVM's code generator contains an `IMAGE_REL_AMD64_REL32` relocation with a negative implicit addend at the relocation site (e.g., `0xFFFFFFFC`).
2. `RuntimeDyldCOFFX86_64::processRelocationRef` reads this 4-byte addend via `readBytesUnaligned(Displacement, 4)`, which returns `uint64_t`.
3. The value `0xFFFFFFFC` is zero-extended to `0x00000000FFFFFFFC` (4294967292) instead of being sign-extended to `0xFFFFFFFFFFFFFFFC` (-4).
4. In `resolveRelocation`, `Value + RE.Addend` produces an incorrect result that exceeds `INT32_MAX`, triggering the assert.
**Note:** Whether negative implicit addends appear depends on the code generator, optimization level, and layout of the emitted object. They are more likely with `CodeModel::Small` and PIC relocations on Windows x64.
## LLVM Source
Obtained from the official LLVM GitHub repository via vcpkg: `llvmorg-21.1.8` tag at https://github.com/llvm/llvm-project.
The bug is also present in LLVM 18.x and likely all prior versions — the `processRelocationRef` code in `RuntimeDyldCOFFX86_64.h` has not changed in this area.
## Additional Note
`RuntimeDyldCOFFI386.h` and `RuntimeDyldCOFFThumb.h` may have the same issue in their `processRelocationRef` implementations and should be audited.
_______________________________________________
llvm-bugs mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-bugs