Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions lib/Target/X86/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ llvm_add_library(
x86_64PLT.cpp
x86_64GOT.cpp
X86_32Emulation.cpp
X86_32GOT.cpp
X86_32LDBackend.cpp
X86_32Relocator.cpp)

Expand Down
26 changes: 26 additions & 0 deletions lib/Target/X86/X86_32GOT.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
//===- X86_32GOT.cpp------------------------------------------------------===//
// Part of the eld Project, under the BSD License
// See https://github.com/qualcomm/eld/LICENSE.txt for license information.
// SPDX-License-Identifier: BSD-3-Clause
//===----------------------------------------------------------------------===//

#include "X86_32GOT.h"
#include "eld/Readers/ELFSection.h"
#include <cstring>

using namespace eld;

X86_32GOTPLT0 *X86_32GOTPLT0::Create(ELFSection *pOutput, Module *pModule) {
return make<X86_32GOTPLT0>(pOutput, pModule);
}

llvm::ArrayRef<uint8_t> X86_32GOTPLT0::getContent() const {
std::memset(m_Value, 0, sizeof(m_Value));
if (m_Module) {
if (ELFSection *Dynamic = m_Module->getSection(".dynamic")) {
const uint32_t DynamicAddress = static_cast<uint32_t>(Dynamic->addr());
std::memcpy(m_Value, &DynamicAddress, sizeof(DynamicAddress));
}
}
return llvm::ArrayRef(m_Value);
}
35 changes: 35 additions & 0 deletions lib/Target/X86/X86_32GOT.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
//===- X86_32GOT.h--------------------------------------------------------===//
// Part of the eld Project, under the BSD License
// See https://github.com/qualcomm/eld/LICENSE.txt for license information.
// SPDX-License-Identifier: BSD-3-Clause
//===----------------------------------------------------------------------===//

#ifndef ELD_TARGET_X86_32_GOT_H
#define ELD_TARGET_X86_32_GOT_H

#include "eld/Core/Module.h"
#include "eld/Fragment/GOT.h"

namespace eld {

class X86_32GOTPLT0 : public GOT {
public:
X86_32GOTPLT0(ELFSection *pOutput, Module *pModule)
: GOT(GOT::GOTPLT0, pOutput, nullptr, 4, 12), m_Module(pModule),
m_Value{} {
if (pOutput)
pOutput->addFragmentAndUpdateSize(this);
}

llvm::ArrayRef<uint8_t> getContent() const override;

static X86_32GOTPLT0 *Create(ELFSection *pOutput, Module *pModule);

private:
Module *m_Module;
mutable uint8_t m_Value[12];
};

} // namespace eld

#endif
74 changes: 73 additions & 1 deletion lib/Target/X86/X86_32LDBackend.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,13 @@
//===----------------------------------------------------------------------===//

#include "X86_32LDBackend.h"
#include "X86_32GOT.h"
#include "X86_32Relocator.h"
#include "X86_32StandaloneInfo.h"
#include "eld/SymbolResolver/IRBuilder.h"
#include "llvm/BinaryFormat/ELF.h"

namespace eld {

X86_32LDBackend::X86_32LDBackend(Module &pModule, X86_32Info *pInfo)
: GNULDBackend(pModule, pInfo) {}

Expand All @@ -28,6 +30,76 @@ bool X86_32LDBackend::initRelocator() {

void X86_32LDBackend::initTargetSections(ObjectBuilder &) {}

void X86_32LDBackend::initDynamicSections(InputFile &pInputFile) {
// i386 dynamic relocations use 8-byte Elf32_Rel records with two 4-byte
// fields.
GNULDBackend::initDynamicSections(pInputFile,
{llvm::ELF::SHT_REL, 4, 4, 4, 4});
}

void X86_32LDBackend::ensureGOTPLT0() {
if (m_pGOTPLT0)
return;

if (!getGOTPLT()->hasFragments())
m_pGOTPLT0 = X86_32GOTPLT0::Create(getGOTPLT(), &m_Module);
}

void X86_32LDBackend::ensureGOTBaseRelocations() {
m_HasGOTBaseRelocations = true;
ensureGOTPLT0();
// R_386_GOTPC refers to this linker-defined symbol directly. Define it
// while scanning, before scanRelocation checks for undefined symbols.
if (!m_pGOTSymbol && !defineGOTSymbol())
m_Module.setFailure(true);
}

void X86_32LDBackend::initTargetSymbols() {
// _GLOBAL_OFFSET_TABLE_ is created by ensureGOTBaseRelocations() only after
// scanning identifies a supported GOT-base relocation. Creating it here
// would expose the symbol for links that do not use the i386 GOT base.
}

bool X86_32LDBackend::defineGOTSymbol() {
if (m_pGOTSymbol)
return true;
if (!m_pGOTPLT0)
return false;

constexpr const char *GOTSymbolName = "_GLOBAL_OFFSET_TABLE_";
InputFile *GOTInput = m_pGOTPLT0->getOwningSection()->getInputFile();
FragmentRef *GOTRef = make<FragmentRef>(*m_pGOTPLT0, 0);
m_pGOTSymbol =
m_Module.getIRBuilder()->addSymbol<IRBuilder::Force, IRBuilder::Resolve>(
GOTInput, GOTSymbolName, ResolveInfo::Object, ResolveInfo::Define,
ResolveInfo::Local, 0, 0, GOTRef, ResolveInfo::Hidden);

if (!m_pGOTSymbol)
return false;

m_pGOTSymbol->setShouldIgnore(false);
if (m_Module.getConfig().options().isSymbolTracingRequested() &&
m_Module.getConfig().options().traceSymbol(GOTSymbolName))
config().raise(Diag::target_specific_symbol) << GOTSymbolName;
return true;
}

// Bind _GLOBAL_OFFSET_TABLE_ to the address of the GOT base once layout has
// assigned final section addresses. The minimal i386 backend uses the
// GOTPLT0 fragment as the GOT base for R_386_GOTOFF/R_386_GOTPC in both static
// and shared final links.
bool X86_32LDBackend::finalizeTargetSymbols() {
if (config().codeGenType() == LinkerConfig::Object ||
!m_HasGOTBaseRelocations)
return true;

if (!m_pGOTSymbol || !m_pGOTPLT0)
return false;

m_pGOTSymbol->setValue(m_pGOTPLT0->getAddr(config().getDiagEngine()));
return true;
}

void X86_32LDBackend::initializeAttributes() {
getInfo().initializeAttributes(m_Module.getIRBuilder()->getInputBuilder());
}
Expand Down
17 changes: 14 additions & 3 deletions lib/Target/X86/X86_32LDBackend.h
Original file line number Diff line number Diff line change
Expand Up @@ -14,19 +14,26 @@
namespace eld {

class X86_32Info;
class X86_32GOTPLT0;

class X86_32LDBackend : public GNULDBackend {
public:
X86_32LDBackend(Module &pModule, X86_32Info *pInfo);
~X86_32LDBackend() override;

bool finalizeTargetSymbols() override { return true; }
bool finalizeTargetSymbols() override;
Relocator *getRelocator() const override;
bool initRelocator() override;
void initTargetSections(ObjectBuilder &pBuilder) override;
void initTargetSymbols() override {}
void initDynamicSections(InputFile &pInputFile) override;
void initTargetSymbols() override;
void initializeAttributes() override;

// Ensure that the i386 GOTPLT0 header exists when GOT-base relocations are
// used, even if no imported symbols need GOT slots.
void ensureGOTPLT0();
void ensureGOTBaseRelocations();

std::size_t PLTEntriesCount() const override { return 0; }
std::size_t GOTEntriesCount() const override { return 0; }

Expand All @@ -36,10 +43,14 @@ class X86_32LDBackend : public GNULDBackend {
}

private:
size_t getRelEntrySize() override { return 0; }
bool defineGOTSymbol();

size_t getRelEntrySize() override { return 8; }
size_t getRelaEntrySize() override { return 0; }

Relocator *m_pRelocator = nullptr;
X86_32GOTPLT0 *m_pGOTPLT0 = nullptr;
bool m_HasGOTBaseRelocations = false;
};

GNULDBackend *createX86_32LDBackend(Module &pModule);
Expand Down
33 changes: 33 additions & 0 deletions lib/Target/X86/X86_32RelocationFunctions.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
//===- X86_32RelocationFunctions.h---------------------------------------===//
// Part of the eld Project, under the BSD License
// See https://github.com/qualcomm/eld/LICENSE.txt for license information.
// SPDX-License-Identifier: BSD-3-Clause
//===----------------------------------------------------------------------===//
#ifndef ELD_TARGET_X86_32_RELOCATION_FUNCTIONS_H
#define ELD_TARGET_X86_32_RELOCATION_FUNCTIONS_H

#include "X86_32Relocator.h"

namespace eld {
namespace x86_32 {

using RelocationHandler = Relocator::Result (*)(Relocation &pReloc,
X86_32Relocator &pParent);

// R_386_NONE.
Relocator::Result none(Relocation &pReloc, X86_32Relocator &pParent);
// R_386_8, R_386_16, and R_386_32: S + A.
Relocator::Result relocAbs(Relocation &pReloc, X86_32Relocator &pParent);
// R_386_PC8, R_386_PC16, and R_386_PC32: S + A - P.
Relocator::Result relocPCRel(Relocation &pReloc, X86_32Relocator &pParent);
// R_386_GOTOFF: S + A - GOT.
Relocator::Result relocGOTOFF(Relocation &pReloc, X86_32Relocator &pParent);
// R_386_GOTPC: GOT + A - P.
Relocator::Result relocGOTPC(Relocation &pReloc, X86_32Relocator &pParent);
// Any relocation not implemented by the current i386 backend.
Relocator::Result unsupported(Relocation &pReloc, X86_32Relocator &pParent);

} // namespace x86_32
} // namespace eld

#endif
63 changes: 63 additions & 0 deletions lib/Target/X86/X86_32RelocationInfo.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
//===- X86_32RelocationInfo.h--------------------------------------------===//
// Part of the eld Project, under the BSD License
// See https://github.com/qualcomm/eld/LICENSE.txt for license information.
// SPDX-License-Identifier: BSD-3-Clause
//===----------------------------------------------------------------------===//
// Basic ELD policy types for i386 ELF32 relocations.
//
// The descriptor table is defined in X86_32RelocationTable.h and is indexed by
// the ELF relocation number.
//===----------------------------------------------------------------------===//

#ifndef ELD_TARGET_X86_32_RELOCATION_INFO_H
#define ELD_TARGET_X86_32_RELOCATION_INFO_H

#include <cstdint>

namespace eld {
namespace x86_32 {

// The width, in bits, of the field an i386 relocation writes into.
enum class EncodingWidth : uint8_t { None, Bits8, Bits16, Bits32 };

// No range check is required for a full-width field. Narrow fields accept
// values representable as either signed or unsigned.
//
// Signed checks require the result to fit the signed field range. R_386_PC16
// is a special case: it accepts a signed 17-bit result before truncating it
// to the 16-bit field.
enum class RangeCheck { None, SignedOrUnsigned, Signed, SignedPC16 };

// Keep the table size tied to LLVM's canonical relocation list. The array may
// contain holes because ELF relocation values are not required to be dense.
inline constexpr uint32_t getRelocationTableSize() {
uint32_t Max = 0;
#define ELF_RELOC(RelocName, Value) \
if (Value > Max) \
Max = Value;
#include "llvm/BinaryFormat/ELFRelocs/i386.def"
#undef ELF_RELOC
return Max + 1;
}

inline constexpr uint32_t RelocationTableSize = getRelocationTableSize();

// Number of bits in the field an i386 relocation writes.
inline constexpr unsigned getFieldBits(EncodingWidth Width) {
switch (Width) {
case EncodingWidth::Bits8:
return 8;
case EncodingWidth::Bits16:
return 16;
case EncodingWidth::Bits32:
return 32;
case EncodingWidth::None:
return 0;
}
return 0;
}

} // namespace x86_32
} // namespace eld

#endif
80 changes: 80 additions & 0 deletions lib/Target/X86/X86_32RelocationTable.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
//===- X86_32RelocationTable.h--------------------------------------------===//
// Part of the eld Project, under the BSD License
// See https://github.com/qualcomm/eld/LICENSE.txt for license information.
// SPDX-License-Identifier: BSD-3-Clause
//===----------------------------------------------------------------------===//
// The i386 relocation descriptor table.
//===----------------------------------------------------------------------===//

#ifndef ELD_TARGET_X86_32_RELOCATION_TABLE_H
#define ELD_TARGET_X86_32_RELOCATION_TABLE_H

#include "X86_32RelocationFunctions.h"
#include "X86_32RelocationInfo.h"
#include "llvm/BinaryFormat/ELF.h"

#include <array>

namespace eld {
namespace x86_32 {

struct RelocationDescriptor {
const char *Name = nullptr;
EncodingWidth Width = EncodingWidth::None;
RangeCheck Range = RangeCheck::None;
RelocationHandler Handler = &unsupported;
};

// This list contains ELD-specific behavior. Relocation names and numeric
// values come from LLVM's i386.def and are populated separately below.
// clang-format off
#define X86_32_RELOC_OVERRIDES(Add) \
Add(llvm::ELF::R_386_NONE, none, EncodingWidth::None, RangeCheck::None) \
Add(llvm::ELF::R_386_32, relocAbs, EncodingWidth::Bits32, RangeCheck::None) \
Add(llvm::ELF::R_386_16, relocAbs, EncodingWidth::Bits16, \
RangeCheck::SignedOrUnsigned) \
Add(llvm::ELF::R_386_8, relocAbs, EncodingWidth::Bits8, \
RangeCheck::SignedOrUnsigned) \
Add(llvm::ELF::R_386_PC32, relocPCRel, EncodingWidth::Bits32, \
RangeCheck::None) \
Add(llvm::ELF::R_386_PC16, relocPCRel, EncodingWidth::Bits16, \
RangeCheck::SignedPC16) \
Add(llvm::ELF::R_386_PC8, relocPCRel, EncodingWidth::Bits8, \
RangeCheck::Signed) \
Add(llvm::ELF::R_386_GOTOFF, relocGOTOFF, EncodingWidth::Bits32, \
RangeCheck::None) \
Add(llvm::ELF::R_386_GOTPC, relocGOTPC, EncodingWidth::Bits32, \
RangeCheck::None)
// clang-format on

inline std::array<RelocationDescriptor, RelocationTableSize>
createRelocationDescriptors() {
std::array<RelocationDescriptor, RelocationTableSize> Entries{};

// LLVM's i386.def is the source of truth for public relocation names and
// values. Entries not present in that definition remain unnamed and
// unsupported.
#define ELF_RELOC(RelocName, Value) Entries[Value].Name = #RelocName;
#include "llvm/BinaryFormat/ELFRelocs/i386.def"
#undef ELF_RELOC

// Add the ELD handler and encoding policy for each supported relocation.
#define ADD_X86_32_RELOC_OVERRIDE(Type, Function, FieldWidth, RangePolicy) \
Entries[Type].Handler = &Function; \
Entries[Type].Width = FieldWidth; \
Entries[Type].Range = RangePolicy;
X86_32_RELOC_OVERRIDES(ADD_X86_32_RELOC_OVERRIDE)
#undef ADD_X86_32_RELOC_OVERRIDE

return Entries;
}

inline const std::array<RelocationDescriptor, RelocationTableSize> Relocs =
createRelocationDescriptors();

#undef X86_32_RELOC_OVERRIDES

} // namespace x86_32
} // namespace eld

#endif
Loading
Loading