[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