forked from OSchip/llvm-project
[DWARF][BOLT] Convert DW_AT_(low|high)_pc to DW_AT_ranges only if necessary
Summary: While updating DWARF, we used to convert address ranges for functions into DW_AT_ranges format, even if the ranges were not split and still had a simple [low, high) form. We had to do this because functions with contiguous ranges could be sharing an abbrev with non-contiguous range function, and we had to convert the abbrev. It turns out, that the excessive usage of DW_AT_ranges may lead to internal core dumps in gdb in the presence of .gdb_index. I still don't know the root cause of it, but reducing the number DW_AT_ranges used by DW_TAG_subprogram DIEs does alleviate the issue. We can keep a simple range for DIEs that are guaranteed not to share an abbrev with any non-contiguous function. Hence we have to postpone the update of function ranges until we've seen all DIEs. Note that DIEs from different compilation units could share the same abbrev, and hence we have to process DIEs from all compilation units. (cherry picked from FBD14814043)
This commit is contained in:
parent
c8a927696c
commit
624a0e810d
|
@ -61,6 +61,12 @@ void DWARFRewriter::updateDebugInfo() {
|
|||
SectionPatchers[".debug_abbrev"] = llvm::make_unique<DebugAbbrevPatcher>();
|
||||
SectionPatchers[".debug_info"] = llvm::make_unique<SimpleBinaryPatcher>();
|
||||
|
||||
DebugInfoPatcher =
|
||||
static_cast<SimpleBinaryPatcher *>(SectionPatchers[".debug_info"].get());
|
||||
AbbrevPatcher =
|
||||
static_cast<DebugAbbrevPatcher *>(SectionPatchers[".debug_abbrev"].get());
|
||||
assert(DebugInfoPatcher && AbbrevPatcher && "Patchers not initialized.");
|
||||
|
||||
RangesSectionsWriter = llvm::make_unique<DebugRangesSectionsWriter>(&BC);
|
||||
LocationListWriter = llvm::make_unique<DebugLocWriter>(&BC);
|
||||
|
||||
|
@ -69,6 +75,8 @@ void DWARFRewriter::updateDebugInfo() {
|
|||
std::vector<const BinaryFunction *>{});
|
||||
}
|
||||
|
||||
flushPendingRanges();
|
||||
|
||||
finalizeDebugSections();
|
||||
|
||||
updateGdbIndexSection();
|
||||
|
@ -98,19 +106,29 @@ void DWARFRewriter::updateUnitDebugInfo(
|
|||
if (DIE.getLowAndHighPC(LowPC, HighPC, SectionIndex)) {
|
||||
IsFunctionDef = true;
|
||||
const auto *Function = BC.getBinaryFunctionAtAddress(LowPC);
|
||||
if (Function && Function->isFolded()) {
|
||||
if (Function && Function->isFolded())
|
||||
Function = nullptr;
|
||||
}
|
||||
FunctionStack.push_back(Function);
|
||||
auto RangesSectionOffset =
|
||||
RangesSectionsWriter->getEmptyRangesOffset();
|
||||
if (Function) {
|
||||
auto FunctionRanges = Function->getOutputAddressRanges();
|
||||
RangesSectionOffset =
|
||||
RangesSectionsWriter->addRanges(Function,
|
||||
std::move(FunctionRanges));
|
||||
|
||||
const auto *Abbrev = DIE.getAbbreviationDeclarationPtr();
|
||||
assert(Abbrev && "abbrev expected");
|
||||
|
||||
DebugAddressRangesVector FunctionRanges;
|
||||
if (Function)
|
||||
FunctionRanges = Function->getOutputAddressRanges();
|
||||
|
||||
if (FunctionRanges.size() > 1) {
|
||||
convertPending(Abbrev);
|
||||
convertToRanges(DIE, FunctionRanges);
|
||||
} else if (ConvertedRangesAbbrevs.find(Abbrev) !=
|
||||
ConvertedRangesAbbrevs.end()) {
|
||||
convertToRanges(DIE, FunctionRanges);
|
||||
} else {
|
||||
if (FunctionRanges.empty())
|
||||
FunctionRanges.emplace_back(DebugAddressRange());
|
||||
PendingRanges[Abbrev].emplace_back(
|
||||
std::make_pair(DIE, FunctionRanges.front()));
|
||||
}
|
||||
updateDWARFObjectAddressRanges(DIE, RangesSectionOffset);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
@ -184,9 +202,6 @@ void DWARFRewriter::updateUnitDebugInfo(
|
|||
}
|
||||
}
|
||||
|
||||
auto DebugInfoPatcher =
|
||||
static_cast<SimpleBinaryPatcher *>(
|
||||
SectionPatchers[".debug_info"].get());
|
||||
DebugInfoPatcher->addLE32Patch(AttrOffset, LocListSectionOffset);
|
||||
} else {
|
||||
assert((Value.isFormClass(DWARFFormValue::FC_Exprloc) ||
|
||||
|
@ -206,9 +221,6 @@ void DWARFRewriter::updateUnitDebugInfo(
|
|||
<< " for DIE with tag " << DIE.getTag()
|
||||
<< " to 0x" << Twine::utohexstr(NewAddress) << '\n');
|
||||
}
|
||||
auto DebugInfoPatcher =
|
||||
static_cast<SimpleBinaryPatcher *>(
|
||||
SectionPatchers[".debug_info"].get());
|
||||
DebugInfoPatcher->addLE64Patch(AttrOffset, NewAddress);
|
||||
} else if (opts::Verbosity >= 1) {
|
||||
errs() << "BOLT-WARNING: unexpected form value for attribute at 0x"
|
||||
|
@ -237,17 +249,10 @@ void DWARFRewriter::updateDWARFObjectAddressRanges(
|
|||
}
|
||||
|
||||
if (opts::Verbosity >= 2 && DebugRangesOffset == -1U) {
|
||||
errs() << "BOLT-WARNING: using invalid DW_AT_range for DIE at offset 0x"
|
||||
errs() << "BOLT-WARNING: using invalid DW_AT_ranges for DIE at offset 0x"
|
||||
<< Twine::utohexstr(DIE.getOffset()) << '\n';
|
||||
}
|
||||
|
||||
auto DebugInfoPatcher =
|
||||
static_cast<SimpleBinaryPatcher *>(SectionPatchers[".debug_info"].get());
|
||||
auto AbbrevPatcher =
|
||||
static_cast<DebugAbbrevPatcher*>(SectionPatchers[".debug_abbrev"].get());
|
||||
|
||||
assert(DebugInfoPatcher && AbbrevPatcher && "Patchers not initialized.");
|
||||
|
||||
const auto *AbbreviationDecl = DIE.getAbbreviationDeclarationPtr();
|
||||
if (!AbbreviationDecl) {
|
||||
if (opts::Verbosity >= 1) {
|
||||
|
@ -258,8 +263,6 @@ void DWARFRewriter::updateDWARFObjectAddressRanges(
|
|||
return;
|
||||
}
|
||||
|
||||
auto AbbrevCode = AbbreviationDecl->getCode();
|
||||
|
||||
if (AbbreviationDecl->findAttributeIndex(dwarf::DW_AT_ranges)) {
|
||||
// Case 1: The object was already non-contiguous and had DW_AT_ranges.
|
||||
// In this case we simply need to update the value of DW_AT_ranges.
|
||||
|
@ -282,50 +285,8 @@ void DWARFRewriter::updateDWARFObjectAddressRanges(
|
|||
// large size.
|
||||
if (AbbreviationDecl->findAttributeIndex(dwarf::DW_AT_low_pc) &&
|
||||
AbbreviationDecl->findAttributeIndex(dwarf::DW_AT_high_pc)) {
|
||||
uint32_t LowPCOffset = -1U;
|
||||
uint32_t HighPCOffset = -1U;
|
||||
DWARFFormValue LowPCFormValue =
|
||||
*DIE.find(dwarf::DW_AT_low_pc, &LowPCOffset);
|
||||
DWARFFormValue HighPCFormValue =
|
||||
*DIE.find(dwarf::DW_AT_high_pc, &HighPCOffset);
|
||||
|
||||
if (LowPCFormValue.getForm() != dwarf::DW_FORM_addr ||
|
||||
(HighPCFormValue.getForm() != dwarf::DW_FORM_addr &&
|
||||
HighPCFormValue.getForm() != dwarf::DW_FORM_data8 &&
|
||||
HighPCFormValue.getForm() != dwarf::DW_FORM_data4)) {
|
||||
errs() << "BOLT-WARNING: unexpected form value. Cannot update DIE "
|
||||
<< "at offset 0x" << Twine::utohexstr(DIE.getOffset())
|
||||
<< "\n";
|
||||
return;
|
||||
}
|
||||
if (LowPCOffset == -1U || (LowPCOffset + 8 != HighPCOffset)) {
|
||||
errs() << "BOLT-WARNING: high_pc expected immediately after low_pc. "
|
||||
<< "Cannot update DIE at offset 0x"
|
||||
<< Twine::utohexstr(DIE.getOffset()) << '\n';
|
||||
return;
|
||||
}
|
||||
|
||||
AbbrevPatcher->addAttributePatch(DIE.getDwarfUnit(),
|
||||
AbbrevCode,
|
||||
dwarf::DW_AT_low_pc,
|
||||
dwarf::DW_AT_ranges,
|
||||
dwarf::DW_FORM_sec_offset);
|
||||
AbbrevPatcher->addAttributePatch(DIE.getDwarfUnit(),
|
||||
AbbrevCode,
|
||||
dwarf::DW_AT_high_pc,
|
||||
dwarf::DW_AT_low_pc,
|
||||
dwarf::DW_FORM_udata);
|
||||
unsigned LowPCSize = 0;
|
||||
if (HighPCFormValue.getForm() == dwarf::DW_FORM_addr ||
|
||||
HighPCFormValue.getForm() == dwarf::DW_FORM_data8) {
|
||||
LowPCSize = 12;
|
||||
} else if (HighPCFormValue.getForm() == dwarf::DW_FORM_data4) {
|
||||
LowPCSize = 8;
|
||||
} else {
|
||||
llvm_unreachable("unexpected form");
|
||||
}
|
||||
DebugInfoPatcher->addLE32Patch(LowPCOffset, DebugRangesOffset);
|
||||
DebugInfoPatcher->addUDataPatch(LowPCOffset + 4, 0, LowPCSize);
|
||||
convertToRanges(AbbreviationDecl);
|
||||
convertToRanges(DIE, DebugRangesOffset);
|
||||
} else {
|
||||
if (opts::Verbosity >= 1) {
|
||||
errs() << "BOLT-WARNING: Cannot update ranges for DIE at offset 0x"
|
||||
|
@ -597,3 +558,116 @@ void DWARFRewriter::updateGdbIndexSection() {
|
|||
NewGdbIndexContents,
|
||||
NewGdbIndexSize);
|
||||
}
|
||||
|
||||
void
|
||||
DWARFRewriter::convertToRanges(const DWARFAbbreviationDeclaration *Abbrev) {
|
||||
AbbrevPatcher->addAttributePatch(Abbrev,
|
||||
dwarf::DW_AT_low_pc,
|
||||
dwarf::DW_AT_ranges,
|
||||
dwarf::DW_FORM_sec_offset);
|
||||
AbbrevPatcher->addAttributePatch(Abbrev,
|
||||
dwarf::DW_AT_high_pc,
|
||||
dwarf::DW_AT_low_pc,
|
||||
dwarf::DW_FORM_udata);
|
||||
}
|
||||
|
||||
void DWARFRewriter::convertToRanges(DWARFDie DIE,
|
||||
const DebugAddressRangesVector &Ranges) {
|
||||
uint64_t RangesSectionOffset;
|
||||
if (Ranges.empty()) {
|
||||
RangesSectionOffset = RangesSectionsWriter->getEmptyRangesOffset();
|
||||
} else {
|
||||
RangesSectionOffset = RangesSectionsWriter->addRanges(Ranges);
|
||||
}
|
||||
|
||||
convertToRanges(DIE, RangesSectionOffset);
|
||||
}
|
||||
|
||||
void DWARFRewriter::convertPending(const DWARFAbbreviationDeclaration *Abbrev) {
|
||||
if (ConvertedRangesAbbrevs.count(Abbrev))
|
||||
return;
|
||||
|
||||
convertToRanges(Abbrev);
|
||||
|
||||
auto I = PendingRanges.find(Abbrev);
|
||||
if (I != PendingRanges.end()) {
|
||||
for (auto &Pair : I->second) {
|
||||
convertToRanges(Pair.first, {Pair.second});
|
||||
}
|
||||
PendingRanges.erase(I);
|
||||
}
|
||||
|
||||
ConvertedRangesAbbrevs.emplace(Abbrev);
|
||||
}
|
||||
|
||||
void DWARFRewriter::flushPendingRanges() {
|
||||
for (auto &I : PendingRanges) {
|
||||
for (auto &RangePair : I.second) {
|
||||
patchLowHigh(RangePair.first, RangePair.second);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
void getRangeAttrData(
|
||||
DWARFDie DIE,
|
||||
uint32_t &LowPCOffset, uint32_t &HighPCOffset,
|
||||
DWARFFormValue &LowPCFormValue, DWARFFormValue &HighPCFormValue) {
|
||||
LowPCOffset = -1U;
|
||||
HighPCOffset = -1U;
|
||||
LowPCFormValue = *DIE.find(dwarf::DW_AT_low_pc, &LowPCOffset);
|
||||
HighPCFormValue = *DIE.find(dwarf::DW_AT_high_pc, &HighPCOffset);
|
||||
|
||||
if (LowPCFormValue.getForm() != dwarf::DW_FORM_addr ||
|
||||
(HighPCFormValue.getForm() != dwarf::DW_FORM_addr &&
|
||||
HighPCFormValue.getForm() != dwarf::DW_FORM_data8 &&
|
||||
HighPCFormValue.getForm() != dwarf::DW_FORM_data4)) {
|
||||
errs() << "BOLT-WARNING: unexpected form value. Cannot update DIE "
|
||||
<< "at offset 0x" << Twine::utohexstr(DIE.getOffset()) << "\n";
|
||||
return;
|
||||
}
|
||||
if (LowPCOffset == -1U || (LowPCOffset + 8 != HighPCOffset)) {
|
||||
errs() << "BOLT-WARNING: high_pc expected immediately after low_pc. "
|
||||
<< "Cannot update DIE at offset 0x"
|
||||
<< Twine::utohexstr(DIE.getOffset()) << '\n';
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void DWARFRewriter::patchLowHigh(DWARFDie DIE, DebugAddressRange Range) {
|
||||
uint32_t LowPCOffset, HighPCOffset;
|
||||
DWARFFormValue LowPCFormValue, HighPCFormValue;
|
||||
getRangeAttrData(
|
||||
DIE, LowPCOffset, HighPCOffset, LowPCFormValue, HighPCFormValue);
|
||||
DebugInfoPatcher->addLE64Patch(LowPCOffset, Range.LowPC);
|
||||
if (HighPCFormValue.getForm() == dwarf::DW_FORM_addr ||
|
||||
HighPCFormValue.getForm() == dwarf::DW_FORM_data8) {
|
||||
DebugInfoPatcher->addLE64Patch(HighPCOffset, Range.HighPC - Range.LowPC);
|
||||
} else {
|
||||
DebugInfoPatcher->addLE32Patch(HighPCOffset, Range.HighPC - Range.LowPC);
|
||||
}
|
||||
}
|
||||
|
||||
void DWARFRewriter::convertToRanges(DWARFDie DIE,
|
||||
uint64_t RangesSectionOffset) {
|
||||
uint32_t LowPCOffset, HighPCOffset;
|
||||
DWARFFormValue LowPCFormValue, HighPCFormValue;
|
||||
getRangeAttrData(
|
||||
DIE, LowPCOffset, HighPCOffset, LowPCFormValue, HighPCFormValue);
|
||||
|
||||
unsigned LowPCSize = 0;
|
||||
if (HighPCFormValue.getForm() == dwarf::DW_FORM_addr ||
|
||||
HighPCFormValue.getForm() == dwarf::DW_FORM_data8) {
|
||||
LowPCSize = 12;
|
||||
} else if (HighPCFormValue.getForm() == dwarf::DW_FORM_data4) {
|
||||
LowPCSize = 8;
|
||||
} else {
|
||||
llvm_unreachable("unexpected form");
|
||||
}
|
||||
DebugInfoPatcher->addLE32Patch(LowPCOffset, RangesSectionOffset);
|
||||
DebugInfoPatcher->addUDataPatch(LowPCOffset + 4, 0, LowPCSize);
|
||||
}
|
||||
|
||||
|
|
|
@ -14,6 +14,7 @@
|
|||
|
||||
#include "DebugData.h"
|
||||
#include "RewriteInstance.h"
|
||||
#include <map>
|
||||
|
||||
namespace llvm {
|
||||
|
||||
|
@ -30,6 +31,9 @@ class DWARFRewriter {
|
|||
|
||||
SectionPatchersType &SectionPatchers;
|
||||
|
||||
SimpleBinaryPatcher *DebugInfoPatcher{nullptr};
|
||||
DebugAbbrevPatcher *AbbrevPatcher{nullptr};
|
||||
|
||||
/// Stores and serializes information that will be put into the .debug_ranges
|
||||
/// and .debug_aranges DWARF sections.
|
||||
std::unique_ptr<DebugRangesSectionsWriter> RangesSectionsWriter;
|
||||
|
@ -62,6 +66,34 @@ class DWARFRewriter {
|
|||
/// Rewrite .gdb_index section if present.
|
||||
void updateGdbIndexSection();
|
||||
|
||||
/// Abbreviations that were converted to use DW_AT_ranges.
|
||||
std::set<const DWARFAbbreviationDeclaration *> ConvertedRangesAbbrevs;
|
||||
|
||||
/// DIEs with abbrevs that were not converted to DW_AT_ranges.
|
||||
/// We only update those when all DIEs have been processed to guarantee that
|
||||
/// the abbrev (which is shared) is intact.
|
||||
std::map<const DWARFAbbreviationDeclaration *,
|
||||
std::vector<std::pair<DWARFDie, DebugAddressRange>>> PendingRanges;
|
||||
|
||||
/// Convert \p Abbrev from using a simple DW_AT_(low|high)_pc range to
|
||||
/// DW_AT_ranges.
|
||||
void convertToRanges(const DWARFAbbreviationDeclaration *Abbrev);
|
||||
|
||||
/// Update \p DIE that was using DW_AT_(low|high)_pc with DW_AT_ranges offset.
|
||||
void convertToRanges(DWARFDie DIE, uint64_t RangesSectionOffset);
|
||||
|
||||
/// Same as above, but takes a vector of \p Ranges as a parameter.
|
||||
void convertToRanges(DWARFDie DIE, const DebugAddressRangesVector &Ranges);
|
||||
|
||||
/// Patch DW_AT_(low|high)_pc values for the \p DIE based on \p Range.
|
||||
void patchLowHigh(DWARFDie DIE, DebugAddressRange Range);
|
||||
|
||||
/// Convert pending ranges associated with the given \p Abbrev.
|
||||
void convertPending(const DWARFAbbreviationDeclaration *Abbrev);
|
||||
|
||||
/// Once all DIEs were seen, update DW_AT_(low|high)_pc values.
|
||||
void flushPendingRanges();
|
||||
|
||||
public:
|
||||
DWARFRewriter(BinaryContext &BC,
|
||||
SectionPatchersType &SectionPatchers)
|
||||
|
|
|
@ -229,30 +229,23 @@ void SimpleBinaryPatcher::patchBinary(std::string &BinaryContents) {
|
|||
}
|
||||
}
|
||||
|
||||
void DebugAbbrevPatcher::addAttributePatch(const DWARFUnit *Unit,
|
||||
uint32_t AbbrevCode,
|
||||
dwarf::Attribute AttrTag,
|
||||
uint8_t NewAttrTag,
|
||||
uint8_t NewAttrForm) {
|
||||
assert(Unit && "No compile unit specified.");
|
||||
void DebugAbbrevPatcher::addAttributePatch(
|
||||
const DWARFAbbreviationDeclaration *Abbrev,
|
||||
dwarf::Attribute AttrTag,
|
||||
uint8_t NewAttrTag,
|
||||
uint8_t NewAttrForm) {
|
||||
assert(Abbrev && "no abbreviation specified");
|
||||
AbbrevPatches.emplace(
|
||||
AbbrevAttrPatch{Unit, AbbrevCode, AttrTag, NewAttrTag, NewAttrForm});
|
||||
AbbrevAttrPatch{Abbrev, AttrTag, NewAttrTag, NewAttrForm});
|
||||
}
|
||||
|
||||
void DebugAbbrevPatcher::patchBinary(std::string &Contents) {
|
||||
SimpleBinaryPatcher Patcher;
|
||||
|
||||
for (const auto &Patch : AbbrevPatches) {
|
||||
const auto *UnitAbbreviations = Patch.Unit->getAbbreviations();
|
||||
assert(UnitAbbreviations &&
|
||||
"Compile unit doesn't have associated abbreviations.");
|
||||
const auto *AbbreviationDeclaration =
|
||||
UnitAbbreviations->getAbbreviationDeclaration(Patch.Code);
|
||||
assert(AbbreviationDeclaration && "No abbreviation with given code.");
|
||||
const auto Attribute =
|
||||
AbbreviationDeclaration->findAttribute(Patch.Attr);
|
||||
|
||||
const auto Attribute = Patch.Abbrev->findAttribute(Patch.Attr);
|
||||
assert(Attribute && "Specified attribute doesn't occur in abbreviation.");
|
||||
|
||||
// Because we're only handling standard values (i.e. no DW_FORM_GNU_* or
|
||||
// DW_AT_APPLE_*), they are all small (< 128) and encoded in a single
|
||||
// byte in ULEB128, otherwise it'll be more tricky as we may need to
|
||||
|
|
|
@ -234,21 +234,19 @@ class DebugAbbrevPatcher : public BinaryPatcher {
|
|||
private:
|
||||
/// Patch of changing one attribute to another.
|
||||
struct AbbrevAttrPatch {
|
||||
const DWARFUnit *Unit; // Containing DWARF unit
|
||||
uint32_t Code; // Code of abbreviation to be modified.
|
||||
const DWARFAbbreviationDeclaration *Abbrev;
|
||||
dwarf::Attribute Attr; // ID of attribute to be replaced.
|
||||
uint8_t NewAttr; // ID of the new attribute.
|
||||
uint8_t NewForm; // Form of the new attribute.
|
||||
|
||||
bool operator==(const AbbrevAttrPatch &RHS) const {
|
||||
return Unit == RHS.Unit && Code == RHS.Code && Attr == RHS.Attr;
|
||||
return Abbrev == RHS.Abbrev && Attr == RHS.Attr;
|
||||
}
|
||||
};
|
||||
|
||||
struct AbbrevHash {
|
||||
std::size_t operator()(const AbbrevAttrPatch &P) const {
|
||||
return std::hash<uint64_t>()(
|
||||
((uint64_t)P.Unit->getOffset() << 32) + (P.Code << 16) + P.Attr);
|
||||
return std::hash<uint64_t>()(((uint64_t)P.Abbrev << 16) + P.Attr);
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -256,15 +254,13 @@ private:
|
|||
|
||||
public:
|
||||
~DebugAbbrevPatcher() { }
|
||||
/// Adds a patch to change an attribute of an abbreviation that belongs to
|
||||
/// \p Unit to another attribute.
|
||||
/// \p AbbrevCode code of the abbreviation to be modified.
|
||||
/// Adds a patch to change an attribute of the abbreviation
|
||||
/// \p Abbrev the abbreviation to be modified.
|
||||
/// \p AttrTag ID of the attribute to be replaced.
|
||||
/// \p NewAttrTag ID of the new attribute.
|
||||
/// \p NewAttrForm Form of the new attribute.
|
||||
/// We only handle standard forms, that are encoded in a single byte.
|
||||
void addAttributePatch(const DWARFUnit *Unit,
|
||||
uint32_t AbbrevCode,
|
||||
void addAttributePatch(const DWARFAbbreviationDeclaration *Abbrev,
|
||||
dwarf::Attribute AttrTag,
|
||||
uint8_t NewAttrTag,
|
||||
uint8_t NewAttrForm);
|
||||
|
|
Loading…
Reference in New Issue