diff --git a/bolt/include/bolt/Core/Relocation.h b/bolt/include/bolt/Core/Relocation.h --- a/bolt/include/bolt/Core/Relocation.h +++ b/bolt/include/bolt/Core/Relocation.h @@ -84,6 +84,7 @@ /// Special relocation type that allows the linker to modify the instruction. static bool isX86GOTPCRELX(uint64_t Type); + static bool isX86GOTPC64(uint64_t Type); /// Return true if relocation type is NONE static bool isNone(uint64_t Type); diff --git a/bolt/lib/Core/Relocation.cpp b/bolt/lib/Core/Relocation.cpp --- a/bolt/lib/Core/Relocation.cpp +++ b/bolt/lib/Core/Relocation.cpp @@ -35,6 +35,7 @@ case ELF::R_X86_64_PC32: case ELF::R_X86_64_PC64: case ELF::R_X86_64_PLT32: + case ELF::R_X86_64_GOTPC64: case ELF::R_X86_64_GOTPCREL: case ELF::R_X86_64_GOTTPOFF: case ELF::R_X86_64_TPOFF32: @@ -131,6 +132,7 @@ return 4; case ELF::R_X86_64_PC64: case ELF::R_X86_64_64: + case ELF::R_X86_64_GOTPC64: return 8; } } @@ -625,6 +627,7 @@ case ELF::R_X86_64_PLT32: case ELF::R_X86_64_GOTOFF64: case ELF::R_X86_64_GOTPC32: + case ELF::R_X86_64_GOTPC64: case ELF::R_X86_64_GOTTPOFF: case ELF::R_X86_64_GOTPCRELX: case ELF::R_X86_64_REX_GOTPCRELX: @@ -762,6 +765,12 @@ return Type == ELF::R_X86_64_GOTPCRELX || Type == ELF::R_X86_64_REX_GOTPCRELX; } +bool Relocation::isX86GOTPC64(uint64_t Type) { + if (Arch != Triple::x86_64) + return false; + return Type == ELF::R_X86_64_GOTPC64; +} + bool Relocation::isNone(uint64_t Type) { return Type == getNone(); } bool Relocation::isRelative(uint64_t Type) { diff --git a/bolt/lib/Target/X86/X86MCPlusBuilder.cpp b/bolt/lib/Target/X86/X86MCPlusBuilder.cpp --- a/bolt/lib/Target/X86/X86MCPlusBuilder.cpp +++ b/bolt/lib/Target/X86/X86MCPlusBuilder.cpp @@ -403,6 +403,7 @@ case ELF::R_X86_64_PC8: case ELF::R_X86_64_PC32: case ELF::R_X86_64_PC64: + case ELF::R_X86_64_GOTPC64: case ELF::R_X86_64_GOTPCRELX: case ELF::R_X86_64_REX_GOTPCRELX: return true; diff --git a/bolt/lib/Target/X86/X86MCSymbolizer.h b/bolt/lib/Target/X86/X86MCSymbolizer.h --- a/bolt/lib/Target/X86/X86MCSymbolizer.h +++ b/bolt/lib/Target/X86/X86MCSymbolizer.h @@ -20,6 +20,9 @@ BinaryFunction &Function; bool CreateNewSymbols{true}; + std::pair handleGOTPC64(const Relocation &R, + uint64_t InstrAddr); + public: X86MCSymbolizer(BinaryFunction &Function, bool CreateNewSymbols = true) : MCSymbolizer(*Function.getBinaryContext().Ctx.get(), nullptr), diff --git a/bolt/lib/Target/X86/X86MCSymbolizer.cpp b/bolt/lib/Target/X86/X86MCSymbolizer.cpp --- a/bolt/lib/Target/X86/X86MCSymbolizer.cpp +++ b/bolt/lib/Target/X86/X86MCSymbolizer.cpp @@ -130,6 +130,15 @@ if (!Relocation) return processPCRelOperandNoRel(); + // GOTPC64 is special because the X86 Assembler doesn't know how to emit + // a PC-relative 8-byte fixup, which is what we need to cover this. The + // only way to do this is to use the symbol name _GLOBAL_OFFSET_TABLE_. + if (Relocation::isX86GOTPC64(Relocation->Type)) { + auto [Sym, Addend] = handleGOTPC64(*Relocation, InstAddress); + addOperand(Sym, Addend); + return true; + } + uint64_t SymbolValue = Relocation->Value - Relocation->Addend; if (Relocation->isPCRelative()) SymbolValue += InstAddress + ImmOffset; @@ -149,6 +158,29 @@ return true; } +std::pair +X86MCSymbolizer::handleGOTPC64(const Relocation &R, uint64_t InstrAddr) { + BinaryContext &BC = Function.getBinaryContext(); + BinaryData *GOTSymBD = BC.getBinaryDataByName("_GLOBAL_OFFSET_TABLE_"); + if (!GOTSymBD || !GOTSymBD->getAddress()) { + errs() << "BOLT-ERROR: R_X86_GOTPC64 relocation is present but we did " + "not detect _GLOBAL_OFFSET_TABLE_ in the symbol table.\n"; + exit(1); + } + // R_X86_GOTPC64 are not relative to the Reloc nor end of instruction, + // but the start of the MOVABSQ instruction. So the Target Address is + // whatever is encoded in the original operand when we disassembled + // the binary (here, R.Value) plus MOVABSQ address (InstrAddr). + // Here we extract the intended Addend by subtracting the real + // GOT addr. + int64_t Addend = R.Value + InstrAddr - GOTSymBD->getAddress(); + for (MCSymbol *GOTSym : GOTSymBD->symbols()) { + if (GOTSym->getName() == "_GLOBAL_OFFSET_TABLE_") + return std::make_pair(GOTSym, Addend); + } + llvm_unreachable("_GLOBAL_OFFSET_TABLE_ is missing"); +} + void X86MCSymbolizer::tryAddingPcLoadReferenceComment(raw_ostream &CStream, int64_t Value, uint64_t Address) {} diff --git a/bolt/test/runtime/X86/gotoff-large-code-model.s b/bolt/test/runtime/X86/gotoff-large-code-model.s new file mode 100644 --- /dev/null +++ b/bolt/test/runtime/X86/gotoff-large-code-model.s @@ -0,0 +1,55 @@ +# REQUIRES: system-linux + +# RUN: llvm-mc -filetype=obj -triple x86_64-unknown-unknown \ +# RUN: %s -o %t.o +# RUN: %clang %cflags -no-pie %t.o -o %t.exe -Wl,-q + +# RUN: llvm-bolt %t.exe --funcs init_impls -lite \ +# RUN: -o %t.bolted +# RUN: %t.bolted | FileCheck %s + + .section .rodata.str1.1,"aMS",@progbits,1 +.LC2: + .string "Hello, world\n" + .text + .p2align 4 + .globl init_impls + .type init_impls, @function +init_impls: + .cfi_startproc + push %rbp + mov %rsp,%rbp + push %r15 + push %rbx + sub $0x10,%rsp +1: + lea 1b(%rip),%rbx + # R_X86_64_GOTPC64 _GLOBAL_OFFSET_TABLE_+0x9 + movabsq $_GLOBAL_OFFSET_TABLE_-1b, %r11 + add %r11,%rbx + # R_X86_64_GOTOFF64 .LC2 + movabs $.LC2@gotoff,%rax + lea (%rbx,%rax,1),%rax + mov %rax,%rdi + mov %rbx,%r15 + # R_X86_64_PLTOFF64 puts + movabs $puts@pltoff,%rax + add %rbx,%rax + call *%rax + add $0x10,%rsp + pop %rbx + pop %r15 + pop %rbp + retq + .cfi_endproc + .size init_impls, .-init_impls + + .globl main + .type main, @function + .p2align 4 +main: + callq init_impls + xorq %rax, %rax + ret + +# CHECK: Hello, world