Skip to content

[llvm-debuginfo-analyzer] Fix crash with WebAssembly dead code #141616

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
8 changes: 8 additions & 0 deletions llvm/include/llvm/DebugInfo/LogicalView/Core/LVObject.h
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,14 @@ using LVTypes = SmallVector<LVType *, 8>;

using LVOffsets = SmallVector<LVOffset, 8>;

// 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<uint64_t>::max();

enum class LVBinaryType { NONE, ELF, COFF };
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,9 @@ class LVDWARFReader final : public LVBinaryReader {
bool FoundLowPC = false;
bool FoundHighPC = false;

// The value is updated for each Compile Unit that is processed.
std::optional<LVAddress> TombstoneAddress;

// Cross references (Elements).
using LVElementSet = std::unordered_set<LVElement *>;
struct LVElementEntry {
Expand Down Expand Up @@ -127,6 +130,12 @@ class LVDWARFReader final : public LVBinaryReader {
LVAddress getCUHighAddress() const { return CUHighAddress; }
void setCUHighAddress(LVAddress Address) { CUHighAddress = Address; }

void setTombstoneAddress(LVAddress Address) { TombstoneAddress = Address; }
LVAddress getTombstoneAddress() const {
assert(TombstoneAddress && "Unset tombstone value");
return TombstoneAddress.value();
}

const LVSymbols &GetSymbolsWithLocations() const {
return SymbolsWithLocations;
}
Expand Down
15 changes: 11 additions & 4 deletions llvm/lib/DebugInfo/LogicalView/Readers/LVDWARFReader.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -629,6 +631,11 @@ Error LVDWARFReader::createScopes() {
: DwarfContext->dwo_compile_units();
for (const std::unique_ptr<DWARFUnit> &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
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
# REQUIRES: x86-registered-target

# Test that DWARF tombstones are correctly detected/respected in wasm
# 32 bit object files.

# 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-32bit-tombstone.s
#
# 2) Creating a single function, tombstoning it in the assembly, by
# 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-32bit-tombstone.s \
# RUN: -o %t.wasm-32bit-tombstone.wasm

# RUN: llvm-debuginfo-analyzer --select-elements=Discarded \
# RUN: --print=elements \
# RUN: %t.wasm-32bit-tombstone.wasm 2>&1 | \
# RUN: FileCheck --strict-whitespace -check-prefix=ONE %s

# ONE: Logical View:
# ONE-NEXT: {File} '{{.*}}wasm-32bit-tombstone.wasm'
# ONE-EMPTY:
# ONE-NEXT: {CompileUnit} 'test-clang.cpp'
# ONE-NEXT: {Function} not_inlined 'foo' -> 'void'

# RUN: llvm-dwarfdump --debug-info %t.wasm-32bit-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"
Loading