[llvm] [DWARFLinker] Keep address-independent block attributes in the type unit (PR #215932)
Jonas Devlieghere via llvm-commits
llvm-commits at lists.llvm.org
Wed Aug 12 18:18:45 PDT 2026
https://github.com/JDevlieghere created https://github.com/llvm/llvm-project/pull/215932
cloneBlockAttr dropped every block and exprloc attribute whose DIE was placed in the artificial type unit. The intent was to discard location expressions that resolve against a single origin compile unit, since a type unit is shared by every unit referencing the type, but the check was incorrectly applied to all block-form attributes.
Clang encodes DW_AT_data_member_location as DW_FORM_block1 at DWARF 2 and as a constant from DWARF 3 onward, so at DWARF 2 every member and inheritance offset of a deduplicated record collapsed to zero. This caused LLDB to crash when trying to build types using the wrong offsets, but that's a separate issue.
Drop only expressions that carry a target address or reference a base type DIE, which are the cases the type unit cannot resolve.
>From 0212ac2cdddb6a628d158338a002ed9c7053aae7 Mon Sep 17 00:00:00 2001
From: Jonas Devlieghere <jonas at devlieghere.com>
Date: Wed, 12 Aug 2026 18:16:47 -0700
Subject: [PATCH] [DWARFLinker] Keep address-independent block attributes in
the type unit
cloneBlockAttr dropped every block and exprloc attribute whose DIE was
placed in the artificial type unit. The intent was to discard location
expressions that resolve against a single origin compile unit, since a
type unit is shared by every unit referencing the type, but the check
was incorrectly applied to all block-form attributes.
Clang encodes DW_AT_data_member_location as DW_FORM_block1 at DWARF 2
and as a constant from DWARF 3 onward, so at DWARF 2 every member and
inheritance offset of a deduplicated record collapsed to zero. This
caused LLDB to crash when trying to build types using the wrong offsets,
but that's a separate issue.
Drop only expressions that carry a target address or reference a base
type DIE, which are the cases the type unit cannot resolve.
---
.../Parallel/DIEAttributeCloner.cpp | 29 +-
.../dwarf2-member-location.s | 261 ++++++++++++++++++
2 files changed, 287 insertions(+), 3 deletions(-)
create mode 100644 llvm/test/tools/dsymutil/X86/DWARFLinkerParallel/dwarf2-member-location.s
diff --git a/llvm/lib/DWARFLinker/Parallel/DIEAttributeCloner.cpp b/llvm/lib/DWARFLinker/Parallel/DIEAttributeCloner.cpp
index c58d2937f3ed0..f58869d20291c 100644
--- a/llvm/lib/DWARFLinker/Parallel/DIEAttributeCloner.cpp
+++ b/llvm/lib/DWARFLinker/Parallel/DIEAttributeCloner.cpp
@@ -549,13 +549,30 @@ size_t DIEAttributeCloner::cloneScalarAttr(
return Result.second;
}
+static bool expressionDependsOnOriginUnit(const DWARFExpression &Expr) {
+ using Encoding = DWARFExpression::Operation::Encoding;
+
+ for (const DWARFExpression::Operation &Op : Expr) {
+ switch (Op.getCode()) {
+ case dwarf::DW_OP_addr:
+ case dwarf::DW_OP_addrx:
+ case dwarf::DW_OP_constx:
+ return true;
+ default:
+ break;
+ }
+
+ if (llvm::is_contained(Op.getDescription().Op, Encoding::BaseTypeRef))
+ return true;
+ }
+
+ return false;
+}
+
size_t DIEAttributeCloner::cloneBlockAttr(
const DWARFFormValue &Val,
const DWARFAbbreviationDeclaration::AttributeSpec &AttrSpec) {
- if (OutUnit.isTypeUnit())
- return 0;
-
size_t NumberOfPatchesAtStart = PatchesOffsets.size();
// If the block is a DWARF Expression, clone it into the temporary
@@ -569,6 +586,12 @@ size_t DIEAttributeCloner::cloneBlockAttr(
DWARFExpression Expr(Data, InUnit.getOrigUnit().getAddressByteSize(),
InUnit.getFormParams().Format);
+ // A type unit is shared by every compile unit that references the type, so
+ // an expression resolving against one origin unit has no single correct
+ // value there.
+ if (OutUnit.isTypeUnit() && expressionDependsOnOriginUnit(Expr))
+ return 0;
+
InUnit.cloneDieAttrExpression(Expr, Buffer, DebugInfoOutputSection,
VarAddressAdjustment, PatchesOffsets);
Bytes = Buffer;
diff --git a/llvm/test/tools/dsymutil/X86/DWARFLinkerParallel/dwarf2-member-location.s b/llvm/test/tools/dsymutil/X86/DWARFLinkerParallel/dwarf2-member-location.s
new file mode 100644
index 0000000000000..81d5e5d28a976
--- /dev/null
+++ b/llvm/test/tools/dsymutil/X86/DWARFLinkerParallel/dwarf2-member-location.s
@@ -0,0 +1,261 @@
+# DWARF 2 encodes DW_AT_data_member_location as a location expression rather
+# than a constant, and DW_AT_vtable_elem_location is an expression at every
+# DWARF version. Both must survive placement in the artificial type unit.
+
+# RUN: llvm-mc -triple x86_64-apple-darwin -filetype=obj %s -o %t.o
+# RUN: llvm-dwarfdump --verify %t.o
+
+# RUN: echo '---' > %t.map
+# RUN: echo "triple: 'x86_64-apple-darwin'" >> %t.map
+# RUN: echo 'objects:' >> %t.map
+# RUN: echo " - filename: '%t.o'" >> %t.map
+# RUN: echo ' symbols:' >> %t.map
+# RUN: echo ' - { sym: __Z4keepP7Derived, objAddr: 0x0, binAddr: 0x10000, size: 0x1 }' >> %t.map
+# RUN: echo '...' >> %t.map
+
+# RUN: dsymutil --linker=parallel -y %t.map -f -o %t.parallel.dSYM
+# RUN: llvm-dwarfdump --verify %t.parallel.dSYM
+# RUN: llvm-dwarfdump -debug-info %t.parallel.dSYM \
+# RUN: | FileCheck %s --check-prefix=PARALLEL
+
+# RUN: dsymutil --linker=classic -y %t.map -f -o %t.classic.dSYM
+# RUN: llvm-dwarfdump --verify %t.classic.dSYM
+# RUN: llvm-dwarfdump -debug-info %t.classic.dSYM \
+# RUN: | FileCheck %s --check-prefix=CLASSIC
+
+# The parallel linker moves the deduplicated types into the artificial type unit
+# and the classic linker keeps them in the compile unit, so the two emit them in
+# different orders.
+
+# PARALLEL: DW_AT_name{{.*}}"__artificial_type_unit"
+# PARALLEL: DW_TAG_structure_type
+# PARALLEL: DW_AT_name{{.*}}"Base"
+# PARALLEL: DW_TAG_member
+# PARALLEL: DW_AT_name{{.*}}"first"
+# PARALLEL: DW_AT_data_member_location{{.*}}(DW_OP_plus_uconst 0x0)
+# PARALLEL: DW_TAG_member
+# PARALLEL: DW_AT_name{{.*}}"second"
+# PARALLEL: DW_AT_data_member_location{{.*}}(DW_OP_plus_uconst 0x4)
+# PARALLEL: DW_TAG_structure_type
+# PARALLEL: DW_AT_name{{.*}}"Derived"
+# PARALLEL: DW_TAG_inheritance
+# PARALLEL: DW_AT_data_member_location{{.*}}(DW_OP_plus_uconst 0x8)
+# PARALLEL: DW_TAG_member
+# PARALLEL: DW_AT_name{{.*}}"third"
+# PARALLEL: DW_AT_data_member_location{{.*}}(DW_OP_plus_uconst 0x10)
+# PARALLEL: DW_TAG_subprogram
+# PARALLEL: DW_AT_name{{.*}}"virt"
+# PARALLEL: DW_AT_vtable_elem_location{{.*}}(DW_OP_constu 0x0)
+
+# CLASSIC: DW_TAG_structure_type
+# CLASSIC: DW_AT_name{{.*}}"Derived"
+# CLASSIC: DW_TAG_inheritance
+# CLASSIC: DW_AT_data_member_location{{.*}}(DW_OP_plus_uconst 0x8)
+# CLASSIC: DW_TAG_member
+# CLASSIC: DW_AT_name{{.*}}"third"
+# CLASSIC: DW_AT_data_member_location{{.*}}(DW_OP_plus_uconst 0x10)
+# CLASSIC: DW_TAG_subprogram
+# CLASSIC: DW_AT_name{{.*}}"virt"
+# CLASSIC: DW_AT_vtable_elem_location{{.*}}(DW_OP_constu 0x0)
+# CLASSIC: DW_TAG_structure_type
+# CLASSIC: DW_AT_name{{.*}}"Base"
+# CLASSIC: DW_TAG_member
+# CLASSIC: DW_AT_name{{.*}}"first"
+# CLASSIC: DW_AT_data_member_location{{.*}}(DW_OP_plus_uconst 0x0)
+# CLASSIC: DW_TAG_member
+# CLASSIC: DW_AT_name{{.*}}"second"
+# CLASSIC: DW_AT_data_member_location{{.*}}(DW_OP_plus_uconst 0x4)
+
+ .section __TEXT,__text,regular,pure_instructions
+ .globl __Z4keepP7Derived
+__Z4keepP7Derived:
+Lfunc_begin0:
+ retq
+Lfunc_end0:
+
+ .section __DWARF,__debug_abbrev,regular,debug
+Lsection_abbrev:
+ .byte 1 ## Abbreviation Code
+ .byte 17 ## DW_TAG_compile_unit
+ .byte 1 ## DW_CHILDREN_yes
+ .byte 37 ## DW_AT_producer
+ .byte 8 ## DW_FORM_string
+ .byte 19 ## DW_AT_language
+ .byte 5 ## DW_FORM_data2
+ .byte 3 ## DW_AT_name
+ .byte 8 ## DW_FORM_string
+ .byte 0, 0
+
+ .byte 2 ## Abbreviation Code
+ .byte 46 ## DW_TAG_subprogram
+ .byte 1 ## DW_CHILDREN_yes
+ .byte 3 ## DW_AT_name
+ .byte 8 ## DW_FORM_string
+ .byte 0x87, 0x40 ## DW_AT_MIPS_linkage_name (0x2007)
+ .byte 8 ## DW_FORM_string
+ .byte 17 ## DW_AT_low_pc
+ .byte 1 ## DW_FORM_addr
+ .byte 18 ## DW_AT_high_pc
+ .byte 1 ## DW_FORM_addr
+ .byte 63 ## DW_AT_external
+ .byte 12 ## DW_FORM_flag
+ .byte 0, 0
+
+ .byte 3 ## Abbreviation Code
+ .byte 5 ## DW_TAG_formal_parameter
+ .byte 0 ## DW_CHILDREN_no
+ .byte 73 ## DW_AT_type
+ .byte 19 ## DW_FORM_ref4
+ .byte 0, 0
+
+ .byte 4 ## Abbreviation Code
+ .byte 19 ## DW_TAG_structure_type
+ .byte 1 ## DW_CHILDREN_yes
+ .byte 3 ## DW_AT_name
+ .byte 8 ## DW_FORM_string
+ .byte 11 ## DW_AT_byte_size
+ .byte 11 ## DW_FORM_data1
+ .byte 0, 0
+
+ .byte 5 ## Abbreviation Code
+ .byte 28 ## DW_TAG_inheritance
+ .byte 0 ## DW_CHILDREN_no
+ .byte 73 ## DW_AT_type
+ .byte 19 ## DW_FORM_ref4
+ .byte 56 ## DW_AT_data_member_location
+ .byte 10 ## DW_FORM_block1
+ .byte 0, 0
+
+ .byte 6 ## Abbreviation Code
+ .byte 13 ## DW_TAG_member
+ .byte 0 ## DW_CHILDREN_no
+ .byte 3 ## DW_AT_name
+ .byte 8 ## DW_FORM_string
+ .byte 73 ## DW_AT_type
+ .byte 19 ## DW_FORM_ref4
+ .byte 56 ## DW_AT_data_member_location
+ .byte 10 ## DW_FORM_block1
+ .byte 0, 0
+
+ .byte 7 ## Abbreviation Code
+ .byte 46 ## DW_TAG_subprogram
+ .byte 0 ## DW_CHILDREN_no
+ .byte 3 ## DW_AT_name
+ .byte 8 ## DW_FORM_string
+ .byte 0x87, 0x40 ## DW_AT_MIPS_linkage_name (0x2007)
+ .byte 8 ## DW_FORM_string
+ .byte 76 ## DW_AT_virtuality
+ .byte 11 ## DW_FORM_data1
+ .byte 77 ## DW_AT_vtable_elem_location
+ .byte 10 ## DW_FORM_block1
+ .byte 60 ## DW_AT_declaration
+ .byte 12 ## DW_FORM_flag
+ .byte 0, 0
+
+ .byte 8 ## Abbreviation Code
+ .byte 36 ## DW_TAG_base_type
+ .byte 0 ## DW_CHILDREN_no
+ .byte 3 ## DW_AT_name
+ .byte 8 ## DW_FORM_string
+ .byte 11 ## DW_AT_byte_size
+ .byte 11 ## DW_FORM_data1
+ .byte 62 ## DW_AT_encoding
+ .byte 11 ## DW_FORM_data1
+ .byte 0, 0
+
+ .byte 9 ## Abbreviation Code
+ .byte 15 ## DW_TAG_pointer_type
+ .byte 0 ## DW_CHILDREN_no
+ .byte 73 ## DW_AT_type
+ .byte 19 ## DW_FORM_ref4
+ .byte 0, 0
+
+ .byte 0 ## EOM(3)
+
+ .section __DWARF,__debug_info,regular,debug
+Lsection_info:
+ .long Lcu_end - Lcu_start ## Length of Unit
+Lcu_start:
+ .short 2 ## DWARF version number
+ .long 0 ## Offset Into Abbrev. Section
+ .byte 8 ## Address Size (in bytes)
+
+ .byte 1 ## Abbrev [1] DW_TAG_compile_unit
+ .asciz "hand-written" ## DW_AT_producer
+ .short 0x0004 ## DW_AT_language (DW_LANG_C_plus_plus)
+ .asciz "dwarf2-member-location.cpp" ## DW_AT_name
+
+ .byte 2 ## Abbrev [2] DW_TAG_subprogram
+ .asciz "keep" ## DW_AT_name
+ .asciz "__Z4keepP7Derived" ## DW_AT_MIPS_linkage_name
+ .quad Lfunc_begin0 ## DW_AT_low_pc
+ .quad Lfunc_end0 ## DW_AT_high_pc
+ .byte 1 ## DW_AT_external
+
+ .byte 3 ## Abbrev [3] DW_TAG_formal_parameter
+ .long Lderived_ptr - Lsection_info ## DW_AT_type
+
+ .byte 0 ## End Of Children Mark (keep)
+
+Lderived:
+ .byte 4 ## Abbrev [4] DW_TAG_structure_type
+ .asciz "Derived" ## DW_AT_name
+ .byte 0x18 ## DW_AT_byte_size
+
+ .byte 5 ## Abbrev [5] DW_TAG_inheritance
+ .long Lbase - Lsection_info ## DW_AT_type
+ .byte 2 ## DW_AT_data_member_location length
+ .byte 0x23 ## DW_OP_plus_uconst
+ .byte 0x08 ## 0x8
+
+ .byte 6 ## Abbrev [6] DW_TAG_member
+ .asciz "third" ## DW_AT_name
+ .long Lint - Lsection_info ## DW_AT_type
+ .byte 2 ## DW_AT_data_member_location length
+ .byte 0x23 ## DW_OP_plus_uconst
+ .byte 0x10 ## 0x10
+
+ .byte 7 ## Abbrev [7] DW_TAG_subprogram
+ .asciz "virt" ## DW_AT_name
+ .asciz "__ZN7Derived4virtEv" ## DW_AT_MIPS_linkage_name
+ .byte 1 ## DW_AT_virtuality (DW_VIRTUALITY_virtual)
+ .byte 2 ## DW_AT_vtable_elem_location length
+ .byte 0x10 ## DW_OP_constu
+ .byte 0x00 ## 0x0
+ .byte 1 ## DW_AT_declaration
+
+ .byte 0 ## End Of Children Mark (Derived)
+
+Lbase:
+ .byte 4 ## Abbrev [4] DW_TAG_structure_type
+ .asciz "Base" ## DW_AT_name
+ .byte 0x08 ## DW_AT_byte_size
+
+ .byte 6 ## Abbrev [6] DW_TAG_member
+ .asciz "first" ## DW_AT_name
+ .long Lint - Lsection_info ## DW_AT_type
+ .byte 2 ## DW_AT_data_member_location length
+ .byte 0x23 ## DW_OP_plus_uconst
+ .byte 0x00 ## 0x0
+
+ .byte 6 ## Abbrev [6] DW_TAG_member
+ .asciz "second" ## DW_AT_name
+ .long Lint - Lsection_info ## DW_AT_type
+ .byte 2 ## DW_AT_data_member_location length
+ .byte 0x23 ## DW_OP_plus_uconst
+ .byte 0x04 ## 0x4
+
+ .byte 0 ## End Of Children Mark (Base)
+
+Lint:
+ .byte 8 ## Abbrev [8] DW_TAG_base_type
+ .asciz "int" ## DW_AT_name
+ .byte 4 ## DW_AT_byte_size
+ .byte 5 ## DW_AT_encoding (DW_ATE_signed)
+
+Lderived_ptr:
+ .byte 9 ## Abbrev [9] DW_TAG_pointer_type
+ .long Lderived - Lsection_info ## DW_AT_type
+
+ .byte 0 ## End Of Children Mark (CU)
+Lcu_end:
More information about the llvm-commits
mailing list