forked from OSchip/llvm-project
[BOLT] Fix debug info for input with continuous range.
Summary: When we see a compilation unit with continuous range on input, it has two attributes: DW_AT_low_pc and DW_AT_high_pc. We convert the range to a non-continuous one and change the attributes to DW_AT_ranges and DW_AT_producer. However, gdb seems to expect every compilation unit to have a base address specified via DW_AT_low_pc, even when its value is always 0. Otherwise gdb will not show proper debug info for such modules. With this diff we produce DW_AT_ranges followed by DW_AT_low_pc. The problem is that the first attribute takes DW_FORM_sec_offset which is exactly 4 bytes, and in many cases we are left with 12 bytes to fill in. We used to fill this space with DW_AT_producer, which took an arbitrary-length field. For DW_AT_low_pc we can use a trick of using DW_FORM_udata (unsigned ULEB128 encoded integer) which can take up to 12 bytes, even when the value is 0. (cherry picked from FBD5109798)
This commit is contained in:
parent
4806b13835
commit
457b7f14b9
|
@ -174,15 +174,18 @@ void RewriteInstance::updateDWARFObjectAddressRanges(
|
||||||
DIE->getAttributeValue(Unit, dwarf::DW_AT_ranges, FormValue, &AttrOffset);
|
DIE->getAttributeValue(Unit, dwarf::DW_AT_ranges, FormValue, &AttrOffset);
|
||||||
DebugInfoPatcher->addLE32Patch(AttrOffset, DebugRangesOffset);
|
DebugInfoPatcher->addLE32Patch(AttrOffset, DebugRangesOffset);
|
||||||
} else {
|
} else {
|
||||||
// Case 2: The object has both DW_AT_low_pc and DW_AT_high_pc.
|
// Case 2: The object has both DW_AT_low_pc and DW_AT_high_pc emitted back
|
||||||
// We require the compiler to put both attributes one after the other
|
// to back. We replace the attributes with DW_AT_ranges and DW_AT_low_pc.
|
||||||
// for our approach to work. low_pc and high_pc both occupy 8 bytes
|
// The low_pc attribute is required for DW_TAG_compile_units to set a base
|
||||||
// as we're dealing with a 64-bit ELF. We basically change low_pc to
|
// address.
|
||||||
// DW_AT_ranges and high_pc to DW_AT_producer. ranges spans only 4 bytes
|
//
|
||||||
// in 32-bit DWARF, which we assume to be used, which leaves us with 12
|
// Since DW_AT_ranges takes 4-byte DW_FROM_sec_offset value, we have to fill
|
||||||
// more bytes. We then set the value of DW_AT_producer as an arbitrary
|
// in up to 12-bytes left after removal of low/high pc field from
|
||||||
// 12-byte string that fills the remaining space and leaves the rest of
|
// .debug_info.
|
||||||
// the abbreviation layout unchanged.
|
//
|
||||||
|
// To fill in the gap we use a variable length DW_FORM_udata encoding for
|
||||||
|
// DW_AT_low_pc. We exploit the fact that the encoding can take an arbitrary
|
||||||
|
// large size.
|
||||||
if (AbbreviationDecl->findAttributeIndex(dwarf::DW_AT_low_pc) != -1U &&
|
if (AbbreviationDecl->findAttributeIndex(dwarf::DW_AT_low_pc) != -1U &&
|
||||||
AbbreviationDecl->findAttributeIndex(dwarf::DW_AT_high_pc) != -1U) {
|
AbbreviationDecl->findAttributeIndex(dwarf::DW_AT_high_pc) != -1U) {
|
||||||
uint32_t LowPCOffset = -1U;
|
uint32_t LowPCOffset = -1U;
|
||||||
|
@ -197,19 +200,15 @@ void RewriteInstance::updateDWARFObjectAddressRanges(
|
||||||
(HighPCFormValue.getForm() != dwarf::DW_FORM_addr &&
|
(HighPCFormValue.getForm() != dwarf::DW_FORM_addr &&
|
||||||
HighPCFormValue.getForm() != dwarf::DW_FORM_data8 &&
|
HighPCFormValue.getForm() != dwarf::DW_FORM_data8 &&
|
||||||
HighPCFormValue.getForm() != dwarf::DW_FORM_data4)) {
|
HighPCFormValue.getForm() != dwarf::DW_FORM_data4)) {
|
||||||
if (opts::Verbosity >= 1) {
|
errs() << "BOLT-WARNING: unexpected form value. Cannot update DIE "
|
||||||
errs() << "BOLT-WARNING: unexpected form value. Cannot update DIE "
|
|
||||||
<< "at offset 0x" << Twine::utohexstr(DIE->getOffset())
|
<< "at offset 0x" << Twine::utohexstr(DIE->getOffset())
|
||||||
<< "\n";
|
<< "\n";
|
||||||
}
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (LowPCOffset == -1U || (LowPCOffset + 8 != HighPCOffset)) {
|
if (LowPCOffset == -1U || (LowPCOffset + 8 != HighPCOffset)) {
|
||||||
if (opts::Verbosity >= 1) {
|
errs() << "BOLT-WARNING: high_pc expected immediately after low_pc. "
|
||||||
errs() << "BOLT-WARNING: high_pc expected immediately after low_pc. "
|
<< "Cannot update DIE at offset 0x"
|
||||||
<< "Cannot update DIE at offset 0x"
|
<< Twine::utohexstr(DIE->getOffset()) << '\n';
|
||||||
<< Twine::utohexstr(DIE->getOffset()) << '\n';
|
|
||||||
}
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -221,23 +220,19 @@ void RewriteInstance::updateDWARFObjectAddressRanges(
|
||||||
AbbrevPatcher->addAttributePatch(Unit,
|
AbbrevPatcher->addAttributePatch(Unit,
|
||||||
AbbrevCode,
|
AbbrevCode,
|
||||||
dwarf::DW_AT_high_pc,
|
dwarf::DW_AT_high_pc,
|
||||||
dwarf::DW_AT_producer,
|
dwarf::DW_AT_low_pc,
|
||||||
dwarf::DW_FORM_string);
|
dwarf::DW_FORM_udata);
|
||||||
unsigned StringSize = 0;
|
unsigned LowPCSize = 0;
|
||||||
if (HighPCFormValue.getForm() == dwarf::DW_FORM_addr ||
|
if (HighPCFormValue.getForm() == dwarf::DW_FORM_addr ||
|
||||||
HighPCFormValue.getForm() == dwarf::DW_FORM_data8) {
|
HighPCFormValue.getForm() == dwarf::DW_FORM_data8) {
|
||||||
StringSize = 12;
|
LowPCSize = 12;
|
||||||
} else if (HighPCFormValue.getForm() == dwarf::DW_FORM_data4) {
|
} else if (HighPCFormValue.getForm() == dwarf::DW_FORM_data4) {
|
||||||
StringSize = 8;
|
LowPCSize = 8;
|
||||||
} else {
|
} else {
|
||||||
assert(0 && "unexpected form");
|
llvm_unreachable("unexpected form");
|
||||||
}
|
}
|
||||||
|
|
||||||
DebugInfoPatcher->addLE32Patch(LowPCOffset, DebugRangesOffset);
|
DebugInfoPatcher->addLE32Patch(LowPCOffset, DebugRangesOffset);
|
||||||
std::string ProducerString{"LLVM-BOLT"};
|
DebugInfoPatcher->addUDataPatch(LowPCOffset + 4, 0, LowPCSize);
|
||||||
ProducerString.resize(StringSize, ' ');
|
|
||||||
ProducerString.back() = '\0';
|
|
||||||
DebugInfoPatcher->addBinaryPatch(LowPCOffset + 4, ProducerString);
|
|
||||||
} else {
|
} else {
|
||||||
if (opts::Verbosity >= 1) {
|
if (opts::Verbosity >= 1) {
|
||||||
errs() << "BOLT-WARNING: Cannot update ranges for DIE at offset 0x"
|
errs() << "BOLT-WARNING: Cannot update ranges for DIE at offset 0x"
|
||||||
|
|
|
@ -15,6 +15,7 @@
|
||||||
#include "llvm/MC/MCSymbol.h"
|
#include "llvm/MC/MCSymbol.h"
|
||||||
#include "llvm/MC/MCObjectWriter.h"
|
#include "llvm/MC/MCObjectWriter.h"
|
||||||
#include "llvm/Support/CommandLine.h"
|
#include "llvm/Support/CommandLine.h"
|
||||||
|
#include "llvm/Support/LEB128.h"
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <cassert>
|
#include <cassert>
|
||||||
|
|
||||||
|
@ -270,6 +271,18 @@ void SimpleBinaryPatcher::addLEPatch(uint32_t Offset, uint64_t NewValue,
|
||||||
Patches.emplace_back(std::make_pair(Offset, LE64));
|
Patches.emplace_back(std::make_pair(Offset, LE64));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void SimpleBinaryPatcher::addUDataPatch(uint32_t Offset, uint64_t Value, uint64_t Size) {
|
||||||
|
const auto EncodedSize = getULEB128Size(Value);
|
||||||
|
assert(EncodedSize <= Size && "value did not fit");
|
||||||
|
|
||||||
|
const auto Padding = Size - EncodedSize;
|
||||||
|
std::string Buff;
|
||||||
|
raw_string_ostream OS(Buff);
|
||||||
|
encodeULEB128(Value, OS, Padding);
|
||||||
|
|
||||||
|
Patches.emplace_back(Offset, OS.str());
|
||||||
|
}
|
||||||
|
|
||||||
void SimpleBinaryPatcher::addLE64Patch(uint32_t Offset, uint64_t NewValue) {
|
void SimpleBinaryPatcher::addLE64Patch(uint32_t Offset, uint64_t NewValue) {
|
||||||
addLEPatch(Offset, NewValue, 8);
|
addLEPatch(Offset, NewValue, 8);
|
||||||
}
|
}
|
||||||
|
|
|
@ -345,6 +345,11 @@ public:
|
||||||
/// little-endian value at offset \p Offset.
|
/// little-endian value at offset \p Offset.
|
||||||
void addLE32Patch(uint32_t Offset, uint32_t NewValue);
|
void addLE32Patch(uint32_t Offset, uint32_t NewValue);
|
||||||
|
|
||||||
|
/// Add a patch at \p Offset with \p Value using unsigned LEB128 encoding with
|
||||||
|
/// size \p Size. \p Size should not be less than a minimum number of bytes
|
||||||
|
/// needed to encode \p Value.
|
||||||
|
void addUDataPatch(uint32_t Offset, uint64_t Value, uint64_t Size);
|
||||||
|
|
||||||
void patchBinary(std::string &BinaryContents) override;
|
void patchBinary(std::string &BinaryContents) override;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue