Index: include/llvm/DebugInfo/DWARF/DWARFDebugAranges.h =================================================================== --- include/llvm/DebugInfo/DWARF/DWARFDebugAranges.h +++ include/llvm/DebugInfo/DWARF/DWARFDebugAranges.h @@ -60,11 +60,11 @@ struct RangeEndpoint { uint64_t Address; + uint64_t LowPC; uint32_t CUOffset; - bool IsRangeStart; - RangeEndpoint(uint64_t Address, uint32_t CUOffset, bool IsRangeStart) - : Address(Address), CUOffset(CUOffset), IsRangeStart(IsRangeStart) {} + RangeEndpoint(uint64_t Address, uint64_t LowPC, uint32_t CUOffset) + : Address(Address), LowPC(LowPC), CUOffset(CUOffset) {} bool operator<(const RangeEndpoint &Other) const { return Address < Other.Address; Index: lib/DebugInfo/DWARF/DWARFDebugAranges.cpp =================================================================== --- lib/DebugInfo/DWARF/DWARFDebugAranges.cpp +++ lib/DebugInfo/DWARF/DWARFDebugAranges.cpp @@ -75,38 +75,33 @@ uint64_t HighPC) { if (LowPC >= HighPC) return; - Endpoints.emplace_back(LowPC, CUOffset, true); - Endpoints.emplace_back(HighPC, CUOffset, false); + Endpoints.emplace_back(LowPC, LowPC, CUOffset); + Endpoints.emplace_back(HighPC, LowPC, CUOffset); } void DWARFDebugAranges::construct() { - std::multiset ValidCUs; // Maintain the set of CUs describing - // a current address range. + // Maintain the set of CUs that cross the sweep line. + std::multiset> LiveCUs; llvm::sort(Endpoints); uint64_t PrevAddress = -1ULL; for (const auto &E : Endpoints) { - if (PrevAddress < E.Address && !ValidCUs.empty()) { - // If the address range between two endpoints is described by some - // CU, first try to extend the last range in Aranges. If we can't - // do it, start a new range. - if (!Aranges.empty() && Aranges.back().HighPC() == PrevAddress && - ValidCUs.find(Aranges.back().CUOffset) != ValidCUs.end()) { - Aranges.back().setHighPC(E.Address); - } else { - Aranges.emplace_back(PrevAddress, E.Address, *ValidCUs.begin()); - } - } - // Update the set of valid CUs. - if (E.IsRangeStart) { - ValidCUs.insert(E.CUOffset); - } else { - auto CUPos = ValidCUs.find(E.CUOffset); - assert(CUPos != ValidCUs.end()); - ValidCUs.erase(CUPos); - } + // Add a new range. If there are multiple live CUs, pick the one with the + // largest LowPC. + // + // In a --gc-sections link, a .debug_info associated with a discarded section + // may not be discarded and has zero DW_AT_low_pc. If its DW_AT_high_pc is + // sufficiently large, it may overlap with a good one. Use this heuristic to + // let the good one prevail. + if (PrevAddress < E.Address && !LiveCUs.empty()) + Aranges.emplace_back(PrevAddress, E.Address, LiveCUs.rbegin()->second); + // For a LowPC event, add a CU; for a HighPC event, remove a CU. + if (E.Address == E.LowPC) + LiveCUs.emplace(E.LowPC, E.CUOffset); + else + LiveCUs.erase(LiveCUs.find({E.LowPC, E.CUOffset})); PrevAddress = E.Address; } - assert(ValidCUs.empty()); + assert(LiveCUs.empty()); // Endpoints are not needed now. Endpoints.clear(); Index: test/tools/llvm-symbolizer/gc-sections-zero-low-pc.s =================================================================== --- /dev/null +++ test/tools/llvm-symbolizer/gc-sections-zero-low-pc.s @@ -0,0 +1,120 @@ +# RUN: llvm-mc -filetype=obj -triple x86_64-pc-linux %s -o %t +# RUN: llvm-symbolizer --obj=%t 0x1fffff 0x200000 0x200003 0x200004 | FileCheck %s + +# 0.c spans [0x0, 0x200010). Zero DW_AT_low_pc due to discarded relocation. +# 1.c spans [0x200000, 0x200004) +# 0.c's code is garbage collected but its debug info is left in %t. +# We want [0x200000, 0x200004) to symbolize to 1.c +# Ideally other addresses don't symbolize to 0.c since it is garbage collected, +# but we currently fail to do so. + +# CHECK: 0.c:1:0 +# CHECK: 1.c:1:0 +# CHECK: 1.c:1:0 +# CHECK: 0.c:1:0 + +.section .debug_abbrev,"",@progbits + .byte 1 # Abbreviation Code + .byte 17 # DW_TAG_compile_unit + .byte 1 # DW_children_yes + .byte 16 # DW_AT_stmt_list + .byte 23 # DW_FROM_sec_offset + .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 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 0 # EOM(3) + +.section .debug_info,"",@progbits +.macro define_cu i, low_pc, high_pc +.Lcu_begin\i: + .long .Lcu_end\i - .Lcu_begin\i - 4 # Length of Unit + .short 5 # DWARF version number + .byte 1 # DWARF Unit Type + .byte 8 # Address Size (in bytes) + .long .debug_abbrev # Offset Into Abbrev. + .byte 1 # Abbrev [1] + .long .Lline_table_start\i # DW_AT_stmt_list + .quad \low_pc # DW_AT_low_pc + .long \high_pc # DW_AT_high_pc + .byte 2 # Abbrev [2] DW_TAG_subprogram + .if \i + .asciz "1.c" # DW_AT_name + .else + .asciz "0.c" # DW_AT_name + .endif + .quad \low_pc # DW_AT_low_pc + .long \high_pc # DW_AT_high_pc + .byte 0 +.Lcu_end\i: +.endm + +define_cu 0, 0x0, 0x200010 +define_cu 1, 0x200000, 0x4 + +.section .debug_line,"",@progbits + +# Define line tables for 0.c and 1.c. They differ in the name and addresses. +.irp i, 0, 1 +.Lline_table_start\i: + .long .Lline_table_end\i - .Lline_table_start\i - 4 # Length of Unit + .short 5 # DWARF version number + .byte 8 # Address Size + .byte 0 # Segment Selector Size + .long .Lline_table_header_end\i - .Lline_table_params\i # Length of Prologue +.Lline_table_params\i: + .byte 1 # Minimum Instruction Length + .byte 1 # Maximum Operations per Instruction + .byte 1 # Default is_stmt + .byte -5 # Line Base + .byte 14 # Line Range + .byte 13 # Opcode Base + .byte 0, 1, 1, 1, 1, 0, 0, 0, 1, 0, 0, 1 # Standard Opcode Lengths + # Directory table format + .byte 1 # One element per directory entry + .byte 1 # DW_LNCT_path + .byte 0x08 # DW_FORM_string + # Directory table entries + .byte 1 # 1 directory + .asciz "/tmp" + # File table format + .byte 2 # 2 elements per file entry + .byte 1 # DW_LNCT_path + .byte 0x08 # DW_FORM_string + .byte 2 # DW_LNCT_directory_index + .byte 0x0b # DW_FORM_data1 + # File table entries + .byte 1 # 1 file + .if \i + .asciz "1.c" + .else + .asciz "0.c" + .endif + .byte 0 +.Lline_table_header_end\i: +.if \i + .byte 0,9,2 # DW_LNE_set_address + .quad 0x200000 + .byte 1 # DW_LNS_copy + .byte 75 # Addr += 4, Line += 1 + .byte 0,1,1 # DW_LNE_end_sequence +.else + .byte 1 # DW_LNS_copy + .byte 2,0x90,0x80,0x80,0x1 # DW_LNS_advance_pc 0x200010 + .byte 0,1,1 # DW_LNE_end_sequence +.endif +.Lline_table_end\i: +.endr