From 01205f79a46f1f28164e5ec6d941030b242d2e9d Mon Sep 17 00:00:00 2001 From: Rafael Espindola Date: Tue, 22 Sep 2015 18:19:46 +0000 Subject: [PATCH] Start adding target abstractions. This is just enough to get PLT working on 32 bit x86. The idea behind using a virtual interface is that it should be easy to convert any of the functions to template parameters if any turns out to be performance critical. llvm-svn: 248308 --- lld/ELF/CMakeLists.txt | 1 + lld/ELF/InputSection.cpp | 9 ++-- lld/ELF/OutputSections.cpp | 41 +++------------ lld/ELF/OutputSections.h | 3 -- lld/ELF/SymbolTable.cpp | 10 +++- lld/ELF/SymbolTable.h | 2 +- lld/ELF/Target.cpp | 100 +++++++++++++++++++++++++++++++++++++ lld/ELF/Target.h | 54 ++++++++++++++++++++ lld/ELF/Writer.cpp | 5 +- lld/test/elf2/plt-i686.s | 57 +++++++++++++++++++++ 10 files changed, 235 insertions(+), 47 deletions(-) create mode 100644 lld/ELF/Target.cpp create mode 100644 lld/ELF/Target.h create mode 100644 lld/test/elf2/plt-i686.s diff --git a/lld/ELF/CMakeLists.txt b/lld/ELF/CMakeLists.txt index 12ae12a50821..3f98a9cb7df4 100644 --- a/lld/ELF/CMakeLists.txt +++ b/lld/ELF/CMakeLists.txt @@ -11,6 +11,7 @@ add_llvm_library(lldELF2 OutputSections.cpp SymbolTable.cpp Symbols.cpp + Target.cpp Writer.cpp LINK_COMPONENTS diff --git a/lld/ELF/InputSection.cpp b/lld/ELF/InputSection.cpp index 04dc1eb9f2d7..e938e175f25e 100644 --- a/lld/ELF/InputSection.cpp +++ b/lld/ELF/InputSection.cpp @@ -11,6 +11,7 @@ #include "Error.h" #include "InputFiles.h" #include "OutputSections.h" +#include "Target.h" #include "llvm/Support/raw_ostream.h" @@ -114,12 +115,12 @@ void InputSection::relocate( break; } case SymbolBody::SharedKind: - if (relocNeedsPLT(Type)) { + if (Target->relocNeedsPlt(Type)) { SymVA = PltSec.getEntryAddr(*Body); - Type = R_X86_64_PC32; - } else if (relocNeedsGOT(Type)) { + Type = Target->getPCRelReloc(); + } else if (Target->relocNeedsGot(Type)) { SymVA = GotSec.getEntryAddr(*Body); - Type = R_X86_64_PC32; + Type = Target->getPCRelReloc(); } else { continue; } diff --git a/lld/ELF/OutputSections.cpp b/lld/ELF/OutputSections.cpp index 4640d987d660..014503b178e4 100644 --- a/lld/ELF/OutputSections.cpp +++ b/lld/ELF/OutputSections.cpp @@ -10,6 +10,7 @@ #include "OutputSections.h" #include "Config.h" #include "SymbolTable.h" +#include "Target.h" using namespace llvm; using namespace llvm::object; @@ -42,21 +43,11 @@ template void PltSection::writeTo(uint8_t *Buf) { uintptr_t Start = reinterpret_cast(Buf); ArrayRef Jmp = {0xff, 0x25}; // jmpq *val(%rip) for (const SymbolBody *E : Entries) { + uint64_t GotEntryAddr = GotSec.getEntryAddr(*E); uintptr_t InstPos = reinterpret_cast(Buf); - - memcpy(Buf, Jmp.data(), Jmp.size()); - Buf += Jmp.size(); - - uintptr_t OffsetInPLT = (InstPos + 6) - Start; - intptr_t Delta = GotSec.getEntryAddr(*E) - (this->getVA() + OffsetInPLT); - assert(isInt<32>(Delta)); - support::endian::write32le(Buf, Delta); - Buf += 4; - - *Buf = 0x90; // nop - ++Buf; - *Buf = 0x90; // nop - ++Buf; + uint64_t PltEntryAddr = (InstPos - Start) + this->getVA(); + Target->writePltEntry(Buf, GotEntryAddr, PltEntryAddr); + Buf += 8; } } @@ -71,26 +62,6 @@ PltSection::getEntryAddr(const SymbolBody &B) const { return this->getVA() + B.getPltIndex() * EntrySize; } -bool lld::elf2::relocNeedsPLT(uint32_t Type) { - switch (Type) { - default: - return false; - case R_X86_64_PLT32: - return true; - } -} - -bool lld::elf2::relocNeedsGOT(uint32_t Type) { - if (relocNeedsPLT(Type)) - return true; - switch (Type) { - default: - return false; - case R_X86_64_GOTPCREL: - return true; - } -} - template void RelocationSection::writeTo(uint8_t *Buf) { const unsigned EntrySize = IsRela ? sizeof(Elf_Rela) : sizeof(Elf_Rel); bool IsMips64EL = Relocs[0].C.getFile()->getObj()->isMips64EL(); @@ -104,7 +75,7 @@ template void RelocationSection::writeTo(uint8_t *Buf) { uint32_t SymIndex = RI.getSymbol(IsMips64EL); const SymbolBody *Body = C.getFile()->getSymbolBody(SymIndex); uint32_t Type = RI.getType(IsMips64EL); - if (relocNeedsGOT(Type)) { + if (Target->relocNeedsGot(Type)) { P->r_offset = GotSec.getEntryAddr(*Body); P->setSymbolAndType(Body->getDynamicSymbolTableIndex(), R_X86_64_GLOB_DAT, IsMips64EL); diff --git a/lld/ELF/OutputSections.h b/lld/ELF/OutputSections.h index d23d3797f5f1..da25c750f277 100644 --- a/lld/ELF/OutputSections.h +++ b/lld/ELF/OutputSections.h @@ -29,9 +29,6 @@ template class OutputSection; template class ObjectFile; template class DefinedRegular; -bool relocNeedsPLT(uint32_t Type); -bool relocNeedsGOT(uint32_t Type); - template typename llvm::object::ELFFile::uintX_t getSymVA(const DefinedRegular *DR); diff --git a/lld/ELF/SymbolTable.cpp b/lld/ELF/SymbolTable.cpp index 43b82d688d04..ffebf1025e9c 100644 --- a/lld/ELF/SymbolTable.cpp +++ b/lld/ELF/SymbolTable.cpp @@ -11,9 +11,11 @@ #include "Config.h" #include "Error.h" #include "Symbols.h" +#include "Target.h" using namespace llvm; using namespace llvm::object; +using namespace llvm::ELF; using namespace lld; using namespace lld::elf2; @@ -37,7 +39,11 @@ void SymbolTable::addFile(std::unique_ptr File) { addELFFile(cast(FileP)); } -template void SymbolTable::init() { +template void SymbolTable::init(uint16_t EMachine) { + if (EMachine == EM_X86_64) + Target.reset(new X86_64TargetInfo()); + else + Target.reset(new X86TargetInfo()); if (Config->Shared) return; EntrySym = new (Alloc) Undefined("_start", Undefined::Synthetic); @@ -49,7 +55,7 @@ template void SymbolTable::addELFFile(ELFFileBase *File) { if (!Old->isCompatibleWith(*File)) error(Twine(Old->getName() + " is incompatible with " + File->getName())); } else { - init(); + init(File->getEMachine()); } if (auto *O = dyn_cast(File)) { diff --git a/lld/ELF/SymbolTable.h b/lld/ELF/SymbolTable.h index be7c32483b6c..bd69290fc98d 100644 --- a/lld/ELF/SymbolTable.h +++ b/lld/ELF/SymbolTable.h @@ -68,7 +68,7 @@ private: void addLazy(Lazy *New); void addMemberFile(Lazy *Body); - template void init(); + template void init(uint16_t EMachine); template void resolve(SymbolBody *Body); std::vector> ArchiveFiles; diff --git a/lld/ELF/Target.cpp b/lld/ELF/Target.cpp new file mode 100644 index 000000000000..a951a81f8af7 --- /dev/null +++ b/lld/ELF/Target.cpp @@ -0,0 +1,100 @@ +//===- Target.cpp ---------------------------------------------------------===// +// +// The LLVM Linker +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "Target.h" + +#include "llvm/ADT/ArrayRef.h" +#include "llvm/Support/Endian.h" +#include "llvm/Support/ELF.h" + +using namespace llvm; +using namespace llvm::ELF; + +namespace lld { +namespace elf2 { + +std::unique_ptr Target; + +TargetInfo::~TargetInfo() {} + +X86TargetInfo::X86TargetInfo() { PCRelReloc = R_386_PC32; } + +void X86TargetInfo::writePltEntry(uint8_t *Buf, uint64_t GotEntryAddr, + uint64_t PltEntryAddr) const { + ArrayRef Jmp = {0xff, 0x25}; // jmpl *val + memcpy(Buf, Jmp.data(), Jmp.size()); + Buf += Jmp.size(); + + assert(isUInt<32>(GotEntryAddr)); + support::endian::write32le(Buf, GotEntryAddr); + Buf += 4; + + ArrayRef Nops = {0x90, 0x90}; + memcpy(Buf, Nops.data(), Nops.size()); +} + +bool X86TargetInfo::relocNeedsGot(uint32_t Type) const { + if (relocNeedsPlt(Type)) + return true; + switch (Type) { + default: + return false; + case R_386_GOT32: + return true; + } +} + +bool X86TargetInfo::relocNeedsPlt(uint32_t Type) const { + switch (Type) { + default: + return false; + case R_386_PLT32: + return true; + } +} + +X86_64TargetInfo::X86_64TargetInfo() { PCRelReloc = R_X86_64_PC32; } + +void X86_64TargetInfo::writePltEntry(uint8_t *Buf, uint64_t GotEntryAddr, + uint64_t PltEntryAddr) const { + ArrayRef Jmp = {0xff, 0x25}; // jmpq *val(%rip) + memcpy(Buf, Jmp.data(), Jmp.size()); + Buf += Jmp.size(); + + uintptr_t NextPC = PltEntryAddr + 6; + uintptr_t Delta = GotEntryAddr - NextPC; + assert(isInt<32>(Delta)); + support::endian::write32le(Buf, Delta); + Buf += 4; + + ArrayRef Nops = {0x90, 0x90}; + memcpy(Buf, Nops.data(), Nops.size()); +} + +bool X86_64TargetInfo::relocNeedsGot(uint32_t Type) const { + if (relocNeedsPlt(Type)) + return true; + switch (Type) { + default: + return false; + case R_X86_64_GOTPCREL: + return true; + } +} + +bool X86_64TargetInfo::relocNeedsPlt(uint32_t Type) const { + switch (Type) { + default: + return false; + case R_X86_64_PLT32: + return true; + } +} +} +} diff --git a/lld/ELF/Target.h b/lld/ELF/Target.h new file mode 100644 index 000000000000..d45e88c30454 --- /dev/null +++ b/lld/ELF/Target.h @@ -0,0 +1,54 @@ +//===- Target.h -------------------------------------------------*- C++ -*-===// +// +// The LLVM Linker +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef LLD_ELF_TARGET_H +#define LLD_ELF_TARGET_H + +#include + +namespace lld { +namespace elf2 { +class SymbolBody; + +class TargetInfo { +public: + unsigned getPCRelReloc() const { return PCRelReloc; } + virtual void writePltEntry(uint8_t *Buf, uint64_t GotEntryAddr, + uint64_t PltEntryAddr) const = 0; + virtual bool relocNeedsGot(uint32_t Type) const = 0; + virtual bool relocNeedsPlt(uint32_t Type) const = 0; + virtual ~TargetInfo(); + +protected: + unsigned PCRelReloc; +}; + +class X86TargetInfo final : public TargetInfo { +public: + X86TargetInfo(); + void writePltEntry(uint8_t *Buf, uint64_t GotEntryAddr, + uint64_t PltEntryAddr) const override; + bool relocNeedsGot(uint32_t Type) const override; + bool relocNeedsPlt(uint32_t Type) const override; +}; + +class X86_64TargetInfo final : public TargetInfo { +public: + X86_64TargetInfo(); + void writePltEntry(uint8_t *Buf, uint64_t GotEntryAddr, + uint64_t PltEntryAddr) const override; + bool relocNeedsGot(uint32_t Type) const override; + bool relocNeedsPlt(uint32_t Type) const override; +}; + +extern std::unique_ptr Target; +} +} + +#endif diff --git a/lld/ELF/Writer.cpp b/lld/ELF/Writer.cpp index 9ba202d9f03c..b467ef379ea2 100644 --- a/lld/ELF/Writer.cpp +++ b/lld/ELF/Writer.cpp @@ -11,6 +11,7 @@ #include "Config.h" #include "OutputSections.h" #include "SymbolTable.h" +#include "Target.h" #include "llvm/Support/FileOutputBuffer.h" @@ -256,12 +257,12 @@ void Writer::scanRelocs( if (!S) continue; uint32_t Type = RI.getType(IsMips64EL); - if (relocNeedsPLT(Type)) { + if (Target->relocNeedsPlt(Type)) { if (Body->isInPlt()) continue; PltSec.addEntry(Body); } - if (relocNeedsGOT(Type)) { + if (Target->relocNeedsGot(Type)) { if (Body->isInGot()) continue; GotSec.addEntry(Body); diff --git a/lld/test/elf2/plt-i686.s b/lld/test/elf2/plt-i686.s new file mode 100644 index 000000000000..8891f9e35420 --- /dev/null +++ b/lld/test/elf2/plt-i686.s @@ -0,0 +1,57 @@ +// RUN: llvm-mc -filetype=obj -triple=i686-unknown-linux %s -o %t.o +// RUN: llvm-mc -filetype=obj -triple=i686-unknown-linux %p/Inputs/shared.s -o %t2.o +// RUN: lld -flavor gnu2 -shared %t2.o -o %t2.so +// RUN: lld -flavor gnu2 %t.o %t2.so -o %t +// RUN: llvm-readobj -s -r %t | FileCheck %s +// RUN: llvm-objdump -d %t | FileCheck --check-prefix=DISASM %s +// REQUIRES: x86 + +// CHECK: Name: .plt +// CHECK-NEXT: Type: SHT_PROGBITS +// CHECK-NEXT: Flags [ +// CHECK-NEXT: SHF_ALLOC +// CHECK-NEXT: SHF_EXECINSTR +// CHECK-NEXT: ] +// CHECK-NEXT: Address: 0x16000 +// CHECK-NEXT: Offset: +// CHECK-NEXT: Size: 16 +// CHECK-NEXT: Link: 0 +// CHECK-NEXT: Info: 0 +// CHECK-NEXT: AddressAlignment: 16 + +// CHECK: Relocations [ +// CHECK-NEXT: Section ({{.*}}) .rel.dyn { +// CHECK-NEXT: 0x15000 R_386_GLOB_DAT bar 0x0 +// CHECK-NEXT: 0x15004 R_386_GLOB_DAT zed 0x0 +// CHECK-NEXT: } +// CHECK-NEXT: ] + +// Unfortunately FileCheck can't do math, so we have to check for explicit +// values: + +// 0x16000 - (0x11000 + 1) - 4 = 20475 +// 0x16000 - (0x11005 + 1) - 4 = 20470 +// 0x16008 - (0x1100a + 1) - 4 = 20473 + +// DISASM: _start: +// DISASM-NEXT: 11000: e9 fb 4f 00 00 jmp 20475 +// DISASM-NEXT: 11005: e9 f6 4f 00 00 jmp 20470 +// DISASM-NEXT: 1100a: e9 f9 4f 00 00 jmp 20473 + +// 0x15000 = 86016 +// 0x15004 = 86020 + +// DISASM: Disassembly of section .plt: +// DISASM-NEXT: .plt: +// DISASM-NEXT: 16000: ff 25 00 50 01 00 jmpl *86016 +// DISASM-NEXT: 16006: 90 nop +// DISASM-NEXT: 16007: 90 nop +// DISASM-NEXT: 16008: ff 25 04 50 01 00 jmpl *86020 +// DISASM-NEXT: 1600e: 90 nop +// DISASM-NEXT: 1600f: 90 nop + +.global _start +_start: + jmp bar@PLT + jmp bar@PLT + jmp zed@PLT