[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