|
|
@@ -0,0 +1,5779 @@
|
|
|
+diff --git a/lldb/include/lldb/Core/Module.h b/lldb/include/lldb/Core/Module.h
|
|
|
+index dd7100c4616c..97d70daadbdc 100644
|
|
|
+--- a/lldb/include/lldb/Core/Module.h
|
|
|
++++ b/lldb/include/lldb/Core/Module.h
|
|
|
+@@ -41,6 +41,7 @@
|
|
|
+
|
|
|
+ namespace lldb_private {
|
|
|
+ class CompilerDeclContext;
|
|
|
++class DWARFEvaluatorFactory;
|
|
|
+ class Function;
|
|
|
+ class Log;
|
|
|
+ class ObjectFile;
|
|
|
+@@ -859,6 +860,8 @@ public:
|
|
|
+ /// Update the ArchSpec to a more specific variant.
|
|
|
+ bool MergeArchitecture(const ArchSpec &arch_spec);
|
|
|
+
|
|
|
++ DWARFEvaluatorFactory *GetDWARFExpressionEvaluatorFactory();
|
|
|
++
|
|
|
+ /// \class LookupInfo Module.h "lldb/Core/Module.h"
|
|
|
+ /// A class that encapsulates name lookup information.
|
|
|
+ ///
|
|
|
+@@ -985,6 +988,8 @@ protected:
|
|
|
+ m_first_file_changed_log : 1; /// See if the module was modified after it
|
|
|
+ /// was initially opened.
|
|
|
+
|
|
|
++ std::unique_ptr<DWARFEvaluatorFactory> m_dwarf_evaluator_factory;
|
|
|
++
|
|
|
+ /// Resolve a file or load virtual address.
|
|
|
+ ///
|
|
|
+ /// Tries to resolve \a vm_addr as a file address (if \a
|
|
|
+diff --git a/lldb/include/lldb/Core/PluginManager.h b/lldb/include/lldb/Core/PluginManager.h
|
|
|
+index be91929c62e1..8d876fc1fa2f 100644
|
|
|
+--- a/lldb/include/lldb/Core/PluginManager.h
|
|
|
++++ b/lldb/include/lldb/Core/PluginManager.h
|
|
|
+@@ -508,6 +508,17 @@ public:
|
|
|
+ static bool CreateSettingForStructuredDataPlugin(
|
|
|
+ Debugger &debugger, const lldb::OptionValuePropertiesSP &properties_sp,
|
|
|
+ ConstString description, bool is_global_property);
|
|
|
++
|
|
|
++ // DWARFEvaluatorFactory
|
|
|
++ static bool
|
|
|
++ RegisterPlugin(ConstString name, const char *description,
|
|
|
++ DWARFEvaluatorFactoryCreateInstance create_callback);
|
|
|
++
|
|
|
++ static bool
|
|
|
++ UnregisterPlugin(DWARFEvaluatorFactoryCreateInstance create_callback);
|
|
|
++
|
|
|
++ static DWARFEvaluatorFactoryCreateInstance
|
|
|
++ GetDWARFEvaluatorFactoryCreateCallbackAtIndex(uint32_t idx);
|
|
|
+ };
|
|
|
+
|
|
|
+ } // namespace lldb_private
|
|
|
+diff --git a/lldb/include/lldb/Expression/DWARFEvaluator.h b/lldb/include/lldb/Expression/DWARFEvaluator.h
|
|
|
+new file mode 100644
|
|
|
+index 000000000000..6811cbeae3d3
|
|
|
+--- /dev/null
|
|
|
++++ b/lldb/include/lldb/Expression/DWARFEvaluator.h
|
|
|
+@@ -0,0 +1,110 @@
|
|
|
++//===-- DWARFEvaluator.h ----------------------------------------*- C++ -*-===//
|
|
|
++//
|
|
|
++// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
|
|
++// See https://llvm.org/LICENSE.txt for license information.
|
|
|
++// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
|
|
++//
|
|
|
++//===----------------------------------------------------------------------===//
|
|
|
++
|
|
|
++#ifndef LLDB_EXPRESSION_DWARFEVALUATOR_H
|
|
|
++#define LLDB_EXPRESSION_DWARFEVALUATOR_H
|
|
|
++
|
|
|
++#include "lldb/lldb-private.h"
|
|
|
++#include <vector>
|
|
|
++
|
|
|
++namespace lldb_private {
|
|
|
++
|
|
|
++class DWARFExpression;
|
|
|
++
|
|
|
++/// \class DWARFEvaluator DWARFEvaluator.h
|
|
|
++/// "lldb/Expression/DWARFEvaluator.h" Evaluates DWARF opcodes.
|
|
|
++///
|
|
|
++class DWARFEvaluator {
|
|
|
++public:
|
|
|
++ /// Crates a DWARF location expression evaluator
|
|
|
++ ///
|
|
|
++ /// \param[in] dwarf_expression
|
|
|
++ /// The DWARF expression to evaluate.
|
|
|
++ ///
|
|
|
++ /// \param[in] exe_ctx
|
|
|
++ /// The execution context in which to evaluate the location
|
|
|
++ /// expression. The location expression may access the target's
|
|
|
++ /// memory, especially if it comes from the expression parser.
|
|
|
++ ///
|
|
|
++ /// \param[in] reg_ctx
|
|
|
++ /// An optional parameter which provides a RegisterContext for use
|
|
|
++ /// when evaluating the expression (i.e. for fetching register values).
|
|
|
++ /// Normally this will come from the ExecutionContext's StackFrame but
|
|
|
++ /// in the case where an expression needs to be evaluated while building
|
|
|
++ /// the stack frame list, this short-cut is available.
|
|
|
++ ///
|
|
|
++ /// \param[in] initial_value_ptr
|
|
|
++ /// A value to put on top of the interpreter stack before evaluating
|
|
|
++ /// the expression, if the expression is parametrized. Can be NULL.
|
|
|
++ ///
|
|
|
++ /// \param[in] object_address_ptr
|
|
|
++ ///
|
|
|
++ DWARFEvaluator(const DWARFExpression &dwarf_expression,
|
|
|
++ ExecutionContext *exe_ctx, RegisterContext *reg_ctx,
|
|
|
++ const Value *initial_value_ptr,
|
|
|
++ const Value *object_address_ptr);
|
|
|
++
|
|
|
++ /// DWARFEvaluator protocol.
|
|
|
++ /// \{
|
|
|
++
|
|
|
++ /// Evaluate the DWARF location expression
|
|
|
++ ///
|
|
|
++ /// \param[in] result
|
|
|
++ /// A value into which the result of evaluating the expression is
|
|
|
++ /// to be placed.
|
|
|
++ ///
|
|
|
++ /// \param[in] error_ptr
|
|
|
++ /// If non-NULL, used to report errors in expression evaluation.
|
|
|
++ ///
|
|
|
++ /// \return
|
|
|
++ /// True on success; false otherwise. If error_ptr is non-NULL,
|
|
|
++ /// details of the failure are provided through it.
|
|
|
++ virtual bool Evaluate(Value &result, Status *error_ptr);
|
|
|
++
|
|
|
++ /// Evaluate the DWARF location expression with the opcodes specified.
|
|
|
++ ///
|
|
|
++ /// \param[in] opcodes
|
|
|
++ /// The DWARF opcodes to evaluate.
|
|
|
++ ///
|
|
|
++ /// \param[in] result
|
|
|
++ /// A value into which the result of evaluating the expression is
|
|
|
++ /// to be placed.
|
|
|
++ ///
|
|
|
++ /// \param[in] error_ptr
|
|
|
++ /// If non-NULL, used to report errors in expression evaluation.
|
|
|
++ ///
|
|
|
++ /// \return
|
|
|
++ /// True on success; false otherwise. If error_ptr is non-NULL,
|
|
|
++ /// details of the failure are provided through it.
|
|
|
++ virtual bool Evaluate(const DataExtractor &opcodes, Value &result,
|
|
|
++ Status *error_ptr);
|
|
|
++
|
|
|
++ /// Evaluates a specific DWARF opcode in the context of a DWARF expression
|
|
|
++ virtual bool Evaluate(const uint8_t op, Process *process, StackFrame *frame,
|
|
|
++ std::vector<Value> &stack, const DataExtractor &opcodes,
|
|
|
++ lldb::offset_t &offset, Value &pieces,
|
|
|
++ uint64_t &op_piece_offset, Log *log, Status *error_ptr);
|
|
|
++
|
|
|
++ /// \}
|
|
|
++
|
|
|
++protected:
|
|
|
++ const DWARFExpression &m_dwarf_expression;
|
|
|
++ ExecutionContext *m_exe_ctx;
|
|
|
++ RegisterContext *m_reg_ctx;
|
|
|
++ const Value *m_initial_value_ptr;
|
|
|
++ const Value *m_object_address_ptr;
|
|
|
++
|
|
|
++private:
|
|
|
++ DWARFEvaluator(const DWARFEvaluator &);
|
|
|
++ const DWARFEvaluator &operator=(const DWARFEvaluator &) = delete;
|
|
|
++
|
|
|
++};
|
|
|
++
|
|
|
++} // namespace lldb_private
|
|
|
++
|
|
|
++#endif // LLDB_EXPRESSION_DWARFEVALUATOR_H
|
|
|
+diff --git a/lldb/include/lldb/Expression/DWARFEvaluatorFactory.h b/lldb/include/lldb/Expression/DWARFEvaluatorFactory.h
|
|
|
+new file mode 100644
|
|
|
+index 000000000000..f3b496c580e4
|
|
|
+--- /dev/null
|
|
|
++++ b/lldb/include/lldb/Expression/DWARFEvaluatorFactory.h
|
|
|
+@@ -0,0 +1,56 @@
|
|
|
++//===-- DWARFEvaluatorFactory.h ---------------------------------*- C++ -*-===//
|
|
|
++//
|
|
|
++// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
|
|
++// See https://llvm.org/LICENSE.txt for license information.
|
|
|
++// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
|
|
++//
|
|
|
++//===----------------------------------------------------------------------===//
|
|
|
++
|
|
|
++#ifndef LLDB_EXPRESSION_DWARFEVALUATORFACTORY_H
|
|
|
++#define LLDB_EXPRESSION_DWARFEVALUATORFACTORY_H
|
|
|
++
|
|
|
++#include "lldb/Core/PluginInterface.h"
|
|
|
++#include "lldb/Utility/ConstString.h"
|
|
|
++#include "lldb/lldb-private.h"
|
|
|
++
|
|
|
++class DWARFUnit;
|
|
|
++
|
|
|
++namespace lldb_private {
|
|
|
++
|
|
|
++class DWARFEvaluator;
|
|
|
++class DWARFExpression;
|
|
|
++
|
|
|
++/// \class DWARFEvaluatorFactory DWARFEvaluatorFactory.h
|
|
|
++/// "lldb/Expression/DWARFEvaluatorFactory.h" Factory class that allows the
|
|
|
++/// registration of platform-specific DWARF expression evaluators, used to
|
|
|
++/// handle platform-specific DWARF opcodes.
|
|
|
++class DWARFEvaluatorFactory : public PluginInterface {
|
|
|
++public:
|
|
|
++ static std::unique_ptr<DWARFEvaluatorFactory> FindPlugin(Module *module);
|
|
|
++
|
|
|
++ /// PluginInterface protocol.
|
|
|
++ /// \{
|
|
|
++ ConstString GetPluginName() override;
|
|
|
++
|
|
|
++ uint32_t GetPluginVersion() override { return 1; }
|
|
|
++ /// \}
|
|
|
++
|
|
|
++ DWARFEvaluatorFactory() {}
|
|
|
++
|
|
|
++ /// DWARFEvaluatorFactory protocol.
|
|
|
++ /// \{
|
|
|
++ virtual std::unique_ptr<DWARFEvaluator>
|
|
|
++ CreateDWARFEvaluator(const DWARFExpression &dwarf_expression,
|
|
|
++ ExecutionContext *exe_ctx, RegisterContext *reg_ctx,
|
|
|
++ const Value *initial_value_ptr,
|
|
|
++ const Value *object_address_ptr);
|
|
|
++ /// \}
|
|
|
++
|
|
|
++private:
|
|
|
++ DWARFEvaluatorFactory(const DWARFEvaluatorFactory &);
|
|
|
++ const DWARFEvaluatorFactory &operator=(const DWARFEvaluatorFactory &) = delete;
|
|
|
++};
|
|
|
++
|
|
|
++} // namespace lldb_private
|
|
|
++
|
|
|
++#endif // LLDB_EXPRESSION_DWARFEVALUATORFACTORY_H
|
|
|
+diff --git a/lldb/include/lldb/Expression/DWARFExpression.h b/lldb/include/lldb/Expression/DWARFExpression.h
|
|
|
+index 1490ac2d614a..35c741d4e6ba 100644
|
|
|
+--- a/lldb/include/lldb/Expression/DWARFExpression.h
|
|
|
++++ b/lldb/include/lldb/Expression/DWARFExpression.h
|
|
|
+@@ -120,6 +120,10 @@ public:
|
|
|
+
|
|
|
+ void SetModule(const lldb::ModuleSP &module) { m_module_wp = module; }
|
|
|
+
|
|
|
++ lldb::ModuleSP GetModule() const { return m_module_wp.lock(); }
|
|
|
++
|
|
|
++ const DWARFUnit *GetDWARFCompileUnit() const { return m_dwarf_cu; }
|
|
|
++
|
|
|
+ bool ContainsThreadLocalStorage() const;
|
|
|
+
|
|
|
+ bool LinkThreadLocalStorage(
|
|
|
+@@ -140,7 +144,7 @@ public:
|
|
|
+ lldb::addr_t func_file_addr);
|
|
|
+
|
|
|
+ /// Return the call-frame-info style register kind
|
|
|
+- int GetRegisterKind();
|
|
|
++ lldb::RegisterKind GetRegisterKind() const;
|
|
|
+
|
|
|
+ /// Set the call-frame-info style register kind
|
|
|
+ ///
|
|
|
+@@ -219,6 +223,9 @@ public:
|
|
|
+
|
|
|
+ bool MatchesOperand(StackFrame &frame, const Instruction::Operand &op);
|
|
|
+
|
|
|
++ static lldb::addr_t ReadAddressFromDebugAddrSection(const DWARFUnit *dwarf_cu,
|
|
|
++ uint32_t index);
|
|
|
++
|
|
|
+ llvm::Optional<DataExtractor>
|
|
|
+ GetLocationExpression(lldb::addr_t load_function_start,
|
|
|
+ lldb::addr_t addr) const;
|
|
|
+diff --git a/lldb/include/lldb/Target/Process.h b/lldb/include/lldb/Target/Process.h
|
|
|
+index aaa2470d2931..c15f2db52fbc 100644
|
|
|
+--- a/lldb/include/lldb/Target/Process.h
|
|
|
++++ b/lldb/include/lldb/Target/Process.h
|
|
|
+@@ -1434,7 +1434,7 @@ public:
|
|
|
+ /// vm_addr, \a buf, and \a size updated appropriately. Zero is
|
|
|
+ /// returned in the case of an error.
|
|
|
+ virtual size_t ReadMemory(lldb::addr_t vm_addr, void *buf, size_t size,
|
|
|
+- Status &error);
|
|
|
++ Status &error, ExecutionContext *exe_ctx = nullptr);
|
|
|
+
|
|
|
+ /// Read of memory from a process.
|
|
|
+ ///
|
|
|
+diff --git a/lldb/include/lldb/Target/ProcessTrace.h b/lldb/include/lldb/Target/ProcessTrace.h
|
|
|
+index 7b9d6b13dd6f..9525fc9750fd 100644
|
|
|
+--- a/lldb/include/lldb/Target/ProcessTrace.h
|
|
|
++++ b/lldb/include/lldb/Target/ProcessTrace.h
|
|
|
+@@ -59,7 +59,7 @@ public:
|
|
|
+ bool WarnBeforeDetach() const override { return false; }
|
|
|
+
|
|
|
+ size_t ReadMemory(lldb::addr_t addr, void *buf, size_t size,
|
|
|
+- Status &error) override;
|
|
|
++ Status &error, ExecutionContext *exe_ctx = nullptr) override;
|
|
|
+
|
|
|
+ size_t DoReadMemory(lldb::addr_t addr, void *buf, size_t size,
|
|
|
+ Status &error) override;
|
|
|
+diff --git a/lldb/include/lldb/lldb-forward.h b/lldb/include/lldb/lldb-forward.h
|
|
|
+index ad5298151e4a..5a3c0b27a738 100644
|
|
|
+--- a/lldb/include/lldb/lldb-forward.h
|
|
|
++++ b/lldb/include/lldb/lldb-forward.h
|
|
|
+@@ -74,6 +74,7 @@ class Disassembler;
|
|
|
+ class DumpValueObjectOptions;
|
|
|
+ class DynamicCheckerFunctions;
|
|
|
+ class DynamicLoader;
|
|
|
++class DWARFEvaluatorFactory;
|
|
|
+ class Editline;
|
|
|
+ class EmulateInstruction;
|
|
|
+ class Environment;
|
|
|
+diff --git a/lldb/include/lldb/lldb-private-interfaces.h b/lldb/include/lldb/lldb-private-interfaces.h
|
|
|
+index 2ed083ec8ae9..f4d500d198e8 100644
|
|
|
+--- a/lldb/include/lldb/lldb-private-interfaces.h
|
|
|
++++ b/lldb/include/lldb/lldb-private-interfaces.h
|
|
|
+@@ -113,6 +113,8 @@ typedef lldb::REPLSP (*REPLCreateInstance)(Status &error,
|
|
|
+ const char *repl_options);
|
|
|
+ typedef int (*ComparisonFunction)(const void *, const void *);
|
|
|
+ typedef void (*DebuggerInitializeCallback)(Debugger &debugger);
|
|
|
++typedef DWARFEvaluatorFactory *(*DWARFEvaluatorFactoryCreateInstance)(
|
|
|
++ Module *module);
|
|
|
+ /// Trace
|
|
|
+ /// \{
|
|
|
+ typedef llvm::Expected<lldb::TraceSP> (*TraceCreateInstanceForSessionFile)(
|
|
|
+diff --git a/lldb/source/Core/Module.cpp b/lldb/source/Core/Module.cpp
|
|
|
+index 19c97be15066..1647f93ec4f3 100644
|
|
|
+--- a/lldb/source/Core/Module.cpp
|
|
|
++++ b/lldb/source/Core/Module.cpp
|
|
|
+@@ -16,6 +16,7 @@
|
|
|
+ #include "lldb/Core/ModuleSpec.h"
|
|
|
+ #include "lldb/Core/SearchFilter.h"
|
|
|
+ #include "lldb/Core/Section.h"
|
|
|
++#include "lldb/Expression/DWARFEvaluatorFactory.h"
|
|
|
+ #include "lldb/Host/FileSystem.h"
|
|
|
+ #include "lldb/Host/Host.h"
|
|
|
+ #include "lldb/Host/HostInfo.h"
|
|
|
+@@ -1659,3 +1660,9 @@ bool Module::GetIsDynamicLinkEditor() {
|
|
|
+
|
|
|
+ return false;
|
|
|
+ }
|
|
|
++
|
|
|
++DWARFEvaluatorFactory *Module::GetDWARFExpressionEvaluatorFactory() {
|
|
|
++ if (!m_dwarf_evaluator_factory)
|
|
|
++ m_dwarf_evaluator_factory = DWARFEvaluatorFactory::FindPlugin(this);
|
|
|
++ return m_dwarf_evaluator_factory.get();
|
|
|
++}
|
|
|
+diff --git a/lldb/source/Core/PluginManager.cpp b/lldb/source/Core/PluginManager.cpp
|
|
|
+index fcaa868b083e..59a404d4a7e1 100644
|
|
|
+--- a/lldb/source/Core/PluginManager.cpp
|
|
|
++++ b/lldb/source/Core/PluginManager.cpp
|
|
|
+@@ -1597,3 +1597,32 @@ bool PluginManager::CreateSettingForStructuredDataPlugin(
|
|
|
+ ConstString("Settings for structured data plug-ins"), properties_sp,
|
|
|
+ description, is_global_property);
|
|
|
+ }
|
|
|
++
|
|
|
++#pragma mark DWARFEvaluator
|
|
|
++
|
|
|
++typedef PluginInstance<DWARFEvaluatorFactoryCreateInstance>
|
|
|
++ DWARFEvaluatorFactoryInstance;
|
|
|
++typedef PluginInstances<DWARFEvaluatorFactoryInstance>
|
|
|
++ DWARFEvaluatorFactoryInstances;
|
|
|
++
|
|
|
++static DWARFEvaluatorFactoryInstances &GetDWARFEvaluatorFactoryInstances() {
|
|
|
++ static DWARFEvaluatorFactoryInstances g_instances;
|
|
|
++ return g_instances;
|
|
|
++}
|
|
|
++
|
|
|
++bool PluginManager::RegisterPlugin(
|
|
|
++ ConstString name, const char *description,
|
|
|
++ DWARFEvaluatorFactoryCreateInstance create_callback) {
|
|
|
++ return GetDWARFEvaluatorFactoryInstances().RegisterPlugin(name, description,
|
|
|
++ create_callback);
|
|
|
++}
|
|
|
++
|
|
|
++bool PluginManager::UnregisterPlugin(
|
|
|
++ DWARFEvaluatorFactoryCreateInstance create_callback) {
|
|
|
++ return GetDWARFEvaluatorFactoryInstances().UnregisterPlugin(create_callback);
|
|
|
++}
|
|
|
++
|
|
|
++DWARFEvaluatorFactoryCreateInstance
|
|
|
++PluginManager::GetDWARFEvaluatorFactoryCreateCallbackAtIndex(uint32_t idx) {
|
|
|
++ return GetDWARFEvaluatorFactoryInstances().GetCallbackAtIndex(idx);
|
|
|
++}
|
|
|
+diff --git a/lldb/source/Core/Value.cpp b/lldb/source/Core/Value.cpp
|
|
|
+index fb57c0fedf04..f92d6a54de94 100644
|
|
|
+--- a/lldb/source/Core/Value.cpp
|
|
|
++++ b/lldb/source/Core/Value.cpp
|
|
|
+@@ -538,7 +538,7 @@ Status Value::GetValueAsData(ExecutionContext *exe_ctx, DataExtractor &data,
|
|
|
+
|
|
|
+ if (process) {
|
|
|
+ const size_t bytes_read =
|
|
|
+- process->ReadMemory(address, dst, byte_size, error);
|
|
|
++ process->ReadMemory(address, dst, byte_size, error, exe_ctx);
|
|
|
+ if (bytes_read != byte_size)
|
|
|
+ error.SetErrorStringWithFormat(
|
|
|
+ "read memory from 0x%" PRIx64 " failed (%u of %u bytes read)",
|
|
|
+diff --git a/lldb/source/Core/ValueObject.cpp b/lldb/source/Core/ValueObject.cpp
|
|
|
+index 9c1ba99da1d0..b15b214b2a2f 100644
|
|
|
+--- a/lldb/source/Core/ValueObject.cpp
|
|
|
++++ b/lldb/source/Core/ValueObject.cpp
|
|
|
+@@ -735,7 +735,7 @@ size_t ValueObject::GetPointeeData(DataExtractor &data, uint32_t item_idx,
|
|
|
+ if (process) {
|
|
|
+ heap_buf_ptr->SetByteSize(bytes);
|
|
|
+ size_t bytes_read = process->ReadMemory(
|
|
|
+- addr + offset, heap_buf_ptr->GetBytes(), bytes, error);
|
|
|
++ addr + offset, heap_buf_ptr->GetBytes(), bytes, error, &exe_ctx);
|
|
|
+ if (error.Success() || bytes_read > 0) {
|
|
|
+ data.SetData(data_sp);
|
|
|
+ return bytes_read;
|
|
|
+diff --git a/lldb/source/Expression/CMakeLists.txt b/lldb/source/Expression/CMakeLists.txt
|
|
|
+index bf94361dd6c1..4e76d547aeaf 100644
|
|
|
+--- a/lldb/source/Expression/CMakeLists.txt
|
|
|
++++ b/lldb/source/Expression/CMakeLists.txt
|
|
|
+@@ -1,5 +1,7 @@
|
|
|
+ add_lldb_library(lldbExpression
|
|
|
+ DiagnosticManager.cpp
|
|
|
++ DWARFEvaluator.cpp
|
|
|
++ DWARFEvaluatorFactory.cpp
|
|
|
+ DWARFExpression.cpp
|
|
|
+ Expression.cpp
|
|
|
+ ExpressionVariable.cpp
|
|
|
+diff --git a/lldb/source/Expression/DWARFEvaluator.cpp b/lldb/source/Expression/DWARFEvaluator.cpp
|
|
|
+new file mode 100644
|
|
|
+index 000000000000..06107e136197
|
|
|
+--- /dev/null
|
|
|
++++ b/lldb/source/Expression/DWARFEvaluator.cpp
|
|
|
+@@ -0,0 +1,1952 @@
|
|
|
++//===-- DWARFEvaluator.cpp ------------ -----------------------------------===//
|
|
|
++//
|
|
|
++// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
|
|
++// See https://llvm.org/LICENSE.txt for license information.
|
|
|
++// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
|
|
++//
|
|
|
++//===----------------------------------------------------------------------===//
|
|
|
++
|
|
|
++#include "lldb/Expression/DWARFEvaluator.h"
|
|
|
++#include "lldb/Expression/DWARFExpression.h"
|
|
|
++
|
|
|
++#include "lldb/Core/Module.h"
|
|
|
++#include "lldb/Core/Value.h"
|
|
|
++#include "lldb/Core/dwarf.h"
|
|
|
++
|
|
|
++#include "lldb/Utility/Log.h"
|
|
|
++#include "lldb/Utility/RegisterValue.h"
|
|
|
++
|
|
|
++#include "lldb/Target/Process.h"
|
|
|
++#include "lldb/Target/RegisterContext.h"
|
|
|
++#include "lldb/Target/StackFrame.h"
|
|
|
++
|
|
|
++#include "Plugins/SymbolFile/DWARF/DWARFUnit.h"
|
|
|
++
|
|
|
++using namespace lldb;
|
|
|
++using namespace lldb_private;
|
|
|
++
|
|
|
++DWARFEvaluator::DWARFEvaluator(const DWARFExpression &dwarf_expression,
|
|
|
++ ExecutionContext *exe_ctx,
|
|
|
++ RegisterContext *reg_ctx,
|
|
|
++ const Value *initial_value_ptr,
|
|
|
++ const Value *object_address_ptr)
|
|
|
++ : m_dwarf_expression(dwarf_expression), m_exe_ctx(exe_ctx),
|
|
|
++ m_reg_ctx(reg_ctx), m_initial_value_ptr(initial_value_ptr),
|
|
|
++ m_object_address_ptr(object_address_ptr) {}
|
|
|
++
|
|
|
++static bool ReadRegisterValueAsScalar(RegisterContext *reg_ctx,
|
|
|
++ lldb::RegisterKind reg_kind,
|
|
|
++ uint32_t reg_num, Status *error_ptr,
|
|
|
++ Value &value) {
|
|
|
++ if (reg_ctx == nullptr) {
|
|
|
++ if (error_ptr)
|
|
|
++ error_ptr->SetErrorStringWithFormat("No register context in frame.\n");
|
|
|
++ } else {
|
|
|
++ uint32_t native_reg =
|
|
|
++ reg_ctx->ConvertRegisterKindToRegisterNumber(reg_kind, reg_num);
|
|
|
++ if (native_reg == LLDB_INVALID_REGNUM) {
|
|
|
++ if (error_ptr)
|
|
|
++ error_ptr->SetErrorStringWithFormat("Unable to convert register "
|
|
|
++ "kind=%u reg_num=%u to a native "
|
|
|
++ "register number.\n",
|
|
|
++ reg_kind, reg_num);
|
|
|
++ } else {
|
|
|
++ const RegisterInfo *reg_info =
|
|
|
++ reg_ctx->GetRegisterInfoAtIndex(native_reg);
|
|
|
++ RegisterValue reg_value;
|
|
|
++ if (reg_ctx->ReadRegister(reg_info, reg_value)) {
|
|
|
++ if (reg_value.GetScalarValue(value.GetScalar())) {
|
|
|
++ value.SetValueType(Value::ValueType::Scalar);
|
|
|
++ value.SetContext(Value::ContextType::RegisterInfo,
|
|
|
++ const_cast<RegisterInfo *>(reg_info));
|
|
|
++ if (error_ptr)
|
|
|
++ error_ptr->Clear();
|
|
|
++ return true;
|
|
|
++ } else {
|
|
|
++ // If we get this error, then we need to implement a value buffer in
|
|
|
++ // the dwarf expression evaluation function...
|
|
|
++ if (error_ptr)
|
|
|
++ error_ptr->SetErrorStringWithFormat(
|
|
|
++ "register %s can't be converted to a scalar value",
|
|
|
++ reg_info->name);
|
|
|
++ }
|
|
|
++ } else {
|
|
|
++ if (error_ptr)
|
|
|
++ error_ptr->SetErrorStringWithFormat("register %s is not available",
|
|
|
++ reg_info->name);
|
|
|
++ }
|
|
|
++ }
|
|
|
++ }
|
|
|
++ return false;
|
|
|
++}
|
|
|
++
|
|
|
++static bool Evaluate_DW_OP_entry_value(std::vector<Value> &stack,
|
|
|
++ ExecutionContext *exe_ctx,
|
|
|
++ RegisterContext *reg_ctx,
|
|
|
++ const DataExtractor &opcodes,
|
|
|
++ lldb::offset_t &opcode_offset,
|
|
|
++ Status *error_ptr, Log *log) {
|
|
|
++ // DW_OP_entry_value(sub-expr) describes the location a variable had upon
|
|
|
++ // function entry: this variable location is presumed to be optimized out at
|
|
|
++ // the current PC value. The caller of the function may have call site
|
|
|
++ // information that describes an alternate location for the variable (e.g. a
|
|
|
++ // constant literal, or a spilled stack value) in the parent frame.
|
|
|
++ //
|
|
|
++ // Example (this is pseudo-code & pseudo-DWARF, but hopefully illustrative):
|
|
|
++ //
|
|
|
++ // void child(int &sink, int x) {
|
|
|
++ // ...
|
|
|
++ // /* "x" gets optimized out. */
|
|
|
++ //
|
|
|
++ // /* The location of "x" here is: DW_OP_entry_value($reg2). */
|
|
|
++ // ++sink;
|
|
|
++ // }
|
|
|
++ //
|
|
|
++ // void parent() {
|
|
|
++ // int sink;
|
|
|
++ //
|
|
|
++ // /*
|
|
|
++ // * The callsite information emitted here is:
|
|
|
++ // *
|
|
|
++ // * DW_TAG_call_site
|
|
|
++ // * DW_AT_return_pc ... (for "child(sink, 123);")
|
|
|
++ // * DW_TAG_call_site_parameter (for "sink")
|
|
|
++ // * DW_AT_location ($reg1)
|
|
|
++ // * DW_AT_call_value ($SP - 8)
|
|
|
++ // * DW_TAG_call_site_parameter (for "x")
|
|
|
++ // * DW_AT_location ($reg2)
|
|
|
++ // * DW_AT_call_value ($literal 123)
|
|
|
++ // *
|
|
|
++ // * DW_TAG_call_site
|
|
|
++ // * DW_AT_return_pc ... (for "child(sink, 456);")
|
|
|
++ // * ...
|
|
|
++ // */
|
|
|
++ // child(sink, 123);
|
|
|
++ // child(sink, 456);
|
|
|
++ // }
|
|
|
++ //
|
|
|
++ // When the program stops at "++sink" within `child`, the debugger determines
|
|
|
++ // the call site by analyzing the return address. Once the call site is found,
|
|
|
++ // the debugger determines which parameter is referenced by DW_OP_entry_value
|
|
|
++ // and evaluates the corresponding location for that parameter in `parent`.
|
|
|
++
|
|
|
++ // 1. Find the function which pushed the current frame onto the stack.
|
|
|
++ if ((!exe_ctx || !exe_ctx->HasTargetScope()) || !reg_ctx) {
|
|
|
++ LLDB_LOG(log, "Evaluate_DW_OP_entry_value: no exe/reg context");
|
|
|
++ return false;
|
|
|
++ }
|
|
|
++
|
|
|
++ StackFrame *current_frame = exe_ctx->GetFramePtr();
|
|
|
++ Thread *thread = exe_ctx->GetThreadPtr();
|
|
|
++ if (!current_frame || !thread) {
|
|
|
++ LLDB_LOG(log, "Evaluate_DW_OP_entry_value: no current frame/thread");
|
|
|
++ return false;
|
|
|
++ }
|
|
|
++
|
|
|
++ Target &target = exe_ctx->GetTargetRef();
|
|
|
++ StackFrameSP parent_frame = nullptr;
|
|
|
++ addr_t return_pc = LLDB_INVALID_ADDRESS;
|
|
|
++ uint32_t current_frame_idx = current_frame->GetFrameIndex();
|
|
|
++ uint32_t num_frames = thread->GetStackFrameCount();
|
|
|
++ for (uint32_t parent_frame_idx = current_frame_idx + 1;
|
|
|
++ parent_frame_idx < num_frames; ++parent_frame_idx) {
|
|
|
++ parent_frame = thread->GetStackFrameAtIndex(parent_frame_idx);
|
|
|
++ // Require a valid sequence of frames.
|
|
|
++ if (!parent_frame)
|
|
|
++ break;
|
|
|
++
|
|
|
++ // Record the first valid return address, even if this is an inlined frame,
|
|
|
++ // in order to look up the associated call edge in the first non-inlined
|
|
|
++ // parent frame.
|
|
|
++ if (return_pc == LLDB_INVALID_ADDRESS) {
|
|
|
++ return_pc = parent_frame->GetFrameCodeAddress().GetLoadAddress(&target);
|
|
|
++ LLDB_LOG(log,
|
|
|
++ "Evaluate_DW_OP_entry_value: immediate ancestor with pc = {0:x}",
|
|
|
++ return_pc);
|
|
|
++ }
|
|
|
++
|
|
|
++ // If we've found an inlined frame, skip it (these have no call site
|
|
|
++ // parameters).
|
|
|
++ if (parent_frame->IsInlined())
|
|
|
++ continue;
|
|
|
++
|
|
|
++ // We've found the first non-inlined parent frame.
|
|
|
++ break;
|
|
|
++ }
|
|
|
++ if (!parent_frame || !parent_frame->GetRegisterContext()) {
|
|
|
++ LLDB_LOG(log, "Evaluate_DW_OP_entry_value: no parent frame with reg ctx");
|
|
|
++ return false;
|
|
|
++ }
|
|
|
++
|
|
|
++ Function *parent_func =
|
|
|
++ parent_frame->GetSymbolContext(eSymbolContextFunction).function;
|
|
|
++ if (!parent_func) {
|
|
|
++ LLDB_LOG(log, "Evaluate_DW_OP_entry_value: no parent function");
|
|
|
++ return false;
|
|
|
++ }
|
|
|
++
|
|
|
++ // 2. Find the call edge in the parent function responsible for creating the
|
|
|
++ // current activation.
|
|
|
++ Function *current_func =
|
|
|
++ current_frame->GetSymbolContext(eSymbolContextFunction).function;
|
|
|
++ if (!current_func) {
|
|
|
++ LLDB_LOG(log, "Evaluate_DW_OP_entry_value: no current function");
|
|
|
++ return false;
|
|
|
++ }
|
|
|
++
|
|
|
++ CallEdge *call_edge = nullptr;
|
|
|
++ ModuleList &modlist = target.GetImages();
|
|
|
++ ExecutionContext parent_exe_ctx = *exe_ctx;
|
|
|
++ parent_exe_ctx.SetFrameSP(parent_frame);
|
|
|
++ if (!parent_frame->IsArtificial()) {
|
|
|
++ // If the parent frame is not artificial, the current activation may be
|
|
|
++ // produced by an ambiguous tail call. In this case, refuse to proceed.
|
|
|
++ call_edge = parent_func->GetCallEdgeForReturnAddress(return_pc, target);
|
|
|
++ if (!call_edge) {
|
|
|
++ LLDB_LOG(log,
|
|
|
++ "Evaluate_DW_OP_entry_value: no call edge for retn-pc = {0:x} "
|
|
|
++ "in parent frame {1}",
|
|
|
++ return_pc, parent_func->GetName());
|
|
|
++ return false;
|
|
|
++ }
|
|
|
++ Function *callee_func = call_edge->GetCallee(modlist, parent_exe_ctx);
|
|
|
++ if (callee_func != current_func) {
|
|
|
++ LLDB_LOG(log, "Evaluate_DW_OP_entry_value: ambiguous call sequence, "
|
|
|
++ "can't find real parent frame");
|
|
|
++ return false;
|
|
|
++ }
|
|
|
++ } else {
|
|
|
++ // The StackFrameList solver machinery has deduced that an unambiguous tail
|
|
|
++ // call sequence that produced the current activation. The first edge in
|
|
|
++ // the parent that points to the current function must be valid.
|
|
|
++ for (auto &edge : parent_func->GetTailCallingEdges()) {
|
|
|
++ if (edge->GetCallee(modlist, parent_exe_ctx) == current_func) {
|
|
|
++ call_edge = edge.get();
|
|
|
++ break;
|
|
|
++ }
|
|
|
++ }
|
|
|
++ }
|
|
|
++ if (!call_edge) {
|
|
|
++ LLDB_LOG(log, "Evaluate_DW_OP_entry_value: no unambiguous edge from parent "
|
|
|
++ "to current function");
|
|
|
++ return false;
|
|
|
++ }
|
|
|
++
|
|
|
++ // 3. Attempt to locate the DW_OP_entry_value expression in the set of
|
|
|
++ // available call site parameters. If found, evaluate the corresponding
|
|
|
++ // parameter in the context of the parent frame.
|
|
|
++ const uint32_t subexpr_len = opcodes.GetULEB128(&opcode_offset);
|
|
|
++ const void *subexpr_data = opcodes.GetData(&opcode_offset, subexpr_len);
|
|
|
++ if (!subexpr_data) {
|
|
|
++ LLDB_LOG(log, "Evaluate_DW_OP_entry_value: subexpr could not be read");
|
|
|
++ return false;
|
|
|
++ }
|
|
|
++
|
|
|
++ const CallSiteParameter *matched_param = nullptr;
|
|
|
++ for (const CallSiteParameter ¶m : call_edge->GetCallSiteParameters()) {
|
|
|
++ DataExtractor param_subexpr_extractor;
|
|
|
++ if (!param.LocationInCallee.GetExpressionData(param_subexpr_extractor))
|
|
|
++ continue;
|
|
|
++ lldb::offset_t param_subexpr_offset = 0;
|
|
|
++ const void *param_subexpr_data =
|
|
|
++ param_subexpr_extractor.GetData(¶m_subexpr_offset, subexpr_len);
|
|
|
++ if (!param_subexpr_data ||
|
|
|
++ param_subexpr_extractor.BytesLeft(param_subexpr_offset) != 0)
|
|
|
++ continue;
|
|
|
++
|
|
|
++ // At this point, the DW_OP_entry_value sub-expression and the callee-side
|
|
|
++ // expression in the call site parameter are known to have the same length.
|
|
|
++ // Check whether they are equal.
|
|
|
++ //
|
|
|
++ // Note that an equality check is sufficient: the contents of the
|
|
|
++ // DW_OP_entry_value subexpression are only used to identify the right call
|
|
|
++ // site parameter in the parent, and do not require any special handling.
|
|
|
++ if (memcmp(subexpr_data, param_subexpr_data, subexpr_len) == 0) {
|
|
|
++ matched_param = ¶m;
|
|
|
++ break;
|
|
|
++ }
|
|
|
++ }
|
|
|
++ if (!matched_param) {
|
|
|
++ LLDB_LOG(log,
|
|
|
++ "Evaluate_DW_OP_entry_value: no matching call site param found");
|
|
|
++ return false;
|
|
|
++ }
|
|
|
++
|
|
|
++ // TODO: Add support for DW_OP_push_object_address within a DW_OP_entry_value
|
|
|
++ // subexpresion whenever llvm does.
|
|
|
++ Value result;
|
|
|
++ const DWARFExpression ¶m_expr = matched_param->LocationInCaller;
|
|
|
++ if (!param_expr.Evaluate(&parent_exe_ctx,
|
|
|
++ parent_frame->GetRegisterContext().get(),
|
|
|
++ /*loclist_base_addr=*/LLDB_INVALID_ADDRESS,
|
|
|
++ /*initial_value_ptr=*/nullptr,
|
|
|
++ /*object_address_ptr=*/nullptr, result, error_ptr)) {
|
|
|
++ LLDB_LOG(log,
|
|
|
++ "Evaluate_DW_OP_entry_value: call site param evaluation failed");
|
|
|
++ return false;
|
|
|
++ }
|
|
|
++
|
|
|
++ stack.push_back(result);
|
|
|
++ return true;
|
|
|
++}
|
|
|
++
|
|
|
++bool DWARFEvaluator::Evaluate(Value &result, Status *error_ptr) {
|
|
|
++ DataExtractor opcodes;
|
|
|
++ if (!m_dwarf_expression.GetExpressionData(opcodes)) {
|
|
|
++ if (error_ptr)
|
|
|
++ error_ptr->SetErrorString(
|
|
|
++ "no location, value may have been optimized out");
|
|
|
++ return false;
|
|
|
++ }
|
|
|
++ return Evaluate(opcodes, result, error_ptr);
|
|
|
++}
|
|
|
++
|
|
|
++bool DWARFEvaluator::Evaluate(const DataExtractor &opcodes, Value &result,
|
|
|
++ Status *error_ptr) {
|
|
|
++ if (opcodes.GetByteSize() == 0) {
|
|
|
++ if (error_ptr)
|
|
|
++ error_ptr->SetErrorString(
|
|
|
++ "no location, value may have been optimized out");
|
|
|
++ return false;
|
|
|
++ }
|
|
|
++ std::vector<Value> stack;
|
|
|
++
|
|
|
++ Process *process = nullptr;
|
|
|
++ StackFrame *frame = nullptr;
|
|
|
++
|
|
|
++ if (m_exe_ctx) {
|
|
|
++ process = m_exe_ctx->GetProcessPtr();
|
|
|
++ frame = m_exe_ctx->GetFramePtr();
|
|
|
++ }
|
|
|
++ if (m_reg_ctx == nullptr && frame)
|
|
|
++ m_reg_ctx = frame->GetRegisterContext().get();
|
|
|
++
|
|
|
++ if (m_initial_value_ptr)
|
|
|
++ stack.push_back(*m_initial_value_ptr);
|
|
|
++
|
|
|
++ lldb::offset_t offset = 0;
|
|
|
++
|
|
|
++ /// Insertion point for evaluating multi-piece expression.
|
|
|
++ uint64_t op_piece_offset = 0;
|
|
|
++ Value pieces; // Used for DW_OP_piece
|
|
|
++
|
|
|
++ Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_EXPRESSIONS));
|
|
|
++
|
|
|
++ uint8_t _opcode = 0;
|
|
|
++
|
|
|
++ while (opcodes.ValidOffset(offset)) {
|
|
|
++ const lldb::offset_t op_offset = offset;
|
|
|
++ const uint8_t op = opcodes.GetU8(&offset);
|
|
|
++ _opcode = op;
|
|
|
++
|
|
|
++ if (log && log->GetVerbose()) {
|
|
|
++ size_t count = stack.size();
|
|
|
++ LLDB_LOGF(log, "Stack before operation has %" PRIu64 " values:",
|
|
|
++ (uint64_t)count);
|
|
|
++ for (size_t i = 0; i < count; ++i) {
|
|
|
++ StreamString new_value;
|
|
|
++ new_value.Printf("[%" PRIu64 "]", (uint64_t)i);
|
|
|
++ stack[i].Dump(&new_value);
|
|
|
++ LLDB_LOGF(log, " %s", new_value.GetData());
|
|
|
++ }
|
|
|
++ LLDB_LOGF(log, "0x%8.8" PRIx64 ": %s", op_offset,
|
|
|
++ DW_OP_value_to_name(op));
|
|
|
++ }
|
|
|
++
|
|
|
++ if (!Evaluate(op, process, frame, stack, opcodes, offset, pieces,
|
|
|
++ op_piece_offset, log, error_ptr))
|
|
|
++ return false;
|
|
|
++ }
|
|
|
++
|
|
|
++ 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 (pieces.GetBuffer().GetByteSize())
|
|
|
++ result = pieces;
|
|
|
++ else {
|
|
|
++ if (error_ptr)
|
|
|
++ error_ptr->SetErrorString("Stack empty after evaluation.");
|
|
|
++ return false;
|
|
|
++ }
|
|
|
++ } else {
|
|
|
++ if (log && log->GetVerbose()) {
|
|
|
++ size_t count = stack.size();
|
|
|
++ LLDB_LOGF(log, "Stack after operation has %" PRIu64 " values:",
|
|
|
++ (uint64_t)count);
|
|
|
++ for (size_t i = 0; i < count; ++i) {
|
|
|
++ StreamString new_value;
|
|
|
++ new_value.Printf("[%" PRIu64 "]", (uint64_t)i);
|
|
|
++ stack[i].Dump(&new_value);
|
|
|
++ LLDB_LOGF(log, " %s", new_value.GetData());
|
|
|
++ }
|
|
|
++ }
|
|
|
++ result = stack.back();
|
|
|
++ }
|
|
|
++ return true; // Return true on success
|
|
|
++}
|
|
|
++
|
|
|
++bool DWARFEvaluator::Evaluate(const uint8_t op, Process *process,
|
|
|
++ StackFrame *frame, std::vector<Value> &stack,
|
|
|
++ const DataExtractor &opcodes,
|
|
|
++ lldb::offset_t &offset, Value &pieces,
|
|
|
++ uint64_t &op_piece_offset, Log *log,
|
|
|
++ Status *error_ptr) {
|
|
|
++ Value tmp;
|
|
|
++ uint32_t reg_num;
|
|
|
++
|
|
|
++ lldb::ModuleSP module_sp = m_dwarf_expression.GetModule();
|
|
|
++ const DWARFUnit *dwarf_cu = m_dwarf_expression.GetDWARFCompileUnit();
|
|
|
++ const lldb::RegisterKind reg_kind = m_dwarf_expression.GetRegisterKind();
|
|
|
++
|
|
|
++ switch (op) {
|
|
|
++ // The DW_OP_addr operation has a single operand that encodes a machine
|
|
|
++ // address and whose size is the size of an address on the target machine.
|
|
|
++ case DW_OP_addr:
|
|
|
++ stack.push_back(Scalar(opcodes.GetAddress(&offset)));
|
|
|
++ stack.back().SetValueType(Value::ValueType::FileAddress);
|
|
|
++ // Convert the file address to a load address, so subsequent
|
|
|
++ // DWARF operators can operate on it.
|
|
|
++ if (frame)
|
|
|
++ stack.back().ConvertToLoadAddress(module_sp.get(),
|
|
|
++ frame->CalculateTarget().get());
|
|
|
++ break;
|
|
|
++
|
|
|
++ // The DW_OP_addr_sect_offset4 is used for any location expressions in
|
|
|
++ // shared libraries that have a location like:
|
|
|
++ // DW_OP_addr(0x1000)
|
|
|
++ // If this address resides in a shared library, then this virtual address
|
|
|
++ // won't make sense when it is evaluated in the context of a running
|
|
|
++ // process where shared libraries have been slid. To account for this, this
|
|
|
++ // new address type where we can store the section pointer and a 4 byte
|
|
|
++ // offset.
|
|
|
++ // case DW_OP_addr_sect_offset4:
|
|
|
++ // {
|
|
|
++ // result_type = eResultTypeFileAddress;
|
|
|
++ // lldb::Section *sect = (lldb::Section
|
|
|
++ // *)opcodes.GetMaxU64(&offset, sizeof(void *));
|
|
|
++ // lldb::addr_t sect_offset = opcodes.GetU32(&offset);
|
|
|
++ //
|
|
|
++ // Address so_addr (sect, sect_offset);
|
|
|
++ // lldb::addr_t load_addr = so_addr.GetLoadAddress();
|
|
|
++ // if (load_addr != LLDB_INVALID_ADDRESS)
|
|
|
++ // {
|
|
|
++ // // We successfully resolve a file address to a load
|
|
|
++ // // address.
|
|
|
++ // stack.push_back(load_addr);
|
|
|
++ // break;
|
|
|
++ // }
|
|
|
++ // else
|
|
|
++ // {
|
|
|
++ // // We were able
|
|
|
++ // if (error_ptr)
|
|
|
++ // error_ptr->SetErrorStringWithFormat ("Section %s in
|
|
|
++ // %s is not currently loaded.\n",
|
|
|
++ // sect->GetName().AsCString(),
|
|
|
++ // sect->GetModule()->GetFileSpec().GetFilename().AsCString());
|
|
|
++ // return false;
|
|
|
++ // }
|
|
|
++ // }
|
|
|
++ // break;
|
|
|
++
|
|
|
++ // OPCODE: DW_OP_deref
|
|
|
++ // OPERANDS: none
|
|
|
++ // DESCRIPTION: Pops the top stack entry and treats it as an address.
|
|
|
++ // The value retrieved from that address is pushed. The size of the data
|
|
|
++ // retrieved from the dereferenced address is the size of an address on the
|
|
|
++ // target machine.
|
|
|
++ case DW_OP_deref: {
|
|
|
++ if (stack.empty()) {
|
|
|
++ if (error_ptr)
|
|
|
++ error_ptr->SetErrorString("Expression stack empty for DW_OP_deref.");
|
|
|
++ return false;
|
|
|
++ }
|
|
|
++ Value::ValueType value_type = stack.back().GetValueType();
|
|
|
++ switch (value_type) {
|
|
|
++ case Value::ValueType::HostAddress: {
|
|
|
++ void *src = (void *)stack.back().GetScalar().ULongLong();
|
|
|
++ intptr_t ptr;
|
|
|
++ ::memcpy(&ptr, src, sizeof(void *));
|
|
|
++ stack.back().GetScalar() = ptr;
|
|
|
++ stack.back().ClearContext();
|
|
|
++ } break;
|
|
|
++ case Value::ValueType::FileAddress: {
|
|
|
++ auto file_addr = stack.back().GetScalar().ULongLong(LLDB_INVALID_ADDRESS);
|
|
|
++ if (!module_sp) {
|
|
|
++ if (error_ptr)
|
|
|
++ error_ptr->SetErrorStringWithFormat(
|
|
|
++ "need module to resolve file address for DW_OP_deref");
|
|
|
++ return false;
|
|
|
++ }
|
|
|
++ Address so_addr;
|
|
|
++ if (!module_sp->ResolveFileAddress(file_addr, so_addr)) {
|
|
|
++ if (error_ptr)
|
|
|
++ error_ptr->SetErrorStringWithFormat(
|
|
|
++ "failed to resolve file address in module");
|
|
|
++ return false;
|
|
|
++ }
|
|
|
++ addr_t load_Addr = so_addr.GetLoadAddress(m_exe_ctx->GetTargetPtr());
|
|
|
++ if (load_Addr == LLDB_INVALID_ADDRESS) {
|
|
|
++ if (error_ptr)
|
|
|
++ error_ptr->SetErrorStringWithFormat("failed to resolve load address");
|
|
|
++ return false;
|
|
|
++ }
|
|
|
++ stack.back().GetScalar() = load_Addr;
|
|
|
++ stack.back().SetValueType(Value::ValueType::LoadAddress);
|
|
|
++ // Fall through to load address code below...
|
|
|
++ }
|
|
|
++ LLVM_FALLTHROUGH;
|
|
|
++ case Value::ValueType::LoadAddress:
|
|
|
++ if (m_exe_ctx) {
|
|
|
++ if (process) {
|
|
|
++ lldb::addr_t pointer_addr =
|
|
|
++ stack.back().GetScalar().ULongLong(LLDB_INVALID_ADDRESS);
|
|
|
++ Status error;
|
|
|
++ lldb::addr_t pointer_value =
|
|
|
++ process->ReadPointerFromMemory(pointer_addr, error);
|
|
|
++ if (pointer_value != LLDB_INVALID_ADDRESS) {
|
|
|
++ stack.back().GetScalar() = pointer_value;
|
|
|
++ stack.back().ClearContext();
|
|
|
++ } else {
|
|
|
++ if (error_ptr)
|
|
|
++ error_ptr->SetErrorStringWithFormat(
|
|
|
++ "Failed to dereference pointer from 0x%" PRIx64
|
|
|
++ " for DW_OP_deref: %s\n",
|
|
|
++ pointer_addr, error.AsCString());
|
|
|
++ return false;
|
|
|
++ }
|
|
|
++ } else {
|
|
|
++ if (error_ptr)
|
|
|
++ error_ptr->SetErrorStringWithFormat(
|
|
|
++ "NULL process for DW_OP_deref.\n");
|
|
|
++ return false;
|
|
|
++ }
|
|
|
++ } else {
|
|
|
++ if (error_ptr)
|
|
|
++ error_ptr->SetErrorStringWithFormat(
|
|
|
++ "NULL execution context for DW_OP_deref.\n");
|
|
|
++ return false;
|
|
|
++ }
|
|
|
++ break;
|
|
|
++
|
|
|
++ default:
|
|
|
++ break;
|
|
|
++ }
|
|
|
++
|
|
|
++ } break;
|
|
|
++
|
|
|
++ // OPCODE: DW_OP_deref_size
|
|
|
++ // OPERANDS: 1
|
|
|
++ // 1 - uint8_t that specifies the size of the data to dereference.
|
|
|
++ // DESCRIPTION: Behaves like the DW_OP_deref operation: it pops the top
|
|
|
++ // stack entry and treats it as an address. The value retrieved from that
|
|
|
++ // address is pushed. In the DW_OP_deref_size operation, however, the size
|
|
|
++ // in bytes of the data retrieved from the dereferenced address is
|
|
|
++ // specified by the single operand. This operand is a 1-byte unsigned
|
|
|
++ // integral constant whose value may not be larger than the size of an
|
|
|
++ // address on the target machine. The data retrieved is zero extended to
|
|
|
++ // the size of an address on the target machine before being pushed on the
|
|
|
++ // expression stack.
|
|
|
++ case DW_OP_deref_size: {
|
|
|
++ if (stack.empty()) {
|
|
|
++ if (error_ptr)
|
|
|
++ error_ptr->SetErrorString(
|
|
|
++ "Expression stack empty for DW_OP_deref_size.");
|
|
|
++ return false;
|
|
|
++ }
|
|
|
++ uint8_t size = opcodes.GetU8(&offset);
|
|
|
++ Value::ValueType value_type = stack.back().GetValueType();
|
|
|
++ switch (value_type) {
|
|
|
++ case Value::ValueType::HostAddress: {
|
|
|
++ void *src = (void *)stack.back().GetScalar().ULongLong();
|
|
|
++ intptr_t ptr;
|
|
|
++ ::memcpy(&ptr, src, sizeof(void *));
|
|
|
++ // I can't decide whether the size operand should apply to the bytes in
|
|
|
++ // their
|
|
|
++ // lldb-host endianness or the target endianness.. I doubt this'll ever
|
|
|
++ // come up but I'll opt for assuming big endian regardless.
|
|
|
++ switch (size) {
|
|
|
++ case 1:
|
|
|
++ ptr = ptr & 0xff;
|
|
|
++ break;
|
|
|
++ case 2:
|
|
|
++ ptr = ptr & 0xffff;
|
|
|
++ break;
|
|
|
++ case 3:
|
|
|
++ ptr = ptr & 0xffffff;
|
|
|
++ break;
|
|
|
++ case 4:
|
|
|
++ ptr = ptr & 0xffffffff;
|
|
|
++ break;
|
|
|
++ // the casts are added to work around the case where intptr_t is a 32
|
|
|
++ // bit quantity;
|
|
|
++ // presumably we won't hit the 5..7 cases if (void*) is 32-bits in this
|
|
|
++ // program.
|
|
|
++ case 5:
|
|
|
++ ptr = (intptr_t)ptr & 0xffffffffffULL;
|
|
|
++ break;
|
|
|
++ case 6:
|
|
|
++ ptr = (intptr_t)ptr & 0xffffffffffffULL;
|
|
|
++ break;
|
|
|
++ case 7:
|
|
|
++ ptr = (intptr_t)ptr & 0xffffffffffffffULL;
|
|
|
++ break;
|
|
|
++ default:
|
|
|
++ break;
|
|
|
++ }
|
|
|
++ stack.back().GetScalar() = ptr;
|
|
|
++ stack.back().ClearContext();
|
|
|
++ } break;
|
|
|
++ case Value::ValueType::LoadAddress:
|
|
|
++ if (m_exe_ctx) {
|
|
|
++ if (process) {
|
|
|
++ lldb::addr_t pointer_addr =
|
|
|
++ stack.back().GetScalar().ULongLong(LLDB_INVALID_ADDRESS);
|
|
|
++ uint8_t addr_bytes[sizeof(lldb::addr_t)];
|
|
|
++ Status error;
|
|
|
++ if (process->ReadMemory(pointer_addr, &addr_bytes, size, error) ==
|
|
|
++ size) {
|
|
|
++ DataExtractor addr_data(addr_bytes, sizeof(addr_bytes),
|
|
|
++ process->GetByteOrder(), size);
|
|
|
++ lldb::offset_t addr_data_offset = 0;
|
|
|
++ switch (size) {
|
|
|
++ case 1:
|
|
|
++ stack.back().GetScalar() = addr_data.GetU8(&addr_data_offset);
|
|
|
++ break;
|
|
|
++ case 2:
|
|
|
++ stack.back().GetScalar() = addr_data.GetU16(&addr_data_offset);
|
|
|
++ break;
|
|
|
++ case 4:
|
|
|
++ stack.back().GetScalar() = addr_data.GetU32(&addr_data_offset);
|
|
|
++ break;
|
|
|
++ case 8:
|
|
|
++ stack.back().GetScalar() = addr_data.GetU64(&addr_data_offset);
|
|
|
++ break;
|
|
|
++ default:
|
|
|
++ stack.back().GetScalar() =
|
|
|
++ addr_data.GetAddress(&addr_data_offset);
|
|
|
++ }
|
|
|
++ stack.back().ClearContext();
|
|
|
++ } else {
|
|
|
++ if (error_ptr)
|
|
|
++ error_ptr->SetErrorStringWithFormat(
|
|
|
++ "Failed to dereference pointer from 0x%" PRIx64
|
|
|
++ " for DW_OP_deref: %s\n",
|
|
|
++ pointer_addr, error.AsCString());
|
|
|
++ return false;
|
|
|
++ }
|
|
|
++ } else {
|
|
|
++ if (error_ptr)
|
|
|
++ error_ptr->SetErrorStringWithFormat(
|
|
|
++ "NULL process for DW_OP_deref.\n");
|
|
|
++ return false;
|
|
|
++ }
|
|
|
++ } else {
|
|
|
++ if (error_ptr)
|
|
|
++ error_ptr->SetErrorStringWithFormat(
|
|
|
++ "NULL execution context for DW_OP_deref.\n");
|
|
|
++ return false;
|
|
|
++ }
|
|
|
++ break;
|
|
|
++
|
|
|
++ default:
|
|
|
++ break;
|
|
|
++ }
|
|
|
++
|
|
|
++ } break;
|
|
|
++
|
|
|
++ // OPCODE: DW_OP_xderef_size
|
|
|
++ // OPERANDS: 1
|
|
|
++ // 1 - uint8_t that specifies the size of the data to dereference.
|
|
|
++ // DESCRIPTION: Behaves like the DW_OP_xderef operation: the entry at
|
|
|
++ // the top of the stack is treated as an address. The second stack entry is
|
|
|
++ // treated as an "address space identifier" for those architectures that
|
|
|
++ // support multiple address spaces. The top two stack elements are popped,
|
|
|
++ // a data item is retrieved through an implementation-defined address
|
|
|
++ // calculation and pushed as the new stack top. In the DW_OP_xderef_size
|
|
|
++ // operation, however, the size in bytes of the data retrieved from the
|
|
|
++ // dereferenced address is specified by the single operand. This operand is
|
|
|
++ // a 1-byte unsigned integral constant whose value may not be larger than
|
|
|
++ // the size of an address on the target machine. The data retrieved is zero
|
|
|
++ // extended to the size of an address on the target machine before being
|
|
|
++ // pushed on the expression stack.
|
|
|
++ case DW_OP_xderef_size:
|
|
|
++ if (error_ptr)
|
|
|
++ error_ptr->SetErrorString("Unimplemented opcode: DW_OP_xderef_size.");
|
|
|
++ return false;
|
|
|
++ // OPCODE: DW_OP_xderef
|
|
|
++ // OPERANDS: none
|
|
|
++ // DESCRIPTION: Provides an extended dereference mechanism. The entry at
|
|
|
++ // the top of the stack is treated as an address. The second stack entry is
|
|
|
++ // treated as an "address space identifier" for those architectures that
|
|
|
++ // support multiple address spaces. The top two stack elements are popped,
|
|
|
++ // a data item is retrieved through an implementation-defined address
|
|
|
++ // calculation and pushed as the new stack top. The size of the data
|
|
|
++ // retrieved from the dereferenced address is the size of an address on the
|
|
|
++ // target machine.
|
|
|
++ case DW_OP_xderef:
|
|
|
++ if (error_ptr)
|
|
|
++ error_ptr->SetErrorString("Unimplemented opcode: DW_OP_xderef.");
|
|
|
++ return false;
|
|
|
++
|
|
|
++ // All DW_OP_constXXX opcodes have a single operand as noted below:
|
|
|
++ //
|
|
|
++ // Opcode Operand 1
|
|
|
++ // DW_OP_const1u 1-byte unsigned integer constant DW_OP_const1s
|
|
|
++ // 1-byte signed integer constant DW_OP_const2u 2-byte unsigned integer
|
|
|
++ // constant DW_OP_const2s 2-byte signed integer constant DW_OP_const4u
|
|
|
++ // 4-byte unsigned integer constant DW_OP_const4s 4-byte signed integer
|
|
|
++ // constant DW_OP_const8u 8-byte unsigned integer constant DW_OP_const8s
|
|
|
++ // 8-byte signed integer constant DW_OP_constu unsigned LEB128 integer
|
|
|
++ // constant DW_OP_consts signed LEB128 integer constant
|
|
|
++ case DW_OP_const1u:
|
|
|
++ stack.push_back(Scalar((uint8_t)opcodes.GetU8(&offset)));
|
|
|
++ break;
|
|
|
++ case DW_OP_const1s:
|
|
|
++ stack.push_back(Scalar((int8_t)opcodes.GetU8(&offset)));
|
|
|
++ break;
|
|
|
++ case DW_OP_const2u:
|
|
|
++ stack.push_back(Scalar((uint16_t)opcodes.GetU16(&offset)));
|
|
|
++ break;
|
|
|
++ case DW_OP_const2s:
|
|
|
++ stack.push_back(Scalar((int16_t)opcodes.GetU16(&offset)));
|
|
|
++ break;
|
|
|
++ case DW_OP_const4u:
|
|
|
++ stack.push_back(Scalar((uint32_t)opcodes.GetU32(&offset)));
|
|
|
++ break;
|
|
|
++ case DW_OP_const4s:
|
|
|
++ stack.push_back(Scalar((int32_t)opcodes.GetU32(&offset)));
|
|
|
++ break;
|
|
|
++ case DW_OP_const8u:
|
|
|
++ stack.push_back(Scalar((uint64_t)opcodes.GetU64(&offset)));
|
|
|
++ break;
|
|
|
++ case DW_OP_const8s:
|
|
|
++ stack.push_back(Scalar((int64_t)opcodes.GetU64(&offset)));
|
|
|
++ break;
|
|
|
++ case DW_OP_constu:
|
|
|
++ stack.push_back(Scalar(opcodes.GetULEB128(&offset)));
|
|
|
++ break;
|
|
|
++ case DW_OP_consts:
|
|
|
++ stack.push_back(Scalar(opcodes.GetSLEB128(&offset)));
|
|
|
++ break;
|
|
|
++
|
|
|
++ // OPCODE: DW_OP_dup
|
|
|
++ // OPERANDS: none
|
|
|
++ // DESCRIPTION: duplicates the value at the top of the stack
|
|
|
++ case DW_OP_dup:
|
|
|
++ if (stack.empty()) {
|
|
|
++ if (error_ptr)
|
|
|
++ error_ptr->SetErrorString("Expression stack empty for DW_OP_dup.");
|
|
|
++ return false;
|
|
|
++ } else
|
|
|
++ stack.push_back(stack.back());
|
|
|
++ break;
|
|
|
++
|
|
|
++ // OPCODE: DW_OP_drop
|
|
|
++ // OPERANDS: none
|
|
|
++ // DESCRIPTION: pops the value at the top of the stack
|
|
|
++ case DW_OP_drop:
|
|
|
++ if (stack.empty()) {
|
|
|
++ if (error_ptr)
|
|
|
++ error_ptr->SetErrorString("Expression stack empty for DW_OP_drop.");
|
|
|
++ return false;
|
|
|
++ } else
|
|
|
++ stack.pop_back();
|
|
|
++ break;
|
|
|
++
|
|
|
++ // OPCODE: DW_OP_over
|
|
|
++ // OPERANDS: none
|
|
|
++ // DESCRIPTION: Duplicates the entry currently second in the stack at
|
|
|
++ // the top of the stack.
|
|
|
++ case DW_OP_over:
|
|
|
++ if (stack.size() < 2) {
|
|
|
++ if (error_ptr)
|
|
|
++ error_ptr->SetErrorString(
|
|
|
++ "Expression stack needs at least 2 items for DW_OP_over.");
|
|
|
++ return false;
|
|
|
++ } else
|
|
|
++ stack.push_back(stack[stack.size() - 2]);
|
|
|
++ break;
|
|
|
++
|
|
|
++ // OPCODE: DW_OP_pick
|
|
|
++ // OPERANDS: uint8_t index into the current stack
|
|
|
++ // DESCRIPTION: The stack entry with the specified index (0 through 255,
|
|
|
++ // inclusive) is pushed on the stack
|
|
|
++ case DW_OP_pick: {
|
|
|
++ uint8_t pick_idx = opcodes.GetU8(&offset);
|
|
|
++ if (pick_idx < stack.size())
|
|
|
++ stack.push_back(stack[stack.size() - 1 - pick_idx]);
|
|
|
++ else {
|
|
|
++ if (error_ptr)
|
|
|
++ error_ptr->SetErrorStringWithFormat(
|
|
|
++ "Index %u out of range for DW_OP_pick.\n", pick_idx);
|
|
|
++ return false;
|
|
|
++ }
|
|
|
++ } break;
|
|
|
++
|
|
|
++ // OPCODE: DW_OP_swap
|
|
|
++ // OPERANDS: none
|
|
|
++ // DESCRIPTION: swaps the top two stack entries. The entry at the top
|
|
|
++ // of the stack becomes the second stack entry, and the second entry
|
|
|
++ // becomes the top of the stack
|
|
|
++ case DW_OP_swap:
|
|
|
++ if (stack.size() < 2) {
|
|
|
++ if (error_ptr)
|
|
|
++ error_ptr->SetErrorString(
|
|
|
++ "Expression stack needs at least 2 items for DW_OP_swap.");
|
|
|
++ return false;
|
|
|
++ } else {
|
|
|
++ tmp = stack.back();
|
|
|
++ stack.back() = stack[stack.size() - 2];
|
|
|
++ stack[stack.size() - 2] = tmp;
|
|
|
++ }
|
|
|
++ break;
|
|
|
++
|
|
|
++ // OPCODE: DW_OP_rot
|
|
|
++ // OPERANDS: none
|
|
|
++ // DESCRIPTION: Rotates the first three stack entries. The entry at
|
|
|
++ // the top of the stack becomes the third stack entry, the second entry
|
|
|
++ // becomes the top of the stack, and the third entry becomes the second
|
|
|
++ // entry.
|
|
|
++ case DW_OP_rot:
|
|
|
++ if (stack.size() < 3) {
|
|
|
++ if (error_ptr)
|
|
|
++ error_ptr->SetErrorString(
|
|
|
++ "Expression stack needs at least 3 items for DW_OP_rot.");
|
|
|
++ return false;
|
|
|
++ } else {
|
|
|
++ size_t last_idx = stack.size() - 1;
|
|
|
++ Value old_top = stack[last_idx];
|
|
|
++ stack[last_idx] = stack[last_idx - 1];
|
|
|
++ stack[last_idx - 1] = stack[last_idx - 2];
|
|
|
++ stack[last_idx - 2] = old_top;
|
|
|
++ }
|
|
|
++ break;
|
|
|
++
|
|
|
++ // OPCODE: DW_OP_abs
|
|
|
++ // OPERANDS: none
|
|
|
++ // DESCRIPTION: pops the top stack entry, interprets it as a signed
|
|
|
++ // value and pushes its absolute value. If the absolute value can not be
|
|
|
++ // represented, the result is undefined.
|
|
|
++ case DW_OP_abs:
|
|
|
++ if (stack.empty()) {
|
|
|
++ if (error_ptr)
|
|
|
++ error_ptr->SetErrorString(
|
|
|
++ "Expression stack needs at least 1 item for DW_OP_abs.");
|
|
|
++ return false;
|
|
|
++ } else if (!stack.back().ResolveValue(m_exe_ctx).AbsoluteValue()) {
|
|
|
++ if (error_ptr)
|
|
|
++ error_ptr->SetErrorString(
|
|
|
++ "Failed to take the absolute value of the first stack item.");
|
|
|
++ return false;
|
|
|
++ }
|
|
|
++ break;
|
|
|
++
|
|
|
++ // OPCODE: DW_OP_and
|
|
|
++ // OPERANDS: none
|
|
|
++ // DESCRIPTION: pops the top two stack values, performs a bitwise and
|
|
|
++ // operation on the two, and pushes the result.
|
|
|
++ case DW_OP_and:
|
|
|
++ if (stack.size() < 2) {
|
|
|
++ if (error_ptr)
|
|
|
++ error_ptr->SetErrorString(
|
|
|
++ "Expression stack needs at least 2 items for DW_OP_and.");
|
|
|
++ return false;
|
|
|
++ } else {
|
|
|
++ tmp = stack.back();
|
|
|
++ stack.pop_back();
|
|
|
++ stack.back().ResolveValue(m_exe_ctx) =
|
|
|
++ stack.back().ResolveValue(m_exe_ctx) & tmp.ResolveValue(m_exe_ctx);
|
|
|
++ }
|
|
|
++ break;
|
|
|
++
|
|
|
++ // OPCODE: DW_OP_div
|
|
|
++ // OPERANDS: none
|
|
|
++ // DESCRIPTION: pops the top two stack values, divides the former second
|
|
|
++ // entry by the former top of the stack using signed division, and pushes
|
|
|
++ // the result.
|
|
|
++ case DW_OP_div:
|
|
|
++ if (stack.size() < 2) {
|
|
|
++ if (error_ptr)
|
|
|
++ error_ptr->SetErrorString(
|
|
|
++ "Expression stack needs at least 2 items for DW_OP_div.");
|
|
|
++ return false;
|
|
|
++ } else {
|
|
|
++ tmp = stack.back();
|
|
|
++ if (tmp.ResolveValue(m_exe_ctx).IsZero()) {
|
|
|
++ if (error_ptr)
|
|
|
++ error_ptr->SetErrorString("Divide by zero.");
|
|
|
++ return false;
|
|
|
++ } else {
|
|
|
++ stack.pop_back();
|
|
|
++ stack.back() =
|
|
|
++ stack.back().ResolveValue(m_exe_ctx) / tmp.ResolveValue(m_exe_ctx);
|
|
|
++ if (!stack.back().ResolveValue(m_exe_ctx).IsValid()) {
|
|
|
++ if (error_ptr)
|
|
|
++ error_ptr->SetErrorString("Divide failed.");
|
|
|
++ return false;
|
|
|
++ }
|
|
|
++ }
|
|
|
++ }
|
|
|
++ break;
|
|
|
++
|
|
|
++ // OPCODE: DW_OP_minus
|
|
|
++ // OPERANDS: none
|
|
|
++ // DESCRIPTION: pops the top two stack values, subtracts the former top
|
|
|
++ // of the stack from the former second entry, and pushes the result.
|
|
|
++ case DW_OP_minus:
|
|
|
++ if (stack.size() < 2) {
|
|
|
++ if (error_ptr)
|
|
|
++ error_ptr->SetErrorString(
|
|
|
++ "Expression stack needs at least 2 items for DW_OP_minus.");
|
|
|
++ return false;
|
|
|
++ } else {
|
|
|
++ tmp = stack.back();
|
|
|
++ stack.pop_back();
|
|
|
++ stack.back().ResolveValue(m_exe_ctx) =
|
|
|
++ stack.back().ResolveValue(m_exe_ctx) - tmp.ResolveValue(m_exe_ctx);
|
|
|
++ }
|
|
|
++ break;
|
|
|
++
|
|
|
++ // OPCODE: DW_OP_mod
|
|
|
++ // OPERANDS: none
|
|
|
++ // DESCRIPTION: pops the top two stack values and pushes the result of
|
|
|
++ // the calculation: former second stack entry modulo the former top of the
|
|
|
++ // stack.
|
|
|
++ case DW_OP_mod:
|
|
|
++ if (stack.size() < 2) {
|
|
|
++ if (error_ptr)
|
|
|
++ error_ptr->SetErrorString(
|
|
|
++ "Expression stack needs at least 2 items for DW_OP_mod.");
|
|
|
++ return false;
|
|
|
++ } else {
|
|
|
++ tmp = stack.back();
|
|
|
++ stack.pop_back();
|
|
|
++ stack.back().ResolveValue(m_exe_ctx) =
|
|
|
++ stack.back().ResolveValue(m_exe_ctx) % tmp.ResolveValue(m_exe_ctx);
|
|
|
++ }
|
|
|
++ break;
|
|
|
++
|
|
|
++ // OPCODE: DW_OP_mul
|
|
|
++ // OPERANDS: none
|
|
|
++ // DESCRIPTION: pops the top two stack entries, multiplies them
|
|
|
++ // together, and pushes the result.
|
|
|
++ case DW_OP_mul:
|
|
|
++ if (stack.size() < 2) {
|
|
|
++ if (error_ptr)
|
|
|
++ error_ptr->SetErrorString(
|
|
|
++ "Expression stack needs at least 2 items for DW_OP_mul.");
|
|
|
++ return false;
|
|
|
++ } else {
|
|
|
++ tmp = stack.back();
|
|
|
++ stack.pop_back();
|
|
|
++ stack.back().ResolveValue(m_exe_ctx) =
|
|
|
++ stack.back().ResolveValue(m_exe_ctx) * tmp.ResolveValue(m_exe_ctx);
|
|
|
++ }
|
|
|
++ break;
|
|
|
++
|
|
|
++ // OPCODE: DW_OP_neg
|
|
|
++ // OPERANDS: none
|
|
|
++ // DESCRIPTION: pops the top stack entry, and pushes its negation.
|
|
|
++ case DW_OP_neg:
|
|
|
++ if (stack.empty()) {
|
|
|
++ if (error_ptr)
|
|
|
++ error_ptr->SetErrorString(
|
|
|
++ "Expression stack needs at least 1 item for DW_OP_neg.");
|
|
|
++ return false;
|
|
|
++ } else {
|
|
|
++ if (!stack.back().ResolveValue(m_exe_ctx).UnaryNegate()) {
|
|
|
++ if (error_ptr)
|
|
|
++ error_ptr->SetErrorString("Unary negate failed.");
|
|
|
++ return false;
|
|
|
++ }
|
|
|
++ }
|
|
|
++ break;
|
|
|
++
|
|
|
++ // OPCODE: DW_OP_not
|
|
|
++ // OPERANDS: none
|
|
|
++ // DESCRIPTION: pops the top stack entry, and pushes its bitwise
|
|
|
++ // complement
|
|
|
++ case DW_OP_not:
|
|
|
++ if (stack.empty()) {
|
|
|
++ if (error_ptr)
|
|
|
++ error_ptr->SetErrorString(
|
|
|
++ "Expression stack needs at least 1 item for DW_OP_not.");
|
|
|
++ return false;
|
|
|
++ } else {
|
|
|
++ if (!stack.back().ResolveValue(m_exe_ctx).OnesComplement()) {
|
|
|
++ if (error_ptr)
|
|
|
++ error_ptr->SetErrorString("Logical NOT failed.");
|
|
|
++ return false;
|
|
|
++ }
|
|
|
++ }
|
|
|
++ break;
|
|
|
++
|
|
|
++ // OPCODE: DW_OP_or
|
|
|
++ // OPERANDS: none
|
|
|
++ // DESCRIPTION: pops the top two stack entries, performs a bitwise or
|
|
|
++ // operation on the two, and pushes the result.
|
|
|
++ case DW_OP_or:
|
|
|
++ if (stack.size() < 2) {
|
|
|
++ if (error_ptr)
|
|
|
++ error_ptr->SetErrorString(
|
|
|
++ "Expression stack needs at least 2 items for DW_OP_or.");
|
|
|
++ return false;
|
|
|
++ } else {
|
|
|
++ tmp = stack.back();
|
|
|
++ stack.pop_back();
|
|
|
++ stack.back().ResolveValue(m_exe_ctx) =
|
|
|
++ stack.back().ResolveValue(m_exe_ctx) | tmp.ResolveValue(m_exe_ctx);
|
|
|
++ }
|
|
|
++ break;
|
|
|
++
|
|
|
++ // OPCODE: DW_OP_plus
|
|
|
++ // OPERANDS: none
|
|
|
++ // DESCRIPTION: pops the top two stack entries, adds them together, and
|
|
|
++ // pushes the result.
|
|
|
++ case DW_OP_plus:
|
|
|
++ if (stack.size() < 2) {
|
|
|
++ if (error_ptr)
|
|
|
++ error_ptr->SetErrorString(
|
|
|
++ "Expression stack needs at least 2 items for DW_OP_plus.");
|
|
|
++ return false;
|
|
|
++ } else {
|
|
|
++ tmp = stack.back();
|
|
|
++ stack.pop_back();
|
|
|
++ stack.back().GetScalar() += tmp.GetScalar();
|
|
|
++ }
|
|
|
++ break;
|
|
|
++
|
|
|
++ // OPCODE: DW_OP_plus_uconst
|
|
|
++ // OPERANDS: none
|
|
|
++ // DESCRIPTION: pops the top stack entry, adds it to the unsigned LEB128
|
|
|
++ // constant operand and pushes the result.
|
|
|
++ case DW_OP_plus_uconst:
|
|
|
++ if (stack.empty()) {
|
|
|
++ if (error_ptr)
|
|
|
++ error_ptr->SetErrorString(
|
|
|
++ "Expression stack needs at least 1 item for DW_OP_plus_uconst.");
|
|
|
++ return false;
|
|
|
++ } else {
|
|
|
++ const uint64_t uconst_value = opcodes.GetULEB128(&offset);
|
|
|
++ // Implicit conversion from a UINT to a Scalar...
|
|
|
++ stack.back().GetScalar() += uconst_value;
|
|
|
++ if (!stack.back().GetScalar().IsValid()) {
|
|
|
++ if (error_ptr)
|
|
|
++ error_ptr->SetErrorString("DW_OP_plus_uconst failed.");
|
|
|
++ return false;
|
|
|
++ }
|
|
|
++ }
|
|
|
++ break;
|
|
|
++
|
|
|
++ // OPCODE: DW_OP_shl
|
|
|
++ // OPERANDS: none
|
|
|
++ // DESCRIPTION: pops the top two stack entries, shifts the former
|
|
|
++ // second entry left by the number of bits specified by the former top of
|
|
|
++ // the stack, and pushes the result.
|
|
|
++ case DW_OP_shl:
|
|
|
++ if (stack.size() < 2) {
|
|
|
++ if (error_ptr)
|
|
|
++ error_ptr->SetErrorString(
|
|
|
++ "Expression stack needs at least 2 items for DW_OP_shl.");
|
|
|
++ return false;
|
|
|
++ } else {
|
|
|
++ tmp = stack.back();
|
|
|
++ stack.pop_back();
|
|
|
++ stack.back().ResolveValue(m_exe_ctx) <<= tmp.ResolveValue(m_exe_ctx);
|
|
|
++ }
|
|
|
++ break;
|
|
|
++
|
|
|
++ // OPCODE: DW_OP_shr
|
|
|
++ // OPERANDS: none
|
|
|
++ // DESCRIPTION: pops the top two stack entries, shifts the former second
|
|
|
++ // entry right logically (filling with zero bits) by the number of bits
|
|
|
++ // specified by the former top of the stack, and pushes the result.
|
|
|
++ case DW_OP_shr:
|
|
|
++ if (stack.size() < 2) {
|
|
|
++ if (error_ptr)
|
|
|
++ error_ptr->SetErrorString(
|
|
|
++ "Expression stack needs at least 2 items for DW_OP_shr.");
|
|
|
++ return false;
|
|
|
++ } else {
|
|
|
++ tmp = stack.back();
|
|
|
++ stack.pop_back();
|
|
|
++ if (!stack.back().ResolveValue(m_exe_ctx).ShiftRightLogical(
|
|
|
++ tmp.ResolveValue(m_exe_ctx))) {
|
|
|
++ if (error_ptr)
|
|
|
++ error_ptr->SetErrorString("DW_OP_shr failed.");
|
|
|
++ return false;
|
|
|
++ }
|
|
|
++ }
|
|
|
++ break;
|
|
|
++
|
|
|
++ // OPCODE: DW_OP_shra
|
|
|
++ // OPERANDS: none
|
|
|
++ // DESCRIPTION: pops the top two stack entries, shifts the former second
|
|
|
++ // entry right arithmetically (divide the magnitude by 2, keep the same
|
|
|
++ // sign for the result) by the number of bits specified by the former top
|
|
|
++ // of the stack, and pushes the result.
|
|
|
++ case DW_OP_shra:
|
|
|
++ if (stack.size() < 2) {
|
|
|
++ if (error_ptr)
|
|
|
++ error_ptr->SetErrorString(
|
|
|
++ "Expression stack needs at least 2 items for DW_OP_shra.");
|
|
|
++ return false;
|
|
|
++ } else {
|
|
|
++ tmp = stack.back();
|
|
|
++ stack.pop_back();
|
|
|
++ stack.back().ResolveValue(m_exe_ctx) >>= tmp.ResolveValue(m_exe_ctx);
|
|
|
++ }
|
|
|
++ break;
|
|
|
++
|
|
|
++ // OPCODE: DW_OP_xor
|
|
|
++ // OPERANDS: none
|
|
|
++ // DESCRIPTION: pops the top two stack entries, performs the bitwise
|
|
|
++ // exclusive-or operation on the two, and pushes the result.
|
|
|
++ case DW_OP_xor:
|
|
|
++ if (stack.size() < 2) {
|
|
|
++ if (error_ptr)
|
|
|
++ error_ptr->SetErrorString(
|
|
|
++ "Expression stack needs at least 2 items for DW_OP_xor.");
|
|
|
++ return false;
|
|
|
++ } else {
|
|
|
++ tmp = stack.back();
|
|
|
++ stack.pop_back();
|
|
|
++ stack.back().ResolveValue(m_exe_ctx) =
|
|
|
++ stack.back().ResolveValue(m_exe_ctx) ^ tmp.ResolveValue(m_exe_ctx);
|
|
|
++ }
|
|
|
++ break;
|
|
|
++
|
|
|
++ // OPCODE: DW_OP_skip
|
|
|
++ // OPERANDS: int16_t
|
|
|
++ // DESCRIPTION: An unconditional branch. Its single operand is a 2-byte
|
|
|
++ // signed integer constant. The 2-byte constant is the number of bytes of
|
|
|
++ // the DWARF expression to skip forward or backward from the current
|
|
|
++ // operation, beginning after the 2-byte constant.
|
|
|
++ case DW_OP_skip: {
|
|
|
++ int16_t skip_offset = (int16_t)opcodes.GetU16(&offset);
|
|
|
++ lldb::offset_t new_offset = offset + skip_offset;
|
|
|
++ if (opcodes.ValidOffset(new_offset))
|
|
|
++ offset = new_offset;
|
|
|
++ else {
|
|
|
++ if (error_ptr)
|
|
|
++ error_ptr->SetErrorString("Invalid opcode offset in DW_OP_skip.");
|
|
|
++ return false;
|
|
|
++ }
|
|
|
++ } break;
|
|
|
++
|
|
|
++ // OPCODE: DW_OP_bra
|
|
|
++ // OPERANDS: int16_t
|
|
|
++ // DESCRIPTION: A conditional branch. Its single operand is a 2-byte
|
|
|
++ // signed integer constant. This operation pops the top of stack. If the
|
|
|
++ // value popped is not the constant 0, the 2-byte constant operand is the
|
|
|
++ // number of bytes of the DWARF expression to skip forward or backward from
|
|
|
++ // the current operation, beginning after the 2-byte constant.
|
|
|
++ case DW_OP_bra:
|
|
|
++ if (stack.empty()) {
|
|
|
++ if (error_ptr)
|
|
|
++ error_ptr->SetErrorString(
|
|
|
++ "Expression stack needs at least 1 item for DW_OP_bra.");
|
|
|
++ return false;
|
|
|
++ } else {
|
|
|
++ tmp = stack.back();
|
|
|
++ stack.pop_back();
|
|
|
++ int16_t bra_offset = (int16_t)opcodes.GetU16(&offset);
|
|
|
++ Scalar zero(0);
|
|
|
++ if (tmp.ResolveValue(m_exe_ctx) != zero) {
|
|
|
++ lldb::offset_t new_offset = offset + bra_offset;
|
|
|
++ if (opcodes.ValidOffset(new_offset))
|
|
|
++ offset = new_offset;
|
|
|
++ else {
|
|
|
++ if (error_ptr)
|
|
|
++ error_ptr->SetErrorString("Invalid opcode offset in DW_OP_bra.");
|
|
|
++ return false;
|
|
|
++ }
|
|
|
++ }
|
|
|
++ }
|
|
|
++ break;
|
|
|
++
|
|
|
++ // OPCODE: DW_OP_eq
|
|
|
++ // OPERANDS: none
|
|
|
++ // DESCRIPTION: pops the top two stack values, compares using the
|
|
|
++ // equals (==) operator.
|
|
|
++ // STACK RESULT: push the constant value 1 onto the stack if the result
|
|
|
++ // of the operation is true or the constant value 0 if the result of the
|
|
|
++ // operation is false.
|
|
|
++ case DW_OP_eq:
|
|
|
++ if (stack.size() < 2) {
|
|
|
++ if (error_ptr)
|
|
|
++ error_ptr->SetErrorString(
|
|
|
++ "Expression stack needs at least 2 items for DW_OP_eq.");
|
|
|
++ return false;
|
|
|
++ } else {
|
|
|
++ tmp = stack.back();
|
|
|
++ stack.pop_back();
|
|
|
++ stack.back().ResolveValue(m_exe_ctx) =
|
|
|
++ stack.back().ResolveValue(m_exe_ctx) == tmp.ResolveValue(m_exe_ctx);
|
|
|
++ }
|
|
|
++ break;
|
|
|
++
|
|
|
++ // OPCODE: DW_OP_ge
|
|
|
++ // OPERANDS: none
|
|
|
++ // DESCRIPTION: pops the top two stack values, compares using the
|
|
|
++ // greater than or equal to (>=) operator.
|
|
|
++ // STACK RESULT: push the constant value 1 onto the stack if the result
|
|
|
++ // of the operation is true or the constant value 0 if the result of the
|
|
|
++ // operation is false.
|
|
|
++ case DW_OP_ge:
|
|
|
++ if (stack.size() < 2) {
|
|
|
++ if (error_ptr)
|
|
|
++ error_ptr->SetErrorString(
|
|
|
++ "Expression stack needs at least 2 items for DW_OP_ge.");
|
|
|
++ return false;
|
|
|
++ } else {
|
|
|
++ tmp = stack.back();
|
|
|
++ stack.pop_back();
|
|
|
++ stack.back().ResolveValue(m_exe_ctx) =
|
|
|
++ stack.back().ResolveValue(m_exe_ctx) >= tmp.ResolveValue(m_exe_ctx);
|
|
|
++ }
|
|
|
++ break;
|
|
|
++
|
|
|
++ // OPCODE: DW_OP_gt
|
|
|
++ // OPERANDS: none
|
|
|
++ // DESCRIPTION: pops the top two stack values, compares using the
|
|
|
++ // greater than (>) operator.
|
|
|
++ // STACK RESULT: push the constant value 1 onto the stack if the result
|
|
|
++ // of the operation is true or the constant value 0 if the result of the
|
|
|
++ // operation is false.
|
|
|
++ case DW_OP_gt:
|
|
|
++ if (stack.size() < 2) {
|
|
|
++ if (error_ptr)
|
|
|
++ error_ptr->SetErrorString(
|
|
|
++ "Expression stack needs at least 2 items for DW_OP_gt.");
|
|
|
++ return false;
|
|
|
++ } else {
|
|
|
++ tmp = stack.back();
|
|
|
++ stack.pop_back();
|
|
|
++ stack.back().ResolveValue(m_exe_ctx) =
|
|
|
++ stack.back().ResolveValue(m_exe_ctx) > tmp.ResolveValue(m_exe_ctx);
|
|
|
++ }
|
|
|
++ break;
|
|
|
++
|
|
|
++ // OPCODE: DW_OP_le
|
|
|
++ // OPERANDS: none
|
|
|
++ // DESCRIPTION: pops the top two stack values, compares using the
|
|
|
++ // less than or equal to (<=) operator.
|
|
|
++ // STACK RESULT: push the constant value 1 onto the stack if the result
|
|
|
++ // of the operation is true or the constant value 0 if the result of the
|
|
|
++ // operation is false.
|
|
|
++ case DW_OP_le:
|
|
|
++ if (stack.size() < 2) {
|
|
|
++ if (error_ptr)
|
|
|
++ error_ptr->SetErrorString(
|
|
|
++ "Expression stack needs at least 2 items for DW_OP_le.");
|
|
|
++ return false;
|
|
|
++ } else {
|
|
|
++ tmp = stack.back();
|
|
|
++ stack.pop_back();
|
|
|
++ stack.back().ResolveValue(m_exe_ctx) =
|
|
|
++ stack.back().ResolveValue(m_exe_ctx) <= tmp.ResolveValue(m_exe_ctx);
|
|
|
++ }
|
|
|
++ break;
|
|
|
++
|
|
|
++ // OPCODE: DW_OP_lt
|
|
|
++ // OPERANDS: none
|
|
|
++ // DESCRIPTION: pops the top two stack values, compares using the
|
|
|
++ // less than (<) operator.
|
|
|
++ // STACK RESULT: push the constant value 1 onto the stack if the result
|
|
|
++ // of the operation is true or the constant value 0 if the result of the
|
|
|
++ // operation is false.
|
|
|
++ case DW_OP_lt:
|
|
|
++ if (stack.size() < 2) {
|
|
|
++ if (error_ptr)
|
|
|
++ error_ptr->SetErrorString(
|
|
|
++ "Expression stack needs at least 2 items for DW_OP_lt.");
|
|
|
++ return false;
|
|
|
++ } else {
|
|
|
++ tmp = stack.back();
|
|
|
++ stack.pop_back();
|
|
|
++ stack.back().ResolveValue(m_exe_ctx) =
|
|
|
++ stack.back().ResolveValue(m_exe_ctx) < tmp.ResolveValue(m_exe_ctx);
|
|
|
++ }
|
|
|
++ break;
|
|
|
++
|
|
|
++ // OPCODE: DW_OP_ne
|
|
|
++ // OPERANDS: none
|
|
|
++ // DESCRIPTION: pops the top two stack values, compares using the
|
|
|
++ // not equal (!=) operator.
|
|
|
++ // STACK RESULT: push the constant value 1 onto the stack if the result
|
|
|
++ // of the operation is true or the constant value 0 if the result of the
|
|
|
++ // operation is false.
|
|
|
++ case DW_OP_ne:
|
|
|
++ if (stack.size() < 2) {
|
|
|
++ if (error_ptr)
|
|
|
++ error_ptr->SetErrorString(
|
|
|
++ "Expression stack needs at least 2 items for DW_OP_ne.");
|
|
|
++ return false;
|
|
|
++ } else {
|
|
|
++ tmp = stack.back();
|
|
|
++ stack.pop_back();
|
|
|
++ stack.back().ResolveValue(m_exe_ctx) =
|
|
|
++ stack.back().ResolveValue(m_exe_ctx) != tmp.ResolveValue(m_exe_ctx);
|
|
|
++ }
|
|
|
++ break;
|
|
|
++
|
|
|
++ // OPCODE: DW_OP_litn
|
|
|
++ // OPERANDS: none
|
|
|
++ // DESCRIPTION: encode the unsigned literal values from 0 through 31.
|
|
|
++ // STACK RESULT: push the unsigned literal constant value onto the top
|
|
|
++ // of the stack.
|
|
|
++ case DW_OP_lit0:
|
|
|
++ case DW_OP_lit1:
|
|
|
++ case DW_OP_lit2:
|
|
|
++ case DW_OP_lit3:
|
|
|
++ case DW_OP_lit4:
|
|
|
++ case DW_OP_lit5:
|
|
|
++ case DW_OP_lit6:
|
|
|
++ case DW_OP_lit7:
|
|
|
++ case DW_OP_lit8:
|
|
|
++ case DW_OP_lit9:
|
|
|
++ case DW_OP_lit10:
|
|
|
++ case DW_OP_lit11:
|
|
|
++ case DW_OP_lit12:
|
|
|
++ case DW_OP_lit13:
|
|
|
++ case DW_OP_lit14:
|
|
|
++ case DW_OP_lit15:
|
|
|
++ case DW_OP_lit16:
|
|
|
++ case DW_OP_lit17:
|
|
|
++ case DW_OP_lit18:
|
|
|
++ case DW_OP_lit19:
|
|
|
++ case DW_OP_lit20:
|
|
|
++ case DW_OP_lit21:
|
|
|
++ case DW_OP_lit22:
|
|
|
++ case DW_OP_lit23:
|
|
|
++ case DW_OP_lit24:
|
|
|
++ case DW_OP_lit25:
|
|
|
++ case DW_OP_lit26:
|
|
|
++ case DW_OP_lit27:
|
|
|
++ case DW_OP_lit28:
|
|
|
++ case DW_OP_lit29:
|
|
|
++ case DW_OP_lit30:
|
|
|
++ case DW_OP_lit31:
|
|
|
++ stack.push_back(Scalar((uint64_t)(op - DW_OP_lit0)));
|
|
|
++ break;
|
|
|
++
|
|
|
++ // OPCODE: DW_OP_regN
|
|
|
++ // OPERANDS: none
|
|
|
++ // DESCRIPTION: Push the value in register n on the top of the stack.
|
|
|
++ case DW_OP_reg0:
|
|
|
++ case DW_OP_reg1:
|
|
|
++ case DW_OP_reg2:
|
|
|
++ case DW_OP_reg3:
|
|
|
++ case DW_OP_reg4:
|
|
|
++ case DW_OP_reg5:
|
|
|
++ case DW_OP_reg6:
|
|
|
++ case DW_OP_reg7:
|
|
|
++ case DW_OP_reg8:
|
|
|
++ case DW_OP_reg9:
|
|
|
++ case DW_OP_reg10:
|
|
|
++ case DW_OP_reg11:
|
|
|
++ case DW_OP_reg12:
|
|
|
++ case DW_OP_reg13:
|
|
|
++ case DW_OP_reg14:
|
|
|
++ case DW_OP_reg15:
|
|
|
++ case DW_OP_reg16:
|
|
|
++ case DW_OP_reg17:
|
|
|
++ case DW_OP_reg18:
|
|
|
++ case DW_OP_reg19:
|
|
|
++ case DW_OP_reg20:
|
|
|
++ case DW_OP_reg21:
|
|
|
++ case DW_OP_reg22:
|
|
|
++ case DW_OP_reg23:
|
|
|
++ case DW_OP_reg24:
|
|
|
++ case DW_OP_reg25:
|
|
|
++ case DW_OP_reg26:
|
|
|
++ case DW_OP_reg27:
|
|
|
++ case DW_OP_reg28:
|
|
|
++ case DW_OP_reg29:
|
|
|
++ case DW_OP_reg30:
|
|
|
++ case DW_OP_reg31: {
|
|
|
++ reg_num = op - DW_OP_reg0;
|
|
|
++
|
|
|
++ if (ReadRegisterValueAsScalar(m_reg_ctx, reg_kind, reg_num, error_ptr, tmp))
|
|
|
++ stack.push_back(tmp);
|
|
|
++ else
|
|
|
++ return false;
|
|
|
++ } break;
|
|
|
++ // OPCODE: DW_OP_regx
|
|
|
++ // OPERANDS:
|
|
|
++ // ULEB128 literal operand that encodes the register.
|
|
|
++ // DESCRIPTION: Push the value in register on the top of the stack.
|
|
|
++ case DW_OP_regx: {
|
|
|
++ reg_num = opcodes.GetULEB128(&offset);
|
|
|
++ if (ReadRegisterValueAsScalar(m_reg_ctx, reg_kind, reg_num, error_ptr, tmp))
|
|
|
++ stack.push_back(tmp);
|
|
|
++ else
|
|
|
++ return false;
|
|
|
++ } break;
|
|
|
++
|
|
|
++ // OPCODE: DW_OP_bregN
|
|
|
++ // OPERANDS:
|
|
|
++ // SLEB128 offset from register N
|
|
|
++ // DESCRIPTION: Value is in memory at the address specified by register
|
|
|
++ // N plus an offset.
|
|
|
++ case DW_OP_breg0:
|
|
|
++ case DW_OP_breg1:
|
|
|
++ case DW_OP_breg2:
|
|
|
++ case DW_OP_breg3:
|
|
|
++ case DW_OP_breg4:
|
|
|
++ case DW_OP_breg5:
|
|
|
++ case DW_OP_breg6:
|
|
|
++ case DW_OP_breg7:
|
|
|
++ case DW_OP_breg8:
|
|
|
++ case DW_OP_breg9:
|
|
|
++ case DW_OP_breg10:
|
|
|
++ case DW_OP_breg11:
|
|
|
++ case DW_OP_breg12:
|
|
|
++ case DW_OP_breg13:
|
|
|
++ case DW_OP_breg14:
|
|
|
++ case DW_OP_breg15:
|
|
|
++ case DW_OP_breg16:
|
|
|
++ case DW_OP_breg17:
|
|
|
++ case DW_OP_breg18:
|
|
|
++ case DW_OP_breg19:
|
|
|
++ case DW_OP_breg20:
|
|
|
++ case DW_OP_breg21:
|
|
|
++ case DW_OP_breg22:
|
|
|
++ case DW_OP_breg23:
|
|
|
++ case DW_OP_breg24:
|
|
|
++ case DW_OP_breg25:
|
|
|
++ case DW_OP_breg26:
|
|
|
++ case DW_OP_breg27:
|
|
|
++ case DW_OP_breg28:
|
|
|
++ case DW_OP_breg29:
|
|
|
++ case DW_OP_breg30:
|
|
|
++ case DW_OP_breg31: {
|
|
|
++ reg_num = op - DW_OP_breg0;
|
|
|
++
|
|
|
++ if (ReadRegisterValueAsScalar(m_reg_ctx, reg_kind, reg_num, error_ptr,
|
|
|
++ tmp)) {
|
|
|
++ int64_t breg_offset = opcodes.GetSLEB128(&offset);
|
|
|
++ tmp.ResolveValue(m_exe_ctx) += (uint64_t)breg_offset;
|
|
|
++ tmp.ClearContext();
|
|
|
++ stack.push_back(tmp);
|
|
|
++ stack.back().SetValueType(Value::ValueType::LoadAddress);
|
|
|
++ } else
|
|
|
++ return false;
|
|
|
++ } break;
|
|
|
++ // OPCODE: DW_OP_bregx
|
|
|
++ // OPERANDS: 2
|
|
|
++ // ULEB128 literal operand that encodes the register.
|
|
|
++ // SLEB128 offset from register N
|
|
|
++ // DESCRIPTION: Value is in memory at the address specified by register
|
|
|
++ // N plus an offset.
|
|
|
++ case DW_OP_bregx: {
|
|
|
++ reg_num = opcodes.GetULEB128(&offset);
|
|
|
++
|
|
|
++ if (ReadRegisterValueAsScalar(m_reg_ctx, reg_kind, reg_num, error_ptr,
|
|
|
++ tmp)) {
|
|
|
++ int64_t breg_offset = opcodes.GetSLEB128(&offset);
|
|
|
++ tmp.ResolveValue(m_exe_ctx) += (uint64_t)breg_offset;
|
|
|
++ tmp.ClearContext();
|
|
|
++ stack.push_back(tmp);
|
|
|
++ stack.back().SetValueType(Value::ValueType::LoadAddress);
|
|
|
++ } else
|
|
|
++ return false;
|
|
|
++ } break;
|
|
|
++
|
|
|
++ case DW_OP_fbreg:
|
|
|
++ if (m_exe_ctx) {
|
|
|
++ if (frame) {
|
|
|
++ Scalar value;
|
|
|
++ if (frame->GetFrameBaseValue(value, error_ptr)) {
|
|
|
++ int64_t fbreg_offset = opcodes.GetSLEB128(&offset);
|
|
|
++ value += fbreg_offset;
|
|
|
++ stack.push_back(value);
|
|
|
++ stack.back().SetValueType(Value::ValueType::LoadAddress);
|
|
|
++ } else
|
|
|
++ return false;
|
|
|
++ } else {
|
|
|
++ if (error_ptr)
|
|
|
++ error_ptr->SetErrorString(
|
|
|
++ "Invalid stack frame in context for DW_OP_fbreg opcode.");
|
|
|
++ return false;
|
|
|
++ }
|
|
|
++ } else {
|
|
|
++ if (error_ptr)
|
|
|
++ error_ptr->SetErrorStringWithFormat(
|
|
|
++ "NULL execution context for DW_OP_fbreg.\n");
|
|
|
++ return false;
|
|
|
++ }
|
|
|
++
|
|
|
++ break;
|
|
|
++
|
|
|
++ // OPCODE: DW_OP_nop
|
|
|
++ // OPERANDS: none
|
|
|
++ // DESCRIPTION: A place holder. It has no effect on the location stack
|
|
|
++ // or any of its values.
|
|
|
++ case DW_OP_nop:
|
|
|
++ break;
|
|
|
++
|
|
|
++ // OPCODE: DW_OP_piece
|
|
|
++ // OPERANDS: 1
|
|
|
++ // ULEB128: byte size of the piece
|
|
|
++ // DESCRIPTION: The operand describes the size in bytes of the piece of
|
|
|
++ // the object referenced by the DWARF expression whose result is at the top
|
|
|
++ // of the stack. If the piece is located in a register, but does not occupy
|
|
|
++ // the entire register, the placement of the piece within that register is
|
|
|
++ // defined by the ABI.
|
|
|
++ //
|
|
|
++ // Many compilers store a single variable in sets of registers, or store a
|
|
|
++ // variable partially in memory and partially in registers. DW_OP_piece
|
|
|
++ // provides a way of describing how large a part of a variable a particular
|
|
|
++ // DWARF expression refers to.
|
|
|
++ case DW_OP_piece: {
|
|
|
++ const uint64_t piece_byte_size = opcodes.GetULEB128(&offset);
|
|
|
++
|
|
|
++ if (piece_byte_size > 0) {
|
|
|
++ Value curr_piece;
|
|
|
++
|
|
|
++ if (stack.empty()) {
|
|
|
++ // In a multi-piece expression, this means that the current piece is
|
|
|
++ // not available. Fill with zeros for now by resizing the data and
|
|
|
++ // appending it
|
|
|
++ curr_piece.ResizeData(piece_byte_size);
|
|
|
++ // Note that "0" is not a correct value for the unknown bits.
|
|
|
++ // It would be better to also return a mask of valid bits together
|
|
|
++ // with the expression result, so the debugger can print missing
|
|
|
++ // members as "<optimized out>" or something.
|
|
|
++ ::memset(curr_piece.GetBuffer().GetBytes(), 0, piece_byte_size);
|
|
|
++ pieces.AppendDataToHostBuffer(curr_piece);
|
|
|
++ } else {
|
|
|
++ Status error;
|
|
|
++ // Extract the current piece into "curr_piece"
|
|
|
++ Value curr_piece_source_value(stack.back());
|
|
|
++ stack.pop_back();
|
|
|
++
|
|
|
++ const Value::ValueType curr_piece_source_value_type =
|
|
|
++ curr_piece_source_value.GetValueType();
|
|
|
++ switch (curr_piece_source_value_type) {
|
|
|
++ case Value::ValueType::LoadAddress:
|
|
|
++ if (process) {
|
|
|
++ if (curr_piece.ResizeData(piece_byte_size) == piece_byte_size) {
|
|
|
++ lldb::addr_t load_addr =
|
|
|
++ curr_piece_source_value.GetScalar().ULongLong(
|
|
|
++ LLDB_INVALID_ADDRESS);
|
|
|
++ if (process->ReadMemory(
|
|
|
++ load_addr, curr_piece.GetBuffer().GetBytes(),
|
|
|
++ piece_byte_size, error) != piece_byte_size) {
|
|
|
++ if (error_ptr)
|
|
|
++ error_ptr->SetErrorStringWithFormat(
|
|
|
++ "failed to read memory DW_OP_piece(%" PRIu64
|
|
|
++ ") from 0x%" PRIx64,
|
|
|
++ piece_byte_size, load_addr);
|
|
|
++ return false;
|
|
|
++ }
|
|
|
++ } else {
|
|
|
++ if (error_ptr)
|
|
|
++ error_ptr->SetErrorStringWithFormat(
|
|
|
++ "failed to resize the piece memory buffer for "
|
|
|
++ "DW_OP_piece(%" PRIu64 ")",
|
|
|
++ piece_byte_size);
|
|
|
++ return false;
|
|
|
++ }
|
|
|
++ }
|
|
|
++ break;
|
|
|
++
|
|
|
++ case Value::ValueType::FileAddress:
|
|
|
++ case Value::ValueType::HostAddress:
|
|
|
++ if (error_ptr) {
|
|
|
++ lldb::addr_t addr = curr_piece_source_value.GetScalar().ULongLong(
|
|
|
++ LLDB_INVALID_ADDRESS);
|
|
|
++ error_ptr->SetErrorStringWithFormat(
|
|
|
++ "failed to read memory DW_OP_piece(%" PRIu64
|
|
|
++ ") from %s address 0x%" PRIx64,
|
|
|
++ piece_byte_size,
|
|
|
++ curr_piece_source_value.GetValueType() ==
|
|
|
++ Value::ValueType::FileAddress
|
|
|
++ ? "file"
|
|
|
++ : "host",
|
|
|
++ addr);
|
|
|
++ }
|
|
|
++ return false;
|
|
|
++
|
|
|
++ case Value::ValueType::Scalar: {
|
|
|
++ uint32_t bit_size = piece_byte_size * 8;
|
|
|
++ uint32_t bit_offset = 0;
|
|
|
++ Scalar &scalar = curr_piece_source_value.GetScalar();
|
|
|
++ if (!scalar.ExtractBitfield(bit_size, bit_offset)) {
|
|
|
++ if (error_ptr)
|
|
|
++ error_ptr->SetErrorStringWithFormat(
|
|
|
++ "unable to extract %" PRIu64 " bytes from a %" PRIu64
|
|
|
++ " byte scalar value.",
|
|
|
++ piece_byte_size,
|
|
|
++ (uint64_t)curr_piece_source_value.GetScalar().GetByteSize());
|
|
|
++ return false;
|
|
|
++ }
|
|
|
++ // Create curr_piece with bit_size. By default Scalar
|
|
|
++ // grows to the nearest host integer type.
|
|
|
++ llvm::APInt fail_value(1, 0, false);
|
|
|
++ llvm::APInt ap_int = scalar.UInt128(fail_value);
|
|
|
++ assert(ap_int.getBitWidth() >= bit_size);
|
|
|
++ llvm::ArrayRef<uint64_t> buf{ap_int.getRawData(),
|
|
|
++ ap_int.getNumWords()};
|
|
|
++ curr_piece.GetScalar() = Scalar(llvm::APInt(bit_size, buf));
|
|
|
++ } break;
|
|
|
++ }
|
|
|
++
|
|
|
++ // Check if this is the first piece?
|
|
|
++ if (op_piece_offset == 0) {
|
|
|
++ // This is the first piece, we should push it back onto the stack
|
|
|
++ // so subsequent pieces will be able to access this piece and add
|
|
|
++ // to it.
|
|
|
++ if (pieces.AppendDataToHostBuffer(curr_piece) == 0) {
|
|
|
++ if (error_ptr)
|
|
|
++ error_ptr->SetErrorString("failed to append piece data");
|
|
|
++ return false;
|
|
|
++ }
|
|
|
++ } else {
|
|
|
++ // If this is the second or later piece there should be a value on
|
|
|
++ // the stack.
|
|
|
++ if (pieces.GetBuffer().GetByteSize() != op_piece_offset) {
|
|
|
++ if (error_ptr)
|
|
|
++ error_ptr->SetErrorStringWithFormat(
|
|
|
++ "DW_OP_piece for offset %" PRIu64
|
|
|
++ " but top of stack is of size %" PRIu64,
|
|
|
++ op_piece_offset, pieces.GetBuffer().GetByteSize());
|
|
|
++ return false;
|
|
|
++ }
|
|
|
++
|
|
|
++ if (pieces.AppendDataToHostBuffer(curr_piece) == 0) {
|
|
|
++ if (error_ptr)
|
|
|
++ error_ptr->SetErrorString("failed to append piece data");
|
|
|
++ return false;
|
|
|
++ }
|
|
|
++ }
|
|
|
++ }
|
|
|
++ op_piece_offset += piece_byte_size;
|
|
|
++ }
|
|
|
++ } break;
|
|
|
++
|
|
|
++ case DW_OP_bit_piece: // 0x9d ULEB128 bit size, ULEB128 bit offset (DWARF3);
|
|
|
++ if (stack.size() < 1) {
|
|
|
++ if (error_ptr)
|
|
|
++ error_ptr->SetErrorString(
|
|
|
++ "Expression stack needs at least 1 item for DW_OP_bit_piece.");
|
|
|
++ return false;
|
|
|
++ } else {
|
|
|
++ const uint64_t piece_bit_size = opcodes.GetULEB128(&offset);
|
|
|
++ const uint64_t piece_bit_offset = opcodes.GetULEB128(&offset);
|
|
|
++ switch (stack.back().GetValueType()) {
|
|
|
++ case Value::ValueType::Scalar: {
|
|
|
++ if (!stack.back().GetScalar().ExtractBitfield(piece_bit_size,
|
|
|
++ piece_bit_offset)) {
|
|
|
++ if (error_ptr)
|
|
|
++ error_ptr->SetErrorStringWithFormat(
|
|
|
++ "unable to extract %" PRIu64 " bit value with %" PRIu64
|
|
|
++ " bit offset from a %" PRIu64 " bit scalar value.",
|
|
|
++ piece_bit_size, piece_bit_offset,
|
|
|
++ (uint64_t)(stack.back().GetScalar().GetByteSize() * 8));
|
|
|
++ return false;
|
|
|
++ }
|
|
|
++ } break;
|
|
|
++
|
|
|
++ case Value::ValueType::FileAddress:
|
|
|
++ case Value::ValueType::LoadAddress:
|
|
|
++ case Value::ValueType::HostAddress:
|
|
|
++ if (error_ptr) {
|
|
|
++ error_ptr->SetErrorStringWithFormat(
|
|
|
++ "unable to extract DW_OP_bit_piece(bit_size = %" PRIu64
|
|
|
++ ", bit_offset = %" PRIu64 ") from an address value.",
|
|
|
++ piece_bit_size, piece_bit_offset);
|
|
|
++ }
|
|
|
++ return false;
|
|
|
++ }
|
|
|
++ }
|
|
|
++ break;
|
|
|
++
|
|
|
++ // OPCODE: DW_OP_push_object_address
|
|
|
++ // OPERANDS: none
|
|
|
++ // DESCRIPTION: Pushes the address of the object currently being
|
|
|
++ // evaluated as part of evaluation of a user presented expression. This
|
|
|
++ // object may correspond to an independent variable described by its own
|
|
|
++ // DIE or it may be a component of an array, structure, or class whose
|
|
|
++ // address has been dynamically determined by an earlier step during user
|
|
|
++ // expression evaluation.
|
|
|
++ case DW_OP_push_object_address:
|
|
|
++ if (m_object_address_ptr)
|
|
|
++ stack.push_back(*m_object_address_ptr);
|
|
|
++ else {
|
|
|
++ if (error_ptr)
|
|
|
++ error_ptr->SetErrorString("DW_OP_push_object_address used without "
|
|
|
++ "specifying an object address");
|
|
|
++ return false;
|
|
|
++ }
|
|
|
++ break;
|
|
|
++
|
|
|
++ // OPCODE: DW_OP_call2
|
|
|
++ // OPERANDS:
|
|
|
++ // uint16_t compile unit relative offset of a DIE
|
|
|
++ // DESCRIPTION: Performs subroutine calls during evaluation
|
|
|
++ // of a DWARF expression. The operand is the 2-byte unsigned offset of a
|
|
|
++ // debugging information entry in the current compilation unit.
|
|
|
++ //
|
|
|
++ // Operand interpretation is exactly like that for DW_FORM_ref2.
|
|
|
++ //
|
|
|
++ // This operation transfers control of DWARF expression evaluation to the
|
|
|
++ // DW_AT_location attribute of the referenced DIE. If there is no such
|
|
|
++ // attribute, then there is no effect. Execution of the DWARF expression of
|
|
|
++ // a DW_AT_location attribute may add to and/or remove from values on the
|
|
|
++ // stack. Execution returns to the point following the call when the end of
|
|
|
++ // the attribute is reached. Values on the stack at the time of the call
|
|
|
++ // may be used as parameters by the called expression and values left on
|
|
|
++ // the stack by the called expression may be used as return values by prior
|
|
|
++ // agreement between the calling and called expressions.
|
|
|
++ case DW_OP_call2:
|
|
|
++ if (error_ptr)
|
|
|
++ error_ptr->SetErrorString("Unimplemented opcode DW_OP_call2.");
|
|
|
++ return false;
|
|
|
++ // OPCODE: DW_OP_call4
|
|
|
++ // OPERANDS: 1
|
|
|
++ // uint32_t compile unit relative offset of a DIE
|
|
|
++ // DESCRIPTION: Performs a subroutine call during evaluation of a DWARF
|
|
|
++ // expression. For DW_OP_call4, the operand is a 4-byte unsigned offset of
|
|
|
++ // a debugging information entry in the current compilation unit.
|
|
|
++ //
|
|
|
++ // Operand interpretation DW_OP_call4 is exactly like that for
|
|
|
++ // DW_FORM_ref4.
|
|
|
++ //
|
|
|
++ // This operation transfers control of DWARF expression evaluation to the
|
|
|
++ // DW_AT_location attribute of the referenced DIE. If there is no such
|
|
|
++ // attribute, then there is no effect. Execution of the DWARF expression of
|
|
|
++ // a DW_AT_location attribute may add to and/or remove from values on the
|
|
|
++ // stack. Execution returns to the point following the call when the end of
|
|
|
++ // the attribute is reached. Values on the stack at the time of the call
|
|
|
++ // may be used as parameters by the called expression and values left on
|
|
|
++ // the stack by the called expression may be used as return values by prior
|
|
|
++ // agreement between the calling and called expressions.
|
|
|
++ case DW_OP_call4:
|
|
|
++ if (error_ptr)
|
|
|
++ error_ptr->SetErrorString("Unimplemented opcode DW_OP_call4.");
|
|
|
++ return false;
|
|
|
++
|
|
|
++ // OPCODE: DW_OP_stack_value
|
|
|
++ // OPERANDS: None
|
|
|
++ // DESCRIPTION: Specifies that the object does not exist in memory but
|
|
|
++ // rather is a constant value. The value from the top of the stack is the
|
|
|
++ // value to be used. This is the actual object value and not the location.
|
|
|
++ case DW_OP_stack_value:
|
|
|
++ if (stack.empty()) {
|
|
|
++ if (error_ptr)
|
|
|
++ error_ptr->SetErrorString(
|
|
|
++ "Expression stack needs at least 1 item for DW_OP_stack_value.");
|
|
|
++ return false;
|
|
|
++ }
|
|
|
++ stack.back().SetValueType(Value::ValueType::Scalar);
|
|
|
++ break;
|
|
|
++
|
|
|
++ // OPCODE: DW_OP_convert
|
|
|
++ // OPERANDS: 1
|
|
|
++ // A ULEB128 that is either a DIE offset of a
|
|
|
++ // DW_TAG_base_type or 0 for the generic (pointer-sized) type.
|
|
|
++ //
|
|
|
++ // DESCRIPTION: Pop the top stack element, convert it to a
|
|
|
++ // different type, and push the result.
|
|
|
++ case DW_OP_convert: {
|
|
|
++ if (stack.size() < 1) {
|
|
|
++ if (error_ptr)
|
|
|
++ error_ptr->SetErrorString(
|
|
|
++ "Expression stack needs at least 1 item for DW_OP_convert.");
|
|
|
++ return false;
|
|
|
++ }
|
|
|
++ const uint64_t die_offset = opcodes.GetULEB128(&offset);
|
|
|
++ uint64_t bit_size;
|
|
|
++ bool sign;
|
|
|
++ if (die_offset == 0) {
|
|
|
++ // The generic type has the size of an address on the target
|
|
|
++ // machine and an unspecified signedness. Scalar has no
|
|
|
++ // "unspecified signedness", so we use unsigned types.
|
|
|
++ if (!module_sp) {
|
|
|
++ if (error_ptr)
|
|
|
++ error_ptr->SetErrorString("No module");
|
|
|
++ return false;
|
|
|
++ }
|
|
|
++ sign = false;
|
|
|
++ bit_size = module_sp->GetArchitecture().GetAddressByteSize() * 8;
|
|
|
++ if (!bit_size) {
|
|
|
++ if (error_ptr)
|
|
|
++ error_ptr->SetErrorString("unspecified architecture");
|
|
|
++ return false;
|
|
|
++ }
|
|
|
++ } else {
|
|
|
++ // Retrieve the type DIE that the value is being converted to.
|
|
|
++ // FIXME: the constness has annoying ripple effects.
|
|
|
++ DWARFDIE die = const_cast<DWARFUnit *>(dwarf_cu)->GetDIE(die_offset);
|
|
|
++ if (!die) {
|
|
|
++ if (error_ptr)
|
|
|
++ error_ptr->SetErrorString("Cannot resolve DW_OP_convert type DIE");
|
|
|
++ return false;
|
|
|
++ }
|
|
|
++ uint64_t encoding =
|
|
|
++ die.GetAttributeValueAsUnsigned(DW_AT_encoding, DW_ATE_hi_user);
|
|
|
++ bit_size = die.GetAttributeValueAsUnsigned(DW_AT_byte_size, 0) * 8;
|
|
|
++ if (!bit_size)
|
|
|
++ bit_size = die.GetAttributeValueAsUnsigned(DW_AT_bit_size, 0);
|
|
|
++ if (!bit_size) {
|
|
|
++ if (error_ptr)
|
|
|
++ error_ptr->SetErrorString("Unsupported type size in DW_OP_convert");
|
|
|
++ return false;
|
|
|
++ }
|
|
|
++ switch (encoding) {
|
|
|
++ case DW_ATE_signed:
|
|
|
++ case DW_ATE_signed_char:
|
|
|
++ sign = true;
|
|
|
++ break;
|
|
|
++ case DW_ATE_unsigned:
|
|
|
++ case DW_ATE_unsigned_char:
|
|
|
++ sign = false;
|
|
|
++ break;
|
|
|
++ default:
|
|
|
++ if (error_ptr)
|
|
|
++ error_ptr->SetErrorString("Unsupported encoding in DW_OP_convert");
|
|
|
++ return false;
|
|
|
++ }
|
|
|
++ }
|
|
|
++ Scalar &top = stack.back().ResolveValue(m_exe_ctx);
|
|
|
++ top.TruncOrExtendTo(bit_size, sign);
|
|
|
++ break;
|
|
|
++ }
|
|
|
++
|
|
|
++ // OPCODE: DW_OP_call_frame_cfa
|
|
|
++ // OPERANDS: None
|
|
|
++ // DESCRIPTION: Specifies a DWARF expression that pushes the value of
|
|
|
++ // the canonical frame address consistent with the call frame information
|
|
|
++ // located in .debug_frame (or in the FDEs of the eh_frame section).
|
|
|
++ case DW_OP_call_frame_cfa:
|
|
|
++ if (frame) {
|
|
|
++ // Note that we don't have to parse FDEs because this DWARF expression
|
|
|
++ // is commonly evaluated with a valid stack frame.
|
|
|
++ StackID id = frame->GetStackID();
|
|
|
++ addr_t cfa = id.GetCallFrameAddress();
|
|
|
++ if (cfa != LLDB_INVALID_ADDRESS) {
|
|
|
++ stack.push_back(Scalar(cfa));
|
|
|
++ stack.back().SetValueType(Value::ValueType::LoadAddress);
|
|
|
++ } else if (error_ptr)
|
|
|
++ error_ptr->SetErrorString("Stack frame does not include a canonical "
|
|
|
++ "frame address for DW_OP_call_frame_cfa "
|
|
|
++ "opcode.");
|
|
|
++ } else {
|
|
|
++ if (error_ptr)
|
|
|
++ error_ptr->SetErrorString("Invalid stack frame in context for "
|
|
|
++ "DW_OP_call_frame_cfa opcode.");
|
|
|
++ return false;
|
|
|
++ }
|
|
|
++ break;
|
|
|
++
|
|
|
++ // OPCODE: DW_OP_form_tls_address (or the old pre-DWARFv3 vendor extension
|
|
|
++ // opcode, DW_OP_GNU_push_tls_address)
|
|
|
++ // OPERANDS: none
|
|
|
++ // DESCRIPTION: Pops a TLS offset from the stack, converts it to
|
|
|
++ // an address in the current thread's thread-local storage block, and
|
|
|
++ // pushes it on the stack.
|
|
|
++ case DW_OP_form_tls_address:
|
|
|
++ case DW_OP_GNU_push_tls_address: {
|
|
|
++ if (stack.size() < 1) {
|
|
|
++ if (error_ptr) {
|
|
|
++ if (op == DW_OP_form_tls_address)
|
|
|
++ error_ptr->SetErrorString(
|
|
|
++ "DW_OP_form_tls_address needs an argument.");
|
|
|
++ else
|
|
|
++ error_ptr->SetErrorString(
|
|
|
++ "DW_OP_GNU_push_tls_address needs an argument.");
|
|
|
++ }
|
|
|
++ return false;
|
|
|
++ }
|
|
|
++
|
|
|
++ if (!m_exe_ctx || !module_sp) {
|
|
|
++ if (error_ptr)
|
|
|
++ error_ptr->SetErrorString("No context to evaluate TLS within.");
|
|
|
++ return false;
|
|
|
++ }
|
|
|
++
|
|
|
++ Thread *thread = m_exe_ctx->GetThreadPtr();
|
|
|
++ if (!thread) {
|
|
|
++ if (error_ptr)
|
|
|
++ error_ptr->SetErrorString("No thread to evaluate TLS within.");
|
|
|
++ return false;
|
|
|
++ }
|
|
|
++
|
|
|
++ // Lookup the TLS block address for this thread and module.
|
|
|
++ const addr_t tls_file_addr =
|
|
|
++ stack.back().GetScalar().ULongLong(LLDB_INVALID_ADDRESS);
|
|
|
++ const addr_t tls_load_addr =
|
|
|
++ thread->GetThreadLocalData(module_sp, tls_file_addr);
|
|
|
++
|
|
|
++ if (tls_load_addr == LLDB_INVALID_ADDRESS) {
|
|
|
++ if (error_ptr)
|
|
|
++ error_ptr->SetErrorString(
|
|
|
++ "No TLS data currently exists for this thread.");
|
|
|
++ return false;
|
|
|
++ }
|
|
|
++
|
|
|
++ stack.back().GetScalar() = tls_load_addr;
|
|
|
++ stack.back().SetValueType(Value::ValueType::LoadAddress);
|
|
|
++ } break;
|
|
|
++
|
|
|
++ // OPCODE: DW_OP_addrx (DW_OP_GNU_addr_index is the legacy name.)
|
|
|
++ // OPERANDS: 1
|
|
|
++ // ULEB128: index to the .debug_addr section
|
|
|
++ // DESCRIPTION: Pushes an address to the stack from the .debug_addr
|
|
|
++ // section with the base address specified by the DW_AT_addr_base attribute
|
|
|
++ // and the 0 based index is the ULEB128 encoded index.
|
|
|
++ case DW_OP_addrx:
|
|
|
++ case DW_OP_GNU_addr_index: {
|
|
|
++ if (!dwarf_cu) {
|
|
|
++ if (error_ptr)
|
|
|
++ error_ptr->SetErrorString("DW_OP_GNU_addr_index found without a "
|
|
|
++ "compile unit being specified");
|
|
|
++ return false;
|
|
|
++ }
|
|
|
++ uint64_t index = opcodes.GetULEB128(&offset);
|
|
|
++ lldb::addr_t value =
|
|
|
++ DWARFExpression::ReadAddressFromDebugAddrSection(dwarf_cu, index);
|
|
|
++ stack.push_back(Scalar(value));
|
|
|
++ stack.back().SetValueType(Value::ValueType::FileAddress);
|
|
|
++ } break;
|
|
|
++
|
|
|
++ // OPCODE: DW_OP_GNU_const_index
|
|
|
++ // OPERANDS: 1
|
|
|
++ // ULEB128: index to the .debug_addr section
|
|
|
++ // DESCRIPTION: Pushes an constant with the size of a machine address to
|
|
|
++ // the stack from the .debug_addr section with the base address specified
|
|
|
++ // by the DW_AT_addr_base attribute and the 0 based index is the ULEB128
|
|
|
++ // encoded index.
|
|
|
++ case DW_OP_GNU_const_index: {
|
|
|
++ if (!dwarf_cu) {
|
|
|
++ if (error_ptr)
|
|
|
++ error_ptr->SetErrorString("DW_OP_GNU_const_index found without a "
|
|
|
++ "compile unit being specified");
|
|
|
++ return false;
|
|
|
++ }
|
|
|
++ uint64_t index = opcodes.GetULEB128(&offset);
|
|
|
++ lldb::addr_t value =
|
|
|
++ DWARFExpression::ReadAddressFromDebugAddrSection(dwarf_cu, index);
|
|
|
++ stack.push_back(Scalar(value));
|
|
|
++ } break;
|
|
|
++
|
|
|
++ case DW_OP_entry_value: {
|
|
|
++ if (!Evaluate_DW_OP_entry_value(stack, m_exe_ctx, m_reg_ctx, opcodes,
|
|
|
++ offset, error_ptr, log)) {
|
|
|
++ LLDB_ERRORF(error_ptr, "Could not evaluate %s.", DW_OP_value_to_name(op));
|
|
|
++ return false;
|
|
|
++ }
|
|
|
++ break;
|
|
|
++ }
|
|
|
++
|
|
|
++ default:
|
|
|
++ LLDB_LOGF(log, "Unhandled opcode %s in DWARFExpression.",
|
|
|
++ DW_OP_value_to_name(op));
|
|
|
++ break;
|
|
|
++ }
|
|
|
++
|
|
|
++ return true;
|
|
|
++}
|
|
|
+diff --git a/lldb/source/Expression/DWARFEvaluatorFactory.cpp b/lldb/source/Expression/DWARFEvaluatorFactory.cpp
|
|
|
+new file mode 100644
|
|
|
+index 000000000000..c0612641204a
|
|
|
+--- /dev/null
|
|
|
++++ b/lldb/source/Expression/DWARFEvaluatorFactory.cpp
|
|
|
+@@ -0,0 +1,57 @@
|
|
|
++//===-- DWARFEvaluatorFactory.cpp -----------------------------------------===//
|
|
|
++//
|
|
|
++// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
|
|
++// See https://llvm.org/LICENSE.txt for license information.
|
|
|
++// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
|
|
++//
|
|
|
++//===----------------------------------------------------------------------===//
|
|
|
++
|
|
|
++#include "lldb/Expression/DWARFEvaluatorFactory.h"
|
|
|
++#include "lldb/Expression/DWARFEvaluator.h"
|
|
|
++
|
|
|
++#include "lldb/Core/PluginManager.h"
|
|
|
++#include "lldb/Core/Value.h"
|
|
|
++#include "lldb/Target/RegisterContext.h"
|
|
|
++
|
|
|
++using namespace lldb;
|
|
|
++using namespace lldb_private;
|
|
|
++
|
|
|
++// PluginInterface protocol
|
|
|
++lldb_private::ConstString DWARFEvaluatorFactory::GetPluginName() {
|
|
|
++ static ConstString g_name("vendor-default");
|
|
|
++ return g_name;
|
|
|
++}
|
|
|
++
|
|
|
++// FindPlugin
|
|
|
++//
|
|
|
++// Platforms can register a callback to use when creating DWARF expression
|
|
|
++// evaluators to allow handling platform-specific DWARF codes.
|
|
|
++std::unique_ptr<DWARFEvaluatorFactory>
|
|
|
++DWARFEvaluatorFactory::FindPlugin(Module *module) {
|
|
|
++ std::unique_ptr<DWARFEvaluatorFactory> instance_up;
|
|
|
++ DWARFEvaluatorFactoryCreateInstance create_callback;
|
|
|
++
|
|
|
++ for (size_t idx = 0;
|
|
|
++ (create_callback =
|
|
|
++ PluginManager::GetDWARFEvaluatorFactoryCreateCallbackAtIndex(
|
|
|
++ idx)) != nullptr;
|
|
|
++ ++idx) {
|
|
|
++ instance_up.reset(create_callback(module));
|
|
|
++
|
|
|
++ if (instance_up) {
|
|
|
++ return instance_up;
|
|
|
++ }
|
|
|
++ }
|
|
|
++
|
|
|
++ instance_up.reset(new DWARFEvaluatorFactory());
|
|
|
++ return instance_up;
|
|
|
++}
|
|
|
++
|
|
|
++std::unique_ptr<DWARFEvaluator> DWARFEvaluatorFactory::CreateDWARFEvaluator(
|
|
|
++ const DWARFExpression &dwarf_expression, ExecutionContext *exe_ctx,
|
|
|
++ RegisterContext *reg_ctx, const Value *initial_value_ptr,
|
|
|
++ const Value *object_address_ptr) {
|
|
|
++ return std::make_unique<DWARFEvaluator>(dwarf_expression, exe_ctx, reg_ctx,
|
|
|
++ initial_value_ptr,
|
|
|
++ object_address_ptr);
|
|
|
++}
|
|
|
+diff --git a/lldb/source/Expression/DWARFExpression.cpp b/lldb/source/Expression/DWARFExpression.cpp
|
|
|
+index a10546c1deae..4d13e4642af3 100644
|
|
|
+--- a/lldb/source/Expression/DWARFExpression.cpp
|
|
|
++++ b/lldb/source/Expression/DWARFExpression.cpp
|
|
|
+@@ -15,6 +15,8 @@
|
|
|
+ #include "lldb/Core/Module.h"
|
|
|
+ #include "lldb/Core/Value.h"
|
|
|
+ #include "lldb/Core/dwarf.h"
|
|
|
++#include "lldb/Expression/DWARFEvaluator.h"
|
|
|
++#include "lldb/Expression/DWARFEvaluatorFactory.h"
|
|
|
+ #include "lldb/Utility/DataEncoder.h"
|
|
|
+ #include "lldb/Utility/Log.h"
|
|
|
+ #include "lldb/Utility/RegisterValue.h"
|
|
|
+@@ -41,8 +43,8 @@
|
|
|
+ using namespace lldb;
|
|
|
+ using namespace lldb_private;
|
|
|
+
|
|
|
+-static lldb::addr_t
|
|
|
+-ReadAddressFromDebugAddrSection(const DWARFUnit *dwarf_cu,
|
|
|
++lldb::addr_t
|
|
|
++DWARFExpression::ReadAddressFromDebugAddrSection(const DWARFUnit *dwarf_cu,
|
|
|
+ uint32_t index) {
|
|
|
+ uint32_t index_size = dwarf_cu->GetAddressByteSize();
|
|
|
+ dw_offset_t addr_base = dwarf_cu->GetAddrBase();
|
|
|
+@@ -96,7 +98,7 @@ void DWARFExpression::SetLocationListAddresses(addr_t cu_file_addr,
|
|
|
+ m_loclist_addresses = LoclistAddresses{cu_file_addr, func_file_addr};
|
|
|
+ }
|
|
|
+
|
|
|
+-int DWARFExpression::GetRegisterKind() { return m_reg_kind; }
|
|
|
++RegisterKind DWARFExpression::GetRegisterKind() const { return m_reg_kind; }
|
|
|
+
|
|
|
+ void DWARFExpression::SetRegisterKind(RegisterKind reg_kind) {
|
|
|
+ m_reg_kind = reg_kind;
|
|
|
+@@ -150,52 +152,6 @@ void DWARFExpression::GetDescription(Stream *s, lldb::DescriptionLevel level,
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+-static bool ReadRegisterValueAsScalar(RegisterContext *reg_ctx,
|
|
|
+- lldb::RegisterKind reg_kind,
|
|
|
+- uint32_t reg_num, Status *error_ptr,
|
|
|
+- Value &value) {
|
|
|
+- if (reg_ctx == nullptr) {
|
|
|
+- if (error_ptr)
|
|
|
+- error_ptr->SetErrorString("No register context in frame.\n");
|
|
|
+- } else {
|
|
|
+- uint32_t native_reg =
|
|
|
+- reg_ctx->ConvertRegisterKindToRegisterNumber(reg_kind, reg_num);
|
|
|
+- if (native_reg == LLDB_INVALID_REGNUM) {
|
|
|
+- if (error_ptr)
|
|
|
+- error_ptr->SetErrorStringWithFormat("Unable to convert register "
|
|
|
+- "kind=%u reg_num=%u to a native "
|
|
|
+- "register number.\n",
|
|
|
+- reg_kind, reg_num);
|
|
|
+- } else {
|
|
|
+- const RegisterInfo *reg_info =
|
|
|
+- reg_ctx->GetRegisterInfoAtIndex(native_reg);
|
|
|
+- RegisterValue reg_value;
|
|
|
+- if (reg_ctx->ReadRegister(reg_info, reg_value)) {
|
|
|
+- if (reg_value.GetScalarValue(value.GetScalar())) {
|
|
|
+- value.SetValueType(Value::ValueType::Scalar);
|
|
|
+- value.SetContext(Value::ContextType::RegisterInfo,
|
|
|
+- const_cast<RegisterInfo *>(reg_info));
|
|
|
+- if (error_ptr)
|
|
|
+- error_ptr->Clear();
|
|
|
+- return true;
|
|
|
+- } else {
|
|
|
+- // If we get this error, then we need to implement a value buffer in
|
|
|
+- // the dwarf expression evaluation function...
|
|
|
+- if (error_ptr)
|
|
|
+- error_ptr->SetErrorStringWithFormat(
|
|
|
+- "register %s can't be converted to a scalar value",
|
|
|
+- reg_info->name);
|
|
|
+- }
|
|
|
+- } else {
|
|
|
+- if (error_ptr)
|
|
|
+- error_ptr->SetErrorStringWithFormat("register %s is not available",
|
|
|
+- reg_info->name);
|
|
|
+- }
|
|
|
+- }
|
|
|
+- }
|
|
|
+- return false;
|
|
|
+-}
|
|
|
+-
|
|
|
+ /// Return the length in bytes of the set of operands for \p op. No guarantees
|
|
|
+ /// are made on the state of \p data after this call.
|
|
|
+ static offset_t GetOpcodeDataSize(const DataExtractor &data,
|
|
|
+@@ -955,1719 +911,17 @@ bool DWARFExpression::Evaluate(
|
|
|
+ const Value *initial_value_ptr, const Value *object_address_ptr,
|
|
|
+ Value &result, Status *error_ptr) {
|
|
|
+
|
|
|
+- if (opcodes.GetByteSize() == 0) {
|
|
|
+- if (error_ptr)
|
|
|
+- error_ptr->SetErrorString(
|
|
|
+- "no location, value may have been optimized out");
|
|
|
+- return false;
|
|
|
+- }
|
|
|
+- std::vector<Value> stack;
|
|
|
+-
|
|
|
+- Process *process = nullptr;
|
|
|
+- StackFrame *frame = nullptr;
|
|
|
+-
|
|
|
+- if (exe_ctx) {
|
|
|
+- process = exe_ctx->GetProcessPtr();
|
|
|
+- frame = exe_ctx->GetFramePtr();
|
|
|
+- }
|
|
|
+- if (reg_ctx == nullptr && frame)
|
|
|
+- reg_ctx = frame->GetRegisterContext().get();
|
|
|
+-
|
|
|
+- if (initial_value_ptr)
|
|
|
+- stack.push_back(*initial_value_ptr);
|
|
|
+-
|
|
|
+- lldb::offset_t offset = 0;
|
|
|
+- Value tmp;
|
|
|
+- uint32_t reg_num;
|
|
|
+-
|
|
|
+- /// Insertion point for evaluating multi-piece expression.
|
|
|
+- uint64_t op_piece_offset = 0;
|
|
|
+- Value pieces; // Used for DW_OP_piece
|
|
|
+-
|
|
|
+- Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_EXPRESSIONS));
|
|
|
+- // A generic type is "an integral type that has the size of an address and an
|
|
|
+- // unspecified signedness". For now, just use the signedness of the operand.
|
|
|
+- // TODO: Implement a real typed stack, and store the genericness of the value
|
|
|
+- // there.
|
|
|
+- auto to_generic = [&](auto v) {
|
|
|
+- bool is_signed = std::is_signed<decltype(v)>::value;
|
|
|
+- return Scalar(llvm::APSInt(
|
|
|
+- llvm::APInt(8 * opcodes.GetAddressByteSize(), v, is_signed),
|
|
|
+- !is_signed));
|
|
|
+- };
|
|
|
+-
|
|
|
+- // The default kind is a memory location. This is updated by any
|
|
|
+- // operation that changes this, such as DW_OP_stack_value, and reset
|
|
|
+- // by composition operations like DW_OP_piece.
|
|
|
+- LocationDescriptionKind dwarf4_location_description_kind = Memory;
|
|
|
+-
|
|
|
+- while (opcodes.ValidOffset(offset)) {
|
|
|
+- const lldb::offset_t op_offset = offset;
|
|
|
+- const uint8_t op = opcodes.GetU8(&offset);
|
|
|
+-
|
|
|
+- if (log && log->GetVerbose()) {
|
|
|
+- size_t count = stack.size();
|
|
|
+- LLDB_LOGF(log, "Stack before operation has %" PRIu64 " values:",
|
|
|
+- (uint64_t)count);
|
|
|
+- for (size_t i = 0; i < count; ++i) {
|
|
|
+- StreamString new_value;
|
|
|
+- new_value.Printf("[%" PRIu64 "]", (uint64_t)i);
|
|
|
+- stack[i].Dump(&new_value);
|
|
|
+- LLDB_LOGF(log, " %s", new_value.GetData());
|
|
|
+- }
|
|
|
+- LLDB_LOGF(log, "0x%8.8" PRIx64 ": %s", op_offset,
|
|
|
+- DW_OP_value_to_name(op));
|
|
|
+- }
|
|
|
+-
|
|
|
+- switch (op) {
|
|
|
+- // The DW_OP_addr operation has a single operand that encodes a machine
|
|
|
+- // address and whose size is the size of an address on the target machine.
|
|
|
+- case DW_OP_addr:
|
|
|
+- stack.push_back(Scalar(opcodes.GetAddress(&offset)));
|
|
|
+- stack.back().SetValueType(Value::ValueType::FileAddress);
|
|
|
+- // Convert the file address to a load address, so subsequent
|
|
|
+- // DWARF operators can operate on it.
|
|
|
+- if (frame)
|
|
|
+- stack.back().ConvertToLoadAddress(module_sp.get(),
|
|
|
+- frame->CalculateTarget().get());
|
|
|
+- break;
|
|
|
+-
|
|
|
+- // The DW_OP_addr_sect_offset4 is used for any location expressions in
|
|
|
+- // shared libraries that have a location like:
|
|
|
+- // DW_OP_addr(0x1000)
|
|
|
+- // If this address resides in a shared library, then this virtual address
|
|
|
+- // won't make sense when it is evaluated in the context of a running
|
|
|
+- // process where shared libraries have been slid. To account for this, this
|
|
|
+- // new address type where we can store the section pointer and a 4 byte
|
|
|
+- // offset.
|
|
|
+- // case DW_OP_addr_sect_offset4:
|
|
|
+- // {
|
|
|
+- // result_type = eResultTypeFileAddress;
|
|
|
+- // lldb::Section *sect = (lldb::Section
|
|
|
+- // *)opcodes.GetMaxU64(&offset, sizeof(void *));
|
|
|
+- // lldb::addr_t sect_offset = opcodes.GetU32(&offset);
|
|
|
+- //
|
|
|
+- // Address so_addr (sect, sect_offset);
|
|
|
+- // lldb::addr_t load_addr = so_addr.GetLoadAddress();
|
|
|
+- // if (load_addr != LLDB_INVALID_ADDRESS)
|
|
|
+- // {
|
|
|
+- // // We successfully resolve a file address to a load
|
|
|
+- // // address.
|
|
|
+- // stack.push_back(load_addr);
|
|
|
+- // break;
|
|
|
+- // }
|
|
|
+- // else
|
|
|
+- // {
|
|
|
+- // // We were able
|
|
|
+- // if (error_ptr)
|
|
|
+- // error_ptr->SetErrorStringWithFormat ("Section %s in
|
|
|
+- // %s is not currently loaded.\n",
|
|
|
+- // sect->GetName().AsCString(),
|
|
|
+- // sect->GetModule()->GetFileSpec().GetFilename().AsCString());
|
|
|
+- // return false;
|
|
|
+- // }
|
|
|
+- // }
|
|
|
+- // break;
|
|
|
+-
|
|
|
+- // OPCODE: DW_OP_deref
|
|
|
+- // OPERANDS: none
|
|
|
+- // DESCRIPTION: Pops the top stack entry and treats it as an address.
|
|
|
+- // The value retrieved from that address is pushed. The size of the data
|
|
|
+- // retrieved from the dereferenced address is the size of an address on the
|
|
|
+- // target machine.
|
|
|
+- case DW_OP_deref: {
|
|
|
+- if (stack.empty()) {
|
|
|
+- if (error_ptr)
|
|
|
+- error_ptr->SetErrorString("Expression stack empty for DW_OP_deref.");
|
|
|
+- return false;
|
|
|
+- }
|
|
|
+- Value::ValueType value_type = stack.back().GetValueType();
|
|
|
+- switch (value_type) {
|
|
|
+- case Value::ValueType::HostAddress: {
|
|
|
+- void *src = (void *)stack.back().GetScalar().ULongLong();
|
|
|
+- intptr_t ptr;
|
|
|
+- ::memcpy(&ptr, src, sizeof(void *));
|
|
|
+- stack.back().GetScalar() = ptr;
|
|
|
+- stack.back().ClearContext();
|
|
|
+- } break;
|
|
|
+- case Value::ValueType::FileAddress: {
|
|
|
+- auto file_addr = stack.back().GetScalar().ULongLong(
|
|
|
+- LLDB_INVALID_ADDRESS);
|
|
|
+- if (!module_sp) {
|
|
|
+- if (error_ptr)
|
|
|
+- error_ptr->SetErrorString(
|
|
|
+- "need module to resolve file address for DW_OP_deref");
|
|
|
+- return false;
|
|
|
+- }
|
|
|
+- Address so_addr;
|
|
|
+- if (!module_sp->ResolveFileAddress(file_addr, so_addr)) {
|
|
|
+- if (error_ptr)
|
|
|
+- error_ptr->SetErrorString(
|
|
|
+- "failed to resolve file address in module");
|
|
|
+- return false;
|
|
|
+- }
|
|
|
+- addr_t load_Addr = so_addr.GetLoadAddress(exe_ctx->GetTargetPtr());
|
|
|
+- if (load_Addr == LLDB_INVALID_ADDRESS) {
|
|
|
+- if (error_ptr)
|
|
|
+- error_ptr->SetErrorString("failed to resolve load address");
|
|
|
+- return false;
|
|
|
+- }
|
|
|
+- stack.back().GetScalar() = load_Addr;
|
|
|
+- // Fall through to load address promotion code below.
|
|
|
+- } LLVM_FALLTHROUGH;
|
|
|
+- case Value::ValueType::Scalar:
|
|
|
+- // Promote Scalar to LoadAddress and fall through.
|
|
|
+- stack.back().SetValueType(Value::ValueType::LoadAddress);
|
|
|
+- LLVM_FALLTHROUGH;
|
|
|
+- case Value::ValueType::LoadAddress:
|
|
|
+- if (exe_ctx) {
|
|
|
+- if (process) {
|
|
|
+- lldb::addr_t pointer_addr =
|
|
|
+- stack.back().GetScalar().ULongLong(LLDB_INVALID_ADDRESS);
|
|
|
+- Status error;
|
|
|
+- lldb::addr_t pointer_value =
|
|
|
+- process->ReadPointerFromMemory(pointer_addr, error);
|
|
|
+- if (pointer_value != LLDB_INVALID_ADDRESS) {
|
|
|
+- if (ABISP abi_sp = process->GetABI())
|
|
|
+- pointer_value = abi_sp->FixCodeAddress(pointer_value);
|
|
|
+- stack.back().GetScalar() = pointer_value;
|
|
|
+- stack.back().ClearContext();
|
|
|
+- } else {
|
|
|
+- if (error_ptr)
|
|
|
+- error_ptr->SetErrorStringWithFormat(
|
|
|
+- "Failed to dereference pointer from 0x%" PRIx64
|
|
|
+- " for DW_OP_deref: %s\n",
|
|
|
+- pointer_addr, error.AsCString());
|
|
|
+- return false;
|
|
|
+- }
|
|
|
+- } else {
|
|
|
+- if (error_ptr)
|
|
|
+- error_ptr->SetErrorString("NULL process for DW_OP_deref.\n");
|
|
|
+- return false;
|
|
|
+- }
|
|
|
+- } else {
|
|
|
+- if (error_ptr)
|
|
|
+- error_ptr->SetErrorString(
|
|
|
+- "NULL execution context for DW_OP_deref.\n");
|
|
|
+- return false;
|
|
|
+- }
|
|
|
+- break;
|
|
|
+-
|
|
|
+- case Value::ValueType::Invalid:
|
|
|
+- if (error_ptr)
|
|
|
+- error_ptr->SetErrorString("Invalid value type for DW_OP_deref.\n");
|
|
|
+- return false;
|
|
|
+- }
|
|
|
+-
|
|
|
+- } break;
|
|
|
+-
|
|
|
+- // OPCODE: DW_OP_deref_size
|
|
|
+- // OPERANDS: 1
|
|
|
+- // 1 - uint8_t that specifies the size of the data to dereference.
|
|
|
+- // DESCRIPTION: Behaves like the DW_OP_deref operation: it pops the top
|
|
|
+- // stack entry and treats it as an address. The value retrieved from that
|
|
|
+- // address is pushed. In the DW_OP_deref_size operation, however, the size
|
|
|
+- // in bytes of the data retrieved from the dereferenced address is
|
|
|
+- // specified by the single operand. This operand is a 1-byte unsigned
|
|
|
+- // integral constant whose value may not be larger than the size of an
|
|
|
+- // address on the target machine. The data retrieved is zero extended to
|
|
|
+- // the size of an address on the target machine before being pushed on the
|
|
|
+- // expression stack.
|
|
|
+- case DW_OP_deref_size: {
|
|
|
+- if (stack.empty()) {
|
|
|
+- if (error_ptr)
|
|
|
+- error_ptr->SetErrorString(
|
|
|
+- "Expression stack empty for DW_OP_deref_size.");
|
|
|
+- return false;
|
|
|
+- }
|
|
|
+- uint8_t size = opcodes.GetU8(&offset);
|
|
|
+- Value::ValueType value_type = stack.back().GetValueType();
|
|
|
+- switch (value_type) {
|
|
|
+- case Value::ValueType::HostAddress: {
|
|
|
+- void *src = (void *)stack.back().GetScalar().ULongLong();
|
|
|
+- intptr_t ptr;
|
|
|
+- ::memcpy(&ptr, src, sizeof(void *));
|
|
|
+- // I can't decide whether the size operand should apply to the bytes in
|
|
|
+- // their
|
|
|
+- // lldb-host endianness or the target endianness.. I doubt this'll ever
|
|
|
+- // come up but I'll opt for assuming big endian regardless.
|
|
|
+- switch (size) {
|
|
|
+- case 1:
|
|
|
+- ptr = ptr & 0xff;
|
|
|
+- break;
|
|
|
+- case 2:
|
|
|
+- ptr = ptr & 0xffff;
|
|
|
+- break;
|
|
|
+- case 3:
|
|
|
+- ptr = ptr & 0xffffff;
|
|
|
+- break;
|
|
|
+- case 4:
|
|
|
+- ptr = ptr & 0xffffffff;
|
|
|
+- break;
|
|
|
+- // the casts are added to work around the case where intptr_t is a 32
|
|
|
+- // bit quantity;
|
|
|
+- // presumably we won't hit the 5..7 cases if (void*) is 32-bits in this
|
|
|
+- // program.
|
|
|
+- case 5:
|
|
|
+- ptr = (intptr_t)ptr & 0xffffffffffULL;
|
|
|
+- break;
|
|
|
+- case 6:
|
|
|
+- ptr = (intptr_t)ptr & 0xffffffffffffULL;
|
|
|
+- break;
|
|
|
+- case 7:
|
|
|
+- ptr = (intptr_t)ptr & 0xffffffffffffffULL;
|
|
|
+- break;
|
|
|
+- default:
|
|
|
+- break;
|
|
|
+- }
|
|
|
+- stack.back().GetScalar() = ptr;
|
|
|
+- stack.back().ClearContext();
|
|
|
+- } break;
|
|
|
+- case Value::ValueType::Scalar:
|
|
|
+- case Value::ValueType::LoadAddress:
|
|
|
+- if (exe_ctx) {
|
|
|
+- if (process) {
|
|
|
+- lldb::addr_t pointer_addr =
|
|
|
+- stack.back().GetScalar().ULongLong(LLDB_INVALID_ADDRESS);
|
|
|
+- uint8_t addr_bytes[sizeof(lldb::addr_t)];
|
|
|
+- Status error;
|
|
|
+- if (process->ReadMemory(pointer_addr, &addr_bytes, size, error) ==
|
|
|
+- size) {
|
|
|
+- DataExtractor addr_data(addr_bytes, sizeof(addr_bytes),
|
|
|
+- process->GetByteOrder(), size);
|
|
|
+- lldb::offset_t addr_data_offset = 0;
|
|
|
+- switch (size) {
|
|
|
+- case 1:
|
|
|
+- stack.back().GetScalar() = addr_data.GetU8(&addr_data_offset);
|
|
|
+- break;
|
|
|
+- case 2:
|
|
|
+- stack.back().GetScalar() = addr_data.GetU16(&addr_data_offset);
|
|
|
+- break;
|
|
|
+- case 4:
|
|
|
+- stack.back().GetScalar() = addr_data.GetU32(&addr_data_offset);
|
|
|
+- break;
|
|
|
+- case 8:
|
|
|
+- stack.back().GetScalar() = addr_data.GetU64(&addr_data_offset);
|
|
|
+- break;
|
|
|
+- default:
|
|
|
+- stack.back().GetScalar() =
|
|
|
+- addr_data.GetAddress(&addr_data_offset);
|
|
|
+- }
|
|
|
+- stack.back().ClearContext();
|
|
|
+- } else {
|
|
|
+- if (error_ptr)
|
|
|
+- error_ptr->SetErrorStringWithFormat(
|
|
|
+- "Failed to dereference pointer from 0x%" PRIx64
|
|
|
+- " for DW_OP_deref: %s\n",
|
|
|
+- pointer_addr, error.AsCString());
|
|
|
+- return false;
|
|
|
+- }
|
|
|
+- } else {
|
|
|
+- if (error_ptr)
|
|
|
+- error_ptr->SetErrorString("NULL process for DW_OP_deref_size.\n");
|
|
|
+- return false;
|
|
|
+- }
|
|
|
+- } else {
|
|
|
+- if (error_ptr)
|
|
|
+- error_ptr->SetErrorString(
|
|
|
+- "NULL execution context for DW_OP_deref_size.\n");
|
|
|
+- return false;
|
|
|
+- }
|
|
|
+- break;
|
|
|
+-
|
|
|
+- case Value::ValueType::FileAddress:
|
|
|
+- case Value::ValueType::Invalid:
|
|
|
+- if (error_ptr)
|
|
|
+- error_ptr->SetErrorString("Invalid value for DW_OP_deref_size.\n");
|
|
|
+- return false;
|
|
|
+- }
|
|
|
+-
|
|
|
+- } break;
|
|
|
+-
|
|
|
+- // OPCODE: DW_OP_xderef_size
|
|
|
+- // OPERANDS: 1
|
|
|
+- // 1 - uint8_t that specifies the size of the data to dereference.
|
|
|
+- // DESCRIPTION: Behaves like the DW_OP_xderef operation: the entry at
|
|
|
+- // the top of the stack is treated as an address. The second stack entry is
|
|
|
+- // treated as an "address space identifier" for those architectures that
|
|
|
+- // support multiple address spaces. The top two stack elements are popped,
|
|
|
+- // a data item is retrieved through an implementation-defined address
|
|
|
+- // calculation and pushed as the new stack top. In the DW_OP_xderef_size
|
|
|
+- // operation, however, the size in bytes of the data retrieved from the
|
|
|
+- // dereferenced address is specified by the single operand. This operand is
|
|
|
+- // a 1-byte unsigned integral constant whose value may not be larger than
|
|
|
+- // the size of an address on the target machine. The data retrieved is zero
|
|
|
+- // extended to the size of an address on the target machine before being
|
|
|
+- // pushed on the expression stack.
|
|
|
+- case DW_OP_xderef_size:
|
|
|
+- if (error_ptr)
|
|
|
+- error_ptr->SetErrorString("Unimplemented opcode: DW_OP_xderef_size.");
|
|
|
+- return false;
|
|
|
+- // OPCODE: DW_OP_xderef
|
|
|
+- // OPERANDS: none
|
|
|
+- // DESCRIPTION: Provides an extended dereference mechanism. The entry at
|
|
|
+- // the top of the stack is treated as an address. The second stack entry is
|
|
|
+- // treated as an "address space identifier" for those architectures that
|
|
|
+- // support multiple address spaces. The top two stack elements are popped,
|
|
|
+- // a data item is retrieved through an implementation-defined address
|
|
|
+- // calculation and pushed as the new stack top. The size of the data
|
|
|
+- // retrieved from the dereferenced address is the size of an address on the
|
|
|
+- // target machine.
|
|
|
+- case DW_OP_xderef:
|
|
|
+- if (error_ptr)
|
|
|
+- error_ptr->SetErrorString("Unimplemented opcode: DW_OP_xderef.");
|
|
|
+- return false;
|
|
|
+-
|
|
|
+- // All DW_OP_constXXX opcodes have a single operand as noted below:
|
|
|
+- //
|
|
|
+- // Opcode Operand 1
|
|
|
+- // DW_OP_const1u 1-byte unsigned integer constant
|
|
|
+- // DW_OP_const1s 1-byte signed integer constant
|
|
|
+- // DW_OP_const2u 2-byte unsigned integer constant
|
|
|
+- // DW_OP_const2s 2-byte signed integer constant
|
|
|
+- // DW_OP_const4u 4-byte unsigned integer constant
|
|
|
+- // DW_OP_const4s 4-byte signed integer constant
|
|
|
+- // DW_OP_const8u 8-byte unsigned integer constant
|
|
|
+- // DW_OP_const8s 8-byte signed integer constant
|
|
|
+- // DW_OP_constu unsigned LEB128 integer constant
|
|
|
+- // DW_OP_consts signed LEB128 integer constant
|
|
|
+- case DW_OP_const1u:
|
|
|
+- stack.push_back(to_generic(opcodes.GetU8(&offset)));
|
|
|
+- break;
|
|
|
+- case DW_OP_const1s:
|
|
|
+- stack.push_back(to_generic((int8_t)opcodes.GetU8(&offset)));
|
|
|
+- break;
|
|
|
+- case DW_OP_const2u:
|
|
|
+- stack.push_back(to_generic(opcodes.GetU16(&offset)));
|
|
|
+- break;
|
|
|
+- case DW_OP_const2s:
|
|
|
+- stack.push_back(to_generic((int16_t)opcodes.GetU16(&offset)));
|
|
|
+- break;
|
|
|
+- case DW_OP_const4u:
|
|
|
+- stack.push_back(to_generic(opcodes.GetU32(&offset)));
|
|
|
+- break;
|
|
|
+- case DW_OP_const4s:
|
|
|
+- stack.push_back(to_generic((int32_t)opcodes.GetU32(&offset)));
|
|
|
+- break;
|
|
|
+- case DW_OP_const8u:
|
|
|
+- stack.push_back(to_generic(opcodes.GetU64(&offset)));
|
|
|
+- break;
|
|
|
+- case DW_OP_const8s:
|
|
|
+- stack.push_back(to_generic((int64_t)opcodes.GetU64(&offset)));
|
|
|
+- break;
|
|
|
+- // These should also use to_generic, but we can't do that due to a
|
|
|
+- // producer-side bug in llvm. See llvm.org/pr48087.
|
|
|
+- case DW_OP_constu:
|
|
|
+- stack.push_back(Scalar(opcodes.GetULEB128(&offset)));
|
|
|
+- break;
|
|
|
+- case DW_OP_consts:
|
|
|
+- stack.push_back(Scalar(opcodes.GetSLEB128(&offset)));
|
|
|
+- break;
|
|
|
+-
|
|
|
+- // OPCODE: DW_OP_dup
|
|
|
+- // OPERANDS: none
|
|
|
+- // DESCRIPTION: duplicates the value at the top of the stack
|
|
|
+- case DW_OP_dup:
|
|
|
+- if (stack.empty()) {
|
|
|
+- if (error_ptr)
|
|
|
+- error_ptr->SetErrorString("Expression stack empty for DW_OP_dup.");
|
|
|
+- return false;
|
|
|
+- } else
|
|
|
+- stack.push_back(stack.back());
|
|
|
+- break;
|
|
|
+-
|
|
|
+- // OPCODE: DW_OP_drop
|
|
|
+- // OPERANDS: none
|
|
|
+- // DESCRIPTION: pops the value at the top of the stack
|
|
|
+- case DW_OP_drop:
|
|
|
+- if (stack.empty()) {
|
|
|
+- if (error_ptr)
|
|
|
+- error_ptr->SetErrorString("Expression stack empty for DW_OP_drop.");
|
|
|
+- return false;
|
|
|
+- } else
|
|
|
+- stack.pop_back();
|
|
|
+- break;
|
|
|
+-
|
|
|
+- // OPCODE: DW_OP_over
|
|
|
+- // OPERANDS: none
|
|
|
+- // DESCRIPTION: Duplicates the entry currently second in the stack at
|
|
|
+- // the top of the stack.
|
|
|
+- case DW_OP_over:
|
|
|
+- if (stack.size() < 2) {
|
|
|
+- if (error_ptr)
|
|
|
+- error_ptr->SetErrorString(
|
|
|
+- "Expression stack needs at least 2 items for DW_OP_over.");
|
|
|
+- return false;
|
|
|
+- } else
|
|
|
+- stack.push_back(stack[stack.size() - 2]);
|
|
|
+- break;
|
|
|
+-
|
|
|
+- // OPCODE: DW_OP_pick
|
|
|
+- // OPERANDS: uint8_t index into the current stack
|
|
|
+- // DESCRIPTION: The stack entry with the specified index (0 through 255,
|
|
|
+- // inclusive) is pushed on the stack
|
|
|
+- case DW_OP_pick: {
|
|
|
+- uint8_t pick_idx = opcodes.GetU8(&offset);
|
|
|
+- if (pick_idx < stack.size())
|
|
|
+- stack.push_back(stack[stack.size() - 1 - pick_idx]);
|
|
|
+- else {
|
|
|
+- if (error_ptr)
|
|
|
+- error_ptr->SetErrorStringWithFormat(
|
|
|
+- "Index %u out of range for DW_OP_pick.\n", pick_idx);
|
|
|
+- return false;
|
|
|
+- }
|
|
|
+- } break;
|
|
|
+-
|
|
|
+- // OPCODE: DW_OP_swap
|
|
|
+- // OPERANDS: none
|
|
|
+- // DESCRIPTION: swaps the top two stack entries. The entry at the top
|
|
|
+- // of the stack becomes the second stack entry, and the second entry
|
|
|
+- // becomes the top of the stack
|
|
|
+- case DW_OP_swap:
|
|
|
+- if (stack.size() < 2) {
|
|
|
+- if (error_ptr)
|
|
|
+- error_ptr->SetErrorString(
|
|
|
+- "Expression stack needs at least 2 items for DW_OP_swap.");
|
|
|
+- return false;
|
|
|
+- } else {
|
|
|
+- tmp = stack.back();
|
|
|
+- stack.back() = stack[stack.size() - 2];
|
|
|
+- stack[stack.size() - 2] = tmp;
|
|
|
+- }
|
|
|
+- break;
|
|
|
+-
|
|
|
+- // OPCODE: DW_OP_rot
|
|
|
+- // OPERANDS: none
|
|
|
+- // DESCRIPTION: Rotates the first three stack entries. The entry at
|
|
|
+- // the top of the stack becomes the third stack entry, the second entry
|
|
|
+- // becomes the top of the stack, and the third entry becomes the second
|
|
|
+- // entry.
|
|
|
+- case DW_OP_rot:
|
|
|
+- if (stack.size() < 3) {
|
|
|
+- if (error_ptr)
|
|
|
+- error_ptr->SetErrorString(
|
|
|
+- "Expression stack needs at least 3 items for DW_OP_rot.");
|
|
|
+- return false;
|
|
|
+- } else {
|
|
|
+- size_t last_idx = stack.size() - 1;
|
|
|
+- Value old_top = stack[last_idx];
|
|
|
+- stack[last_idx] = stack[last_idx - 1];
|
|
|
+- stack[last_idx - 1] = stack[last_idx - 2];
|
|
|
+- stack[last_idx - 2] = old_top;
|
|
|
+- }
|
|
|
+- break;
|
|
|
+-
|
|
|
+- // OPCODE: DW_OP_abs
|
|
|
+- // OPERANDS: none
|
|
|
+- // DESCRIPTION: pops the top stack entry, interprets it as a signed
|
|
|
+- // value and pushes its absolute value. If the absolute value can not be
|
|
|
+- // represented, the result is undefined.
|
|
|
+- case DW_OP_abs:
|
|
|
+- if (stack.empty()) {
|
|
|
+- if (error_ptr)
|
|
|
+- error_ptr->SetErrorString(
|
|
|
+- "Expression stack needs at least 1 item for DW_OP_abs.");
|
|
|
+- return false;
|
|
|
+- } else if (!stack.back().ResolveValue(exe_ctx).AbsoluteValue()) {
|
|
|
+- if (error_ptr)
|
|
|
+- error_ptr->SetErrorString(
|
|
|
+- "Failed to take the absolute value of the first stack item.");
|
|
|
+- return false;
|
|
|
+- }
|
|
|
+- break;
|
|
|
+-
|
|
|
+- // OPCODE: DW_OP_and
|
|
|
+- // OPERANDS: none
|
|
|
+- // DESCRIPTION: pops the top two stack values, performs a bitwise and
|
|
|
+- // operation on the two, and pushes the result.
|
|
|
+- case DW_OP_and:
|
|
|
+- if (stack.size() < 2) {
|
|
|
+- if (error_ptr)
|
|
|
+- error_ptr->SetErrorString(
|
|
|
+- "Expression stack needs at least 2 items for DW_OP_and.");
|
|
|
+- return false;
|
|
|
+- } else {
|
|
|
+- tmp = stack.back();
|
|
|
+- stack.pop_back();
|
|
|
+- stack.back().ResolveValue(exe_ctx) =
|
|
|
+- stack.back().ResolveValue(exe_ctx) & tmp.ResolveValue(exe_ctx);
|
|
|
+- }
|
|
|
+- break;
|
|
|
+-
|
|
|
+- // OPCODE: DW_OP_div
|
|
|
+- // OPERANDS: none
|
|
|
+- // DESCRIPTION: pops the top two stack values, divides the former second
|
|
|
+- // entry by the former top of the stack using signed division, and pushes
|
|
|
+- // the result.
|
|
|
+- case DW_OP_div:
|
|
|
+- if (stack.size() < 2) {
|
|
|
+- if (error_ptr)
|
|
|
+- error_ptr->SetErrorString(
|
|
|
+- "Expression stack needs at least 2 items for DW_OP_div.");
|
|
|
+- return false;
|
|
|
+- } else {
|
|
|
+- tmp = stack.back();
|
|
|
+- if (tmp.ResolveValue(exe_ctx).IsZero()) {
|
|
|
+- if (error_ptr)
|
|
|
+- error_ptr->SetErrorString("Divide by zero.");
|
|
|
+- return false;
|
|
|
+- } else {
|
|
|
+- stack.pop_back();
|
|
|
+- stack.back() =
|
|
|
+- stack.back().ResolveValue(exe_ctx) / tmp.ResolveValue(exe_ctx);
|
|
|
+- if (!stack.back().ResolveValue(exe_ctx).IsValid()) {
|
|
|
+- if (error_ptr)
|
|
|
+- error_ptr->SetErrorString("Divide failed.");
|
|
|
+- return false;
|
|
|
+- }
|
|
|
+- }
|
|
|
+- }
|
|
|
+- break;
|
|
|
+-
|
|
|
+- // OPCODE: DW_OP_minus
|
|
|
+- // OPERANDS: none
|
|
|
+- // DESCRIPTION: pops the top two stack values, subtracts the former top
|
|
|
+- // of the stack from the former second entry, and pushes the result.
|
|
|
+- case DW_OP_minus:
|
|
|
+- if (stack.size() < 2) {
|
|
|
+- if (error_ptr)
|
|
|
+- error_ptr->SetErrorString(
|
|
|
+- "Expression stack needs at least 2 items for DW_OP_minus.");
|
|
|
+- return false;
|
|
|
+- } else {
|
|
|
+- tmp = stack.back();
|
|
|
+- stack.pop_back();
|
|
|
+- stack.back().ResolveValue(exe_ctx) =
|
|
|
+- stack.back().ResolveValue(exe_ctx) - tmp.ResolveValue(exe_ctx);
|
|
|
+- }
|
|
|
+- break;
|
|
|
+-
|
|
|
+- // OPCODE: DW_OP_mod
|
|
|
+- // OPERANDS: none
|
|
|
+- // DESCRIPTION: pops the top two stack values and pushes the result of
|
|
|
+- // the calculation: former second stack entry modulo the former top of the
|
|
|
+- // stack.
|
|
|
+- case DW_OP_mod:
|
|
|
+- if (stack.size() < 2) {
|
|
|
+- if (error_ptr)
|
|
|
+- error_ptr->SetErrorString(
|
|
|
+- "Expression stack needs at least 2 items for DW_OP_mod.");
|
|
|
+- return false;
|
|
|
+- } else {
|
|
|
+- tmp = stack.back();
|
|
|
+- stack.pop_back();
|
|
|
+- stack.back().ResolveValue(exe_ctx) =
|
|
|
+- stack.back().ResolveValue(exe_ctx) % tmp.ResolveValue(exe_ctx);
|
|
|
+- }
|
|
|
+- break;
|
|
|
+-
|
|
|
+- // OPCODE: DW_OP_mul
|
|
|
+- // OPERANDS: none
|
|
|
+- // DESCRIPTION: pops the top two stack entries, multiplies them
|
|
|
+- // together, and pushes the result.
|
|
|
+- case DW_OP_mul:
|
|
|
+- if (stack.size() < 2) {
|
|
|
+- if (error_ptr)
|
|
|
+- error_ptr->SetErrorString(
|
|
|
+- "Expression stack needs at least 2 items for DW_OP_mul.");
|
|
|
+- return false;
|
|
|
+- } else {
|
|
|
+- tmp = stack.back();
|
|
|
+- stack.pop_back();
|
|
|
+- stack.back().ResolveValue(exe_ctx) =
|
|
|
+- stack.back().ResolveValue(exe_ctx) * tmp.ResolveValue(exe_ctx);
|
|
|
+- }
|
|
|
+- break;
|
|
|
+-
|
|
|
+- // OPCODE: DW_OP_neg
|
|
|
+- // OPERANDS: none
|
|
|
+- // DESCRIPTION: pops the top stack entry, and pushes its negation.
|
|
|
+- case DW_OP_neg:
|
|
|
+- if (stack.empty()) {
|
|
|
+- if (error_ptr)
|
|
|
+- error_ptr->SetErrorString(
|
|
|
+- "Expression stack needs at least 1 item for DW_OP_neg.");
|
|
|
+- return false;
|
|
|
+- } else {
|
|
|
+- if (!stack.back().ResolveValue(exe_ctx).UnaryNegate()) {
|
|
|
+- if (error_ptr)
|
|
|
+- error_ptr->SetErrorString("Unary negate failed.");
|
|
|
+- return false;
|
|
|
+- }
|
|
|
+- }
|
|
|
+- break;
|
|
|
+-
|
|
|
+- // OPCODE: DW_OP_not
|
|
|
+- // OPERANDS: none
|
|
|
+- // DESCRIPTION: pops the top stack entry, and pushes its bitwise
|
|
|
+- // complement
|
|
|
+- case DW_OP_not:
|
|
|
+- if (stack.empty()) {
|
|
|
+- if (error_ptr)
|
|
|
+- error_ptr->SetErrorString(
|
|
|
+- "Expression stack needs at least 1 item for DW_OP_not.");
|
|
|
+- return false;
|
|
|
+- } else {
|
|
|
+- if (!stack.back().ResolveValue(exe_ctx).OnesComplement()) {
|
|
|
+- if (error_ptr)
|
|
|
+- error_ptr->SetErrorString("Logical NOT failed.");
|
|
|
+- return false;
|
|
|
+- }
|
|
|
+- }
|
|
|
+- break;
|
|
|
+-
|
|
|
+- // OPCODE: DW_OP_or
|
|
|
+- // OPERANDS: none
|
|
|
+- // DESCRIPTION: pops the top two stack entries, performs a bitwise or
|
|
|
+- // operation on the two, and pushes the result.
|
|
|
+- case DW_OP_or:
|
|
|
+- if (stack.size() < 2) {
|
|
|
+- if (error_ptr)
|
|
|
+- error_ptr->SetErrorString(
|
|
|
+- "Expression stack needs at least 2 items for DW_OP_or.");
|
|
|
+- return false;
|
|
|
+- } else {
|
|
|
+- tmp = stack.back();
|
|
|
+- stack.pop_back();
|
|
|
+- stack.back().ResolveValue(exe_ctx) =
|
|
|
+- stack.back().ResolveValue(exe_ctx) | tmp.ResolveValue(exe_ctx);
|
|
|
+- }
|
|
|
+- break;
|
|
|
+-
|
|
|
+- // OPCODE: DW_OP_plus
|
|
|
+- // OPERANDS: none
|
|
|
+- // DESCRIPTION: pops the top two stack entries, adds them together, and
|
|
|
+- // pushes the result.
|
|
|
+- case DW_OP_plus:
|
|
|
+- if (stack.size() < 2) {
|
|
|
+- if (error_ptr)
|
|
|
+- error_ptr->SetErrorString(
|
|
|
+- "Expression stack needs at least 2 items for DW_OP_plus.");
|
|
|
+- return false;
|
|
|
+- } else {
|
|
|
+- tmp = stack.back();
|
|
|
+- stack.pop_back();
|
|
|
+- stack.back().GetScalar() += tmp.GetScalar();
|
|
|
+- }
|
|
|
+- break;
|
|
|
+-
|
|
|
+- // OPCODE: DW_OP_plus_uconst
|
|
|
+- // OPERANDS: none
|
|
|
+- // DESCRIPTION: pops the top stack entry, adds it to the unsigned LEB128
|
|
|
+- // constant operand and pushes the result.
|
|
|
+- case DW_OP_plus_uconst:
|
|
|
+- if (stack.empty()) {
|
|
|
+- if (error_ptr)
|
|
|
+- error_ptr->SetErrorString(
|
|
|
+- "Expression stack needs at least 1 item for DW_OP_plus_uconst.");
|
|
|
+- return false;
|
|
|
+- } else {
|
|
|
+- const uint64_t uconst_value = opcodes.GetULEB128(&offset);
|
|
|
+- // Implicit conversion from a UINT to a Scalar...
|
|
|
+- stack.back().GetScalar() += uconst_value;
|
|
|
+- if (!stack.back().GetScalar().IsValid()) {
|
|
|
+- if (error_ptr)
|
|
|
+- error_ptr->SetErrorString("DW_OP_plus_uconst failed.");
|
|
|
+- return false;
|
|
|
+- }
|
|
|
+- }
|
|
|
+- break;
|
|
|
+-
|
|
|
+- // OPCODE: DW_OP_shl
|
|
|
+- // OPERANDS: none
|
|
|
+- // DESCRIPTION: pops the top two stack entries, shifts the former
|
|
|
+- // second entry left by the number of bits specified by the former top of
|
|
|
+- // the stack, and pushes the result.
|
|
|
+- case DW_OP_shl:
|
|
|
+- if (stack.size() < 2) {
|
|
|
+- if (error_ptr)
|
|
|
+- error_ptr->SetErrorString(
|
|
|
+- "Expression stack needs at least 2 items for DW_OP_shl.");
|
|
|
+- return false;
|
|
|
+- } else {
|
|
|
+- tmp = stack.back();
|
|
|
+- stack.pop_back();
|
|
|
+- stack.back().ResolveValue(exe_ctx) <<= tmp.ResolveValue(exe_ctx);
|
|
|
+- }
|
|
|
+- break;
|
|
|
+-
|
|
|
+- // OPCODE: DW_OP_shr
|
|
|
+- // OPERANDS: none
|
|
|
+- // DESCRIPTION: pops the top two stack entries, shifts the former second
|
|
|
+- // entry right logically (filling with zero bits) by the number of bits
|
|
|
+- // specified by the former top of the stack, and pushes the result.
|
|
|
+- case DW_OP_shr:
|
|
|
+- if (stack.size() < 2) {
|
|
|
+- if (error_ptr)
|
|
|
+- error_ptr->SetErrorString(
|
|
|
+- "Expression stack needs at least 2 items for DW_OP_shr.");
|
|
|
+- return false;
|
|
|
+- } else {
|
|
|
+- tmp = stack.back();
|
|
|
+- stack.pop_back();
|
|
|
+- if (!stack.back().ResolveValue(exe_ctx).ShiftRightLogical(
|
|
|
+- tmp.ResolveValue(exe_ctx))) {
|
|
|
+- if (error_ptr)
|
|
|
+- error_ptr->SetErrorString("DW_OP_shr failed.");
|
|
|
+- return false;
|
|
|
+- }
|
|
|
+- }
|
|
|
+- break;
|
|
|
+-
|
|
|
+- // OPCODE: DW_OP_shra
|
|
|
+- // OPERANDS: none
|
|
|
+- // DESCRIPTION: pops the top two stack entries, shifts the former second
|
|
|
+- // entry right arithmetically (divide the magnitude by 2, keep the same
|
|
|
+- // sign for the result) by the number of bits specified by the former top
|
|
|
+- // of the stack, and pushes the result.
|
|
|
+- case DW_OP_shra:
|
|
|
+- if (stack.size() < 2) {
|
|
|
+- if (error_ptr)
|
|
|
+- error_ptr->SetErrorString(
|
|
|
+- "Expression stack needs at least 2 items for DW_OP_shra.");
|
|
|
+- return false;
|
|
|
+- } else {
|
|
|
+- tmp = stack.back();
|
|
|
+- stack.pop_back();
|
|
|
+- stack.back().ResolveValue(exe_ctx) >>= tmp.ResolveValue(exe_ctx);
|
|
|
+- }
|
|
|
+- break;
|
|
|
+-
|
|
|
+- // OPCODE: DW_OP_xor
|
|
|
+- // OPERANDS: none
|
|
|
+- // DESCRIPTION: pops the top two stack entries, performs the bitwise
|
|
|
+- // exclusive-or operation on the two, and pushes the result.
|
|
|
+- case DW_OP_xor:
|
|
|
+- if (stack.size() < 2) {
|
|
|
+- if (error_ptr)
|
|
|
+- error_ptr->SetErrorString(
|
|
|
+- "Expression stack needs at least 2 items for DW_OP_xor.");
|
|
|
+- return false;
|
|
|
+- } else {
|
|
|
+- tmp = stack.back();
|
|
|
+- stack.pop_back();
|
|
|
+- stack.back().ResolveValue(exe_ctx) =
|
|
|
+- stack.back().ResolveValue(exe_ctx) ^ tmp.ResolveValue(exe_ctx);
|
|
|
+- }
|
|
|
+- break;
|
|
|
+-
|
|
|
+- // OPCODE: DW_OP_skip
|
|
|
+- // OPERANDS: int16_t
|
|
|
+- // DESCRIPTION: An unconditional branch. Its single operand is a 2-byte
|
|
|
+- // signed integer constant. The 2-byte constant is the number of bytes of
|
|
|
+- // the DWARF expression to skip forward or backward from the current
|
|
|
+- // operation, beginning after the 2-byte constant.
|
|
|
+- case DW_OP_skip: {
|
|
|
+- int16_t skip_offset = (int16_t)opcodes.GetU16(&offset);
|
|
|
+- lldb::offset_t new_offset = offset + skip_offset;
|
|
|
+- if (opcodes.ValidOffset(new_offset))
|
|
|
+- offset = new_offset;
|
|
|
+- else {
|
|
|
+- if (error_ptr)
|
|
|
+- error_ptr->SetErrorString("Invalid opcode offset in DW_OP_skip.");
|
|
|
+- return false;
|
|
|
+- }
|
|
|
+- } break;
|
|
|
+-
|
|
|
+- // OPCODE: DW_OP_bra
|
|
|
+- // OPERANDS: int16_t
|
|
|
+- // DESCRIPTION: A conditional branch. Its single operand is a 2-byte
|
|
|
+- // signed integer constant. This operation pops the top of stack. If the
|
|
|
+- // value popped is not the constant 0, the 2-byte constant operand is the
|
|
|
+- // number of bytes of the DWARF expression to skip forward or backward from
|
|
|
+- // the current operation, beginning after the 2-byte constant.
|
|
|
+- case DW_OP_bra:
|
|
|
+- if (stack.empty()) {
|
|
|
+- if (error_ptr)
|
|
|
+- error_ptr->SetErrorString(
|
|
|
+- "Expression stack needs at least 1 item for DW_OP_bra.");
|
|
|
+- return false;
|
|
|
+- } else {
|
|
|
+- tmp = stack.back();
|
|
|
+- stack.pop_back();
|
|
|
+- int16_t bra_offset = (int16_t)opcodes.GetU16(&offset);
|
|
|
+- Scalar zero(0);
|
|
|
+- if (tmp.ResolveValue(exe_ctx) != zero) {
|
|
|
+- lldb::offset_t new_offset = offset + bra_offset;
|
|
|
+- if (opcodes.ValidOffset(new_offset))
|
|
|
+- offset = new_offset;
|
|
|
+- else {
|
|
|
+- if (error_ptr)
|
|
|
+- error_ptr->SetErrorString("Invalid opcode offset in DW_OP_bra.");
|
|
|
+- return false;
|
|
|
+- }
|
|
|
+- }
|
|
|
+- }
|
|
|
+- break;
|
|
|
+-
|
|
|
+- // OPCODE: DW_OP_eq
|
|
|
+- // OPERANDS: none
|
|
|
+- // DESCRIPTION: pops the top two stack values, compares using the
|
|
|
+- // equals (==) operator.
|
|
|
+- // STACK RESULT: push the constant value 1 onto the stack if the result
|
|
|
+- // of the operation is true or the constant value 0 if the result of the
|
|
|
+- // operation is false.
|
|
|
+- case DW_OP_eq:
|
|
|
+- if (stack.size() < 2) {
|
|
|
+- if (error_ptr)
|
|
|
+- error_ptr->SetErrorString(
|
|
|
+- "Expression stack needs at least 2 items for DW_OP_eq.");
|
|
|
+- return false;
|
|
|
+- } else {
|
|
|
+- tmp = stack.back();
|
|
|
+- stack.pop_back();
|
|
|
+- stack.back().ResolveValue(exe_ctx) =
|
|
|
+- stack.back().ResolveValue(exe_ctx) == tmp.ResolveValue(exe_ctx);
|
|
|
+- }
|
|
|
+- break;
|
|
|
+-
|
|
|
+- // OPCODE: DW_OP_ge
|
|
|
+- // OPERANDS: none
|
|
|
+- // DESCRIPTION: pops the top two stack values, compares using the
|
|
|
+- // greater than or equal to (>=) operator.
|
|
|
+- // STACK RESULT: push the constant value 1 onto the stack if the result
|
|
|
+- // of the operation is true or the constant value 0 if the result of the
|
|
|
+- // operation is false.
|
|
|
+- case DW_OP_ge:
|
|
|
+- if (stack.size() < 2) {
|
|
|
+- if (error_ptr)
|
|
|
+- error_ptr->SetErrorString(
|
|
|
+- "Expression stack needs at least 2 items for DW_OP_ge.");
|
|
|
+- return false;
|
|
|
+- } else {
|
|
|
+- tmp = stack.back();
|
|
|
+- stack.pop_back();
|
|
|
+- stack.back().ResolveValue(exe_ctx) =
|
|
|
+- stack.back().ResolveValue(exe_ctx) >= tmp.ResolveValue(exe_ctx);
|
|
|
+- }
|
|
|
+- break;
|
|
|
+-
|
|
|
+- // OPCODE: DW_OP_gt
|
|
|
+- // OPERANDS: none
|
|
|
+- // DESCRIPTION: pops the top two stack values, compares using the
|
|
|
+- // greater than (>) operator.
|
|
|
+- // STACK RESULT: push the constant value 1 onto the stack if the result
|
|
|
+- // of the operation is true or the constant value 0 if the result of the
|
|
|
+- // operation is false.
|
|
|
+- case DW_OP_gt:
|
|
|
+- if (stack.size() < 2) {
|
|
|
+- if (error_ptr)
|
|
|
+- error_ptr->SetErrorString(
|
|
|
+- "Expression stack needs at least 2 items for DW_OP_gt.");
|
|
|
+- return false;
|
|
|
+- } else {
|
|
|
+- tmp = stack.back();
|
|
|
+- stack.pop_back();
|
|
|
+- stack.back().ResolveValue(exe_ctx) =
|
|
|
+- stack.back().ResolveValue(exe_ctx) > tmp.ResolveValue(exe_ctx);
|
|
|
+- }
|
|
|
+- break;
|
|
|
+-
|
|
|
+- // OPCODE: DW_OP_le
|
|
|
+- // OPERANDS: none
|
|
|
+- // DESCRIPTION: pops the top two stack values, compares using the
|
|
|
+- // less than or equal to (<=) operator.
|
|
|
+- // STACK RESULT: push the constant value 1 onto the stack if the result
|
|
|
+- // of the operation is true or the constant value 0 if the result of the
|
|
|
+- // operation is false.
|
|
|
+- case DW_OP_le:
|
|
|
+- if (stack.size() < 2) {
|
|
|
+- if (error_ptr)
|
|
|
+- error_ptr->SetErrorString(
|
|
|
+- "Expression stack needs at least 2 items for DW_OP_le.");
|
|
|
+- return false;
|
|
|
+- } else {
|
|
|
+- tmp = stack.back();
|
|
|
+- stack.pop_back();
|
|
|
+- stack.back().ResolveValue(exe_ctx) =
|
|
|
+- stack.back().ResolveValue(exe_ctx) <= tmp.ResolveValue(exe_ctx);
|
|
|
+- }
|
|
|
+- break;
|
|
|
+-
|
|
|
+- // OPCODE: DW_OP_lt
|
|
|
+- // OPERANDS: none
|
|
|
+- // DESCRIPTION: pops the top two stack values, compares using the
|
|
|
+- // less than (<) operator.
|
|
|
+- // STACK RESULT: push the constant value 1 onto the stack if the result
|
|
|
+- // of the operation is true or the constant value 0 if the result of the
|
|
|
+- // operation is false.
|
|
|
+- case DW_OP_lt:
|
|
|
+- if (stack.size() < 2) {
|
|
|
+- if (error_ptr)
|
|
|
+- error_ptr->SetErrorString(
|
|
|
+- "Expression stack needs at least 2 items for DW_OP_lt.");
|
|
|
+- return false;
|
|
|
+- } else {
|
|
|
+- tmp = stack.back();
|
|
|
+- stack.pop_back();
|
|
|
+- stack.back().ResolveValue(exe_ctx) =
|
|
|
+- stack.back().ResolveValue(exe_ctx) < tmp.ResolveValue(exe_ctx);
|
|
|
+- }
|
|
|
+- break;
|
|
|
+-
|
|
|
+- // OPCODE: DW_OP_ne
|
|
|
+- // OPERANDS: none
|
|
|
+- // DESCRIPTION: pops the top two stack values, compares using the
|
|
|
+- // not equal (!=) operator.
|
|
|
+- // STACK RESULT: push the constant value 1 onto the stack if the result
|
|
|
+- // of the operation is true or the constant value 0 if the result of the
|
|
|
+- // operation is false.
|
|
|
+- case DW_OP_ne:
|
|
|
+- if (stack.size() < 2) {
|
|
|
+- if (error_ptr)
|
|
|
+- error_ptr->SetErrorString(
|
|
|
+- "Expression stack needs at least 2 items for DW_OP_ne.");
|
|
|
+- return false;
|
|
|
+- } else {
|
|
|
+- tmp = stack.back();
|
|
|
+- stack.pop_back();
|
|
|
+- stack.back().ResolveValue(exe_ctx) =
|
|
|
+- stack.back().ResolveValue(exe_ctx) != tmp.ResolveValue(exe_ctx);
|
|
|
+- }
|
|
|
+- break;
|
|
|
+-
|
|
|
+- // OPCODE: DW_OP_litn
|
|
|
+- // OPERANDS: none
|
|
|
+- // DESCRIPTION: encode the unsigned literal values from 0 through 31.
|
|
|
+- // STACK RESULT: push the unsigned literal constant value onto the top
|
|
|
+- // of the stack.
|
|
|
+- case DW_OP_lit0:
|
|
|
+- case DW_OP_lit1:
|
|
|
+- case DW_OP_lit2:
|
|
|
+- case DW_OP_lit3:
|
|
|
+- case DW_OP_lit4:
|
|
|
+- case DW_OP_lit5:
|
|
|
+- case DW_OP_lit6:
|
|
|
+- case DW_OP_lit7:
|
|
|
+- case DW_OP_lit8:
|
|
|
+- case DW_OP_lit9:
|
|
|
+- case DW_OP_lit10:
|
|
|
+- case DW_OP_lit11:
|
|
|
+- case DW_OP_lit12:
|
|
|
+- case DW_OP_lit13:
|
|
|
+- case DW_OP_lit14:
|
|
|
+- case DW_OP_lit15:
|
|
|
+- case DW_OP_lit16:
|
|
|
+- case DW_OP_lit17:
|
|
|
+- case DW_OP_lit18:
|
|
|
+- case DW_OP_lit19:
|
|
|
+- case DW_OP_lit20:
|
|
|
+- case DW_OP_lit21:
|
|
|
+- case DW_OP_lit22:
|
|
|
+- case DW_OP_lit23:
|
|
|
+- case DW_OP_lit24:
|
|
|
+- case DW_OP_lit25:
|
|
|
+- case DW_OP_lit26:
|
|
|
+- case DW_OP_lit27:
|
|
|
+- case DW_OP_lit28:
|
|
|
+- case DW_OP_lit29:
|
|
|
+- case DW_OP_lit30:
|
|
|
+- case DW_OP_lit31:
|
|
|
+- stack.push_back(to_generic(op - DW_OP_lit0));
|
|
|
+- break;
|
|
|
+-
|
|
|
+- // OPCODE: DW_OP_regN
|
|
|
+- // OPERANDS: none
|
|
|
+- // DESCRIPTION: Push the value in register n on the top of the stack.
|
|
|
+- case DW_OP_reg0:
|
|
|
+- case DW_OP_reg1:
|
|
|
+- case DW_OP_reg2:
|
|
|
+- case DW_OP_reg3:
|
|
|
+- case DW_OP_reg4:
|
|
|
+- case DW_OP_reg5:
|
|
|
+- case DW_OP_reg6:
|
|
|
+- case DW_OP_reg7:
|
|
|
+- case DW_OP_reg8:
|
|
|
+- case DW_OP_reg9:
|
|
|
+- case DW_OP_reg10:
|
|
|
+- case DW_OP_reg11:
|
|
|
+- case DW_OP_reg12:
|
|
|
+- case DW_OP_reg13:
|
|
|
+- case DW_OP_reg14:
|
|
|
+- case DW_OP_reg15:
|
|
|
+- case DW_OP_reg16:
|
|
|
+- case DW_OP_reg17:
|
|
|
+- case DW_OP_reg18:
|
|
|
+- case DW_OP_reg19:
|
|
|
+- case DW_OP_reg20:
|
|
|
+- case DW_OP_reg21:
|
|
|
+- case DW_OP_reg22:
|
|
|
+- case DW_OP_reg23:
|
|
|
+- case DW_OP_reg24:
|
|
|
+- case DW_OP_reg25:
|
|
|
+- case DW_OP_reg26:
|
|
|
+- case DW_OP_reg27:
|
|
|
+- case DW_OP_reg28:
|
|
|
+- case DW_OP_reg29:
|
|
|
+- case DW_OP_reg30:
|
|
|
+- case DW_OP_reg31: {
|
|
|
+- dwarf4_location_description_kind = Register;
|
|
|
+- reg_num = op - DW_OP_reg0;
|
|
|
+-
|
|
|
+- if (ReadRegisterValueAsScalar(reg_ctx, reg_kind, reg_num, error_ptr, tmp))
|
|
|
+- stack.push_back(tmp);
|
|
|
+- else
|
|
|
+- return false;
|
|
|
+- } break;
|
|
|
+- // OPCODE: DW_OP_regx
|
|
|
+- // OPERANDS:
|
|
|
+- // ULEB128 literal operand that encodes the register.
|
|
|
+- // DESCRIPTION: Push the value in register on the top of the stack.
|
|
|
+- case DW_OP_regx: {
|
|
|
+- dwarf4_location_description_kind = Register;
|
|
|
+- reg_num = opcodes.GetULEB128(&offset);
|
|
|
+- if (ReadRegisterValueAsScalar(reg_ctx, reg_kind, reg_num, error_ptr, tmp))
|
|
|
+- stack.push_back(tmp);
|
|
|
+- else
|
|
|
+- return false;
|
|
|
+- } break;
|
|
|
+-
|
|
|
+- // OPCODE: DW_OP_bregN
|
|
|
+- // OPERANDS:
|
|
|
+- // SLEB128 offset from register N
|
|
|
+- // DESCRIPTION: Value is in memory at the address specified by register
|
|
|
+- // N plus an offset.
|
|
|
+- case DW_OP_breg0:
|
|
|
+- case DW_OP_breg1:
|
|
|
+- case DW_OP_breg2:
|
|
|
+- case DW_OP_breg3:
|
|
|
+- case DW_OP_breg4:
|
|
|
+- case DW_OP_breg5:
|
|
|
+- case DW_OP_breg6:
|
|
|
+- case DW_OP_breg7:
|
|
|
+- case DW_OP_breg8:
|
|
|
+- case DW_OP_breg9:
|
|
|
+- case DW_OP_breg10:
|
|
|
+- case DW_OP_breg11:
|
|
|
+- case DW_OP_breg12:
|
|
|
+- case DW_OP_breg13:
|
|
|
+- case DW_OP_breg14:
|
|
|
+- case DW_OP_breg15:
|
|
|
+- case DW_OP_breg16:
|
|
|
+- case DW_OP_breg17:
|
|
|
+- case DW_OP_breg18:
|
|
|
+- case DW_OP_breg19:
|
|
|
+- case DW_OP_breg20:
|
|
|
+- case DW_OP_breg21:
|
|
|
+- case DW_OP_breg22:
|
|
|
+- case DW_OP_breg23:
|
|
|
+- case DW_OP_breg24:
|
|
|
+- case DW_OP_breg25:
|
|
|
+- case DW_OP_breg26:
|
|
|
+- case DW_OP_breg27:
|
|
|
+- case DW_OP_breg28:
|
|
|
+- case DW_OP_breg29:
|
|
|
+- case DW_OP_breg30:
|
|
|
+- case DW_OP_breg31: {
|
|
|
+- reg_num = op - DW_OP_breg0;
|
|
|
+-
|
|
|
+- if (ReadRegisterValueAsScalar(reg_ctx, reg_kind, reg_num, error_ptr,
|
|
|
+- tmp)) {
|
|
|
+- int64_t breg_offset = opcodes.GetSLEB128(&offset);
|
|
|
+- tmp.ResolveValue(exe_ctx) += (uint64_t)breg_offset;
|
|
|
+- tmp.ClearContext();
|
|
|
+- stack.push_back(tmp);
|
|
|
+- stack.back().SetValueType(Value::ValueType::LoadAddress);
|
|
|
+- } else
|
|
|
+- return false;
|
|
|
+- } break;
|
|
|
+- // OPCODE: DW_OP_bregx
|
|
|
+- // OPERANDS: 2
|
|
|
+- // ULEB128 literal operand that encodes the register.
|
|
|
+- // SLEB128 offset from register N
|
|
|
+- // DESCRIPTION: Value is in memory at the address specified by register
|
|
|
+- // N plus an offset.
|
|
|
+- case DW_OP_bregx: {
|
|
|
+- reg_num = opcodes.GetULEB128(&offset);
|
|
|
+-
|
|
|
+- if (ReadRegisterValueAsScalar(reg_ctx, reg_kind, reg_num, error_ptr,
|
|
|
+- tmp)) {
|
|
|
+- int64_t breg_offset = opcodes.GetSLEB128(&offset);
|
|
|
+- tmp.ResolveValue(exe_ctx) += (uint64_t)breg_offset;
|
|
|
+- tmp.ClearContext();
|
|
|
+- stack.push_back(tmp);
|
|
|
+- stack.back().SetValueType(Value::ValueType::LoadAddress);
|
|
|
+- } else
|
|
|
+- return false;
|
|
|
+- } break;
|
|
|
+-
|
|
|
+- case DW_OP_fbreg:
|
|
|
+- if (exe_ctx) {
|
|
|
+- if (frame) {
|
|
|
+- Scalar value;
|
|
|
+- if (frame->GetFrameBaseValue(value, error_ptr)) {
|
|
|
+- int64_t fbreg_offset = opcodes.GetSLEB128(&offset);
|
|
|
+- value += fbreg_offset;
|
|
|
+- stack.push_back(value);
|
|
|
+- stack.back().SetValueType(Value::ValueType::LoadAddress);
|
|
|
+- } else
|
|
|
+- return false;
|
|
|
+- } else {
|
|
|
+- if (error_ptr)
|
|
|
+- error_ptr->SetErrorString(
|
|
|
+- "Invalid stack frame in context for DW_OP_fbreg opcode.");
|
|
|
+- return false;
|
|
|
+- }
|
|
|
+- } else {
|
|
|
+- if (error_ptr)
|
|
|
+- error_ptr->SetErrorString(
|
|
|
+- "NULL execution context for DW_OP_fbreg.\n");
|
|
|
+- return false;
|
|
|
+- }
|
|
|
+-
|
|
|
+- break;
|
|
|
+-
|
|
|
+- // OPCODE: DW_OP_nop
|
|
|
+- // OPERANDS: none
|
|
|
+- // DESCRIPTION: A place holder. It has no effect on the location stack
|
|
|
+- // or any of its values.
|
|
|
+- case DW_OP_nop:
|
|
|
+- break;
|
|
|
+-
|
|
|
+- // OPCODE: DW_OP_piece
|
|
|
+- // OPERANDS: 1
|
|
|
+- // ULEB128: byte size of the piece
|
|
|
+- // DESCRIPTION: The operand describes the size in bytes of the piece of
|
|
|
+- // the object referenced by the DWARF expression whose result is at the top
|
|
|
+- // of the stack. If the piece is located in a register, but does not occupy
|
|
|
+- // the entire register, the placement of the piece within that register is
|
|
|
+- // defined by the ABI.
|
|
|
+- //
|
|
|
+- // Many compilers store a single variable in sets of registers, or store a
|
|
|
+- // variable partially in memory and partially in registers. DW_OP_piece
|
|
|
+- // provides a way of describing how large a part of a variable a particular
|
|
|
+- // DWARF expression refers to.
|
|
|
+- case DW_OP_piece: {
|
|
|
+- LocationDescriptionKind piece_locdesc = dwarf4_location_description_kind;
|
|
|
+- // Reset for the next piece.
|
|
|
+- dwarf4_location_description_kind = Memory;
|
|
|
+-
|
|
|
+- const uint64_t piece_byte_size = opcodes.GetULEB128(&offset);
|
|
|
+-
|
|
|
+- if (piece_byte_size > 0) {
|
|
|
+- Value curr_piece;
|
|
|
+-
|
|
|
+- if (stack.empty()) {
|
|
|
+- UpdateValueTypeFromLocationDescription(
|
|
|
+- log, dwarf_cu, LocationDescriptionKind::Empty);
|
|
|
+- // In a multi-piece expression, this means that the current piece is
|
|
|
+- // not available. Fill with zeros for now by resizing the data and
|
|
|
+- // appending it
|
|
|
+- curr_piece.ResizeData(piece_byte_size);
|
|
|
+- // Note that "0" is not a correct value for the unknown bits.
|
|
|
+- // It would be better to also return a mask of valid bits together
|
|
|
+- // with the expression result, so the debugger can print missing
|
|
|
+- // members as "<optimized out>" or something.
|
|
|
+- ::memset(curr_piece.GetBuffer().GetBytes(), 0, piece_byte_size);
|
|
|
+- pieces.AppendDataToHostBuffer(curr_piece);
|
|
|
+- } else {
|
|
|
+- Status error;
|
|
|
+- // Extract the current piece into "curr_piece"
|
|
|
+- Value curr_piece_source_value(stack.back());
|
|
|
+- stack.pop_back();
|
|
|
+- UpdateValueTypeFromLocationDescription(log, dwarf_cu, piece_locdesc,
|
|
|
+- &curr_piece_source_value);
|
|
|
+-
|
|
|
+- const Value::ValueType curr_piece_source_value_type =
|
|
|
+- curr_piece_source_value.GetValueType();
|
|
|
+- switch (curr_piece_source_value_type) {
|
|
|
+- case Value::ValueType::Invalid:
|
|
|
+- return false;
|
|
|
+- case Value::ValueType::LoadAddress:
|
|
|
+- if (process) {
|
|
|
+- if (curr_piece.ResizeData(piece_byte_size) == piece_byte_size) {
|
|
|
+- lldb::addr_t load_addr =
|
|
|
+- curr_piece_source_value.GetScalar().ULongLong(
|
|
|
+- LLDB_INVALID_ADDRESS);
|
|
|
+- if (process->ReadMemory(
|
|
|
+- load_addr, curr_piece.GetBuffer().GetBytes(),
|
|
|
+- piece_byte_size, error) != piece_byte_size) {
|
|
|
+- if (error_ptr)
|
|
|
+- error_ptr->SetErrorStringWithFormat(
|
|
|
+- "failed to read memory DW_OP_piece(%" PRIu64
|
|
|
+- ") from 0x%" PRIx64,
|
|
|
+- piece_byte_size, load_addr);
|
|
|
+- return false;
|
|
|
+- }
|
|
|
+- } else {
|
|
|
+- if (error_ptr)
|
|
|
+- error_ptr->SetErrorStringWithFormat(
|
|
|
+- "failed to resize the piece memory buffer for "
|
|
|
+- "DW_OP_piece(%" PRIu64 ")",
|
|
|
+- piece_byte_size);
|
|
|
+- return false;
|
|
|
+- }
|
|
|
+- }
|
|
|
+- break;
|
|
|
+-
|
|
|
+- case Value::ValueType::FileAddress:
|
|
|
+- case Value::ValueType::HostAddress:
|
|
|
+- if (error_ptr) {
|
|
|
+- lldb::addr_t addr = curr_piece_source_value.GetScalar().ULongLong(
|
|
|
+- LLDB_INVALID_ADDRESS);
|
|
|
+- error_ptr->SetErrorStringWithFormat(
|
|
|
+- "failed to read memory DW_OP_piece(%" PRIu64
|
|
|
+- ") from %s address 0x%" PRIx64,
|
|
|
+- piece_byte_size, curr_piece_source_value.GetValueType() ==
|
|
|
+- Value::ValueType::FileAddress
|
|
|
+- ? "file"
|
|
|
+- : "host",
|
|
|
+- addr);
|
|
|
+- }
|
|
|
+- return false;
|
|
|
+-
|
|
|
+- case Value::ValueType::Scalar: {
|
|
|
+- uint32_t bit_size = piece_byte_size * 8;
|
|
|
+- uint32_t bit_offset = 0;
|
|
|
+- Scalar &scalar = curr_piece_source_value.GetScalar();
|
|
|
+- if (!scalar.ExtractBitfield(
|
|
|
+- bit_size, bit_offset)) {
|
|
|
+- if (error_ptr)
|
|
|
+- error_ptr->SetErrorStringWithFormat(
|
|
|
+- "unable to extract %" PRIu64 " bytes from a %" PRIu64
|
|
|
+- " byte scalar value.",
|
|
|
+- piece_byte_size,
|
|
|
+- (uint64_t)curr_piece_source_value.GetScalar()
|
|
|
+- .GetByteSize());
|
|
|
+- return false;
|
|
|
+- }
|
|
|
+- // Create curr_piece with bit_size. By default Scalar
|
|
|
+- // grows to the nearest host integer type.
|
|
|
+- llvm::APInt fail_value(1, 0, false);
|
|
|
+- llvm::APInt ap_int = scalar.UInt128(fail_value);
|
|
|
+- assert(ap_int.getBitWidth() >= bit_size);
|
|
|
+- llvm::ArrayRef<uint64_t> buf{ap_int.getRawData(),
|
|
|
+- ap_int.getNumWords()};
|
|
|
+- curr_piece.GetScalar() = Scalar(llvm::APInt(bit_size, buf));
|
|
|
+- } break;
|
|
|
+- }
|
|
|
+-
|
|
|
+- // Check if this is the first piece?
|
|
|
+- if (op_piece_offset == 0) {
|
|
|
+- // This is the first piece, we should push it back onto the stack
|
|
|
+- // so subsequent pieces will be able to access this piece and add
|
|
|
+- // to it.
|
|
|
+- if (pieces.AppendDataToHostBuffer(curr_piece) == 0) {
|
|
|
+- if (error_ptr)
|
|
|
+- error_ptr->SetErrorString("failed to append piece data");
|
|
|
+- return false;
|
|
|
+- }
|
|
|
+- } else {
|
|
|
+- // If this is the second or later piece there should be a value on
|
|
|
+- // the stack.
|
|
|
+- if (pieces.GetBuffer().GetByteSize() != op_piece_offset) {
|
|
|
+- if (error_ptr)
|
|
|
+- error_ptr->SetErrorStringWithFormat(
|
|
|
+- "DW_OP_piece for offset %" PRIu64
|
|
|
+- " but top of stack is of size %" PRIu64,
|
|
|
+- op_piece_offset, pieces.GetBuffer().GetByteSize());
|
|
|
+- return false;
|
|
|
+- }
|
|
|
+-
|
|
|
+- if (pieces.AppendDataToHostBuffer(curr_piece) == 0) {
|
|
|
+- if (error_ptr)
|
|
|
+- error_ptr->SetErrorString("failed to append piece data");
|
|
|
+- return false;
|
|
|
+- }
|
|
|
+- }
|
|
|
+- }
|
|
|
+- op_piece_offset += piece_byte_size;
|
|
|
+- }
|
|
|
+- } break;
|
|
|
+-
|
|
|
+- case DW_OP_bit_piece: // 0x9d ULEB128 bit size, ULEB128 bit offset (DWARF3);
|
|
|
+- if (stack.size() < 1) {
|
|
|
+- UpdateValueTypeFromLocationDescription(log, dwarf_cu,
|
|
|
+- LocationDescriptionKind::Empty);
|
|
|
+- // Reset for the next piece.
|
|
|
+- dwarf4_location_description_kind = Memory;
|
|
|
+- if (error_ptr)
|
|
|
+- error_ptr->SetErrorString(
|
|
|
+- "Expression stack needs at least 1 item for DW_OP_bit_piece.");
|
|
|
+- return false;
|
|
|
+- } else {
|
|
|
+- UpdateValueTypeFromLocationDescription(
|
|
|
+- log, dwarf_cu, dwarf4_location_description_kind, &stack.back());
|
|
|
+- // Reset for the next piece.
|
|
|
+- dwarf4_location_description_kind = Memory;
|
|
|
+- const uint64_t piece_bit_size = opcodes.GetULEB128(&offset);
|
|
|
+- const uint64_t piece_bit_offset = opcodes.GetULEB128(&offset);
|
|
|
+- switch (stack.back().GetValueType()) {
|
|
|
+- case Value::ValueType::Invalid:
|
|
|
+- return false;
|
|
|
+- case Value::ValueType::Scalar: {
|
|
|
+- if (!stack.back().GetScalar().ExtractBitfield(piece_bit_size,
|
|
|
+- piece_bit_offset)) {
|
|
|
+- if (error_ptr)
|
|
|
+- error_ptr->SetErrorStringWithFormat(
|
|
|
+- "unable to extract %" PRIu64 " bit value with %" PRIu64
|
|
|
+- " bit offset from a %" PRIu64 " bit scalar value.",
|
|
|
+- piece_bit_size, piece_bit_offset,
|
|
|
+- (uint64_t)(stack.back().GetScalar().GetByteSize() * 8));
|
|
|
+- return false;
|
|
|
+- }
|
|
|
+- } break;
|
|
|
+-
|
|
|
+- case Value::ValueType::FileAddress:
|
|
|
+- case Value::ValueType::LoadAddress:
|
|
|
+- case Value::ValueType::HostAddress:
|
|
|
+- if (error_ptr) {
|
|
|
+- error_ptr->SetErrorStringWithFormat(
|
|
|
+- "unable to extract DW_OP_bit_piece(bit_size = %" PRIu64
|
|
|
+- ", bit_offset = %" PRIu64 ") from an address value.",
|
|
|
+- piece_bit_size, piece_bit_offset);
|
|
|
+- }
|
|
|
+- return false;
|
|
|
+- }
|
|
|
+- }
|
|
|
+- break;
|
|
|
+-
|
|
|
+- // OPCODE: DW_OP_implicit_value
|
|
|
+- // OPERANDS: 2
|
|
|
+- // ULEB128 size of the value block in bytes
|
|
|
+- // uint8_t* block bytes encoding value in target's memory
|
|
|
+- // representation
|
|
|
+- // DESCRIPTION: Value is immediately stored in block in the debug info with
|
|
|
+- // the memory representation of the target.
|
|
|
+- case DW_OP_implicit_value: {
|
|
|
+- dwarf4_location_description_kind = Implicit;
|
|
|
+-
|
|
|
+- const uint32_t len = opcodes.GetULEB128(&offset);
|
|
|
+- const void *data = opcodes.GetData(&offset, len);
|
|
|
+-
|
|
|
+- if (!data) {
|
|
|
+- LLDB_LOG(log, "Evaluate_DW_OP_implicit_value: could not be read data");
|
|
|
+- LLDB_ERRORF(error_ptr, "Could not evaluate %s.",
|
|
|
+- DW_OP_value_to_name(op));
|
|
|
+- return false;
|
|
|
+- }
|
|
|
+-
|
|
|
+- Value result(data, len);
|
|
|
+- stack.push_back(result);
|
|
|
+- break;
|
|
|
+- }
|
|
|
+-
|
|
|
+- case DW_OP_implicit_pointer: {
|
|
|
+- dwarf4_location_description_kind = Implicit;
|
|
|
+- LLDB_ERRORF(error_ptr, "Could not evaluate %s.", DW_OP_value_to_name(op));
|
|
|
+- return false;
|
|
|
+- }
|
|
|
+-
|
|
|
+- // OPCODE: DW_OP_push_object_address
|
|
|
+- // OPERANDS: none
|
|
|
+- // DESCRIPTION: Pushes the address of the object currently being
|
|
|
+- // evaluated as part of evaluation of a user presented expression. This
|
|
|
+- // object may correspond to an independent variable described by its own
|
|
|
+- // DIE or it may be a component of an array, structure, or class whose
|
|
|
+- // address has been dynamically determined by an earlier step during user
|
|
|
+- // expression evaluation.
|
|
|
+- case DW_OP_push_object_address:
|
|
|
+- if (object_address_ptr)
|
|
|
+- stack.push_back(*object_address_ptr);
|
|
|
+- else {
|
|
|
+- if (error_ptr)
|
|
|
+- error_ptr->SetErrorString("DW_OP_push_object_address used without "
|
|
|
+- "specifying an object address");
|
|
|
+- return false;
|
|
|
+- }
|
|
|
+- break;
|
|
|
+-
|
|
|
+- // OPCODE: DW_OP_call2
|
|
|
+- // OPERANDS:
|
|
|
+- // uint16_t compile unit relative offset of a DIE
|
|
|
+- // DESCRIPTION: Performs subroutine calls during evaluation
|
|
|
+- // of a DWARF expression. The operand is the 2-byte unsigned offset of a
|
|
|
+- // debugging information entry in the current compilation unit.
|
|
|
+- //
|
|
|
+- // Operand interpretation is exactly like that for DW_FORM_ref2.
|
|
|
+- //
|
|
|
+- // This operation transfers control of DWARF expression evaluation to the
|
|
|
+- // DW_AT_location attribute of the referenced DIE. If there is no such
|
|
|
+- // attribute, then there is no effect. Execution of the DWARF expression of
|
|
|
+- // a DW_AT_location attribute may add to and/or remove from values on the
|
|
|
+- // stack. Execution returns to the point following the call when the end of
|
|
|
+- // the attribute is reached. Values on the stack at the time of the call
|
|
|
+- // may be used as parameters by the called expression and values left on
|
|
|
+- // the stack by the called expression may be used as return values by prior
|
|
|
+- // agreement between the calling and called expressions.
|
|
|
+- case DW_OP_call2:
|
|
|
+- if (error_ptr)
|
|
|
+- error_ptr->SetErrorString("Unimplemented opcode DW_OP_call2.");
|
|
|
+- return false;
|
|
|
+- // OPCODE: DW_OP_call4
|
|
|
+- // OPERANDS: 1
|
|
|
+- // uint32_t compile unit relative offset of a DIE
|
|
|
+- // DESCRIPTION: Performs a subroutine call during evaluation of a DWARF
|
|
|
+- // expression. For DW_OP_call4, the operand is a 4-byte unsigned offset of
|
|
|
+- // a debugging information entry in the current compilation unit.
|
|
|
+- //
|
|
|
+- // Operand interpretation DW_OP_call4 is exactly like that for
|
|
|
+- // DW_FORM_ref4.
|
|
|
+- //
|
|
|
+- // This operation transfers control of DWARF expression evaluation to the
|
|
|
+- // DW_AT_location attribute of the referenced DIE. If there is no such
|
|
|
+- // attribute, then there is no effect. Execution of the DWARF expression of
|
|
|
+- // a DW_AT_location attribute may add to and/or remove from values on the
|
|
|
+- // stack. Execution returns to the point following the call when the end of
|
|
|
+- // the attribute is reached. Values on the stack at the time of the call
|
|
|
+- // may be used as parameters by the called expression and values left on
|
|
|
+- // the stack by the called expression may be used as return values by prior
|
|
|
+- // agreement between the calling and called expressions.
|
|
|
+- case DW_OP_call4:
|
|
|
+- if (error_ptr)
|
|
|
+- error_ptr->SetErrorString("Unimplemented opcode DW_OP_call4.");
|
|
|
+- return false;
|
|
|
+-
|
|
|
+- // OPCODE: DW_OP_stack_value
|
|
|
+- // OPERANDS: None
|
|
|
+- // DESCRIPTION: Specifies that the object does not exist in memory but
|
|
|
+- // rather is a constant value. The value from the top of the stack is the
|
|
|
+- // value to be used. This is the actual object value and not the location.
|
|
|
+- case DW_OP_stack_value:
|
|
|
+- dwarf4_location_description_kind = Implicit;
|
|
|
+- if (stack.empty()) {
|
|
|
+- if (error_ptr)
|
|
|
+- error_ptr->SetErrorString(
|
|
|
+- "Expression stack needs at least 1 item for DW_OP_stack_value.");
|
|
|
+- return false;
|
|
|
+- }
|
|
|
+- stack.back().SetValueType(Value::ValueType::Scalar);
|
|
|
+- break;
|
|
|
+-
|
|
|
+- // OPCODE: DW_OP_convert
|
|
|
+- // OPERANDS: 1
|
|
|
+- // A ULEB128 that is either a DIE offset of a
|
|
|
+- // DW_TAG_base_type or 0 for the generic (pointer-sized) type.
|
|
|
+- //
|
|
|
+- // DESCRIPTION: Pop the top stack element, convert it to a
|
|
|
+- // different type, and push the result.
|
|
|
+- case DW_OP_convert: {
|
|
|
+- if (stack.size() < 1) {
|
|
|
+- if (error_ptr)
|
|
|
+- error_ptr->SetErrorString(
|
|
|
+- "Expression stack needs at least 1 item for DW_OP_convert.");
|
|
|
+- return false;
|
|
|
+- }
|
|
|
+- const uint64_t die_offset = opcodes.GetULEB128(&offset);
|
|
|
+- uint64_t bit_size;
|
|
|
+- bool sign;
|
|
|
+- if (die_offset == 0) {
|
|
|
+- // The generic type has the size of an address on the target
|
|
|
+- // machine and an unspecified signedness. Scalar has no
|
|
|
+- // "unspecified signedness", so we use unsigned types.
|
|
|
+- if (!module_sp) {
|
|
|
+- if (error_ptr)
|
|
|
+- error_ptr->SetErrorString("No module");
|
|
|
+- return false;
|
|
|
+- }
|
|
|
+- sign = false;
|
|
|
+- bit_size = module_sp->GetArchitecture().GetAddressByteSize() * 8;
|
|
|
+- if (!bit_size) {
|
|
|
+- if (error_ptr)
|
|
|
+- error_ptr->SetErrorString("unspecified architecture");
|
|
|
+- return false;
|
|
|
+- }
|
|
|
+- } else {
|
|
|
+- // Retrieve the type DIE that the value is being converted to.
|
|
|
+- // FIXME: the constness has annoying ripple effects.
|
|
|
+- DWARFDIE die = const_cast<DWARFUnit *>(dwarf_cu)->GetDIE(die_offset);
|
|
|
+- if (!die) {
|
|
|
+- if (error_ptr)
|
|
|
+- error_ptr->SetErrorString("Cannot resolve DW_OP_convert type DIE");
|
|
|
+- return false;
|
|
|
+- }
|
|
|
+- uint64_t encoding =
|
|
|
+- die.GetAttributeValueAsUnsigned(DW_AT_encoding, DW_ATE_hi_user);
|
|
|
+- bit_size = die.GetAttributeValueAsUnsigned(DW_AT_byte_size, 0) * 8;
|
|
|
+- if (!bit_size)
|
|
|
+- bit_size = die.GetAttributeValueAsUnsigned(DW_AT_bit_size, 0);
|
|
|
+- if (!bit_size) {
|
|
|
+- if (error_ptr)
|
|
|
+- error_ptr->SetErrorString("Unsupported type size in DW_OP_convert");
|
|
|
+- return false;
|
|
|
+- }
|
|
|
+- switch (encoding) {
|
|
|
+- case DW_ATE_signed:
|
|
|
+- case DW_ATE_signed_char:
|
|
|
+- sign = true;
|
|
|
+- break;
|
|
|
+- case DW_ATE_unsigned:
|
|
|
+- case DW_ATE_unsigned_char:
|
|
|
+- sign = false;
|
|
|
+- break;
|
|
|
+- default:
|
|
|
+- if (error_ptr)
|
|
|
+- error_ptr->SetErrorString("Unsupported encoding in DW_OP_convert");
|
|
|
+- return false;
|
|
|
+- }
|
|
|
+- }
|
|
|
+- Scalar &top = stack.back().ResolveValue(exe_ctx);
|
|
|
+- top.TruncOrExtendTo(bit_size, sign);
|
|
|
+- break;
|
|
|
+- }
|
|
|
+-
|
|
|
+- // OPCODE: DW_OP_call_frame_cfa
|
|
|
+- // OPERANDS: None
|
|
|
+- // DESCRIPTION: Specifies a DWARF expression that pushes the value of
|
|
|
+- // the canonical frame address consistent with the call frame information
|
|
|
+- // located in .debug_frame (or in the FDEs of the eh_frame section).
|
|
|
+- case DW_OP_call_frame_cfa:
|
|
|
+- if (frame) {
|
|
|
+- // Note that we don't have to parse FDEs because this DWARF expression
|
|
|
+- // is commonly evaluated with a valid stack frame.
|
|
|
+- StackID id = frame->GetStackID();
|
|
|
+- addr_t cfa = id.GetCallFrameAddress();
|
|
|
+- if (cfa != LLDB_INVALID_ADDRESS) {
|
|
|
+- stack.push_back(Scalar(cfa));
|
|
|
+- stack.back().SetValueType(Value::ValueType::LoadAddress);
|
|
|
+- } else if (error_ptr)
|
|
|
+- error_ptr->SetErrorString("Stack frame does not include a canonical "
|
|
|
+- "frame address for DW_OP_call_frame_cfa "
|
|
|
+- "opcode.");
|
|
|
+- } else {
|
|
|
+- if (error_ptr)
|
|
|
+- error_ptr->SetErrorString("Invalid stack frame in context for "
|
|
|
+- "DW_OP_call_frame_cfa opcode.");
|
|
|
+- return false;
|
|
|
+- }
|
|
|
+- break;
|
|
|
+-
|
|
|
+- // OPCODE: DW_OP_form_tls_address (or the old pre-DWARFv3 vendor extension
|
|
|
+- // opcode, DW_OP_GNU_push_tls_address)
|
|
|
+- // OPERANDS: none
|
|
|
+- // DESCRIPTION: Pops a TLS offset from the stack, converts it to
|
|
|
+- // an address in the current thread's thread-local storage block, and
|
|
|
+- // pushes it on the stack.
|
|
|
+- case DW_OP_form_tls_address:
|
|
|
+- case DW_OP_GNU_push_tls_address: {
|
|
|
+- if (stack.size() < 1) {
|
|
|
+- if (error_ptr) {
|
|
|
+- if (op == DW_OP_form_tls_address)
|
|
|
+- error_ptr->SetErrorString(
|
|
|
+- "DW_OP_form_tls_address needs an argument.");
|
|
|
+- else
|
|
|
+- error_ptr->SetErrorString(
|
|
|
+- "DW_OP_GNU_push_tls_address needs an argument.");
|
|
|
+- }
|
|
|
+- return false;
|
|
|
+- }
|
|
|
+-
|
|
|
+- if (!exe_ctx || !module_sp) {
|
|
|
+- if (error_ptr)
|
|
|
+- error_ptr->SetErrorString("No context to evaluate TLS within.");
|
|
|
+- return false;
|
|
|
+- }
|
|
|
+-
|
|
|
+- Thread *thread = exe_ctx->GetThreadPtr();
|
|
|
+- if (!thread) {
|
|
|
+- if (error_ptr)
|
|
|
+- error_ptr->SetErrorString("No thread to evaluate TLS within.");
|
|
|
+- return false;
|
|
|
+- }
|
|
|
+-
|
|
|
+- // Lookup the TLS block address for this thread and module.
|
|
|
+- const addr_t tls_file_addr =
|
|
|
+- stack.back().GetScalar().ULongLong(LLDB_INVALID_ADDRESS);
|
|
|
+- const addr_t tls_load_addr =
|
|
|
+- thread->GetThreadLocalData(module_sp, tls_file_addr);
|
|
|
+-
|
|
|
+- if (tls_load_addr == LLDB_INVALID_ADDRESS) {
|
|
|
+- if (error_ptr)
|
|
|
+- error_ptr->SetErrorString(
|
|
|
+- "No TLS data currently exists for this thread.");
|
|
|
+- return false;
|
|
|
+- }
|
|
|
+-
|
|
|
+- stack.back().GetScalar() = tls_load_addr;
|
|
|
+- stack.back().SetValueType(Value::ValueType::LoadAddress);
|
|
|
+- } break;
|
|
|
+-
|
|
|
+- // OPCODE: DW_OP_addrx (DW_OP_GNU_addr_index is the legacy name.)
|
|
|
+- // OPERANDS: 1
|
|
|
+- // ULEB128: index to the .debug_addr section
|
|
|
+- // DESCRIPTION: Pushes an address to the stack from the .debug_addr
|
|
|
+- // section with the base address specified by the DW_AT_addr_base attribute
|
|
|
+- // and the 0 based index is the ULEB128 encoded index.
|
|
|
+- case DW_OP_addrx:
|
|
|
+- case DW_OP_GNU_addr_index: {
|
|
|
+- if (!dwarf_cu) {
|
|
|
+- if (error_ptr)
|
|
|
+- error_ptr->SetErrorString("DW_OP_GNU_addr_index found without a "
|
|
|
+- "compile unit being specified");
|
|
|
+- return false;
|
|
|
+- }
|
|
|
+- uint64_t index = opcodes.GetULEB128(&offset);
|
|
|
+- lldb::addr_t value = ReadAddressFromDebugAddrSection(dwarf_cu, index);
|
|
|
+- stack.push_back(Scalar(value));
|
|
|
+- stack.back().SetValueType(Value::ValueType::FileAddress);
|
|
|
+- } break;
|
|
|
+-
|
|
|
+- // OPCODE: DW_OP_GNU_const_index
|
|
|
+- // OPERANDS: 1
|
|
|
+- // ULEB128: index to the .debug_addr section
|
|
|
+- // DESCRIPTION: Pushes an constant with the size of a machine address to
|
|
|
+- // the stack from the .debug_addr section with the base address specified
|
|
|
+- // by the DW_AT_addr_base attribute and the 0 based index is the ULEB128
|
|
|
+- // encoded index.
|
|
|
+- case DW_OP_GNU_const_index: {
|
|
|
+- if (!dwarf_cu) {
|
|
|
+- if (error_ptr)
|
|
|
+- error_ptr->SetErrorString("DW_OP_GNU_const_index found without a "
|
|
|
+- "compile unit being specified");
|
|
|
+- return false;
|
|
|
+- }
|
|
|
+- uint64_t index = opcodes.GetULEB128(&offset);
|
|
|
+- lldb::addr_t value = ReadAddressFromDebugAddrSection(dwarf_cu, index);
|
|
|
+- stack.push_back(Scalar(value));
|
|
|
+- } break;
|
|
|
+-
|
|
|
+- case DW_OP_GNU_entry_value:
|
|
|
+- case DW_OP_entry_value: {
|
|
|
+- if (!Evaluate_DW_OP_entry_value(stack, exe_ctx, reg_ctx, opcodes, offset,
|
|
|
+- error_ptr, log)) {
|
|
|
+- LLDB_ERRORF(error_ptr, "Could not evaluate %s.",
|
|
|
+- DW_OP_value_to_name(op));
|
|
|
+- return false;
|
|
|
+- }
|
|
|
+- break;
|
|
|
+- }
|
|
|
+-
|
|
|
+- default:
|
|
|
+- if (error_ptr)
|
|
|
+- error_ptr->SetErrorStringWithFormatv(
|
|
|
+- "Unhandled opcode {0} in DWARFExpression", LocationAtom(op));
|
|
|
+- return false;
|
|
|
+- }
|
|
|
+- }
|
|
|
+-
|
|
|
+- 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 (pieces.GetBuffer().GetByteSize()) {
|
|
|
+- result = pieces;
|
|
|
+- return true;
|
|
|
+- }
|
|
|
+- if (error_ptr)
|
|
|
+- error_ptr->SetErrorString("Stack empty after evaluation.");
|
|
|
+- return false;
|
|
|
+- }
|
|
|
+-
|
|
|
+- UpdateValueTypeFromLocationDescription(
|
|
|
+- log, dwarf_cu, dwarf4_location_description_kind, &stack.back());
|
|
|
+-
|
|
|
+- if (log && log->GetVerbose()) {
|
|
|
+- size_t count = stack.size();
|
|
|
+- LLDB_LOGF(log,
|
|
|
+- "Stack after operation has %" PRIu64 " values:", (uint64_t)count);
|
|
|
+- for (size_t i = 0; i < count; ++i) {
|
|
|
+- StreamString new_value;
|
|
|
+- new_value.Printf("[%" PRIu64 "]", (uint64_t)i);
|
|
|
+- stack[i].Dump(&new_value);
|
|
|
+- LLDB_LOGF(log, " %s", new_value.GetData());
|
|
|
+- }
|
|
|
+- }
|
|
|
+- result = stack.back();
|
|
|
+- return true; // Return true on success
|
|
|
++ DWARFExpression expr(module_sp, opcodes, dwarf_cu);
|
|
|
++ expr.SetRegisterKind(reg_kind);
|
|
|
++
|
|
|
++ // Use the DWARF expression evaluator registered for this module (or
|
|
|
++ // DWARFEvaluator by default).
|
|
|
++ DWARFEvaluatorFactory *evaluator_factory =
|
|
|
++ module_sp->GetDWARFExpressionEvaluatorFactory();
|
|
|
++ std::unique_ptr<DWARFEvaluator> evaluator =
|
|
|
++ evaluator_factory->CreateDWARFEvaluator(
|
|
|
++ expr, exe_ctx, reg_ctx, initial_value_ptr, object_address_ptr);
|
|
|
++ return evaluator->Evaluate(result, error_ptr);
|
|
|
+ }
|
|
|
+
|
|
|
+ static DataExtractor ToDataExtractor(const llvm::DWARFLocationExpression &loc,
|
|
|
+diff --git a/lldb/source/Interpreter/CommandInterpreter.cpp b/lldb/source/Interpreter/CommandInterpreter.cpp
|
|
|
+index 00e9ccb762c3..2137a1ac8324 100644
|
|
|
+--- a/lldb/source/Interpreter/CommandInterpreter.cpp
|
|
|
++++ b/lldb/source/Interpreter/CommandInterpreter.cpp
|
|
|
+@@ -759,6 +759,24 @@ void CommandInterpreter::LoadCommandDictionary() {
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
++ std::unique_ptr<CommandObjectRegexCommand> connect_wasm_cmd_up(
|
|
|
++ new CommandObjectRegexCommand(
|
|
|
++ *this, "wasm",
|
|
|
++ "Connect to a WebAssembly process via remote GDB server. "
|
|
|
++ "If no host is specifed, localhost is assumed.",
|
|
|
++ "wasm [<hostname>:]<portnum>", 2, 0, false));
|
|
|
++ if (connect_wasm_cmd_up) {
|
|
|
++ if (connect_wasm_cmd_up->AddRegexCommand(
|
|
|
++ "^([^:]+|\\[[0-9a-fA-F:]+.*\\]):([0-9]+)$",
|
|
|
++ "process connect --plugin wasm connect://%1:%2") &&
|
|
|
++ connect_wasm_cmd_up->AddRegexCommand(
|
|
|
++ "^([[:digit:]]+)$",
|
|
|
++ "process connect --plugin wasm connect://localhost:%1")) {
|
|
|
++ CommandObjectSP command_sp(connect_wasm_cmd_up.release());
|
|
|
++ m_command_dict[std::string(command_sp->GetCommandName())] = command_sp;
|
|
|
++ }
|
|
|
++ }
|
|
|
++
|
|
|
+ std::unique_ptr<CommandObjectRegexCommand> connect_kdp_remote_cmd_up(
|
|
|
+ new CommandObjectRegexCommand(
|
|
|
+ *this, "kdp-remote",
|
|
|
+diff --git a/lldb/source/Plugins/CMakeLists.txt b/lldb/source/Plugins/CMakeLists.txt
|
|
|
+index 9181a4e47675..2be6ec3657c0 100644
|
|
|
+--- a/lldb/source/Plugins/CMakeLists.txt
|
|
|
++++ b/lldb/source/Plugins/CMakeLists.txt
|
|
|
+@@ -2,6 +2,7 @@ add_subdirectory(ABI)
|
|
|
+ add_subdirectory(Architecture)
|
|
|
+ add_subdirectory(Disassembler)
|
|
|
+ add_subdirectory(DynamicLoader)
|
|
|
++add_subdirectory(DWARFEvaluator)
|
|
|
+ add_subdirectory(ExpressionParser)
|
|
|
+ add_subdirectory(Instruction)
|
|
|
+ add_subdirectory(InstrumentationRuntime)
|
|
|
+@@ -32,6 +33,7 @@ set(LLDB_ENUM_PLUGINS "")
|
|
|
+ # FIXME: ProcessWindowsCommon needs to be initialized after all other process
|
|
|
+ # plugins but before ProcessGDBRemote.
|
|
|
+ set(LLDB_PROCESS_WINDOWS_PLUGIN "")
|
|
|
++set(LLDB_PROCESS_WASM_PLUGIN "")
|
|
|
+ set(LLDB_PROCESS_GDB_PLUGIN "")
|
|
|
+
|
|
|
+ foreach(p ${LLDB_ALL_PLUGINS})
|
|
|
+@@ -43,6 +45,8 @@ foreach(p ${LLDB_ALL_PLUGINS})
|
|
|
+ set(LLDB_PROCESS_WINDOWS_PLUGIN "LLDB_PLUGIN(${pStripped})\n")
|
|
|
+ elseif(${pStripped} STREQUAL "ProcessGDBRemote")
|
|
|
+ set(LLDB_PROCESS_GDB_PLUGIN "LLDB_PLUGIN(${pStripped})\n")
|
|
|
++ elseif(${pStripped} STREQUAL "ProcessWasm")
|
|
|
++ set(LLDB_PROCESS_WASM_PLUGIN "LLDB_PLUGIN(${pStripped})\n")
|
|
|
+ else()
|
|
|
+ set(LLDB_ENUM_PLUGINS "${LLDB_ENUM_PLUGINS}LLDB_PLUGIN(${pStripped})\n")
|
|
|
+ endif()
|
|
|
+diff --git a/lldb/source/Plugins/DWARFEvaluator/CMakeLists.txt b/lldb/source/Plugins/DWARFEvaluator/CMakeLists.txt
|
|
|
+new file mode 100644
|
|
|
+index 000000000000..73fad41e1a72
|
|
|
+--- /dev/null
|
|
|
++++ b/lldb/source/Plugins/DWARFEvaluator/CMakeLists.txt
|
|
|
+@@ -0,0 +1 @@
|
|
|
++add_subdirectory(wasm)
|
|
|
+diff --git a/lldb/source/Plugins/DWARFEvaluator/wasm/CMakeLists.txt b/lldb/source/Plugins/DWARFEvaluator/wasm/CMakeLists.txt
|
|
|
+new file mode 100644
|
|
|
+index 000000000000..e50b1bef7e69
|
|
|
+--- /dev/null
|
|
|
++++ b/lldb/source/Plugins/DWARFEvaluator/wasm/CMakeLists.txt
|
|
|
+@@ -0,0 +1,10 @@
|
|
|
++add_lldb_library(lldbPluginWasmDWARFEvaluatorFactory PLUGIN
|
|
|
++ WasmDWARFEvaluator.cpp
|
|
|
++ WasmDWARFEvaluatorFactory.cpp
|
|
|
++
|
|
|
++ LINK_LIBS
|
|
|
++ lldbCore
|
|
|
++ lldbHost
|
|
|
++ lldbSymbol
|
|
|
++ lldbPluginObjectFileWasm
|
|
|
++ )
|
|
|
+diff --git a/lldb/source/Plugins/DWARFEvaluator/wasm/WasmDWARFEvaluator.cpp b/lldb/source/Plugins/DWARFEvaluator/wasm/WasmDWARFEvaluator.cpp
|
|
|
+new file mode 100644
|
|
|
+index 000000000000..fdda1991d19f
|
|
|
+--- /dev/null
|
|
|
++++ b/lldb/source/Plugins/DWARFEvaluator/wasm/WasmDWARFEvaluator.cpp
|
|
|
+@@ -0,0 +1,126 @@
|
|
|
++//===-- WasmDWARFEvaluator.cpp --------------------------------------------===//
|
|
|
++//
|
|
|
++// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
|
|
++// See https://llvm.org/LICENSE.txt for license information.
|
|
|
++// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
|
|
++//
|
|
|
++//===----------------------------------------------------------------------===//
|
|
|
++
|
|
|
++#include "WasmDWARFEvaluator.h"
|
|
|
++
|
|
|
++#include "Plugins/ObjectFile/wasm/ObjectFileWasm.h"
|
|
|
++#include "Plugins/Process/wasm/ProcessWasm.h"
|
|
|
++#include "lldb/Core/Module.h"
|
|
|
++#include "lldb/Core/PluginManager.h"
|
|
|
++#include "lldb/Core/Value.h"
|
|
|
++#include "lldb/Core/dwarf.h"
|
|
|
++#include "lldb/Expression/DWARFExpression.h"
|
|
|
++
|
|
|
++using namespace lldb;
|
|
|
++using namespace lldb_private;
|
|
|
++using namespace lldb_private::wasm;
|
|
|
++
|
|
|
++bool WasmDWARFEvaluator::Evaluate(const uint8_t op, Process *process,
|
|
|
++ StackFrame *frame, std::vector<Value> &stack,
|
|
|
++ const DataExtractor &opcodes,
|
|
|
++ lldb::offset_t &offset, Value &pieces,
|
|
|
++ uint64_t &op_piece_offset, Log *log,
|
|
|
++ Status *error_ptr) {
|
|
|
++ lldb::ModuleSP module_sp = m_dwarf_expression.GetModule();
|
|
|
++
|
|
|
++ switch (op) {
|
|
|
++ case DW_OP_WASM_location: {
|
|
|
++ if (frame) {
|
|
|
++ const llvm::Triple::ArchType machine =
|
|
|
++ frame->CalculateTarget()->GetArchitecture().GetMachine();
|
|
|
++ if (machine != llvm::Triple::wasm32) {
|
|
|
++ if (error_ptr)
|
|
|
++ error_ptr->SetErrorString("Invalid target architecture for "
|
|
|
++ "DW_OP_WASM_location opcode.");
|
|
|
++ return false;
|
|
|
++ }
|
|
|
++
|
|
|
++ ProcessWasm *wasm_process =
|
|
|
++ static_cast<wasm::ProcessWasm *>(frame->CalculateProcess().get());
|
|
|
++ int frame_index = frame->GetConcreteFrameIndex();
|
|
|
++ uint64_t wasm_op = opcodes.GetULEB128(&offset);
|
|
|
++ uint64_t index = opcodes.GetULEB128(&offset);
|
|
|
++ uint8_t buf[16];
|
|
|
++ size_t size = 0;
|
|
|
++ switch (wasm_op) {
|
|
|
++ case 0: // Local
|
|
|
++ if (!wasm_process->GetWasmLocal(frame_index, index, buf, 16, size)) {
|
|
|
++ return false;
|
|
|
++ }
|
|
|
++ break;
|
|
|
++ case 1: // Global
|
|
|
++ if (!wasm_process->GetWasmGlobal(frame_index, index, buf, 16, size)) {
|
|
|
++ return false;
|
|
|
++ }
|
|
|
++ break;
|
|
|
++ case 2: // Operand Stack
|
|
|
++ if (!wasm_process->GetWasmStackValue(frame_index, index, buf, 16,
|
|
|
++ size)) {
|
|
|
++ return false;
|
|
|
++ }
|
|
|
++ break;
|
|
|
++ default:
|
|
|
++ return false;
|
|
|
++ }
|
|
|
++
|
|
|
++ if (size == sizeof(uint32_t)) {
|
|
|
++ uint32_t value;
|
|
|
++ memcpy(&value, buf, size);
|
|
|
++ stack.push_back(Scalar(value));
|
|
|
++ } else if (size == sizeof(uint64_t)) {
|
|
|
++ uint64_t value;
|
|
|
++ memcpy(&value, buf, size);
|
|
|
++ stack.push_back(Scalar(value));
|
|
|
++ } else
|
|
|
++ return false;
|
|
|
++ } else {
|
|
|
++ if (error_ptr)
|
|
|
++ error_ptr->SetErrorString("Invalid stack frame in context for "
|
|
|
++ "DW_OP_WASM_location opcode.");
|
|
|
++ return false;
|
|
|
++ }
|
|
|
++ } break;
|
|
|
++
|
|
|
++ case DW_OP_addr: {
|
|
|
++ /// {addr} is an offset in the module Data section.
|
|
|
++ lldb::addr_t addr = opcodes.GetAddress(&offset);
|
|
|
++ stack.push_back(Scalar(addr));
|
|
|
++ stack.back().SetValueType(Value::ValueType::LoadAddress);
|
|
|
++ } break;
|
|
|
++
|
|
|
++ case DW_OP_fbreg:
|
|
|
++ if (m_exe_ctx) {
|
|
|
++ if (frame) {
|
|
|
++ Scalar value;
|
|
|
++ if (frame->GetFrameBaseValue(value, error_ptr)) {
|
|
|
++ // The value is an address in the Wasm Memory space.
|
|
|
++ int64_t fbreg_offset = opcodes.GetSLEB128(&offset);
|
|
|
++ stack.push_back(Scalar(value.ULong() + fbreg_offset));
|
|
|
++ stack.back().SetValueType(Value::ValueType::LoadAddress);
|
|
|
++ } else
|
|
|
++ return false;
|
|
|
++ } else {
|
|
|
++ if (error_ptr)
|
|
|
++ error_ptr->SetErrorString(
|
|
|
++ "Invalid stack frame in context for DW_OP_fbreg opcode.");
|
|
|
++ return false;
|
|
|
++ }
|
|
|
++ } else {
|
|
|
++ if (error_ptr)
|
|
|
++ error_ptr->SetErrorStringWithFormat(
|
|
|
++ "NULL execution context for DW_OP_fbreg.\n");
|
|
|
++ return false;
|
|
|
++ }
|
|
|
++ break;
|
|
|
++
|
|
|
++ default:
|
|
|
++ return DWARFEvaluator::Evaluate(op, process, frame, stack, opcodes, offset,
|
|
|
++ pieces, op_piece_offset, log, error_ptr);
|
|
|
++ }
|
|
|
++ return true;
|
|
|
++}
|
|
|
+diff --git a/lldb/source/Plugins/DWARFEvaluator/wasm/WasmDWARFEvaluator.h b/lldb/source/Plugins/DWARFEvaluator/wasm/WasmDWARFEvaluator.h
|
|
|
+new file mode 100644
|
|
|
+index 000000000000..a01159064a39
|
|
|
+--- /dev/null
|
|
|
++++ b/lldb/source/Plugins/DWARFEvaluator/wasm/WasmDWARFEvaluator.h
|
|
|
+@@ -0,0 +1,47 @@
|
|
|
++//===-- WasmDWARFEvaluator.h ------------------------------------*- C++ -*-===//
|
|
|
++//
|
|
|
++// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
|
|
++// See https://llvm.org/LICENSE.txt for license information.
|
|
|
++// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
|
|
++//
|
|
|
++//===----------------------------------------------------------------------===//
|
|
|
++
|
|
|
++#ifndef LLDB_SOURCE_PLUGINS_DWARFEVALUATOR_WASM_WASMDWARFEVALUATOR_H
|
|
|
++#define LLDB_SOURCE_PLUGINS_DWARFEVALUATOR_WASM_WASMDWARFEVALUATOR_H
|
|
|
++
|
|
|
++#include "lldb/Expression/DWARFEvaluator.h"
|
|
|
++#include "lldb/lldb-private.h"
|
|
|
++
|
|
|
++namespace lldb_private {
|
|
|
++namespace wasm {
|
|
|
++
|
|
|
++/// \class WasmDWARFEvaluator evaluates DWARF expressions in the context of a
|
|
|
++/// WebAssembly process.
|
|
|
++///
|
|
|
++class WasmDWARFEvaluator : public DWARFEvaluator {
|
|
|
++public:
|
|
|
++ WasmDWARFEvaluator(const DWARFExpression &dwarf_expression,
|
|
|
++ ExecutionContext *exe_ctx, RegisterContext *reg_ctx,
|
|
|
++ const Value *initial_value_ptr,
|
|
|
++ const Value *object_address_ptr)
|
|
|
++ : DWARFEvaluator(dwarf_expression, exe_ctx, reg_ctx, initial_value_ptr,
|
|
|
++ object_address_ptr) {}
|
|
|
++
|
|
|
++ /// DWARFEvaluator protocol.
|
|
|
++ /// \{
|
|
|
++ bool Evaluate(const uint8_t op, Process *process, StackFrame *frame,
|
|
|
++ std::vector<Value> &stack, const DataExtractor &opcodes,
|
|
|
++ lldb::offset_t &offset, Value &pieces,
|
|
|
++ uint64_t &op_piece_offset, Log *log,
|
|
|
++ Status *error_ptr) override;
|
|
|
++ /// \}
|
|
|
++
|
|
|
++private:
|
|
|
++ WasmDWARFEvaluator(const WasmDWARFEvaluator &);
|
|
|
++ const WasmDWARFEvaluator &operator=(const WasmDWARFEvaluator &) = delete;
|
|
|
++};
|
|
|
++
|
|
|
++} // namespace wasm
|
|
|
++} // namespace lldb_private
|
|
|
++
|
|
|
++#endif // LLDB_SOURCE_PLUGINS_DWARFEVALUATOR_WASM_WASMDWARFEVALUATOR_H
|
|
|
+diff --git a/lldb/source/Plugins/DWARFEvaluator/wasm/WasmDWARFEvaluatorFactory.cpp b/lldb/source/Plugins/DWARFEvaluator/wasm/WasmDWARFEvaluatorFactory.cpp
|
|
|
+new file mode 100644
|
|
|
+index 000000000000..d43e96a34d37
|
|
|
+--- /dev/null
|
|
|
++++ b/lldb/source/Plugins/DWARFEvaluator/wasm/WasmDWARFEvaluatorFactory.cpp
|
|
|
+@@ -0,0 +1,64 @@
|
|
|
++//===-- WasmDWARFEvaluatorFactory.cpp -------------------------------------===//
|
|
|
++//
|
|
|
++// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
|
|
++// See https://llvm.org/LICENSE.txt for license information.
|
|
|
++// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
|
|
++//
|
|
|
++//===----------------------------------------------------------------------===//
|
|
|
++
|
|
|
++#include "WasmDWARFEvaluatorFactory.h"
|
|
|
++#include "WasmDWARFEvaluator.h"
|
|
|
++
|
|
|
++#include "Plugins/ObjectFile/wasm/ObjectFileWasm.h"
|
|
|
++#include "lldb/Core/Module.h"
|
|
|
++#include "lldb/Core/PluginManager.h"
|
|
|
++
|
|
|
++using namespace lldb;
|
|
|
++using namespace lldb_private;
|
|
|
++using namespace lldb_private::wasm;
|
|
|
++
|
|
|
++LLDB_PLUGIN_DEFINE(WasmDWARFEvaluatorFactory)
|
|
|
++
|
|
|
++void WasmDWARFEvaluatorFactory::Initialize() {
|
|
|
++ PluginManager::RegisterPlugin(GetPluginNameStatic(),
|
|
|
++ GetPluginDescriptionStatic(), CreateInstance);
|
|
|
++}
|
|
|
++
|
|
|
++void WasmDWARFEvaluatorFactory::Terminate() {
|
|
|
++ PluginManager::UnregisterPlugin(CreateInstance);
|
|
|
++}
|
|
|
++
|
|
|
++lldb_private::ConstString WasmDWARFEvaluatorFactory::GetPluginNameStatic() {
|
|
|
++ static ConstString g_name("WASM");
|
|
|
++ return g_name;
|
|
|
++}
|
|
|
++
|
|
|
++const char *WasmDWARFEvaluatorFactory::GetPluginDescriptionStatic() {
|
|
|
++ return "DWARF expression evaluator factory for WASM.";
|
|
|
++}
|
|
|
++
|
|
|
++// CreateInstance
|
|
|
++//
|
|
|
++// Platforms can register a callback to use when creating DWARF expression
|
|
|
++// evaluators to allow handling platform-specific DWARF codes.
|
|
|
++DWARFEvaluatorFactory *
|
|
|
++WasmDWARFEvaluatorFactory::CreateInstance(Module *module) {
|
|
|
++ if (!module)
|
|
|
++ return nullptr;
|
|
|
++
|
|
|
++ ObjectFileWasm *obj_file =
|
|
|
++ llvm::dyn_cast_or_null<ObjectFileWasm>(module->GetObjectFile());
|
|
|
++ if (!obj_file)
|
|
|
++ return nullptr;
|
|
|
++
|
|
|
++ return new WasmDWARFEvaluatorFactory();
|
|
|
++}
|
|
|
++
|
|
|
++std::unique_ptr<DWARFEvaluator> WasmDWARFEvaluatorFactory::CreateDWARFEvaluator(
|
|
|
++ const DWARFExpression &dwarf_expression, ExecutionContext *exe_ctx,
|
|
|
++ RegisterContext *reg_ctx, const Value *initial_value_ptr,
|
|
|
++ const Value *object_address_ptr) {
|
|
|
++ return std::make_unique<WasmDWARFEvaluator>(dwarf_expression, exe_ctx,
|
|
|
++ reg_ctx, initial_value_ptr,
|
|
|
++ object_address_ptr);
|
|
|
++}
|
|
|
+diff --git a/lldb/source/Plugins/DWARFEvaluator/wasm/WasmDWARFEvaluatorFactory.h b/lldb/source/Plugins/DWARFEvaluator/wasm/WasmDWARFEvaluatorFactory.h
|
|
|
+new file mode 100644
|
|
|
+index 000000000000..8a946592a09a
|
|
|
+--- /dev/null
|
|
|
++++ b/lldb/source/Plugins/DWARFEvaluator/wasm/WasmDWARFEvaluatorFactory.h
|
|
|
+@@ -0,0 +1,55 @@
|
|
|
++//===-- WasmDWARFEvaluatorFactory.h -----------------------------*- C++ -*-===//
|
|
|
++//
|
|
|
++// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
|
|
++// See https://llvm.org/LICENSE.txt for license information.
|
|
|
++// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
|
|
++//
|
|
|
++//===----------------------------------------------------------------------===//
|
|
|
++
|
|
|
++#ifndef LLDB_SOURCE_PLUGINS_DWARFEVALUATOR_WASM_WASMDWARFEVALUATORFACTORY_H
|
|
|
++#define LLDB_SOURCE_PLUGINS_DWARFEVALUATOR_WASM_WASMDWARFEVALUATORFACTORY_H
|
|
|
++
|
|
|
++#include "lldb/Expression/DWARFEvaluatorFactory.h"
|
|
|
++
|
|
|
++namespace lldb_private {
|
|
|
++namespace wasm {
|
|
|
++
|
|
|
++/// \class WasmDWARFEvaluatorFactory creates DWARF evaluators specialized to
|
|
|
++/// manage DWARF-specific opcodes.
|
|
|
++class WasmDWARFEvaluatorFactory : public DWARFEvaluatorFactory {
|
|
|
++public:
|
|
|
++ static void Initialize();
|
|
|
++ static void Terminate();
|
|
|
++ static lldb_private::ConstString GetPluginNameStatic();
|
|
|
++ static const char *GetPluginDescriptionStatic();
|
|
|
++
|
|
|
++ static lldb_private::DWARFEvaluatorFactory *CreateInstance(Module *module);
|
|
|
++
|
|
|
++ /// PluginInterface protocol.
|
|
|
++ /// \{
|
|
|
++ lldb_private::ConstString GetPluginName() override {
|
|
|
++ return GetPluginNameStatic();
|
|
|
++ }
|
|
|
++ uint32_t GetPluginVersion() override { return 1; }
|
|
|
++ /// \}
|
|
|
++
|
|
|
++ WasmDWARFEvaluatorFactory() {}
|
|
|
++
|
|
|
++ /// DWARFEvaluatorFactory protocol.
|
|
|
++ /// \{
|
|
|
++ std::unique_ptr<DWARFEvaluator>
|
|
|
++ CreateDWARFEvaluator(const DWARFExpression &dwarf_expression,
|
|
|
++ ExecutionContext *exe_ctx, RegisterContext *reg_ctx,
|
|
|
++ const Value *initial_value_ptr,
|
|
|
++ const Value *object_address_ptr) override;
|
|
|
++ /// \}
|
|
|
++
|
|
|
++private:
|
|
|
++ WasmDWARFEvaluatorFactory(const WasmDWARFEvaluatorFactory &);
|
|
|
++ const WasmDWARFEvaluatorFactory &operator=(const WasmDWARFEvaluatorFactory &) = delete;
|
|
|
++};
|
|
|
++
|
|
|
++} // namespace wasm
|
|
|
++} // namespace lldb_private
|
|
|
++
|
|
|
++#endif // LLDB_SOURCE_PLUGINS_DWARFEVALUATOR_WASM_WASMDWARFEVALUATORFACTORY_H
|
|
|
+diff --git a/lldb/source/Plugins/DynamicLoader/wasm-DYLD/DynamicLoaderWasmDYLD.cpp b/lldb/source/Plugins/DynamicLoader/wasm-DYLD/DynamicLoaderWasmDYLD.cpp
|
|
|
+index ae7e011eaa52..24ea75d1971c 100644
|
|
|
+--- a/lldb/source/Plugins/DynamicLoader/wasm-DYLD/DynamicLoaderWasmDYLD.cpp
|
|
|
++++ b/lldb/source/Plugins/DynamicLoader/wasm-DYLD/DynamicLoaderWasmDYLD.cpp
|
|
|
+@@ -62,6 +62,15 @@ void DynamicLoaderWasmDYLD::DidAttach() {
|
|
|
+ // Ask the process for the list of loaded WebAssembly modules.
|
|
|
+ auto error = m_process->LoadModules();
|
|
|
+ LLDB_LOG_ERROR(log, std::move(error), "Couldn't load modules: {0}");
|
|
|
++
|
|
|
++ // TODO: multi-modules support ?
|
|
|
++ Target &target = m_process->GetTarget();
|
|
|
++ const ModuleList &modules = target.GetImages();
|
|
|
++ ModuleSP module_sp(modules.GetModuleAtIndex(0));
|
|
|
++ // module_sp is nullptr if without libxml2
|
|
|
++ if(module_sp) {
|
|
|
++ module_sp->PreloadSymbols();
|
|
|
++ }
|
|
|
+ }
|
|
|
+
|
|
|
+ ThreadPlanSP DynamicLoaderWasmDYLD::GetStepThroughTrampolinePlan(Thread &thread,
|
|
|
+diff --git a/lldb/source/Plugins/ObjectFile/wasm/ObjectFileWasm.cpp b/lldb/source/Plugins/ObjectFile/wasm/ObjectFileWasm.cpp
|
|
|
+index 5272da9ab33a..abc5523bfd70 100644
|
|
|
+--- a/lldb/source/Plugins/ObjectFile/wasm/ObjectFileWasm.cpp
|
|
|
++++ b/lldb/source/Plugins/ObjectFile/wasm/ObjectFileWasm.cpp
|
|
|
+@@ -23,6 +23,7 @@
|
|
|
+ #include "llvm/BinaryFormat/Wasm.h"
|
|
|
+ #include "llvm/Support/Endian.h"
|
|
|
+ #include "llvm/Support/Format.h"
|
|
|
++#include "Plugins/Process/wasm/ProcessWasm.h"
|
|
|
+
|
|
|
+ using namespace lldb;
|
|
|
+ using namespace lldb_private;
|
|
|
+@@ -341,6 +342,8 @@ void ObjectFileWasm::CreateSections(SectionList &unified_section_list) {
|
|
|
+ 0, // Alignment of the section
|
|
|
+ 0, // Flags for this section.
|
|
|
+ 1)); // Number of host bytes per target byte
|
|
|
++ if (section_type == eSectionTypeCode)
|
|
|
++ section_sp->SetPermissions(ePermissionsReadable|ePermissionsExecutable);
|
|
|
+ m_sections_up->AddSection(section_sp);
|
|
|
+ unified_section_list.AddSection(section_sp);
|
|
|
+ }
|
|
|
+@@ -367,6 +370,7 @@ bool ObjectFileWasm::SetLoadAddress(Target &target, lldb::addr_t load_address,
|
|
|
+ assert(m_memory_addr == LLDB_INVALID_ADDRESS ||
|
|
|
+ m_memory_addr == load_address);
|
|
|
+
|
|
|
++ lldb::addr_t adjust_addr;
|
|
|
+ ModuleSP module_sp = GetModule();
|
|
|
+ if (!module_sp)
|
|
|
+ return false;
|
|
|
+@@ -381,8 +385,9 @@ bool ObjectFileWasm::SetLoadAddress(Target &target, lldb::addr_t load_address,
|
|
|
+ const size_t num_sections = section_list->GetSize();
|
|
|
+ for (size_t sect_idx = 0; sect_idx < num_sections; ++sect_idx) {
|
|
|
+ SectionSP section_sp(section_list->GetSectionAtIndex(sect_idx));
|
|
|
++ adjust_addr = load_address;
|
|
|
+ if (target.SetSectionLoadAddress(
|
|
|
+- section_sp, load_address | section_sp->GetFileOffset())) {
|
|
|
++ section_sp, adjust_addr | section_sp->GetFileOffset())) {
|
|
|
+ ++num_loaded_sections;
|
|
|
+ }
|
|
|
+ }
|
|
|
+diff --git a/lldb/source/Plugins/Platform/CMakeLists.txt b/lldb/source/Plugins/Platform/CMakeLists.txt
|
|
|
+index 5f284e517dca..6084cbc9378d 100644
|
|
|
+--- a/lldb/source/Plugins/Platform/CMakeLists.txt
|
|
|
++++ b/lldb/source/Plugins/Platform/CMakeLists.txt
|
|
|
+@@ -15,3 +15,4 @@
|
|
|
+ add_subdirectory(POSIX)
|
|
|
+ add_subdirectory(gdb-server)
|
|
|
+ add_subdirectory(Android)
|
|
|
++add_subdirectory(wasm-remote)
|
|
|
+diff --git a/lldb/source/Plugins/Platform/wasm-remote/CMakeLists.txt b/lldb/source/Plugins/Platform/wasm-remote/CMakeLists.txt
|
|
|
+new file mode 100644
|
|
|
+index 000000000000..4a65765a5659
|
|
|
+--- /dev/null
|
|
|
++++ b/lldb/source/Plugins/Platform/wasm-remote/CMakeLists.txt
|
|
|
+@@ -0,0 +1,10 @@
|
|
|
++add_lldb_library(lldbPluginPlatformWasm PLUGIN
|
|
|
++ PlatformRemoteWasmServer.cpp
|
|
|
++
|
|
|
++ LINK_LIBS
|
|
|
++ lldbBreakpoint
|
|
|
++ lldbCore
|
|
|
++ lldbHost
|
|
|
++ lldbTarget
|
|
|
++ lldbPluginProcessUtility
|
|
|
++ )
|
|
|
+diff --git a/lldb/source/Plugins/Platform/wasm-remote/PlatformRemoteWasmServer.cpp b/lldb/source/Plugins/Platform/wasm-remote/PlatformRemoteWasmServer.cpp
|
|
|
+new file mode 100644
|
|
|
+index 000000000000..f26d11f00e5c
|
|
|
+--- /dev/null
|
|
|
++++ b/lldb/source/Plugins/Platform/wasm-remote/PlatformRemoteWasmServer.cpp
|
|
|
+@@ -0,0 +1,139 @@
|
|
|
++#include "PlatformRemoteWasmServer.h"
|
|
|
++#include "lldb/Host/Config.h"
|
|
|
++
|
|
|
++#include "lldb/Breakpoint/BreakpointLocation.h"
|
|
|
++#include "lldb/Core/Debugger.h"
|
|
|
++#include "lldb/Core/Module.h"
|
|
|
++#include "lldb/Core/ModuleList.h"
|
|
|
++#include "lldb/Core/ModuleSpec.h"
|
|
|
++#include "lldb/Core/PluginManager.h"
|
|
|
++#include "lldb/Core/StreamFile.h"
|
|
|
++#include "lldb/Host/ConnectionFileDescriptor.h"
|
|
|
++#include "lldb/Host/Host.h"
|
|
|
++#include "lldb/Host/HostInfo.h"
|
|
|
++#include "lldb/Host/PosixApi.h"
|
|
|
++#include "lldb/Target/Process.h"
|
|
|
++#include "lldb/Target/Target.h"
|
|
|
++#include "lldb/Utility/FileSpec.h"
|
|
|
++#include "lldb/Utility/Log.h"
|
|
|
++#include "lldb/Utility/ProcessInfo.h"
|
|
|
++#include "lldb/Utility/Status.h"
|
|
|
++#include "lldb/Utility/StreamString.h"
|
|
|
++#include "lldb/Utility/UriParser.h"
|
|
|
++
|
|
|
++#include "Plugins/Process/Utility/GDBRemoteSignals.h"
|
|
|
++
|
|
|
++using namespace lldb;
|
|
|
++using namespace lldb_private;
|
|
|
++using namespace lldb_private::platform_wasm_server;
|
|
|
++
|
|
|
++LLDB_PLUGIN_DEFINE_ADV(PlatformRemoteWASMServer, PlatformWasm)
|
|
|
++
|
|
|
++static bool g_initialized = false;
|
|
|
++
|
|
|
++void PlatformRemoteWASMServer::Initialize() {
|
|
|
++ Platform::Initialize();
|
|
|
++
|
|
|
++ if (!g_initialized) {
|
|
|
++ g_initialized = true;
|
|
|
++ PluginManager::RegisterPlugin(
|
|
|
++ PlatformRemoteWASMServer::GetPluginNameStatic(),
|
|
|
++ PlatformRemoteWASMServer::GetDescriptionStatic(),
|
|
|
++ PlatformRemoteWASMServer::CreateInstance);
|
|
|
++ }
|
|
|
++}
|
|
|
++
|
|
|
++void PlatformRemoteWASMServer::Terminate() {
|
|
|
++ if (g_initialized) {
|
|
|
++ g_initialized = false;
|
|
|
++ PluginManager::UnregisterPlugin(PlatformRemoteWASMServer::CreateInstance);
|
|
|
++ }
|
|
|
++
|
|
|
++ Platform::Terminate();
|
|
|
++}
|
|
|
++
|
|
|
++PlatformSP PlatformRemoteWASMServer::CreateInstance(bool force,
|
|
|
++ const ArchSpec *arch) {
|
|
|
++ Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_PLATFORM));
|
|
|
++ if (log) {
|
|
|
++ const char *arch_name;
|
|
|
++ if (arch && arch->GetArchitectureName())
|
|
|
++ arch_name = arch->GetArchitectureName();
|
|
|
++ else
|
|
|
++ arch_name = "<null>";
|
|
|
++
|
|
|
++ const char *triple_cstr =
|
|
|
++ arch ? arch->GetTriple().getTriple().c_str() : "<null>";
|
|
|
++
|
|
|
++ LLDB_LOGF(log, "PlatformRemoteWASMServer::%s(force=%s, arch={%s,%s})",
|
|
|
++ __FUNCTION__, force ? "true" : "false", arch_name, triple_cstr);
|
|
|
++ }
|
|
|
++
|
|
|
++ bool create = force;
|
|
|
++ if (!create && arch && arch->IsValid()) {
|
|
|
++ const llvm::Triple &triple = arch->GetTriple();
|
|
|
++ if (arch->GetMachine() == llvm::Triple::wasm32 &&
|
|
|
++ triple.getOS() == llvm::Triple::WASI) {
|
|
|
++ create = true;
|
|
|
++ }
|
|
|
++ }
|
|
|
++
|
|
|
++ if (create) {
|
|
|
++ if (log)
|
|
|
++ LLDB_LOGF(log, "PlatformRemoteWASMServer::%s() creating platform",
|
|
|
++ __FUNCTION__);
|
|
|
++ return PlatformSP(new PlatformRemoteWASMServer());
|
|
|
++ }
|
|
|
++
|
|
|
++ if (log)
|
|
|
++ LLDB_LOGF(log,
|
|
|
++ "PlatformRemoteWASMServer::%s() aborting creation of platform",
|
|
|
++ __FUNCTION__);
|
|
|
++ return PlatformSP();
|
|
|
++}
|
|
|
++
|
|
|
++ConstString PlatformRemoteWASMServer::GetPluginNameStatic() {
|
|
|
++ static ConstString g_name("remote-wasm-server");
|
|
|
++ return g_name;
|
|
|
++}
|
|
|
++
|
|
|
++ConstString PlatformRemoteWASMServer::GetPluginName() {
|
|
|
++ return GetPluginNameStatic();
|
|
|
++}
|
|
|
++
|
|
|
++const char *PlatformRemoteWASMServer::GetDescriptionStatic() {
|
|
|
++ return "A platform that uses the GDB remote protocol as the communication "
|
|
|
++ "transport for Wasm Runtime";
|
|
|
++}
|
|
|
++
|
|
|
++size_t PlatformRemoteWASMServer::ConnectToWaitingProcesses(Debugger &debugger,
|
|
|
++ Status &error) {
|
|
|
++ std::vector<std::string> connection_urls;
|
|
|
++ GetPendingGdbServerList(connection_urls);
|
|
|
++
|
|
|
++ for (size_t i = 0; i < connection_urls.size(); ++i) {
|
|
|
++ ConnectProcess(connection_urls[i].c_str(), "wasm", debugger, nullptr, error);
|
|
|
++ if (error.Fail())
|
|
|
++ return i; // We already connected to i process succsessfully
|
|
|
++ }
|
|
|
++ return connection_urls.size();
|
|
|
++}
|
|
|
++
|
|
|
++bool PlatformRemoteWASMServer::GetSupportedArchitectureAtIndex(uint32_t idx,
|
|
|
++ ArchSpec &arch) {
|
|
|
++ ArchSpec remote_arch = m_gdb_client.GetSystemArchitecture();
|
|
|
++ if (idx == 0) {
|
|
|
++ arch = remote_arch;
|
|
|
++ return arch.IsValid();
|
|
|
++ } else if (idx == 1 && remote_arch.IsValid() &&
|
|
|
++ remote_arch.GetTriple().getOS() == llvm::Triple::WASI) {
|
|
|
++ return arch.IsValid();
|
|
|
++ }
|
|
|
++ return false;
|
|
|
++}
|
|
|
++
|
|
|
++/// Default Constructor
|
|
|
++PlatformRemoteWASMServer::PlatformRemoteWASMServer()
|
|
|
++ : PlatformRemoteGDBServer()
|
|
|
++ {
|
|
|
++ }
|
|
|
+\ No newline at end of file
|
|
|
+diff --git a/lldb/source/Plugins/Platform/wasm-remote/PlatformRemoteWasmServer.h b/lldb/source/Plugins/Platform/wasm-remote/PlatformRemoteWasmServer.h
|
|
|
+new file mode 100644
|
|
|
+index 000000000000..f306a79d3f4f
|
|
|
+--- /dev/null
|
|
|
++++ b/lldb/source/Plugins/Platform/wasm-remote/PlatformRemoteWasmServer.h
|
|
|
+@@ -0,0 +1,37 @@
|
|
|
++#ifndef LLDB_SOURCE_PLUGINS_PLATFORM_GDB_SERVER_PLATFORMREMOTEWASMSERVER_H
|
|
|
++#define LLDB_SOURCE_PLUGINS_PLATFORM_GDB_SERVER_PLATFORMREMOTEWASMSERVER_H
|
|
|
++
|
|
|
++#include "Plugins/Platform/gdb-server/PlatformRemoteGDBServer.h"
|
|
|
++#include "lldb/Target/Platform.h"
|
|
|
++
|
|
|
++namespace lldb_private {
|
|
|
++namespace platform_wasm_server {
|
|
|
++
|
|
|
++class PlatformRemoteWASMServer : public lldb_private::platform_gdb_server::PlatformRemoteGDBServer{
|
|
|
++
|
|
|
++public:
|
|
|
++ static void Initialize();
|
|
|
++
|
|
|
++ static void Terminate();
|
|
|
++
|
|
|
++ static lldb::PlatformSP CreateInstance(bool force, const ArchSpec *arch);
|
|
|
++
|
|
|
++ static ConstString GetPluginNameStatic();
|
|
|
++
|
|
|
++ static const char *GetDescriptionStatic();
|
|
|
++
|
|
|
++ size_t ConnectToWaitingProcesses(lldb_private::Debugger &debugger,
|
|
|
++ lldb_private::Status &error) override;
|
|
|
++
|
|
|
++ bool GetSupportedArchitectureAtIndex(uint32_t idx, ArchSpec &arch) override;
|
|
|
++
|
|
|
++ ConstString GetPluginName() override;
|
|
|
++
|
|
|
++ PlatformRemoteWASMServer();
|
|
|
++
|
|
|
++};
|
|
|
++
|
|
|
++} // namespace platform_wasm_server
|
|
|
++} // namespace lldb_private
|
|
|
++
|
|
|
++#endif
|
|
|
+\ No newline at end of file
|
|
|
+diff --git a/lldb/source/Plugins/Plugins.def.in b/lldb/source/Plugins/Plugins.def.in
|
|
|
+index bf54598fb2f3..b0bd7b9965fe 100644
|
|
|
+--- a/lldb/source/Plugins/Plugins.def.in
|
|
|
++++ b/lldb/source/Plugins/Plugins.def.in
|
|
|
+@@ -31,6 +31,7 @@
|
|
|
+
|
|
|
+ @LLDB_ENUM_PLUGINS@
|
|
|
+ @LLDB_PROCESS_WINDOWS_PLUGIN@
|
|
|
++@LLDB_PROCESS_WASM_PLUGIN@
|
|
|
+ @LLDB_PROCESS_GDB_PLUGIN@
|
|
|
+
|
|
|
+ #undef LLDB_PLUGIN
|
|
|
+diff --git a/lldb/source/Plugins/Process/CMakeLists.txt b/lldb/source/Plugins/Process/CMakeLists.txt
|
|
|
+index bea5bac9eb21..7a0855e02ca2 100644
|
|
|
+--- a/lldb/source/Plugins/Process/CMakeLists.txt
|
|
|
++++ b/lldb/source/Plugins/Process/CMakeLists.txt
|
|
|
+@@ -18,3 +18,4 @@ add_subdirectory(Utility)
|
|
|
+ add_subdirectory(elf-core)
|
|
|
+ add_subdirectory(mach-core)
|
|
|
+ add_subdirectory(minidump)
|
|
|
++add_subdirectory(wasm)
|
|
|
+diff --git a/lldb/source/Plugins/Process/elf-core/ProcessElfCore.cpp b/lldb/source/Plugins/Process/elf-core/ProcessElfCore.cpp
|
|
|
+index 12bc7390c729..707ab85e5615 100644
|
|
|
+--- a/lldb/source/Plugins/Process/elf-core/ProcessElfCore.cpp
|
|
|
++++ b/lldb/source/Plugins/Process/elf-core/ProcessElfCore.cpp
|
|
|
+@@ -285,7 +285,7 @@ bool ProcessElfCore::IsAlive() { return true; }
|
|
|
+
|
|
|
+ // Process Memory
|
|
|
+ size_t ProcessElfCore::ReadMemory(lldb::addr_t addr, void *buf, size_t size,
|
|
|
+- Status &error) {
|
|
|
++ Status &error, ExecutionContext *exe_ctx) {
|
|
|
+ // Don't allow the caching that lldb_private::Process::ReadMemory does since
|
|
|
+ // in core files we have it all cached our our core file anyway.
|
|
|
+ return DoReadMemory(addr, buf, size, error);
|
|
|
+diff --git a/lldb/source/Plugins/Process/elf-core/ProcessElfCore.h b/lldb/source/Plugins/Process/elf-core/ProcessElfCore.h
|
|
|
+index d8e3cc9ae3e1..f0bf9c4d3b00 100644
|
|
|
+--- a/lldb/source/Plugins/Process/elf-core/ProcessElfCore.h
|
|
|
++++ b/lldb/source/Plugins/Process/elf-core/ProcessElfCore.h
|
|
|
+@@ -84,7 +84,8 @@ public:
|
|
|
+
|
|
|
+ // Process Memory
|
|
|
+ size_t ReadMemory(lldb::addr_t addr, void *buf, size_t size,
|
|
|
+- lldb_private::Status &error) override;
|
|
|
++ lldb_private::Status &error,
|
|
|
++ lldb_private::ExecutionContext *exe_ctx = nullptr) override;
|
|
|
+
|
|
|
+ size_t DoReadMemory(lldb::addr_t addr, void *buf, size_t size,
|
|
|
+ lldb_private::Status &error) override;
|
|
|
+diff --git a/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp b/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp
|
|
|
+index 6914b37348ea..bb8a056049f3 100644
|
|
|
+--- a/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp
|
|
|
++++ b/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp
|
|
|
+@@ -334,6 +334,11 @@ ConstString ProcessGDBRemote::GetPluginName() { return GetPluginNameStatic(); }
|
|
|
+
|
|
|
+ uint32_t ProcessGDBRemote::GetPluginVersion() { return 1; }
|
|
|
+
|
|
|
++std::shared_ptr<ThreadGDBRemote>
|
|
|
++ProcessGDBRemote::CreateThread(lldb::tid_t tid) {
|
|
|
++ return std::make_shared<ThreadGDBRemote>(*this, tid);
|
|
|
++}
|
|
|
++
|
|
|
+ bool ProcessGDBRemote::ParsePythonTargetDefinition(
|
|
|
+ const FileSpec &target_definition_fspec) {
|
|
|
+ ScriptInterpreter *interpreter =
|
|
|
+@@ -1626,7 +1631,7 @@ bool ProcessGDBRemote::DoUpdateThreadList(ThreadList &old_thread_list,
|
|
|
+ ThreadSP thread_sp(
|
|
|
+ old_thread_list_copy.RemoveThreadByProtocolID(tid, false));
|
|
|
+ if (!thread_sp) {
|
|
|
+- thread_sp = std::make_shared<ThreadGDBRemote>(*this, tid);
|
|
|
++ thread_sp = CreateThread(tid);
|
|
|
+ LLDB_LOGV(log, "Making new thread: {0} for thread ID: {1:x}.",
|
|
|
+ thread_sp.get(), thread_sp->GetID());
|
|
|
+ } else {
|
|
|
+@@ -1742,7 +1747,7 @@ ThreadSP ProcessGDBRemote::SetThreadStopInfo(
|
|
|
+
|
|
|
+ if (!thread_sp) {
|
|
|
+ // Create the thread if we need to
|
|
|
+- thread_sp = std::make_shared<ThreadGDBRemote>(*this, tid);
|
|
|
++ thread_sp = CreateThread(tid);
|
|
|
+ m_thread_list_real.AddThread(thread_sp);
|
|
|
+ }
|
|
|
+ }
|
|
|
+diff --git a/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.h b/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.h
|
|
|
+index fe04cdddd0f5..e4a14c64579a 100644
|
|
|
+--- a/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.h
|
|
|
++++ b/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.h
|
|
|
+@@ -237,6 +237,8 @@ protected:
|
|
|
+
|
|
|
+ bool SupportsMemoryTagging() override;
|
|
|
+
|
|
|
++ virtual std::shared_ptr<ThreadGDBRemote> CreateThread(lldb::tid_t tid);
|
|
|
++
|
|
|
+ /// Broadcaster event bits definitions.
|
|
|
+ enum {
|
|
|
+ eBroadcastBitAsyncContinue = (1 << 0),
|
|
|
+diff --git a/lldb/source/Plugins/Process/mach-core/ProcessMachCore.cpp b/lldb/source/Plugins/Process/mach-core/ProcessMachCore.cpp
|
|
|
+index 84548edb5caa..0ae6f7e4a177 100644
|
|
|
+--- a/lldb/source/Plugins/Process/mach-core/ProcessMachCore.cpp
|
|
|
++++ b/lldb/source/Plugins/Process/mach-core/ProcessMachCore.cpp
|
|
|
+@@ -596,7 +596,7 @@ bool ProcessMachCore::WarnBeforeDetach() const { return false; }
|
|
|
+
|
|
|
+ // Process Memory
|
|
|
+ size_t ProcessMachCore::ReadMemory(addr_t addr, void *buf, size_t size,
|
|
|
+- Status &error) {
|
|
|
++ Status &error, ExecutionContext *exe_ctx) {
|
|
|
+ // Don't allow the caching that lldb_private::Process::ReadMemory does since
|
|
|
+ // in core files we have it all cached our our core file anyway.
|
|
|
+ return DoReadMemory(addr, buf, size, error);
|
|
|
+diff --git a/lldb/source/Plugins/Process/mach-core/ProcessMachCore.h b/lldb/source/Plugins/Process/mach-core/ProcessMachCore.h
|
|
|
+index db77e96f1072..1c930896c743 100644
|
|
|
+--- a/lldb/source/Plugins/Process/mach-core/ProcessMachCore.h
|
|
|
++++ b/lldb/source/Plugins/Process/mach-core/ProcessMachCore.h
|
|
|
+@@ -65,7 +65,8 @@ public:
|
|
|
+
|
|
|
+ // Process Memory
|
|
|
+ size_t ReadMemory(lldb::addr_t addr, void *buf, size_t size,
|
|
|
+- lldb_private::Status &error) override;
|
|
|
++ lldb_private::Status &error,
|
|
|
++ lldb_private::ExecutionContext *exe_ctx = nullptr) override;
|
|
|
+
|
|
|
+ size_t DoReadMemory(lldb::addr_t addr, void *buf, size_t size,
|
|
|
+ lldb_private::Status &error) override;
|
|
|
+diff --git a/lldb/source/Plugins/Process/minidump/ProcessMinidump.cpp b/lldb/source/Plugins/Process/minidump/ProcessMinidump.cpp
|
|
|
+index 385557422758..d8bb21581086 100644
|
|
|
+--- a/lldb/source/Plugins/Process/minidump/ProcessMinidump.cpp
|
|
|
++++ b/lldb/source/Plugins/Process/minidump/ProcessMinidump.cpp
|
|
|
+@@ -374,7 +374,7 @@ bool ProcessMinidump::IsAlive() { return true; }
|
|
|
+ bool ProcessMinidump::WarnBeforeDetach() const { return false; }
|
|
|
+
|
|
|
+ size_t ProcessMinidump::ReadMemory(lldb::addr_t addr, void *buf, size_t size,
|
|
|
+- Status &error) {
|
|
|
++ Status &error, ExecutionContext *exe_ctx) {
|
|
|
+ // Don't allow the caching that lldb_private::Process::ReadMemory does since
|
|
|
+ // we have it all cached in our dump file anyway.
|
|
|
+ return DoReadMemory(addr, buf, size, error);
|
|
|
+diff --git a/lldb/source/Plugins/Process/minidump/ProcessMinidump.h b/lldb/source/Plugins/Process/minidump/ProcessMinidump.h
|
|
|
+index 27b0da0047a5..e94ecab430c1 100644
|
|
|
+--- a/lldb/source/Plugins/Process/minidump/ProcessMinidump.h
|
|
|
++++ b/lldb/source/Plugins/Process/minidump/ProcessMinidump.h
|
|
|
+@@ -69,8 +69,8 @@ public:
|
|
|
+
|
|
|
+ bool WarnBeforeDetach() const override;
|
|
|
+
|
|
|
+- size_t ReadMemory(lldb::addr_t addr, void *buf, size_t size,
|
|
|
+- Status &error) override;
|
|
|
++ size_t ReadMemory(lldb::addr_t addr, void *buf, size_t size, Status &error,
|
|
|
++ ExecutionContext *exe_ctx = nullptr) override;
|
|
|
+
|
|
|
+ size_t DoReadMemory(lldb::addr_t addr, void *buf, size_t size,
|
|
|
+ Status &error) override;
|
|
|
+diff --git a/lldb/source/Plugins/Process/wasm/CMakeLists.txt b/lldb/source/Plugins/Process/wasm/CMakeLists.txt
|
|
|
+new file mode 100644
|
|
|
+index 000000000000..61efb933fa62
|
|
|
+--- /dev/null
|
|
|
++++ b/lldb/source/Plugins/Process/wasm/CMakeLists.txt
|
|
|
+@@ -0,0 +1,12 @@
|
|
|
++
|
|
|
++add_lldb_library(lldbPluginProcessWasm PLUGIN
|
|
|
++ ProcessWasm.cpp
|
|
|
++ ThreadWasm.cpp
|
|
|
++ UnwindWasm.cpp
|
|
|
++
|
|
|
++ LINK_LIBS
|
|
|
++ lldbCore
|
|
|
++ ${LLDB_PLUGINS}
|
|
|
++ LINK_COMPONENTS
|
|
|
++ Support
|
|
|
++ )
|
|
|
+diff --git a/lldb/source/Plugins/Process/wasm/ProcessWasm.cpp b/lldb/source/Plugins/Process/wasm/ProcessWasm.cpp
|
|
|
+new file mode 100644
|
|
|
+index 000000000000..b8ffcac12df2
|
|
|
+--- /dev/null
|
|
|
++++ b/lldb/source/Plugins/Process/wasm/ProcessWasm.cpp
|
|
|
+@@ -0,0 +1,256 @@
|
|
|
++//===-- ProcessWasm.cpp ---------------------------------------------------===//
|
|
|
++//
|
|
|
++// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
|
|
++// See https://llvm.org/LICENSE.txt for license information.
|
|
|
++// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
|
|
++//
|
|
|
++//===----------------------------------------------------------------------===//
|
|
|
++
|
|
|
++#include "ProcessWasm.h"
|
|
|
++#include "ThreadWasm.h"
|
|
|
++#include "lldb/Core/Module.h"
|
|
|
++#include "lldb/Core/PluginManager.h"
|
|
|
++#include "lldb/Utility/DataBufferHeap.h"
|
|
|
++
|
|
|
++using namespace lldb;
|
|
|
++using namespace lldb_private;
|
|
|
++using namespace lldb_private::process_gdb_remote;
|
|
|
++using namespace lldb_private::wasm;
|
|
|
++
|
|
|
++LLDB_PLUGIN_DEFINE(ProcessWasm)
|
|
|
++
|
|
|
++// ProcessGDBRemote constructor
|
|
|
++ProcessWasm::ProcessWasm(lldb::TargetSP target_sp, ListenerSP listener_sp)
|
|
|
++ : ProcessGDBRemote(target_sp, listener_sp) {}
|
|
|
++
|
|
|
++void ProcessWasm::Initialize() {
|
|
|
++ static llvm::once_flag g_once_flag;
|
|
|
++
|
|
|
++ llvm::call_once(g_once_flag, []() {
|
|
|
++ PluginManager::RegisterPlugin(GetPluginNameStatic(),
|
|
|
++ GetPluginDescriptionStatic(), CreateInstance,
|
|
|
++ DebuggerInitialize);
|
|
|
++ });
|
|
|
++}
|
|
|
++
|
|
|
++void ProcessWasm::DebuggerInitialize(Debugger &debugger) {
|
|
|
++ ProcessGDBRemote::DebuggerInitialize(debugger);
|
|
|
++}
|
|
|
++
|
|
|
++// PluginInterface
|
|
|
++ConstString ProcessWasm::GetPluginName() { return GetPluginNameStatic(); }
|
|
|
++
|
|
|
++uint32_t ProcessWasm::GetPluginVersion() { return 1; }
|
|
|
++
|
|
|
++ConstString ProcessWasm::GetPluginNameStatic() {
|
|
|
++ static ConstString g_name("wasm");
|
|
|
++ return g_name;
|
|
|
++}
|
|
|
++
|
|
|
++const char *ProcessWasm::GetPluginDescriptionStatic() {
|
|
|
++ return "GDB Remote protocol based WebAssembly debugging plug-in.";
|
|
|
++}
|
|
|
++
|
|
|
++void ProcessWasm::Terminate() {
|
|
|
++ PluginManager::UnregisterPlugin(ProcessWasm::CreateInstance);
|
|
|
++}
|
|
|
++
|
|
|
++lldb::ProcessSP ProcessWasm::CreateInstance(lldb::TargetSP target_sp,
|
|
|
++ ListenerSP listener_sp,
|
|
|
++ const FileSpec *crash_file_path,
|
|
|
++ bool can_connect) {
|
|
|
++ lldb::ProcessSP process_sp;
|
|
|
++ if (crash_file_path == nullptr)
|
|
|
++ process_sp = std::make_shared<ProcessWasm>(target_sp, listener_sp);
|
|
|
++ return process_sp;
|
|
|
++}
|
|
|
++
|
|
|
++bool ProcessWasm::CanDebug(lldb::TargetSP target_sp,
|
|
|
++ bool plugin_specified_by_name) {
|
|
|
++ if (plugin_specified_by_name)
|
|
|
++ return true;
|
|
|
++
|
|
|
++ Module *exe_module = target_sp->GetExecutableModulePointer();
|
|
|
++ if (exe_module) {
|
|
|
++ ObjectFile *exe_objfile = exe_module->GetObjectFile();
|
|
|
++ return exe_objfile->GetArchitecture().GetMachine() == llvm::Triple::wasm32;
|
|
|
++ }
|
|
|
++ // However, if there is no wasm module, we return false, otherwise,
|
|
|
++ // we might use ProcessWasm to attach gdb remote.
|
|
|
++ return false;
|
|
|
++}
|
|
|
++
|
|
|
++
|
|
|
++
|
|
|
++std::shared_ptr<ThreadGDBRemote> ProcessWasm::CreateThread(lldb::tid_t tid) {
|
|
|
++ return std::make_shared<ThreadWasm>(*this, tid);
|
|
|
++}
|
|
|
++
|
|
|
++size_t ProcessWasm::ReadMemory(lldb::addr_t vm_addr, void *buf, size_t size,
|
|
|
++ Status &error, ExecutionContext *exe_ctx) {
|
|
|
++ wasm_addr_t wasm_addr(vm_addr);
|
|
|
++ size_t nread = 0;
|
|
|
++
|
|
|
++ switch (wasm_addr.GetType()) {
|
|
|
++ case WasmAddressType::Memory:
|
|
|
++ case WasmAddressType::Object:
|
|
|
++ return ProcessGDBRemote::ReadMemory(vm_addr, buf, size, error);
|
|
|
++ case WasmAddressType::Invalid:
|
|
|
++ default:
|
|
|
++ error.SetErrorStringWithFormat(
|
|
|
++ "Wasm read failed for invalid address 0x%" PRIx64, vm_addr);
|
|
|
++ return 0;
|
|
|
++ }
|
|
|
++}
|
|
|
++
|
|
|
++size_t ProcessWasm::WasmReadMemory(uint32_t wasm_module_id, lldb::addr_t addr,
|
|
|
++ void *buf, size_t buffer_size) {
|
|
|
++ char packet[64];
|
|
|
++ int packet_len =
|
|
|
++ ::snprintf(packet, sizeof(packet), "qWasmMem:%d;%" PRIx64 ";%" PRIx64,
|
|
|
++ wasm_module_id, static_cast<uint64_t>(addr),
|
|
|
++ static_cast<uint64_t>(buffer_size));
|
|
|
++ assert(packet_len + 1 < (int)sizeof(packet));
|
|
|
++ UNUSED_IF_ASSERT_DISABLED(packet_len);
|
|
|
++ StringExtractorGDBRemote response;
|
|
|
++ if (m_gdb_comm.SendPacketAndWaitForResponse(packet, response, GetInterruptTimeout()) ==
|
|
|
++ GDBRemoteCommunication::PacketResult::Success) {
|
|
|
++ if (response.IsNormalResponse()) {
|
|
|
++ return response.GetHexBytes(llvm::MutableArrayRef<uint8_t>(
|
|
|
++ static_cast<uint8_t *>(buf), buffer_size),
|
|
|
++ '\xdd');
|
|
|
++ }
|
|
|
++ }
|
|
|
++ return 0;
|
|
|
++}
|
|
|
++
|
|
|
++size_t ProcessWasm::WasmReadData(uint32_t wasm_module_id, lldb::addr_t addr,
|
|
|
++ void *buf, size_t buffer_size) {
|
|
|
++ char packet[64];
|
|
|
++ int packet_len =
|
|
|
++ ::snprintf(packet, sizeof(packet), "qWasmData:%d;%" PRIx64 ";%" PRIx64,
|
|
|
++ wasm_module_id, static_cast<uint64_t>(addr),
|
|
|
++ static_cast<uint64_t>(buffer_size));
|
|
|
++ assert(packet_len + 1 < (int)sizeof(packet));
|
|
|
++ UNUSED_IF_ASSERT_DISABLED(packet_len);
|
|
|
++ StringExtractorGDBRemote response;
|
|
|
++ if (m_gdb_comm.SendPacketAndWaitForResponse(packet, response, GetInterruptTimeout()) ==
|
|
|
++ GDBRemoteCommunication::PacketResult::Success) {
|
|
|
++ if (response.IsNormalResponse()) {
|
|
|
++ return response.GetHexBytes(llvm::MutableArrayRef<uint8_t>(
|
|
|
++ static_cast<uint8_t *>(buf), buffer_size),
|
|
|
++ '\xdd');
|
|
|
++ }
|
|
|
++ }
|
|
|
++ return 0;
|
|
|
++}
|
|
|
++
|
|
|
++bool ProcessWasm::GetWasmLocal(int frame_index, int index, void *buf,
|
|
|
++ size_t buffer_size, size_t &size) {
|
|
|
++ StreamString packet;
|
|
|
++ packet.Printf("qWasmLocal:");
|
|
|
++ packet.Printf("%d;%d", frame_index, index);
|
|
|
++ StringExtractorGDBRemote response;
|
|
|
++ if (m_gdb_comm.SendPacketAndWaitForResponse(packet.GetString(), response) !=
|
|
|
++ GDBRemoteCommunication::PacketResult::Success) {
|
|
|
++ return false;
|
|
|
++ }
|
|
|
++
|
|
|
++ if (!response.IsNormalResponse()) {
|
|
|
++ return false;
|
|
|
++ }
|
|
|
++
|
|
|
++ DataBufferSP buffer_sp(
|
|
|
++ new DataBufferHeap(response.GetStringRef().size() / 2, 0));
|
|
|
++ response.GetHexBytes(buffer_sp->GetData(), '\xcc');
|
|
|
++ size = buffer_sp->GetByteSize();
|
|
|
++ if (size <= buffer_size) {
|
|
|
++ memcpy(buf, buffer_sp->GetBytes(), size);
|
|
|
++ return true;
|
|
|
++ }
|
|
|
++
|
|
|
++ return false;
|
|
|
++}
|
|
|
++
|
|
|
++bool ProcessWasm::GetWasmGlobal(int frame_index, int index, void *buf,
|
|
|
++ size_t buffer_size, size_t &size) {
|
|
|
++ StreamString packet;
|
|
|
++ packet.PutCString("qWasmGlobal:");
|
|
|
++ packet.Printf("%d;%d", frame_index, index);
|
|
|
++ StringExtractorGDBRemote response;
|
|
|
++ if (m_gdb_comm.SendPacketAndWaitForResponse(packet.GetString(), response) !=
|
|
|
++ GDBRemoteCommunication::PacketResult::Success) {
|
|
|
++ return false;
|
|
|
++ }
|
|
|
++
|
|
|
++ if (!response.IsNormalResponse()) {
|
|
|
++ return false;
|
|
|
++ }
|
|
|
++
|
|
|
++ DataBufferSP buffer_sp(
|
|
|
++ new DataBufferHeap(response.GetStringRef().size() / 2, 0));
|
|
|
++ response.GetHexBytes(buffer_sp->GetData(), '\xcc');
|
|
|
++ size = buffer_sp->GetByteSize();
|
|
|
++ if (size <= buffer_size) {
|
|
|
++ memcpy(buf, buffer_sp->GetBytes(), size);
|
|
|
++ return true;
|
|
|
++ }
|
|
|
++
|
|
|
++ return false;
|
|
|
++}
|
|
|
++
|
|
|
++bool ProcessWasm::GetWasmStackValue(int frame_index, int index, void *buf,
|
|
|
++ size_t buffer_size, size_t &size) {
|
|
|
++ StreamString packet;
|
|
|
++ packet.PutCString("qWasmStackValue:");
|
|
|
++ packet.Printf("%d;%d", frame_index, index);
|
|
|
++ StringExtractorGDBRemote response;
|
|
|
++ if (m_gdb_comm.SendPacketAndWaitForResponse(packet.GetString(), response) !=
|
|
|
++ GDBRemoteCommunication::PacketResult::Success) {
|
|
|
++ return false;
|
|
|
++ }
|
|
|
++
|
|
|
++ if (!response.IsNormalResponse()) {
|
|
|
++ return false;
|
|
|
++ }
|
|
|
++
|
|
|
++ DataBufferSP buffer_sp(
|
|
|
++ new DataBufferHeap(response.GetStringRef().size() / 2, 0));
|
|
|
++ response.GetHexBytes(buffer_sp->GetData(), '\xcc');
|
|
|
++ size = buffer_sp->GetByteSize();
|
|
|
++ if (size <= buffer_size) {
|
|
|
++ memcpy(buf, buffer_sp->GetBytes(), size);
|
|
|
++ return true;
|
|
|
++ }
|
|
|
++
|
|
|
++ return false;
|
|
|
++}
|
|
|
++
|
|
|
++bool ProcessWasm::GetWasmCallStack(lldb::tid_t tid,
|
|
|
++ std::vector<lldb::addr_t> &call_stack_pcs) {
|
|
|
++ call_stack_pcs.clear();
|
|
|
++ StreamString packet;
|
|
|
++ packet.Printf("qWasmCallStack:");
|
|
|
++ packet.Printf("%lx", tid);
|
|
|
++ StringExtractorGDBRemote response;
|
|
|
++ if (m_gdb_comm.SendPacketAndWaitForResponse(packet.GetString(), response) !=
|
|
|
++ GDBRemoteCommunication::PacketResult::Success) {
|
|
|
++ return false;
|
|
|
++ }
|
|
|
++
|
|
|
++ if (!response.IsNormalResponse()) {
|
|
|
++ return false;
|
|
|
++ }
|
|
|
++
|
|
|
++ addr_t buf[1024 / sizeof(addr_t)];
|
|
|
++ size_t bytes = response.GetHexBytes(
|
|
|
++ llvm::MutableArrayRef<uint8_t>((uint8_t *)buf, sizeof(buf)), '\xdd');
|
|
|
++ if (bytes == 0) {
|
|
|
++ return false;
|
|
|
++ }
|
|
|
++
|
|
|
++ for (size_t i = 0; i < bytes / sizeof(addr_t); i++) {
|
|
|
++ call_stack_pcs.push_back(buf[i]);
|
|
|
++ }
|
|
|
++ return true;
|
|
|
++}
|
|
|
+diff --git a/lldb/source/Plugins/Process/wasm/ProcessWasm.h b/lldb/source/Plugins/Process/wasm/ProcessWasm.h
|
|
|
+new file mode 100644
|
|
|
+index 000000000000..d3aece7a6554
|
|
|
+--- /dev/null
|
|
|
++++ b/lldb/source/Plugins/Process/wasm/ProcessWasm.h
|
|
|
+@@ -0,0 +1,128 @@
|
|
|
++//===-- ProcessWasm.h -------------------------------------------*- C++ -*-===//
|
|
|
++//
|
|
|
++// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
|
|
++// See https://llvm.org/LICENSE.txt for license information.
|
|
|
++// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
|
|
++//
|
|
|
++//===----------------------------------------------------------------------===//
|
|
|
++
|
|
|
++#ifndef LLDB_SOURCE_PLUGINS_PROCESS_WASM_PROCESSWASM_H
|
|
|
++#define LLDB_SOURCE_PLUGINS_PROCESS_WASM_PROCESSWASM_H
|
|
|
++
|
|
|
++#include "Plugins/Process/gdb-remote/ProcessGDBRemote.h"
|
|
|
++#include "lldb/Target/RegisterContext.h"
|
|
|
++
|
|
|
++namespace lldb_private {
|
|
|
++namespace wasm {
|
|
|
++
|
|
|
++// Each WebAssembly module has separated address spaces for Code and Memory.
|
|
|
++// A WebAssembly module also has a Data section which, when the module is
|
|
|
++// loaded, gets mapped into a region in the module Memory.
|
|
|
++// For the purpose of debugging, we can represent all these separated 32-bit
|
|
|
++// address spaces with a single virtual 64-bit address space.
|
|
|
++//
|
|
|
++// Struct wasm_addr_t provides this encoding using bitfields
|
|
|
++//
|
|
|
++enum WasmAddressType {
|
|
|
++ Memory = 0x00,
|
|
|
++ Object = 0x01,
|
|
|
++ Invalid = 0x03
|
|
|
++};
|
|
|
++struct wasm_addr_t {
|
|
|
++ uint64_t offset : 32;
|
|
|
++ uint64_t module_id : 30;
|
|
|
++ uint64_t type : 2;
|
|
|
++
|
|
|
++ wasm_addr_t(lldb::addr_t addr)
|
|
|
++ : type(addr >> 62), module_id((addr & 0x00ffffff00000000) >> 32),
|
|
|
++ offset(addr & 0x00000000ffffffff) {}
|
|
|
++
|
|
|
++ wasm_addr_t(WasmAddressType type_, uint32_t module_id_, uint32_t offset_)
|
|
|
++ : type(type_), module_id(module_id_), offset(offset_) {}
|
|
|
++
|
|
|
++ WasmAddressType GetType() { return static_cast<WasmAddressType>(type); }
|
|
|
++ operator lldb::addr_t() { return *(uint64_t *)this; }
|
|
|
++};
|
|
|
++
|
|
|
++/// ProcessWasm provides the access to the Wasm program state
|
|
|
++/// retrieved from the Wasm engine.
|
|
|
++class ProcessWasm : public process_gdb_remote::ProcessGDBRemote {
|
|
|
++public:
|
|
|
++ ProcessWasm(lldb::TargetSP target_sp, lldb::ListenerSP listener_sp);
|
|
|
++ ~ProcessWasm() override = default;
|
|
|
++
|
|
|
++ static lldb::ProcessSP CreateInstance(lldb::TargetSP target_sp,
|
|
|
++ lldb::ListenerSP listener_sp,
|
|
|
++ const FileSpec *crash_file_path,
|
|
|
++ bool can_connect);
|
|
|
++
|
|
|
++ static void Initialize();
|
|
|
++ static void DebuggerInitialize(Debugger &debugger);
|
|
|
++ static void Terminate();
|
|
|
++ static ConstString GetPluginNameStatic();
|
|
|
++ static const char *GetPluginDescriptionStatic();
|
|
|
++
|
|
|
++ /// PluginInterface protocol.
|
|
|
++ /// \{
|
|
|
++ ConstString GetPluginName() override;
|
|
|
++ uint32_t GetPluginVersion() override;
|
|
|
++ /// \}
|
|
|
++
|
|
|
++ /// Process protocol.
|
|
|
++ /// \{
|
|
|
++ size_t ReadMemory(lldb::addr_t vm_addr, void *buf, size_t size, Status &error,
|
|
|
++ ExecutionContext *exe_ctx = nullptr) override;
|
|
|
++ /// \}
|
|
|
++
|
|
|
++ /// Query the value of a WebAssembly local variable from the WebAssembly
|
|
|
++ /// remote process.
|
|
|
++ bool GetWasmLocal(int frame_index, int index, void *buf, size_t buffer_size,
|
|
|
++ size_t &size);
|
|
|
++
|
|
|
++ /// Query the value of a WebAssembly global variable from the WebAssembly
|
|
|
++ /// remote process.
|
|
|
++ bool GetWasmGlobal(int frame_index, int index, void *buf, size_t buffer_size,
|
|
|
++ size_t &size);
|
|
|
++
|
|
|
++ /// Query the value of an item in the WebAssembly operand stack from the
|
|
|
++ /// WebAssembly remote process.
|
|
|
++ bool GetWasmStackValue(int frame_index, int index, void *buf,
|
|
|
++ size_t buffer_size, size_t &size);
|
|
|
++
|
|
|
++ /// Read from the WebAssembly Memory space.
|
|
|
++ size_t WasmReadMemory(uint32_t wasm_module_id, lldb::addr_t addr, void *buf,
|
|
|
++ size_t buffer_size);
|
|
|
++
|
|
|
++ /// Read from the WebAssembly Data space.
|
|
|
++ size_t WasmReadData(uint32_t wasm_module_id, lldb::addr_t addr, void *buf,
|
|
|
++ size_t buffer_size);
|
|
|
++
|
|
|
++ /// Retrieve the current call stack from the WebAssembly remote process.
|
|
|
++ bool GetWasmCallStack(lldb::tid_t tid,
|
|
|
++ std::vector<lldb::addr_t> &call_stack_pcs);
|
|
|
++
|
|
|
++ // Check if a given Process
|
|
|
++ bool CanDebug(lldb::TargetSP target_sp,
|
|
|
++ bool plugin_specified_by_name) override;
|
|
|
++
|
|
|
++protected:
|
|
|
++ /// ProcessGDBRemote protocol.
|
|
|
++ /// \{
|
|
|
++ std::shared_ptr<process_gdb_remote::ThreadGDBRemote>
|
|
|
++ CreateThread(lldb::tid_t tid) override;
|
|
|
++ /// \}
|
|
|
++
|
|
|
++private:
|
|
|
++ friend class UnwindWasm;
|
|
|
++ process_gdb_remote::GDBRemoteDynamicRegisterInfoSP &GetRegisterInfo() {
|
|
|
++ return m_register_info_sp;
|
|
|
++ }
|
|
|
++
|
|
|
++ ProcessWasm(const ProcessWasm &);
|
|
|
++ const ProcessWasm &operator=(const ProcessWasm &) = delete;
|
|
|
++};
|
|
|
++
|
|
|
++} // namespace wasm
|
|
|
++} // namespace lldb_private
|
|
|
++
|
|
|
++#endif // LLDB_SOURCE_PLUGINS_PROCESS_WASM_PROCESSWASM_H
|
|
|
+diff --git a/lldb/source/Plugins/Process/wasm/ThreadWasm.cpp b/lldb/source/Plugins/Process/wasm/ThreadWasm.cpp
|
|
|
+new file mode 100644
|
|
|
+index 000000000000..fa02073e7a52
|
|
|
+--- /dev/null
|
|
|
++++ b/lldb/source/Plugins/Process/wasm/ThreadWasm.cpp
|
|
|
+@@ -0,0 +1,35 @@
|
|
|
++//===-- ThreadWasm.cpp ----------------------------------------------------===//
|
|
|
++//
|
|
|
++// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
|
|
++// See https://llvm.org/LICENSE.txt for license information.
|
|
|
++// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
|
|
++//
|
|
|
++//===----------------------------------------------------------------------===//
|
|
|
++
|
|
|
++#include "ThreadWasm.h"
|
|
|
++
|
|
|
++#include "ProcessWasm.h"
|
|
|
++#include "UnwindWasm.h"
|
|
|
++#include "lldb/Target/Target.h"
|
|
|
++
|
|
|
++using namespace lldb;
|
|
|
++using namespace lldb_private;
|
|
|
++using namespace lldb_private::wasm;
|
|
|
++
|
|
|
++Unwind &ThreadWasm::GetUnwinder() {
|
|
|
++ if (!m_unwinder_up) {
|
|
|
++ assert(CalculateTarget()->GetArchitecture().GetMachine() ==
|
|
|
++ llvm::Triple::wasm32);
|
|
|
++ m_unwinder_up.reset(new wasm::UnwindWasm(*this));
|
|
|
++ }
|
|
|
++ return *m_unwinder_up;
|
|
|
++}
|
|
|
++
|
|
|
++bool ThreadWasm::GetWasmCallStack(std::vector<lldb::addr_t> &call_stack_pcs) {
|
|
|
++ ProcessSP process_sp(GetProcess());
|
|
|
++ if (process_sp) {
|
|
|
++ ProcessWasm *wasm_process = static_cast<ProcessWasm *>(process_sp.get());
|
|
|
++ return wasm_process->GetWasmCallStack(GetID(), call_stack_pcs);
|
|
|
++ }
|
|
|
++ return false;
|
|
|
++}
|
|
|
+diff --git a/lldb/source/Plugins/Process/wasm/ThreadWasm.h b/lldb/source/Plugins/Process/wasm/ThreadWasm.h
|
|
|
+new file mode 100644
|
|
|
+index 000000000000..0a33c07de994
|
|
|
+--- /dev/null
|
|
|
++++ b/lldb/source/Plugins/Process/wasm/ThreadWasm.h
|
|
|
+@@ -0,0 +1,41 @@
|
|
|
++//===-- ThreadWasm.h --------------------------------------------*- C++ -*-===//
|
|
|
++//
|
|
|
++// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
|
|
++// See https://llvm.org/LICENSE.txt for license information.
|
|
|
++// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
|
|
++//
|
|
|
++//===----------------------------------------------------------------------===//
|
|
|
++
|
|
|
++#ifndef LLDB_SOURCE_PLUGINS_PROCESS_WASM_THREADWASM_H
|
|
|
++#define LLDB_SOURCE_PLUGINS_PROCESS_WASM_THREADWASM_H
|
|
|
++
|
|
|
++#include "Plugins/Process/gdb-remote/ThreadGDBRemote.h"
|
|
|
++
|
|
|
++namespace lldb_private {
|
|
|
++namespace wasm {
|
|
|
++
|
|
|
++/// ProcessWasm provides the access to the Wasm program state
|
|
|
++/// retrieved from the Wasm engine.
|
|
|
++class ThreadWasm : public process_gdb_remote::ThreadGDBRemote {
|
|
|
++public:
|
|
|
++ ThreadWasm(Process &process, lldb::tid_t tid)
|
|
|
++ : process_gdb_remote::ThreadGDBRemote(process, tid) {}
|
|
|
++ ~ThreadWasm() override = default;
|
|
|
++
|
|
|
++ /// Retrieve the current call stack from the WebAssembly remote process.
|
|
|
++ bool GetWasmCallStack(std::vector<lldb::addr_t> &call_stack_pcs);
|
|
|
++
|
|
|
++protected:
|
|
|
++ /// Thread protocol.
|
|
|
++ /// \{
|
|
|
++ Unwind &GetUnwinder() override;
|
|
|
++ /// \}
|
|
|
++
|
|
|
++ ThreadWasm(const ThreadWasm &);
|
|
|
++ const ThreadWasm &operator=(const ThreadWasm &) = delete;
|
|
|
++};
|
|
|
++
|
|
|
++} // namespace wasm
|
|
|
++} // namespace lldb_private
|
|
|
++
|
|
|
++#endif // LLDB_SOURCE_PLUGINS_PROCESS_WASM_THREADWASM_H
|
|
|
+diff --git a/lldb/source/Plugins/Process/wasm/UnwindWasm.cpp b/lldb/source/Plugins/Process/wasm/UnwindWasm.cpp
|
|
|
+new file mode 100644
|
|
|
+index 000000000000..1a195cb9361a
|
|
|
+--- /dev/null
|
|
|
++++ b/lldb/source/Plugins/Process/wasm/UnwindWasm.cpp
|
|
|
+@@ -0,0 +1,74 @@
|
|
|
++//===-- UnwindWasm.cpp ----------------------------------------------------===//
|
|
|
++//
|
|
|
++// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
|
|
++// See https://llvm.org/LICENSE.txt for license information.
|
|
|
++// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
|
|
++//
|
|
|
++//===----------------------------------------------------------------------===//
|
|
|
++
|
|
|
++#include "UnwindWasm.h"
|
|
|
++#include "Plugins/Process/gdb-remote/ThreadGDBRemote.h"
|
|
|
++#include "Plugins/Process/wasm/ProcessWasm.h"
|
|
|
++#include "Plugins/Process/wasm/ThreadWasm.h"
|
|
|
++
|
|
|
++using namespace lldb;
|
|
|
++using namespace lldb_private;
|
|
|
++using namespace process_gdb_remote;
|
|
|
++using namespace wasm;
|
|
|
++
|
|
|
++class WasmGDBRemoteRegisterContext : public GDBRemoteRegisterContext {
|
|
|
++public:
|
|
|
++ WasmGDBRemoteRegisterContext(ThreadGDBRemote &thread,
|
|
|
++ uint32_t concrete_frame_idx,
|
|
|
++ GDBRemoteDynamicRegisterInfoSP ®_info_sp,
|
|
|
++ uint64_t pc)
|
|
|
++ : GDBRemoteRegisterContext(thread, concrete_frame_idx, reg_info_sp, false,
|
|
|
++ false) {
|
|
|
++ PrivateSetRegisterValue(0, pc);
|
|
|
++ }
|
|
|
++};
|
|
|
++
|
|
|
++lldb::RegisterContextSP
|
|
|
++UnwindWasm::DoCreateRegisterContextForFrame(lldb_private::StackFrame *frame) {
|
|
|
++ if (m_frames.size() <= frame->GetFrameIndex()) {
|
|
|
++ return lldb::RegisterContextSP();
|
|
|
++ }
|
|
|
++
|
|
|
++ ThreadSP thread = frame->GetThread();
|
|
|
++ ThreadGDBRemote *gdb_thread = static_cast<ThreadGDBRemote *>(thread.get());
|
|
|
++ ProcessWasm *wasm_process =
|
|
|
++ static_cast<ProcessWasm *>(thread->GetProcess().get());
|
|
|
++ std::shared_ptr<GDBRemoteRegisterContext> reg_ctx_sp =
|
|
|
++ std::make_shared<WasmGDBRemoteRegisterContext>(
|
|
|
++ *gdb_thread, frame->GetConcreteFrameIndex(),
|
|
|
++ wasm_process->GetRegisterInfo(), m_frames[frame->GetFrameIndex()]);
|
|
|
++ return reg_ctx_sp;
|
|
|
++}
|
|
|
++
|
|
|
++uint32_t UnwindWasm::DoGetFrameCount() {
|
|
|
++ if (!m_unwind_complete) {
|
|
|
++ m_unwind_complete = true;
|
|
|
++ m_frames.clear();
|
|
|
++
|
|
|
++ ThreadWasm &wasm_thread = static_cast<ThreadWasm &>(GetThread());
|
|
|
++ if (!wasm_thread.GetWasmCallStack(m_frames))
|
|
|
++ m_frames.clear();
|
|
|
++ }
|
|
|
++ return m_frames.size();
|
|
|
++}
|
|
|
++
|
|
|
++bool UnwindWasm::DoGetFrameInfoAtIndex(uint32_t frame_idx, lldb::addr_t &cfa,
|
|
|
++ lldb::addr_t &pc,
|
|
|
++ bool &behaves_like_zeroth_frame) {
|
|
|
++ if (m_frames.size() == 0) {
|
|
|
++ DoGetFrameCount();
|
|
|
++ }
|
|
|
++
|
|
|
++ if (frame_idx < m_frames.size()) {
|
|
|
++ behaves_like_zeroth_frame = (frame_idx == 0);
|
|
|
++ cfa = 0;
|
|
|
++ pc = m_frames[frame_idx];
|
|
|
++ return true;
|
|
|
++ }
|
|
|
++ return false;
|
|
|
++}
|
|
|
+\ No newline at end of file
|
|
|
+diff --git a/lldb/source/Plugins/Process/wasm/UnwindWasm.h b/lldb/source/Plugins/Process/wasm/UnwindWasm.h
|
|
|
+new file mode 100644
|
|
|
+index 000000000000..9bd1dac9a98a
|
|
|
+--- /dev/null
|
|
|
++++ b/lldb/source/Plugins/Process/wasm/UnwindWasm.h
|
|
|
+@@ -0,0 +1,55 @@
|
|
|
++//===-- UnwindWasm.h --------------------------------------------*- C++ -*-===//
|
|
|
++//
|
|
|
++// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
|
|
++// See https://llvm.org/LICENSE.txt for license information.
|
|
|
++// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
|
|
++//
|
|
|
++//===----------------------------------------------------------------------===//
|
|
|
++
|
|
|
++#ifndef lldb_UnwindWasm_h_
|
|
|
++#define lldb_UnwindWasm_h_
|
|
|
++
|
|
|
++#include "lldb/Target/RegisterContext.h"
|
|
|
++#include "lldb/Target/Unwind.h"
|
|
|
++#include <vector>
|
|
|
++
|
|
|
++namespace lldb_private {
|
|
|
++namespace wasm {
|
|
|
++
|
|
|
++/// UnwindWasm manages stack unwinding for a WebAssembly process.
|
|
|
++class UnwindWasm : public lldb_private::Unwind {
|
|
|
++public:
|
|
|
++ UnwindWasm(lldb_private::Thread &thread)
|
|
|
++ : Unwind(thread), m_frames(), m_unwind_complete(false) {}
|
|
|
++ ~UnwindWasm() override = default;
|
|
|
++
|
|
|
++protected:
|
|
|
++ /// Unwind protocol.
|
|
|
++ /// \{
|
|
|
++ void DoClear() override {
|
|
|
++ m_frames.clear();
|
|
|
++ m_unwind_complete = false;
|
|
|
++ }
|
|
|
++
|
|
|
++ uint32_t DoGetFrameCount() override;
|
|
|
++
|
|
|
++ bool DoGetFrameInfoAtIndex(uint32_t frame_idx, lldb::addr_t &cfa,
|
|
|
++ lldb::addr_t &pc,
|
|
|
++ bool &behaves_like_zeroth_frame) override;
|
|
|
++
|
|
|
++ lldb::RegisterContextSP
|
|
|
++ DoCreateRegisterContextForFrame(lldb_private::StackFrame *frame) override;
|
|
|
++ /// \}
|
|
|
++
|
|
|
++private:
|
|
|
++ std::vector<lldb::addr_t> m_frames;
|
|
|
++ bool m_unwind_complete;
|
|
|
++
|
|
|
++ UnwindWasm(const UnwindWasm &);
|
|
|
++ const UnwindWasm &operator=(const UnwindWasm &) = delete;
|
|
|
++};
|
|
|
++
|
|
|
++} // namespace wasm
|
|
|
++} // namespace lldb_private
|
|
|
++
|
|
|
++#endif // lldb_UnwindWasm_h_
|
|
|
+diff --git a/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.cpp b/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.cpp
|
|
|
+index ccaf31317d75..c3ef5aebd46d 100644
|
|
|
+--- a/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.cpp
|
|
|
++++ b/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.cpp
|
|
|
+@@ -3212,8 +3212,13 @@ VariableSP SymbolFileDWARF::ParseVariableDIE(const SymbolContext &sc,
|
|
|
+ GetDWARFDeclContext(die).GetQualifiedNameAsConstString().GetCString();
|
|
|
+ }
|
|
|
+
|
|
|
+- if (tag == DW_TAG_formal_parameter)
|
|
|
++ if (tag == DW_TAG_formal_parameter) {
|
|
|
+ scope = eValueTypeVariableArgument;
|
|
|
++ // For Wasm dwarft, pamameter may don't have location attr,
|
|
|
++ // so set module here
|
|
|
++ if (!location.GetModule())
|
|
|
++ location.SetModule(module);
|
|
|
++ }
|
|
|
+ else {
|
|
|
+ // DWARF doesn't specify if a DW_TAG_variable is a local, global
|
|
|
+ // or static variable, so we have to do a little digging:
|
|
|
+diff --git a/lldb/source/Target/Platform.cpp b/lldb/source/Target/Platform.cpp
|
|
|
+index a77ecddfbab6..e257f93508f6 100644
|
|
|
+--- a/lldb/source/Target/Platform.cpp
|
|
|
++++ b/lldb/source/Target/Platform.cpp
|
|
|
+@@ -1970,6 +1970,12 @@ size_t Platform::GetSoftwareBreakpointTrapOpcode(Target &target,
|
|
|
+ trap_opcode_size = sizeof(g_i386_opcode);
|
|
|
+ } break;
|
|
|
+
|
|
|
++ case llvm::Triple::wasm32: {
|
|
|
++ static const uint8_t g_wasm_opcode[] = {0x00}; // unreachable
|
|
|
++ trap_opcode = g_wasm_opcode;
|
|
|
++ trap_opcode_size = sizeof(g_wasm_opcode);
|
|
|
++ } break;
|
|
|
++
|
|
|
+ default:
|
|
|
+ return 0;
|
|
|
+ }
|
|
|
+diff --git a/lldb/source/Target/Process.cpp b/lldb/source/Target/Process.cpp
|
|
|
+index 8ecc66b592ea..f148987915de 100644
|
|
|
+--- a/lldb/source/Target/Process.cpp
|
|
|
++++ b/lldb/source/Target/Process.cpp
|
|
|
+@@ -1892,7 +1892,8 @@ Status Process::DisableSoftwareBreakpoint(BreakpointSite *bp_site) {
|
|
|
+ // code
|
|
|
+ //#define VERIFY_MEMORY_READS
|
|
|
+
|
|
|
+-size_t Process::ReadMemory(addr_t addr, void *buf, size_t size, Status &error) {
|
|
|
++size_t Process::ReadMemory(addr_t addr, void *buf, size_t size, Status &error,
|
|
|
++ ExecutionContext *exe_ctx) {
|
|
|
+ error.Clear();
|
|
|
+ if (!GetDisableMemoryCache()) {
|
|
|
+ #if defined(VERIFY_MEMORY_READS)
|
|
|
+diff --git a/lldb/source/Target/ProcessTrace.cpp b/lldb/source/Target/ProcessTrace.cpp
|
|
|
+index c878a2ac4eb9..ad5945b0ad1f 100644
|
|
|
+--- a/lldb/source/Target/ProcessTrace.cpp
|
|
|
++++ b/lldb/source/Target/ProcessTrace.cpp
|
|
|
+@@ -88,7 +88,7 @@ void ProcessTrace::RefreshStateAfterStop() {}
|
|
|
+ Status ProcessTrace::DoDestroy() { return Status(); }
|
|
|
+
|
|
|
+ size_t ProcessTrace::ReadMemory(addr_t addr, void *buf, size_t size,
|
|
|
+- Status &error) {
|
|
|
++ Status &error, ExecutionContext *exe_ctx) {
|
|
|
+ // Don't allow the caching that lldb_private::Process::ReadMemory does since
|
|
|
+ // we have it all cached in the trace files.
|
|
|
+ return DoReadMemory(addr, buf, size, error);
|