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
2 changes: 1 addition & 1 deletion lib/Target/X86/X86_32LDBackend.h
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ class X86_32LDBackend : public GNULDBackend {
}

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

Relocator *m_pRelocator = nullptr;
Expand Down
27 changes: 27 additions & 0 deletions lib/Target/X86/X86_32RelocationFunctions.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
//===- 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);
// Any relocation not implemented by the current i386 backend.
Relocator::Result unsupported(Relocation &pReloc, X86_32Relocator &pParent);

} // namespace x86_32
} // namespace eld

#endif
59 changes: 59 additions & 0 deletions lib/Target/X86/X86_32RelocationInfo.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
//===- 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.
enum class RangeCheck { None, SignedOrUnsigned };

// 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
70 changes: 70 additions & 0 deletions lib/Target/X86/X86_32RelocationTable.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
//===- 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)
// 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
157 changes: 149 additions & 8 deletions lib/Target/X86/X86_32Relocator.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,28 +3,169 @@
// See https://github.com/qualcomm/eld/LICENSE.txt for license information.
// SPDX-License-Identifier: BSD-3-Clause
//===----------------------------------------------------------------------===//
// i386 uses Elf32_Rel records: there is no r_addend field, so the addend is
// read from (and the result is written back into) the relocation's target
// field.
//===----------------------------------------------------------------------===//

#include "X86_32Relocator.h"
#include "X86_32RelocationTable.h"
#include "eld/Config/LinkerConfig.h"
#include "eld/Diagnostics/DiagnosticEngine.h"
#include "eld/SymbolResolver/LDSymbol.h"
#include "llvm/BinaryFormat/ELF.h"
#include "llvm/Support/MathExtras.h"

namespace eld {
namespace {

bool isSupportedRelocation(Relocation::Type Type) {
return Type < x86_32::RelocationTableSize &&
x86_32::Relocs[Type].Handler != &x86_32::unsupported;
}

// i386 is SHT_REL: the addend is stored in the target field itself, sign
// extended from the width of the field it was read from.
Relocator::DWord getImplicitAddend(const Relocation &pReloc) {
const x86_32::RelocationDescriptor &Info = x86_32::Relocs[pReloc.type()];
const unsigned Bits = x86_32::getFieldBits(Info.Width);
return llvm::SignExtend64(pReloc.target(), Bits) + pReloc.addend();
}

// Range-check the computed relocation value and, if it fits, encode it into
// the low bits of the relocation's target field.
Relocator::Result applyRelocationValue(Relocation &pReloc, uint64_t Value,
X86_32Relocator &Parent) {
const x86_32::RelocationDescriptor &Info = x86_32::Relocs[pReloc.type()];
const unsigned Bits = x86_32::getFieldBits(Info.Width);
// ELF32 relocation expressions wrap at 32 bits before narrow-field checks.
const uint32_t Value32 = static_cast<uint32_t>(Value);
const int64_t SignedValue = llvm::SignExtend64(Value32, 32);

if (Info.Range == x86_32::RangeCheck::SignedOrUnsigned) {
if (!llvm::isIntN(Bits, SignedValue) &&
!llvm::isUIntN(Bits, static_cast<uint64_t>(SignedValue))) {
pReloc.issueSignedOverflow(Parent, SignedValue, llvm::minIntN(Bits),
llvm::maxUIntN(Bits));
return Relocator::Overflow;
}
}

const uint32_t Mask = Bits == 32 ? 0xFFFFFFFFU : (1U << Bits) - 1;
pReloc.target() =
(static_cast<uint32_t>(pReloc.target()) & ~Mask) | (Value32 & Mask);
return Relocator::OK;
}

} // namespace

//===--------------------------------------------------------------------===//
// X86_32Relocator
//===--------------------------------------------------------------------===//
X86_32Relocator::X86_32Relocator(X86_32LDBackend &pParent,
LinkerConfig &pConfig, Module &pModule)
: Relocator(pConfig, pModule), m_Target(pParent) {}

Relocator::Result X86_32Relocator::applyRelocation(Relocation &) {
return Relocator::Unsupport;
Relocator::Result X86_32Relocator::applyRelocation(Relocation &pRelocation) {
const Relocation::Type Type = pRelocation.type();
if (Type >= x86_32::RelocationTableSize)
return Relocator::Unknown;

ResolveInfo *SymInfo = pRelocation.symInfo();
if (SymInfo) {
LDSymbol *OutSymbol = SymInfo->outSymbol();
if (OutSymbol && OutSymbol->hasFragRef()) {
ELFSection *Section = OutSymbol->fragRef()->frag()->getOwningSection();
if (Section->isDiscard() || (Section->getOutputSection() &&
Section->getOutputSection()->isDiscard())) {
std::lock_guard<std::mutex> RelocGuard(m_RelocMutex);
issueUndefRef(pRelocation, *Section->getInputFile(), Section);
return Relocator::OK;
}
}
}

return x86_32::Relocs[Type].Handler(pRelocation, *this);
}

void X86_32Relocator::scanRelocation(Relocation &pReloc, eld::IRBuilder &,
ELFSection &pSection,
InputFile &pInputFile, CopyRelocs &) {
if (LinkerConfig::Object == config().codeGenType())
return;

if (!isSupportedRelocation(pReloc.type())) {
config().raise(Diag::unsupported_reloc)
<< pReloc.type() << pSection.getDecoratedName(config().options())
<< pInputFile.getInput()->decoratedPath();
return;
}

ResolveInfo *SymInfo = pReloc.symInfo();
assert(SymInfo != nullptr &&
"ResolveInfo of relocation not set while scanRelocation");

if (m_Module.getPrinter()->traceReloc()) {
std::lock_guard<std::mutex> RelocGuard(m_RelocMutex);
const std::string RelocName = getName(pReloc.type());
if (config().options().traceReloc(RelocName))
config().raise(Diag::reloc_trace)
<< RelocName << SymInfo->name()
<< pInputFile.getInput()->decoratedPath();
}

if (SymInfo->isUndef() || SymInfo->isBitCode()) {
std::lock_guard<std::mutex> RelocGuard(m_RelocMutex);
if (m_Target.canIssueUndef(SymInfo)) {
if (SymInfo->visibility() != ResolveInfo::Default)
issueInvisibleRef(pReloc, pInputFile);
issueUndefRef(pReloc, pInputFile, &pSection);
}
}
}

void X86_32Relocator::scanRelocation(Relocation &, eld::IRBuilder &,
ELFSection &, InputFile &, CopyRelocs &) {}
const char *X86_32Relocator::getName(Relocation::Type pType) const {
if (pType >= x86_32::RelocationTableSize || !x86_32::Relocs[pType].Name)
return "INVALID_RELOC";
return x86_32::Relocs[pType].Name;
}

Relocation::Size X86_32Relocator::getSize(Relocation::Type pType) const {
if (pType >= x86_32::RelocationTableSize)
return 0;
return x86_32::getFieldBits(x86_32::Relocs[pType].Width);
}

const char *X86_32Relocator::getName(Relocation::Type) const {
return "<unsupported i386 relocation>";
uint32_t X86_32Relocator::getNumRelocs() const {
return x86_32::RelocationTableSize;
}

Relocation::Size X86_32Relocator::getSize(Relocation::Type) const { return 0; }
//===--------------------------------------------------------------------===//
// Relocation handlers
//===--------------------------------------------------------------------===//
namespace x86_32 {

uint32_t X86_32Relocator::getNumRelocs() const { return 0; }
Relocator::Result none(Relocation &, X86_32Relocator &) {
return Relocator::OK;
}

// R_386_8, R_386_16, R_386_32: S + A.
Relocator::Result relocAbs(Relocation &pReloc, X86_32Relocator &pParent) {
Relocator::Address Symbol = pReloc.symValue(pParent.module());
const Relocator::DWord Addend = getImplicitAddend(pReloc);
ResolveInfo *SymInfo = pReloc.symInfo();

// A weak undefined symbol has value zero in a static executable.
if (SymInfo && SymInfo->isWeakUndef() &&
pParent.config().codeGenType() == LinkerConfig::Exec)
Symbol = 0;

return applyRelocationValue(pReloc, Symbol + Addend, pParent);
}

Relocator::Result unsupported(Relocation &, X86_32Relocator &) {
return Relocator::Unsupport;
}

} // namespace x86_32
} // namespace eld
2 changes: 2 additions & 0 deletions lib/Target/X86/X86_32Relocator.h
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@

#include "X86_32LDBackend.h"
#include "eld/Target/Relocator.h"
#include "llvm/BinaryFormat/ELF.h"

namespace eld {

Expand All @@ -31,6 +32,7 @@ class X86_32Relocator : public Relocator {

Size getSize(Relocation::Type) const override;
uint32_t getNumRelocs() const override;
uint32_t relocType() const override { return llvm::ELF::SHT_REL; }

private:
X86_32LDBackend &m_Target;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
#---AbsoluteRelocation.test------------ i386 ELF32 relocations -------------#
#BEGIN_COMMENT
# Verify normal i386 absolute relocations use the addend stored in the target
# field and write the expected 8-, 16-, and 32-bit values.
#END_COMMENT
#START_TEST
RUN: %clang -c %p/Inputs/absolute.s -o %t.absolute.o
RUN: %link %linkopts -m elf_i386 -T %p/Inputs/absolute.t %t.absolute.o -o %t.absolute.out
RUN: %readelf -x .text %t.absolute.out | %filecheck %s --check-prefix=BYTES
RUN: %readelf -r %t.absolute.out | %filecheck %s --check-prefix=RELOCS

# target is at address 8. The in-place addends are 1, 2, and 3.
BYTES: Hex dump of section '.text':
BYTES-NEXT: 0x00000000 090a000b 0000005a 00

RELOCS: R_386_8
RELOCS-NEXT: R_386_16
RELOCS-NEXT: R_386_32
RELOCS-NEXT: R_386_NONE
#END_TEST
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
.section .text,"ax",@progbits
.globl _start
.type _start,@function
_start:
.byte 0x01
.value 0x0002
.long 0x00000003
.byte 0x5a
.reloc 0, R_386_8, target
.reloc 1, R_386_16, target
.reloc 3, R_386_32, target
.reloc 7, R_386_NONE, target
target:
.byte 0
.size _start, .-_start
Loading
Loading