diff --git a/bolt/include/bolt/Core/BinaryContext.h b/bolt/include/bolt/Core/BinaryContext.h --- a/bolt/include/bolt/Core/BinaryContext.h +++ b/bolt/include/bolt/Core/BinaryContext.h @@ -871,6 +871,15 @@ return nullptr; } + /// Retrieves a reference to ELF's _GLOBAL_OFFSET_TABLE_ symbol, which points + /// at GOT, or null if it is not present in the input binary symtab. + BinaryData *getGOTSymbol(); + + /// Checks if symbol name refers to ELF's _GLOBAL_OFFSET_TABLE_ symbol + bool isGOTSymbol(StringRef SymName) const { + return SymName == "_GLOBAL_OFFSET_TABLE_"; + } + /// Return true if \p SymbolName was generated internally and was not present /// in the input binary. bool isInternalSymbolName(const StringRef Name) { 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/BinaryContext.cpp b/bolt/lib/Core/BinaryContext.cpp --- a/bolt/lib/Core/BinaryContext.cpp +++ b/bolt/lib/Core/BinaryContext.cpp @@ -1026,6 +1026,31 @@ return nullptr; } +BinaryData *BinaryContext::getGOTSymbol() { + // First tries to find a global symbol with that name + BinaryData *GOTSymBD = getBinaryDataByName("_GLOBAL_OFFSET_TABLE_"); + if (GOTSymBD) + return GOTSymBD; + + // This symbol might be hidden from run-time link, so fetch the local + // definition if available. + GOTSymBD = getBinaryDataByName("_GLOBAL_OFFSET_TABLE_/1"); + if (!GOTSymBD) + return nullptr; + + // If the local symbol is not unique, fail + unsigned Index = 2; + SmallString<30> Storage; + while (const BinaryData *BD = + getBinaryDataByName(Twine("_GLOBAL_OFFSET_TABLE_/") + .concat(Twine(Index++)) + .toStringRef(Storage))) + if (BD->getAddress() != GOTSymBD->getAddress()) + return nullptr; + + return GOTSymBD; +} + bool BinaryContext::setBinaryDataSize(uint64_t Address, uint64_t Size) { auto NI = BinaryDataMap.find(Address); assert(NI != BinaryDataMap.end()); 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: @@ -136,6 +137,7 @@ return 4; case ELF::R_X86_64_PC64: case ELF::R_X86_64_64: + case ELF::R_X86_64_GOTPC64: return 8; } } @@ -655,6 +657,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: @@ -797,6 +800,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/Rewrite/JITLinkLinker.cpp b/bolt/lib/Rewrite/JITLinkLinker.cpp --- a/bolt/lib/Rewrite/JITLinkLinker.cpp +++ b/bolt/lib/Rewrite/JITLinkLinker.cpp @@ -141,6 +141,19 @@ orc::ExecutorAddr(Address), JITSymbolFlags()); continue; } + + if (Linker.BC.isGOTSymbol(SymName)) { + if (const BinaryData *I = Linker.BC.getGOTSymbol()) { + uint64_t Address = + I->isMoved() ? I->getOutputAddress() : I->getAddress(); + LLVM_DEBUG(dbgs() << "Resolved to address 0x" + << Twine::utohexstr(Address) << "\n"); + AllResults[Symbol.first] = orc::ExecutorSymbolDef( + orc::ExecutorAddr(Address), JITSymbolFlags()); + continue; + } + } + LLVM_DEBUG(dbgs() << "Resolved to address 0x0\n"); AllResults[Symbol.first] = orc::ExecutorSymbolDef(orc::ExecutorAddr(0), JITSymbolFlags()); diff --git a/bolt/lib/Rewrite/RewriteInstance.cpp b/bolt/lib/Rewrite/RewriteInstance.cpp --- a/bolt/lib/Rewrite/RewriteInstance.cpp +++ b/bolt/lib/Rewrite/RewriteInstance.cpp @@ -2394,9 +2394,13 @@ } MCSymbol *ReferencedSymbol = nullptr; - if (!IsSectionRelocation) + if (!IsSectionRelocation) { if (BinaryData *BD = BC->getBinaryDataByName(SymbolName)) ReferencedSymbol = BD->getSymbol(); + else if (BC->isGOTSymbol(SymbolName)) + if (BinaryData *BD = BC->getGOTSymbol()) + ReferencedSymbol = BD->getSymbol(); + } ErrorOr ReferencedSection{std::errc::bad_address}; symbol_iterator SymbolIter = Rel.getSymbol(); 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,26 @@ return true; } +std::pair +X86MCSymbolizer::handleGOTPC64(const Relocation &R, uint64_t InstrAddr) { + BinaryContext &BC = Function.getBinaryContext(); + const BinaryData *GOTSymBD = BC.getGOTSymbol(); + if (!GOTSymBD || !GOTSymBD->getAddress()) { + errs() << "BOLT-ERROR: R_X86_GOTPC64 relocation is present but we did " + "not detect a valid _GLOBAL_OFFSET_TABLE_ in 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. + const int64_t Addend = R.Value + InstrAddr - GOTSymBD->getAddress(); + return std::make_pair(BC.Ctx->getOrCreateSymbol("_GLOBAL_OFFSET_TABLE_"), + Addend); +} + void X86MCSymbolizer::tryAddingPcLoadReferenceComment(raw_ostream &CStream, int64_t Value, uint64_t Address) {} diff --git a/bolt/test/runtime/X86/gotoff-large-code-model-2.s b/bolt/test/runtime/X86/gotoff-large-code-model-2.s new file mode 100644 --- /dev/null +++ b/bolt/test/runtime/X86/gotoff-large-code-model-2.s @@ -0,0 +1,57 @@ +# A variation of gotoff-large-code-model.s that accesses GOT value +# with a slightly different code sequence. + +# 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 $0x8,%rsp + lea 1f(%rip),%rbx + # R_X86_64_GOTPC64 _GLOBAL_OFFSET_TABLE_+0x2 +1: movabsq $_GLOBAL_OFFSET_TABLE_, %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 $0x8,%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 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 $0x8,%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 $0x8,%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