Skip to content

symbolize: fix legacy DWARF 5 reader for clang -gline-tables-only binaries - #270

Open
akasakariko wants to merge 1 commit into
google:masterfrom
akasakariko:fix-issue-255-dwarf5-str-offsets
Open

akasakariko wants to merge 1 commit into
google:masterfrom
akasakariko:fix-issue-255-dwarf5-str-offsets

Conversation

@akasakariko

@akasakariko akasakariko commented Oct 9, 2026 •

Copy link
Copy Markdown

symbolize: fix legacy DWARF 5 reader for clang -gline-tables-only binaries

Fixes #255.

Problem

create_gcov aborts on any DWARF 5 binary built by clang (for example LLVM 19 with -gdwarf-5 -gline-tables-only -fdebug-info-for-profiling):

F... dwarf2reader.cc:754] Check failed: str_offsets_buffer_ != NULL

As noted in the issue, Google3Addr2line::Prepare() never loads .debug_str_offsets, so the first DW_FORM_strx* attribute fails the CHECK. GCC uses strp/line_strp forms, so it does not hit this.

Loading the section stops the crash, but the output is still wrong on the same binaries, because several DWARF 5 paths were never exercised before. This PR fixes them too:

  • String tables in line programs: addr2line_inlinestack.cc and functioninfo.cc passed .debug_line_str to LineInfo as both .debug_str and .debug_str_offsets, and dereferenced end() when .debug_line_str was absent.
  • Unit base attributes: clang emits DW_AT_str_offsets_base/DW_AT_addr_base after DW_AT_name (strx) and DW_AT_low_pc (addrx). Every unit after the first therefore used the first unit's tables. The result was a wrong unit base address and wrong DW_RLE_offset_pair inline ranges. Both bases are now read from the unit DIE first (CompilationUnit::ReadUnitBases), as LLVM does.
  • DW_LNCT_MD5: clang emits it by default, and it made the v5 line header "malformed", so there was no line info. Content types that are not needed are now skipped (LineInfo::SkipForm).
  • 0-based DWARF 5 file and directory indices: these were stored 1-based. clang's file 0 / DW_AT_call_file 0 were dropped (which then caused a NULL callsite_filename() dereference in GetInlineStack), and GCC DWARF 5 file names were off by one.
  • Unit-level DW_AT_ranges with DW_FORM_rnglistx: C++, -ffunction-sections and ThinLTO builds hit a CHECK here.
  • SkipAttribute: it did not handle DW_FORM_line_strp.
  • Optional sections: strx attributes are now skipped instead of CHECK-failing when .debug_str/.debug_str_offsets is missing or the index is out of bounds.

I also checked the other DWARF 5 sections. .debug_addr, .debug_rnglists and .debug_line_str were already loaded. .debug_loclists is not used by the symbolizer, and DW_FORM_loclistx is already skipped.

Testing

New tests in addr2line_test.cc:

  • Dwarf5ClangLineTablesOnlyBinary: new testdata/dwarf5_line_tables_only.bin (18 KB, 3 units). It was built by clang 19 from the committed testdata/dwarf5_line_tables_only_*.c sources; the command is in the test comment. The test checks full inline stacks at 5 addresses against values from llvm-symbolizer --inlining. Without this patch the test aborts with the Crashes with str_offsets_buffer_ = NULL #255 CHECK.
  • Dwarf5StrxUsesLaterStrOffsetsBase, Dwarf5StrxWithoutStrOffsetsSectionIsSkipped, Dwarf5StrxOutOfRangeIndexIsSkipped: synthetic units. Without this patch they fail, abort and fail respectively.

Results:

  • Builds: GCOV tool with clang 19.1.7 (Ninja, shared) and with GCC 15.2.0 (make, as in CI). In both, ctest passes 4/4 and addr2line_test passes 9/9. The tests also pass under ASan+UBSan.
  • Inline stacks from the legacy symbolizer:
    • Match llvm-symbolizer exactly for every instruction of the new test binary, built with clang 19 and with clang 21.
    • Same as DWARF 4 for clang 19 DWARF 5 builds of Lua 5.4.7 (plain and -gdwarf64), the C++ googletest samples, and a GCC 15 DWARF 5 build of Lua.
  • No regressions: output for DWARF 4 binaries is unchanged.
  • Existing testdata/dwarf2_dwarf5.bin (GCC): wrong file names drop from 194,301 frames to 0.
  • create_gcov run end to end (text profile) on a clang DWARF 5 binary: it used to abort and now writes a correct profile.

Not tested: the ENABLE_TOOL=LLVM configuration (llvm-project submodule not fetched locally) and real perf.data input.

…aries

create_gcov aborted with "Check failed: str_offsets_buffer_ != NULL" on
DWARF 5 binaries built by clang (e.g. LLVM 19 with -gline-tables-only
-fdebug-info-for-profiling), because Google3Addr2line::Prepare() never
loaded .debug_str_offsets, so every DW_FORM_strx* attribute hit the CHECK.

Loading the section exposes further gaps in the legacy DWARF 5 path, all
of which are hit by the same clang binaries:

- The line-table readers were handed .debug_line_str in place of
  .debug_str and .debug_str_offsets (and dereferenced end() when
  .debug_line_str was absent).
- DW_AT_str_offsets_base and DW_AT_addr_base follow the unit DIE
  attributes that use them (DW_AT_name as strx, DW_AT_low_pc as addrx),
  so every unit after the first resolved them against the first unit's
  contributions. This produced a wrong unit base address and therefore
  wrong DW_RLE_offset_pair inline ranges. Read both bases from the unit
  DIE before processing it, as LLVM does.
- DWARF 5 line tables with DW_LNCT_MD5 (emitted by clang by default)
  were rejected as malformed, leaving no line information. Skip content
  types that are not needed, as the DWARF 5 spec intends.
- DWARF 5 directory and file indices are 0-based, but were stored 1-based.
  File 0 / DW_AT_call_file 0 (clang) were dropped, which made
  GetInlineStack() dereference a null callsite file name, and GCC DWARF 5
  file names were off by one.
- A unit-level DW_AT_ranges using DW_FORM_rnglistx (clang with
  -ffunction-sections, C++, LTO) hit a CHECK.
- SkipAttribute() did not know DW_FORM_line_strp.

Also skip, instead of CHECK-failing on, strx attributes when .debug_str or
.debug_str_offsets is missing or the index is outside the section.

With this change the legacy symbolizer's inline stacks for clang 19 and
GCC 15 DWARF 5 binaries match those for the same code compiled with
DWARF 4, and match llvm-symbolizer on the new test binary.

Fixes google#255.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Crashes with str_offsets_buffer_ = NULL

1 participant