mirror of
https://github.com/bytecodealliance/wasm-micro-runtime.git
synced 2024-11-26 15:32:05 +00:00
fa2f29fd8a
- Update lldb patch due to swig was upgraded to 4.1 in macos - Export LD_LIBRARY_PATH for searching libpython3.10.so when validating wamr-lldb in Ubuntu-20.04 - Rename lldb-wasm.patch to lldb_wasm.path
5907 lines
217 KiB
Diff
5907 lines
217 KiB
Diff
diff --git a/lldb/bindings/CMakeLists.txt b/lldb/bindings/CMakeLists.txt
|
|
index 9759b069fdc4..25b427f8bcf2 100644
|
|
--- a/lldb/bindings/CMakeLists.txt
|
|
+++ b/lldb/bindings/CMakeLists.txt
|
|
@@ -26,8 +26,6 @@ set(SWIG_COMMON_FLAGS
|
|
-features autodoc
|
|
-I${LLDB_SOURCE_DIR}/include
|
|
-I${CMAKE_CURRENT_SOURCE_DIR}
|
|
- -D__STDC_LIMIT_MACROS
|
|
- -D__STDC_CONSTANT_MACROS
|
|
${DARWIN_EXTRAS}
|
|
)
|
|
|
|
diff --git a/lldb/bindings/interfaces.swig b/lldb/bindings/interfaces.swig
|
|
index c9a6d0f06056..021c7683d170 100644
|
|
--- a/lldb/bindings/interfaces.swig
|
|
+++ b/lldb/bindings/interfaces.swig
|
|
@@ -1,8 +1,5 @@
|
|
/* Various liblldb typedefs that SWIG needs to know about. */
|
|
#define __extension__ /* Undefine GCC keyword to make Swig happy when processing glibc's stdint.h. */
|
|
-/* The ISO C99 standard specifies that in C++ implementations limit macros such
|
|
- as INT32_MAX should only be defined if __STDC_LIMIT_MACROS is. */
|
|
-#define __STDC_LIMIT_MACROS
|
|
%include "stdint.i"
|
|
|
|
%include "lldb/lldb-defines.h"
|
|
diff --git a/lldb/bindings/python/python-typemaps.swig b/lldb/bindings/python/python-typemaps.swig
|
|
index b1ace4ff3b1e..5f8f4aa678c4 100644
|
|
--- a/lldb/bindings/python/python-typemaps.swig
|
|
+++ b/lldb/bindings/python/python-typemaps.swig
|
|
@@ -439,7 +439,7 @@ bool SetNumberFromPyObject<double>(double &number, PyObject *obj) {
|
|
|
|
%typemap(out) lldb::FileSP {
|
|
$result = nullptr;
|
|
- lldb::FileSP &sp = $1;
|
|
+ const lldb::FileSP &sp = $1;
|
|
if (sp) {
|
|
PythonFile pyfile = unwrapOrSetPythonException(PythonFile::FromFile(*sp));
|
|
if (!pyfile.IsValid())
|
|
diff --git a/lldb/include/lldb/Breakpoint/Breakpoint.h b/lldb/include/lldb/Breakpoint/Breakpoint.h
|
|
index f2e2a0d22784..426d1129bd10 100644
|
|
--- a/lldb/include/lldb/Breakpoint/Breakpoint.h
|
|
+++ b/lldb/include/lldb/Breakpoint/Breakpoint.h
|
|
@@ -9,6 +9,7 @@
|
|
#ifndef LLDB_BREAKPOINT_BREAKPOINT_H
|
|
#define LLDB_BREAKPOINT_BREAKPOINT_H
|
|
|
|
+#include <limits>
|
|
#include <memory>
|
|
#include <string>
|
|
#include <unordered_set>
|
|
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..9c0fc7b7f270
|
|
--- /dev/null
|
|
+++ b/lldb/source/Plugins/Process/wasm/ProcessWasm.cpp
|
|
@@ -0,0 +1,261 @@
|
|
+//===-- 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"
|
|
+
|
|
+#include "lldb/Target/UnixSignals.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) {
|
|
+ /* always use linux signals for wasm process */
|
|
+ m_unix_signals_sp = UnixSignals::Create(ArchSpec{"wasm32-Ant-wasi-wasm"});
|
|
+}
|
|
+
|
|
+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("%llx", 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/PathMappingList.cpp b/lldb/source/Target/PathMappingList.cpp
|
|
index b660c310ef31..cd76421cec18 100644
|
|
--- a/lldb/source/Target/PathMappingList.cpp
|
|
+++ b/lldb/source/Target/PathMappingList.cpp
|
|
@@ -218,7 +218,12 @@ bool PathMappingList::ReverseRemapPath(const FileSpec &file, FileSpec &fixed) co
|
|
}
|
|
|
|
llvm::Optional<FileSpec> PathMappingList::FindFile(const FileSpec &orig_spec) const {
|
|
- if (auto remapped = RemapPath(orig_spec.GetPath(), /*only_if_exists=*/true))
|
|
+ // We must normalize the orig_spec again using the host's path style,
|
|
+ // otherwise there will be mismatch between the host and remote platform
|
|
+ // if they use different path styles.
|
|
+ if (auto remapped = RemapPath(
|
|
+ NormalizePath(ConstString(orig_spec.GetCString())).GetStringRef(),
|
|
+ /*only_if_exists=*/true))
|
|
return remapped;
|
|
|
|
return {};
|
|
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);
|
|
diff --git a/lldb/source/Target/ThreadPlanStepRange.cpp b/lldb/source/Target/ThreadPlanStepRange.cpp
|
|
index 896e647bbb52..f76307016102 100644
|
|
--- a/lldb/source/Target/ThreadPlanStepRange.cpp
|
|
+++ b/lldb/source/Target/ThreadPlanStepRange.cpp
|
|
@@ -334,7 +334,10 @@ bool ThreadPlanStepRange::SetNextBranchBreakpoint() {
|
|
// If we didn't find a branch, run to the end of the range.
|
|
if (branch_index == UINT32_MAX) {
|
|
uint32_t last_index = instructions->GetSize() - 1;
|
|
- if (last_index - pc_index > 1) {
|
|
+ /* This line causes the "step over was treated as step in" issue, we
|
|
+ * modify it as a workaround */
|
|
+ /* The origin line is: if (last_index - pc_index > 1) { */
|
|
+ if (last_index - pc_index >= 1) {
|
|
InstructionSP last_inst =
|
|
instructions->GetInstructionAtIndex(last_index);
|
|
size_t last_inst_size = last_inst->GetOpcode().GetByteSize();
|
|
diff --git a/lldb/source/Target/UnixSignals.cpp b/lldb/source/Target/UnixSignals.cpp
|
|
index 4ec2e25c7e3b..24c88fe9ae4f 100644
|
|
--- a/lldb/source/Target/UnixSignals.cpp
|
|
+++ b/lldb/source/Target/UnixSignals.cpp
|
|
@@ -46,6 +46,8 @@ lldb::UnixSignalsSP UnixSignals::Create(const ArchSpec &arch) {
|
|
return std::make_shared<FreeBSDSignals>();
|
|
case llvm::Triple::NetBSD:
|
|
return std::make_shared<NetBSDSignals>();
|
|
+ case llvm::Triple::WASI:
|
|
+ return std::make_shared<LinuxSignals>();
|
|
default:
|
|
return std::make_shared<UnixSignals>();
|
|
}
|
|
diff --git a/llvm/include/llvm/ExecutionEngine/Orc/OrcRPCExecutorProcessControl.h b/llvm/include/llvm/ExecutionEngine/Orc/OrcRPCExecutorProcessControl.h
|
|
index 4310ba9ce9e0..297b3387999d 100644
|
|
--- a/llvm/include/llvm/ExecutionEngine/Orc/OrcRPCExecutorProcessControl.h
|
|
+++ b/llvm/include/llvm/ExecutionEngine/Orc/OrcRPCExecutorProcessControl.h
|
|
@@ -13,6 +13,7 @@
|
|
#ifndef LLVM_EXECUTIONENGINE_ORC_ORCRPCEXECUTORPROCESSCONTROL_H
|
|
#define LLVM_EXECUTIONENGINE_ORC_ORCRPCEXECUTORPROCESSCONTROL_H
|
|
|
|
+#include "llvm/ExecutionEngine/Orc/Core.h"
|
|
#include "llvm/ExecutionEngine/Orc/ExecutorProcessControl.h"
|
|
#include "llvm/ExecutionEngine/Orc/Shared/RPCUtils.h"
|
|
#include "llvm/ExecutionEngine/Orc/Shared/RawByteChannel.h"
|
|
diff --git a/llvm/include/llvm/Support/MathExtras.h b/llvm/include/llvm/Support/MathExtras.h
|
|
index 753b1998c40c..27370c62dd6e 100644
|
|
--- a/llvm/include/llvm/Support/MathExtras.h
|
|
+++ b/llvm/include/llvm/Support/MathExtras.h
|
|
@@ -16,6 +16,7 @@
|
|
#include "llvm/Support/Compiler.h"
|
|
#include <cassert>
|
|
#include <climits>
|
|
+#include <limits>
|
|
#include <cmath>
|
|
#include <cstdint>
|
|
#include <cstring>
|