[lldb] [llvm] support in LLDB for the DWARF operations `DW_OP_GNU_implicit_pointer/DW_OP_implicit_pointer`. (PR #142747)
via llvm-commits
llvm-commits at lists.llvm.org
Wed Jun 4 07:19:48 PDT 2025
https://github.com/Thrrreeee updated https://github.com/llvm/llvm-project/pull/142747
>From 64785ba97f670ace37c3d1e43aef1141a232c00c Mon Sep 17 00:00:00 2001
From: jinruiShi <1379998393 at qq.com>
Date: Wed, 4 Jun 2025 16:24:53 +0800
Subject: [PATCH] LLDB support for
DW_OP_GNU_implicit_pointer/DW_OP_implicit_pointer operation
---
lldb/include/lldb/Core/Value.h | 10 +++
lldb/include/lldb/Symbol/Variable.h | 8 +-
.../Commands/CommandObjectDWIMPrint.cpp | 9 ++-
lldb/source/Core/Value.cpp | 21 +++++-
lldb/source/Expression/DWARFExpression.cpp | 20 ++++-
.../source/Expression/DWARFExpressionList.cpp | 73 ++++++++++++++++++-
lldb/source/Expression/Materializer.cpp | 40 ++++++++--
lldb/source/Symbol/Variable.cpp | 4 +-
.../ValueObject/ValueObjectVariable.cpp | 17 +++--
llvm/include/llvm/BinaryFormat/Dwarf.def | 2 +
llvm/lib/DebugInfo/DWARF/DWARFExpression.cpp | 2 +
.../DebugInfo/LogicalView/Core/LVLocation.cpp | 1 +
12 files changed, 181 insertions(+), 26 deletions(-)
diff --git a/lldb/include/lldb/Core/Value.h b/lldb/include/lldb/Core/Value.h
index 3714621b469ec..b5411e2377c93 100644
--- a/lldb/include/lldb/Core/Value.h
+++ b/lldb/include/lldb/Core/Value.h
@@ -82,6 +82,10 @@ class Value {
ValueType GetValueType() const;
+ void setImplictPointerDIEoffset(uint64_t offset);
+ void setImplictPointerOffset(int64_t offset);
+ uint64_t getImplictPointerDIEoffset() const;
+ int64_t getImplictPointerOffset() const;
AddressType GetValueAddressType() const;
ContextType GetContextType() const { return m_context_type; }
@@ -182,6 +186,12 @@ class Value {
ValueType m_value_type = ValueType::Scalar;
ContextType m_context_type = ContextType::Invalid;
DataBufferHeap m_data_buffer;
+ struct {
+ /* 4- or 8-byte offset of DIE */
+ uint64_t die_offset = 0;
+ /* The byte offset into the resulting data. */
+ int64_t result_offset;
+ } implicit_pointer;
};
class ValueList {
diff --git a/lldb/include/lldb/Symbol/Variable.h b/lldb/include/lldb/Symbol/Variable.h
index c437624d1ea6d..d38945f32f89e 100644
--- a/lldb/include/lldb/Symbol/Variable.h
+++ b/lldb/include/lldb/Symbol/Variable.h
@@ -34,7 +34,7 @@ class Variable : public UserID, public std::enable_shared_from_this<Variable> {
SymbolContextScope *owner_scope, const RangeList &scope_range,
Declaration *decl, const DWARFExpressionList &location,
bool external, bool artificial, bool location_is_constant_data,
- bool static_member = false);
+ bool static_member = false, bool is_implicit_pointer = false);
virtual ~Variable();
@@ -97,6 +97,9 @@ class Variable : public UserID, public std::enable_shared_from_this<Variable> {
void SetLocationIsConstantValueData(bool b) { m_loc_is_const_data = b; }
+ bool IsImplicitPointer() const { return m_is_implicit_pointer; }
+
+ void SetIsImplicitPointer(bool b) { m_is_implicit_pointer = b; }
typedef size_t (*GetVariableCallback)(void *baton, const char *name,
VariableList &var_list);
@@ -140,7 +143,8 @@ class Variable : public UserID, public std::enable_shared_from_this<Variable> {
unsigned m_loc_is_const_data : 1;
/// Non-zero if variable is static member of a class or struct.
unsigned m_static_member : 1;
-
+ /// Non-zero if the variable is a implicit pointer type.
+ unsigned m_is_implicit_pointer : 1;
private:
Variable(const Variable &rhs) = delete;
Variable &operator=(const Variable &rhs) = delete;
diff --git a/lldb/source/Commands/CommandObjectDWIMPrint.cpp b/lldb/source/Commands/CommandObjectDWIMPrint.cpp
index 17c60297a521e..1cf82dfb780a4 100644
--- a/lldb/source/Commands/CommandObjectDWIMPrint.cpp
+++ b/lldb/source/Commands/CommandObjectDWIMPrint.cpp
@@ -19,6 +19,7 @@
#include "lldb/Target/StackFrame.h"
#include "lldb/Utility/ConstString.h"
#include "lldb/ValueObject/ValueObject.h"
+#include "lldb/Symbol/Variable.h"
#include "lldb/lldb-defines.h"
#include "lldb/lldb-enumerations.h"
#include "lldb/lldb-forward.h"
@@ -174,7 +175,13 @@ void CommandObjectDWIMPrint::DoExecute(StringRef command,
if (auto persisted_valobj = valobj_sp->Persist())
valobj_sp = persisted_valobj;
}
-
+ // FIXME: LLDB haven't implemented "optimization out" output.
+ if (valobj_sp->GetVariable()->IsImplicitPointer()) {
+ result.AppendMessageWithFormatv("expression `{0}` is an implicit "
+ "pointer, value has been optimized out",
+ expr);
+ return;
+ }
if (verbosity == eDWIMPrintVerbosityFull) {
StringRef flags;
if (args.HasArgs())
diff --git a/lldb/source/Core/Value.cpp b/lldb/source/Core/Value.cpp
index c91b3f852f986..4e7956c7894b5 100644
--- a/lldb/source/Core/Value.cpp
+++ b/lldb/source/Core/Value.cpp
@@ -55,7 +55,8 @@ Value::Value(const void *bytes, int len)
Value::Value(const Value &v)
: m_value(v.m_value), m_compiler_type(v.m_compiler_type),
m_context(v.m_context), m_value_type(v.m_value_type),
- m_context_type(v.m_context_type), m_data_buffer() {
+ m_context_type(v.m_context_type), m_data_buffer(),
+ implicit_pointer(v.implicit_pointer) {
const uintptr_t rhs_value =
(uintptr_t)v.m_value.ULongLong(LLDB_INVALID_ADDRESS);
if ((rhs_value != 0) &&
@@ -65,6 +66,8 @@ Value::Value(const Value &v)
m_value = (uintptr_t)m_data_buffer.GetBytes();
}
+ implicit_pointer.result_offset = v.implicit_pointer.result_offset;
+ implicit_pointer.die_offset = v.implicit_pointer.die_offset;
}
Value &Value::operator=(const Value &rhs) {
@@ -74,6 +77,7 @@ Value &Value::operator=(const Value &rhs) {
m_context = rhs.m_context;
m_value_type = rhs.m_value_type;
m_context_type = rhs.m_context_type;
+ implicit_pointer = rhs.implicit_pointer;
const uintptr_t rhs_value =
(uintptr_t)rhs.m_value.ULongLong(LLDB_INVALID_ADDRESS);
if ((rhs_value != 0) &&
@@ -641,6 +645,8 @@ void Value::Clear() {
m_context = nullptr;
m_context_type = ContextType::Invalid;
m_data_buffer.Clear();
+ implicit_pointer.result_offset = 0;
+ implicit_pointer.die_offset = 0;
}
const char *Value::GetValueTypeAsCString(ValueType value_type) {
@@ -659,6 +665,19 @@ const char *Value::GetValueTypeAsCString(ValueType value_type) {
llvm_unreachable("enum cases exhausted.");
}
+void Value::setImplictPointerDIEoffset(uint64_t offset) {
+ implicit_pointer.die_offset = offset;
+}
+void Value::setImplictPointerOffset(int64_t offset) {
+ implicit_pointer.result_offset = offset;
+}
+uint64_t Value::getImplictPointerDIEoffset() const {
+ return implicit_pointer.die_offset;
+}
+int64_t Value::getImplictPointerOffset() const {
+ return implicit_pointer.result_offset;
+}
+
const char *Value::GetContextTypeAsCString(ContextType context_type) {
switch (context_type) {
case ContextType::Invalid:
diff --git a/lldb/source/Expression/DWARFExpression.cpp b/lldb/source/Expression/DWARFExpression.cpp
index f48f3ab9307dd..aa84c415b8fad 100644
--- a/lldb/source/Expression/DWARFExpression.cpp
+++ b/lldb/source/Expression/DWARFExpression.cpp
@@ -357,7 +357,7 @@ static lldb::offset_t GetOpcodeDataSize(const DataExtractor &data,
offset += block_len;
return offset - data_offset;
}
-
+ case DW_OP_GNU_implicit_pointer:
case DW_OP_implicit_pointer: // 0xa0 4-byte (or 8-byte for DWARF 64) constant
// + LEB128
{
@@ -2066,11 +2066,23 @@ llvm::Expected<Value> DWARFExpression::Evaluate(
stack.push_back(result);
break;
}
-
+ // OPCODE: DW_OP_implicit_pointer
+ // OPERANDS:
+ // (1) 2 4- or 8-byte offset of DIE
+ // (2) SLEB128 constant offset
+ // DESCRIPTION: First offset is a reference to a debugging information entry that describes the dereferenced object’s value
+ // and a signed number that is treated as a byte offset from the start of that value
+ case DW_OP_GNU_implicit_pointer:
case DW_OP_implicit_pointer: {
dwarf4_location_description_kind = Implicit;
- return llvm::createStringError("Could not evaluate %s.",
- DW_OP_value_to_name(op));
+ uint64_t die_offset = opcodes.GetU32(&offset);
+ int64_t result_offset = opcodes.GetSLEB128(&offset);
+
+ Value implicit_ptr_value;
+ implicit_ptr_value.setImplictPointerDIEoffset(die_offset);
+ implicit_ptr_value.setImplictPointerOffset(result_offset);
+ stack.push_back(implicit_ptr_value);
+ break;
}
// OPCODE: DW_OP_push_object_address
diff --git a/lldb/source/Expression/DWARFExpressionList.cpp b/lldb/source/Expression/DWARFExpressionList.cpp
index be6c0a151159d..1715aee155ad2 100644
--- a/lldb/source/Expression/DWARFExpressionList.cpp
+++ b/lldb/source/Expression/DWARFExpressionList.cpp
@@ -9,13 +9,16 @@
#include "lldb/Expression/DWARFExpressionList.h"
#include "Plugins/SymbolFile/DWARF/DWARFUnit.h"
#include "lldb/Symbol/Function.h"
+#include "lldb/Symbol/VariableList.h"
#include "lldb/Target/RegisterContext.h"
#include "lldb/Target/StackFrame.h"
+#include "lldb/ValueObject/ValueObject.h"
#include "llvm/DebugInfo/DWARF/DWARFDebugLoc.h"
#include "llvm/DebugInfo/DWARF/DWARFFormValue.h"
using namespace lldb;
using namespace lldb_private;
+using namespace lldb_private::plugin::dwarf;
bool DWARFExpressionList::IsAlwaysValidSingleExpr() const {
return GetAlwaysValidExpr() != nullptr;
@@ -197,6 +200,58 @@ void DWARFExpressionList::GetDescription(Stream *s,
}
}
+//===----------------------------------------------------------------------===//
+// ResolveImplicitPointerValue resolves an implicit pointer value from a DIE
+// offset and an offset within that DIE. It retrieves the variable name from
+// the DIE, finds the corresponding variable in the current frame's variable
+// list, and constructs a Value object representing the implicit pointer.
+static llvm::Expected<Value> ResolveImplicitPointerValue(ExecutionContext *exe_ctx,
+ const DWARFUnit *dwarf_cu,
+ uint64_t die_offset,
+ uint64_t offset) {
+ if (!exe_ctx || !dwarf_cu)
+ return llvm::createStringError("invalid execution context or DWARF CU");
+
+ DWARFDIE die = const_cast<DWARFUnit *>(dwarf_cu)->GetDIE(die_offset);
+ if (!die) {
+ return llvm::createStringError(llvm::inconvertibleErrorCode(),
+ "Could not locate DIE at offset 0x%" PRIx64
+ " for implicit pointer.",
+ die_offset);
+ }
+
+ DWARFAttributes attrs = die.GetAttributes();
+ const char *name = die.GetAttributeValueAsString(dwarf::DW_AT_name, nullptr);
+ StackFrame *frame = exe_ctx->GetFramePtr();
+ if (!frame)
+ return llvm::createStringError("no frame for implicit pointer");
+
+ const bool get_file_globals = true;
+ VariableListSP var_list_sp(frame->GetInScopeVariableList(get_file_globals));
+ VariableList *variable_list = var_list_sp.get();
+ if (!variable_list)
+ return llvm::createStringError("no variable list for implicit pointer");
+
+ VariableSP var_sp = variable_list->FindVariable(ConstString(name), false);
+ ValueObjectSP valobj_sp;
+ if (var_sp && !valobj_sp) {
+ valobj_sp =
+ frame->GetValueObjectForFrameVariable(var_sp, lldb::eNoDynamicValues);
+ }
+ if (!valobj_sp) {
+ return llvm::createStringError("invalid variable path '{0}'", name);
+ }
+ valobj_sp->UpdateValueIfNeeded(false);
+ lldb::addr_t base_addr = valobj_sp->GetValue().GetScalar().ULongLong(0);
+ if (base_addr == LLDB_INVALID_ADDRESS) {
+ return llvm::createStringError("invalid base address");
+ }
+
+ valobj_sp->GetValue().setImplictPointerDIEoffset(die_offset);
+ valobj_sp->GetValue().setImplictPointerOffset(offset);
+ return valobj_sp->GetValue();
+}
+
llvm::Expected<Value> DWARFExpressionList::Evaluate(
ExecutionContext *exe_ctx, RegisterContext *reg_ctx,
lldb::addr_t func_load_addr, const Value *initial_value_ptr,
@@ -233,7 +288,19 @@ llvm::Expected<Value> DWARFExpressionList::Evaluate(
}
expr.GetExpressionData(data);
reg_kind = expr.GetRegisterKind();
- return DWARFExpression::Evaluate(exe_ctx, reg_ctx, module_sp, data,
- m_dwarf_cu, reg_kind, initial_value_ptr,
- object_address_ptr);
+ llvm::Expected<Value> maybe_value = DWARFExpression::Evaluate(
+ exe_ctx, reg_ctx, module_sp, data, m_dwarf_cu, reg_kind,
+ initial_value_ptr, object_address_ptr);
+ if (maybe_value) {
+ Value &value = *maybe_value;
+ if (value.getImplictPointerDIEoffset() != 0) {
+ auto resolved = ResolveImplicitPointerValue(
+ exe_ctx, m_dwarf_cu, value.getImplictPointerDIEoffset(),
+ value.getImplictPointerOffset());
+ if (!resolved)
+ return resolved.takeError();
+ maybe_value = *resolved;
+ }
+ }
+ return maybe_value;
}
diff --git a/lldb/source/Expression/Materializer.cpp b/lldb/source/Expression/Materializer.cpp
index 8d48b5e50041c..710a26a1adcee 100644
--- a/lldb/source/Expression/Materializer.cpp
+++ b/lldb/source/Expression/Materializer.cpp
@@ -540,8 +540,9 @@ class EntityVariableBase : public Materializer::Entity {
return;
}
- if (data.GetByteSize() <
- llvm::expectedToOptional(GetByteSize(scope)).value_or(0)) {
+ if ((data.GetByteSize() <
+ llvm::expectedToOptional(GetByteSize(scope)).value_or(0)) &&
+ !valobj_sp->GetVariable()->IsImplicitPointer()) {
if (data.GetByteSize() == 0 && !LocationExpressionIsValid()) {
err = Status::FromErrorStringWithFormat(
"the variable '%s' has no location, "
@@ -599,10 +600,17 @@ class EntityVariableBase : public Materializer::Entity {
return;
}
+ lldb::addr_t tmp_allocation = m_temporary_allocation;
+ if (valobj_sp->GetVariable()->IsImplicitPointer()) {
+ // If the variable is an implicit pointer, we need to write the
+ // address of the temporary region into the memory location
+ // that the implicit pointer points to.
+ uint64_t ptr_offset = valobj_sp->GetValue().getImplictPointerOffset();
+ MallocTmp(map, tmp_allocation, byte_align, ptr_offset, err);
+ }
Status pointer_write_error;
- map.WritePointerToMemory(load_addr, m_temporary_allocation,
- pointer_write_error);
+ map.WritePointerToMemory(load_addr, tmp_allocation, pointer_write_error);
if (!pointer_write_error.Success()) {
err = Status::FromErrorStringWithFormat(
@@ -613,6 +621,24 @@ class EntityVariableBase : public Materializer::Entity {
}
}
+void MallocTmp(IRMemoryMap &map, lldb::addr_t &allocation,
+ size_t byte_align, uint64_t offset, Status &err) {
+ Status alloc_error;
+ const bool zero_memory = false;
+ allocation = map.Malloc(sizeof(lldb::addr_t), byte_align,
+ lldb::ePermissionsReadable | lldb::ePermissionsWritable,
+ IRMemoryMap::eAllocationPolicyMirror,
+ zero_memory, alloc_error);
+ if (!alloc_error.Success()) {
+ err = Status::FromErrorStringWithFormat(
+ "Couldn't allocate a temporary region for %s: %s",
+ GetName().AsCString(), alloc_error.AsCString());
+ return;
+ }
+ Status pointer_write_error;
+ map.WritePointerToMemory(allocation, m_temporary_allocation + offset,
+ pointer_write_error);
+}
void Dematerialize(lldb::StackFrameSP &frame_sp, IRMemoryMap &map,
lldb::addr_t process_address, lldb::addr_t frame_top,
lldb::addr_t frame_bottom, Status &err) override {
@@ -659,15 +685,15 @@ class EntityVariableBase : public Materializer::Entity {
bool actually_write = true;
if (m_original_data) {
- if ((data.GetByteSize() == m_original_data->GetByteSize()) &&
+ if (((data.GetByteSize() == m_original_data->GetByteSize()) &&
!memcmp(m_original_data->GetBytes(), data.GetDataStart(),
- data.GetByteSize())) {
+ data.GetByteSize())) ||
+ valobj_sp->GetVariable()->IsImplicitPointer()) {
actually_write = false;
}
}
Status set_error;
-
if (actually_write) {
valobj_sp->SetData(data, set_error);
diff --git a/lldb/source/Symbol/Variable.cpp b/lldb/source/Symbol/Variable.cpp
index 8244725aba545..745dece41ac2f 100644
--- a/lldb/source/Symbol/Variable.cpp
+++ b/lldb/source/Symbol/Variable.cpp
@@ -43,13 +43,13 @@ Variable::Variable(lldb::user_id_t uid, const char *name, const char *mangled,
const RangeList &scope_range, Declaration *decl_ptr,
const DWARFExpressionList &location_list, bool external,
bool artificial, bool location_is_constant_data,
- bool static_member)
+ bool static_member, bool is_implicit_pointer)
: UserID(uid), m_name(name), m_mangled(ConstString(mangled)),
m_symfile_type_sp(symfile_type_sp), m_scope(scope),
m_owner_scope(context), m_scope_range(scope_range),
m_declaration(decl_ptr), m_location_list(location_list), m_external(external),
m_artificial(artificial), m_loc_is_const_data(location_is_constant_data),
- m_static_member(static_member) {}
+ m_static_member(static_member), m_is_implicit_pointer(is_implicit_pointer) {}
Variable::~Variable() = default;
diff --git a/lldb/source/ValueObject/ValueObjectVariable.cpp b/lldb/source/ValueObject/ValueObjectVariable.cpp
index 12a84f9f2ed74..1d87dfae5138f 100644
--- a/lldb/source/ValueObject/ValueObjectVariable.cpp
+++ b/lldb/source/ValueObject/ValueObjectVariable.cpp
@@ -165,12 +165,17 @@ bool ValueObjectVariable::UpdateValue() {
if (maybe_value) {
m_value = *maybe_value;
m_resolved_value = m_value;
- m_value.SetContext(Value::ContextType::Variable, variable);
-
- CompilerType compiler_type = GetCompilerType();
- if (compiler_type.IsValid())
- m_value.SetCompilerType(compiler_type);
-
+ CompilerType compiler_type;
+ if (m_value.getImplictPointerDIEoffset() != 0) {
+ compiler_type = m_value.GetCompilerType();
+ variable->SetIsImplicitPointer(true);
+ m_override_type = compiler_type;
+ } else {
+ m_value.SetContext(Value::ContextType::Variable, variable);
+ compiler_type = GetCompilerType();
+ if (compiler_type.IsValid())
+ m_value.SetCompilerType(compiler_type);
+ }
Value::ValueType value_type = m_value.GetValueType();
// The size of the buffer within m_value can be less than the size
diff --git a/llvm/include/llvm/BinaryFormat/Dwarf.def b/llvm/include/llvm/BinaryFormat/Dwarf.def
index e52324a8ebc12..b1b406cbebf97 100644
--- a/llvm/include/llvm/BinaryFormat/Dwarf.def
+++ b/llvm/include/llvm/BinaryFormat/Dwarf.def
@@ -887,6 +887,8 @@ HANDLE_DW_OP(0xed, WASM_location, -1, -1, 0, WASM)
HANDLE_DW_OP(0xee, WASM_location_int, -1, -1, 0, WASM)
// Historic and not implemented in LLVM.
HANDLE_DW_OP(0xf0, APPLE_uninit, -1, -1, 0, APPLE)
+// The GNU implicit pointer extension.
+HANDLE_DW_OP(0xf2, GNU_implicit_pointer, 2, 0, 5, GNU)
// The GNU entry value extension.
HANDLE_DW_OP(0xf3, GNU_entry_value, 2, 0, 0, GNU)
HANDLE_DW_OP(0xf8, PGI_omp_thread_num, -1, -1, 0, PGI)
diff --git a/llvm/lib/DebugInfo/DWARF/DWARFExpression.cpp b/llvm/lib/DebugInfo/DWARF/DWARFExpression.cpp
index 2ae5ff3efc8c5..17e816a1e4552 100644
--- a/llvm/lib/DebugInfo/DWARF/DWARFExpression.cpp
+++ b/llvm/lib/DebugInfo/DWARF/DWARFExpression.cpp
@@ -104,6 +104,8 @@ static std::vector<Desc> getOpDescriptions() {
Descriptions[DW_OP_GNU_addr_index] = Desc(Op::Dwarf4, Op::SizeLEB);
Descriptions[DW_OP_GNU_const_index] = Desc(Op::Dwarf4, Op::SizeLEB);
Descriptions[DW_OP_GNU_entry_value] = Desc(Op::Dwarf4, Op::SizeLEB);
+ Descriptions[DW_OP_GNU_implicit_pointer] =
+ Desc(Op::Dwarf4, Op::SizeRefAddr, Op::SignedSizeLEB);
// This Description acts as a marker that getSubOpDesc must be called
// to fetch the final Description for the operation. Each such final
// Description must share the same first SizeSubOpLEB operand.
diff --git a/llvm/lib/DebugInfo/LogicalView/Core/LVLocation.cpp b/llvm/lib/DebugInfo/LogicalView/Core/LVLocation.cpp
index 3c078d8ee74b8..7c1a025e9a4b9 100644
--- a/llvm/lib/DebugInfo/LogicalView/Core/LVLocation.cpp
+++ b/llvm/lib/DebugInfo/LogicalView/Core/LVLocation.cpp
@@ -284,6 +284,7 @@ std::string LVOperation::getOperandsDWARFInfo() {
case dwarf::DW_OP_implicit_value:
Stream << "TODO: DW_OP_implicit_value";
break;
+ case dwarf::DW_OP_GNU_implicit_pointer:
case dwarf::DW_OP_implicit_pointer:
Stream << "implicit_pointer DIE offset " << hexString(Operands[0]) << " "
<< int(Operands[1]);
More information about the llvm-commits
mailing list