Relocate old .eh_frame section next to the new one.

Summary:
In order to improve gdb experience with BOLT we have to make
sure the output file has a single .eh_frame section. Otherwise
gdb will use either old or new section for unwinding purposes.

This diff relocates the original .eh_frame section next to
the new one generated by LLVM. Later we merge two sections
into one and make sure only the newly created section has
.eh_frame name.

(cherry picked from FBD4203943)
This commit is contained in:
Maksim Panchenko
2016-11-11 14:33:34 -08:00
parent 809c28f585
commit a7fb610eba
7 changed files with 312 additions and 69 deletions

View File

@@ -67,7 +67,8 @@ public:
std::multimap<uint64_t, std::string> GlobalAddresses;
/// [MCSymbol] -> [BinaryFunction]
std::unordered_map<const MCSymbol *, const BinaryFunction *> SymbolToFunctionMap;
std::unordered_map<const MCSymbol *,
const BinaryFunction *> SymbolToFunctionMap;
/// Map virtual address to a section.
std::map<uint64_t, SectionRef> AllocatableSections;

View File

@@ -3391,5 +3391,56 @@ void DynoStats::operator+=(const DynoStats &Other) {
}
}
size_t Relocation::getSizeForType(uint64_t Type) {
switch (Type) {
default:
llvm_unreachable("unsupported relocation type");
case ELF::R_X86_64_PC8:
return 1;
case ELF::R_X86_64_PLT32:
case ELF::R_X86_64_PC32:
case ELF::R_X86_64_32S:
case ELF::R_X86_64_32:
case ELF::R_X86_64_GOTPCREL:
case ELF::R_X86_64_GOTTPOFF:
case ELF::R_X86_64_TPOFF32:
return 4;
case ELF::R_X86_64_PC64:
case ELF::R_X86_64_64:
return 8;
}
}
size_t Relocation::emitTo(MCStreamer *Streamer) {
const auto Size = getSizeForType(Type);
auto &Ctx = Streamer->getContext();
switch (Type) {
default:
llvm_unreachable("unsupported relocation type");
case ELF::R_X86_64_PC8:
case ELF::R_X86_64_PC32: {
auto *TempLabel = Ctx.createTempSymbol();
Streamer->EmitLabel(TempLabel);
auto Value =
MCBinaryExpr::createSub(MCSymbolRefExpr::create(Symbol, Ctx),
MCSymbolRefExpr::create(TempLabel, Ctx),
Ctx);
if (Addend) {
Value = MCBinaryExpr::createAdd(Value,
MCConstantExpr::create(Addend, Ctx),
Ctx);
}
Streamer->EmitValue(Value, Size);
break;
}
case ELF::R_X86_64_64:
case ELF::R_X86_64_32:
case ELF::R_X86_64_32S:
Streamer->EmitSymbolValue(Symbol, Size);
break;
}
return Size;
}
} // namespace bolt
} // namespace llvm

View File

@@ -143,6 +143,26 @@ inline raw_ostream &operator<<(raw_ostream &OS, const DynoStats &Stats) {
DynoStats operator+(const DynoStats &A, const DynoStats &B);
/// Relocation class.
struct Relocation {
uint64_t Offset;
MCSymbol *Symbol;
uint64_t Type;
uint64_t Addend;
/// Return size of the given relocation \p Type.
static size_t getSizeForType(uint64_t Type);
/// Emit relocation at a current \p Streamer' position. The caller is
/// responsible for setting the position correctly.
size_t emitTo(MCStreamer *Streamer);
};
/// Relocation ordering by offset.
inline bool operator<(const Relocation &A, const Relocation &B) {
return A.Offset < B.Offset;
}
/// BinaryFunction is a representation of machine-level function.
///
/// We use the term "Binary" as "Machine" was already taken.

View File

@@ -693,28 +693,24 @@ bool CFIReaderWriter::fillCFIInfoFor(BinaryFunction &Function) const {
}
std::vector<char> CFIReaderWriter::generateEHFrameHeader(
const DWARFFrame &OldEHFrame,
const DWARFFrame &NewEHFrame,
uint64_t EHFrameHeaderAddress,
std::vector<uint64_t> &FailedAddresses) const {
// Common PC -> FDE map to be written into the new .eh_frame_hdr.
// Common PC -> FDE map to be written into .eh_frame_hdr.
std::map<uint64_t, uint64_t> PCToFDE;
// Presort array for binary search.
std::sort(FailedAddresses.begin(), FailedAddresses.end());
// Initialize PCToFDE using NewEHFrame.
for (const auto &NewEntry : NewEHFrame.Entries) {
const auto *NewFDE = dyn_cast<dwarf::FDE>(NewEntry.get());
// Ignore CIE entries.
if (!NewFDE) {
continue;
}
const auto FuncAddress = NewFDE->getInitialLocation();
const auto FDEAddress = NewEHFrame.EHFrameAddress + NewFDE->getOffset();
NewEHFrame.for_each_FDE([&](const dwarf::FDE *FDE) {
const auto FuncAddress = FDE->getInitialLocation();
const auto FDEAddress = NewEHFrame.EHFrameAddress + FDE->getOffset();
// Ignore FDEs pointing to zero.
// Ignore unused FDEs.
if (FuncAddress == 0)
continue;
return;
// Add the address to the map unless we failed to write it.
if (!std::binary_search(FailedAddresses.begin(), FailedAddresses.end(),
@@ -724,17 +720,17 @@ std::vector<char> CFIReaderWriter::generateEHFrameHeader(
<< Twine::utohexstr(FDEAddress) << '\n');
PCToFDE[FuncAddress] = FDEAddress;
}
}
});
DEBUG(dbgs() << "BOLT-DEBUG: new .eh_frame contains "
<< NewEHFrame.Entries.size() << " entries\n");
// Add entries from the original .eh_frame corresponding to the functions
// that we did not update.
for (const auto &FDEI : getFDEs()) {
const auto *OldFDE = FDEI.second;
const auto FuncAddress = OldFDE->getInitialLocation();
const auto FDEAddress = EHFrame.EHFrameAddress + OldFDE->getOffset();
OldEHFrame.for_each_FDE([&](const dwarf::FDE *FDE) {
const auto FuncAddress = FDE->getInitialLocation();
const auto FDEAddress = OldEHFrame.EHFrameAddress + FDE->getOffset();
// Add the address if we failed to write it.
if (PCToFDE.count(FuncAddress) == 0) {
DEBUG(dbgs() << "BOLT-DEBUG: old FDE for function at 0x"
@@ -742,10 +738,10 @@ std::vector<char> CFIReaderWriter::generateEHFrameHeader(
<< Twine::utohexstr(FDEAddress) << '\n');
PCToFDE[FuncAddress] = FDEAddress;
}
}
});
DEBUG(dbgs() << "BOLT-DEBUG: old .eh_frame contains "
<< EHFrame.Entries.size() << " entries\n");
<< OldEHFrame.Entries.size() << " entries\n");
// Generate a new .eh_frame_hdr based on the new map.

View File

@@ -44,11 +44,12 @@ public:
///
/// Take FDEs from the \p NewEHFrame unless their initial_pc is listed
/// in \p FailedAddresses. All other entries are taken from the
/// original .eh_frame.
/// \p OldEHFrame.
///
/// \p EHFrameHeaderAddress specifies location of the .eh_frame_hdr,
/// and is required to be set for relative addressing.
/// \p EHFrameHeaderAddress specifies location of .eh_frame_hdr,
/// and is required for relative addressing used in the section.
std::vector<char> generateEHFrameHeader(
const DWARFFrame &OldEHFrame,
const DWARFFrame &NewEHFrame,
uint64_t EHFrameHeaderAddress,
std::vector<uint64_t> &FailedAddresses) const;

View File

@@ -44,6 +44,7 @@
#include "llvm/Object/SymbolicFile.h"
#include "llvm/Support/Casting.h"
#include "llvm/Support/CommandLine.h"
#include "llvm/Support/Dwarf.h"
#include "llvm/Support/Errc.h"
#include "llvm/Support/ManagedStatic.h"
#include "llvm/Support/TargetSelect.h"
@@ -481,6 +482,7 @@ void RewriteInstance::reset() {
EHFrame = nullptr;
FailedAddresses.clear();
RangesSectionsWriter.reset();
SectionRelocations.clear();
TotalScore = 0;
}
@@ -994,6 +996,65 @@ void RewriteInstance::adjustFunctionBoundaries() {
}
}
void RewriteInstance::relocateEHFrameSection() {
assert(EHFrameSection.getObject() != nullptr &&
"non-empty .eh_frame section expected");
DWARFFrame EHFrame(EHFrameSection.getAddress());
StringRef EHFrameSectionContents;
EHFrameSection.getContents(EHFrameSectionContents);
DataExtractor DE(EHFrameSectionContents,
BC->AsmInfo->isLittleEndian(),
BC->AsmInfo->getPointerSize());
auto createReloc = [&](uint64_t Value, uint64_t Offset, uint64_t DwarfType) {
if (DwarfType == dwarf::DW_EH_PE_omit)
return;
if (!(DwarfType & dwarf::DW_EH_PE_pcrel) &&
!(DwarfType & dwarf::DW_EH_PE_textrel) &&
!(DwarfType & dwarf::DW_EH_PE_funcrel) &&
!(DwarfType & dwarf::DW_EH_PE_datarel)) {
return;
}
if (!(DwarfType & dwarf::DW_EH_PE_sdata4))
return;
uint64_t RelType;
switch (DwarfType & 0x0f) {
default:
llvm_unreachable("unsupported DWARF encoding type");
case dwarf::DW_EH_PE_sdata4:
case dwarf::DW_EH_PE_udata4:
RelType = ELF::R_X86_64_PC32;
break;
case dwarf::DW_EH_PE_sdata8:
case dwarf::DW_EH_PE_udata8:
RelType = ELF::R_X86_64_PC64;
break;
}
DEBUG(dbgs() << "BOLT-DEBUG: adding DWARF reference\n");
auto *Symbol = BC->getGlobalSymbolAtAddress(Value);
if (!Symbol) {
DEBUG(dbgs() << "BOLT-DEBUG: creating symbol for DWARF reference at 0x"
<< Twine::utohexstr(Value) << '\n');
Symbol = BC->getOrCreateGlobalSymbol(Value, "FUNCat");
}
addSectionRelocation(EHFrameSection, Offset, Symbol, RelType);
};
EHFrame.parse(DE, createReloc);
if (!EHFrame.ParseError.empty()) {
errs() << "BOLT-ERROR: EHFrame reader failed with message \""
<< EHFrame.ParseError << '\n';
exit(1);
}
}
BinaryFunction *RewriteInstance::createBinaryFunction(
const std::string &Name, SectionRef Section, uint64_t Address,
uint64_t Size, bool IsSimple) {
@@ -1022,6 +1083,8 @@ void RewriteInstance::readSpecialSections() {
LSDAAddress = Section.getAddress();
} else if (SectionName == ".debug_loc") {
DebugLocSize = Section.getSize();
} else if (SectionName == ".eh_frame") {
EHFrameSection = Section;
}
// Ignore zero-size allocatable sections as they present no interest to us.
@@ -1353,8 +1416,8 @@ void emitFunction(MCStreamer &Streamer, BinaryFunction &Function,
// from that. We also update the current CU debug info with the
// filename of the inlined function.
if (RowReference.DwCompileUnitIndex != OrigUnitID) {
Unit =
BC.DwCtx->getCompileUnitForOffset(RowReference.DwCompileUnitIndex);
Unit = BC.DwCtx->
getCompileUnitForOffset(RowReference.DwCompileUnitIndex);
OriginalLineTable = BC.DwCtx->getLineTableForUnit(Unit);
const auto Filenum =
OriginalLineTable->Rows[RowReference.RowIndex - 1].File;
@@ -1485,6 +1548,12 @@ void RewriteInstance::emitFunctions() {
if (opts::UpdateDebugSections)
updateDebugLineInfoForNonSimpleFunctions();
// Relocate .eh_frame to .eh_frame_old.
if (EHFrameSection.getObject() != nullptr) {
relocateEHFrameSection();
emitDataSection(Streamer.get(), EHFrameSection, ".eh_frame_old");
}
Streamer->Finish();
//////////////////////////////////////////////////////////////////////////////
@@ -1605,7 +1674,9 @@ void RewriteInstance::emitFunctions() {
}
// Map special sections to their addresses in the output image.
std::vector<std::string> Sections = { ".eh_frame", ".gcc_except_table",
// The order is important here.
std::vector<std::string> Sections = { ".eh_frame", ".eh_frame_old",
".gcc_except_table",
".rodata", ".rodata.cold" };
for (auto &SectionName : Sections) {
auto SMII = EFMM->SectionMapInfo.find(SectionName);
@@ -1656,6 +1727,55 @@ void RewriteInstance::emitFunctions() {
TempOut->keep();
}
void RewriteInstance::emitDataSection(MCStreamer *Streamer, SectionRef Section,
std::string Name) {
StringRef SectionName;
if (!Name.empty())
SectionName = Name;
else
Section.getName(SectionName);
auto *ELFSection = BC->Ctx->getELFSection(SectionName,
ELF::SHT_PROGBITS,
ELF::SHF_WRITE | ELF::SHF_ALLOC);
StringRef SectionContents;
Section.getContents(SectionContents);
Streamer->SwitchSection(ELFSection);
Streamer->EmitValueToAlignment(Section.getAlignment());
DEBUG(dbgs() << "BOLT-DEBUG: emitting section " << SectionName << '\n');
auto SRI = SectionRelocations.find(Section);
if (SRI == SectionRelocations.end()) {
Streamer->EmitBytes(SectionContents);
return;
}
auto &Relocations = SRI->second;
uint64_t SectionOffset = 0;
std::sort(Relocations.begin(), Relocations.end());
for (auto &Relocation : Relocations) {
assert(Relocation.Offset < Section.getSize() && "overflow detected");
if (SectionOffset < Relocation.Offset) {
Streamer->EmitBytes(
SectionContents.substr(SectionOffset,
Relocation.Offset - SectionOffset));
SectionOffset = Relocation.Offset;
}
DEBUG(dbgs() << "BOLT-DEBUG: emitting relocation for symbol "
<< Relocation.Symbol->getName() << " at offset 0x"
<< Twine::utohexstr(Relocation.Offset)
<< " with size "
<< Relocation::getSizeForType(Relocation.Type) << '\n');
auto RelocationSize = Relocation.emitTo(Streamer);
SectionOffset += RelocationSize;
}
assert(SectionOffset <= SectionContents.size() && "overflow error");
if (SectionOffset < SectionContents.size()) {
Streamer->EmitBytes(SectionContents.substr(SectionOffset));
}
}
bool RewriteInstance::checkLargeFunctions() {
LargeFunctions.clear();
for (auto &BFI : BinaryFunctions) {
@@ -1799,7 +1919,7 @@ void RewriteInstance::rewriteNoteSections() {
// New section size.
uint64_t Size = 0;
// Copy over section contents unless it's one of the sections we ovewrite.
// Copy over section contents unless it's one of the sections we overwrite.
if (!shouldOverwriteSection(*SectionName)) {
Size = Section.sh_size;
std::string Data = InputFile->getData().substr(Section.sh_offset, Size);
@@ -1916,6 +2036,7 @@ void RewriteInstance::patchELFSectionHeaderTable(ELFObjectFile<ELFT> *File) {
if (SMII != SectionMM->SectionMapInfo.end()) {
auto &SecInfo = SMII->second;
SecInfo.ShName = Section.sh_name;
NewSection.sh_name = 0;
}
OS.write(reinterpret_cast<const char *>(&NewSection), sizeof(NewSection));
@@ -2116,47 +2237,10 @@ void RewriteInstance::rewriteFile() {
SI.FileOffset);
}
// If .eh_frame is present it requires special handling.
// If .eh_frame is present create .eh_frame_hdr.
auto SMII = SectionMM->SectionMapInfo.find(".eh_frame");
if (SMII != SectionMM->SectionMapInfo.end()) {
auto &EHFrameSecInfo = SMII->second;
DEBUG(dbgs() << "BOLT: writing a new .eh_frame_hdr\n");
auto PaddingSize = OffsetToAlignment(NextAvailableAddress, EHFrameHdrAlign);
writePadding(Out->os(), PaddingSize);
NextAvailableAddress += PaddingSize;
SectionInfo EHFrameHdrSecInfo;
EHFrameHdrSecInfo.FileAddress = NextAvailableAddress;
EHFrameHdrSecInfo.FileOffset = getFileOffsetFor(NextAvailableAddress);
DWARFFrame NewEHFrame(EHFrameSecInfo.FileAddress);
NewEHFrame.parse(
DataExtractor(StringRef(reinterpret_cast<const char *>(
EHFrameSecInfo.AllocAddress),
EHFrameSecInfo.Size),
BC->AsmInfo->isLittleEndian(),
BC->AsmInfo->getPointerSize()));
if (!NewEHFrame.ParseError.empty()) {
errs() << "BOLT-ERROR: EHFrame reader failed with message \""
<< NewEHFrame.ParseError << '\n';
exit(1);
}
auto NewEHFrameHdr =
CFIRdWrt->generateEHFrameHeader(NewEHFrame,
EHFrameHdrSecInfo.FileAddress,
FailedAddresses);
EHFrameHdrSecInfo.Size = NewEHFrameHdr.size();
assert(Out->os().tell() == EHFrameHdrSecInfo.FileOffset &&
"offset mismatch");
Out->os().write(NewEHFrameHdr.data(), EHFrameHdrSecInfo.Size);
SectionMM->SectionMapInfo[".eh_frame_hdr"] = EHFrameHdrSecInfo;
NextAvailableAddress += EHFrameHdrSecInfo.Size;
writeEHFrameHeader(SMII->second);
}
// Patch program header table.
@@ -2188,6 +2272,71 @@ void RewriteInstance::rewriteFile() {
}
}
void RewriteInstance::writeEHFrameHeader(SectionInfo &EHFrameSecInfo) {
DWARFFrame NewEHFrame(EHFrameSecInfo.FileAddress);
NewEHFrame.parse(
DataExtractor(StringRef(reinterpret_cast<const char *>(
EHFrameSecInfo.AllocAddress),
EHFrameSecInfo.Size),
BC->AsmInfo->isLittleEndian(),
BC->AsmInfo->getPointerSize()));
if (!NewEHFrame.ParseError.empty()) {
errs() << "BOLT-ERROR: EHFrame reader failed with message \""
<< NewEHFrame.ParseError << '\n';
exit(1);
}
auto OldSMII = SectionMM->SectionMapInfo.find(".eh_frame_old");
assert(OldSMII != SectionMM->SectionMapInfo.end() &&
"expected .eh_frame_old to be present");
auto &OldEHFrameSecInfo = OldSMII->second;
DWARFFrame OldEHFrame(OldEHFrameSecInfo.FileAddress);
OldEHFrame.parse(
DataExtractor(StringRef(reinterpret_cast<const char *>(
OldEHFrameSecInfo.AllocAddress),
OldEHFrameSecInfo.Size),
BC->AsmInfo->isLittleEndian(),
BC->AsmInfo->getPointerSize()));
if (!OldEHFrame.ParseError.empty()) {
errs() << "BOLT-ERROR: EHFrame reader failed with message \""
<< OldEHFrame.ParseError << '\n';
exit(1);
}
DEBUG(dbgs() << "BOLT: writing a new .eh_frame_hdr\n");
auto PaddingSize = OffsetToAlignment(NextAvailableAddress, EHFrameHdrAlign);
writePadding(Out->os(), PaddingSize);
NextAvailableAddress += PaddingSize;
SectionInfo EHFrameHdrSecInfo;
EHFrameHdrSecInfo.FileAddress = NextAvailableAddress;
EHFrameHdrSecInfo.FileOffset = getFileOffsetFor(NextAvailableAddress);
auto NewEHFrameHdr =
CFIRdWrt->generateEHFrameHeader(OldEHFrame,
NewEHFrame,
EHFrameHdrSecInfo.FileAddress,
FailedAddresses);
EHFrameHdrSecInfo.Size = NewEHFrameHdr.size();
assert(Out->os().tell() == EHFrameHdrSecInfo.FileOffset &&
"offset mismatch");
Out->os().write(NewEHFrameHdr.data(), EHFrameHdrSecInfo.Size);
SectionMM->SectionMapInfo[".eh_frame_hdr"] = EHFrameHdrSecInfo;
NextAvailableAddress += EHFrameHdrSecInfo.Size;
// Merge .eh_frame and .eh_frame_old so that gdb can locate all FDEs.
EHFrameSecInfo.Size = OldEHFrameSecInfo.FileAddress + OldEHFrameSecInfo.Size
- EHFrameSecInfo.FileAddress;
SectionMM->SectionMapInfo.erase(OldSMII);
DEBUG(dbgs() << "BOLT-DEBUG: size of .eh_frame after merge is "
<< EHFrameSecInfo.Size << '\n');
}
bool RewriteInstance::shouldOverwriteSection(StringRef SectionName) {
if (opts::UpdateDebugSections) {
for (auto &OverwriteName : DebugSectionsToOverwrite) {

View File

@@ -156,6 +156,11 @@ public:
/// performing final relaxation.
void emitFunctions();
/// Emit data \p Section, possibly with relocations. Use name \p Name if
/// non-empty.
void emitDataSection(MCStreamer *Streamer, SectionRef Section,
std::string Name = "");
/// Update debug information in the file for re-written code.
void updateDebugInfo();
@@ -170,6 +175,19 @@ public:
/// rewritten.
void updateDebugLineInfoForNonSimpleFunctions();
/// Add section relocation.
void addSectionRelocation(SectionRef Section, uint64_t Address,
MCSymbol *Symbol, uint64_t Type,
uint64_t Addend = 0) {
auto RI = SectionRelocations.find(Section);
if (RI == SectionRelocations.end()) {
auto Result =
SectionRelocations.emplace(Section, std::vector<Relocation>());
RI = Result.first;
}
RI->second.emplace_back(Relocation{Address, Symbol, Type, Addend});
}
/// Rewrite back all functions (hopefully optimized) that fit in the original
/// memory footprint for that function. If the function is now larger and does
/// not fit in the binary, reject it and preserve the original version of the
@@ -187,9 +205,15 @@ private:
/// symbol table has been processed.
void adjustFunctionBoundaries();
/// Make .eh_frame section relocatable.
void relocateEHFrameSection();
/// Rewrite non-allocatable sections with modifications.
void rewriteNoteSections();
/// Write .eh_frame_hdr.
void writeEHFrameHeader(SectionInfo &EHFrameSecInfo);
/// Patch ELF book-keeping info.
void patchELF();
void patchELFPHDRTable();
@@ -313,12 +337,9 @@ private:
uint64_t NewTextSegmentOffset{0};
uint64_t NewTextSegmentSize{0};
/// Track next available address in the new text segment.
/// Track next available address for new allocatable sections.
uint64_t NextAvailableAddress{0};
/// Information on sections to re-write in the binary.
std::map<std::string, SectionInfo> SectionsToRewrite;
/// Store all non-zero symbols in this map for a quick address lookup.
std::map<uint64_t, llvm::object::SymbolRef> FileSymRefs;
@@ -337,6 +358,7 @@ private:
ArrayRef<uint8_t> LSDAData;
uint64_t LSDAAddress{0};
const llvm::DWARFFrame *EHFrame{nullptr};
SectionRef EHFrameSection;
/// Keep track of functions we fail to write in the binary. We need to avoid
/// rewriting CFI info for these functions.
@@ -352,6 +374,9 @@ private:
/// Total hotness score according to profiling data for this binary.
uint64_t TotalScore{0};
/// Section relocations.
std::map<SectionRef, std::vector<Relocation>> SectionRelocations;
/// Construct BinaryFunction object and add it to internal maps.
BinaryFunction *createBinaryFunction(const std::string &Name,
object::SectionRef Section,