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-lto.cpp =================================================================== --- /dev/null +++ debuginfo-tests/dwarflinker/Inputs/gc-debuginfo-main-lto.cpp @@ -0,0 +1,16 @@ +#include + +int mod1_f1(int); +int mod2_f1(const char*); + +int main(void) { + int v1 = mod1_f1(3); + int v2 = mod2_f1("hello"); + + for (int idx = 0; idx < 3; idx++) { + printf("%d", v1+idx); + printf("%d", v2+idx); + } + + return 0; +} Index: debuginfo-tests/dwarflinker/Inputs/gc-debuginfo-main.c =================================================================== --- /dev/null +++ debuginfo-tests/dwarflinker/Inputs/gc-debuginfo-main.c @@ -0,0 +1,18 @@ +#include + +int mod1_f1(int); +int mod2_f1(int); + +__attribute__((optnone)) void func(int p) { + printf("\n%d", p); +} + +__attribute__((optnone)) int main(void) { + int v1 = mod1_f1(3); + int v2 = mod2_f1(5); + + for (int idx = 0; idx < 3; idx++) + func(v1 * idx + v2); + + return 0; +} Index: debuginfo-tests/dwarflinker/Inputs/gc-debuginfo-mod1-lto.cpp =================================================================== --- /dev/null +++ debuginfo-tests/dwarflinker/Inputs/gc-debuginfo-mod1-lto.cpp @@ -0,0 +1,6 @@ + +__attribute__((optnone)) int mod1_f1(int p1) { return p1 + 10; } + +__attribute__((optnone)) int mod1_not_used(int p1) { + return p1+100; +} Index: debuginfo-tests/dwarflinker/Inputs/gc-debuginfo-mod1.c =================================================================== --- /dev/null +++ debuginfo-tests/dwarflinker/Inputs/gc-debuginfo-mod1.c @@ -0,0 +1,9 @@ + +int mod1_f1(int p1) { return p1 + 10; } + +int mod1_not_used(int p1) { + int r = 0; + for (int i = 0; i < p1; i++) + r++; + return r; +} Index: debuginfo-tests/dwarflinker/Inputs/gc-debuginfo-mod2-lto.cpp =================================================================== --- /dev/null +++ debuginfo-tests/dwarflinker/Inputs/gc-debuginfo-mod2-lto.cpp @@ -0,0 +1,23 @@ + +class S { + public: +__attribute__((optnone)) S(const char* str) { + l = 0; + while (!*str) + l++; + } + +__attribute__((optnone)) int Length() { + return l; + } + + int l = 0; +}; + +__attribute__((optnone)) int mod2_f1(const char* n) { + return S(n).Length(); +} + +__attribute__((optnone)) int mod2_not_used ( int p1) { + return p1+100; +} Index: debuginfo-tests/dwarflinker/Inputs/gc-debuginfo-mod2.c =================================================================== --- /dev/null +++ debuginfo-tests/dwarflinker/Inputs/gc-debuginfo-mod2.c @@ -0,0 +1,10 @@ + +int mod2_f1(int p1) { return p1 + 10; } + +int mod2_not_used(int p1) { + int r = 0; + for (int i = 0; i < p1; i++) + r++; + + return r; +} Index: debuginfo-tests/dwarflinker/gc-debuginfo-lto.cpp =================================================================== --- /dev/null +++ debuginfo-tests/dwarflinker/gc-debuginfo-lto.cpp @@ -0,0 +1,96 @@ +// RUN: %clang++ %target_itanium_abi_host_triple -gdwarf-4 -ffunction-sections \ +// RUN: -O -flto %p/Inputs/gc-debuginfo-main-lto.cpp -c -o %t.o +// RUN: %clang++ %target_itanium_abi_host_triple -gdwarf-4 -ffunction-sections \ +// RUN: -O -flto %p/Inputs/gc-debuginfo-mod1-lto.cpp -c -o %t1.o +// RUN: %clang++ %target_itanium_abi_host_triple -gdwarf-4 -ffunction-sections \ +// RUN: -O -flto %p/Inputs/gc-debuginfo-mod2-lto.cpp -c -o %t2.o +// RUN: %clang++ %target_itanium_abi_host_triple -gdwarf-4 -O -fuse-ld=lld \ +// RUN: -flto -Wl,--gc-sections,--gc-debuginfo %t.o %t1.o %t2.o -o %t3.out +// RUN: llvm-dwarfdump -a %t3.out | FileCheck %s +// RUN: llvm-dwarfdump --verify %t3.out | FileCheck %s --check-prefix=VERIFY + +// UNSUPPORTED: system-darwin + +// This test checks that --gc-debuginfo removes unused debug info +// correctly in case -flto. After -flto there would be single .debug_info +// section containing all compile units. The test checks that dwarflinker +// correctly processes several compilation units placed into single +// .debug info section. The test checks that debug info related +// to mod1_not_used() and mod2_not_used() functions +// is deleted from resulting .debug* tables. + +CHECK: .debug_info contents: +CHECK: 0x0000000b: DW_TAG_compile_unit +CHECK: DW_AT_name ("{{.*}}gc-debuginfo-main-lto.cpp") +CHECK: DW_AT_stmt_list (0x[[MAIN_STMT_LIST:[0-9A-Fa-f]+]]) +CHECK: DW_AT_low_pc (0x{{0*}}[[MAIN_LPC:[0-9A-Fa-f]+]]) +CHECK: DW_AT_high_pc (0x{{0*}}[[MAIN_HPC:[0-9A-Fa-f]+]]) +CHECK: DW_TAG_subprogram +CHECK: DW_AT_low_pc (0x{{0*}}[[MAIN_LPC]]) +CHECK: DW_AT_high_pc (0x{{0*}}[[MAIN_HPC]]) +CHECK: DW_AT_name ("main") +CHECK: DW_TAG_variable +CHECK: DW_AT_location +CHECK: DW_AT_name ("v1") +CHECK: DW_TAG_variable +CHECK: DW_AT_location +CHECK: DW_AT_name ("v2") +CHECK: DW_TAG_variable +CHECK: DW_AT_location +CHECK: DW_AT_name ("idx") +CHECK: DW_TAG_GNU_call_site +CHECK: DW_AT_low_pc (0x{{0*}}[[MOD1_VAL:[0-9A-Fa-f]+]]) +CHECK: DW_TAG_GNU_call_site +CHECK: DW_AT_low_pc (0x{{0*}}[[MOD2_VAL:[0-9A-Fa-f]+]]) +CHECK: Compile Unit +CHECK_NOT: 0x0000000b +CHECK: DW_TAG_compile_unit +CHECK: DW_AT_name ("{{.*}}gc-debuginfo-mod2-lto.cpp") +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{{0*}}[[MOD2_VAL_ADDR:[0-9A-Fa-f]+]], 0x{{[0-9a-zA-Z]*}} +CHECK-NEXT: 0x{{0*}}[[MOD2_LENGTH_ADDR:[0-9A-Fa-f]+]], 0x{{[0-9a-zA-Z]*}} +CHECK-NEXT: 0x{{0*}}[[MOD2_CONSTR_ADDR:[0-9A-Fa-f]+]], 0x{{[0-9a-zA-Z]*}} +CHECK: DW_TAG_class_type +CHECK: DW_AT_name ("S") +CHECK: DW_TAG_member +CHECK: DW_AT_name ("l") +CHECK: DW_TAG_subprogram +CHECK: DW_AT_name ("S") +CHECK: DW_TAG_subprogram +CHECK: DW_AT_name ("Length") +CHECK: DW_TAG_subprogram +CHECK: DW_AT_low_pc (0x{{0*}}[[MOD2_VAL_ADDR]]) +CHECK: DW_AT_name ("mod2_f1") +CHECK: DW_TAG_subprogram +CHECK: DW_AT_low_pc (0x{{0*}}[[MOD2_LENGTH_ADDR]]) +CHECK: DW_TAG_subprogram +CHECK: DW_AT_low_pc (0x{{0*}}[[MOD2_CONSTR_ADDR]]) +CHECK: Compile Unit +CHECK_NOT: 0x0000000b +CHECK: DW_TAG_compile_unit +CHECK: DW_AT_name ("{{.*}}gc-debuginfo-mod1-lto.cpp") +CHECK: DW_AT_stmt_list (0x{{0*}}[[MOD1_STMT_LIST:[0-9A-Fa-f]+]]) +CHECK: DW_AT_low_pc (0x{{0*}}[[MOD1_LPC:[0-9A-Fa-f]+]]) +CHECK: DW_AT_high_pc (0x{{0*}}[[MOD1_HPC:[0-9A-Fa-f]+]]) +CHECK: .debug_loc contents +CHECK: .debug_line contents: +CHECK: debug_line[0x[[MAIN_STMT_LIST]]] +CHECK: Address +CHECK: 0x{{0*}}[[MAIN_LPC]] +CHECK: debug_line[0x{{0*}}[[MOD2_STMT_LIST]]] +CHECK: Address +CHECK: 0x{{0*}}[[MOD2_VAL_ADDR]] +CHECK: 0x{{0*}}[[MOD2_LENGTH_ADDR]] +CHECK: 0x{{0*}}[[MOD2_CONSTR_ADDR]] +CHECK: debug_line[0x{{0*}}[[MOD1_STMT_LIST]]] +CHECK: Address +CHECK: 0x{{0*}}[[MOD1_LPC]] + +CHECK: .debug_str contents +CHECK-NOT: mod1_not_used +CHECK-NOT: mod2_not_used +CHECK: .debug_ranges contents: + +VERIFY: Verifying +VERIFY: No errors. Index: debuginfo-tests/dwarflinker/gc-debuginfo.c =================================================================== --- /dev/null +++ debuginfo-tests/dwarflinker/gc-debuginfo.c @@ -0,0 +1,96 @@ +// 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 +// RUN: llvm-dwarfdump --verify %t3.out | FileCheck %s --check-prefix=VERIFY + +// UNSUPPORTED: system-darwin + +// This test checks that --gc-debuginfo removes unused debug info. +// It checks that debug info related to mod1_not_used() and +// mod2_not_used() functions is deleted from resulting .debug* +// tables. .debug_loc table should be empty since mod1_f1() +// and mod2_f1() 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: mod1_not_used +CHECK-NOT: mod2_not_used +CHECK: DW_AT_name ("main") +CHECK: DW_TAG_subprogram +CHECK-NOT: mod1_not_used +CHECK-NOT: mod2_not_used +CHECK: DW_AT_name ("mod1_f1") +CHECK: DW_TAG_subprogram +CHECK-NOT: mod1_not_used +CHECK-NOT: mod2_not_used +CHECK: DW_AT_name ("mod2_f1") +CHECK: Compile Unit +CHECK: DW_TAG_compile_unit +CHECK-NOT: mod1_not_used +CHECK-NOT: mod2_not_used +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: mod1_not_used +CHECK-NOT: mod2_not_used +CHECK: DW_AT_name ("mod1_f1") +CHECK: Compile Unit +CHECK: DW_TAG_compile_unit +CHECK-NOT: mod1_not_used +CHECK-NOT: mod2_not_used +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: mod1_not_used +CHECK-NOT: mod2_not_used +CHECK: DW_AT_name ("mod2_f1") +CHECK-NOT: mod1_not_used +CHECK-NOT: mod2_not_used +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: mod1_not_used +CHECK-NOT: mod2_not_used + +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]] + +VERIFY: Verifying +VERIFY: No errors. Index: debuginfo-tests/llgdb-tests/dwarflinker/Inputs/gc-debuginfo-mod1-lto.cpp =================================================================== --- /dev/null +++ debuginfo-tests/llgdb-tests/dwarflinker/Inputs/gc-debuginfo-mod1-lto.cpp @@ -0,0 +1,9 @@ +struct S { + int a1; + int a2; +}; +int mod1_func(void) { + volatile S var; + var.a1 = 100; var.a2 = 200; + return var.a1 + var.a2; +} 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 mod1_f1(int p1) { + int r = 0; + for (int i = 0; i < p1; i++) + r++; + + return r; +} + +int mod1_not_used(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 mod2_f1(int p1) { + int r = 0; + for (int i = 0; i < p1; i++) + r += p1; + + return r; +} + +int mod2_not_used(int p1) { return p1 + 10; } Index: debuginfo-tests/llgdb-tests/dwarflinker/gc-debuginfo-lto.cpp =================================================================== --- /dev/null +++ debuginfo-tests/llgdb-tests/dwarflinker/gc-debuginfo-lto.cpp @@ -0,0 +1,40 @@ +// RUN: %clang++ %target_itanium_abi_host_triple -gdwarf-4 -ffunction-sections \ +// RUN: -O2 %s -c -o %t.o +// RUN: %clang++ %target_itanium_abi_host_triple -gdwarf-4 -ffunction-sections \ +// RUN: -O2 %p/Inputs/gc-debuginfo-mod1-lto.cpp -c -o %t1.o +// RUN: %clang++ %target_itanium_abi_host_triple -gdwarf-4 -O2 \ +// RUN: -fuse-ld=lld -Wl,--gc-sections,--gc-debuginfo %t.o %t1.o -o %t3.out +// RUN: %test_debuginfo %s %t3.out +// RUN: %clang++ %target_itanium_abi_host_triple -gdwarf-4 -ffunction-sections \ +// RUN: -O2 -flto %s -c -o %t.o +// RUN: %clang++ %target_itanium_abi_host_triple -gdwarf-4 -ffunction-sections \ +// RUN: -O2 -flto %p/Inputs/gc-debuginfo-mod1-lto.cpp -c -o %t1.o +// RUN: %clang++ %target_itanium_abi_host_triple -gdwarf-4 -O2 -flto \ +// RUN: -fuse-ld=lld -Wl,--gc-sections,--gc-debuginfo %t.o %t1.o -o %t3.out +// RUN: %test_debuginfo %s %t3.out +// RUN: %clang++ %target_itanium_abi_host_triple -gdwarf-4 -ffunction-sections \ +// RUN: -O2 -flto=thin %s -c -o %t.o +// RUN: %clang++ %target_itanium_abi_host_triple -gdwarf-4 -ffunction-sections \ +// RUN: -O2 -flto=thin %p/Inputs/gc-debuginfo-mod1-lto.cpp -c -o %t1.o +// RUN: %clang++ %target_itanium_abi_host_triple -gdwarf-4 -O2 -flto=thin \ +// RUN: -fuse-ld=lld -Wl,--gc-sections,--gc-debuginfo %t.o %t1.o -o %t3.out +// RUN: %test_debuginfo %s %t3.out + +// XFAIL:* + +// UNSUPPORTED: system-darwin + +// This test checks that debug info for mod1_func() function is properly +// preserved. Specifically, it should print proper values for var variable. +// Check that non-lto, full-lto, thin-lto modes are work correctly. + +int mod1_func(void); +int main(void) { + return mod1_func(); +} + +// DEBUGGER: b gc-debuginfo-mod1-lto.cpp:8 +// DEBUGGER: r +// DEBUGGER: p var +// CHECK: return var.a1 + var.a2 +// CHECK: a1 = 100, a2 = 200 Index: debuginfo-tests/llgdb-tests/dwarflinker/gc-debuginfo.c =================================================================== --- /dev/null +++ debuginfo-tests/llgdb-tests/dwarflinker/gc-debuginfo.c @@ -0,0 +1,50 @@ +// 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. + +#include + +int mod1_f1(); +int mod2_f1(); + +__attribute__((optnone)) void func(int p) { + printf("\n %d", p); +} + +int main(void) { + func(mod1_f1(1)); + + func(mod2_f1(2)); + + return 0; +} + +// DEBUGGER: break main +// CHECK: gc-debuginfo.c +// DEBUGGER: r +// DEBUGGER: s +// CHECK: mod1_f1 (p1=1) +// DEBUGGER: s +// DEBUGGER: p r +// CHECK: = 1 +// DEBUGGER: n +// DEBUGGER: s +// CHECK: mod2_f1 (p1=2) +// DEBUGGER: s +// DEBUGGER: p r +// 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 @@ -142,6 +142,7 @@ bool checkSections; bool compressDebugSections; bool cref; + bool debugNames; bool defineCommon; bool demangle = true; bool dependentLibraries; @@ -155,6 +156,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( + const LLDDWARFSection &sec, ArrayRef rels, 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,9 +26,14 @@ namespace lld { namespace elf { template LLDDwarfObj::LLDDwarfObj(ObjFile *obj) { + StringMap sectionNumber; for (InputSectionBase *sec : obj->getSections()) { - if (!sec) + if (!sec) { + sectionNames.push_back({"", true}); continue; + } + sectionNames.push_back({sec->name, true}); + sectionNumber[sec->name]++; if (LLDDWARFSection *m = StringSwitch(sec->name) @@ -40,6 +45,7 @@ .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; @@ -53,6 +59,9 @@ else if (sec->name == ".debug_line_str") lineStrSection = toStringRef(sec->data()); } + for (SectionName &sec : sectionNames) + if (!sec.Name.empty() && sectionNumber.lookup(sec.Name) > 1) + sec.IsNameUnique = false; } namespace { @@ -118,6 +127,75 @@ 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->template relas(), startPos, endPos, + relHandler); + else + enumerateRelocations(sec, sec.sec->template rels(), startPos, endPos, + relHandler); +} + +// This function calls specified handler for all relocations pointing +// to live section and located from "startPos" till "endPos" from section +// relocations(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( + const LLDDWARFSection &sec, ArrayRef rels, uint64_t startPos, + uint64_t endPos, + llvm::function_ref + relHandler) const { + // Check if there are no more relocations. + if (sec.nextReloc >= rels.size()) + return; + + // Skip relocations, started from nextReloc until relocPos < startPos. + uint64_t relocPos = rels[sec.nextReloc].r_offset; + while (relocPos < startPos && sec.nextReloc < rels.size() - 1) + relocPos = rels[++sec.nextReloc].r_offset; + + // Enumerate relocations in range [startPos, endPos). + const ObjFile *file = sec.sec->getFile(); + while (relocPos >= startPos && relocPos < endPos) { + const RelTy &rel = rels[sec.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()) { + // Create architecture neutral relocation representation. + // Later we will fully calculate destination value, thus + // we do not need any expression. Set R_ABS then. + Relocation reloc = {R_ABS, rel.getType(config->isMips64EL), rel.r_offset, + getAddend(rel), &file->getRelocTargetSym(rel)}; + + relHandler(reloc, referencedSection); + } + + if (++sec.nextReloc >= rels.size()) + break; + + relocPos = rels[sec.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 @@ -286,6 +286,7 @@ static void initLLVM() { InitializeAllTargets(); InitializeAllTargetMCs(); + InitializeAllTargetInfos(); InitializeAllAsmPrinters(); InitializeAllAsmParsers(); } @@ -330,6 +331,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) @@ -340,6 +343,12 @@ 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->gcDebugInfo && config->debugNames) + error("--debug-names may not be used without --gc-debuginfo"); + if (config->executeOnly) { if (config->emachine != EM_AARCH64) error("-execute-only is only supported on AArch64 targets"); @@ -881,6 +890,8 @@ !args.hasArg(OPT_relocatable)); config->optimizeBBJumps = args.hasFlag(OPT_optimize_bb_jumps, OPT_no_optimize_bb_jumps, false); + config->debugNames = + args.hasFlag(OPT_debug_names, OPT_no_debug_names, false); config->demangle = args.hasFlag(OPT_demangle, OPT_no_demangle, true); config->dependentLibraries = args.hasFlag(OPT_dependent_libraries, OPT_no_dependent_libraries, true); config->disableVerify = args.hasArg(OPT_disable_verify); @@ -907,6 +918,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); @@ -1970,11 +1983,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); } @@ -390,8 +392,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,289 @@ +//===- 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 InputSectionBase -> SectionIndex. + DenseMap secToIndex; + uint64_t curIdx = 0; + for (InputSectionBase *sec : objFile->getSections()) { + if (sec) + secToIndex[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() || !secToIndex.count(sec)) + continue; + + hasValidRelocations = true; + + SymbolAddresses addr = getSymbolAddresses(symbol, sec); + addressRanges[{addr.objectAddress, secToIndex[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 ret = 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 (!ret) { + if (!sec->isDebug) { + info.AddrAdjust = + getSymbolAddresses(rel.sym, sec).inputSectionAddress; + info.InDebugMap = true; + ret = true; + } + } + }); + }); + + return ret; + } + + bool applyValidRelocs(MutableArrayRef data, uint64_t baseOffset, + bool) override { + bool ret = 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); + assert(rel.offset - baseOffset < data.size()); + + // Apply relocation. + getTarget()->relocate( + (uint8_t *)(data.data() + rel.offset - baseOffset), rel, + Addr.inputSectionAddress + Addr.objectAddress + rel.addend); + ret = true; + }); + }); + + return ret; + } + + 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. + // It would reserve half of original debug info size. + // That is average ratio of debug info size decreasing. + uint64_t estimatedSize = 0; + for (const InputSectionBase *sec : inputSections) + if (sec->isDebug && sec->isLive()) + estimatedSize += sec->getSize() * 0.5f; + outDebugInfoBytes.reserve(estimatedSize); + + auto ReportWarn = [&](const Twine &message, StringRef context, + const DWARFDie *) { + if (context.empty()) + warn(message); + else + warn(context + ": " + message); + }; + + // Create output streamer. + raw_svector_ostream stream(outDebugInfoBytes); + DwarfStreamer outStreamer(OutputFileType::Object, stream, + [](StringRef input) { return input; }, false, + ReportWarn, ReportWarn); + 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(ReportWarn); + debugInfoLinker.setWarningHandler(ReportWarn); + + std::vector> objectsForLinking(objectFiles.size()); + std::vector> addresssMapForLinking( + objectFiles.size()); + + // 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(), std::vector()); + + debugInfoLinker.addObjectFile(*objectsForLinking[i]); + } + } + + // Link debug info. + debugInfoLinker.link(); + outStreamer.finish(); + + if (outDebugInfoBytes.empty()) + return; + + // Create sections map: Section Name -> Section Bits. + // outDebugInfoBytes contains elf file with all + // generated debug sections. We create a map here to assign + // section bits to output section later. + 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; + else + sectionsMap[*name] = ""; + } else { + warn("Empty section name inside generated debug info"); + return; + } + } + } else { + warn("Generated debug info is broken"); + return; + } + + // Set linked debug info to corresponding output section. + for (OutputSection *sec : outputSections) + if (isDebugSection(sec->name)) + sec->setDebugData(sectionsMap.lookup(sec->name)); +} + +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 @@ -106,6 +106,10 @@ "Output cross reference table", "Do not output cross reference table">; +defm debug_names: B<"debug-names", + "Enable .debug_names table generation", + "Disable .debug_names table generation (default)">; + defm define_common: B<"define-common", "Assign space to common symbols", "Do not assign space to common symbols">; @@ -187,6 +191,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,23 @@ void sortInitFini(); void sortCtorsDtors(); + void setDebugData(StringRef data) { + debugData = data; + size = data.size(); + } + + bool hasDebugData(void) const { return debugData.hasValue(); } + 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. + llvm::Optional debugData; }; int getPriority(StringRef s); Index: lld/ELF/OutputSections.cpp =================================================================== --- lld/ELF/OutputSections.cpp +++ lld/ELF/OutputSections.cpp @@ -330,6 +330,12 @@ return; } + // If there are data generated by dwarflinker just write it down. + if (hasDebugData()) { + memcpy(buf, debugData->data(), debugData->size()); + return; + } + // Write leading padding. std::vector sections = getInputSections(this); std::array filler = getFiller(); Index: lld/ELF/Relocations.cpp =================================================================== --- lld/ELF/Relocations.cpp +++ lld/ELF/Relocations.cpp @@ -1492,7 +1492,6 @@ for (auto i = rels.begin(), end = rels.end(); i != end;) scanReloc(sec, getOffset, i, end); - // Sort relocations by offset for more efficient searching for // R_RISCV_PCREL_HI20 and R_PPC64_ADDR64. if (config->emachine == EM_RISCV || Index: lld/ELF/SyntheticSections.h =================================================================== --- lld/ELF/SyntheticSections.h +++ lld/ELF/SyntheticSections.h @@ -783,6 +783,17 @@ size_t size; }; +/// DebugSection is used to keep data generated by DWARFLinker. +class DebugSection final : public SyntheticSection { +public: + DebugSection(StringRef name); + template static DebugSection *create(StringRef name); + + void writeTo(uint8_t *) override {} + size_t getSize() const override { return 0; } + bool isNeeded() const override { return true; } +}; + // --eh-frame-hdr option tells linker to construct a header for all the // .eh_frame sections. This header is placed to a section named .eh_frame_hdr // and also to a PT_GNU_EH_FRAME segment. Index: lld/ELF/SyntheticSections.cpp =================================================================== --- lld/ELF/SyntheticSections.cpp +++ lld/ELF/SyntheticSections.cpp @@ -2912,6 +2912,13 @@ bool GdbIndexSection::isNeeded() const { return !chunks.empty(); } +DebugSection::DebugSection(StringRef name) + : SyntheticSection(0, SHT_PROGBITS, 1, name) {} + +template DebugSection *DebugSection::create(StringRef name) { + return make(name); +} + EhFrameHeader::EhFrameHeader() : SyntheticSection(SHF_ALLOC, SHT_PROGBITS, 4, ".eh_frame_hdr") {} @@ -3717,6 +3724,11 @@ std::vector partitions; Partition *mainPart; +template DebugSection *DebugSection::create(StringRef); +template DebugSection *DebugSection::create(StringRef); +template DebugSection *DebugSection::create(StringRef); +template DebugSection *DebugSection::create(StringRef); + template GdbIndexSection *GdbIndexSection::create(); template GdbIndexSection *GdbIndexSection::create(); template GdbIndexSection *GdbIndexSection::create(); 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" @@ -81,6 +82,7 @@ void writeBuildId(); std::unique_ptr &buffer; + DebugDataBits debugInfoBytes; void addRelIpltSymbols(); void addStartEndSymbols(); @@ -509,6 +511,9 @@ if (config->gdbIndex) add(GdbIndexSection::create()); + if (config->debugNames) + add(DebugSection::create(".debug_names")); + // We always need to add rel[a].plt to output if it has entries. // Even for static linking it can contain R_[*]_IRELATIVE relocations. in.relaPlt = make>( @@ -575,6 +580,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. @@ -1037,7 +1046,8 @@ // Note that relocations for non-alloc sections are directly // processed by InputSection::relocateNonAlloc. for (InputSectionBase *isec : inputSections) - if (isec->isLive() && isa(isec) && (isec->flags & SHF_ALLOC)) + if (isec->isLive() && isa(isec) && + ((isec->flags & SHF_ALLOC))) fn(*isec); for (Partition &part : partitions) { for (EhInputSection *es : part.ehFrame->sections) Index: lld/docs/ld.lld.1 =================================================================== --- lld/docs/ld.lld.1 +++ lld/docs/ld.lld.1 @@ -140,6 +140,8 @@ .Ql --defsym=foo=bar or .Ql --defsym=foo=bar+0x100 . +.It Fl -debug-names +Enable .debug_names table generation. .It Fl -demangle Demangle symbol names. .It Fl -disable-new-dtags @@ -207,6 +209,8 @@ .Cm default is a synonym for .Cm elf . +.It Fl -gc-debuginfo +Enable removing obsolete debug info. .It Fl -gc-sections Enable garbage collection of unused sections. .It Fl -gdb-index @@ -282,6 +286,8 @@ for shared libraries. .It Fl -no-color-diagnostics Do not use colors in diagnostics. +.It Fl -no-debug-names +Disable .debug_names table generation. .It Fl -no-define-common Do not assign space to common symbols. .It Fl -no-demangle @@ -290,6 +296,8 @@ Inhibit output of an .Li .interp section. +.It Fl -no-gc-debuginfo +Disable removing obsolete debug info. .It Fl -no-gc-sections Disable garbage collection of unused sections. .It Fl -no-gnu-unique 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/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/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 @@ -49,6 +49,7 @@ MAI.reset(TheTarget->createMCAsmInfo(*MRI, TripleName, MCOptions)); if (!MAI) 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())); @@ -102,6 +103,8 @@ Asm.reset(TheTarget->createAsmPrinter(*TM, std::unique_ptr(MS))); if (!Asm) return error("no asm printer for target " + TripleName, Context), false; + const_cast(Asm->MAI)->setDwarfUseRelocationsAcrossSections( + false); RangesSectionSize = 0; LocSectionSize = 0; @@ -300,8 +303,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); @@ -329,8 +333,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. @@ -381,7 +385,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); Index: llvm/tools/dsymutil/DwarfLinkerForBinary.h =================================================================== --- llvm/tools/dsymutil/DwarfLinkerForBinary.h +++ llvm/tools/dsymutil/DwarfLinkerForBinary.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 @@ -658,7 +658,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/unittests/DebugInfo/DWARF/CMakeLists.txt =================================================================== --- llvm/unittests/DebugInfo/DWARF/CMakeLists.txt +++ llvm/unittests/DebugInfo/DWARF/CMakeLists.txt @@ -3,6 +3,7 @@ AsmPrinter BinaryFormat DebugInfoDWARF + DWARFLinker MC Object ObjectYAML @@ -21,6 +22,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