Skip to content
Merged
Show file tree
Hide file tree
Changes from 18 commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
2378de1
Add a mechanism to GC when building .dwps.
khuey May 30, 2026
b38275f
Address review comments.
khuey Jun 5, 2026
addd10b
Fix DW_AT_sibling handling to take into consideration the level of th…
khuey Jun 6, 2026
b2ef1a6
Don't try to keep anything in the ranges to mark that a DIE is tombst…
khuey Jun 6, 2026
7c15c71
Replace Session::DataHolder with hardcoded Rc.
khuey Jun 11, 2026
ae04db2
Set has_type_units when a .debug_types.dwo section is present.
khuey Jun 11, 2026
267b544
Replace leftover debug log with descriptive message
khuey Jun 11, 2026
552b647
When no DIEs are dead, gc_debug_info returns rewritten == None but
khuey Jun 11, 2026
d535f34
Return an error instead of panicking in case we receive malformed inp…
khuey Jun 11, 2026
f339353
Guard against API misuse by returning an error instead of panicking i…
khuey Jun 11, 2026
4e234e4
Factor out code for writing the list sections.
khuey Jun 11, 2026
bf714a9
Ensure maybe_gc() early returns don't drop saved sections.
khuey Jun 11, 2026
917a4bc
Simplify AddrResolver since Ok(None) is never used.
khuey Jun 15, 2026
72fde63
Patch CU-relative references in loclists when offset_entry_count == 0.
khuey Jun 15, 2026
cf29918
Support GCing DWPs.
khuey Jun 19, 2026
7518af0
Handle cases where the same input object appears multiple times.
khuey Jun 22, 2026
8e53d46
Handle non-zero DW_AT_GNU_ranges_base.
khuey Jun 22, 2026
471593a
cleanups
khuey Jun 22, 2026
6451fb4
Add an off-by-default gc feature to disable the new gc code.
khuey Jun 24, 2026
381977f
Use released gimli.
khuey Jun 25, 2026
83201c6
Rustfmt
khuey Jun 25, 2026
4a84373
Try bumping tests to LLVM 16.
khuey Jun 25, 2026
882d18e
Update FileChecks for new LLVM version (wider addresses mostly).
khuey Jun 25, 2026
dbddac7
Try LLVM 17.
khuey Jun 25, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
/target
/tests/.lit_test_times.txt
*~
38 changes: 31 additions & 7 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 3 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,6 @@ members = [
"thorin",
"thorin-bin"
]

[patch.crates-io]
gimli = { git = "https://github.com/gimli-rs/gimli", rev = "1bb1457480938ea412c7b2a99e07534e7846d1df" }
28 changes: 28 additions & 0 deletions tests/gc-all-dead.test
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
# Test that GC does not remove an entirely dead CU. The skeleton CU in the
# executable references this CU by DwoId; dropping it would leave a dangling
# reference that we cannot fix without modifying the executable.

# Assemble the .dwo (split debug info).
# RUN: llvm-mc --triple=x86_64-unknown-linux --filetype=obj %p/inputs/gc-all-dead-dwo.s -o %t.dwo

# Generate the exec assembly with the .dwo path substituted in, then assemble it.
# RUN: sed 's|DWO_PATH|%t.dwo|' %p/inputs/gc-all-dead-exec.s | llvm-mc --triple=x86_64-unknown-linux --filetype=obj -o %t.o

# Baseline: both subprograms survive when not garbage-collecting.
# RUN: thorin -e %t.o -o %t-nogc.dwp
# RUN: llvm-dwarfdump -v %t-nogc.dwp | FileCheck --check-prefixes=CHECK,NOGC %s

# With garbage collection the dead subprograms are removed but the CU survives.
# RUN: thorin --gc -e %t.o -o %t-gc.dwp
# RUN: llvm-dwarfdump -v %t-gc.dwp | FileCheck --check-prefixes=CHECK,GC %s

# The compile unit must always be present.
# CHECK: .debug_info.dwo contents:
# CHECK: DW_TAG_compile_unit

# Without GC both dead subprograms are present.
# NOGC: DW_TAG_subprogram
# NOGC: DW_TAG_subprogram

# After GC both subprograms are removed but the CU itself survives.
# GC-NOT: DW_TAG_subprogram
67 changes: 67 additions & 0 deletions tests/gc-compact-gnu-str-index.test
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
# Test that GC compacts the .debug_str_offsets.dwo table when string attributes
# use DW_FORM_GNU_str_index (DWARF4 split-DWARF extension), by removing entries
# for strings only referenced by dead DIEs, and that surviving DIEs' string
# attributes are correctly remapped to the new indices.
#
# The .dwo has 4 string offset entries (indices 0-3). The dead subprogram
# uses index 2 ("dead_func") and the live subprogram uses index 3
# ("live_func"). After GC the dead entry at index 2 is removed, so the live
# subprogram's string must be remapped from index 3 to index 2. Without the
# DW_FORM_GNU_str_index fix, the old index is copied verbatim and the string
# resolves incorrectly.

# Assemble the .dwo (split debug info).
# RUN: llvm-mc --triple=x86_64-unknown-linux --filetype=obj %p/inputs/gc-compact-gnu-str-index-dwo.s -o %t.dwo

# Generate the exec assembly with the .dwo path substituted in, then assemble it.
# RUN: sed 's|DWO_PATH|%t.dwo|' %p/inputs/gc-compact-gnu-str-index-exec.s | llvm-mc --triple=x86_64-unknown-linux --filetype=obj -o %t.o

# Baseline: all subprograms and strings survive when not garbage-collecting.
# RUN: thorin -e %t.o -o %t-nogc.dwp
# RUN: llvm-dwarfdump -debug-info -debug-str-offsets -debug-str %t-nogc.dwp | FileCheck --check-prefixes=CHECK,NOGC %s

# With garbage collection the dead subprogram is removed and str_offsets compacted.
# RUN: thorin --gc -e %t.o -o %t-gc.dwp
# RUN: llvm-dwarfdump -debug-info -debug-str-offsets -debug-str %t-gc.dwp | FileCheck --check-prefixes=CHECK,GC %s

# -- debug_info checks --
# CHECK: .debug_info.dwo contents:
# CHECK: DW_TAG_compile_unit
# CHECK: DW_AT_name ("test.c")
# CHECK: DW_AT_comp_dir ("/home")

# Without GC, the dead subprogram comes first.
# NOGC: DW_TAG_subprogram
# NOGC: DW_AT_name ("dead_func")

# The live subprogram must always be present with correct name.
# CHECK: DW_TAG_subprogram
# CHECK: DW_AT_name ("live_func")

# After GC there must be no further subprograms.
# GC: NULL
# GC-NOT: DW_TAG_subprogram

# -- debug_str checks (appears before str_offsets in output) --
# After GC the dead string should not be in .debug_str.dwo.
# GC: .debug_str.dwo contents:
# GC: "test.c"
# GC: "live_func"
# GC-NOT: "dead_func"

# -- str_offsets checks --
# CHECK: .debug_str_offsets.dwo contents:

# Without GC the table has 4 entries (16 bytes, no header in DWARF4).
# NOGC: Contribution size = 16
# NOGC: "test.c"
# NOGC: "/home"
# NOGC: "dead_func"
# NOGC: "live_func"

# After GC the table has 3 entries (12 bytes, no header in DWARF4).
# GC: Contribution size = 12
# GC: "test.c"
# GC: "/home"
# GC: "live_func"
# GC-NOT: "dead_func"
74 changes: 74 additions & 0 deletions tests/gc-compact-loclists.test
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
# Test that GC compacts the .debug_loclists.dwo offset table by removing
# entries for location lists only referenced by dead subprograms, and that
# surviving DIEs' loclistx attributes are correctly renumbered.
#
# The .dwo has 3 location lists (loclistx 0, 1, 2). The live subprogram's
# variable uses loclistx 1. After GC the dead entries at indices 0 and 2
# are removed, so the surviving variable's loclistx must be renumbered
# from 1 to 0. Without the fix, the old index is copied verbatim and the
# location list resolves incorrectly.

# Assemble the .dwo (split debug info).
# RUN: llvm-mc --triple=x86_64-unknown-linux --filetype=obj %p/inputs/gc-compact-loclists-dwo.s -o %t.dwo

# Generate the exec assembly with the .dwo path substituted in, then assemble it.
# RUN: sed 's|DWO_PATH|%t.dwo|' %p/inputs/gc-compact-loclists-exec.s | llvm-mc --triple=x86_64-unknown-linux --filetype=obj -o %t.o

# Baseline: all subprograms and location lists survive when not garbage-collecting.
# RUN: thorin -e %t.o -o %t-nogc.dwp
# RUN: llvm-dwarfdump -debug-info -debug-loclists %t-nogc.dwp | FileCheck --check-prefixes=CHECK,NOGC %s

# With garbage collection the dead subprograms are removed and loclists compacted.
# RUN: thorin --gc -e %t.o -o %t-gc.dwp
# RUN: llvm-dwarfdump -debug-info -debug-loclists %t-gc.dwp | FileCheck --check-prefixes=CHECK,GC %s

# -- debug_info checks --
# CHECK: .debug_info.dwo contents:
# CHECK: DW_TAG_compile_unit

# Without GC, dead_func1 comes first.
# NOGC: DW_TAG_subprogram
# NOGC: DW_AT_name ("dead_func1")

# The live subprogram must always be present.
# CHECK: DW_TAG_subprogram
# CHECK: DW_AT_name ("live_func")

# Its variable with a location list must survive.
# Without GC the original loclistx index 1 is preserved.
# NOGC: DW_TAG_variable
# NOGC: DW_AT_location (indexed (0x1) loclist =
# NOGC: <default>: DW_OP_reg1 RDX)

# After GC the loclistx must be renumbered from 1 to 0.
# GC: DW_TAG_variable
# GC: DW_AT_location (indexed (0x0) loclist =
# GC: <default>: DW_OP_reg1 RDX)

# Without GC, dead_func2 is also present.
# NOGC: DW_TAG_subprogram
# NOGC: DW_AT_name ("dead_func2")

# After GC there must be no further subprograms.
# GC: NULL
# GC-NOT: DW_TAG_subprogram

# -- loclists checks --
# CHECK: .debug_loclists.dwo contents:

# Without GC the offset table has 3 entries.
# NOGC: offset_entry_count = 0x00000003
# NOGC: offsets: [
# NOGC: 0x{{[0-9a-f]+}}
# NOGC-NEXT: 0x{{[0-9a-f]+}}
# NOGC-NEXT: 0x{{[0-9a-f]+}}
# NOGC-NEXT: ]

# After GC the offset table has only 1 entry (compacted).
# GC: offset_entry_count = 0x00000001
# GC: offsets: [
# GC: 0x{{[0-9a-f]+}}
# GC-NEXT: ]

# The surviving location list data is correct.
# GC: <default>: DW_OP_reg1 RDX
69 changes: 69 additions & 0 deletions tests/gc-compact-rnglists.test
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
# Test that GC compacts the .debug_rnglists.dwo offset table by removing
# entries for range lists only referenced by dead subprograms, and that
# surviving DIEs' rnglistx attributes are correctly renumbered.
#
# The .dwo has 3 range lists (rnglistx 0, 1, 2). The live subprogram uses
# rnglistx 1. After GC the dead entries at indices 0 and 2 are removed,
# so the surviving subprogram's rnglistx must be renumbered from 1 to 0.
# Without the fix, the old index is copied verbatim and the range list
# resolves incorrectly.

# Assemble the .dwo (split debug info).
# RUN: llvm-mc --triple=x86_64-unknown-linux --filetype=obj %p/inputs/gc-compact-rnglists-dwo.s -o %t.dwo

# Generate the exec assembly with the .dwo path substituted in, then assemble it.
# RUN: sed 's|DWO_PATH|%t.dwo|' %p/inputs/gc-compact-rnglists-exec.s | llvm-mc --triple=x86_64-unknown-linux --filetype=obj -o %t.o

# Baseline: all subprograms and range lists survive when not garbage-collecting.
# RUN: thorin -e %t.o -o %t-nogc.dwp
# RUN: llvm-dwarfdump -debug-info -debug-rnglists %t-nogc.dwp | FileCheck --check-prefixes=CHECK,NOGC %s

# With garbage collection the dead subprograms are removed and rnglists compacted.
# RUN: thorin --gc -e %t.o -o %t-gc.dwp
# RUN: llvm-dwarfdump -debug-info -debug-rnglists %t-gc.dwp | FileCheck --check-prefixes=CHECK,GC %s

# -- debug_info checks --
# CHECK: .debug_info.dwo contents:
# CHECK: DW_TAG_compile_unit

# Without GC, dead_func1 comes first.
# NOGC: DW_TAG_subprogram
# NOGC: DW_AT_name ("dead_func1")

# The live subprogram must always be present.
# CHECK: DW_TAG_subprogram

# Without GC the original rnglistx index 1 is preserved.
# NOGC: DW_AT_ranges (indexed (0x1) rangelist =
# After GC the rnglistx must be renumbered from 1 to 0.
# GC: DW_AT_ranges (indexed (0x0) rangelist =

# CHECK: DW_AT_name ("live_func")

# Without GC, dead_func2 is also present.
# NOGC: DW_TAG_subprogram
# NOGC: DW_AT_name ("dead_func2")

# After GC there must be no further subprograms.
# GC: NULL
# GC-NOT: DW_TAG_subprogram

# -- rnglists checks --
# CHECK: .debug_rnglists.dwo contents:

# Without GC the offset table has 3 entries.
# NOGC: offset_entry_count = 0x00000003
# NOGC: offsets: [
# NOGC: 0x{{[0-9a-f]+}}
# NOGC-NEXT: 0x{{[0-9a-f]+}}
# NOGC-NEXT: 0x{{[0-9a-f]+}}
# NOGC-NEXT: ]

# After GC the offset table has only 1 entry (compacted).
# GC: offset_entry_count = 0x00000001
# GC: offsets: [
# GC: 0x{{[0-9a-f]+}}
# GC-NEXT: ]

# The surviving range list data for live_func is correct.
# GC: [0x{{[0-9a-f]+}}, 0x{{[0-9a-f]+}})
Loading
Loading