[llvm] [BOLT][DWARF] Rewrite DIE references in location lists (PR #221245)

Alexander Wilson via llvm-commits llvm-commits at lists.llvm.org
Fri Sep 4 08:11:01 PDT 2026


https://github.com/rdtscp created https://github.com/llvm/llvm-project/pull/221245

DWARF 5 location-list expressions can contain CU-relative references to base-type DIEs, such as the operand of DW_OP_convert. BOLT rewrote these references in inline expressions but copied location-list expressions before final DIE layout, leaving stale offsets when referenced DIEs moved.

Record reference-bearing location expressions with fixed-width operands and patch them after DIE layout is finalized.

Assisted-by: OpenAI Codex

>From 6e21a86c0976f39e9a8999e1b0e379a6659a6197 Mon Sep 17 00:00:00 2001
From: Alexander Wilson <rdtscp at meta.com>
Date: Thu, 3 Sep 2026 16:11:26 -0700
Subject: [PATCH] [BOLT][DWARF] Rewrite DIE references in location lists

DWARF 5 location-list expressions can contain CU-relative references to base-type DIEs, such as the operand of DW_OP_convert. BOLT rewrote these references in inline expressions but copied location-list expressions before final DIE layout, leaving stale offsets when referenced DIEs moved.

Record reference-bearing location expressions with fixed-width operands and patch them after DIE layout is finalized.

Assisted-by: OpenAI Codex
---
 bolt/include/bolt/Core/DIEBuilder.h           |  8 ++
 bolt/include/bolt/Core/DebugData.h            | 19 ++++-
 bolt/lib/Core/DIEBuilder.cpp                  | 12 +++
 bolt/lib/Core/DebugData.cpp                   | 75 +++++++++++++------
 bolt/lib/Rewrite/DWARFRewriter.cpp            |  5 +-
 .../Inputs/dwarf5-locexpr-referrence-helper.s | 33 +++++---
 bolt/test/X86/dwarf5-locexpr-referrence.test  |  8 +-
 7 files changed, 120 insertions(+), 40 deletions(-)

diff --git a/bolt/include/bolt/Core/DIEBuilder.h b/bolt/include/bolt/Core/DIEBuilder.h
index a9ccd35c90298..f8bca1ff2355a 100644
--- a/bolt/include/bolt/Core/DIEBuilder.h
+++ b/bolt/include/bolt/Core/DIEBuilder.h
@@ -17,6 +17,7 @@
 
 #include "bolt/Core/BinaryContext.h"
 #include "bolt/Core/DebugNames.h"
+#include "llvm/ADT/ArrayRef.h"
 #include "llvm/CodeGen/DIE.h"
 #include "llvm/DebugInfo/DWARF/DWARFAbbreviationDeclaration.h"
 #include "llvm/DebugInfo/DWARF/DWARFDie.h"
@@ -350,6 +351,13 @@ class DIEBuilder {
   /// Finish current DIE construction.
   void finish();
 
+  /// Rewrite base type references in a location expression. During the initial
+  /// rewrite, references are padded so their size remains stable when patched
+  /// after DIE layout is finalized.
+  bool rewriteExpressionReferences(ArrayRef<uint8_t> Input, DWARFUnit &U,
+                                   SmallVectorImpl<uint8_t> &Output,
+                                   bool Patch);
+
   /// Update debug names table.
   void updateDebugNamesTable();
 
diff --git a/bolt/include/bolt/Core/DebugData.h b/bolt/include/bolt/Core/DebugData.h
index b06b0d90a7aee..fcc533ae6fdf8 100644
--- a/bolt/include/bolt/Core/DebugData.h
+++ b/bolt/include/bolt/Core/DebugData.h
@@ -587,11 +587,14 @@ class DebugLocWriter {
 
   /// Writes out location lists and stores internal patches.
   virtual void addList(DIEBuilder &DIEBldr, DIE &Die, DIEValue &AttrInfo,
-                       DebugLocationsVector &LocList);
+                       DebugLocationsVector &LocList, DWARFUnit &Unit);
 
   /// Writes out locations in to a local buffer, and adds Debug Info patches.
   virtual void finalize(DIEBuilder &DIEBldr, DIE &Die);
 
+  /// Updates references in location expressions after DIE layout is finalized.
+  virtual void updateReferences(DIEBuilder &DIEBldr);
+
   /// Return internal buffer.
   virtual std::unique_ptr<DebugBufferVector> getBuffer();
 
@@ -658,11 +661,13 @@ class DebugLoclistWriter : public DebugLocWriter {
 
   /// Stores location lists internally to be written out during finalize phase.
   virtual void addList(DIEBuilder &DIEBldr, DIE &Die, DIEValue &AttrInfo,
-                       DebugLocationsVector &LocList) override;
+                       DebugLocationsVector &LocList, DWARFUnit &Unit) override;
 
   /// Writes out locations in to a local buffer and applies debug info patches.
   void finalize(DIEBuilder &DIEBldr, DIE &Die) override;
 
+  void updateReferences(DIEBuilder &DIEBldr) override;
+
   /// Returns CU ID.
   /// For Skeleton CU it is a CU Offset.
   /// For DWO CU it is a DWO ID.
@@ -681,6 +686,15 @@ class DebugLoclistWriter : public DebugLocWriter {
   constexpr static uint32_t InvalidIndex = UINT32_MAX;
 
 private:
+  struct LocExpressionReference {
+    uint64_t Offset;
+    DWARFUnit *Unit;
+    SmallVector<uint8_t, 32> Expression;
+  };
+
+  void writeDWARF5LocList(DIEBuilder &DIEBldr, DIE &Die, DIEValue &AttrInfo,
+                          DebugLocationsVector &LocList, DWARFUnit &Unit);
+
   /// Writes out locations in to a local buffer and applies debug info patches.
   void finalizeDWARF5(DIEBuilder &DIEBldr, DIE &Die);
 
@@ -691,6 +705,7 @@ class DebugLoclistWriter : public DebugLocWriter {
   std::unique_ptr<DebugBufferVector> LocBodyBuffer;
   std::unique_ptr<raw_svector_ostream> LocBodyStream;
   std::vector<uint64_t> RelativeLocListOffsets;
+  std::vector<LocExpressionReference> LocExpressionReferences;
   uint32_t NumberOfEntries{0};
 };
 
diff --git a/bolt/lib/Core/DIEBuilder.cpp b/bolt/lib/Core/DIEBuilder.cpp
index fd2b872d66eae..016b110cccee6 100644
--- a/bolt/lib/Core/DIEBuilder.cpp
+++ b/bolt/lib/Core/DIEBuilder.cpp
@@ -802,6 +802,18 @@ bool DIEBuilder::cloneExpression(const DataExtractor &Data,
   return DoesContainReference;
 }
 
+bool DIEBuilder::rewriteExpressionReferences(ArrayRef<uint8_t> Input,
+                                             DWARFUnit &U,
+                                             SmallVectorImpl<uint8_t> &Output,
+                                             bool Patch) {
+  DataExtractor Data(Input, U.isLittleEndian());
+  DWARFExpression Expression(Data, U.getAddressByteSize(),
+                             U.getFormParams().Format);
+  return cloneExpression(Data, Expression, U, Output,
+                         Patch ? CloneExpressionStage::PATCH
+                               : CloneExpressionStage::INIT);
+}
+
 void DIEBuilder::cloneBlockAttribute(
     DIE &Die, DWARFUnit &U,
     const DWARFAbbreviationDeclaration::AttributeSpec AttrSpec,
diff --git a/bolt/lib/Core/DebugData.cpp b/bolt/lib/Core/DebugData.cpp
index 51a6ec0716a3c..e691310e243a0 100644
--- a/bolt/lib/Core/DebugData.cpp
+++ b/bolt/lib/Core/DebugData.cpp
@@ -613,7 +613,7 @@ void DebugLocWriter::init() {
 }
 
 void DebugLocWriter::addList(DIEBuilder &DIEBldr, DIE &Die, DIEValue &AttrInfo,
-                             DebugLocationsVector &LocList) {
+                             DebugLocationsVector &LocList, DWARFUnit &) {
   if (LocList.empty()) {
     replaceLocValbyForm(DIEBldr, Die, AttrInfo, AttrInfo.getForm(),
                         DebugLocWriter::EmptyListOffset);
@@ -646,6 +646,8 @@ std::unique_ptr<DebugBufferVector> DebugLocWriter::getBuffer() {
 // DWARF 4: 2.6.2
 void DebugLocWriter::finalize(DIEBuilder &DIEBldr, DIE &Die) {}
 
+void DebugLocWriter::updateReferences(DIEBuilder &) {}
+
 /// Rebases all pending location list offsets by adding \p Base,
 /// updating the corresponding attributes in each patched DIE.
 void DebugLocWriter::applyBase(DIEBuilder &DIEBldr, uint64_t Base) {
@@ -715,64 +717,70 @@ static void writeLegacyLocList(DIEValue &AttrInfo,
   replaceLocValbyForm(DIEBldr, Die, AttrInfo, AttrInfo.getForm(), EntryOffset);
 }
 
-static void writeDWARF5LocList(uint32_t &NumberOfEntries, DIEValue &AttrInfo,
-                               DebugLocationsVector &LocList, DIE &Die,
-                               DIEBuilder &DIEBldr, DebugAddrWriter &AddrWriter,
-                               DebugBufferVector &LocBodyBuffer,
-                               std::vector<uint64_t> &RelativeLocListOffsets,
-                               DWARFUnit &CU,
-                               raw_svector_ostream &LocBodyStream) {
-
+void DebugLoclistWriter::writeDWARF5LocList(DIEBuilder &DIEBldr, DIE &Die,
+                                            DIEValue &AttrInfo,
+                                            DebugLocationsVector &LocList,
+                                            DWARFUnit &Unit) {
   replaceLocValbyForm(DIEBldr, Die, AttrInfo, dwarf::DW_FORM_loclistx,
                       NumberOfEntries);
 
-  RelativeLocListOffsets.push_back(LocBodyBuffer.size());
+  RelativeLocListOffsets.push_back(LocBodyBuffer->size());
   ++NumberOfEntries;
   if (LocList.empty()) {
-    writeEmptyListDwarf5(LocBodyStream);
+    writeEmptyListDwarf5(*LocBodyStream);
     return;
   }
 
   auto writeExpression = [&](uint32_t Index) -> void {
     const DebugLocationEntry &Entry = LocList[Index];
-    encodeULEB128(Entry.Expr.size(), LocBodyStream);
-    LocBodyStream << StringRef(
-        reinterpret_cast<const char *>(Entry.Expr.data()), Entry.Expr.size());
+    SmallVector<uint8_t, 32> Expression;
+    const bool HasReference = DIEBldr.rewriteExpressionReferences(
+        Entry.Expr, Unit, Expression, false);
+    const ArrayRef<uint8_t> ExpressionBytes =
+        HasReference ? ArrayRef<uint8_t>(Expression)
+                     : ArrayRef<uint8_t>(Entry.Expr);
+    encodeULEB128(ExpressionBytes.size(), *LocBodyStream);
+    const uint64_t ExpressionOffset = LocBodyBuffer->size();
+    *LocBodyStream << StringRef(
+        reinterpret_cast<const char *>(ExpressionBytes.data()),
+        ExpressionBytes.size());
+    if (HasReference)
+      LocExpressionReferences.push_back(
+          {ExpressionOffset, &Unit, std::move(Expression)});
   };
   for (unsigned I = 0; I < LocList.size();) {
     if (emitWithBase<DebugLocationsVector, dwarf::LoclistEntries,
-                     DebugLocationEntry>(LocBodyStream, LocList, AddrWriter, CU,
-                                         I, dwarf::DW_LLE_base_addressx,
+                     DebugLocationEntry>(*LocBodyStream, LocList, AddrWriter,
+                                         CU, I, dwarf::DW_LLE_base_addressx,
                                          dwarf::DW_LLE_offset_pair,
                                          writeExpression))
       continue;
 
     const DebugLocationEntry &Entry = LocList[I];
-    support::endian::write(LocBodyStream,
+    support::endian::write(*LocBodyStream,
                            static_cast<uint8_t>(dwarf::DW_LLE_startx_length),
                            llvm::endianness::little);
     const uint32_t Index = AddrWriter.getIndexFromAddress(Entry.LowPC, CU);
-    encodeULEB128(Index, LocBodyStream);
-    encodeULEB128(Entry.HighPC - Entry.LowPC, LocBodyStream);
+    encodeULEB128(Index, *LocBodyStream);
+    encodeULEB128(Entry.HighPC - Entry.LowPC, *LocBodyStream);
     writeExpression(I);
     ++I;
   }
 
-  support::endian::write(LocBodyStream,
+  support::endian::write(*LocBodyStream,
                          static_cast<uint8_t>(dwarf::DW_LLE_end_of_list),
                          llvm::endianness::little);
 }
 
 void DebugLoclistWriter::addList(DIEBuilder &DIEBldr, DIE &Die,
                                  DIEValue &AttrInfo,
-                                 DebugLocationsVector &LocList) {
+                                 DebugLocationsVector &LocList,
+                                 DWARFUnit &Unit) {
   if (DwarfVersion < 5)
     writeLegacyLocList(AttrInfo, LocList, DIEBldr, Die, AddrWriter, *LocBuffer,
                        CU, *LocStream);
   else
-    writeDWARF5LocList(NumberOfEntries, AttrInfo, LocList, Die, DIEBldr,
-                       AddrWriter, *LocBodyBuffer, RelativeLocListOffsets, CU,
-                       *LocBodyStream);
+    writeDWARF5LocList(DIEBldr, Die, AttrInfo, LocList, Unit);
 }
 
 void DebugLoclistWriter::finalizeDWARF5(DIEBuilder &DIEBldr, DIE &Die) {
@@ -805,6 +813,10 @@ void DebugLoclistWriter::finalizeDWARF5(DIEBuilder &DIEBldr, DIE &Die) {
   std::unique_ptr<DebugBufferVector> Header =
       getDWARF5Header({Format, SizeOfArraySection + LocBodyBuffer->size(), 5, 8,
                        0, NumberOfEntries});
+  const uint64_t LocBodyOffset =
+      LocBuffer->size() + Header->size() + LocArrayBuffer->size();
+  for (LocExpressionReference &Reference : LocExpressionReferences)
+    Reference.Offset += LocBodyOffset;
   *LocStream << *Header;
   *LocStream << *LocArrayBuffer;
   *LocStream << *LocBodyBuffer;
@@ -832,6 +844,21 @@ void DebugLoclistWriter::finalize(DIEBuilder &DIEBldr, DIE &Die) {
     finalizeDWARF5(DIEBldr, Die);
 }
 
+void DebugLoclistWriter::updateReferences(DIEBuilder &DIEBldr) {
+  for (LocExpressionReference &Reference : LocExpressionReferences) {
+    SmallVector<uint8_t, 32> Expression;
+    const bool HasReference = DIEBldr.rewriteExpressionReferences(
+        Reference.Expression, *Reference.Unit, Expression, true);
+    assert(HasReference && "location expression reference disappeared");
+    assert(Expression.size() == Reference.Expression.size() &&
+           "location expression size changed while patching references");
+    assert(Reference.Offset + Expression.size() <= LocBuffer->size() &&
+           "location expression patch exceeds section buffer");
+    std::copy(Expression.begin(), Expression.end(),
+              LocBuffer->begin() + Reference.Offset);
+  }
+}
+
 static std::string encodeLE(size_t ByteSize, uint64_t NewValue) {
   std::string LE64(ByteSize, 0);
   for (size_t I = 0; I < ByteSize; ++I) {
diff --git a/bolt/lib/Rewrite/DWARFRewriter.cpp b/bolt/lib/Rewrite/DWARFRewriter.cpp
index 0b4598cccf1c3..8f609334502ce 100644
--- a/bolt/lib/Rewrite/DWARFRewriter.cpp
+++ b/bolt/lib/Rewrite/DWARFRewriter.cpp
@@ -516,6 +516,7 @@ static void emitDWOBuilder(const std::string &DWOName,
   // Populate debug_info and debug_abbrev for current dwo into StringRef.
   DWODIEBuilder.generateAbbrevs();
   DWODIEBuilder.finish();
+  LocWriter.updateReferences(DWODIEBuilder);
 
   SmallVector<char, 20> OutBuffer;
   std::shared_ptr<raw_svector_ostream> ObjOS =
@@ -1061,6 +1062,8 @@ void DWARFRewriter::updateDebugInfo() {
     mergePerBucketRanges(*BucketDIEBlder, LocalWriters[Idx], SortedCUs);
     finalizeCompileUnits(*BucketDIEBlder, DIEBlder, *Streamer, OffsetMap,
                          BucketDIEBlder->getProcessedCUs(), *FinalAddrWriter);
+    for (DWARFUnit *CU : BucketDIEBlder->getProcessedCUs())
+      LocListWritersByCU.at(CU->getOffset())->updateReferences(*BucketDIEBlder);
 
     // Release memory for this bucket.
     BucketDIEBlders[Idx].reset();
@@ -1447,7 +1450,7 @@ void DWARFRewriter::updateUnitDebugInfo(
               // information.
               OutputLL = InputLL;
             }
-            DebugLocWriter.addList(DIEBldr, *Die, LocAttrInfo, OutputLL);
+            DebugLocWriter.addList(DIEBldr, *Die, LocAttrInfo, OutputLL, Unit);
           }
         } else {
           assert((doesFormBelongToClass(LocAttrInfo.getForm(),
diff --git a/bolt/test/X86/Inputs/dwarf5-locexpr-referrence-helper.s b/bolt/test/X86/Inputs/dwarf5-locexpr-referrence-helper.s
index 3dbd945b8075e..438f68fa4eaad 100644
--- a/bolt/test/X86/Inputs/dwarf5-locexpr-referrence-helper.s
+++ b/bolt/test/X86/Inputs/dwarf5-locexpr-referrence-helper.s
@@ -59,8 +59,12 @@ bar:                                    # @bar
 	.byte	4                               # DW_LLE_offset_pair
 	.uleb128 .Ltmp0-.Lfunc_begin0           #   starting offset
 	.uleb128 .Ltmp1-.Lfunc_begin0           #   ending offset
-	.byte	1                               # Loc expr size
-	.byte	80                              # super-register DW_OP_reg0
+	.uleb128 .Ldebug_loc0_expr_end-.Ldebug_loc0_expr # Loc expr size
+.Ldebug_loc0_expr:
+	.byte	48                              # DW_OP_lit0
+	.byte	168                             # DW_OP_convert
+	.uleb128 .Lbase_type0-.Lcu_begin0
+.Ldebug_loc0_expr_end:
 	.byte	0                               # DW_LLE_end_of_list
 .Ldebug_loc1:
 	.byte	4                               # DW_LLE_offset_pair
@@ -218,10 +222,12 @@ bar:                                    # @bar
 	.long	.Lfunc_end0-.Lfunc_begin0       # DW_AT_high_pc
 	.long	.Laddr_table_base0              # DW_AT_addr_base
 	.long	.Lloclists_table_base0          # DW_AT_loclists_base
+.Lcu0_base_type_char:
 	.byte	2                               # Abbrev [2] 0x27:0x4 DW_TAG_base_type
 	.byte	5                               # DW_AT_name
 	.byte	5                               # DW_AT_encoding
 	.byte	1                               # DW_AT_byte_size
+.Lcu0_base_type_int:
 	.byte	2                               # Abbrev [2] 0x2b:0x4 DW_TAG_base_type
 	.byte	4                               # DW_AT_name
 	.byte	5                               # DW_AT_encoding
@@ -235,34 +241,38 @@ bar:                                    # @bar
 	.byte	1                               # DW_AT_decl_file
 	.byte	7                               # DW_AT_decl_line
                                         # DW_AT_prototyped
-	.long	96                              # DW_AT_type
+	.long	.Lbase_type0-.Lcu_begin0        # DW_AT_type
                                         # DW_AT_external
 	.byte	4                               # Abbrev [4] 0x3e:0x9 DW_TAG_formal_parameter
 	.byte	0                               # DW_AT_location
 	.byte	10                              # DW_AT_name
 	.byte	1                               # DW_AT_decl_file
 	.byte	7                               # DW_AT_decl_line
-	.long	96                              # DW_AT_type
+	.long	.Lbase_type0-.Lcu_begin0        # DW_AT_type
 	.byte	5                               # Abbrev [5] 0x47:0x18 DW_TAG_variable
-	.byte	15                              # DW_AT_location
+	.uleb128 .Linline_expr0_end-.Linline_expr0 # DW_AT_location
+.Linline_expr0:
 	.byte	16
 	.byte	32
 	.byte	48
 	.byte	34
 	.byte	168
-	.asciz	"\247\200\200"
+	.uleb128 .Lcu0_base_type_char-.Lcu_begin0
 	.byte	168
-	.asciz	"\253\200\200"
+	.uleb128 .Lcu0_base_type_int-.Lcu_begin0
 	.byte	159
+.Linline_expr0_end:
 	.byte	11                              # DW_AT_name
 	.byte	1                               # DW_AT_decl_file
 	.byte	9                               # DW_AT_decl_line
-	.long	100                             # DW_AT_type
+	.long	.Lbase_type_int0-.Lcu_begin0    # DW_AT_type
 	.byte	0                               # End Of Children Mark
+.Lbase_type0:
 	.byte	2                               # Abbrev [2] 0x60:0x4 DW_TAG_base_type
 	.byte	8                               # DW_AT_name
 	.byte	6                               # DW_AT_encoding
 	.byte	1                               # DW_AT_byte_size
+.Lbase_type_int0:
 	.byte	2                               # Abbrev [2] 0x64:0x4 DW_TAG_base_type
 	.byte	12                              # DW_AT_name
 	.byte	5                               # DW_AT_encoding
@@ -304,14 +314,14 @@ bar:                                    # @bar
 	.byte	2                               # DW_AT_decl_file
 	.byte	1                               # DW_AT_decl_line
                                         # DW_AT_prototyped
-	.long	.debug_info+96                  # DW_AT_type
+	.long	.Lbase_type0                    # DW_AT_type
                                         # DW_AT_external
 	.byte	7                               # Abbrev [7] 0x3e:0x9 DW_TAG_formal_parameter
 	.byte	1                               # DW_AT_location
 	.byte	10                              # DW_AT_name
 	.byte	2                               # DW_AT_decl_file
 	.byte	1                               # DW_AT_decl_line
-	.long	.debug_info+96                  # DW_AT_type
+	.long	.Lbase_type0                    # DW_AT_type
 	.byte	5                               # Abbrev [5] 0x47:0x18 DW_TAG_variable
 	.byte	15                              # DW_AT_location
 	.byte	16
@@ -326,8 +336,9 @@ bar:                                    # @bar
 	.byte	13                              # DW_AT_name
 	.byte	2                               # DW_AT_decl_file
 	.byte	3                               # DW_AT_decl_line
-	.long	96                              # DW_AT_type
+	.long	.Lbase_type1-.Lcu_begin1        # DW_AT_type
 	.byte	0                               # End Of Children Mark
+.Lbase_type1:
 	.byte	2                               # Abbrev [2] 0x60:0x4 DW_TAG_base_type
 	.byte	14                              # DW_AT_name
 	.byte	5                               # DW_AT_encoding
diff --git a/bolt/test/X86/dwarf5-locexpr-referrence.test b/bolt/test/X86/dwarf5-locexpr-referrence.test
index 5b38987e0a712..e38e14eef31e0 100644
--- a/bolt/test/X86/dwarf5-locexpr-referrence.test
+++ b/bolt/test/X86/dwarf5-locexpr-referrence.test
@@ -6,11 +6,15 @@
 # RUN: llvm-bolt %t.exe -o %t.bolt --update-debug-sections --debug-thread-count=4 --cu-processing-batch-size=4
 # RUN: llvm-dwarfdump --show-form --verbose --debug-info %t.bolt | FileCheck --check-prefix=CHECK %s
 # RUN: llvm-dwarfdump --show-form --verbose --debug-addr %t.bolt | FileCheck --check-prefix=CHECKADDR %s
+# RUN: llvm-dwarfdump --verify %t.bolt
 
-## This test checks that we update relative DIE references with DW_OP_convert that are in locexpr
-## and checks the address table is correct.
+## This test checks that we update relative DIE references with DW_OP_convert in
+## location expressions and location lists, and checks the address table.
 
 # CHECK: version = 0x0005
+# CHECK: DW_TAG_formal_parameter
+# CHECK-NEXT: DW_AT_location
+# CHECK-NEXT: DW_OP_lit0, DW_OP_convert
 # CHECK: DW_TAG_variable
 # CHECK-NEXT: DW_AT_location
 # CHECK-SAME: DW_OP_convert (0x00000028 -> 0x00000028)



More information about the llvm-commits mailing list