diff --git a/llvm/include/llvm/DebugInfo/LogicalView/Core/LVObject.h b/llvm/include/llvm/DebugInfo/LogicalView/Core/LVObject.h index ec02120e69b73..42a22039c870f 100644 --- a/llvm/include/llvm/DebugInfo/LogicalView/Core/LVObject.h +++ b/llvm/include/llvm/DebugInfo/LogicalView/Core/LVObject.h @@ -84,6 +84,14 @@ using LVTypes = SmallVector; using LVOffsets = SmallVector; +// The following DWARF documents detail the 'tombstone' concept: +// https://dwarfstd.org/issues/231013.1.html +// https://dwarfstd.org/issues/200609.1.html +// +// The value of the largest representable address offset (for example, +// 0xffffffff when the size of an address is 32 bits). +// +// -1 (0xffffffff) => Valid tombstone const LVAddress MaxAddress = std::numeric_limits::max(); enum class LVBinaryType { NONE, ELF, COFF }; diff --git a/llvm/include/llvm/DebugInfo/LogicalView/Core/LVReader.h b/llvm/include/llvm/DebugInfo/LogicalView/Core/LVReader.h index 371bffb2ed163..20261418791d3 100644 --- a/llvm/include/llvm/DebugInfo/LogicalView/Core/LVReader.h +++ b/llvm/include/llvm/DebugInfo/LogicalView/Core/LVReader.h @@ -156,6 +156,9 @@ class LLVM_ABI LVReader { LVAddress LowerAddress, LVAddress UpperAddress); LVRange *getSectionRanges(LVSectionIndex SectionIndex); + // The value is updated for each Compile Unit that is processed. + std::optional TombstoneAddress; + // Record Compilation Unit entry. void addCompileUnitOffset(LVOffset Offset, LVScopeCompileUnit *CompileUnit) { CompileUnits.emplace(Offset, CompileUnit); @@ -282,6 +285,12 @@ class LLVM_ABI LVReader { return CompileUnit->getCPUType(); } + void setTombstoneAddress(LVAddress Address) { TombstoneAddress = Address; } + LVAddress getTombstoneAddress() const { + assert(TombstoneAddress && "Unset tombstone value"); + return TombstoneAddress.value(); + } + // Access to the scopes root. LVScopeRoot *getScopesRoot() const { return Root; } diff --git a/llvm/include/llvm/DebugInfo/LogicalView/Readers/LVCodeViewReader.h b/llvm/include/llvm/DebugInfo/LogicalView/Readers/LVCodeViewReader.h index 4dd7c967ddc17..612c6fc68f09c 100644 --- a/llvm/include/llvm/DebugInfo/LogicalView/Readers/LVCodeViewReader.h +++ b/llvm/include/llvm/DebugInfo/LogicalView/Readers/LVCodeViewReader.h @@ -193,11 +193,17 @@ class LVCodeViewReader final : public LVBinaryReader { llvm::object::COFFObjectFile &Obj, ScopedPrinter &W, StringRef ExePath) : LVBinaryReader(Filename, FileFormatName, W, LVBinaryType::COFF), - Input(&Obj), ExePath(ExePath), LogicalVisitor(this, W, Input) {} + Input(&Obj), ExePath(ExePath), LogicalVisitor(this, W, Input) { + // CodeView does not have the concept of 'tombstone' address. + setTombstoneAddress(MaxAddress); + } LVCodeViewReader(StringRef Filename, StringRef FileFormatName, llvm::pdb::PDBFile &Pdb, ScopedPrinter &W, StringRef ExePath) : LVBinaryReader(Filename, FileFormatName, W, LVBinaryType::COFF), - Input(&Pdb), ExePath(ExePath), LogicalVisitor(this, W, Input) {} + Input(&Pdb), ExePath(ExePath), LogicalVisitor(this, W, Input) { + // CodeView does not have the concept of 'tombstone' address. + setTombstoneAddress(MaxAddress); + } LVCodeViewReader(const LVCodeViewReader &) = delete; LVCodeViewReader &operator=(const LVCodeViewReader &) = delete; ~LVCodeViewReader() = default; diff --git a/llvm/lib/DebugInfo/LogicalView/Readers/LVDWARFReader.cpp b/llvm/lib/DebugInfo/LogicalView/Readers/LVDWARFReader.cpp index 7ff96ae90a7fd..95b7007d119bc 100644 --- a/llvm/lib/DebugInfo/LogicalView/Readers/LVDWARFReader.cpp +++ b/llvm/lib/DebugInfo/LogicalView/Readers/LVDWARFReader.cpp @@ -214,10 +214,11 @@ void LVDWARFReader::processOneAttribute(const DWARFDie &Die, } } if (FoundLowPC) { - if (CurrentLowPC == MaxAddress) + if (CurrentLowPC == getTombstoneAddress()) CurrentElement->setIsDiscarded(); - // Consider the case of WebAssembly. - CurrentLowPC += WasmCodeSectionOffset; + else + // Consider the case of WebAssembly. + CurrentLowPC += WasmCodeSectionOffset; if (CurrentElement->isCompileUnit()) setCUBaseAddress(CurrentLowPC); } @@ -271,7 +272,8 @@ void LVDWARFReader::processOneAttribute(const DWARFDie &Die, DWARFAddressRangesVector Ranges = RangesOrError.get(); for (DWARFAddressRange &Range : Ranges) { // This seems to be a tombstone for empty ranges. - if (Range.LowPC == Range.HighPC) + if ((Range.LowPC == Range.HighPC) || + (Range.LowPC = getTombstoneAddress())) continue; // Store the real upper limit for the address range. if (UpdateHighAddress && Range.HighPC > 0) @@ -629,6 +631,11 @@ Error LVDWARFReader::createScopes() { : DwarfContext->dwo_compile_units(); for (const std::unique_ptr &CU : CompileUnits) { + // Take into account the address byte size for a correct 'tombstone' + // value identification. + setTombstoneAddress( + dwarf::computeTombstoneAddress(CU->getAddressByteSize())); + // Deduction of index used for the line records. // // For the following test case: test.cpp diff --git a/llvm/test/tools/llvm-debuginfo-analyzer/WebAssembly/wasm-crash-tombstone.s b/llvm/test/tools/llvm-debuginfo-analyzer/WebAssembly/wasm-crash-tombstone.s new file mode 100644 index 0000000000000..7d833b841af99 --- /dev/null +++ b/llvm/test/tools/llvm-debuginfo-analyzer/WebAssembly/wasm-crash-tombstone.s @@ -0,0 +1,124 @@ +# REQUIRES: x86-registered-target + +# llvm-debuginfo-analyzer crashes on dead code +# https://github.com/llvm/llvm-project/issues/136772 + +# For the attached reproducer: +# llvm-dwarfdump out/lzma-lzmadec.wasm --all +# +# shows: +# +# 0x000002b3: DW_TAG_subprogram +# DW_AT_low_pc (dead code) +# DW_AT_high_pc (0x00000362) +# DW_AT_frame_base (DW_OP_WASM_location 0x0 0x6, DW_OP_stack_value) + +# llvm-debuginfo-analyzer out/lzma-lzmadec.wasm --print=instructions +# +# crashes and shows a stack dump: +# +# PLEASE submit a bug report to https://github.com/llvm/llvm-project/issues/ +# and include the crash backtrace. +# Stack dump: +# 0. Program arguments: llvm-debuginfo-analyzer out/lzma-lzmadec.wasm --print=instructions + +# The test case was produced by the following steps: +# +# // test-clang.cpp +# void foo() { +# } +# +# 1) clang --target=wasm32 -S -g test-clang.cpp +# -o Inputs/wasm-crash-tombstone.s +# 2) Manually changing the DW_AT_low_pc for the DW_TAG_subprogram: +# .Lfunc_begin0 to 0xffffffff to mark the function as dead code: +# +# .int8 2 # Abbrev [2] 0x26:0x1b DW_TAG_subprogram +# .int32 .Lfunc_begin0 # DW_AT_low_pc <--------- +# .int32 .Lfunc_end0-.Lfunc_begin0 # DW_AT_high_pc + +# .int8 2 # Abbrev [2] 0x26:0x1b DW_TAG_subprogram +# .int32 0xffffffff # DW_AT_low_pc <--------- +# .int32 .Lfunc_end0-.Lfunc_begin0 # DW_AT_high_pc + +# RUN: llvm-mc -arch=wasm32 -filetype=obj \ +# RUN: %p/wasm-crash-tombstone.s \ +# RUN: -o %t.wasm-crash-tombstone.wasm + +# RUN: llvm-debuginfo-analyzer --select-elements=Discarded \ +# RUN: --print=elements \ +# RUN: %t.wasm-crash-tombstone.wasm 2>&1 | \ +# RUN: FileCheck --strict-whitespace -check-prefix=ONE %s + +# ONE: Logical View: +# ONE-NEXT: {File} '{{.*}}wasm-crash-tombstone.wasm' +# ONE-EMPTY: +# ONE-NEXT: {CompileUnit} 'test-clang.cpp' +# ONE-NEXT: {Function} not_inlined 'foo' -> 'void' + +# RUN: llvm-dwarfdump --debug-info %t.wasm-crash-tombstone.wasm | \ +# RUN: FileCheck %s --check-prefix=TWO + +# TWO: DW_TAG_subprogram +# TWO-NEXT: DW_AT_low_pc (dead code) +# TWO-NEXT: DW_AT_high_pc +# TWO-NEXT: DW_AT_name ("foo") + + .text + .file "test-clang.cpp" + .functype _Z3foov () -> () + .section .text._Z3foov,"",@ +_Z3foov: # @_Z3foov +.Lfunc_begin0: + .functype _Z3foov () -> () + return + end_function +.Lfunc_end0: + # -- End function + .section .debug_abbrev,"",@ + .int8 1 # Abbreviation Code + .int8 17 # DW_TAG_compile_unit + .int8 1 # DW_CHILDREN_yes + .int8 3 # DW_AT_name + .int8 14 # DW_FORM_strp + .int8 17 # DW_AT_low_pc + .int8 1 # DW_FORM_addr + .int8 18 # DW_AT_high_pc + .int8 6 # DW_FORM_data4 + .int8 0 # EOM(1) + .int8 0 # EOM(2) + .int8 2 # Abbreviation Code + .int8 46 # DW_TAG_subprogram + .int8 0 # DW_CHILDREN_no + .int8 17 # DW_AT_low_pc + .int8 1 # DW_FORM_addr + .int8 18 # DW_AT_high_pc + .int8 6 # DW_FORM_data4 + .int8 3 # DW_AT_name + .int8 14 # DW_FORM_strp + .int8 0 # EOM(1) + .int8 0 # EOM(2) + .int8 0 # EOM(3) + .section .debug_info,"",@ +.Lcu_begin0: + .int32 .Ldebug_info_end0-.Ldebug_info_start0 # Length of Unit +.Ldebug_info_start0: + .int16 4 # DWARF version number + .int32 .debug_abbrev0 # Offset Into Abbrev. Section + .int8 4 # Address Size (in bytes) + .int8 1 # Abbrev [1] 0xb:0x37 DW_TAG_compile_unit + .int32 .Linfo_string1 # DW_AT_name + .int32 .Lfunc_begin0 # DW_AT_low_pc + .int32 .Lfunc_end0-.Lfunc_begin0 # DW_AT_high_pc + .int8 2 # Abbrev [2] 0x26:0x1b DW_TAG_subprogram + .int32 0xffffffff # DW_AT_low_pc + .int32 .Lfunc_end0-.Lfunc_begin0 # DW_AT_high_pc + .int32 .Linfo_string4 # DW_AT_name + .int8 0 # End Of Children Mark +.Ldebug_info_end0: + .section .debug_str,"S",@ +.Linfo_string1: + .asciz "test-clang.cpp" # string offset=176 +.Linfo_string4: + .asciz "foo" # string offset=241 + .ident "clang version 19.0.0"