[BOLT][DWARF] Fix gdb index section

Since we now re-write .debug_info the DWARF CU Offsets can change.
Just like for .debug_aranges the GDB Index will need to be updated.

Reviewed By: Amir, maksfb

Differential Revision: https://reviews.llvm.org/D118273
This commit is contained in:
Alexander Yermolovich 2022-01-27 12:03:01 -08:00
parent 1f26aa42ce
commit 612f0f4568
7 changed files with 389 additions and 45 deletions

View File

@ -107,6 +107,13 @@ struct DebugLineTableRowRef {
/// Common buffer vector used for debug info handling.
using DebugBufferVector = SmallVector<char, 16>;
/// Map of old CU offset to new offset and length.
struct CUInfo {
uint32_t Offset;
uint32_t Length;
};
using CUOffsetMap = std::map<uint32_t, CUInfo>;
/// Serializes the .debug_ranges DWARF section.
class DebugRangesSectionWriter {
public:
@ -155,9 +162,8 @@ public:
/// Writes .debug_aranges with the added ranges to the MCObjectWriter.
/// Takes in \p RangesStream to write into, and \p CUMap which maps CU
/// original offsets to new ones.
void
writeARangesSection(raw_svector_ostream &RangesStream,
const std::unordered_map<uint32_t, uint32_t> CUMap) const;
void writeARangesSection(raw_svector_ostream &RangesStream,
const CUOffsetMap &CUMap) const;
/// Resets the writer to a clear state.
void reset() { CUAddressRanges.clear(); }
@ -647,8 +653,8 @@ public:
void setDWPOffset(uint64_t DWPOffset) { DWPUnitOffset = DWPOffset; }
/// When this function is invoked all of the DebugInfo Patches must be done.
/// Returns a map of old CU offsets to new ones.
std::unordered_map<uint32_t, uint32_t> computeNewOffsets();
/// Returns a map of old CU offsets to new offsets and new sizes.
CUOffsetMap computeNewOffsets(DWARFContext &DWCtx, bool IsDWOContext);
private:
struct PatchDeleter {
@ -685,7 +691,7 @@ private:
using UniquePatchPtrType = std::unique_ptr<Patch, PatchDeleter>;
uint64_t DWPUnitOffset{0};
uint32_t ChangeInSize{0};
int32_t ChangeInSize{0};
std::vector<UniquePatchPtrType> DebugPatches;
/// Mutex used for parallel processing of debug info.
std::mutex WriterMutex;

View File

@ -93,14 +93,14 @@ class DWARFRewriter {
makeFinalLocListsSection(SimpleBinaryPatcher &DebugInfoPatcher);
/// Finalize debug sections in the main binary.
void finalizeDebugSections(DebugInfoBinaryPatcher &DebugInfoPatcher);
CUOffsetMap finalizeDebugSections(DebugInfoBinaryPatcher &DebugInfoPatcher);
/// Patches the binary for DWARF address ranges (e.g. in functions and lexical
/// blocks) to be updated.
void updateDebugAddressRanges();
/// Rewrite .gdb_index section if present.
void updateGdbIndexSection();
void updateGdbIndexSection(CUOffsetMap &CUMap);
/// Output .dwo files.
void writeDWOFiles(std::unordered_map<uint64_t, std::string> &DWOIdToName);

View File

@ -121,8 +121,7 @@ void DebugARangesSectionWriter::addCURanges(uint64_t CUOffset,
}
void DebugARangesSectionWriter::writeARangesSection(
raw_svector_ostream &RangesStream,
const std::unordered_map<uint32_t, uint32_t> CUMap) const {
raw_svector_ostream &RangesStream, const CUOffsetMap &CUMap) const {
// For reference on the format of the .debug_aranges section, see the DWARF4
// specification, section 6.1.4 Lookup by Address
// http://www.dwarfstd.org/doc/DWARF4.pdf
@ -148,9 +147,9 @@ void DebugARangesSectionWriter::writeARangesSection(
assert(CUMap.count(Offset) && "Original CU offset is not found in CU Map");
// Header field #3: debug info offset of the correspondent compile unit.
support::endian::write(RangesStream,
static_cast<uint32_t>(CUMap.find(Offset)->second),
support::little);
support::endian::write(
RangesStream, static_cast<uint32_t>(CUMap.find(Offset)->second.Offset),
support::little);
// Header field #4: address size.
// 8 since we only write ELF64 binaries for now.
@ -473,23 +472,32 @@ std::string SimpleBinaryPatcher::patchBinary(StringRef BinaryContents) {
return BinaryContentsStr;
}
std::unordered_map<uint32_t, uint32_t>
DebugInfoBinaryPatcher::computeNewOffsets() {
std::unordered_map<uint32_t, uint32_t> CUMap;
CUOffsetMap DebugInfoBinaryPatcher::computeNewOffsets(DWARFContext &DWCtx,
bool IsDWOContext) {
CUOffsetMap CUMap;
std::sort(DebugPatches.begin(), DebugPatches.end(),
[](const UniquePatchPtrType &V1, const UniquePatchPtrType &V2) {
return V1.get()->Offset < V2.get()->Offset;
});
DWARFUnitVector::compile_unit_range CompileUnits =
IsDWOContext ? DWCtx.dwo_compile_units() : DWCtx.compile_units();
for (const std::unique_ptr<DWARFUnit> &CU : CompileUnits)
CUMap[CU->getOffset()] = {static_cast<uint32_t>(CU->getOffset()),
static_cast<uint32_t>(CU->getLength())};
// Calculating changes in .debug_info size from Patches to build a map of old
// to updated reference destination offsets.
uint32_t PreviousOffset = 0;
int32_t PreviousChangeInSize = 0;
for (UniquePatchPtrType &PatchBase : DebugPatches) {
Patch *P = PatchBase.get();
switch (P->Kind) {
default:
continue;
case DebugPatchKind::PatchValue64to32: {
ChangeInSize -= 4;
PreviousChangeInSize -= 4;
break;
}
case DebugPatchKind::PatchValueVariable: {
@ -498,13 +506,14 @@ DebugInfoBinaryPatcher::computeNewOffsets() {
std::string Temp;
raw_string_ostream OS(Temp);
encodeULEB128(DPV->Value, OS);
ChangeInSize += Temp.size() - DPV->OldValueSize;
PreviousChangeInSize += Temp.size() - DPV->OldValueSize;
break;
}
case DebugPatchKind::DestinationReferenceLabel: {
DestinationReferenceLabel *DRL =
reinterpret_cast<DestinationReferenceLabel *>(P);
OldToNewOffset[DRL->Offset] = DRL->Offset + ChangeInSize;
OldToNewOffset[DRL->Offset] =
DRL->Offset + ChangeInSize + PreviousChangeInSize;
break;
}
case DebugPatchKind::ReferencePatchValue: {
@ -512,7 +521,7 @@ DebugInfoBinaryPatcher::computeNewOffsets() {
// to reduce algorithmic complexity.
DebugPatchReference *RDP = reinterpret_cast<DebugPatchReference *>(P);
if (RDP->PatchInfo.IndirectRelative) {
ChangeInSize += 4 - RDP->PatchInfo.OldValueSize;
PreviousChangeInSize += 4 - RDP->PatchInfo.OldValueSize;
assert(RDP->PatchInfo.OldValueSize <= 4 &&
"Variable encoding reference greater than 4 bytes.");
}
@ -522,11 +531,16 @@ DebugInfoBinaryPatcher::computeNewOffsets() {
DWARFUnitOffsetBaseLabel *BaseLabel =
reinterpret_cast<DWARFUnitOffsetBaseLabel *>(P);
uint32_t CUOffset = BaseLabel->Offset;
ChangeInSize += PreviousChangeInSize;
uint32_t CUOffsetUpdate = CUOffset + ChangeInSize;
CUMap[CUOffset] = CUOffsetUpdate;
CUMap[CUOffset].Offset = CUOffsetUpdate;
CUMap[PreviousOffset].Length += PreviousChangeInSize;
PreviousChangeInSize = 0;
PreviousOffset = CUOffset;
}
}
}
CUMap[PreviousOffset].Length += PreviousChangeInSize;
return CUMap;
}

View File

@ -323,14 +323,14 @@ void DWARFRewriter::updateDebugInfo() {
DebugInfoPatcher->clearDestinationLabels();
flushPendingRanges(*DebugInfoPatcher);
finalizeDebugSections(*DebugInfoPatcher);
CUOffsetMap OffsetMap = finalizeDebugSections(*DebugInfoPatcher);
if (opts::WriteDWP)
writeDWP(DWOIdToName);
else
writeDWOFiles(DWOIdToName);
updateGdbIndexSection();
updateGdbIndexSection(OffsetMap);
}
void DWARFRewriter::updateUnitDebugInfo(
@ -820,8 +820,8 @@ void DWARFRewriter::updateLineTableOffsets(const MCAsmLayout &Layout) {
TypeInfoSection->setIsFinalized();
}
void DWARFRewriter::finalizeDebugSections(
DebugInfoBinaryPatcher &DebugInfoPatcher) {
CUOffsetMap
DWARFRewriter::finalizeDebugSections(DebugInfoBinaryPatcher &DebugInfoPatcher) {
if (StrWriter->isInitialized()) {
RewriteInstance::addToDebugSectionsToOverwrite(".debug_str");
std::unique_ptr<DebugStrBufferVector> DebugStrSectionContents =
@ -898,8 +898,8 @@ void DWARFRewriter::finalizeDebugSections(
}
// No more creating new DebugInfoPatches.
std::unordered_map<uint32_t, uint32_t> CUMap =
DebugInfoPatcher.computeNewOffsets();
CUOffsetMap CUMap =
DebugInfoPatcher.computeNewOffsets(*BC.DwCtx.get(), false);
// Skip .debug_aranges if we are re-generating .gdb_index.
if (opts::KeepARanges || !BC.getGdbIndexSection()) {
@ -916,6 +916,7 @@ void DWARFRewriter::finalizeDebugSections(
copyByteArray(ARangesContents),
ARangesContents.size());
}
return CUMap;
}
// Creates all the data structures necessary for creating MCStreamer.
@ -958,15 +959,14 @@ StringRef getSectionName(const SectionRef &Section) {
// Exctracts an appropriate slice if input is DWP.
// Applies patches or overwrites the section.
Optional<StringRef>
updateDebugData(std::string &Storage, const SectionRef &Section,
const StringMap<KnownSectionsEntry> &KnownSections,
MCStreamer &Streamer, DWARFRewriter &Writer,
const DWARFUnitIndex::Entry *DWOEntry, uint64_t DWOId,
std::unique_ptr<DebugBufferVector> &OutputBuffer) {
Optional<StringRef> updateDebugData(
DWARFContext &DWCtx, std::string &Storage, const SectionRef &Section,
const StringMap<KnownSectionsEntry> &KnownSections, MCStreamer &Streamer,
DWARFRewriter &Writer, const DWARFUnitIndex::Entry *DWOEntry,
uint64_t DWOId, std::unique_ptr<DebugBufferVector> &OutputBuffer) {
auto applyPatch = [&](DebugInfoBinaryPatcher *Patcher,
StringRef Data) -> StringRef {
Patcher->computeNewOffsets();
Patcher->computeNewOffsets(DWCtx, true);
Storage = Patcher->patchBinary(Data);
return StringRef(Storage.c_str(), Storage.size());
};
@ -1127,9 +1127,9 @@ void DWARFRewriter::writeDWP(
for (const SectionRef &Section : DWOFile->sections()) {
std::string Storage = "";
std::unique_ptr<DebugBufferVector> OutputData;
Optional<StringRef> TOutData =
updateDebugData(Storage, Section, KnownSections, *Streamer, *this,
DWOEntry, *DWOId, OutputData);
Optional<StringRef> TOutData = updateDebugData(
(*DWOCU)->getContext(), Storage, Section, KnownSections, *Streamer,
*this, DWOEntry, *DWOId, OutputData);
if (!TOutData)
continue;
@ -1227,9 +1227,9 @@ void DWARFRewriter::writeDWOFiles(
for (const SectionRef &Section : File->sections()) {
std::string Storage = "";
std::unique_ptr<DebugBufferVector> OutputData;
if (Optional<StringRef> OutData =
updateDebugData(Storage, Section, KnownSections, *Streamer, *this,
DWOEntry, *DWOId, OutputData))
if (Optional<StringRef> OutData = updateDebugData(
(*DWOCU)->getContext(), Storage, Section, KnownSections,
*Streamer, *this, DWOEntry, *DWOId, OutputData))
Streamer->emitBytes(*OutData);
}
Streamer->Finish();
@ -1237,7 +1237,7 @@ void DWARFRewriter::writeDWOFiles(
}
}
void DWARFRewriter::updateGdbIndexSection() {
void DWARFRewriter::updateGdbIndexSection(CUOffsetMap &CUMap) {
if (!BC.getGdbIndexSection())
return;
@ -1314,10 +1314,23 @@ void DWARFRewriter::updateGdbIndexSection() {
write32le(Buffer + 20, ConstantPoolOffset + Delta);
Buffer += 24;
// Copy over CU list and types CU list.
memcpy(Buffer, GdbIndexContents.data() + 24,
AddressTableOffset - CUListOffset);
Buffer += AddressTableOffset - CUListOffset;
// Writing out CU List <Offset, Size>
for (auto &CUInfo : CUMap) {
write64le(Buffer, CUInfo.second.Offset);
// Length encoded in CU doesn't contain first 4 bytes that encode length.
write64le(Buffer + 8, CUInfo.second.Length + 4);
Buffer += 16;
}
// Copy over types CU list
// Spec says " triplet, the first value is the CU offset, the second value is
// the type offset in the CU, and the third value is the type signature"
// Looking at what is being generated by gdb-add-index. The first entry is TU
// offset, second entry is offset from it, and third entry is the type
// signature.
memcpy(Buffer, GdbIndexContents.data() + CUTypesOffset,
AddressTableOffset - CUTypesOffset);
Buffer += AddressTableOffset - CUTypesOffset;
// Generate new address table.
for (const std::pair<const uint64_t, DebugAddressRangesVector> &CURangesPair :

View File

@ -0,0 +1,137 @@
.file "test.cpp"
.text
.Ltext0:
.globl main
.type main, @function
main:
.LFB0:
.file 1 "test.cpp"
.loc 1 1 12
.cfi_startproc
pushq %rbp
.cfi_def_cfa_offset 16
.cfi_offset 6, -16
movq %rsp, %rbp
.cfi_def_cfa_register 6
.loc 1 1 21
movl $0, %eax
.loc 1 1 24
popq %rbp
.cfi_def_cfa 7, 8
ret
.cfi_endproc
.LFE0:
.size main, .-main
.Letext0:
.section .debug_info,"",@progbits
.Ldebug_info0:
.long 0x4f
.value 0x4
.long .Ldebug_abbrev0
.byte 0x8
.uleb128 0x1
.long .LASF0
.byte 0x4
.long .LASF1
.long .LASF2
.quad .Ltext0
.quad .Letext0-.Ltext0
.long .Ldebug_line0
.uleb128 0x2
.long .LASF3
.byte 0x1
.byte 0x1
.byte 0x5
.long 0x4b
.quad .LFB0
.quad .LFE0-.LFB0
.uleb128 0x1
.byte 0x9c
.uleb128 0x3
.byte 0x4
.byte 0x5
.string "int"
.byte 0
.section .debug_abbrev,"",@progbits
.Ldebug_abbrev0:
.uleb128 0x1
.uleb128 0x11
.byte 0x1
.uleb128 0x25
.uleb128 0xe
.uleb128 0x13
.uleb128 0xb
.uleb128 0x3
.uleb128 0xe
.uleb128 0x1b
.uleb128 0xe
.uleb128 0x11
.uleb128 0x1
.uleb128 0x12
.uleb128 0x7
.uleb128 0x10
.uleb128 0x17
.byte 0
.byte 0
.uleb128 0x2
.uleb128 0x2e
.byte 0
.uleb128 0x3f
.uleb128 0x19
.uleb128 0x3
.uleb128 0xe
.uleb128 0x3a
.uleb128 0xb
.uleb128 0x3b
.uleb128 0xb
.uleb128 0x39
.uleb128 0xb
.uleb128 0x49
.uleb128 0x13
.uleb128 0x11
.uleb128 0x1
.uleb128 0x12
.uleb128 0x7
.uleb128 0x40
.uleb128 0x18
.uleb128 0x2117
.uleb128 0x19
.byte 0
.byte 0
.uleb128 0x3
.uleb128 0x24
.byte 0
.uleb128 0xb
.uleb128 0xb
.uleb128 0x3e
.uleb128 0xb
.uleb128 0x3
.uleb128 0x8
.byte 0
.byte 0
.byte 0
.section .debug_aranges,"",@progbits
.long 0x2c
.value 0x2
.long .Ldebug_info0
.byte 0x8
.byte 0
.value 0
.value 0
.quad .Ltext0
.quad .Letext0-.Ltext0
.quad 0
.quad 0
.section .debug_line,"",@progbits
.Ldebug_line0:
.section .debug_str,"MS",@progbits,1
.LASF1:
.string "test.cpp"
.LASF0:
.string "GNU C++14 8.5.0 20210514 (Red Hat 8.5.0-3) -mtune=generic -march=x86-64 -g2"
.LASF2:
.string ""
.LASF3:
.string "main"
.ident "GCC: (GNU) 8.5.0 20210514 (Red Hat 8.5.0-3)"
.section .note.GNU-stack,"",@progbits

View File

@ -0,0 +1,142 @@
.file "test2.cpp"
.text
.Ltext0:
.globl _Z5main2v
.type _Z5main2v, @function
_Z5main2v:
.LFB0:
.file 1 "test2.cpp"
.loc 1 1 13
.cfi_startproc
pushq %rbp
.cfi_def_cfa_offset 16
.cfi_offset 6, -16
movq %rsp, %rbp
.cfi_def_cfa_register 6
.loc 1 1 22
movl $0, %eax
.loc 1 1 25
popq %rbp
.cfi_def_cfa 7, 8
ret
.cfi_endproc
.LFE0:
.size _Z5main2v, .-_Z5main2v
.Letext0:
.section .debug_info,"",@progbits
.Ldebug_info0:
.long 0x53
.value 0x4
.long .Ldebug_abbrev0
.byte 0x8
.uleb128 0x1
.long .LASF0
.byte 0x4
.long .LASF1
.long .LASF2
.quad .Ltext0
.quad .Letext0-.Ltext0
.long .Ldebug_line0
.uleb128 0x2
.long .LASF3
.byte 0x1
.byte 0x1
.byte 0x5
.long .LASF4
.long 0x4f
.quad .LFB0
.quad .LFE0-.LFB0
.uleb128 0x1
.byte 0x9c
.uleb128 0x3
.byte 0x4
.byte 0x5
.string "int"
.byte 0
.section .debug_abbrev,"",@progbits
.Ldebug_abbrev0:
.uleb128 0x1
.uleb128 0x11
.byte 0x1
.uleb128 0x25
.uleb128 0xe
.uleb128 0x13
.uleb128 0xb
.uleb128 0x3
.uleb128 0xe
.uleb128 0x1b
.uleb128 0xe
.uleb128 0x11
.uleb128 0x1
.uleb128 0x12
.uleb128 0x7
.uleb128 0x10
.uleb128 0x17
.byte 0
.byte 0
.uleb128 0x2
.uleb128 0x2e
.byte 0
.uleb128 0x3f
.uleb128 0x19
.uleb128 0x3
.uleb128 0xe
.uleb128 0x3a
.uleb128 0xb
.uleb128 0x3b
.uleb128 0xb
.uleb128 0x39
.uleb128 0xb
.uleb128 0x6e
.uleb128 0xe
.uleb128 0x49
.uleb128 0x13
.uleb128 0x11
.uleb128 0x1
.uleb128 0x12
.uleb128 0x7
.uleb128 0x40
.uleb128 0x18
.uleb128 0x2117
.uleb128 0x19
.byte 0
.byte 0
.uleb128 0x3
.uleb128 0x24
.byte 0
.uleb128 0xb
.uleb128 0xb
.uleb128 0x3e
.uleb128 0xb
.uleb128 0x3
.uleb128 0x8
.byte 0
.byte 0
.byte 0
.section .debug_aranges,"",@progbits
.long 0x2c
.value 0x2
.long .Ldebug_info0
.byte 0x8
.byte 0
.value 0
.value 0
.quad .Ltext0
.quad .Letext0-.Ltext0
.quad 0
.quad 0
.section .debug_line,"",@progbits
.Ldebug_line0:
.section .debug_str,"MS",@progbits,1
.LASF3:
.string "main2"
.LASF2:
.string ""
.LASF0:
.string "GNU C++14 8.5.0 20210514 (Red Hat 8.5.0-3) -mtune=generic -march=x86-64 -g2"
.LASF4:
.string "_Z5main2v"
.LASF1:
.string "test2.cpp"
.ident "GCC: (GNU) 8.5.0 20210514 (Red Hat 8.5.0-3)"
.section .note.GNU-stack,"",@progbits

View File

@ -0,0 +1,32 @@
RUN: llvm-mc -filetype=obj -triple x86_64-unknown-unknown %p/Inputs/dwarfdump-gdbindex.s -o %t.o
RUN: llvm-mc -filetype=obj -triple x86_64-unknown-unknown %p/Inputs/dwarfdump-gdbindex2.s -o %t2.o
RUN: ld.lld --gdb-index %t.o %t2.o -o %tfile.exe
RUN: llvm-bolt %tfile.exe -o %tfile.exe.bolt -update-debug-sections
RUN: llvm-dwarfdump -gdb-index %tfile.exe.bolt | FileCheck %s
; test.cpp:
; int main() { return 0; }
; test2.cpp:
; int main2() { return 0; }
; Compiled with:
; gcc -gsplit-dwarf -c test.cpp test2.cpp
; gold --gdb-index test.o test2.o -o dwarfdump-gdbindex-v7.elf-x86-64
; gcc version 5.3.1 20160413, GNU gold (GNU Binutils for Ubuntu 2.26) 1.11
; Info about gdb-index: https://sourceware.org/gdb/onlinedocs/gdb/Index-Section-Format.html
; CHECK-LABEL: .gdb_index contents:
; CHECK: Version = 7
; CHECK: CU list offset = 0x18, has 2 entries:
; CHECK-NEXT: 0: Offset = 0x0, Length = 0x4f
; CHECK-NEXT: 1: Offset = 0x4f, Length = 0x53
; CHECK: Types CU list offset = 0x38, has 0 entries:
; CHECK: Address area offset = 0x38, has 2 entries:
; CHECK-NEXT: Low/High address = [0x20117c, 0x201187) (Size: 0xb), CU id = 0
; CHECK-NEXT: Low/High address = [0x201188, 0x201193) (Size: 0xb), CU id = 1
; CHECK: Symbol table offset = 0x60, size = 1024, filled slots:
; CHECK: Constant pool offset = 0x2060, has 0 CU vectors: