diff --git a/bolt/include/bolt/Core/DebugData.h b/bolt/include/bolt/Core/DebugData.h --- a/bolt/include/bolt/Core/DebugData.h +++ b/bolt/include/bolt/Core/DebugData.h @@ -107,6 +107,13 @@ /// Common buffer vector used for debug info handling. using DebugBufferVector = SmallVector; +/// Map of old CU offset to new offset and length. +struct CUInfo { + uint32_t Offset; + uint32_t Length; +}; +using CUOffsetMap = std::map; + /// Serializes the .debug_ranges DWARF section. class DebugRangesSectionWriter { public: @@ -155,9 +162,8 @@ /// 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 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 @@ 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 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 @@ using UniquePatchPtrType = std::unique_ptr; uint64_t DWPUnitOffset{0}; - uint32_t ChangeInSize{0}; + int32_t ChangeInSize{0}; std::vector DebugPatches; /// Mutex used for parallel processing of debug info. std::mutex WriterMutex; diff --git a/bolt/include/bolt/Rewrite/DWARFRewriter.h b/bolt/include/bolt/Rewrite/DWARFRewriter.h --- a/bolt/include/bolt/Rewrite/DWARFRewriter.h +++ b/bolt/include/bolt/Rewrite/DWARFRewriter.h @@ -93,14 +93,14 @@ 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 &DWOIdToName); diff --git a/bolt/lib/Core/DebugData.cpp b/bolt/lib/Core/DebugData.cpp --- a/bolt/lib/Core/DebugData.cpp +++ b/bolt/lib/Core/DebugData.cpp @@ -121,8 +121,7 @@ } void DebugARangesSectionWriter::writeARangesSection( - raw_svector_ostream &RangesStream, - const std::unordered_map 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 @@ 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(CUMap.find(Offset)->second), - support::little); + support::endian::write( + RangesStream, static_cast(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 @@ return BinaryContentsStr; } -std::unordered_map -DebugInfoBinaryPatcher::computeNewOffsets() { - std::unordered_map 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 &CU : CompileUnits) + CUMap[CU->getOffset()] = {static_cast(CU->getOffset()), + static_cast(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 @@ 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(P); - OldToNewOffset[DRL->Offset] = DRL->Offset + ChangeInSize; + OldToNewOffset[DRL->Offset] = + DRL->Offset + ChangeInSize + PreviousChangeInSize; break; } case DebugPatchKind::ReferencePatchValue: { @@ -512,7 +521,7 @@ // to reduce algorithmic complexity. DebugPatchReference *RDP = reinterpret_cast(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 @@ DWARFUnitOffsetBaseLabel *BaseLabel = reinterpret_cast(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; } diff --git a/bolt/lib/Rewrite/DWARFRewriter.cpp b/bolt/lib/Rewrite/DWARFRewriter.cpp --- a/bolt/lib/Rewrite/DWARFRewriter.cpp +++ b/bolt/lib/Rewrite/DWARFRewriter.cpp @@ -323,14 +323,14 @@ 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 @@ TypeInfoSection->setIsFinalized(); } -void DWARFRewriter::finalizeDebugSections( - DebugInfoBinaryPatcher &DebugInfoPatcher) { +CUOffsetMap +DWARFRewriter::finalizeDebugSections(DebugInfoBinaryPatcher &DebugInfoPatcher) { if (StrWriter->isInitialized()) { RewriteInstance::addToDebugSectionsToOverwrite(".debug_str"); std::unique_ptr DebugStrSectionContents = @@ -898,8 +898,8 @@ } // No more creating new DebugInfoPatches. - std::unordered_map 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 @@ copyByteArray(ARangesContents), ARangesContents.size()); } + return CUMap; } // Creates all the data structures necessary for creating MCStreamer. @@ -958,15 +959,14 @@ // Exctracts an appropriate slice if input is DWP. // Applies patches or overwrites the section. -Optional -updateDebugData(std::string &Storage, const SectionRef &Section, - const StringMap &KnownSections, - MCStreamer &Streamer, DWARFRewriter &Writer, - const DWARFUnitIndex::Entry *DWOEntry, uint64_t DWOId, - std::unique_ptr &OutputBuffer) { +Optional updateDebugData( + DWARFContext &DWCtx, std::string &Storage, const SectionRef &Section, + const StringMap &KnownSections, MCStreamer &Streamer, + DWARFRewriter &Writer, const DWARFUnitIndex::Entry *DWOEntry, + uint64_t DWOId, std::unique_ptr &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 @@ for (const SectionRef &Section : DWOFile->sections()) { std::string Storage = ""; std::unique_ptr OutputData; - Optional TOutData = - updateDebugData(Storage, Section, KnownSections, *Streamer, *this, - DWOEntry, *DWOId, OutputData); + Optional TOutData = updateDebugData( + (*DWOCU)->getContext(), Storage, Section, KnownSections, *Streamer, + *this, DWOEntry, *DWOId, OutputData); if (!TOutData) continue; @@ -1227,9 +1227,9 @@ for (const SectionRef &Section : File->sections()) { std::string Storage = ""; std::unique_ptr OutputData; - if (Optional OutData = - updateDebugData(Storage, Section, KnownSections, *Streamer, *this, - DWOEntry, *DWOId, OutputData)) + if (Optional OutData = updateDebugData( + (*DWOCU)->getContext(), Storage, Section, KnownSections, + *Streamer, *this, DWOEntry, *DWOId, OutputData)) Streamer->emitBytes(*OutData); } Streamer->Finish(); @@ -1237,7 +1237,7 @@ } } -void DWARFRewriter::updateGdbIndexSection() { +void DWARFRewriter::updateGdbIndexSection(CUOffsetMap &CUMap) { if (!BC.getGdbIndexSection()) return; @@ -1314,10 +1314,23 @@ 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 + 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 &CURangesPair : diff --git a/bolt/test/X86/Inputs/dwarfdump-gdbindex.s b/bolt/test/X86/Inputs/dwarfdump-gdbindex.s new file mode 100644 --- /dev/null +++ b/bolt/test/X86/Inputs/dwarfdump-gdbindex.s @@ -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 diff --git a/bolt/test/X86/Inputs/dwarfdump-gdbindex2.s b/bolt/test/X86/Inputs/dwarfdump-gdbindex2.s new file mode 100644 --- /dev/null +++ b/bolt/test/X86/Inputs/dwarfdump-gdbindex2.s @@ -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 diff --git a/bolt/test/X86/gdbindex.test b/bolt/test/X86/gdbindex.test new file mode 100644 --- /dev/null +++ b/bolt/test/X86/gdbindex.test @@ -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: