[Lldb-commits] [lldb] [LLDB] Support for DW_OP_Implicit_pointer (PR #203182)
via lldb-commits
lldb-commits at lists.llvm.org
Thu Jun 11 05:19:28 PDT 2026
https://github.com/Thrrreeee updated https://github.com/llvm/llvm-project/pull/203182
>From 3a6870bcdb1270e2f3a3cad40e9034e2e34ac384 Mon Sep 17 00:00:00 2001
From: shijinrui <shijinrui at bytedance.com>
Date: Thu, 11 Jun 2026 20:19:03 +0800
Subject: [PATCH] support dw_op_implicit_pointer
---
lldb/include/lldb/Core/Value.h | 19 ++
.../include/lldb/Expression/DWARFExpression.h | 1 +
lldb/include/lldb/Symbol/SymbolFile.h | 9 +
lldb/source/Core/Value.cpp | 12 +-
.../DataFormatters/ValueObjectPrinter.cpp | 2 +
lldb/source/Expression/DWARFExpression.cpp | 56 +++-
.../Plugins/SymbolFile/DWARF/DWARFUnit.h | 3 +
.../SymbolFile/DWARF/SymbolFileDWARF.cpp | 63 ++++-
.../SymbolFile/DWARF/SymbolFileDWARF.h | 6 +
lldb/source/Target/ABI.cpp | 3 +
lldb/source/ValueObject/ValueObject.cpp | 46 ++++
.../DWARF/x86/DW_OP_implicit_pointer.s | 243 ++++++++++++++++++
.../Expression/DWARFExpressionTest.cpp | 57 +++-
13 files changed, 497 insertions(+), 23 deletions(-)
create mode 100644 lldb/test/Shell/SymbolFile/DWARF/x86/DW_OP_implicit_pointer.s
diff --git a/lldb/include/lldb/Core/Value.h b/lldb/include/lldb/Core/Value.h
index 3714621b469ec..0fc5867fb71f8 100644
--- a/lldb/include/lldb/Core/Value.h
+++ b/lldb/include/lldb/Core/Value.h
@@ -37,6 +37,11 @@ namespace lldb_private {
class Value {
public:
+ struct ImplicitPointerInfo {
+ uint64_t die_offset = 0;
+ int64_t byte_offset = 0;
+ };
+
/// Type that describes Value::m_value.
enum class ValueType {
Invalid = -1,
@@ -93,6 +98,18 @@ class Value {
m_context_type = ContextType::Invalid;
}
+ bool IsImplicitPointer() const { return m_is_implicit_pointer; }
+
+ ImplicitPointerInfo GetImplicitPointerInfo() const {
+ return m_implicit_pointer_info;
+ }
+
+ void SetImplicitPointer(ImplicitPointerInfo implicit_pointer) {
+ m_implicit_pointer_info = implicit_pointer;
+ m_is_implicit_pointer = true;
+ m_value_type = ValueType::Invalid;
+ }
+
void SetContext(ContextType context_type, void *p) {
m_context_type = context_type;
m_context = p;
@@ -182,6 +199,8 @@ class Value {
ValueType m_value_type = ValueType::Scalar;
ContextType m_context_type = ContextType::Invalid;
DataBufferHeap m_data_buffer;
+ ImplicitPointerInfo m_implicit_pointer_info;
+ bool m_is_implicit_pointer = false;
};
class ValueList {
diff --git a/lldb/include/lldb/Expression/DWARFExpression.h b/lldb/include/lldb/Expression/DWARFExpression.h
index b1895690985a7..6d2b7d05538d6 100644
--- a/lldb/include/lldb/Expression/DWARFExpression.h
+++ b/lldb/include/lldb/Expression/DWARFExpression.h
@@ -44,6 +44,7 @@ class DWARFExpression {
virtual uint16_t GetVersion() const = 0;
virtual dw_addr_t GetBaseAddress() const = 0;
+ virtual uint8_t GetDwarfOffsetByteSize() const = 0;
virtual uint8_t GetAddressByteSize() const = 0;
virtual llvm::Expected<std::pair<uint64_t, bool>>
GetDIEBitSizeAndSign(uint64_t relative_die_offset) const = 0;
diff --git a/lldb/include/lldb/Symbol/SymbolFile.h b/lldb/include/lldb/Symbol/SymbolFile.h
index ae6504c016d7b..924fc61a69833 100644
--- a/lldb/include/lldb/Symbol/SymbolFile.h
+++ b/lldb/include/lldb/Symbol/SymbolFile.h
@@ -13,6 +13,7 @@
#include "lldb/Core/ModuleList.h"
#include "lldb/Core/PluginInterface.h"
#include "lldb/Core/SourceLocationSpec.h"
+#include "lldb/Core/Value.h"
#include "lldb/Symbol/CompilerDecl.h"
#include "lldb/Symbol/CompilerDeclContext.h"
#include "lldb/Symbol/CompilerType.h"
@@ -209,6 +210,14 @@ class SymbolFile : public PluginInterface {
virtual size_t ParseVariablesForContext(const SymbolContext &sc) = 0;
virtual Type *ResolveTypeUID(lldb::user_id_t type_uid) = 0;
+ virtual lldb::ValueObjectSP
+ ResolveImplicitPointer(const Value::ImplicitPointerInfo &implicit_info,
+ CompilerType pointee_type,
+ ExecutionContextScope *exe_scope,
+ Variable *context_var) {
+ return nullptr;
+ }
+
/// The characteristics of an array type.
struct ArrayInfo {
int64_t first_index = 0;
diff --git a/lldb/source/Core/Value.cpp b/lldb/source/Core/Value.cpp
index f9e65d397f05f..24c5a6fda1978 100644
--- a/lldb/source/Core/Value.cpp
+++ b/lldb/source/Core/Value.cpp
@@ -55,7 +55,9 @@ 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(),
+ m_implicit_pointer_info(v.m_implicit_pointer_info),
+ m_is_implicit_pointer(v.m_is_implicit_pointer) {
const uintptr_t rhs_value =
(uintptr_t)v.m_value.ULongLong(LLDB_INVALID_ADDRESS);
if ((rhs_value != 0) &&
@@ -74,6 +76,8 @@ 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;
+ m_implicit_pointer_info = rhs.m_implicit_pointer_info;
+ m_is_implicit_pointer = rhs.m_is_implicit_pointer;
const uintptr_t rhs_value =
(uintptr_t)rhs.m_value.ULongLong(LLDB_INVALID_ADDRESS);
if ((rhs_value != 0) &&
@@ -89,12 +93,16 @@ Value &Value::operator=(const Value &rhs) {
void Value::SetBytes(const void *bytes, int len) {
m_value_type = ValueType::HostAddress;
+ m_implicit_pointer_info = {};
+ m_is_implicit_pointer = false;
m_data_buffer.CopyData(bytes, len);
m_value = (uintptr_t)m_data_buffer.GetBytes();
}
void Value::AppendBytes(const void *bytes, int len) {
m_value_type = ValueType::HostAddress;
+ m_implicit_pointer_info = {};
+ m_is_implicit_pointer = false;
m_data_buffer.AppendData(bytes, len);
m_value = (uintptr_t)m_data_buffer.GetBytes();
}
@@ -639,6 +647,8 @@ void Value::Clear() {
m_value_type = ValueType::Scalar;
m_context = nullptr;
m_context_type = ContextType::Invalid;
+ m_implicit_pointer_info = {};
+ m_is_implicit_pointer = false;
m_data_buffer.Clear();
}
diff --git a/lldb/source/DataFormatters/ValueObjectPrinter.cpp b/lldb/source/DataFormatters/ValueObjectPrinter.cpp
index 002638024c64b..324df9713356b 100644
--- a/lldb/source/DataFormatters/ValueObjectPrinter.cpp
+++ b/lldb/source/DataFormatters/ValueObjectPrinter.cpp
@@ -399,6 +399,8 @@ void ValueObjectPrinter::GetValueSummaryError(std::string &value,
const char *err_cstr = valobj.GetError().AsCString();
if (err_cstr)
error.assign(err_cstr);
+ else if (valobj.GetValue().IsImplicitPointer())
+ error.assign("invalid value");
if (!ShouldPrintValueObject())
return;
diff --git a/lldb/source/Expression/DWARFExpression.cpp b/lldb/source/Expression/DWARFExpression.cpp
index dd436e0c8afd9..b45c480a25ef4 100644
--- a/lldb/source/Expression/DWARFExpression.cpp
+++ b/lldb/source/Expression/DWARFExpression.cpp
@@ -219,7 +219,6 @@ GetOpcodeDataSize(const DataExtractor &data, const lldb::offset_t data_offset,
case DW_OP_APPLE_uninit:
case DW_OP_PGI_omp_thread_num:
case DW_OP_hi_user:
- case DW_OP_GNU_implicit_pointer:
break;
case DW_OP_addr:
@@ -419,11 +418,12 @@ GetOpcodeDataSize(const DataExtractor &data, const lldb::offset_t data_offset,
return offset - data_offset;
}
- case DW_OP_implicit_pointer: // 0xa0 4-byte (or 8-byte for DWARF 64) constant
- // + LEB128
+ case DW_OP_implicit_pointer: // 0xa0 4-byte (or 8-byte for DWARF64) DIE offset
+ case DW_OP_GNU_implicit_pointer: // 0xf2 4-byte (or 8-byte for DWARF64) DIE
+ // offset followed by SLEB128 byte offset.
{
data.Skip_LEB128(&offset);
- return (dwarf_cu ? dwarf_cu->GetAddressByteSize() : 4) + offset -
+ return (dwarf_cu ? dwarf_cu->GetDwarfOffsetByteSize() : 4) + offset -
data_offset;
}
@@ -943,6 +943,9 @@ static llvm::Error Evaluate_DW_OP_deref(EvalContext &eval_ctx,
// Deref a register or implicit location and truncate the value to `size`
// bytes. See the corresponding comment in DW_OP_deref for more details on
// why we deref these locations this way.
+ if (eval_ctx.stack.back().IsImplicitPointer())
+ return llvm::createStringError("cannot dereference an implicit pointer");
+
if (eval_ctx.loc_desc_kind == Register ||
eval_ctx.loc_desc_kind == Implicit) {
// Reset context to default values.
@@ -1077,6 +1080,10 @@ static llvm::Error Evaluate_DW_OP_piece(EvalContext &eval_ctx,
if (piece_byte_size == 0)
return llvm::Error::success();
+ if (eval_ctx.pieces.IsImplicitPointer())
+ return llvm::createStringError(
+ "DW_OP_piece cannot combine DW_OP_implicit_pointer with other pieces");
+
Value curr_piece;
if (eval_ctx.stack.empty()) {
@@ -1100,6 +1107,16 @@ static llvm::Error Evaluate_DW_OP_piece(EvalContext &eval_ctx,
UpdateValueTypeFromLocationDescription(eval_ctx, piece_locdesc,
&curr_piece_source_value);
+ if (curr_piece_source_value.IsImplicitPointer()) {
+ if (eval_ctx.pieces.GetBuffer().GetByteSize() != 0)
+ return llvm::createStringError(
+ "DW_OP_piece cannot combine DW_OP_implicit_pointer with other "
+ "pieces");
+ eval_ctx.pieces = curr_piece_source_value;
+ eval_ctx.op_piece_offset += piece_byte_size;
+ return llvm::Error::success();
+ }
+
const Value::ValueType curr_piece_source_value_type =
curr_piece_source_value.GetValueType();
Scalar &scalar = curr_piece_source_value.GetScalar();
@@ -1299,7 +1316,21 @@ llvm::Expected<Value> DWARFExpression::Evaluate(
const Value *object_address_ptr) {
uint32_t address_size = opcodes.GetAddressByteSize();
llvm::DataExtractor expr_data = opcodes.GetAsLLVM();
- llvm::DWARFExpression expr(expr_data, address_size);
+ std::optional<DwarfFormat> dwarf_format = DWARF32;
+ if (dwarf_cu) {
+ switch (dwarf_cu->GetDwarfOffsetByteSize()) {
+ case 4:
+ dwarf_format = DWARF32;
+ break;
+ case 8:
+ dwarf_format = DWARF64;
+ break;
+ default:
+ dwarf_format = std::nullopt;
+ break;
+ }
+ }
+ llvm::DWARFExpression expr(expr_data, address_size, dwarf_format);
if (expr_data.size() == 0)
return llvm::createStringError(
@@ -1853,10 +1884,16 @@ llvm::Expected<Value> DWARFExpression::Evaluate(
break;
}
- case DW_OP_implicit_pointer: {
+ case DW_OP_implicit_pointer:
+ case DW_OP_GNU_implicit_pointer: {
eval_ctx.loc_desc_kind = Implicit;
- return llvm::createStringError("could not evaluate %s",
- DW_OP_value_to_name(opcode));
+ uint64_t die_offset = op->getRawOperand(0);
+ int64_t byte_offset = static_cast<int64_t>(op->getRawOperand(1));
+
+ Value result;
+ result.SetImplicitPointer({die_offset, byte_offset});
+ stack.push_back(result);
+ break;
}
case DW_OP_push_object_address:
@@ -1981,7 +2018,8 @@ llvm::Expected<Value> DWARFExpression::Evaluate(
if (stack.empty()) {
// Nothing on the stack, check if we created a piece value from DW_OP_piece
// or DW_OP_bit_piece opcodes
- if (eval_ctx.pieces.GetBuffer().GetByteSize())
+ if (eval_ctx.pieces.GetBuffer().GetByteSize() ||
+ eval_ctx.pieces.IsImplicitPointer())
return eval_ctx.pieces;
return llvm::createStringError("stack empty after evaluation");
diff --git a/lldb/source/Plugins/SymbolFile/DWARF/DWARFUnit.h b/lldb/source/Plugins/SymbolFile/DWARF/DWARFUnit.h
index 6fde9af57fa8b..8170cd3bccc14 100644
--- a/lldb/source/Plugins/SymbolFile/DWARF/DWARFUnit.h
+++ b/lldb/source/Plugins/SymbolFile/DWARF/DWARFUnit.h
@@ -122,6 +122,9 @@ class DWARFUnit : public DWARFExpression::Delegate, public UserID {
const llvm::dwarf::FormParams &GetFormParams() const {
return m_header.getFormParams();
}
+ uint8_t GetDwarfOffsetByteSize() const override {
+ return m_header.getDwarfOffsetByteSize();
+ }
const llvm::DWARFAbbreviationDeclarationSet *GetAbbreviations() const;
dw_offset_t GetAbbrevOffset() const;
uint8_t GetAddressByteSize() const override {
diff --git a/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.cpp b/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.cpp
index c81fd1c83be85..bf9b8f1c1933f 100644
--- a/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.cpp
+++ b/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.cpp
@@ -60,7 +60,10 @@
#include "lldb/Symbol/TypeMap.h"
#include "lldb/Symbol/TypeSystem.h"
#include "lldb/Symbol/VariableList.h"
+#include "lldb/ValueObject/ValueObjectConstResult.h"
+#include "lldb/ValueObject/ValueObjectVariable.h"
+#include "lldb/Target/ExecutionContextScope.h"
#include "lldb/Target/Language.h"
#include "lldb/Target/Target.h"
@@ -89,6 +92,7 @@
#include "llvm/Support/FormatVariadic.h"
#include <algorithm>
+#include <cstdint>
#include <map>
#include <memory>
#include <optional>
@@ -96,7 +100,7 @@
#include <cctype>
#include <cstring>
-//#define ENABLE_DEBUG_PRINTF // COMMENT OUT THIS LINE PRIOR TO CHECKIN
+// #define ENABLE_DEBUG_PRINTF // COMMENT OUT THIS LINE PRIOR TO CHECKIN
#ifdef ENABLE_DEBUG_PRINTF
#include <cstdio>
@@ -3443,6 +3447,63 @@ size_t SymbolFileDWARF::ParseVariablesForContext(const SymbolContext &sc) {
return 0;
}
+lldb::ValueObjectSP SymbolFileDWARF::ResolveImplicitPointer(
+ const Value::ImplicitPointerInfo &implicit_info, CompilerType pointee_type,
+ ExecutionContextScope *exe_scope, Variable *context_var) {
+ if (!pointee_type || !exe_scope)
+ return nullptr;
+
+ SymbolContext sc;
+ if (context_var)
+ context_var->CalculateSymbolContext(&sc);
+ if (!sc.comp_unit)
+ if (StackFrameSP frame_sp = exe_scope->CalculateStackFrame())
+ sc = frame_sp->GetSymbolContext(eSymbolContextCompUnit |
+ eSymbolContextFunction |
+ eSymbolContextBlock);
+
+ DWARFDIE target_die =
+ DebugInfo().GetDIE(DIERef::Section::DebugInfo, implicit_info.die_offset);
+ if (!target_die)
+ return nullptr;
+
+ if (!sc.comp_unit) {
+ if (DWARFUnit *dwarf_cu = target_die.GetCU())
+ if (auto *compile_unit = llvm::dyn_cast<DWARFCompileUnit>(dwarf_cu))
+ sc.comp_unit = GetCompUnitForDWARFCompUnit(*compile_unit);
+ }
+
+ VariableSP target_var_sp = ParseVariableDIECached(sc, target_die);
+ if (!target_var_sp)
+ return nullptr;
+
+ ValueObjectSP target_valobj_sp =
+ ValueObjectVariable::Create(exe_scope, target_var_sp);
+ if (!target_valobj_sp || !target_valobj_sp->UpdateValueIfNeeded(false))
+ return nullptr;
+
+ DataExtractor target_data;
+ Status target_error;
+ target_valobj_sp->GetData(target_data, target_error);
+ if (target_error.Fail() || target_data.GetByteSize() == 0)
+ return nullptr;
+
+ if (implicit_info.byte_offset < 0 ||
+ static_cast<uint64_t>(implicit_info.byte_offset) >
+ target_data.GetByteSize())
+ return nullptr;
+
+ lldb::offset_t data_offset = implicit_info.byte_offset;
+ lldb::offset_t data_size = target_data.GetByteSize() - data_offset;
+ if (auto type_size =
+ llvm::expectedToOptional(pointee_type.GetByteSize(exe_scope)))
+ data_size = std::min<lldb::offset_t>(data_size, *type_size);
+
+ DataExtractor pointee_data(target_data, data_offset, data_size);
+ return ValueObjectConstResult::Create(exe_scope, pointee_type, ConstString(),
+ pointee_data, LLDB_INVALID_ADDRESS);
+}
+
VariableSP SymbolFileDWARF::ParseVariableDIECached(const SymbolContext &sc,
const DWARFDIE &die) {
if (!die)
diff --git a/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.h b/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.h
index 21a190d1b38f0..197ecc06716b9 100644
--- a/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.h
+++ b/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.h
@@ -136,6 +136,12 @@ class SymbolFileDWARF : public SymbolFileCommon {
size_t ParseVariablesForContext(const SymbolContext &sc) override;
+ lldb::ValueObjectSP
+ ResolveImplicitPointer(const Value::ImplicitPointerInfo &implicit_info,
+ CompilerType pointee_type,
+ ExecutionContextScope *exe_scope,
+ Variable *context_var) override;
+
std::optional<ArrayInfo>
GetDynamicArrayInfoForUID(lldb::user_id_t type_uid,
const ExecutionContext *exe_ctx) override;
diff --git a/lldb/source/Target/ABI.cpp b/lldb/source/Target/ABI.cpp
index b7b45f9f4c44b..ae136bf749453 100644
--- a/lldb/source/Target/ABI.cpp
+++ b/lldb/source/Target/ABI.cpp
@@ -111,6 +111,9 @@ ValueObjectSP ABI::GetReturnValueObject(Thread &thread, CompilerType &ast_type,
const Value &result_value = live_valobj_sp->GetValue();
+ if (result_value.IsImplicitPointer())
+ return {};
+
switch (result_value.GetValueType()) {
case Value::ValueType::Invalid:
return {};
diff --git a/lldb/source/ValueObject/ValueObject.cpp b/lldb/source/ValueObject/ValueObject.cpp
index 32332c2962e87..d89b8643f2522 100644
--- a/lldb/source/ValueObject/ValueObject.cpp
+++ b/lldb/source/ValueObject/ValueObject.cpp
@@ -23,6 +23,7 @@
#include "lldb/Symbol/CompileUnit.h"
#include "lldb/Symbol/CompilerType.h"
#include "lldb/Symbol/SymbolContext.h"
+#include "lldb/Symbol/SymbolFile.h"
#include "lldb/Symbol/Type.h"
#include "lldb/Symbol/Variable.h"
#include "lldb/Target/ABI.h"
@@ -2393,6 +2394,18 @@ ValueObjectSP ValueObject::GetValueForExpressionPath_Impl(
*final_result = ValueObject::eExpressionPathEndResultTypeInvalid;
return ValueObjectSP();
}
+ root->UpdateValueIfNeeded(false);
+ if (root->GetValue().IsImplicitPointer()) {
+ Status error;
+ root = DereferenceValueOrAlternate(
+ *root, options.m_synthetic_children_traversal, error);
+ if (error.Fail() || !root) {
+ *reason_to_stop =
+ ValueObject::eExpressionPathScanEndReasonDereferencingFailed;
+ *final_result = ValueObject::eExpressionPathEndResultTypeInvalid;
+ return ValueObjectSP();
+ }
+ }
}
[[fallthrough]];
case '.': // or fallthrough from ->
@@ -2817,6 +2830,39 @@ ValueObjectSP ValueObject::Dereference(Status &error) {
if (m_deref_valobj)
return m_deref_valobj->GetSP();
+ UpdateValueIfNeeded(false);
+ if (m_value.IsImplicitPointer()) {
+ CompilerType pointee_type = GetCompilerType().GetPointeeType();
+ if (!pointee_type) {
+ error = Status::FromErrorString("implicit pointer has no pointee type");
+ return ValueObjectSP();
+ }
+
+ ModuleSP module_sp = GetModule();
+ SymbolFile *symbol_file = module_sp ? module_sp->GetSymbolFile() : nullptr;
+ if (!symbol_file) {
+ error = Status::FromErrorString(
+ "cannot resolve DW_OP_implicit_pointer without symbol file");
+ return ValueObjectSP();
+ }
+
+ ExecutionContext exe_ctx(GetExecutionContextRef());
+ Value::ImplicitPointerInfo implicit_pointer =
+ m_value.GetImplicitPointerInfo();
+ ValueObjectSP result_sp = symbol_file->ResolveImplicitPointer(
+ implicit_pointer, pointee_type, exe_ctx.GetBestExecutionContextScope(),
+ GetVariable().get());
+ if (!result_sp) {
+ error = Status::FromErrorStringWithFormat(
+ "cannot resolve DW_OP_implicit_pointer target DIE 0x%" PRIx64,
+ implicit_pointer.die_offset);
+ return ValueObjectSP();
+ }
+
+ error.Clear();
+ return result_sp;
+ }
+
std::string deref_name_str;
uint32_t deref_byte_size = 0;
int32_t deref_byte_offset = 0;
diff --git a/lldb/test/Shell/SymbolFile/DWARF/x86/DW_OP_implicit_pointer.s b/lldb/test/Shell/SymbolFile/DWARF/x86/DW_OP_implicit_pointer.s
new file mode 100644
index 0000000000000..1e4dc45733775
--- /dev/null
+++ b/lldb/test/Shell/SymbolFile/DWARF/x86/DW_OP_implicit_pointer.s
@@ -0,0 +1,243 @@
+# Test DW_OP_implicit_pointer for both scalar types (int, char) and struct types.
+#
+# RUN: llvm-mc -filetype=obj -o %t -triple x86_64-pc-linux %s
+# RUN: %lldb %t \
+# RUN: -o "target variable int_val" \
+# RUN: -o "target variable *int_ptr" \
+# RUN: -o "target variable char_val" \
+# RUN: -o "target variable *char_ptr" \
+# RUN: -o "target variable point" \
+# RUN: -o "target variable *struct_ptr" \
+# RUN: -o "target variable struct_ptr->y" \
+# RUN: -b | FileCheck %s
+
+# CHECK: (lldb) target variable int_val
+# CHECK: (int) int_val = 42
+
+# CHECK: (lldb) target variable *int_ptr
+# CHECK: (int) {{.*}} = 42
+
+# CHECK: (lldb) target variable char_val
+# CHECK: (char) char_val = 'A'
+
+# CHECK: (lldb) target variable *char_ptr
+# CHECK: (char) {{.*}} = 'A'
+
+# CHECK: (lldb) target variable point
+# CHECK: (Point) point = {
+# CHECK-NEXT: x = 10
+# CHECK-NEXT: y = 20
+# CHECK-NEXT: }
+
+# CHECK: (lldb) target variable *struct_ptr
+# CHECK: (Point) {{.*}} = {
+# CHECK-NEXT: x = 10
+# CHECK-NEXT: y = 20
+# CHECK-NEXT: }
+
+# CHECK: (lldb) target variable struct_ptr->y
+# CHECK: (int) {{.*}} = 20
+
+ .section .debug_abbrev,"", at progbits
+ .byte 1 # Abbrev [1] DW_TAG_compile_unit
+ .byte 17 # DW_TAG_compile_unit
+ .byte 1 # DW_CHILDREN_yes
+ .byte 0
+ .byte 0
+
+ .byte 2 # Abbrev [2] DW_TAG_variable
+ .byte 52 # DW_TAG_variable
+ .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 2 # DW_AT_location
+ .byte 24 # DW_FORM_exprloc
+ .byte 0
+ .byte 0
+
+ .byte 3 # Abbrev [3] DW_TAG_base_type
+ .byte 36 # DW_TAG_base_type
+ .byte 0 # DW_CHILDREN_no
+ .byte 3 # DW_AT_name
+ .byte 8 # DW_FORM_string
+ .byte 62 # DW_AT_encoding
+ .byte 11 # DW_FORM_data1
+ .byte 11 # DW_AT_byte_size
+ .byte 11 # DW_FORM_data1
+ .byte 0
+ .byte 0
+
+ .byte 4 # Abbrev [4] DW_TAG_pointer_type
+ .byte 15 # DW_TAG_pointer_type
+ .byte 0 # DW_CHILDREN_no
+ .byte 73 # DW_AT_type
+ .byte 19 # DW_FORM_ref4
+ .byte 0
+ .byte 0
+
+ .byte 5 # Abbrev [5] DW_TAG_structure_type
+ .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
+ .byte 0
+
+ .byte 6 # Abbrev [6] DW_TAG_member
+ .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 11 # DW_FORM_data1
+ .byte 0
+ .byte 0
+
+ .byte 0 # End of abbrev table
+
+# ===================================================================
+ .section .debug_info,"", at progbits
+.Ldummy_cu_begin:
+ .long .Ldummy_cu_end - .Ldummy_cu_start
+.Ldummy_cu_start:
+ .short 5 # DWARF version 5
+ .byte 1 # DW_UT_compile
+ .byte 8 # Address size
+ .long .debug_abbrev
+
+ .byte 1 # DW_TAG_compile_unit
+ .byte 0 # End of compile unit children
+.Ldummy_cu_end:
+
+.Lcu_begin0:
+ .long .Lcu_end0 - .Lcu_start0
+.Lcu_start0:
+ .short 5 # DWARF version 5
+ .byte 1 # DW_UT_compile
+ .byte 8 # Address size
+ .long .debug_abbrev
+
+ .byte 1 # DW_TAG_compile_unit
+
+# ---- Base types ----
+.Lint_type:
+ .byte 3 # DW_TAG_base_type
+ .asciz "int"
+ .byte 5 # DW_ATE_signed
+ .byte 4 # 4 bytes
+
+.Lchar_type:
+ .byte 3 # DW_TAG_base_type
+ .asciz "char"
+ .byte 6 # DW_ATE_signed_char
+ .byte 1 # 1 byte
+
+# ---- Pointer types ----
+.Lint_ptr_type:
+ .byte 4 # DW_TAG_pointer_type
+ .long .Lint_type - .Lcu_begin0
+
+.Lchar_ptr_type:
+ .byte 4 # DW_TAG_pointer_type
+ .long .Lchar_type - .Lcu_begin0
+
+.Lstruct_ptr_type:
+ .byte 4 # DW_TAG_pointer_type
+ .long .Lstruct_type - .Lcu_begin0
+
+# ---- struct Point { int x; int y; } ----
+.Lstruct_type:
+ .byte 5 # DW_TAG_structure_type
+ .asciz "Point"
+ .byte 8 # byte_size = 8
+
+ .byte 6 # DW_TAG_member
+ .asciz "x"
+ .long .Lint_type - .Lcu_begin0
+ .byte 0 # offset 0
+
+ .byte 6 # DW_TAG_member
+ .asciz "y"
+ .long .Lint_type - .Lcu_begin0
+ .byte 4 # offset 4
+
+ .byte 0 # end of struct children
+
+# int_val = 42
+.Lint_val:
+ .byte 2 # DW_TAG_variable
+ .asciz "int_val"
+ .long .Lint_type - .Lcu_begin0
+ .byte .Lint_val_loc_end - .Lint_val_loc_start
+.Lint_val_loc_start:
+ .byte 0x9e # DW_OP_implicit_value
+ .uleb128 4 # length = 4
+ .long 42
+.Lint_val_loc_end:
+
+# char_val = 'A' (0x41)
+.Lchar_val:
+ .byte 2 # DW_TAG_variable
+ .asciz "char_val"
+ .long .Lchar_type - .Lcu_begin0
+ .byte .Lchar_val_loc_end - .Lchar_val_loc_start
+.Lchar_val_loc_start:
+ .byte 0x9e # DW_OP_implicit_value
+ .uleb128 1 # length = 1
+ .byte 0x41 # 'A'
+.Lchar_val_loc_end:
+
+# point = {x=10, y=20}
+.Lpoint_val:
+ .byte 2 # DW_TAG_variable
+ .asciz "point"
+ .long .Lstruct_type - .Lcu_begin0
+ .byte .Lpoint_loc_end - .Lpoint_loc_start
+.Lpoint_loc_start:
+ .byte 0x9e # DW_OP_implicit_value
+ .uleb128 8 # length = 8 (two ints)
+ .long 10 # x = 10
+ .long 20 # y = 20
+.Lpoint_loc_end:
+
+# int *int_ptr -> points to int_val, offset 0
+ .byte 2 # DW_TAG_variable
+ .asciz "int_ptr"
+ .long .Lint_ptr_type - .Lcu_begin0
+ .byte .Lint_ptr_loc_end - .Lint_ptr_loc_start
+.Lint_ptr_loc_start:
+ .byte 0xa0 # DW_OP_implicit_pointer
+ .long .Lint_val # reference to int_val DIE
+ .sleb128 0 # byte offset = 0
+.Lint_ptr_loc_end:
+
+# char *char_ptr -> points to char_val, offset 0
+ .byte 2 # DW_TAG_variable
+ .asciz "char_ptr"
+ .long .Lchar_ptr_type - .Lcu_begin0
+ .byte .Lchar_ptr_loc_end - .Lchar_ptr_loc_start
+.Lchar_ptr_loc_start:
+ .byte 0xa0 # DW_OP_implicit_pointer
+ .long .Lchar_val # reference to char_val DIE
+ .sleb128 0 # byte offset = 0
+.Lchar_ptr_loc_end:
+
+# Point *struct_ptr -> points to point, offset 0
+ .byte 2 # DW_TAG_variable
+ .asciz "struct_ptr"
+ .long .Lstruct_ptr_type - .Lcu_begin0
+ .byte .Lstruct_ptr_loc_end - .Lstruct_ptr_loc_start
+.Lstruct_ptr_loc_start:
+ .byte 0xa0 # DW_OP_implicit_pointer
+ .long .Lpoint_val # reference to point DIE
+ .sleb128 0 # byte offset = 0
+.Lstruct_ptr_loc_end:
+
+ .byte 0 # End of compile unit children
+.Lcu_end0:
diff --git a/lldb/unittests/Expression/DWARFExpressionTest.cpp b/lldb/unittests/Expression/DWARFExpressionTest.cpp
index 6d4a624505390..42e47f7c14b66 100644
--- a/lldb/unittests/Expression/DWARFExpressionTest.cpp
+++ b/lldb/unittests/Expression/DWARFExpressionTest.cpp
@@ -48,11 +48,18 @@ class MockDwarfDelegate : public DWARFExpression::Delegate {
static constexpr uint16_t DEFAULT_DWARF_VERSION = 5;
static MockDwarfDelegate Dwarf5() { return MockDwarfDelegate(5); }
static MockDwarfDelegate Dwarf2() { return MockDwarfDelegate(2); }
+ static MockDwarfDelegate Dwarf64() { return MockDwarfDelegate(5, 8); }
MockDwarfDelegate() : MockDwarfDelegate(DEFAULT_DWARF_VERSION) {}
- explicit MockDwarfDelegate(uint16_t version) : m_dwarf_version(version) {}
+ explicit MockDwarfDelegate(uint16_t version,
+ uint8_t dwarf_offset_byte_size = 4)
+ : m_dwarf_version(version),
+ m_dwarf_offset_byte_size(dwarf_offset_byte_size) {}
uint16_t GetVersion() const override { return m_dwarf_version; }
+ uint8_t GetDwarfOffsetByteSize() const override {
+ return m_dwarf_offset_byte_size;
+ }
dw_addr_t GetBaseAddress() const override { return 0; }
@@ -83,6 +90,7 @@ class MockDwarfDelegate : public DWARFExpression::Delegate {
private:
uint16_t m_dwarf_version;
+ uint8_t m_dwarf_offset_byte_size;
};
/// Mock memory implementation for testing.
@@ -1020,17 +1028,42 @@ TEST(DWARFExpression, DW_OP_call_ref_unhandled) {
"unhandled opcode DW_OP_call_ref in DWARFExpression"));
}
-TEST(DWARFExpression, DW_OP_implicit_pointer_unimplemented) {
- EXPECT_THAT_EXPECTED(
- Evaluate({DW_OP_implicit_pointer, 0x00, 0x00, 0x00, 0x00, 0x00}),
- llvm::Failed());
-}
-
-TEST(DWARFExpression, DW_OP_GNU_implicit_pointer_unhandled) {
- EXPECT_THAT_EXPECTED(
- Evaluate({DW_OP_GNU_implicit_pointer, 0x00, 0x00, 0x00, 0x00, 0x00}),
- llvm::FailedWithMessage(
- "unhandled opcode DW_OP_GNU_implicit_pointer in DWARFExpression"));
+TEST(DWARFExpression, DW_OP_implicit_pointer) {
+ MockDwarfDelegate dwarf32 = MockDwarfDelegate::Dwarf5();
+ llvm::Expected<Value> result = Evaluate(
+ {DW_OP_implicit_pointer, 0x34, 0x12, 0x00, 0x00, 0x7e}, {}, &dwarf32);
+ ASSERT_THAT_EXPECTED(result, llvm::Succeeded());
+ ASSERT_TRUE(result->IsImplicitPointer());
+ Value::ImplicitPointerInfo implicit_pointer =
+ result->GetImplicitPointerInfo();
+ EXPECT_EQ(implicit_pointer.die_offset, 0x1234u);
+ EXPECT_EQ(implicit_pointer.byte_offset, -2);
+}
+
+TEST(DWARFExpression, DW_OP_GNU_implicit_pointer) {
+ MockDwarfDelegate dwarf32 = MockDwarfDelegate::Dwarf5();
+ llvm::Expected<Value> result = Evaluate(
+ {DW_OP_GNU_implicit_pointer, 0x78, 0x56, 0x00, 0x00, 0x02}, {}, &dwarf32);
+ ASSERT_THAT_EXPECTED(result, llvm::Succeeded());
+ ASSERT_TRUE(result->IsImplicitPointer());
+ Value::ImplicitPointerInfo implicit_pointer =
+ result->GetImplicitPointerInfo();
+ EXPECT_EQ(implicit_pointer.die_offset, 0x5678u);
+ EXPECT_EQ(implicit_pointer.byte_offset, 2);
+}
+
+TEST(DWARFExpression, DW_OP_implicit_pointer_DWARF64) {
+ MockDwarfDelegate dwarf64 = MockDwarfDelegate::Dwarf64();
+ llvm::Expected<Value> result =
+ Evaluate({DW_OP_implicit_pointer, 0x78, 0x56, 0x34, 0x12, 0x00, 0x00,
+ 0x00, 0x00, 0x02},
+ {}, &dwarf64);
+ ASSERT_THAT_EXPECTED(result, llvm::Succeeded());
+ ASSERT_TRUE(result->IsImplicitPointer());
+ Value::ImplicitPointerInfo implicit_pointer =
+ result->GetImplicitPointerInfo();
+ EXPECT_EQ(implicit_pointer.die_offset, 0x12345678u);
+ EXPECT_EQ(implicit_pointer.byte_offset, 2);
}
TEST(DWARFExpression, DW_OP_const_type_unhandled) {
More information about the lldb-commits
mailing list