References:
// llvm/include/llvm/MC/MCELFObjectWriter.h
struct ELFRelocationEntry {
uint64_t Offset; // Where is the relocation.
const MCSymbolELF *Symbol; // The symbol to relocate with.
unsigned Type; // The type of the relocation.
uint64_t Addend; // The addend to use.
const MCSymbolELF *OriginalSymbol; // The original value of Symbol if we changed it.
uint64_t OriginalAddend; // The original value of addend.
ELFRelocationEntry(uint64_t Offset, const MCSymbolELF *Symbol, unsigned Type,
uint64_t Addend, const MCSymbolELF *OriginalSymbol,
uint64_t OriginalAddend)
: Offset(Offset), Symbol(Symbol), Type(Type), Addend(Addend),
OriginalSymbol(OriginalSymbol), OriginalAddend(OriginalAddend) {}
void print(raw_ostream &Out) const {
Out << "Off=" << Offset << ", Sym=" << Symbol << ", Type=" << Type
<< ", Addend=" << Addend << ", OriginalSymbol=" << OriginalSymbol
<< ", OriginalAddend=" << OriginalAddend;
}
void dump() const { print(errs()); }
};
// llvm/lib/MC/ELFObjectWriter.cpp
class ELFObjectWriter : public MCObjectWriter {
// ...
DenseMap<const MCSectionELF *, std::vector<ELFRelocationEntry>> Relocations;
// ...
bool hasRelocationAddend() const; // yes for MIPS64
bool shouldRelocateWithSymbol(const MCAssembler &Asm,
const MCSymbolRefExpr *RefA,
const MCSymbolELF *Sym, uint64_t C,
unsigned Type) const;
public:
ELFObjectWriter(std::unique_ptr<MCELFObjectTargetWriter> MOTW)
: TargetObjectWriter(std::move(MOTW)) {}
void reset() override {
Relocations.clear();
Renames.clear();
MCObjectWriter::reset();
}
bool isSymbolRefDifferenceFullyResolvedImpl(const MCAssembler &Asm,
const MCSymbol &SymA,
const MCFragment &FB, bool InSet,
bool IsPCRel) const override;
virtual bool checkRelocation(MCContext &Ctx, SMLoc Loc,
const MCSectionELF *From,
const MCSectionELF *To) {
return true;
}
void recordRelocation(MCAssembler &Asm, const MCAsmLayout &Layout,
const MCFragment *Fragment, const MCFixup &Fixup,
MCValue Target, uint64_t &FixedValue) override;
void executePostLayoutBinding(MCAssembler &Asm,
const MCAsmLayout &Layout) override;
// ...
}
Relocations
is used in
ELFObjectWriter::reset()
ELFWriter::createRelocationSection()
. If OWriter.Relocations[&Sec] is not empty, then a relocation section will be created, with section name xxx.rela
or xxx.rel
.ELFWriter::writeRelocations()
, this is how the relocation section being finally written tothe ELF file??ELFWriter::writeRelocations()
.
void ELFWriter::writeRelocations(const MCAssembler &Asm,
const MCSectionELF &Sec) {
std::vector<ELFRelocationEntry> &Relocs = OWriter.Relocations[&Sec];
// We record relocations by pushing to the end of a vector. Reverse the vector
// to get the relocations in the order they were created.
// In most cases that is not important, but it can be for special sections
// (.eh_frame) or specific relocations (TLS optimizations on SystemZ).
std::reverse(Relocs.begin(), Relocs.end());
// Sort the relocation entries. MIPS needs this.
OWriter.TargetObjectWriter->sortRelocs(Asm, Relocs);
for (unsigned i = 0, e = Relocs.size(); i != e; ++i) {
const ELFRelocationEntry &Entry = Relocs[e - i - 1];
unsigned Index = Entry.Symbol ? Entry.Symbol->getIndex() : 0;
if (is64Bit()) {
write(Entry.Offset);
if (OWriter.TargetObjectWriter->getEMachine() == ELF::EM_MIPS) {
write(uint32_t(Index));
write(OWriter.TargetObjectWriter->getRSsym(Entry.Type));
write(OWriter.TargetObjectWriter->getRType3(Entry.Type));
write(OWriter.TargetObjectWriter->getRType2(Entry.Type));
write(OWriter.TargetObjectWriter->getRType(Entry.Type));
} else {
struct ELF::Elf64_Rela ERE64;
ERE64.setSymbolAndType(Index, Entry.Type);
write(ERE64.r_info);
}
if (hasRelocationAddend())
write(Entry.Addend);
} else {
write(uint32_t(Entry.Offset));
struct ELF::Elf32_Rela ERE32;
ERE32.setSymbolAndType(Index, Entry.Type);
write(ERE32.r_info);
if (hasRelocationAddend())
write(uint32_t(Entry.Addend));
if (OWriter.TargetObjectWriter->getEMachine() == ELF::EM_MIPS) {
if (uint32_t RType =
OWriter.TargetObjectWriter->getRType2(Entry.Type)) {
write(uint32_t(Entry.Offset));
ERE32.setSymbolAndType(0, RType);
write(ERE32.r_info);
write(uint32_t(0));
}
if (uint32_t RType =
OWriter.TargetObjectWriter->getRType3(Entry.Type)) {
write(uint32_t(Entry.Offset));
ERE32.setSymbolAndType(0, RType);
write(ERE32.r_info);
write(uint32_t(0));
}
}
}
}
}
created in ELFWriter::createRelocationSection
. If an section Sec
contains relocatable entries, it will be stored in a vector OWriter.Relocations[&Sec]
. If this vector is not empty(), a new section xxx.rela
or xxx.rel
will be created.
// llvm/lib/MC/ELFObjectWriter.cpp
MCSectionELF *ELFWriter::createRelocationSection(MCContext &Ctx,
const MCSectionELF &Sec) {
if (OWriter.Relocations[&Sec].empty())
return nullptr;
const StringRef SectionName = Sec.getSectionName();
std::string RelaSectionName = hasRelocationAddend() ? ".rela" : ".rel";
RelaSectionName += SectionName;
unsigned EntrySize;
if (hasRelocationAddend())
EntrySize = is64Bit() ? sizeof(ELF::Elf64_Rela) : sizeof(ELF::Elf32_Rela);
else
EntrySize = is64Bit() ? sizeof(ELF::Elf64_Rel) : sizeof(ELF::Elf32_Rel);
unsigned Flags = 0;
if (Sec.getFlags() & ELF::SHF_GROUP)
Flags = ELF::SHF_GROUP;
MCSectionELF *RelaSection = Ctx.createELFRelSection(
RelaSectionName, hasRelocationAddend() ? ELF::SHT_RELA : ELF::SHT_REL,
Flags, EntrySize, Sec.getGroup(), &Sec);
RelaSection->setAlignment(is64Bit() ? Align(8) : Align(4));
return RelaSection;
}
The above function creates a relocation section, by invoking Ctx.createELFRelSection()
. It will setting up with section name, entry size, flags, alignment, and a pointer to the original section.
// llvm/lib/MC/MCContext.cpp
MCSectionELF *MCContext::createELFRelSection(const Twine &Name, unsigned Type,
unsigned Flags, unsigned EntrySize,
const MCSymbolELF *Group,
const MCSectionELF *RelInfoSection) {
StringMap<bool>::iterator I;
bool Inserted;
std::tie(I, Inserted) =
RelSecNames.insert(std::make_pair(Name.str(), true));
return createELFSectionImpl(
I->getKey(), Type, Flags, SectionKind::getReadOnly(), EntrySize, Group,
true, cast<MCSymbolELF>(RelInfoSection->getBeginSymbol()));
}
Then in ELFWriter::writeObject()
, this relocation section is first pushed back to a vector containing all relocation sections Reclocations
and then the relocation entries will be written to the object file by scanning of all relocation sections.
// llvm/lib/MC/ELFObjectWriter.cpp
uint64_t ELFWriter::writeObject(MCAssembler &Asm, const MCAsmLayout &Layout) {
std::vector<MCSectionELF *> Relocations;
for (MCSection &Sec : Asm) {
// ... create relocation section .xxx.rela for .xxx
MCSectionELF *RelSection = createRelocationSection(Ctx, Section);
// ...
if (RelSection) {
SectionIndexMap[RelSection] = addToSectionTable(RelSection);
Relocations.push_back(RelSection);
}
}
// ... scan all relocation sections and write them to object file
// by calling writeRelocations()
for (MCSectionELF *RelSection : Relocations) {
align(RelSection->getAlignment());
// Remember the offset into the file for this section.
uint64_t SecStart = W.OS.tell();
writeRelocations(Asm,
cast<MCSectionELF>(*RelSection->getLinkedToSection()));
uint64_t SecEnd = W.OS.tell();
SectionOffsets[RelSection] = std::make_pair(SecStart, SecEnd);
}
}
writeRelocations()
is where the .xxx.rel
section being filled up based on the vector of relocation entries stored in OWriter.Relocations[&Sec]
:
void ELFWriter::writeRelocations(const MCAssembler &Asm,
const MCSectionELF &Sec) {
std::vector<ELFRelocationEntry> &Relocs = OWriter.Relocations[&Sec];
// We record relocations by pushing to the end of a vector. Reverse the vector
// to get the relocations in the order they were created.
// In most cases that is not important, but it can be for special sections
// (.eh_frame) or specific relocations (TLS optimizations on SystemZ).
std::reverse(Relocs.begin(), Relocs.end());
// Sort the relocation entries. MIPS needs this.
OWriter.TargetObjectWriter->sortRelocs(Asm, Relocs);
for (unsigned i = 0, e = Relocs.size(); i != e; ++i) {
const ELFRelocationEntry &Entry = Relocs[e - i - 1];
unsigned Index = Entry.Symbol ? Entry.Symbol->getIndex() : 0;
if (is64Bit()) {
write(Entry.Offset);
if (OWriter.TargetObjectWriter->getEMachine() == ELF::EM_MIPS) {
write(uint32_t(Index));
write(OWriter.TargetObjectWriter->getRSsym(Entry.Type));
write(OWriter.TargetObjectWriter->getRType3(Entry.Type));
write(OWriter.TargetObjectWriter->getRType2(Entry.Type));
write(OWriter.TargetObjectWriter->getRType(Entry.Type));
} else {
struct ELF::Elf64_Rela ERE64;
ERE64.setSymbolAndType(Index, Entry.Type);
write(ERE64.r_info);
}
if (hasRelocationAddend())
write(Entry.Addend);
} else {
write(uint32_t(Entry.Offset));
struct ELF::Elf32_Rela ERE32;
ERE32.setSymbolAndType(Index, Entry.Type);
write(ERE32.r_info);
if (hasRelocationAddend())
write(uint32_t(Entry.Addend));
if (OWriter.TargetObjectWriter->getEMachine() == ELF::EM_MIPS) {
if (uint32_t RType =
OWriter.TargetObjectWriter->getRType2(Entry.Type)) {
write(uint32_t(Entry.Offset));
ERE32.setSymbolAndType(0, RType);
write(ERE32.r_info);
write(uint32_t(0));
}
if (uint32_t RType =
OWriter.TargetObjectWriter->getRType3(Entry.Type)) {
write(uint32_t(Entry.Offset));
ERE32.setSymbolAndType(0, RType);
write(ERE32.r_info);
write(uint32_t(0));
}
}
}
}
}
All relocations for TextSection
is stored in OWriter.Relocations[&TextSection]
.
Each relocation entry is created by ELFObjectWriter
. See memo on ELFObjectWriter class
MCAssembler::layout ==> MCAssembler::handleFixup() ==>
ELFObjectWriter::recordRelocation() ==> ELFObjectWriter::shouldRelocateWithSymbol() ==>
If you could revise
the fundmental principles of
computer system design
to improve security...
... what would you change?