Index: debuginfo-tests/CMakeLists.txt =================================================================== --- debuginfo-tests/CMakeLists.txt +++ debuginfo-tests/CMakeLists.txt @@ -16,11 +16,11 @@ count llvm-objdump check-gdb-llvm-support + llvm-dwarfdump not ) -# The Windows builder scripts pass -fuse-ld=lld. -if (WIN32) +if (NOT APPLE) set(DEBUGINFO_TEST_DEPS ${DEBUGINFO_TEST_DEPS} lld) endif() Index: debuginfo-tests/dwarflinker/Inputs/gc-debuginfo-main.c =================================================================== --- /dev/null +++ debuginfo-tests/dwarflinker/Inputs/gc-debuginfo-main.c @@ -0,0 +1,30 @@ + +int getModule1Value(int); +int getModule2Value(int); + +extern void writeSum(int sum, int depth) { + int result = sum + 10; + + if (depth > 2) + return; + + writeSum(sum, depth + 1); +} + +int main(void) { + int var1 = getModule1Value(3); + + int var2 = getModule2Value(5); + + int sum = 0; + + for (int idx = 0; idx < 1000; idx++) { + sum = var1 * idx; + + sum += var2; + + writeSum(sum, 0); + } + + return 0; +} Index: debuginfo-tests/dwarflinker/Inputs/gc-debuginfo-mod1.c =================================================================== --- /dev/null +++ debuginfo-tests/dwarflinker/Inputs/gc-debuginfo-mod1.c @@ -0,0 +1,11 @@ + +int getModule1Value(int p1) { return p1 + 10; } + +int getModule1ValueNotUsed(int p1) { + int result = 0; + for (int i = 0; i < p1; i++) { + result++; + } + + return result; +} Index: debuginfo-tests/dwarflinker/Inputs/gc-debuginfo-mod2.c =================================================================== --- /dev/null +++ debuginfo-tests/dwarflinker/Inputs/gc-debuginfo-mod2.c @@ -0,0 +1,11 @@ + +int getModule2Value(int p1) { return p1 + 10; } + +int getModule2ValueNotUsed(int p1) { + int result = 0; + for (int i = 0; i < p1; i++) { + result++; + } + + return result; +} Index: debuginfo-tests/dwarflinker/gc-debuginfo.c =================================================================== --- /dev/null +++ debuginfo-tests/dwarflinker/gc-debuginfo.c @@ -0,0 +1,92 @@ +// RUN: %clang %target_itanium_abi_host_triple -gdwarf-4 -ffunction-sections \ +// RUN: -fno-inline -O %p/Inputs/gc-debuginfo-main.c -c -o %t.o +// RUN: %clang %target_itanium_abi_host_triple -gdwarf-4 -ffunction-sections \ +// RUN: -fno-inline -O %p/Inputs/gc-debuginfo-mod1.c -c -o %t1.o +// RUN: %clang %target_itanium_abi_host_triple -gdwarf-4 -ffunction-sections \ +// RUN: -fno-inline -O %p/Inputs/gc-debuginfo-mod2.c -c -o %t2.o +// RUN: %clang %target_itanium_abi_host_triple -gdwarf-4 -O -fuse-ld=lld \ +// RUN: -Wl,--gc-sections,--gc-debuginfo %t.o %t1.o %t2.o -o %t3.out +// RUN: llvm-dwarfdump -a %t3.out | FileCheck %s + +// UNSUPPORTED: system-darwin + +// This test checks that --gc-debuginfo removes unused debug info. +// It checks that debug info related to getModule1ValueNotUsed() and +// getModule2ValueNotUsed() functions is deleted from resulting .debug* +// tables. .debug_loc table should be empty since getModule1Value() +// and getModule1Value() functions do not refer to .debug_loc. + +CHECK: .debug_info contents: +CHECK: DW_TAG_compile_unit +CHECK: DW_AT_name ("{{.*}}gc-debuginfo-main.c") +CHECK: DW_AT_stmt_list (0x[[MAIN_STMT_LIST:[0-9A-Fa-f]+]]) +CHECK: DW_AT_ranges (0x00000000 +CHECK-NEXT: [0x{{0*}}[[MAIN_ADDR:[0-9A-Fa-f]+]], 0x{{[0-9a-zA-Z]*}})) +CHECK: DW_TAG_subprogram +CHECK: DW_AT_low_pc (0x{{0*}}[[MAIN_ADDR]]) +CHECK-NOT: getModule1ValueNotUsed +CHECK-NOT: getModule2ValueNotUsed +CHECK: DW_AT_name ("main") +CHECK: DW_TAG_subprogram +CHECK-NOT: getModule1ValueNotUsed +CHECK-NOT: getModule2ValueNotUsed +CHECK: DW_AT_name ("getModule1Value") +CHECK: DW_TAG_subprogram +CHECK-NOT: getModule1ValueNotUsed +CHECK-NOT: getModule2ValueNotUsed +CHECK: DW_AT_name ("getModule2Value") +CHECK: Compile Unit +CHECK: DW_TAG_compile_unit +CHECK-NOT: getModule1ValueNotUsed +CHECK-NOT: getModule2ValueNotUsed +CHECK: DW_AT_name ("{{.*}}gc-debuginfo-mod1.c") +CHECK: DW_AT_stmt_list (0x{{0*}}[[MOD1_STMT_LIST:[0-9A-Fa-f]+]]) +CHECK: DW_AT_ranges (0x{{0*}}[[RANGE_LIST_MOD1:[0-9A-Fa-f]+]] +CHECK-NEXT: [0x{{0*}}[[GET_MOD1_ADDR:[0-9A-Fa-f]+]], 0x{{[0-9a-zA-Z]*}})) +CHECK: DW_TAG_subprogram +CHECK: DW_AT_low_pc (0x{{0*}}[[GET_MOD1_ADDR]] +CHECK-NOT: getModule1ValueNotUsed +CHECK-NOT: getModule2ValueNotUsed +CHECK: DW_AT_name ("getModule1Value") +CHECK: Compile Unit +CHECK: DW_TAG_compile_unit +CHECK-NOT: getModule1ValueNotUsed +CHECK-NOT: getModule2ValueNotUsed +CHECK: DW_AT_name ("{{.*}}gc-debuginfo-mod2.c") +CHECK: DW_AT_stmt_list (0x{{0*}}[[MOD2_STMT_LIST:[0-9A-Fa-f]+]]) +CHECK: DW_AT_ranges (0x{{0*}}[[RANGE_LIST_MOD2:[0-9A-Fa-f]+]] +CHECK-NEXT: [0x[[GET_MOD2_ADDR:[0-9A-Fa-f]+]], 0x{{[0-9a-zA-Z]*}})) +CHECK: DW_TAG_subprogram +CHECK: DW_AT_low_pc (0x{{0*}}[[GET_MOD2_ADDR]] +CHECK-NOT: getModule1ValueNotUsed +CHECK-NOT: getModule2ValueNotUsed +CHECK: DW_AT_name ("getModule2Value") +CHECK-NOT: getModule1ValueNotUsed +CHECK-NOT: getModule2ValueNotUsed +CHECK: NULL + +CHECK-NOT: .debug_loc contents: + +CHECK: .debug_line contents: +CHECK: debug_line[0x[[MAIN_STMT_LIST]]] +CHECK: Address +CHECK: 0x{{0*}}[[MAIN_ADDR:[0-9A-Fa-f]+]] +CHECK: debug_line[0x{{0*}}[[MOD1_STMT_LIST]]] +CHECK: Address +CHECK: 0x{{0*}}[[GET_MOD1_ADDR:[0-9A-Fa-f]+]] +CHECK: debug_line[0x{{0*}}[[MOD2_STMT_LIST]]] +CHECK: Address +CHECK: 0x{{0*}}[[GET_MOD2_ADDR:[0-9A-Fa-f]+]] + + +CHECK: .debug_str contents: +CHECK-NOT: getModule1ValueNotUsed +CHECK-NOT: getModule2ValueNotUsed + +CHECK: .debug_ranges contents: +CHECK: 00000000 0000000000000000 {{[0-9a-zA-Z]*}} +CHECK: 00000000 +CHECK: {{0*}}[[RANGE_LIST_MOD1]] 0000000000000000 {{[0-9a-zA-Z]*}} +CHECK: {{0*}}[[RANGE_LIST_MOD1]] +CHECK: {{0*}}[[RANGE_LIST_MOD2]] 0000000000000000 {{[0-9a-zA-Z]*}} +CHECK: {{0*}}[[RANGE_LIST_MOD2]] Index: debuginfo-tests/llgdb-tests/dwarflinker/Inputs/gc-debuginfo-mod1.c =================================================================== --- /dev/null +++ debuginfo-tests/llgdb-tests/dwarflinker/Inputs/gc-debuginfo-mod1.c @@ -0,0 +1,10 @@ + +int getModule1Value(int p1) { + int resultMod1 = 0; + for (int i = 0; i < p1; i++) + resultMod1++; + + return resultMod1; +} + +int getModule1ValueNotUsed(int p1) { return p1 + 10; } Index: debuginfo-tests/llgdb-tests/dwarflinker/Inputs/gc-debuginfo-mod2.c =================================================================== --- /dev/null +++ debuginfo-tests/llgdb-tests/dwarflinker/Inputs/gc-debuginfo-mod2.c @@ -0,0 +1,10 @@ + +int getModule2Value(int p1) { + int resultMod2 = 0; + for (int i = 0; i < p1; i++) + resultMod2 += p1; + + return resultMod2; +} + +int getModule2ValueNotUsed(int p1) { return p1 + 10; } Index: debuginfo-tests/llgdb-tests/dwarflinker/gc-debuginfo.c =================================================================== --- /dev/null +++ debuginfo-tests/llgdb-tests/dwarflinker/gc-debuginfo.c @@ -0,0 +1,53 @@ +// RUN: %clang %target_itanium_abi_host_triple -gdwarf-4 -ffunction-sections \ +// RUN: -O1 -fno-inline %s -c -o %t.o +// RUN: %clang %target_itanium_abi_host_triple -gdwarf-4 -ffunction-sections \ +// RUN: -O1 -fno-inline %p/Inputs/gc-debuginfo-mod1.c -c -o %t1.o +// RUN: %clang %target_itanium_abi_host_triple -gdwarf-4 -ffunction-sections \ +// RUN: -O1 -fno-inline %p/Inputs/gc-debuginfo-mod2.c -c -o %t2.o +// RUN: %clang %target_itanium_abi_host_triple -gdwarf-4 -O1 -fno-inline \ +// RUN: -fuse-ld=lld -Wl,--gc-sections,--gc-debuginfo %t.o %t1.o %t2.o -o %t3.out +// RUN: %test_debuginfo %s %t3.out + +// UNSUPPORTED: system-darwin + +// This test checks that location lists are valid after --gc-debuginfo done. +// It steps into getModule1Value() and getModule2Value()(located in different +// object files) and prints values of internal variable result. It should +// print proper values if location lists are correct. + +int getModule1Value(); +int getModule2Value(); + +extern void writeSum(int sum, int depth) { + int result = sum + 10; + + if (depth > 1) + return; + + writeSum(sum, depth + 1); +} + +int main(void) { + writeSum(getModule1Value(1), 0); + + writeSum(getModule2Value(2), 0); + + return 0; +} + +// DEBUGGER: break main +// CHECK: gc-debuginfo.c, line 31 +// DEBUGGER: r +// DEBUGGER: s +// CHECK: getModule1Value (p1=1) +// DEBUGGER: s +// DEBUGGER: p resultMod1 +// CHECK: = 1 +// DEBUGGER: n +// DEBUGGER: s +// CHECK: getModule2Value (p1=2) +// DEBUGGER: s +// DEBUGGER: p resultMod2 +// CHECK: = 4 +// DEBUGGER: n +// CHECK: return 0 Index: lld/ELF/CMakeLists.txt =================================================================== --- lld/ELF/CMakeLists.txt +++ lld/ELF/CMakeLists.txt @@ -34,6 +34,7 @@ LTO.cpp LinkerScript.cpp MapFile.cpp + LLDDwarfLinker.cpp MarkLive.cpp OutputSections.cpp Relocations.cpp @@ -52,6 +53,8 @@ BitWriter Core DebugInfoDWARF + DWARFLinker + AsmPrinter Demangle LTO MC @@ -59,6 +62,9 @@ Option Passes Support + AllTargetsCodeGens + AllTargetsDescs + AllTargetsInfos LINK_LIBS lldCommon Index: lld/ELF/Config.h =================================================================== --- lld/ELF/Config.h +++ lld/ELF/Config.h @@ -154,6 +154,7 @@ bool fixCortexA53Errata843419; bool fixCortexA8; bool formatBinary = false; + bool gcDebugInfo; bool gcSections; bool gdbIndex; bool gnuHash = false; Index: lld/ELF/DWARF.h =================================================================== --- lld/ELF/DWARF.h +++ lld/ELF/DWARF.h @@ -10,8 +10,10 @@ #define LLD_ELF_DWARF_H #include "InputFiles.h" +#include "Relocations.h" #include "llvm/ADT/STLExtras.h" #include "llvm/DebugInfo/DWARF/DWARFContext.h" +#include "llvm/DebugInfo/DWARF/DWARFSection.h" #include "llvm/Object/ELF.h" namespace lld { @@ -21,6 +23,9 @@ struct LLDDWARFSection final : public llvm::DWARFSection { InputSectionBase *sec = nullptr; + + /// Index into relocations array of the next relocation to consider. + mutable uint64_t nextReloc = 0; }; template class LLDDwarfObj final : public llvm::DWARFObject { @@ -60,6 +65,10 @@ return gnuPubtypesSection; } + const llvm::DWARFSection &getLocSection() const override { + return locSection; + } + StringRef getFileName() const override { return ""; } StringRef getAbbrevSection() const override { return abbrevSection; } StringRef getStrSection() const override { return strSection; } @@ -72,12 +81,31 @@ llvm::Optional find(const llvm::DWARFSection &sec, uint64_t pos) const override; + ArrayRef getSectionNames() const override { + return sectionNames; + } + + /// This function calls specified handler for all relocations + /// pointing to live section and located from "startPos" till "endPos" + /// for the section "s". + void enumerateRelocations( + const llvm::DWARFSection &s, uint64_t startPos, uint64_t endPos, + llvm::function_ref + relHandler) const; + private: template llvm::Optional findAux(const InputSectionBase &sec, uint64_t pos, ArrayRef rels) const; + template + void enumerateRelocations( + InputSectionBase *sec, ArrayRef rels, uint64_t &nextReloc, + uint64_t startPos, uint64_t endPos, + llvm::function_ref + relHandler) const; + LLDDWARFSection gnuPubnamesSection; LLDDWARFSection gnuPubtypesSection; LLDDWARFSection infoSection; @@ -86,9 +114,12 @@ LLDDWARFSection strOffsetsSection; LLDDWARFSection lineSection; LLDDWARFSection addrSection; + LLDDWARFSection locSection; StringRef abbrevSection; StringRef strSection; StringRef lineStrSection; + + std::vector sectionNames; }; } // namespace elf Index: lld/ELF/DWARF.cpp =================================================================== --- lld/ELF/DWARF.cpp +++ lld/ELF/DWARF.cpp @@ -17,8 +17,8 @@ #include "Symbols.h" #include "Target.h" #include "lld/Common/Memory.h" +#include "llvm/BinaryFormat/ELF.h" #include "llvm/DebugInfo/DWARF/DWARFDebugPubTable.h" -#include "llvm/Object/ELFObjectFile.h" using namespace llvm; using namespace llvm::object; @@ -26,33 +26,42 @@ namespace lld { namespace elf { template LLDDwarfObj::LLDDwarfObj(ObjFile *obj) { + StringMap sectionAmountMap; for (InputSectionBase *sec : obj->getSections()) { if (!sec) - continue; - - if (LLDDWARFSection *m = - StringSwitch(sec->name) - .Case(".debug_addr", &addrSection) - .Case(".debug_gnu_pubnames", &gnuPubnamesSection) - .Case(".debug_gnu_pubtypes", &gnuPubtypesSection) - .Case(".debug_info", &infoSection) - .Case(".debug_ranges", &rangesSection) - .Case(".debug_rnglists", &rnglistsSection) - .Case(".debug_str_offsets", &strOffsetsSection) - .Case(".debug_line", &lineSection) - .Default(nullptr)) { - m->Data = toStringRef(sec->data()); - m->sec = sec; - continue; - } + sectionNames.push_back({"", true}); + else { + sectionNames.push_back({sec->name, true}); + sectionAmountMap[sec->name]++; + + if (LLDDWARFSection *m = + StringSwitch(sec->name) + .Case(".debug_addr", &addrSection) + .Case(".debug_gnu_pubnames", &gnuPubnamesSection) + .Case(".debug_gnu_pubtypes", &gnuPubtypesSection) + .Case(".debug_info", &infoSection) + .Case(".debug_ranges", &rangesSection) + .Case(".debug_rnglists", &rnglistsSection) + .Case(".debug_str_offsets", &strOffsetsSection) + .Case(".debug_line", &lineSection) + .Case(".debug_loc", &locSection) + .Default(nullptr)) { + m->Data = toStringRef(sec->data()); + m->sec = sec; + continue; + } - if (sec->name == ".debug_abbrev") - abbrevSection = toStringRef(sec->data()); - else if (sec->name == ".debug_str") - strSection = toStringRef(sec->data()); - else if (sec->name == ".debug_line_str") - lineStrSection = toStringRef(sec->data()); + if (sec->name == ".debug_abbrev") + abbrevSection = toStringRef(sec->data()); + else if (sec->name == ".debug_str") + strSection = toStringRef(sec->data()); + else if (sec->name == ".debug_line_str") + lineStrSection = toStringRef(sec->data()); + } } + for (SectionName &sec : sectionNames) + if (sec.Name.size() > 0 && sectionAmountMap[sec.Name] > 1) + sec.IsNameUnique = false; } namespace { @@ -118,6 +127,72 @@ return findAux(*sec.sec, pos, sec.sec->template rels()); } +// This function calls specified handler for all relocations +// pointing to live section and located from "startPos" till "endPos" +// for the section "s". +template +void LLDDwarfObj::enumerateRelocations( + const llvm::DWARFSection &s, uint64_t startPos, uint64_t endPos, + llvm::function_ref + relHandler) const { + auto &sec = static_cast(s); + if (sec.sec->areRelocsRela) + enumerateRelocations(sec.sec, sec.sec->template relas(), + sec.nextReloc, startPos, endPos, relHandler); + else + enumerateRelocations(sec.sec, sec.sec->template rels(), sec.nextReloc, + startPos, endPos, relHandler); +} + +// This function calls specified handler for all relocations pointing +// to live section and located from "startPos" till "endPos" from relocations +// array "rels". +// Assumption: "startPos"/"endPos" are always specified in increasing +// order(i.e. "enumerateRelocations" are always called with "startPos"/"endPos" +// having greater value than in previous call). "rels" contains relocations +// sorted on r_offset basis. +template +template +void LLDDwarfObj::enumerateRelocations( + InputSectionBase *sec, ArrayRef rels, uint64_t &nextReloc, + uint64_t startPos, uint64_t endPos, + llvm::function_ref + relHandler) const { + // check if there are no more relocations. + if (nextReloc >= rels.size()) + return; + + // skip relocations, started from nextReloc until relocPos < startPos. + uint64_t relocPos = rels[nextReloc].r_offset; + while (relocPos < startPos && nextReloc < rels.size() - 1) + relocPos = rels[++nextReloc].r_offset; + + // enumerate relocations in range [startPos, endPos). + const ObjFile *file = sec->getFile(); + while (relocPos >= startPos && relocPos < endPos) { + const RelTy &rel = rels[nextReloc]; + + const typename ELFT::Sym &sym = + file->template getELFSyms()[rel.getSymbol(config->isMips64EL)]; + uint32_t secIndex = file->getSectionIndex(sym); + + InputSectionBase *referencedSection = file->getSections()[secIndex]; + + // check whether relocation points to live section. + if (referencedSection && referencedSection->isLive()) { + Relocation reloc = {R_ABS, rel.getType(config->isMips64EL), rel.r_offset, + getAddend(rel), &file->getRelocTargetSym(rel)}; + + relHandler(reloc, referencedSection); + } + + if (++nextReloc >= rels.size()) + break; + + relocPos = rels[nextReloc].r_offset; + } +} + template class LLDDwarfObj; template class LLDDwarfObj; template class LLDDwarfObj; Index: lld/ELF/Driver.cpp =================================================================== --- lld/ELF/Driver.cpp +++ lld/ELF/Driver.cpp @@ -285,6 +285,7 @@ static void initLLVM() { InitializeAllTargets(); InitializeAllTargetMCs(); + InitializeAllTargetInfos(); InitializeAllAsmPrinters(); InitializeAllAsmParsers(); } @@ -329,6 +330,8 @@ error("-r and -shared may not be used together"); if (config->gcSections) error("-r and --gc-sections may not be used together"); + if (config->gcDebugInfo) + error("-r and --gc-debuginfo may not be used together"); if (config->gdbIndex) error("-r and --gdb-index may not be used together"); if (config->icf != ICFLevel::None) @@ -339,6 +342,10 @@ error("-r and --export-dynamic may not be used together"); } + if (config->gcDebugInfo && !config->gcSections) { + error("--gc-debuginfo may not be used without --gc-sections"); + } + if (config->executeOnly) { if (config->emachine != EM_AARCH64) error("-execute-only is only supported on AArch64 targets"); @@ -904,6 +911,8 @@ config->fixCortexA8 = args.hasArg(OPT_fix_cortex_a8) && !args.hasArg(OPT_relocatable); config->gcSections = args.hasFlag(OPT_gc_sections, OPT_no_gc_sections, false); + config->gcDebugInfo = + args.hasFlag(OPT_gc_debuginfo, OPT_no_gc_debuginfo, false); config->gnuUnique = args.hasFlag(OPT_gnu_unique, OPT_no_gnu_unique, true); config->gdbIndex = args.hasFlag(OPT_gdb_index, OPT_no_gdb_index, false); config->icf = getICF(args); @@ -1955,11 +1964,11 @@ if (config->strip == StripPolicy::None) return false; - if (isDebugSection(*s)) + if (isDebugSection(s->name)) return true; if (auto *isec = dyn_cast(s)) if (InputSectionBase *rel = isec->getRelocatedSection()) - if (isDebugSection(*rel)) + if (isDebugSection(rel->name)) return true; return false; Index: lld/ELF/InputSection.h =================================================================== --- lld/ELF/InputSection.h +++ lld/ELF/InputSection.h @@ -124,6 +124,8 @@ // its static type. InputFile *file; + bool isDebug = false; + template ObjFile *getFile() const { return cast_or_null>(file); } @@ -357,8 +359,8 @@ template void copyShtGroup(uint8_t *buf); }; -inline bool isDebugSection(const InputSectionBase &sec) { - return sec.name.startswith(".debug") || sec.name.startswith(".zdebug"); +inline bool isDebugSection(StringRef name) { + return name.startswith(".debug") || name.startswith(".zdebug"); } // The list of all input sections. Index: lld/ELF/InputSection.cpp =================================================================== --- lld/ELF/InputSection.cpp +++ lld/ELF/InputSection.cpp @@ -70,6 +70,7 @@ numRelocations = 0; areRelocsRela = false; + isDebug = isDebugSection(name); // The ELF spec states that a value of 0 means the section has // no alignment constraints. @@ -441,7 +442,7 @@ // See the comment in maybeReportUndefined for PPC32 .got2 and PPC64 .toc auto *d = dyn_cast(&sym); if (!d) { - if (!isDebugSection(*sec) && sec->name != ".eh_frame" && + if (!isDebugSection(sec->name) && sec->name != ".eh_frame" && sec->name != ".gcc_except_table" && sec->name != ".got2" && sec->name != ".toc") { uint32_t secIdx = cast(sym).discardedSecIdx; Index: lld/ELF/LLDDwarfLinker.h =================================================================== --- /dev/null +++ lld/ELF/LLDDwarfLinker.h @@ -0,0 +1,28 @@ +//===- LLDDwarfLinker.h -----------------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef LLD_ELF_LLDDWARFLINKER_H +#define LLD_ELF_LLDDWARFLINKER_H + +#include "Writer.h" +#include "llvm/ADT/SmallString.h" + +namespace lld { +namespace elf { + +// This function is used to remove an unused parts of debug data +// which belongs to garbage collected sections. It writes linked +// debug info into specified outDebugInfoBytes container and links +// output sections to their appropriate portions of debug info from +// outDebugInfoBytes. +template void linkDebugInfo(DebugDataBits &outDebugInfoBytes); + +} // namespace elf +} // namespace lld + +#endif // LLD_ELF_LLDDWARFLINKER_H Index: lld/ELF/LLDDwarfLinker.cpp =================================================================== --- /dev/null +++ lld/ELF/LLDDwarfLinker.cpp @@ -0,0 +1,302 @@ +//===- LLDDwarfLinker.cpp -------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file implements --gc-debuginfo functionality: +// +// When the linker does garbage collection, a lot of abandoned debug info +// is left behind. To remove such abandoned debug info LLDDwarfLinker analyzes +// relocations and removes debug info related to deleted sections. +// +// The process of linking debug info is based on liveness information created by +// markLive(). AddressesMap::getValidAddressRanges() creates a list +// of address ranges which points into live sections. DWARFLinker searches +// for 'root DIEs' related to live address ranges and calls cloneDIE() +// recursively. Cloned dies are passed to DWARFStreamer through DwarfEmitter +// interface. DWARFStreamer writes data into plain memory container. +// Corresponding sections of that container are linked with output sections. +// +//===----------------------------------------------------------------------===// + +#include "LLDDwarfLinker.h" +#include "DWARF.h" +#include "InputFiles.h" +#include "InputSection.h" +#include "OutputSections.h" +#include "Symbols.h" +#include "Target.h" +#include "lld/Common/DWARF.h" +#include "lld/Common/ErrorHandler.h" +#include "lld/Common/Threads.h" +#include "llvm/ADT/STLExtras.h" +#include "llvm/ADT/Triple.h" +#include "llvm/DWARFLinker/DWARFLinker.h" +#include "llvm/DWARFLinker/DWARFStreamer.h" +#include "llvm/Object/ELF.h" +#include "llvm/Object/ELFObjectFile.h" +#include "llvm/Support/SmallVectorMemoryBuffer.h" +#include "llvm/Target/TargetLoweringObjectFile.h" + +using namespace llvm; +using namespace llvm::ELF; +using namespace llvm::object; + +namespace lld { +namespace elf { + +template class ObjFileAddressMap : public AddressesMap { +public: + ObjFileAddressMap(DWARFCache &dwarf, ObjFile *objFile) + : dwarf(dwarf), objFile(objFile), + dwarfObj(static_cast &>( + dwarf.getContext()->getDWARFObj())) { + + // Create map InputSection -> SectionIndex. + DenseMap sectionIndexesMap; + uint64_t curIdx = 0; + for (InputSectionBase *sec : objFile->getSections()) { + if (sec) + sectionIndexesMap[sec] = curIdx; + curIdx++; + } + + // Discover live address ranges. + for (const Symbol *symbol : objFile->symbols) { + if (symbol->isFunc() || symbol->isObject()) { + if (symbol->kind() == Symbol::DefinedKind) { + InputSectionBase *sec = + cast(cast(*symbol).section); + + if (sec && sec->isLive() && sectionIndexesMap.count(sec)) { + hasValidRelocations = true; + + SymbolAddresses addr = getSymbolAddresses(symbol, sec); + addressRanges[{addr.objectAddress, sectionIndexesMap[sec]}] = { + addr.objectAddress + symbol->getSize(), + (int64_t)addr.inputSectionAddress}; + } + } + } + } + } + + bool hasValidRelocs(bool resetRelocsPtr = true) override { + // TODO: Support the case multiplue .debug_info sections. + // TODO: HasValidRelocs should receive section + // TODO: which should be examined. + if (resetRelocsPtr) + dwarfObj.forEachInfoSections([&](const llvm::DWARFSection &sec) { + auto &debugInfoSec = static_cast(sec); + debugInfoSec.nextReloc = 0; + }); + + return hasValidRelocations; + } + + bool hasValidRelocationAt(uint64_t startOffset, uint64_t endOffset, + CompileUnit::DIEInfo &info) override { + + bool result = false; + + // TODO: Support the case multiplue .debug_info sections. + // TODO: HasValidRelocationAt should receive section + // TODO: which should be examined. + dwarfObj.forEachInfoSections([&](const llvm::DWARFSection &sec) { + dwarfObj.enumerateRelocations( + sec, startOffset, endOffset, + [&](Relocation rel, const InputSectionBase *sec) { + if (!result) { + if (!sec->isDebug) { + info.AddrAdjust = + getSymbolAddresses(rel.sym, sec).inputSectionAddress; + info.InDebugMap = true; + result = true; + } + } + }); + }); + + return result; + } + + bool applyValidRelocs(MutableArrayRef data, uint64_t baseOffset, + bool) override { + bool result = false; + + // TODO: Support the case multiplue .debug_info sections. + // TODO: ApplyValidRelocs should receive section + // TODO: which should be examined. + dwarfObj.forEachInfoSections([&](const llvm::DWARFSection &sec) { + dwarfObj.enumerateRelocations( + sec, baseOffset, baseOffset + data.size(), + [&](Relocation rel, const InputSectionBase *sec) { + SymbolAddresses Addr = getSymbolAddresses(rel.sym, sec); + + uint64_t Value = Addr.inputSectionAddress + Addr.objectAddress; + Value += rel.addend; + + // Apply relocation. + assert(rel.offset - baseOffset < data.size()); + getTarget()->relocate( + (uint8_t *)(data.data() + rel.offset - baseOffset), rel, Value); + result = true; + }); + }); + + return result; + } + + RangesTy &getValidAddressRanges() override { return addressRanges; }; + + void clear() override { addressRanges.clear(); } + +private: + struct SymbolAddresses { + uint64_t objectAddress = 0; + uint64_t inputSectionAddress = 0; + }; + + // Returns object address(symbol value) and address of input section. + SymbolAddresses + getSymbolAddresses(const Symbol *sym, + const InputSectionBase *referencedSection) { + SymbolAddresses result; + + if (sym && sym->kind() == Symbol::DefinedKind) { + auto &definedSym = cast(*sym); + + result.objectAddress = definedSym.value; + + if (referencedSection && !referencedSection->isDebug) { + result.inputSectionAddress = + referencedSection->getVA(definedSym.value) - definedSym.value; + } + } + + return result; + } + + RangesTy addressRanges; + DWARFCache &dwarf; + ObjFile *objFile; + const LLDDwarfObj &dwarfObj; + bool hasValidRelocations = false; +}; + +// This function is used to remove an unused parts of debug data +// which belongs to garbage collected sections. +template void linkDebugInfo(DebugDataBits &outDebugInfoBytes) { + + // Calculate estimated size of resulting debug info. + uint64_t estimatedSize = 0; + for (const auto sec : inputSections) { + if (sec->isDebug && sec->isLive()) + estimatedSize += sec->getSize() * 0.5f; + } + outDebugInfoBytes.reserve(estimatedSize); + + // Create output streamer. + raw_svector_ostream stream(outDebugInfoBytes); + DwarfStreamer outStreamer( + OutputFileType::Object, stream, + [](StringRef input) -> StringRef { return input; }, false, + [&](const Twine &err, StringRef context, const DWARFDie *) { + if (context.size() > 0) + warn(context + ": " + err); + else + warn(err); + }, + [&](const Twine &warning, StringRef context, const DWARFDie *) { + if (context.size() > 0) + warn(context + ": " + warning); + else + warn(warning); + }); + if (!outStreamer.init(Triple(sys::getProcessTriple()))) { + return; + } + + // Create DWARF linker. + DWARFLinker debugInfoLinker(&outStreamer, DwarfLinkerClient::LLD); + + debugInfoLinker.setEstimatedObjfilesAmount(objectFiles.size()); + debugInfoLinker.setAccelTableKind(AccelTableKind::Dwarf); + debugInfoLinker.setErrorHandler( + [&](const Twine &err, StringRef context, const DWARFDie *) { + if (context.size() > 0) + warn(context + ": " + err); + else + warn(err); + }); + debugInfoLinker.setWarningHandler( + [&](const Twine &warning, StringRef context, const DWARFDie *) { + if (context.size() > 0) + warn(context + ": " + warning); + else + warn(warning); + }); + + std::vector> objectsForLinking(objectFiles.size()); + std::vector> addresssMapForLinking( + objectFiles.size()); + const std::vector emptyWarnings; + + // Add object files to the DWARFLinker. + for (size_t i = 0; i < objectFiles.size(); i++) { + if (ObjFile *obj = cast>(objectFiles[i])) { + addresssMapForLinking[i] = + std::make_unique>(*obj->getDwarf(), obj); + + objectsForLinking[i] = std::make_unique( + objectFiles[i]->getName(), obj->getDwarf()->getContext(), + addresssMapForLinking[i].get(), emptyWarnings); + + debugInfoLinker.addObjectFile(*objectsForLinking[i]); + } + } + + // Link debug info. + debugInfoLinker.link(); + outStreamer.finish(); + + // Create sections map. + DenseMap sectionsMap; + if (Expected> MemFile = + ELFObjectFile::create({outDebugInfoBytes, ""})) { + for (const SectionRef &Sec : MemFile->sections()) { + if (Expected name = Sec.getName()) { + if (!isDebugSection(*name)) + continue; + + if (Expected contents = Sec.getContents()) { + sectionsMap[*name] = *contents; + } + } + } + } + + // Set linked debug info to corresponding output section. + for (auto &sec : outputSections) { + if (!isDebugSection(sec->name)) + continue; + + auto sectionEntry = sectionsMap.find(sec->name); + + if (sectionEntry != sectionsMap.end()) + sec->setDebugData(sectionEntry->second); + else + sec->setDebugData(StringRef()); + } +} + +template void linkDebugInfo(DebugDataBits &); +template void linkDebugInfo(DebugDataBits &); +template void linkDebugInfo(DebugDataBits &); +template void linkDebugInfo(DebugDataBits &); + +} // namespace elf +} // namespace lld Index: lld/ELF/Options.td =================================================================== --- lld/ELF/Options.td +++ lld/ELF/Options.td @@ -183,6 +183,10 @@ "Enable garbage collection of unused sections", "Disable garbage collection of unused sections (default)">; +defm gc_debuginfo: B<"gc-debuginfo", + "Enable garbage collection of unused debug information", + "Disable garbage collection of unused debug information (default)">; + defm gdb_index: B<"gdb-index", "Generate .gdb_index section", "Do not generate .gdb_index section (default)">; Index: lld/ELF/OutputSections.h =================================================================== --- lld/ELF/OutputSections.h +++ lld/ELF/OutputSections.h @@ -108,12 +108,25 @@ void sortInitFini(); void sortCtorsDtors(); + void setDebugData(StringRef data) { + debugDataNonEmpty = true; + debugData = data; + size = data.size(); + } + + bool hasDebugData(void) const { return debugDataNonEmpty; } + private: // Used for implementation of --compress-debug-sections option. std::vector zDebugHeader; llvm::SmallVector compressedData; std::array getFiller(); + + // Used for assign debug data generated by DWARFLinker + // for concrete output section. + StringRef debugData; + bool debugDataNonEmpty = false; }; int getPriority(StringRef s); Index: lld/ELF/OutputSections.cpp =================================================================== --- lld/ELF/OutputSections.cpp +++ lld/ELF/OutputSections.cpp @@ -311,6 +311,11 @@ return; } + if (hasDebugData()) { + memcpy(buf, debugData.data(), debugData.size()); + return; + } + // Write leading padding. std::vector sections = getInputSections(this); std::array filler = getFiller(); Index: lld/ELF/Writer.h =================================================================== --- lld/ELF/Writer.h +++ lld/ELF/Writer.h @@ -25,6 +25,8 @@ void combineEhSections(); template void writeResult(); +using DebugDataBits = SmallString<4096>; + // This describes a program header entry. // Each contains type, access flags and range of output sections that will be // placed in it. Index: lld/ELF/Writer.cpp =================================================================== --- lld/ELF/Writer.cpp +++ lld/ELF/Writer.cpp @@ -11,6 +11,7 @@ #include "ARMErrataFix.h" #include "CallGraphSort.h" #include "Config.h" +#include "LLDDwarfLinker.h" #include "LinkerScript.h" #include "MapFile.h" #include "OutputSections.h" @@ -78,6 +79,7 @@ void writeBuildId(); std::unique_ptr &buffer; + DebugDataBits debugInfoBytes; void addRelIpltSymbols(); void addStartEndSymbols(); @@ -572,6 +574,10 @@ if (errorCount()) return; + // If -gc-debuginfo specified remove unused debug data. + if (config->gcDebugInfo) + linkDebugInfo(debugInfoBytes); + // If -compressed-debug-sections is specified, we need to compress // .debug_* sections. Do it right now because it changes the size of // output sections. Index: lld/test/ELF/gc-debuginfo-cl-errors.s =================================================================== --- /dev/null +++ lld/test/ELF/gc-debuginfo-cl-errors.s @@ -0,0 +1,16 @@ +# REQUIRES: x86 + +# RUN: echo '.global _start; .global main; _start: jmp main; main: ' \ +# RUN: | llvm-mc --filetype=obj --triple=x86_64-unknown-linux - -o %t.o + +# RUN: not ld.lld -r --gc-debuginfo %t.o -o /dev/null 2>&1 \ +# RUN: | FileCheck --check-prefix=RELOC %s + +# RUN: not ld.lld --gc-debuginfo %t.o -o /dev/null 2>&1 \ +# RUN: | FileCheck --check-prefix=GCSEC %s + +## This test checks that proper error message is displayed +## if --gc-debuginfo command line flag is used incorrectly. + +# RELOC: error: -r and --gc-debuginfo may not be used together +# GCSEC: error: --gc-debuginfo may not be used without --gc-sections Index: lld/test/ELF/gc-debuginfo.test =================================================================== --- /dev/null +++ lld/test/ELF/gc-debuginfo.test @@ -0,0 +1,56 @@ +# REQUIRES: x86 + +# RUN: llvm-mc -filetype=obj -triple=x86_64 %s -o %t.o +# RUN: ld.lld %t.o -o %t.default --gc-sections --gc-debuginfo +# RUN: llvm-readelf --sections %t.default | FileCheck %s + +## This is a test for --gc-debuginfo feature. +## It contains .debug_info section which references +## .text.foo section. .text.foo would be deleted by --gc-sections. +## --gc-debuginfo should generate zero sized .debug_info section +## in such case. + +# CHECK: Section Headers: +# CHECK: [Nr] Name Type Address Off Size +# CHECK: [ 2] .debug_info PROGBITS {{[0-9a-fA-F]*}} {{[0-9a-fA-F]*}} 000000 + +.section .text.foo,"axG",@progbits,foo +.globl foo +.Lfunc_begin0: +foo: + ret +.Lfunc_end0: + +.section .debug_abbrev,"",@progbits + .byte 1 # Abbreviation Code + .byte 17 # DW_TAG_compile_unit + .byte 1 # DW_CHILDREN_yes + .byte 17 # DW_AT_low_pc + .byte 1 # DW_FORM_addr + .byte 18 # DW_AT_high_pc + .byte 6 # DW_FORM_data4 + .byte 0 # EOM(1) + .byte 0 # EOM(2) + .byte 2 # Abbreviation Code + .byte 46 # DW_TAG_subprogram + .byte 0 # DW_CHILDREN_no + .byte 3 # DW_AT_name + .byte 8 # DW_FORM_string + .byte 0 # EOM(1) + .byte 0 # EOM(2) + .byte 0 + +.section .debug_info,"",@progbits +.Lcu_begin0: + .long .Lcu_end0 - .Lcu_begin0 - 4 + .short 4 # DWARF version number + .long 0 # Offset Into Abbrev. Section + .byte 4 # Address Size +.Ldie0: + .byte 1 # Abbrev [1] DW_TAG_compile_unit + .quad .Lfunc_begin0 # DW_AT_low_pc + .long .Lfunc_end0 - .Lfunc_begin0 # DW_AT_high_pc + .byte 2 # Abbrev [2] DW_TAG_subprogram + .asciz "foo" # DW_AT_name + .byte 0 +.Lcu_end0: Index: llvm/include/llvm/DWARFLinker/DWARFLinker.h =================================================================== --- llvm/include/llvm/DWARFLinker/DWARFLinker.h +++ llvm/include/llvm/DWARFLinker/DWARFLinker.h @@ -31,21 +31,16 @@ /// Partial address range. Besides an offset, only the /// HighPC is stored. The structure is stored in a map where the LowPC is the /// key. -struct ObjFileAddressRange { +struct AddressHighPC { /// Function HighPC. - uint64_t HighPC; + uint64_t HighPC = 0; /// Offset to apply to the linked address. /// should be 0 for not-linked object file. - int64_t Offset; - - ObjFileAddressRange(uint64_t EndPC, int64_t Offset) - : HighPC(EndPC), Offset(Offset) {} - - ObjFileAddressRange() : HighPC(0), Offset(0) {} + int64_t Offset = 0; }; -/// Map LowPC to ObjFileAddressRange. -using RangesTy = std::map; +/// Map LowPC to AddressHighPC. +using RangesTy = std::map; /// AddressesMap represents information about valid addresses used /// by debug information. Valid addresses are those which points to @@ -55,11 +50,6 @@ public: virtual ~AddressesMap(); - /// Returns true if represented addresses are from linked file. - /// Returns false if represented addresses are from not-linked - /// object file. - virtual bool areRelocationsResolved() const = 0; - /// Checks that there are valid relocations against a .debug_info /// section. Reset current relocation pointer if neccessary. virtual bool hasValidRelocs(bool ResetRelocsPtr = true) = 0; Index: llvm/include/llvm/DWARFLinker/DWARFLinkerCompileUnit.h =================================================================== --- llvm/include/llvm/DWARFLinker/DWARFLinkerCompileUnit.h +++ llvm/include/llvm/DWARFLinker/DWARFLinkerCompileUnit.h @@ -18,12 +18,24 @@ class DeclContext; +template <> struct DenseMapInfo { + static object::SectionedAddress getEmptyKey(); + + static object::SectionedAddress getTombstoneKey(); + + static unsigned getHashValue(const object::SectionedAddress &val); + + static bool isEqual(const object::SectionedAddress &lhs, + const object::SectionedAddress &rhs); +}; + template using HalfOpenIntervalMap = IntervalMap::LeafSize, IntervalMapHalfOpenInfo>; -using FunctionIntervals = HalfOpenIntervalMap; +using FunctionIntervals = + HalfOpenIntervalMap; // FIXME: Delete this structure. struct PatchLocation { @@ -80,6 +92,7 @@ StringRef ClangModuleName) : OrigUnit(OrigUnit), ID(ID), Ranges(RangeAlloc), ClangModuleName(ClangModuleName) { + Info.resize(OrigUnit.getNumDIEs()); auto CUDie = OrigUnit.getUnitDIE(false); @@ -129,7 +142,9 @@ uint64_t getLowPc() const { return LowPc; } uint64_t getHighPc() const { return HighPc; } - bool hasLabelAt(uint64_t Addr) const { return Labels.count(Addr); } + bool hasLabelAt(object::SectionedAddress Addr) const { + return Labels.count(Addr); + } Optional getUnitRangesAttribute() const { return UnitRangeAttribute; @@ -171,11 +186,12 @@ /// Add the low_pc of a label that is relocated by applying /// offset \p PCOffset. - void addLabelLowPc(uint64_t LabelLowPc, int64_t PcOffset); + void addLabelLowPc(object::SectionedAddress LabelLowPc, int64_t PcOffset); /// Add a function range [\p LowPC, \p HighPC) that is relocated by applying /// offset \p PCOffset. - void addFunctionRange(uint64_t LowPC, uint64_t HighPC, int64_t PCOffset); + void addFunctionRange(object::SectionedAddress LowPC, + object::SectionedAddress HighPC, int64_t PCOffset); /// Keep track of a DW_AT_range attribute that we will need to patch up later. void noteRangeAttribute(const DIE &Die, PatchLocation Attr); @@ -284,7 +300,7 @@ FunctionIntervals Ranges; /// The DW_AT_low_pc of each DW_TAG_label. - SmallDenseMap Labels; + SmallDenseMap Labels; /// DW_AT_ranges attributes to patch after we have gathered /// all the unit's function addresses. Index: llvm/include/llvm/DWARFLinker/DWARFStreamer.h =================================================================== --- llvm/include/llvm/DWARFLinker/DWARFStreamer.h +++ llvm/include/llvm/DWARFLinker/DWARFStreamer.h @@ -1,4 +1,4 @@ -//===- tools/dsymutil/DwarfStreamer.h - Dwarf Streamer ----------*- C++ -*-===// +//===- DwarfStreamer.h ------------------------------------------*- C++ -*-===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. @@ -6,37 +6,35 @@ // //===----------------------------------------------------------------------===// -#ifndef LLVM_TOOLS_DSYMUTIL_DWARFSTREAMER_H -#define LLVM_TOOLS_DSYMUTIL_DWARFSTREAMER_H +#ifndef LLVM_DWARFLINKER_DWARFSTREAMER_H +#define LLVM_DWARFLINKER_DWARFSTREAMER_H -#include "DebugMap.h" -#include "LinkUtils.h" #include "llvm/CodeGen/AccelTable.h" #include "llvm/CodeGen/AsmPrinter.h" -#include "llvm/CodeGen/NonRelocatableStringpool.h" #include "llvm/DWARFLinker/DWARFLinker.h" -#include "llvm/DWARFLinker/DWARFLinkerCompileUnit.h" -#include "llvm/DebugInfo/DWARF/DWARFDebugLine.h" -#include "llvm/DebugInfo/DWARF/DWARFDebugRangeList.h" -#include "llvm/MC/MCAsmBackend.h" #include "llvm/MC/MCAsmInfo.h" -#include "llvm/MC/MCCodeEmitter.h" #include "llvm/MC/MCContext.h" -#include "llvm/MC/MCDwarf.h" #include "llvm/MC/MCInstrInfo.h" #include "llvm/MC/MCObjectFileInfo.h" -#include "llvm/MC/MCObjectWriter.h" #include "llvm/MC/MCRegisterInfo.h" -#include "llvm/MC/MCSection.h" -#include "llvm/MC/MCStreamer.h" -#include "llvm/MC/MCSubtargetInfo.h" -#include "llvm/MC/MCSymbol.h" -#include "llvm/MC/MCTargetOptions.h" #include "llvm/Target/TargetMachine.h" -#include "llvm/Target/TargetOptions.h" namespace llvm { -namespace dsymutil { + +enum class OutputFileType { + Object, + Assembly, +}; + +/// User of DwarfStreamer should call initialization code +/// for AsmPrinter: +/// +/// InitializeAllTargetInfos(); +/// InitializeAllTargetMCs(); +/// InitializeAllTargets(); +/// InitializeAllAsmPrinters(); + +class MCCodeEmitter; /// The Dwarf streaming logic. /// @@ -44,13 +42,16 @@ /// information binary representation are handled in this class. class DwarfStreamer : public DwarfEmitter { public: - DwarfStreamer(raw_fd_ostream &OutFile, LinkOptions Options) - : OutFile(OutFile), Options(std::move(Options)) {} + DwarfStreamer(OutputFileType OutFileType, raw_pwrite_stream &OutFile, + std::function Translator, + bool Minimize, messageHandler Error, messageHandler Warning) + : OutFile(OutFile), OutFileType(OutFileType), Translator(Translator), + Minimize(Minimize), ErrorHandler(Error), WarningHandler(Warning) {} bool init(Triple TheTriple); /// Dump the file to the disk. - bool finish(const DebugMap &, SymbolMapTranslator &T); + void finish(); AsmPrinter &getAsmPrinter() const { return *Asm; } @@ -158,6 +159,16 @@ } private: + inline void error(const Twine &Error, StringRef Context = "") { + if (ErrorHandler) + ErrorHandler(Error, Context, nullptr); + } + + inline void warn(const Twine &Warning, StringRef Context = "") { + if (WarningHandler) + WarningHandler(Warning, Context, nullptr); + } + /// \defgroup MCObjects MC layer objects constructed by the streamer /// @{ std::unique_ptr MRI; @@ -174,10 +185,11 @@ std::unique_ptr Asm; /// @} - /// The file we stream the linked Dwarf to. - raw_fd_ostream &OutFile; - - LinkOptions Options; + /// The output file we stream the linked Dwarf to. + raw_pwrite_stream &OutFile; + OutputFileType OutFileType = OutputFileType::Object; + std::function Translator; + bool Minimize = true; uint64_t RangesSectionSize = 0; uint64_t LocSectionSize = 0; @@ -197,9 +209,11 @@ void emitPubSectionForUnit(MCSection *Sec, StringRef Name, const CompileUnit &Unit, const std::vector &Names); + + messageHandler ErrorHandler = nullptr; + messageHandler WarningHandler = nullptr; }; -} // end namespace dsymutil } // end namespace llvm -#endif // LLVM_TOOLS_DSYMUTIL_DWARFSTREAMER_H +#endif // LLVM_DWARFLINKER_DWARFSTREAMER_H Index: llvm/include/llvm/MC/MCAsmInfo.h =================================================================== --- llvm/include/llvm/MC/MCAsmInfo.h +++ llvm/include/llvm/MC/MCAsmInfo.h @@ -637,6 +637,11 @@ return DwarfUsesRelocationsAcrossSections; } + void + setDwarfUseRelocationsAcrossSections(bool UsesRelocationsAcrossSections) { + DwarfUsesRelocationsAcrossSections = UsesRelocationsAcrossSections; + } + bool doDwarfFDESymbolsUseAbsDiff() const { return DwarfFDESymbolsUseAbsDiff; } bool useDwarfRegNumForCFI() const { return DwarfRegNumForCFI; } bool useParensForSymbolVariant() const { return UseParensForSymbolVariant; } Index: llvm/include/llvm/Object/ObjectFile.h =================================================================== --- llvm/include/llvm/Object/ObjectFile.h +++ llvm/include/llvm/Object/ObjectFile.h @@ -152,12 +152,24 @@ std::tie(RHS.SectionIndex, RHS.Address); } +inline bool operator<=(const SectionedAddress &LHS, + const SectionedAddress &RHS) { + return std::tie(LHS.SectionIndex, LHS.Address) <= + std::tie(RHS.SectionIndex, RHS.Address); +} + inline bool operator==(const SectionedAddress &LHS, const SectionedAddress &RHS) { return std::tie(LHS.SectionIndex, LHS.Address) == std::tie(RHS.SectionIndex, RHS.Address); } +inline bool operator!=(const SectionedAddress &LHS, + const SectionedAddress &RHS) { + return std::tie(LHS.SectionIndex, LHS.Address) != + std::tie(RHS.SectionIndex, RHS.Address); +} + raw_ostream &operator<<(raw_ostream &OS, const SectionedAddress &Addr); /// This is a value type class that represents a single symbol in the list of Index: llvm/lib/DWARFLinker/CMakeLists.txt =================================================================== --- llvm/lib/DWARFLinker/CMakeLists.txt +++ llvm/lib/DWARFLinker/CMakeLists.txt @@ -2,6 +2,7 @@ DWARFLinkerCompileUnit.cpp DWARFLinkerDeclContext.cpp DWARFLinker.cpp + DWARFStreamer.cpp DEPENDS intrinsics_gen Index: llvm/lib/DWARFLinker/DWARFLinker.cpp =================================================================== --- llvm/lib/DWARFLinker/DWARFLinker.cpp +++ llvm/lib/DWARFLinker/DWARFLinker.cpp @@ -418,7 +418,7 @@ std::tie(LowPcOffset, LowPcEndOffset) = getAttributeOffsets(Abbrev, *LowPcIdx, Offset, OrigUnit); - auto LowPc = dwarf::toAddress(DIE.find(dwarf::DW_AT_low_pc)); + auto LowPc = dwarf::toSectionedAddress(DIE.find(dwarf::DW_AT_low_pc)); assert(LowPc.hasValue() && "low_pc attribute is not an address."); if (!LowPc || !RelocMgr.hasValidRelocationAt(LowPcOffset, LowPcEndOffset, MyInfo)) @@ -440,7 +440,7 @@ // generation bugs aside, this is really wrong in the case of labels, where // a label marking the end of a function will have a PC == CU's high_pc. if (dwarf::toAddress(OrigUnit.getUnitDIE().find(dwarf::DW_AT_high_pc)) - .getValueOr(UINT64_MAX) <= LowPc) + .getValueOr(UINT64_MAX) <= LowPc->Address) return Flags; Unit.addLabelLowPc(*LowPc, MyInfo.AddrAdjust); return Flags | TF_Keep; @@ -448,7 +448,7 @@ Flags |= TF_Keep; - Optional HighPc = DIE.getHighPC(*LowPc); + Optional HighPc = DIE.getHighPC(LowPc->Address); if (!HighPc) { reportWarning("Function without high_pc. Range will be discarded.\n", File, &DIE); @@ -456,8 +456,9 @@ } // Replace the debug map range with a more accurate one. - Ranges[*LowPc] = ObjFileAddressRange(*HighPc, MyInfo.AddrAdjust); - Unit.addFunctionRange(*LowPc, *HighPc, MyInfo.AddrAdjust); + Ranges[*LowPc] = {*HighPc, MyInfo.AddrAdjust}; + Unit.addFunctionRange(*LowPc, {*HighPc, LowPc->SectionIndex}, + MyInfo.AddrAdjust); return Flags; } @@ -804,7 +805,6 @@ // Switch everything to out of line strings. const char *String = *Val.getAsCString(); auto StringEntry = StringPool.getEntry(String); - // Update attributes info. if (AttrSpec.Attr == dwarf::DW_AT_name) Info.Name = StringEntry; @@ -814,7 +814,6 @@ Die.addValue(DIEAlloc, dwarf::Attribute(AttrSpec.Attr), dwarf::DW_FORM_strp, DIEInteger(StringEntry.getOffset())); - return 4; } @@ -1035,17 +1034,17 @@ : Addr) + Info.PCOffset; else if (Die.getTag() == dwarf::DW_TAG_compile_unit) { - Addr = Unit.getLowPc(); - if (Addr == std::numeric_limits::max()) + const auto &pc = Unit.getLowPc(); + + if (pc != std::numeric_limits::max()) + Addr = pc; + else return 0; } Info.HasLowPc = true; } else if (AttrSpec.Attr == dwarf::DW_AT_high_pc) { if (Die.getTag() == dwarf::DW_TAG_compile_unit) { - if (uint64_t HighPc = Unit.getHighPc()) - Addr = HighPc; - else - return 0; + Addr = Unit.getHighPc(); } else // If we have a high_pc recorded for the input DIE, use // it. Otherwise (when no relocations where applied) just use the @@ -1095,10 +1094,11 @@ if (AttrSpec.Attr == dwarf::DW_AT_high_pc && Die.getTag() == dwarf::DW_TAG_compile_unit) { - if (Unit.getLowPc() == -1ULL) + if (Unit.getLowPc() != std::numeric_limits::max()) + // Dwarf >= 4 high_pc is an size, not an address. + Value = Unit.getHighPc() - Unit.getLowPc(); + else return 0; - // Dwarf >= 4 high_pc is an size, not an address. - Value = Unit.getHighPc() - Unit.getLowPc(); } else if (AttrSpec.Form == dwarf::DW_FORM_sec_offset) Value = *Val.getAsSectionOffset(); else if (AttrSpec.Form == dwarf::DW_FORM_sdata) @@ -1266,7 +1266,6 @@ // Should the DIE appear in the output? if (!Unit.getInfo(Idx).Keep) return nullptr; - uint64_t Offset = InputDIE.getOffset(); assert(!(Die && Info.Clone) && "Can't supply a DIE and a cloned DIE"); if (!Die) { @@ -1307,8 +1306,7 @@ DWARFDataExtractor(DIECopy, Data.isLittleEndian(), Data.getAddressSize()); // Modify the copy with relocated addresses. - if (ObjFile.Addresses->areRelocationsResolved() && - ObjFile.Addresses->applyValidRelocs(DIECopy, Offset, + if (ObjFile.Addresses->applyValidRelocs(DIECopy, Offset, Data.isLittleEndian())) { // If we applied relocations, we store the value of high_pc that was // potentially stored in the input DIE. If high_pc is an address @@ -1366,8 +1364,8 @@ DWARFFormValue Val(AttrSpec.Form); uint64_t AttrSize = Offset; Val.extractValue(Data, &Offset, U.getFormParams(), &U); - AttrSize = Offset - AttrSize; + AttrSize = Offset - AttrSize; OutOffset += cloneAttribute(*Die, InputDIE, File, Unit, StringPool, Val, AttrSpec, AttrSize, AttrInfo, IsLittleEndian); } @@ -1472,12 +1470,12 @@ auto InvalidRange = FunctionRanges.end(), CurrRange = InvalidRange; DWARFUnit &OrigUnit = Unit.getOrigUnit(); auto OrigUnitDie = OrigUnit.getUnitDIE(false); - uint64_t OrigLowPc = - dwarf::toAddress(OrigUnitDie.find(dwarf::DW_AT_low_pc), -1ULL); + uint64_t OrigLowPc = dwarf::toAddress(OrigUnitDie.find(dwarf::DW_AT_low_pc), + std::numeric_limits::max()); // Ranges addresses are based on the unit's low_pc. Compute the // offset we need to apply to adapt to the new unit's low_pc. int64_t UnitPcOffset = 0; - if (OrigLowPc != -1ULL) + if (OrigLowPc != std::numeric_limits::max()) UnitPcOffset = int64_t(OrigLowPc) - Unit.getLowPc(); for (const auto &RangeAttribute : Unit.getRangesAttributes()) { @@ -1493,17 +1491,19 @@ const DWARFDebugRangeList::RangeListEntry &First = Entries.front(); if (CurrRange == InvalidRange || - First.StartAddress + OrigLowPc < CurrRange.start() || - First.StartAddress + OrigLowPc >= CurrRange.stop()) { - CurrRange = FunctionRanges.find(First.StartAddress + OrigLowPc); - if (CurrRange == InvalidRange || - CurrRange.start() > First.StartAddress + OrigLowPc) { + First.SectionIndex != CurrRange.start().SectionIndex || + First.StartAddress + OrigLowPc < CurrRange.start().Address || + First.StartAddress + OrigLowPc >= CurrRange.stop().Address) { + CurrRange = FunctionRanges.find( + {First.StartAddress + OrigLowPc, First.SectionIndex}); + if (CurrRange == InvalidRange || CurrRange == FunctionRanges.end() || + CurrRange.start().SectionIndex != First.SectionIndex || + CurrRange.start().Address > First.StartAddress + OrigLowPc) { reportWarning("no mapping for range.", File); continue; } } } - TheDwarfEmitter->emitRangesEntries(UnitPcOffset, OrigLowPc, CurrRange, Entries, AddressSize); } @@ -1623,37 +1623,43 @@ // it is marked as end_sequence in the input (because in that // case, the relocation offset is accurate and that entry won't // serve as the start of another function). - if (CurrRange == InvalidRange || Row.Address.Address < CurrRange.start() || - Row.Address.Address > CurrRange.stop() || - (Row.Address.Address == CurrRange.stop() && !Row.EndSequence)) { + if (CurrRange == InvalidRange || + Row.Address.SectionIndex != CurrRange.start().SectionIndex || + Row.Address.Address < CurrRange.start().Address || + Row.Address.Address > CurrRange.stop().Address || + (Row.Address.Address == CurrRange.stop().Address && !Row.EndSequence)) { // We just stepped out of a known range. Insert a end_sequence // corresponding to the end of the range. uint64_t StopAddress = CurrRange != InvalidRange - ? CurrRange.stop() + CurrRange.value() - : -1ULL; - CurrRange = FunctionRanges.find(Row.Address.Address); + ? CurrRange.stop().Address + CurrRange.value() + : std::numeric_limits::max(); + CurrRange = FunctionRanges.find(Row.Address); bool CurrRangeValid = - CurrRange != InvalidRange && CurrRange.start() <= Row.Address.Address; + CurrRange != InvalidRange && + CurrRange.start().SectionIndex == Row.Address.SectionIndex && + CurrRange.start().Address <= Row.Address.Address; if (!CurrRangeValid) { CurrRange = InvalidRange; - if (StopAddress != -1ULL) { + if (StopAddress != std::numeric_limits::max()) { // Try harder by looking in the Address ranges map. // There are corner cases where this finds a // valid entry. It's unclear if this is right or wrong, but // for now do as dsymutil. // FIXME: Understand exactly what cases this addresses and // potentially remove it along with the Ranges map. - auto Range = Ranges.lower_bound(Row.Address.Address); + auto Range = Ranges.lower_bound(Row.Address); if (Range != Ranges.begin() && Range != Ranges.end()) --Range; - if (Range != Ranges.end() && Range->first <= Row.Address.Address && + if (Range != Ranges.end() && + Range->first.SectionIndex == Row.Address.SectionIndex && + Range->first.Address <= Row.Address.Address && Range->second.HighPC >= Row.Address.Address) { StopAddress = Row.Address.Address + Range->second.Offset; } } } - if (StopAddress != -1ULL && !Seq.empty()) { + if (StopAddress != std::numeric_limits::max() && !Seq.empty()) { // Insert end sequence row with the computed end address, but // the same line as the previous one. auto NextLine = Seq.back(); @@ -1806,10 +1812,11 @@ // the function entry point, thus we can't just lookup the address // in the debug map. Use the AddressInfo's range map to see if the FDE // describes something that we can relocate. - auto Range = Ranges.upper_bound(Loc); + auto Range = + Ranges.upper_bound({Loc, object::SectionedAddress::UndefSection}); if (Range != Ranges.begin()) --Range; - if (Range == Ranges.end() || Range->first > Loc || + if (Range == Ranges.end() || Range->first.Address > Loc || Range->second.HighPC <= Loc) { // The +4 is to account for the size of the InitialLength field itself. InputOffset = EntryOffset + InitialLength + 4; @@ -2503,7 +2510,6 @@ Pool.async(CloneAll); Pool.wait(); } - return true; } Index: llvm/lib/DWARFLinker/DWARFLinkerCompileUnit.cpp =================================================================== --- llvm/lib/DWARFLinker/DWARFLinkerCompileUnit.cpp +++ llvm/lib/DWARFLinker/DWARFLinkerCompileUnit.cpp @@ -11,6 +11,27 @@ namespace llvm { +object::SectionedAddress DenseMapInfo::getEmptyKey() { + return {object::SectionedAddress::UndefSection, + object::SectionedAddress::UndefSection - 1}; +} + +object::SectionedAddress +DenseMapInfo::getTombstoneKey() { + return {object::SectionedAddress::UndefSection, + object::SectionedAddress::UndefSection - 2}; +} + +unsigned DenseMapInfo::getHashValue( + const object::SectionedAddress &val) { + return llvm::hash_value(std::make_pair(val.Address, val.SectionIndex)); +} + +bool DenseMapInfo::isEqual( + const object::SectionedAddress &lhs, const object::SectionedAddress &rhs) { + return lhs == rhs; +} + /// Check if the DIE at \p Idx is in the scope of a function. static bool inFunctionScope(CompileUnit &U, unsigned Idx) { while (Idx) { @@ -99,19 +120,23 @@ } } -void CompileUnit::addLabelLowPc(uint64_t LabelLowPc, int64_t PcOffset) { +void CompileUnit::addLabelLowPc(object::SectionedAddress LabelLowPc, + int64_t PcOffset) { Labels.insert({LabelLowPc, PcOffset}); } -void CompileUnit::addFunctionRange(uint64_t FuncLowPc, uint64_t FuncHighPc, +void CompileUnit::addFunctionRange(object::SectionedAddress FuncLowPc, + object::SectionedAddress FuncHighPc, int64_t PcOffset) { + assert(FuncLowPc.SectionIndex == FuncHighPc.SectionIndex); + // Don't add empty ranges to the interval map. They are a problem because // the interval map expects half open intervals. This is safe because they // are empty anyway. if (FuncHighPc != FuncLowPc) Ranges.insert(FuncLowPc, FuncHighPc, PcOffset); - this->LowPc = std::min(LowPc, FuncLowPc + PcOffset); - this->HighPc = std::max(HighPc, FuncHighPc + PcOffset); + LowPc = std::min(LowPc, FuncLowPc.Address + PcOffset); + HighPc = std::max(HighPc, FuncHighPc.Address + PcOffset); } void CompileUnit::noteRangeAttribute(const DIE &Die, PatchLocation Attr) { Index: llvm/lib/DWARFLinker/DWARFStreamer.cpp =================================================================== --- llvm/lib/DWARFLinker/DWARFStreamer.cpp +++ llvm/lib/DWARFLinker/DWARFStreamer.cpp @@ -1,4 +1,4 @@ -//===- tools/dsymutil/DwarfStreamer.cpp - Dwarf Streamer ------------------===// +//===- DwarfStreamer.cpp --------------------------------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. @@ -6,25 +6,29 @@ // //===----------------------------------------------------------------------===// -#include "DwarfStreamer.h" -#include "LinkUtils.h" -#include "MachOUtils.h" +#include "llvm/DWARFLinker/DWARFStreamer.h" #include "llvm/ADT/Triple.h" +#include "llvm/CodeGen/NonRelocatableStringpool.h" #include "llvm/DWARFLinker/DWARFLinkerCompileUnit.h" #include "llvm/DebugInfo/DWARF/DWARFContext.h" +#include "llvm/MC/MCAsmBackend.h" +#include "llvm/MC/MCCodeEmitter.h" +#include "llvm/MC/MCDwarf.h" +#include "llvm/MC/MCObjectWriter.h" +#include "llvm/MC/MCSection.h" +#include "llvm/MC/MCStreamer.h" +#include "llvm/MC/MCSubtargetInfo.h" +#include "llvm/MC/MCSymbol.h" #include "llvm/MC/MCTargetOptions.h" #include "llvm/MC/MCTargetOptionsCommandFlags.h" #include "llvm/Support/LEB128.h" #include "llvm/Support/TargetRegistry.h" -#include "llvm/Target/TargetMachine.h" #include "llvm/Target/TargetOptions.h" namespace llvm { static mc::RegisterMCTargetOptionsFlags MOF; -namespace dsymutil { - bool DwarfStreamer::init(Triple TheTriple) { std::string ErrorStr; std::string TripleName; @@ -34,18 +38,20 @@ const Target *TheTarget = TargetRegistry::lookupTarget(TripleName, TheTriple, ErrorStr); if (!TheTarget) - return error(ErrorStr, Context); + return error(ErrorStr, Context), false; TripleName = TheTriple.getTriple(); // Create all the MC Objects. MRI.reset(TheTarget->createMCRegInfo(TripleName)); if (!MRI) - return error(Twine("no register info for target ") + TripleName, Context); + return error(Twine("no register info for target ") + TripleName, Context), + false; MCTargetOptions MCOptions = mc::InitMCTargetOptionsFromFlags(); MAI.reset(TheTarget->createMCAsmInfo(*MRI, TripleName, MCOptions)); if (!MAI) - return error("no asm info for target " + TripleName, Context); + return error("no asm info for target " + TripleName, Context), false; + MAI->setDwarfUseRelocationsAcrossSections(false); MOFI.reset(new MCObjectFileInfo); MC.reset(new MCContext(MAI.get(), MRI.get(), MOFI.get())); @@ -53,21 +59,21 @@ MSTI.reset(TheTarget->createMCSubtargetInfo(TripleName, "", "")); if (!MSTI) - return error("no subtarget info for target " + TripleName, Context); + return error("no subtarget info for target " + TripleName, Context), false; MAB = TheTarget->createMCAsmBackend(*MSTI, *MRI, MCOptions); if (!MAB) - return error("no asm backend for target " + TripleName, Context); + return error("no asm backend for target " + TripleName, Context), false; MII.reset(TheTarget->createMCInstrInfo()); if (!MII) - return error("no instr info info for target " + TripleName, Context); + return error("no instr info info for target " + TripleName, Context), false; MCE = TheTarget->createMCCodeEmitter(*MII, *MRI, *MC); if (!MCE) - return error("no code emitter for target " + TripleName, Context); + return error("no code emitter for target " + TripleName, Context), false; - switch (Options.FileType) { + switch (OutFileType) { case OutputFileType::Assembly: { MIP = TheTarget->createMCInstPrinter(TheTriple, MAI->getAssemblerDialect(), *MAI, *MII, *MRI); @@ -88,17 +94,19 @@ } if (!MS) - return error("no object streamer for target " + TripleName, Context); + return error("no object streamer for target " + TripleName, Context), false; // Finally create the AsmPrinter we'll use to emit the DIEs. TM.reset(TheTarget->createTargetMachine(TripleName, "", "", TargetOptions(), None)); if (!TM) - return error("no target machine for target " + TripleName, Context); + return error("no target machine for target " + TripleName, Context), false; Asm.reset(TheTarget->createAsmPrinter(*TM, std::unique_ptr(MS))); if (!Asm) - return error("no asm printer for target " + TripleName, Context); + return error("no asm printer for target " + TripleName, Context), false; + const_cast(Asm->MAI)->setDwarfUseRelocationsAcrossSections( + false); RangesSectionSize = 0; LocSectionSize = 0; @@ -109,15 +117,7 @@ return true; } -bool DwarfStreamer::finish(const DebugMap &DM, SymbolMapTranslator &T) { - bool Result = true; - if (DM.getTriple().isOSDarwin() && !DM.getBinaryPath().empty() && - Options.FileType == OutputFileType::Object) - Result = MachOUtils::generateDsymCompanion(DM, T, *MS, OutFile); - else - MS->Finish(); - return Result; -} +void DwarfStreamer::finish() { MS->Finish(); } void DwarfStreamer::switchToDebugInfoSection(unsigned DwarfVersion) { MS->SwitchSection(MOFI->getDwarfInfoSection()); @@ -305,8 +305,9 @@ continue; // All range entries should lie in the function range. - if (!(Range.StartAddress + OrigLowPc >= FuncRange.start() && - Range.EndAddress + OrigLowPc <= FuncRange.stop())) + if (!(Range.SectionIndex == FuncRange.start().SectionIndex && + Range.StartAddress + OrigLowPc >= FuncRange.start().Address && + Range.EndAddress + OrigLowPc <= FuncRange.stop().Address)) warn("inconsistent range data.", "emitting debug_ranges"); MS->emitIntValue(Range.StartAddress + PcOffset, AddressSize); MS->emitIntValue(Range.EndAddress + PcOffset, AddressSize); @@ -334,8 +335,8 @@ const auto &FunctionRanges = Unit.getFunctionRanges(); for (auto Range = FunctionRanges.begin(), End = FunctionRanges.end(); Range != End; ++Range) - Ranges.push_back(std::make_pair(Range.start() + Range.value(), - Range.stop() + Range.value())); + Ranges.push_back(std::make_pair(Range.start().Address + Range.value(), + Range.stop().Address + Range.value())); // The object addresses where sorted, but again, the linked // addresses might end up in a different order. @@ -386,7 +387,8 @@ MS->SwitchSection(MC->getObjectFileInfo()->getDwarfRangesSection()); // Offset each range by the right amount. - int64_t PcOffset = -Unit.getLowPc(); + int64_t PcOffset = 0; + PcOffset = -Unit.getLowPc(); // Emit coalesced ranges. for (auto Range = Ranges.begin(), End = Ranges.end(); Range != End; ++Range) { MS->emitIntValue(Range->first + PcOffset, AddressSize); @@ -660,7 +662,7 @@ if (Dir[0] == 0) break; - StringRef Translated = Options.Translator(Dir); + StringRef Translated = Translator(Dir); Asm->OutStreamer->emitBytes(Translated); Asm->emitInt8(0); LineSectionSize += Translated.size() + 1; @@ -672,7 +674,7 @@ if (File[0] == 0) break; - StringRef Translated = Options.Translator(File); + StringRef Translated = Translator(File); Asm->OutStreamer->emitBytes(Translated); Asm->emitInt8(0); LineSectionSize += Translated.size() + 1; @@ -740,7 +742,7 @@ /// Emit .debug_pubnames for \p Unit. void DwarfStreamer::emitPubNamesForUnit(const CompileUnit &Unit) { - if (Options.Minimize) + if (Minimize) return; emitPubSectionForUnit(MC->getObjectFileInfo()->getDwarfPubNamesSection(), "names", Unit, Unit.getPubnames()); @@ -748,7 +750,7 @@ /// Emit .debug_pubtypes for \p Unit. void DwarfStreamer::emitPubTypesForUnit(const CompileUnit &Unit) { - if (Options.Minimize) + if (Minimize) return; emitPubSectionForUnit(MC->getObjectFileInfo()->getDwarfPubTypesSection(), "types", Unit, Unit.getPubtypes()); @@ -776,5 +778,4 @@ FrameSectionSize += FDEBytes.size() + 8 + AddrSize; } -} // namespace dsymutil } // namespace llvm Index: llvm/tools/dsymutil/CMakeLists.txt =================================================================== --- llvm/tools/dsymutil/CMakeLists.txt +++ llvm/tools/dsymutil/CMakeLists.txt @@ -24,7 +24,6 @@ CFBundle.cpp DebugMap.cpp DwarfLinkerForBinary.cpp - DwarfStreamer.cpp MachODebugMapParser.cpp MachOUtils.cpp SymbolMap.cpp Index: llvm/tools/dsymutil/DwarfLinkerForBinary.h =================================================================== --- llvm/tools/dsymutil/DwarfLinkerForBinary.h +++ llvm/tools/dsymutil/DwarfLinkerForBinary.h @@ -11,11 +11,11 @@ #include "BinaryHolder.h" #include "DebugMap.h" -#include "DwarfStreamer.h" #include "LinkUtils.h" #include "llvm/DWARFLinker/DWARFLinker.h" #include "llvm/DWARFLinker/DWARFLinkerCompileUnit.h" #include "llvm/DWARFLinker/DWARFLinkerDeclContext.h" +#include "llvm/DWARFLinker/DWARFStreamer.h" #include "llvm/DebugInfo/DWARF/DWARFContext.h" #include "llvm/Remarks/RemarkFormat.h" #include "llvm/Remarks/RemarkLinker.h" @@ -112,15 +112,14 @@ for (const auto &Entry : DMO.symbols()) { const auto &Mapping = Entry.getValue(); if (Mapping.Size && Mapping.ObjectAddress) - AddressRanges[*Mapping.ObjectAddress] = ObjFileAddressRange( + AddressRanges[{*Mapping.ObjectAddress, + object::SectionedAddress::UndefSection}] = { *Mapping.ObjectAddress + Mapping.Size, - int64_t(Mapping.BinaryAddress) - *Mapping.ObjectAddress); + int64_t(Mapping.BinaryAddress - *Mapping.ObjectAddress)}; } } virtual ~AddressManager() override { clear(); } - virtual bool areRelocationsResolved() const override { return true; } - bool hasValidRelocs(bool ResetRelocsPtr = true) override { if (ResetRelocsPtr) NextValidReloc = 0; Index: llvm/tools/dsymutil/DwarfLinkerForBinary.cpp =================================================================== --- llvm/tools/dsymutil/DwarfLinkerForBinary.cpp +++ llvm/tools/dsymutil/DwarfLinkerForBinary.cpp @@ -9,7 +9,6 @@ #include "DwarfLinkerForBinary.h" #include "BinaryHolder.h" #include "DebugMap.h" -#include "DwarfStreamer.h" #include "MachOUtils.h" #include "dsymutil.h" #include "llvm/ADT/ArrayRef.h" @@ -163,7 +162,14 @@ if (Options.NoOutput) return true; - Streamer = std::make_unique(OutFile, Options); + Streamer = std::make_unique( + Options.FileType, OutFile, Options.Translator, Options.Minimize, + [&](const Twine &Error, StringRef Context, const DWARFDie *) { + error(Error, Context); + }, + [&](const Twine &Warning, StringRef Context, const DWARFDie *) { + warn(Warning, Context); + }); return Streamer->init(TheTriple); } @@ -318,7 +324,7 @@ reportWarning(Warning, Context, DIE); }); GeneralLinker.setErrorHandler( - [&](const Twine &Error, StringRef Context, const DWARFDie *DIE) { + [&](const Twine &Error, StringRef Context, const DWARFDie *) { error(Error, Context); }); GeneralLinker.setObjFileLoader( @@ -442,7 +448,13 @@ return error(toString(std::move(E))); } - return Streamer->finish(Map, Options.Translator); + if (Map.getTriple().isOSDarwin() && !Map.getBinaryPath().empty() && + Options.FileType == OutputFileType::Object) + return MachOUtils::generateDsymCompanion( + Map, Options.Translator, *Streamer->getAsmPrinter().OutStreamer, + OutFile); + else + return Streamer->finish(), true; } static bool isMachOPairedReloc(uint64_t RelocType, uint64_t Arch) { @@ -641,7 +653,6 @@ /// \returns whether any reloc has been applied. bool DwarfLinkerForBinary::AddressManager::applyValidRelocs( MutableArrayRef Data, uint64_t BaseOffset, bool IsLittleEndian) { - assert(areRelocationsResolved()); assert((NextValidReloc == 0 || BaseOffset > ValidRelocs[NextValidReloc - 1].Offset) && "BaseOffset should only be increasing."); Index: llvm/tools/dsymutil/LinkUtils.h =================================================================== --- llvm/tools/dsymutil/LinkUtils.h +++ llvm/tools/dsymutil/LinkUtils.h @@ -16,16 +16,12 @@ #include "llvm/Support/WithColor.h" #include "llvm/DWARFLinker/DWARFLinker.h" +#include "llvm/DWARFLinker/DWARFStreamer.h" #include namespace llvm { namespace dsymutil { -enum class OutputFileType { - Object, - Assembly, -}; - struct LinkOptions { /// Verbosity bool Verbose = false; Index: llvm/unittests/DebugInfo/DWARF/CMakeLists.txt =================================================================== --- llvm/unittests/DebugInfo/DWARF/CMakeLists.txt +++ llvm/unittests/DebugInfo/DWARF/CMakeLists.txt @@ -2,6 +2,7 @@ ${LLVM_TARGETS_TO_BUILD} AsmPrinter DebugInfoDWARF + DWARFLinker MC Object ObjectYAML @@ -20,6 +21,7 @@ DWARFDieTest.cpp DWARFFormValueTest.cpp DWARFLocationExpressionTest.cpp + DWARFLinkerCompileUnit.cpp ) target_link_libraries(DebugInfoDWARFTests PRIVATE LLVMTestingSupport) Index: llvm/unittests/DebugInfo/DWARF/DWARFLinkerCompileUnit.cpp =================================================================== --- /dev/null +++ llvm/unittests/DebugInfo/DWARF/DWARFLinkerCompileUnit.cpp @@ -0,0 +1,165 @@ +//===- llvm/unittest/DebugInfo/DWARFLinkerCompileUnit.cpp -----------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "llvm/DWARFLinker/DWARFLinkerCompileUnit.h" +#include "llvm/BinaryFormat/Dwarf.h" +#include "llvm/DebugInfo/DWARF/DWARFContext.h" +#include "llvm/ObjectYAML/DWARFEmitter.h" +#include "llvm/Testing/Support/Error.h" +#include "gtest/gtest.h" + +using namespace llvm; +using namespace llvm::dwarf; +using object::SectionedAddress; + +namespace { + +TEST(DWARFLinkerCompileUnit, sectionedAddressRanges) { + const char *yamldata = "debug_abbrev:\n" + " - Code: 0x00000001\n" + " Tag: DW_TAG_compile_unit\n" + " Children: DW_CHILDREN_yes\n" + " Attributes:\n" + "debug_info:\n" + " - Length:\n" + " TotalLength: 0\n" + " Version: 4\n" + " AbbrOffset: 0\n" + " AddrSize: 8\n" + " Entries:\n" + " - AbbrCode: 0x00000001\n" + " Values:\n" + " - AbbrCode: 0x00000000\n" + " Values:\n"; + + auto ErrOrSections = DWARFYAML::EmitDebugSections(StringRef(yamldata), true); + ASSERT_TRUE((bool)ErrOrSections); + std::unique_ptr DwarfContext = + DWARFContext::create(*ErrOrSections, 8); + + // Verify the number of compile units is correct. + uint32_t NumCUs = DwarfContext->getNumCompileUnits(); + EXPECT_EQ(NumCUs, 1u); + DWARFCompileUnit *U = cast(DwarfContext->getUnitAtIndex(0)); + + // Check the compile unit DIE is valid. + auto DieDG = U->getUnitDIE(false); + EXPECT_TRUE(DieDG.isValid()); + + // Create DWARFLinker compile unit. + CompileUnit CU(*U, 1, false, ""); + + // Check for adding zero address range from undefined section. + CU.addFunctionRange({0x10, object::SectionedAddress::UndefSection}, + {0x10, object::SectionedAddress::UndefSection}, 0x4000); + EXPECT_TRUE(CU.getFunctionRanges().empty()); + + EXPECT_TRUE(CU.getLowPc() == 0x4010); + EXPECT_TRUE(CU.getHighPc() == 0x4010); + + // Check for adding zero address range from first section. + CU.addFunctionRange({0x10, 1}, {0x10, 1}, 0x4500); + EXPECT_TRUE(CU.getFunctionRanges().empty()); + + EXPECT_TRUE(CU.getLowPc() == 0x4010); + EXPECT_TRUE(CU.getHighPc() == 0x4510); + + // Check for adding valid address range from undefined section. + CU.addFunctionRange({0x10, object::SectionedAddress::UndefSection}, + {0x20, object::SectionedAddress::UndefSection}, 0x4000); + EXPECT_TRUE(!CU.getFunctionRanges().empty()); + EXPECT_TRUE(CU.getFunctionRanges().find( + {0x10, object::SectionedAddress::UndefSection}) != + CU.getFunctionRanges().end()); + EXPECT_TRUE(CU.getFunctionRanges().lookup( + {0x10, object::SectionedAddress::UndefSection}) == 0x4000); + + // Check for adding second valid address range from undefined section. + CU.addFunctionRange({0x20, object::SectionedAddress::UndefSection}, + {0x40, object::SectionedAddress::UndefSection}, 0x5000); + EXPECT_TRUE(!CU.getFunctionRanges().empty()); + EXPECT_TRUE(CU.getFunctionRanges().find( + {0x20, object::SectionedAddress::UndefSection}) != + CU.getFunctionRanges().end()); + EXPECT_TRUE(CU.getFunctionRanges().lookup( + {0x20, object::SectionedAddress::UndefSection}) == 0x5000); + EXPECT_TRUE(CU.getFunctionRanges().lookup( + {0x19, object::SectionedAddress::UndefSection}) == 0x4000); + + // Check for adding third valid address range from undefined section. + CU.addFunctionRange({0x100, object::SectionedAddress::UndefSection}, + {0x200, object::SectionedAddress::UndefSection}, 0x6000); + EXPECT_TRUE(!CU.getFunctionRanges().empty()); + EXPECT_TRUE(CU.getFunctionRanges().find( + {0x100, object::SectionedAddress::UndefSection}) != + CU.getFunctionRanges().end()); + + // Check that function ranges does not contain values which were not put there + // previously. + EXPECT_TRUE(CU.getFunctionRanges().lookup( + {0x5, object::SectionedAddress::UndefSection}, -1LL) == -1LL); + EXPECT_TRUE(CU.getFunctionRanges().lookup( + {0x100, object::SectionedAddress::UndefSection}) == 0x6000); + + // Check compile unit low/high pc values. + EXPECT_TRUE(CU.getLowPc() == 0x4010); + EXPECT_TRUE(CU.getHighPc() == 0x6200); + + // Check for adding valid address range from first section. + CU.addFunctionRange({0x10, 1}, {0x20, 1}, 0x1000); + EXPECT_TRUE(!CU.getFunctionRanges().empty()); + EXPECT_TRUE(CU.getFunctionRanges().find({0x10, 1}) != + CU.getFunctionRanges().end()); + EXPECT_TRUE(CU.getFunctionRanges().lookup({0x10, 1}) == 0x1000); + + // Check for adding second valid address range from first section. + CU.addFunctionRange({0x20, 1}, {0x40, 1}, 0x2000); + EXPECT_TRUE(!CU.getFunctionRanges().empty()); + EXPECT_TRUE(CU.getFunctionRanges().find({0x20, 1}) != + CU.getFunctionRanges().end()); + EXPECT_TRUE(CU.getFunctionRanges().lookup({0x20, 1}) == 0x2000); + EXPECT_TRUE(CU.getFunctionRanges().lookup({0x19, 1}) == 0x1000); + + // Check for adding valid address range from second section. + CU.addFunctionRange({0x10, 2}, {0x20, 2}, 0x3000); + EXPECT_TRUE(!CU.getFunctionRanges().empty()); + EXPECT_TRUE(CU.getFunctionRanges().find({0x10, 2}) != + CU.getFunctionRanges().end()); + EXPECT_TRUE(CU.getFunctionRanges().lookup({0x10, 2}) == 0x3000); + + // Check that function ranges does not contain values which were not put there + // previously. + EXPECT_TRUE(CU.getFunctionRanges().lookup({0x10, 3}, -1LL) == -1LL); + EXPECT_TRUE(CU.getFunctionRanges().lookup({UINT64_MAX, 2}, -1LL) == -1LL); + + EXPECT_TRUE(CU.getLowPc() == 0x1010); + EXPECT_TRUE(CU.getHighPc() == 0x6200); + + // Check for adding valid address range from four section. + CU.addFunctionRange({0x0, 4}, {0x200, 4}, 0x8000); + EXPECT_TRUE(!CU.getFunctionRanges().empty()); + EXPECT_TRUE(CU.getFunctionRanges().find({0x10, 4}) != + CU.getFunctionRanges().end()); + EXPECT_TRUE(CU.getFunctionRanges().lookup({0x10, 4}) == 0x8000); + + EXPECT_TRUE(CU.getLowPc() == 0x1010); + EXPECT_TRUE(CU.getHighPc() == 0x8200); + + // Check adding labels. + CU.addLabelLowPc({0x10, object::SectionedAddress::UndefSection}, 0x4000); + EXPECT_TRUE(CU.hasLabelAt({0x10, object::SectionedAddress::UndefSection})); + EXPECT_TRUE(!CU.hasLabelAt({0x0, object::SectionedAddress::UndefSection})); + + CU.addLabelLowPc({0x10, 1}, 0x4000); + EXPECT_TRUE(CU.hasLabelAt({0x10, 1})); + EXPECT_TRUE(!CU.hasLabelAt({0x0, 1})); + + EXPECT_TRUE(!CU.hasLabelAt({0x10, 2})); +} + +} // end anonymous namespace