Browse Source

Implement source debugging for interpreter and AOT (#769)

Implement source debugging feature for classic interpreter and AOT:
- use `cmake -DWAMR_BUILD_DEBUG_INTERP=1` to enable interpreter debugging
- use `cmake -DWAMR_BUILD_DEBUG_AOT=1` to enable AOT debugging

See doc/source_debugging.md for more details.
Wenyong Huang 4 years ago
parent
commit
9ef37dd781
55 changed files with 10092 additions and 63 deletions
  1. 1 0
      README.md
  2. 6 0
      build-scripts/config_common.cmake
  3. 5779 0
      build-scripts/lldb-wasm.patch
  4. 6 0
      build-scripts/runtime_lib.cmake
  5. 33 3
      core/iwasm/aot/aot_loader.c
  6. 4 0
      core/iwasm/aot/aot_runtime.h
  7. 152 0
      core/iwasm/aot/debug/elf_parser.c
  8. 27 0
      core/iwasm/aot/debug/elf_parser.h
  9. 253 0
      core/iwasm/aot/debug/jit_debug.c
  10. 29 0
      core/iwasm/aot/debug/jit_debug.h
  11. 6 1
      core/iwasm/aot/iwasm_aot.cmake
  12. 26 0
      core/iwasm/common/wasm_exec_env.c
  13. 7 0
      core/iwasm/common/wasm_exec_env.h
  14. 33 2
      core/iwasm/common/wasm_runtime_common.c
  15. 4 4
      core/iwasm/common/wasm_runtime_common.h
  16. 7 0
      core/iwasm/compilation/aot.h
  17. 19 0
      core/iwasm/compilation/aot_compiler.c
  18. 25 13
      core/iwasm/compilation/aot_emit_aot_file.c
  19. 43 8
      core/iwasm/compilation/aot_emit_control.c
  20. 1 1
      core/iwasm/compilation/aot_emit_exception.c
  21. 2 2
      core/iwasm/compilation/aot_emit_function.c
  22. 2 2
      core/iwasm/compilation/aot_emit_memory.c
  23. 36 0
      core/iwasm/compilation/aot_llvm.c
  24. 12 0
      core/iwasm/compilation/aot_llvm.h
  25. 530 0
      core/iwasm/compilation/debug/dwarf_extractor.cpp
  26. 58 0
      core/iwasm/compilation/debug/dwarf_extractor.h
  27. 11 3
      core/iwasm/compilation/iwasm_compl.cmake
  28. 6 0
      core/iwasm/include/aot_export.h
  29. 5 0
      core/iwasm/include/wasm_export.h
  30. 14 0
      core/iwasm/interpreter/wasm.h
  31. 76 5
      core/iwasm/interpreter/wasm_interp_classic.c
  32. 133 13
      core/iwasm/interpreter/wasm_loader.c
  33. 3 1
      core/iwasm/interpreter/wasm_loader.h
  34. 13 0
      core/iwasm/interpreter/wasm_opcode.h
  35. 30 1
      core/iwasm/interpreter/wasm_runtime.c
  36. 1011 0
      core/iwasm/libraries/debug-engine/debug_engine.c
  37. 12 0
      core/iwasm/libraries/debug-engine/debug_engine.cmake
  38. 193 0
      core/iwasm/libraries/debug-engine/debug_engine.h
  39. 177 0
      core/iwasm/libraries/debug-engine/gdbserver.c
  40. 43 0
      core/iwasm/libraries/debug-engine/gdbserver.h
  41. 598 0
      core/iwasm/libraries/debug-engine/handler.c
  42. 55 0
      core/iwasm/libraries/debug-engine/handler.h
  43. 173 0
      core/iwasm/libraries/debug-engine/packets.c
  44. 22 0
      core/iwasm/libraries/debug-engine/packets.h
  45. 45 0
      core/iwasm/libraries/debug-engine/utils.c
  46. 23 0
      core/iwasm/libraries/debug-engine/utils.h
  47. 124 2
      core/iwasm/libraries/thread-mgr/thread_manager.c
  48. 56 0
      core/iwasm/libraries/thread-mgr/thread_manager.h
  49. 86 0
      doc/source_debugging.md
  50. 11 0
      product-mini/platforms/darwin/CMakeLists.txt
  51. 11 0
      product-mini/platforms/linux/CMakeLists.txt
  52. 28 0
      product-mini/platforms/posix/main.c
  53. 11 0
      product-mini/platforms/vxworks/CMakeLists.txt
  54. 15 2
      wamr-compiler/CMakeLists.txt
  55. 6 0
      wamr-compiler/main.c

+ 1 - 0
README.md

@@ -28,6 +28,7 @@ iwasm VM core
 - [Multiple modules as dependencies](./doc/multi_module.md), ref to [sample](samples/multi-module)
 - [Thread management and pthread library](./doc/pthread_library.md), ref to [sample](samples/multi-thread)
 - [Linux SGX (Intel Software Guard Extension) support](./doc/linux_sgx.md)
+- [Source debugging](./doc/source_debugging.md)
 
 ### post-MVP features
 - [Non-trapping float-to-int conversions](https://github.com/WebAssembly/nontrapping-float-to-int-conversions)

+ 6 - 0
build-scripts/config_common.cmake

@@ -239,3 +239,9 @@ endif ()
 if (WAMR_DISABLE_APP_ENTRY EQUAL 1)
   message ("     WAMR application entry functions excluded")
 endif ()
+if (WAMR_BUILD_DEBUG_INTERP EQUAL 1)
+    message ("     Debug Interpreter enabled")
+endif ()
+if (WAMR_BUILD_DEBUG_AOT EQUAL 1)
+    message ("     Debug AOT enabled")
+endif ()

+ 5779 - 0
build-scripts/lldb-wasm.patch

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

+ 6 - 0
build-scripts/runtime_lib.cmake

@@ -84,6 +84,11 @@ if (WAMR_BUILD_LIB_PTHREAD EQUAL 1)
     set (WAMR_BUILD_SHARED_MEMORY 1)
 endif ()
 
+if (WAMR_BUILD_DEBUG_INTERP EQUAL 1)
+    set (WAMR_BUILD_THREAD_MGR 1)
+    include (${IWASM_DIR}/libraries/debug-engine/debug_engine.cmake)
+endif ()
+
 if (WAMR_BUILD_THREAD_MGR EQUAL 1)
     include (${IWASM_DIR}/libraries/thread-mgr/thread_mgr.cmake)
 endif ()
@@ -132,6 +137,7 @@ set (source_all
     ${LIB_PTHREAD_SOURCE}
     ${THREAD_MGR_SOURCE}
     ${LIBC_EMCC_SOURCE}
+    ${DEBUG_ENGINE_SOURCE}
 )
 
 set (WAMR_RUNTIME_LIB_SOURCE ${source_all})

+ 33 - 3
core/iwasm/aot/aot_loader.c

@@ -14,6 +14,10 @@
 #include "../compilation/aot_llvm.h"
 #include "../interpreter/wasm_loader.h"
 #endif
+#if WASM_ENABLE_DEBUG_AOT != 0
+#include "debug/elf_parser.h"
+#include "debug/jit_debug.h"
+#endif
 
 #define XMM_PLT_PREFIX "__xmm@"
 #define REAL_PLT_PREFIX "__real@"
@@ -1231,7 +1235,6 @@ load_init_data_section(const uint8 *buf, const uint8 *buf_end,
     }
 
     return true;
-
 fail:
     return false;
 }
@@ -1257,12 +1260,30 @@ load_text_section(const uint8 *buf, const uint8 *buf_end,
     module->code = (void*)(buf + module->literal_size);
     module->code_size = (uint32)(buf_end - (uint8*)module->code);
 
+#if WASM_ENABLE_DEBUG_AOT != 0
+    module->elf_size = module->code_size;
+
+    if (is_ELF(module->code)) {
+        /* Now code points to an ELF object, we pull it down to .text section */
+        uint64 offset;
+        uint64 size;
+        char *buf = module->code;
+        module->elf_hdr = buf;
+        if (!get_text_section(buf, &offset, &size)) {
+            set_error_buf(error_buf, error_buf_size,
+                          "get text section of ELF failed");
+            return false;
+        }
+        module->code = buf + offset;
+        module->code_size -= (uint32)offset;
+    }
+#endif
+
     if ((module->code_size > 0) && (module->native_symbol_count == 0)) {
         plt_base = (uint8 *)buf_end - get_plt_table_size();
         init_plt_table(plt_base);
     }
     return true;
-
 fail:
     return false;
 }
@@ -1451,7 +1472,6 @@ load_export_section(const uint8 *buf, const uint8 *buf_end,
     }
 
     return true;
-
 fail:
     return false;
 }
@@ -2247,6 +2267,13 @@ load_from_sections(AOTModule *module, AOTSection *sections,
 #if WASM_ENABLE_MEMORY_TRACING != 0
     wasm_runtime_dump_module_mem_consumption((WASMModuleCommon*)module);
 #endif
+
+#if WASM_ENABLE_DEBUG_AOT != 0
+    if (!jit_code_entry_create(module->elf_hdr, module->elf_size)) {
+        set_error_buf(error_buf, error_buf_size, "create jit code entry failed");
+        return false;
+    }
+#endif
     return true;
 }
 
@@ -2893,6 +2920,9 @@ aot_unload(AOTModule *module)
     if (module->data_sections)
         destroy_object_data_sections(module->data_sections,
                                      module->data_section_count);
+#if WASM_ENABLE_DEBUG_AOT != 0
+    jit_code_entry_destroy(module->elf_hdr);
+#endif
 
     wasm_runtime_free(module);
 }

+ 4 - 0
core/iwasm/aot/aot_runtime.h

@@ -254,6 +254,10 @@ typedef struct AOTModule {
     WASIArguments wasi_args;
     bool is_wasi_module;
 #endif
+#if WASM_ENABLE_DEBUG_AOT != 0
+    void *elf_hdr;
+    uint32 elf_size;
+#endif
 } AOTModule;
 
 typedef union {

+ 152 - 0
core/iwasm/aot/debug/elf_parser.c

@@ -0,0 +1,152 @@
+/*
+ * Copyright (C) 2021 Ant Group.  All rights reserved.
+ * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+ */
+
+#include <stdio.h>
+#include <assert.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <errno.h>
+#include <stdbool.h>
+
+#include <elf.h>
+
+#include "aot_runtime.h"
+#include "bh_log.h"
+#include "elf_parser.h"
+
+bool
+is_ELF(void *buf)
+{
+    Elf32_Ehdr *eh = (Elf32_Ehdr *)buf;
+    if (!strncmp((char *)eh->e_ident, "\177ELF", 4)) {
+        LOG_VERBOSE("the buffer is ELF entry!");
+        return true;
+    }
+    LOG_VERBOSE("the buffer is not ELF entry!");
+    return false;
+}
+
+static bool
+is64Bit(Elf32_Ehdr *eh)
+{
+    if (eh->e_ident[EI_CLASS] == ELFCLASS64)
+        return true;
+    else
+        return false;
+}
+
+static bool
+is32Bit(Elf32_Ehdr *eh)
+{
+    if (eh->e_ident[EI_CLASS] == ELFCLASS32)
+        return true;
+    else
+        return false;
+}
+
+bool
+is_ELF64(void *buf)
+{
+    Elf64_Ehdr *eh = (Elf64_Ehdr *)buf;
+    if (!strncmp((char *)eh->e_ident, "\177ELF", 4)) {
+        LOG_VERBOSE("the buffer is ELF entry!");
+        return true;
+    }
+    LOG_VERBOSE("the buffer is not ELF entry!");
+    return false;
+}
+
+static void
+read_section_header_table(Elf32_Ehdr *eh, Elf32_Shdr *sh_table[])
+{
+    uint32_t i;
+    char *buf = (char *)eh;
+    buf += eh->e_shoff;
+    LOG_VERBOSE("str index = %d count=%d", eh->e_shstrndx, eh->e_shnum);
+    for (i = 0; i < eh->e_shnum; i++) {
+        sh_table[i] = (Elf32_Shdr *)buf;
+        buf += eh->e_shentsize;
+    }
+}
+
+static void
+read_section_header_table64(Elf64_Ehdr *eh, Elf64_Shdr *sh_table[])
+{
+    uint32_t i;
+    char *buf = (char *)eh;
+    buf += eh->e_shoff;
+
+    for (i = 0; i < eh->e_shnum; i++) {
+        sh_table[i] = (Elf64_Shdr *)buf;
+        buf += eh->e_shentsize;
+    }
+}
+
+static char *
+get_section(Elf32_Ehdr *eh, Elf32_Shdr *section_header)
+{
+    char *buf = (char *)eh;
+    return buf + section_header->sh_offset;
+}
+
+static char *
+get_section64(Elf64_Ehdr *eh, Elf64_Shdr *section_header)
+{
+    char *buf = (char *)eh;
+    return buf + section_header->sh_offset;
+}
+
+bool
+get_text_section(void *buf, uint64_t *offset, uint64_t *size)
+{
+    bool ret = false;
+    uint32 i;
+    char *sh_str;
+
+    if (is64Bit(buf)) {
+        Elf64_Ehdr *eh = (Elf64_Ehdr *)buf;
+        Elf64_Shdr **sh_table =
+            wasm_runtime_malloc(eh->e_shnum * sizeof(Elf64_Shdr *));
+        if (sh_table) {
+            read_section_header_table64(eh, sh_table);
+            sh_str = get_section64(eh, sh_table[eh->e_shstrndx]);
+            for (i= 0; i < eh->e_shnum; i++) {
+                if (!strcmp(sh_str + sh_table[i]->sh_name, ".text")) {
+                    *offset = sh_table[i]->sh_offset;
+                    *size = sh_table[i]->sh_size;
+                    sh_table[i]->sh_addr = (Elf64_Addr)(uintptr_t)
+                                           ((char *)buf + sh_table[i]->sh_offset);
+                    ret = true;
+                    break;
+                }
+            }
+            wasm_runtime_free(sh_table);
+        }
+    }
+    else if (is32Bit(buf)) {
+        Elf32_Ehdr *eh = (Elf32_Ehdr *)buf;
+        Elf32_Shdr **sh_table =
+            wasm_runtime_malloc(eh->e_shnum * sizeof(Elf32_Shdr *));
+        if (sh_table) {
+            read_section_header_table(eh, sh_table);
+            sh_str = get_section(eh, sh_table[eh->e_shstrndx]);
+            for (i= 0; i < eh->e_shnum; i++) {
+                if (!strcmp(sh_str + sh_table[i]->sh_name, ".text")) {
+                    *offset = sh_table[i]->sh_offset;
+                    *size = sh_table[i]->sh_size;
+                    sh_table[i]->sh_addr = (Elf32_Addr)(uintptr_t)
+                                           ((char *)buf + sh_table[i]->sh_offset);
+                    ret = true;
+                    break;
+                }
+            }
+            wasm_runtime_free(sh_table);
+        }
+    }
+
+    return ret;
+}

+ 27 - 0
core/iwasm/aot/debug/elf_parser.h

@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2021 Ant Group.  All rights reserved.
+ * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+ */
+
+#ifndef _ELF_PARSERE_H_
+#define _ELF_PARSER_H_
+#include <stdbool.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+bool
+is_ELF(void *buf);
+
+bool
+is_ELF64(void *buf);
+
+bool
+get_text_section(void *buf, uint64_t *offset, uint64_t *size);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif

+ 253 - 0
core/iwasm/aot/debug/jit_debug.c

@@ -0,0 +1,253 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * 
+ * Copyright (C) 2021 Ant Group.  All rights reserved.
+ * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+ */
+
+#include "bh_log.h"
+#include "bh_platform.h"
+#include "wasm_runtime.h"
+
+#include <stdio.h>
+#include <assert.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <errno.h>
+#include <stdbool.h>
+
+/* This must be kept in sync with gdb/gdb/jit.h */
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef enum {
+    JIT_NOACTION = 0,
+    JIT_REGISTER_FN,
+    JIT_UNREGISTER_FN
+} JITAction;
+
+typedef struct JITCodeEntry {
+    struct JITCodeEntry *next_;
+    struct JITCodeEntry *prev_;
+    const uint8 *symfile_addr_;
+    uint64 symfile_size_;
+} JITCodeEntry;
+
+typedef struct JITDescriptor {
+    uint32 version_;
+    uint32 action_flag_;
+    JITCodeEntry *relevant_entry_;
+    JITCodeEntry *first_entry_;
+} JITDescriptor;
+
+/* LLVM has already define this */
+#if (WASM_ENABLE_WAMR_COMPILER == 0) && (WASM_ENABLE_JIT == 0)
+/**
+ * GDB will place breakpoint into this function.
+ * To prevent GCC from inlining or removing it we place noinline attribute
+ * and inline assembler statement inside.
+ */
+void __attribute__((noinline)) __jit_debug_register_code();
+
+void __attribute__((noinline)) __jit_debug_register_code()
+{
+    int x;
+    *(char *)&x = '\0';
+}
+
+/**
+ * GDB will inspect contents of this descriptor.
+ * Static initialization is necessary to prevent GDB from seeing
+ * uninitialized descriptor.
+ */
+
+JITDescriptor __jit_debug_descriptor = { 1, JIT_NOACTION, NULL, NULL };
+#else
+extern void __jit_debug_register_code();
+extern JITDescriptor __jit_debug_descriptor;
+#endif
+
+/**
+ * Call __jit_debug_register_code indirectly via global variable.
+ * This gives the debugger an easy way to inject custom code to
+ * handle the events.
+ */
+void (*__jit_debug_register_code_ptr)() = __jit_debug_register_code;
+
+#ifdef __cplusplus
+}
+#endif
+
+typedef struct WASMJITDebugEngine {
+    korp_mutex jit_entry_lock;
+    bh_list jit_entry_list;
+} WASMJITDebugEngine;
+
+typedef struct WASMJITEntryNode {
+    struct WASMJITEntryNode *next;
+    JITCodeEntry *entry;
+} WASMJITEntryNode;
+
+static WASMJITDebugEngine *jit_debug_engine;
+
+static JITCodeEntry *
+CreateJITCodeEntryInternal(const uint8 *symfile_addr, uint64 symfile_size)
+{
+    JITCodeEntry *entry;
+
+    os_mutex_lock(&jit_debug_engine->jit_entry_lock);
+
+    if (!(entry = wasm_runtime_malloc(sizeof(JITCodeEntry)))) {
+        LOG_ERROR("WASM JIT Debug Engine error: failed to allocate memory");
+        os_mutex_unlock(&jit_debug_engine->jit_entry_lock);
+        return NULL;
+    }
+    entry->symfile_addr_ = symfile_addr;
+    entry->symfile_size_ = symfile_size;
+    entry->prev_ = NULL;
+
+    entry->next_ = __jit_debug_descriptor.first_entry_;
+    if (entry->next_ != NULL) {
+        entry->next_->prev_ = entry;
+    }
+    __jit_debug_descriptor.first_entry_ = entry;
+    __jit_debug_descriptor.relevant_entry_ = entry;
+
+    __jit_debug_descriptor.action_flag_ = JIT_REGISTER_FN;
+
+    (*__jit_debug_register_code_ptr)();
+
+    os_mutex_unlock(&jit_debug_engine->jit_entry_lock);
+    return entry;
+}
+
+static void
+DestroyJITCodeEntryInternal(JITCodeEntry *entry)
+{
+    os_mutex_lock(&jit_debug_engine->jit_entry_lock);
+
+    if (entry->prev_ != NULL) {
+        entry->prev_->next_ = entry->next_;
+    }
+    else {
+        __jit_debug_descriptor.first_entry_ = entry->next_;
+    }
+
+    if (entry->next_ != NULL) {
+        entry->next_->prev_ = entry->prev_;
+    }
+
+    __jit_debug_descriptor.relevant_entry_ = entry;
+    __jit_debug_descriptor.action_flag_ = JIT_UNREGISTER_FN;
+    (*__jit_debug_register_code_ptr)();
+
+    wasm_runtime_free(entry);
+
+    os_mutex_unlock(&jit_debug_engine->jit_entry_lock);
+}
+
+bool
+jit_debug_engine_init()
+{
+    if (jit_debug_engine) {
+        return true;
+    }
+
+    if (!(jit_debug_engine =
+                wasm_runtime_malloc(sizeof(WASMJITDebugEngine)))) {
+        LOG_ERROR("WASM JIT Debug Engine error: failed to allocate memory");
+        return false;
+    }
+    memset(jit_debug_engine, 0, sizeof(WASMJITDebugEngine));
+
+    if (os_mutex_init(&jit_debug_engine->jit_entry_lock) != 0) {
+        wasm_runtime_free(jit_debug_engine);
+        jit_debug_engine = NULL;
+        return false;
+    }
+
+    bh_list_init(&jit_debug_engine->jit_entry_list);
+    return true;
+}
+
+void
+jit_debug_engine_destroy()
+{
+    if (jit_debug_engine) {
+        WASMJITEntryNode *node, *node_next;
+
+        /* Destroy all nodes */
+        node = bh_list_first_elem(&jit_debug_engine->jit_entry_list);
+        while (node) {
+            node_next = bh_list_elem_next(node);
+            DestroyJITCodeEntryInternal(node->entry);
+            bh_list_remove(&jit_debug_engine->jit_entry_list, node);
+            wasm_runtime_free(node);
+            node = node_next;
+        }
+
+        /* Destroy JIT Debug Engine */
+        os_mutex_destroy(&jit_debug_engine->jit_entry_lock);
+        wasm_runtime_free(jit_debug_engine);
+        jit_debug_engine = NULL;
+    }
+}
+
+bool
+jit_code_entry_create(const uint8 *symfile_addr, uint64 symfile_size)
+{
+    JITCodeEntry *entry;
+    WASMJITEntryNode *node;
+
+    if (!(node = wasm_runtime_malloc(sizeof(WASMJITEntryNode)))) {
+        LOG_ERROR("WASM JIT Debug Engine error: failed to allocate memory");
+        return false;
+    }
+
+    entry = CreateJITCodeEntryInternal(symfile_addr, symfile_size);
+
+    if (!entry) {
+        wasm_runtime_free(node);
+        return false;
+    }
+
+    node->entry = entry;
+    os_mutex_lock(&jit_debug_engine->jit_entry_lock);
+    bh_list_insert(&jit_debug_engine->jit_entry_list, node);
+    os_mutex_unlock(&jit_debug_engine->jit_entry_lock);
+    return true;
+}
+
+void
+jit_code_entry_destroy(const uint8 *symfile_addr)
+{
+    WASMJITEntryNode *node;
+
+    node = bh_list_first_elem(&jit_debug_engine->jit_entry_list);
+    while (node) {
+        WASMJITEntryNode *next_node = bh_list_elem_next(node);
+        if (node->entry->symfile_addr_ == symfile_addr) {
+            DestroyJITCodeEntryInternal(node->entry);
+            os_mutex_lock(&jit_debug_engine->jit_entry_lock);
+            bh_list_remove(&jit_debug_engine->jit_entry_list, node);
+            os_mutex_unlock(&jit_debug_engine->jit_entry_lock);
+            wasm_runtime_free(node);
+        }
+        node = next_node;
+    }
+}

+ 29 - 0
core/iwasm/aot/debug/jit_debug.h

@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2021 Ant Group.  All rights reserved.
+ * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+ */
+
+#ifndef _JIT_DEBUG_H_
+#define _JIT_DEBUG_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+bool
+jit_debug_engine_init();
+
+void
+jit_debug_engine_destroy();
+
+bool
+jit_code_entry_create(const uint8 *symfile_addr, uint64 symfile_size);
+
+void
+jit_code_entry_destroy(const uint8 *symfile_addr);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif

+ 6 - 1
core/iwasm/aot/iwasm_aot.cmake

@@ -31,5 +31,10 @@ else ()
   message (FATAL_ERROR "Build target isn't set")
 endif ()
 
-set (IWASM_AOT_SOURCE ${c_source_all} ${arch_source})
+if (WAMR_BUILD_DEBUG_AOT EQUAL 1)
+  add_definitions(-DWASM_ENABLE_DEBUG_AOT=1)
+  file(GLOB debug_source ${IWASM_AOT_DIR}/debug/*.c)
+endif()
+
+set (IWASM_AOT_SOURCE ${c_source_all} ${arch_source} ${debug_source})
 

+ 26 - 0
core/iwasm/common/wasm_exec_env.c

@@ -18,6 +18,9 @@
 
 #if WASM_ENABLE_THREAD_MGR != 0
 #include "../libraries/thread-mgr/thread_manager.h"
+#if WASM_ENABLE_DEBUG_INTERP != 0
+#include "../libraries/debug-engine/debug_engine.h"
+#endif
 #endif
 
 WASMExecEnv *
@@ -46,6 +49,12 @@ wasm_exec_env_create_internal(struct WASMModuleInstanceCommon *module_inst,
 
     if (os_cond_init(&exec_env->wait_cond) != 0)
         goto fail3;
+
+#if WASM_ENABLE_DEBUG_INTERP != 0
+    if (!(exec_env->current_status = wasm_cluster_create_exenv_status()))
+        goto fail4;
+#endif
+
 #endif
 
     exec_env->module_inst = module_inst;
@@ -68,6 +77,10 @@ wasm_exec_env_create_internal(struct WASMModuleInstanceCommon *module_inst,
     return exec_env;
 
 #if WASM_ENABLE_THREAD_MGR != 0
+#if WASM_ENABLE_DEBUG_INTERP != 0
+fail4:
+    os_cond_destroy(&exec_env->wait_cond);
+#endif
 fail3:
     os_mutex_destroy(&exec_env->wait_lock);
 fail2:
@@ -86,6 +99,9 @@ wasm_exec_env_destroy_internal(WASMExecEnv *exec_env)
 #if WASM_ENABLE_THREAD_MGR != 0
     os_mutex_destroy(&exec_env->wait_lock);
     os_cond_destroy(&exec_env->wait_cond);
+#if WASM_ENABLE_DEBUG_INTERP != 0
+    wasm_cluster_destroy_exenv_status(exec_env->current_status);
+#endif
 #endif
 #if WASM_ENABLE_AOT != 0
     wasm_runtime_free(exec_env->argv_buf);
@@ -131,6 +147,10 @@ wasm_exec_env_create(struct WASMModuleInstanceCommon *module_inst,
         wasm_exec_env_destroy_internal(exec_env);
         return NULL;
     }
+#if WASM_ENABLE_DEBUG_INTERP != 0
+    wasm_debug_instance_create(cluster);
+#endif
+
 #endif
     return exec_env;
 }
@@ -142,6 +162,12 @@ wasm_exec_env_destroy(WASMExecEnv *exec_env)
     /* Terminate all sub-threads */
     WASMCluster *cluster = wasm_exec_env_get_cluster(exec_env);
     if (cluster) {
+#if WASM_ENABLE_THREAD_MGR != 0
+#if WASM_ENABLE_DEBUG_INTERP != 0
+        wasm_cluster_thread_exited(exec_env);
+        wasm_debug_instance_destroy(cluster);
+#endif
+#endif
         wasm_cluster_terminate_all_except_self(cluster, exec_env);
         wasm_cluster_del_exec_env(cluster, exec_env);
     }

+ 7 - 0
core/iwasm/common/wasm_exec_env.h

@@ -20,6 +20,9 @@ struct WASMInterpFrame;
 
 #if WASM_ENABLE_THREAD_MGR != 0
 typedef struct WASMCluster WASMCluster;
+#if WASM_ENABLE_DEBUG_INTERP != 0
+typedef struct WASMCurrentEnvStatus WASMCurrentEnvStatus;
+#endif
 #endif
 
 #ifdef OS_ENABLE_HW_BOUND_CHECK
@@ -97,6 +100,10 @@ typedef struct WASMExecEnv {
     korp_cond wait_cond;
 #endif
 
+#if WASM_ENABLE_DEBUG_INTERP != 0
+    WASMCurrentEnvStatus *current_status;
+#endif
+
     /* attachment for native function */
     void *attachment;
 

+ 33 - 2
core/iwasm/common/wasm_runtime_common.c

@@ -14,9 +14,15 @@
 #endif
 #if WASM_ENABLE_AOT != 0
 #include "../aot/aot_runtime.h"
+#if WASM_ENABLE_DEBUG_AOT != 0
+#include "../aot/debug/jit_debug.h"
+#endif
 #endif
 #if WASM_ENABLE_THREAD_MGR != 0
 #include "../libraries/thread-mgr/thread_manager.h"
+#if WASM_ENABLE_DEBUG_INTERP != 0
+#include "../libraries/debug-engine/debug_engine.h"
+#endif
 #endif
 #if WASM_ENABLE_SHARED_MEMORY != 0
 #include "wasm_shared_memory.h"
@@ -128,20 +134,29 @@ wasm_runtime_env_init()
         goto fail6;
     }
 #endif
+#if WASM_ENABLE_DEBUG_AOT != 0
+    if (!jit_debug_engine_init()) {
+        goto fail7;
+    }
+#endif
 #endif
 
 #if WASM_ENABLE_REF_TYPES != 0
     if (!wasm_externref_map_init()) {
-        goto fail7;
+        goto fail8;
     }
 #endif
 
     return true;
 
 #if WASM_ENABLE_REF_TYPES != 0
-fail7:
+fail8:
 #endif
 #if WASM_ENABLE_AOT != 0
+#if WASM_ENABLE_DEBUG_AOT != 0
+    jit_debug_engine_destroy();
+fail7:
+#endif
 #ifdef OS_ENABLE_HW_BOUND_CHECK
     aot_signal_destroy();
 fail6:
@@ -201,6 +216,9 @@ wasm_runtime_destroy()
 #endif
 
 #if WASM_ENABLE_AOT != 0
+#if WASM_ENABLE_DEBUG_AOT != 0
+    jit_debug_engine_destroy();
+#endif
 #ifdef OS_ENABLE_HW_BOUND_CHECK
     aot_signal_destroy();
 #endif
@@ -220,6 +238,9 @@ wasm_runtime_destroy()
 #endif
 
 #if (WASM_ENABLE_WAMR_COMPILER == 0) && (WASM_ENABLE_THREAD_MGR != 0)
+#if WASM_ENABLE_DEBUG_INTERP != 0
+    wasm_debug_engine_destroy();
+#endif
     thread_manager_destroy();
 #endif
 
@@ -241,6 +262,16 @@ wasm_runtime_full_init(RuntimeInitArgs *init_args)
         return false;
     }
 
+#if WASM_ENABLE_DEBUG_INTERP != 0
+    if (strlen(init_args->ip_addr))
+        if (!wasm_debug_engine_init(init_args->ip_addr,
+                                   init_args->platform_port,
+                                   init_args->instance_port)) {
+            wasm_runtime_destroy();
+            return false;
+        }
+#endif
+
     if (init_args->n_native_symbols > 0
         && !wasm_runtime_register_natives(init_args->native_module_name,
                                           init_args->native_symbols,

+ 4 - 4
core/iwasm/common/wasm_runtime_common.h

@@ -774,24 +774,24 @@ wasm_runtime_finalize_call_function(WASMExecEnv *exec_env,
 
 bool
 wasm_runtime_get_export_func_type(const WASMModuleCommon *module_comm,
-                                  const WASMExport *export_func_type,
+                                  const WASMExport *export_,
                                   WASMType **out);
 
 bool
 wasm_runtime_get_export_global_type(const WASMModuleCommon *module_comm,
-                                    const WASMExport *export_global_type,
+                                    const WASMExport *export_,
                                     uint8 *out_val_type,
                                     bool *out_mutability);
 
 bool
 wasm_runtime_get_export_memory_type(const WASMModuleCommon *module_comm,
-                                    const WASMExport *export_memory_type,
+                                    const WASMExport *export_,
                                     uint32 *out_min_page,
                                     uint32 *out_max_page);
 
 bool
 wasm_runtime_get_export_table_type(const WASMModuleCommon *module_comm,
-                                   const WASMExport *export_table_type,
+                                   const WASMExport *export_,
                                    uint8 *out_elem_type,
                                    uint32 *out_min_size,
                                    uint32 *out_max_size);

+ 7 - 0
core/iwasm/compilation/aot.h

@@ -21,6 +21,10 @@ typedef InitializerExpression AOTInitExpr;
 typedef WASMType AOTFuncType;
 typedef WASMExport AOTExport;
 
+#if WASM_ENABLE_DEBUG_AOT != 0
+typedef void * dwar_extractor_handle_t;
+#endif
+
 typedef enum AOTIntCond {
   INT_EQZ = 0,
   INT_EQ,
@@ -251,6 +255,9 @@ typedef struct AOTCompData {
   uint32 aux_stack_size;
 
   WASMModule *wasm_module;
+#if WASM_ENABLE_DEBUG_AOT != 0
+  dwar_extractor_handle_t extractor;
+#endif
 } AOTCompData;
 
 typedef struct AOTNativeSymbol {

+ 19 - 0
core/iwasm/compilation/aot_compiler.c

@@ -32,6 +32,9 @@
 #include "../interpreter/wasm_opcode.h"
 #include <errno.h>
 
+#if WASM_ENABLE_DEBUG_AOT != 0
+#include "debug/dwarf_extractor.h"
+#endif
 
 #define CHECK_BUF(buf, buf_end, length) do {                \
   if (buf + length > buf_end) {                             \
@@ -153,6 +156,9 @@ aot_compile_func(AOTCompContext *comp_ctx, uint32 func_index)
   float32 f32_const;
   float64 f64_const;
   AOTFuncType *func_type = NULL;
+#if WASM_ENABLE_DEBUG_AOT != 0
+  LLVMMetadataRef location;
+#endif
 
   /* Start to translate the opcodes */
   LLVMPositionBuilderAtEnd(comp_ctx->builder,
@@ -160,6 +166,15 @@ aot_compile_func(AOTCompContext *comp_ctx, uint32 func_index)
                                    ->llvm_entry_block);
   while (frame_ip < frame_ip_end) {
     opcode = *frame_ip++;
+
+#if WASM_ENABLE_DEBUG_AOT != 0
+    location = dwarf_gen_location(
+      comp_ctx, func_ctx,
+      (frame_ip - 1) - comp_ctx->comp_data->wasm_module->buf_code
+    );
+    LLVMSetCurrentDebugLocation2(comp_ctx->builder, location);
+#endif
+
     switch (opcode) {
       case WASM_OP_UNREACHABLE:
         if (!aot_compile_op_unreachable(comp_ctx, func_ctx, &frame_ip))
@@ -2440,6 +2455,10 @@ aot_compile_wasm(AOTCompContext *comp_ctx)
   errno = 0;
 #endif
 
+#if WASM_ENABLE_DEBUG_AOT != 0
+  LLVMDIBuilderFinalize(comp_ctx->debug_builder);
+#endif
+
   bh_print_time("Begin to verify LLVM module");
 
   ret = LLVMVerifyModule(comp_ctx->module, LLVMPrintMessageAction, &msg);

+ 25 - 13
core/iwasm/compilation/aot_emit_aot_file.c

@@ -1757,22 +1757,34 @@ aot_resolve_target_info(AOTCompContext *comp_ctx, AOTObjectData *obj_data)
 static bool
 aot_resolve_text(AOTObjectData *obj_data)
 {
-    LLVMSectionIteratorRef sec_itr;
-    char *name;
-
-    if (!(sec_itr = LLVMObjectFileCopySectionIterator(obj_data->binary))) {
-        aot_set_last_error("llvm get section iterator failed.");
-        return false;
+#if WASM_ENABLE_DEBUG_AOT != 0
+    LLVMBinaryType bin_type = LLVMBinaryGetType(obj_data->binary);
+    if (bin_type == LLVMBinaryTypeELF32L || bin_type == LLVMBinaryTypeELF64L) {
+        obj_data->text = (char *)LLVMGetBufferStart(obj_data->mem_buf);
+        obj_data->text_size = (uint32)LLVMGetBufferSize(obj_data->mem_buf);
     }
-    while (!LLVMObjectFileIsSectionIteratorAtEnd(obj_data->binary, sec_itr)) {
-        if ((name = (char *)LLVMGetSectionName(sec_itr)) && !strcmp(name, ".text")) {
-            obj_data->text = (char *)LLVMGetSectionContents(sec_itr);
-            obj_data->text_size = (uint32)LLVMGetSectionSize(sec_itr);
-            break;
+    else
+#endif
+    {
+        LLVMSectionIteratorRef sec_itr;
+        char *name;
+
+        if (!(sec_itr = LLVMObjectFileCopySectionIterator(obj_data->binary))) {
+            aot_set_last_error("llvm get section iterator failed.");
+            return false;
         }
-        LLVMMoveToNextSection(sec_itr);
+        while (
+          !LLVMObjectFileIsSectionIteratorAtEnd(obj_data->binary, sec_itr)) {
+            if ((name = (char *)LLVMGetSectionName(sec_itr))
+                && !strcmp(name, ".text")) {
+                obj_data->text = (char *)LLVMGetSectionContents(sec_itr);
+                obj_data->text_size = (uint32)LLVMGetSectionSize(sec_itr);
+                break;
+            }
+            LLVMMoveToNextSection(sec_itr);
+        }
+        LLVMDisposeSectionIterator(sec_itr);
     }
-    LLVMDisposeSectionIterator(sec_itr);
 
     return true;
 }

+ 43 - 8
core/iwasm/compilation/aot_emit_control.c

@@ -8,6 +8,10 @@
 #include "../aot/aot_runtime.h"
 #include "../interpreter/wasm_loader.h"
 
+#if WASM_ENABLE_DEBUG_AOT != 0
+#include "debug/dwarf_extractor.h"
+#endif
+
 static char *block_name_prefix[] = { "block", "loop", "if" };
 static char *block_name_suffix[] = { "begin", "else", "end" };
 
@@ -158,14 +162,22 @@ handle_next_reachable_block(AOTCompContext *comp_ctx,
     uint8 *frame_ip = NULL;
     uint32 i;
     AOTFuncType *func_type;
+    LLVMValueRef ret;
+#if WASM_ENABLE_DEBUG_AOT != 0
+    LLVMMetadataRef return_location;
+#endif
 
     aot_checked_addr_list_destroy(func_ctx);
-
     bh_assert(block);
 
+#if WASM_ENABLE_DEBUG_AOT != 0
+    return_location = dwarf_gen_location(
+      comp_ctx, func_ctx,
+      (*p_frame_ip - 1) - comp_ctx->comp_data->wasm_module->buf_code
+    );
+#endif
     if (block->label_type == LABEL_TYPE_IF
         && block->llvm_else_block
-        && !block->skip_wasm_code_else
         && *p_frame_ip <= block->wasm_code_else) {
         /* Clear value stack and start to translate else branch */
         aot_value_stack_destroy(&block->value_stack);
@@ -237,16 +249,23 @@ handle_next_reachable_block(AOTCompContext *comp_ctx,
     if (block->label_type == LABEL_TYPE_FUNCTION) {
         if (block->result_count) {
             /* Return the first return value */
-            if (!LLVMBuildRet(comp_ctx->builder, block->result_phis[0])) {
+            if (!(ret =
+                    LLVMBuildRet(comp_ctx->builder, block->result_phis[0]))) {
                 aot_set_last_error("llvm build return failed.");
                 goto fail;
             }
+#if WASM_ENABLE_DEBUG_AOT != 0
+            LLVMInstructionSetDebugLoc(ret, return_location);
+#endif
         }
         else {
-            if (!LLVMBuildRetVoid(comp_ctx->builder)) {
+            if (!(ret = LLVMBuildRetVoid(comp_ctx->builder))) {
                 aot_set_last_error("llvm build return void failed.");
                 goto fail;
             }
+#if WASM_ENABLE_DEBUG_AOT != 0
+            LLVMInstructionSetDebugLoc(ret, return_location);
+#endif
         }
     }
     aot_block_destroy(block);
@@ -381,7 +400,7 @@ aot_compile_op_block(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx,
     memset(block_addr_cache, 0, sizeof(block_addr_cache));
 
     /* Get block info */
-    if (!(wasm_loader_find_block_addr((BlockAddr*)block_addr_cache,
+    if (!(wasm_loader_find_block_addr(NULL, (BlockAddr*)block_addr_cache,
                                       *p_frame_ip, frame_ip_end, (uint8)label_type,
                                       &else_addr, &end_addr))) {
         aot_set_last_error("find block end addr failed.");
@@ -709,7 +728,7 @@ check_suspend_flags(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx)
 
     /* Move builder to terminate block */
     SET_BUILDER_POS(terminate_block);
-    if (!aot_build_zero_function_ret(comp_ctx, aot_func_type)) {
+    if (!aot_build_zero_function_ret(comp_ctx, func_ctx, aot_func_type)) {
         goto fail;
     }
 
@@ -1070,12 +1089,22 @@ aot_compile_op_return(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx,
 {
     AOTBlock *block_func = func_ctx->block_stack.block_list_head;
     LLVMValueRef value;
+    LLVMValueRef ret;
     AOTFuncType *func_type;
     uint32 i, param_index, result_index;
+#if WASM_ENABLE_DEBUG_AOT != 0
+    LLVMMetadataRef return_location;
+#endif
 
     bh_assert(block_func);
     func_type = func_ctx->aot_func->func_type;
 
+#if WASM_ENABLE_DEBUG_AOT != 0
+    return_location = dwarf_gen_location(
+      comp_ctx, func_ctx,
+      (*p_frame_ip - 1) - comp_ctx->comp_data->wasm_module->buf_code
+    );
+#endif
     if (block_func->result_count) {
         /* Store extra result values to function parameters */
         for (i = 0; i < block_func->result_count - 1; i++) {
@@ -1091,16 +1120,22 @@ aot_compile_op_return(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx,
         }
         /* Return the first result value */
         POP(value, block_func->result_types[0]);
-        if (!LLVMBuildRet(comp_ctx->builder, value)) {
+        if (!(ret = LLVMBuildRet(comp_ctx->builder, value))) {
             aot_set_last_error("llvm build return failed.");
             goto fail;
         }
+#if WASM_ENABLE_DEBUG_AOT != 0
+        LLVMInstructionSetDebugLoc(ret, return_location);
+#endif
     }
     else {
-        if (!LLVMBuildRetVoid(comp_ctx->builder)) {
+        if (!(ret = LLVMBuildRetVoid(comp_ctx->builder))) {
             aot_set_last_error("llvm build return void failed.");
             goto fail;
         }
+#if WASM_ENABLE_DEBUG_AOT != 0
+        LLVMInstructionSetDebugLoc(ret, return_location);
+#endif
     }
 
     return handle_next_reachable_block(comp_ctx, func_ctx, p_frame_ip);

+ 1 - 1
core/iwasm/compilation/aot_emit_exception.c

@@ -109,7 +109,7 @@ aot_emit_exception(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx,
 
         /* Create return IR */
         AOTFuncType *aot_func_type = func_ctx->aot_func->func_type;
-        if (!aot_build_zero_function_ret(comp_ctx, aot_func_type)) {
+        if (!aot_build_zero_function_ret(comp_ctx, func_ctx, aot_func_type)) {
             return false;
         }
 

+ 2 - 2
core/iwasm/compilation/aot_emit_function.c

@@ -35,7 +35,7 @@ create_func_return_block(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx)
 
         /* Create return IR */
         LLVMPositionBuilderAtEnd(comp_ctx->builder, func_ctx->func_return_block);
-        if (!aot_build_zero_function_ret(comp_ctx, aot_func_type)) {
+        if (!aot_build_zero_function_ret(comp_ctx, func_ctx, aot_func_type)) {
             return false;
         }
     }
@@ -313,7 +313,7 @@ call_aot_alloc_frame_func(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx,
     /* If frame alloc failed, return this function
         so the runtime can catch the exception */
     LLVMPositionBuilderAtEnd(comp_ctx->builder, frame_alloc_fail);
-    if (!aot_build_zero_function_ret(comp_ctx, aot_func_type)) {
+    if (!aot_build_zero_function_ret(comp_ctx, func_ctx, aot_func_type)) {
         return false;
     }
 

+ 2 - 2
core/iwasm/compilation/aot_emit_memory.c

@@ -897,7 +897,7 @@ aot_compile_op_memory_init(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx,
     /* If memory.init failed, return this function
         so the runtime can catch the exception */
     LLVMPositionBuilderAtEnd(comp_ctx->builder, mem_init_fail);
-    if (!aot_build_zero_function_ret(comp_ctx, aot_func_type)) {
+    if (!aot_build_zero_function_ret(comp_ctx, func_ctx, aot_func_type)) {
         goto fail;
     }
 
@@ -1231,7 +1231,7 @@ aot_compile_op_atomic_wait(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx,
     /* If atomic wait failed, return this function
         so the runtime can catch the exception */
     LLVMPositionBuilderAtEnd(comp_ctx->builder, wait_fail);
-    if (!aot_build_zero_function_ret(comp_ctx, aot_func_type)) {
+    if (!aot_build_zero_function_ret(comp_ctx, func_ctx, aot_func_type)) {
         goto fail;
     }
 

+ 36 - 0
core/iwasm/compilation/aot_llvm.c

@@ -9,6 +9,10 @@
 #include "../aot/aot_runtime.h"
 #include "../aot/aot_intrinsic.h"
 
+#if WASM_ENABLE_DEBUG_AOT != 0
+#include "debug/dwarf_extractor.h"
+#endif
+
 LLVMTypeRef
 wasm_type_to_llvm_type(AOTLLVMTypes *llvm_types, uint8 wasm_type)
 {
@@ -634,6 +638,10 @@ aot_create_func_context(AOTCompData *comp_data, AOTCompContext *comp_ctx,
                                             func, aot_func_type)))
         goto fail;
 
+#if WASM_ENABLE_DEBUG_AOT != 0
+    func_ctx->debug_func = dwarf_gen_func_info(comp_ctx, func_ctx);
+#endif
+
     aot_block_stack_push(&func_ctx->block_stack, aot_block);
 
     /* Add local variables */
@@ -1462,6 +1470,29 @@ aot_create_comp_context(AOTCompData *comp_data,
         goto fail;
     }
 
+#if WASM_ENABLE_DEBUG_AOT != 0
+    if (!(comp_ctx->debug_builder = LLVMCreateDIBuilder(comp_ctx->module))) {
+        aot_set_last_error("create LLVM Debug Infor builder failed.");
+        goto fail;
+    }
+
+    LLVMAddModuleFlag(
+      comp_ctx->module, LLVMModuleFlagBehaviorWarning, "Debug Info Version",
+      strlen("Debug Info Version"),
+      LLVMValueAsMetadata(LLVMConstInt(LLVMInt32Type(), 3, false)));
+
+    comp_ctx->debug_file = dwarf_gen_file_info(comp_ctx);
+    if (!comp_ctx->debug_file) {
+        aot_set_last_error("dwarf generate file info failed");
+        goto fail;
+    }
+    comp_ctx->debug_comp_unit = dwarf_gen_comp_unit_info(comp_ctx);
+    if (!comp_ctx->debug_comp_unit) {
+        aot_set_last_error("dwarf generate compile unit info failed");
+        goto fail;
+    }
+#endif
+
     if (option->enable_bulk_memory)
         comp_ctx->enable_bulk_memory = true;
 
@@ -2213,6 +2244,7 @@ aot_checked_addr_list_destroy(AOTFuncContext *func_ctx)
 
 bool
 aot_build_zero_function_ret(AOTCompContext *comp_ctx,
+                            AOTFuncContext *func_ctx,
                             AOTFuncType *func_type)
 {
     LLVMValueRef ret = NULL;
@@ -2251,6 +2283,10 @@ aot_build_zero_function_ret(AOTCompContext *comp_ctx,
         aot_set_last_error("llvm build ret failed.");
         return false;
     }
+#if WASM_ENABLE_DEBUG_AOT != 0
+    LLVMMetadataRef return_location = dwarf_gen_func_ret_location(comp_ctx, func_ctx);
+    LLVMInstructionSetDebugLoc(ret, return_location);
+#endif
     return true;
 }
 

+ 12 - 0
core/iwasm/compilation/aot_llvm.h

@@ -25,6 +25,9 @@
 #include "llvm-c/Initialization.h"
 #include "llvm-c/Support.h"
 #endif
+#if WASM_ENABLE_DEBUG_AOT != 0
+#include "llvm-c/DebugInfo.h"
+#endif
 
 #ifdef __cplusplus
 extern "C" {
@@ -148,6 +151,9 @@ typedef struct AOTFuncContext {
   LLVMBasicBlockRef func_return_block;
   LLVMValueRef exception_id_phi;
   LLVMValueRef func_type_indexes;
+#if WASM_ENABLE_DEBUG_AOT != 0
+  LLVMMetadataRef debug_func;
+#endif
   LLVMValueRef locals[1];
 } AOTFuncContext;
 
@@ -245,6 +251,11 @@ typedef struct AOTCompContext {
   LLVMContextRef context;
   LLVMModuleRef module;
   LLVMBuilderRef builder;
+#if WASM_ENABLE_DEBUG_AOT
+  LLVMDIBuilderRef debug_builder;
+  LLVMMetadataRef debug_file;
+  LLVMMetadataRef debug_comp_unit;
+#endif
   LLVMTargetMachineRef target_machine;
   char *target_cpu;
   char target_arch[16];
@@ -407,6 +418,7 @@ aot_checked_addr_list_destroy(AOTFuncContext *func_ctx);
 
 bool
 aot_build_zero_function_ret(AOTCompContext *comp_ctx,
+                            AOTFuncContext *func_ctx,
                             AOTFuncType *func_type);
 
 LLVMValueRef

+ 530 - 0
core/iwasm/compilation/debug/dwarf_extractor.cpp

@@ -0,0 +1,530 @@
+/*
+ * Copyright (C) 2021 Ant Group.  All rights reserved.
+ * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+ */
+
+#include "lldb/API/SBBlock.h"
+#include "lldb/API/SBCompileUnit.h"
+#include "lldb/API/SBCommandReturnObject.h"
+#include "lldb/API/SBCommandInterpreter.h"
+#include "lldb/API/SBBreakpointLocation.h"
+#include "lldb/API/SBDebugger.h"
+#include "lldb/API//SBFunction.h"
+#include "lldb/API//SBModule.h"
+#include "lldb/API//SBProcess.h"
+#include "lldb/API//SBStream.h"
+#include "lldb/API//SBSymbol.h"
+#include "lldb/API//SBTarget.h"
+#include "lldb/API//SBThread.h"
+#include "lldb/API/SBDeclaration.h"
+
+#include "dwarf_extractor.h"
+#include "../aot_llvm.h"
+
+#include "bh_log.h"
+#include "../../aot/aot_runtime.h"
+
+#include "llvm/BinaryFormat/Dwarf.h"
+
+using namespace lldb;
+
+typedef struct dwar_extractor
+{
+    SBDebugger debugger;
+    SBTarget target;
+    SBModule module;
+
+} dwar_extractor;
+
+#define TO_HANDLE(extractor)  (dwar_extractor_handle_t)(extractor)
+
+#define TO_EXTACTOR(handle)  (dwar_extractor *)(handle)
+
+static bool is_debugger_initialized;
+
+dwar_extractor_handle_t
+create_dwarf_extractor(AOTCompData *comp_data, char * file_name)
+{
+    char *arch = NULL;
+    char *platform = NULL;
+    dwar_extractor * extractor = NULL;
+
+    //__attribute__((constructor)) may be better?
+    if (!is_debugger_initialized) {
+        SBError error = SBDebugger::InitializeWithErrorHandling();
+        if(error.Fail()) {
+            LOG_ERROR("Init Dwarf Debugger failed");
+            return TO_HANDLE(NULL);
+        }
+        is_debugger_initialized = true;
+    }
+
+    SBError error;
+    SBFileSpec exe_file_spec(file_name, true);
+
+    if (!(extractor = new dwar_extractor()) ) {
+        LOG_ERROR("Create Dwarf Extractor error: failed to allocate memory");
+        goto fail3;
+    }
+
+    extractor->debugger = SBDebugger::Create();
+    if (!extractor->debugger.IsValid()) {
+        LOG_ERROR("Create Dwarf Debugger failed");
+        goto fail2;
+    }
+
+    extractor->target = extractor->debugger.CreateTarget(
+      file_name, arch, platform, false, error);
+
+    if (!error.Success()) {
+        LOG_ERROR("Create Dwarf target failed:%s", error.GetCString());
+        goto fail1;
+    }
+
+    if (!extractor->target.IsValid()) {
+        LOG_ERROR("Create Dwarf target not valid");
+        goto fail1;
+    }
+
+    extractor->module = extractor->target.FindModule(exe_file_spec);
+    comp_data->extractor = TO_HANDLE(extractor);
+
+    return TO_HANDLE(extractor); 
+
+fail1:
+    SBDebugger::Destroy(extractor->debugger);
+
+fail2:
+    wasm_runtime_free(extractor);
+
+fail3:
+    return TO_HANDLE(NULL);
+}
+
+void
+destroy_dwarf_extractor(dwar_extractor_handle_t handle)
+{
+    dwar_extractor * extractor = TO_EXTACTOR(handle);
+    if (!extractor)
+        return;
+    extractor->debugger.DeleteTarget(extractor->target);
+    SBDebugger::Destroy(extractor->debugger);
+    delete extractor;
+    SBDebugger::Terminate();
+    is_debugger_initialized = false;
+}
+
+LLVMMetadataRef
+dwarf_gen_file_info(AOTCompContext *comp_ctx)
+{
+    dwar_extractor *extractor;
+    int units_number;
+    LLVMMetadataRef file_info = NULL;
+    const char *file_name;
+    const char *dir_name;
+
+    if (!(extractor = TO_EXTACTOR(comp_ctx->comp_data->extractor)))
+        return NULL;
+
+    units_number = extractor->module.GetNumCompileUnits();
+
+    if (units_number > 0) {
+        SBCompileUnit compile_unit =
+          extractor->module.GetCompileUnitAtIndex(0);
+        auto filespec = compile_unit.GetFileSpec();
+        file_name = filespec.GetFilename();
+        dir_name = filespec.GetDirectory();
+        if (file_name || dir_name) {
+            file_info = LLVMDIBuilderCreateFile(comp_ctx->debug_builder,
+                                                file_name, strlen(file_name),
+                                                dir_name, strlen(dir_name));
+        }
+    }
+    return file_info;
+}
+
+#if 0
+void
+dwarf_gen_mock_vm_info(AOTCompContext *comp_ctx)
+{
+    LLVMMetadataRef file_info = NULL;
+    LLVMMetadataRef comp_unit = NULL;
+    file_info = LLVMDIBuilderCreateFile(comp_ctx->debug_builder,
+                                        "ant_runtime_mock.c", 18, ".", 1);
+
+    comp_unit = LLVMDIBuilderCreateCompileUnit(
+      comp_ctx->debug_builder, LLVMDWARFSourceLanguageC, file_info,
+      "ant compiler", 12, 0, NULL, 0, 1, NULL, 0, LLVMDWARFEmissionFull, 0, 0,
+      0, "/", 1, "", 0);
+
+    LLVMTypeRef ParamTys[] = {
+        LLVMVoidType(),
+    };
+
+    LLVMTypeRef FuncTy = LLVMFunctionType(LLVMVoidType(), ParamTys, 0, 0);
+
+    LLVMValueRef Function =
+      LLVMAddFunction(comp_ctx->module, "ant_runtime_mock", FuncTy);
+
+    LLVMMetadataRef ParamTypes[0];
+    LLVMMetadataRef FunctionTy = LLVMDIBuilderCreateSubroutineType(
+      comp_ctx->debug_builder, file_info, ParamTypes, 0, LLVMDIFlagZero);
+
+    /* 0x0015 is subroutine_type */
+    LLVMMetadataRef ReplaceableFunctionMetadata =
+      LLVMDIBuilderCreateReplaceableCompositeType(
+        comp_ctx->debug_builder, 0x15, "ant_runtime_mock", 16, file_info,
+        file_info, 2, 0, 0, 0, LLVMDIFlagFwdDecl, "", 0);
+
+    LLVMMetadataRef FunctionMetadata = LLVMDIBuilderCreateFunction(
+      comp_ctx->debug_builder, file_info, "ant_runtime_mock", 16,
+      "ant_runtime_mock", 16, file_info, 2, FunctionTy, true, true, 2, LLVMDIFlagZero,
+      false);
+
+    LLVMMetadataReplaceAllUsesWith(ReplaceableFunctionMetadata,
+                                   FunctionMetadata);
+
+    LLVMSetSubprogram(Function, FunctionMetadata);
+
+    comp_ctx->vm_debug_comp_unit = comp_unit;
+    comp_ctx->vm_debug_file = file_info;
+    comp_ctx->vm_debug_func = FunctionMetadata;
+}
+#endif
+
+LLVMMetadataRef
+dwarf_gen_comp_unit_info(AOTCompContext *comp_ctx)
+{
+    dwar_extractor *extractor;
+    int units_number;
+    LLVMMetadataRef comp_unit = NULL;
+
+    if (!(extractor = TO_EXTACTOR(comp_ctx->comp_data->extractor)))
+        return NULL;
+
+    units_number = extractor->module.GetNumCompileUnits();
+
+    if (units_number > 0) {
+        SBCompileUnit compile_unit =
+          extractor->module.GetCompileUnitAtIndex(0);
+        auto lang_type = compile_unit.GetLanguage();
+
+        comp_unit = LLVMDIBuilderCreateCompileUnit(
+          comp_ctx->debug_builder, LLDB_TO_LLVM_LANG_TYPE(lang_type),
+          comp_ctx->debug_file, "ant compiler", 12, 0, NULL, 0, 1, NULL, 0,
+          LLVMDWARFEmissionFull, 0, 0, 0, "/", 1, "", 0);
+    }
+    return comp_unit;
+}
+
+
+bool
+dwarf_get_func_info(dwar_extractor_handle_t handle, uint64_t offset)
+{
+    dwar_extractor *extractor = TO_EXTACTOR(handle);
+    auto sbaddr = extractor->target.ResolveFileAddress(offset);
+    SBSymbolContext sc(
+                sbaddr.GetSymbolContext(eSymbolContextFunction));
+    if (sc.IsValid()) {
+        SBFunction function(sc.GetFunction());
+        if (function.IsValid()) {
+            
+        }
+    }
+}
+
+static LLVMDWARFTypeEncoding
+lldb_get_basic_type_encoding(BasicType basic_type)
+{
+    LLVMDWARFTypeEncoding  encoding = 0;
+    switch (basic_type)
+    {
+    case eBasicTypeUnsignedChar:
+        encoding = llvm::dwarf::DW_ATE_unsigned_char;
+        break;
+    case eBasicTypeSignedChar:
+        encoding = llvm::dwarf::DW_ATE_signed_char;
+        break;
+    case eBasicTypeUnsignedInt:
+    case eBasicTypeUnsignedLong:
+    case eBasicTypeUnsignedLongLong:
+    case eBasicTypeUnsignedWChar:
+    case eBasicTypeUnsignedInt128:
+    case eBasicTypeUnsignedShort:
+        encoding = llvm::dwarf::DW_ATE_unsigned;
+        break;
+    case eBasicTypeInt:
+    case eBasicTypeLong:
+    case eBasicTypeLongLong:
+    case eBasicTypeWChar:
+    case eBasicTypeInt128:
+    case eBasicTypeShort:
+        encoding = llvm::dwarf::DW_ATE_signed;
+        break;
+    case eBasicTypeBool:
+        encoding = llvm::dwarf::DW_ATE_boolean;
+        break;
+    case eBasicTypeHalf:
+    case eBasicTypeFloat:
+    case eBasicTypeDouble:
+    case eBasicTypeLongDouble:
+        encoding = llvm::dwarf::DW_ATE_float;
+        break;
+    default:
+        break;
+    }
+    return encoding;
+}
+
+static LLVMMetadataRef
+lldb_type_to_type_dbi(AOTCompContext *comp_ctx, SBType &type)
+{
+    LLVMMetadataRef type_info = NULL;
+    BasicType basic_type = type.GetBasicType();
+    uint64_t bit_size = type.GetByteSize() * 8;
+    LLVMDIBuilderRef DIB = comp_ctx->debug_builder;
+    LLVMDWARFTypeEncoding encoding;
+
+    if (basic_type != eBasicTypeInvalid) {
+        encoding = lldb_get_basic_type_encoding(basic_type);
+        type_info = LLVMDIBuilderCreateBasicType(
+          DIB, type.GetName(), strlen(type.GetName()), bit_size, encoding,
+          LLVMDIFlagZero);
+    }
+    else if (type.IsPointerType()) {
+        SBType pointee_type = type.GetPointeeType();
+        type_info = LLVMDIBuilderCreatePointerType(
+          DIB, lldb_type_to_type_dbi(comp_ctx, pointee_type), bit_size, 0, 0,
+          "", 0);
+    }
+
+    return type_info;
+}
+
+static LLVMMetadataRef
+lldb_function_to_function_dbi(AOTCompContext *comp_ctx, SBSymbolContext &sc, AOTFuncContext *func_ctx)
+{
+    SBFunction function(sc.GetFunction());
+    const char *function_name = function.GetName();
+    const char *link_name = function.GetName();
+    SBTypeList function_args = function.GetType().GetFunctionArgumentTypes();
+    SBType return_type = function.GetType().GetFunctionReturnType();
+    const size_t num_function_args = function_args.GetSize();
+    dwar_extractor *extractor;
+
+    if (!(extractor = TO_EXTACTOR(comp_ctx->comp_data->extractor)))
+        return NULL;
+    
+    
+    LLVMDIBuilderRef DIB = comp_ctx->debug_builder;
+    LLVMMetadataRef File = comp_ctx->debug_file;
+
+    LLVMMetadataRef ParamTypes[num_function_args + 1];
+
+
+    ParamTypes[0] = lldb_type_to_type_dbi(comp_ctx, return_type);
+
+    for (uint32_t function_arg_idx = 0; function_arg_idx < num_function_args;
+         ++function_arg_idx) {
+        SBType function_arg_type =
+            function_args.GetTypeAtIndex(function_arg_idx);
+
+        if (function_arg_type.IsValid()) {
+            ParamTypes[function_arg_idx + 1] = lldb_type_to_type_dbi(comp_ctx, function_arg_type);
+        }
+    }
+
+    LLVMMetadataRef FunctionTy =
+      LLVMDIBuilderCreateSubroutineType(DIB, File, ParamTypes, num_function_args + 1, LLVMDIFlagZero);
+    
+    auto line_entry = sc.GetLineEntry();
+    LLVMMetadataRef ReplaceableFunctionMetadata =
+      LLVMDIBuilderCreateReplaceableCompositeType(
+        DIB, 0x15, function_name, strlen(function_name), File, File,
+        line_entry.GetLine(), 0, 0, 0, LLVMDIFlagFwdDecl, "", 0);
+
+    LLVMMetadataRef FunctionMetadata =
+      LLVMDIBuilderCreateFunction(DIB, File, function_name, strlen(function_name), link_name, strlen(link_name),
+                                  File, line_entry.GetLine(), FunctionTy, true, true, line_entry.GetLine(), LLVMDIFlagZero, false);
+ 
+    LLVMMetadataReplaceAllUsesWith(ReplaceableFunctionMetadata, FunctionMetadata);
+
+    LLVMSetSubprogram(func_ctx->func, FunctionMetadata);
+
+    LLVMMetadataRef ParamExpression = LLVMDIBuilderCreateExpression(DIB, NULL, 0);
+    auto variable_list = function.GetBlock().GetVariables(extractor->target, true, false,false);
+    if (num_function_args != variable_list.GetSize())
+    {
+        LOG_ERROR("function args number dismatch!:value number=%d, function args=%d", variable_list.GetSize(), num_function_args);
+    }
+
+    LLVMMetadataRef ParamLocation = LLVMDIBuilderCreateDebugLocation(
+              comp_ctx->context, line_entry.GetLine(), 0, FunctionMetadata, NULL);
+    
+    //TODO:change to void *  or WasmExenv * ?
+    LLVMMetadataRef voidtype = LLVMDIBuilderCreateBasicType(DIB, "void", 4, 0, 0, LLVMDIFlagZero);
+    LLVMMetadataRef voidpionter = LLVMDIBuilderCreatePointerType(DIB, voidtype, 64, 0, 0, "void *", 6);
+
+    LLVMMetadataRef ParamVar = LLVMDIBuilderCreateParameterVariable(
+              DIB, FunctionMetadata, "exenv",
+              5, 1,
+              File, //starts form 1, and 1 is exenv,
+              line_entry.GetLine(), voidpionter, true,
+              LLVMDIFlagZero);
+    LLVMValueRef Param =
+              LLVMGetParam(func_ctx->func, 0);
+    LLVMBasicBlockRef block_curr =
+              LLVMGetEntryBasicBlock(func_ctx->func);
+    LLVMDIBuilderInsertDbgValueAtEnd(DIB, Param, ParamVar,
+                                             ParamExpression, ParamLocation,
+                                             block_curr);
+
+    for (uint32_t function_arg_idx = 0; function_arg_idx < variable_list.GetSize();
+         ++function_arg_idx) {
+        SBValue variable(variable_list.GetValueAtIndex(function_arg_idx));
+        if (variable.IsValid()) {
+            SBDeclaration dec(variable.GetDeclaration());
+            auto valtype = variable.GetType();
+            LLVMMetadataRef ParamLocation = LLVMDIBuilderCreateDebugLocation(
+              comp_ctx->context, dec.GetLine(), dec.GetColumn(),
+              FunctionMetadata, NULL);
+            LLVMMetadataRef ParamVar = LLVMDIBuilderCreateParameterVariable(
+              DIB, FunctionMetadata, variable.GetName(),
+              strlen(variable.GetName()), function_arg_idx + 1 + 1,
+              File, //starts form 1, and 1 is exenv,
+              dec.GetLine(), ParamTypes[function_arg_idx + 1], true,
+              LLVMDIFlagZero);
+            LLVMValueRef Param =
+              LLVMGetParam(func_ctx->func, function_arg_idx + 1);
+            LLVMDIBuilderInsertDbgValueAtEnd(DIB, Param, ParamVar,
+                                             ParamExpression, ParamLocation,
+                                             block_curr);
+        }
+    }
+    
+    return FunctionMetadata;
+}
+
+LLVMMetadataRef
+dwarf_gen_func_info(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx)
+{
+    LLVMMetadataRef func_info = NULL;
+    dwar_extractor *extractor;
+    uint64_t vm_offset;
+    AOTFunc *func = func_ctx->aot_func;
+
+
+    if (!(extractor = TO_EXTACTOR(comp_ctx->comp_data->extractor)))
+        return NULL;
+
+    // A code address in DWARF for WebAssembly is the offset of an
+    // instruction relative within the Code section of the WebAssembly file.
+    // For this reason Section::GetFileAddress() must return zero for the
+    // Code section. (refert to ObjectFileWasm.cpp)
+    vm_offset = func->code - comp_ctx->comp_data->wasm_module->buf_code;
+
+    auto sbaddr = extractor->target.ResolveFileAddress(vm_offset);
+    SBSymbolContext sc(
+                sbaddr.GetSymbolContext(eSymbolContextFunction | eSymbolContextLineEntry));
+    if (sc.IsValid()) {
+        SBFunction function(sc.GetFunction());
+        if (function.IsValid()) {
+            func_info = lldb_function_to_function_dbi(comp_ctx, sc, func_ctx);
+        }
+    }
+    return func_info;
+}
+
+void
+dwarf_get_func_name(AOTCompContext *comp_ctx,
+                    AOTFuncContext *func_ctx,
+                    char *name,
+                    int len)
+{
+    LLVMMetadataRef func_info = NULL;
+    dwar_extractor *extractor;
+    uint64_t vm_offset;
+    AOTFunc *func = func_ctx->aot_func;
+
+    name[0] = '\0';
+
+    if (!(extractor = TO_EXTACTOR(comp_ctx->comp_data->extractor)))
+        return ;
+
+    // A code address in DWARF for WebAssembly is the offset of an
+    // instruction relative within the Code section of the WebAssembly file.
+    // For this reason Section::GetFileAddress() must return zero for the
+    // Code section. (refert to ObjectFileWasm.cpp)
+    vm_offset = func->code - comp_ctx->comp_data->wasm_module->buf_code;
+
+    auto sbaddr = extractor->target.ResolveFileAddress(vm_offset);
+    SBSymbolContext sc(sbaddr.GetSymbolContext(eSymbolContextFunction
+                                               | eSymbolContextLineEntry));
+    if (sc.IsValid()) {
+        SBFunction function(sc.GetFunction());
+        if (function.IsValid()) {
+            bh_strcpy_s(name, len, function.GetName());
+        }
+    }
+}
+
+LLVMMetadataRef
+dwarf_gen_location(AOTCompContext *comp_ctx,
+                   AOTFuncContext *func_ctx,
+                   uint64_t vm_offset)
+{
+    LLVMMetadataRef location_info = NULL;
+    dwar_extractor *extractor;
+    AOTFunc *func = func_ctx->aot_func;
+
+    if (!(extractor = TO_EXTACTOR(comp_ctx->comp_data->extractor)))
+        return NULL;
+
+    auto sbaddr = extractor->target.ResolveFileAddress(vm_offset);
+    SBSymbolContext sc(sbaddr.GetSymbolContext(eSymbolContextFunction
+                                               | eSymbolContextLineEntry));
+    if (sc.IsValid()) {
+        //TODO:need to check if the vm_offset is belong to
+        SBFunction function(sc.GetFunction());
+        if (function.IsValid()) {
+            uint64_t start = func_ctx->aot_func->code
+                             - comp_ctx->comp_data->wasm_module->buf_code;
+            uint64_t end = func_ctx->aot_func->code
+                           - comp_ctx->comp_data->wasm_module->buf_code
+                           + func_ctx->aot_func->code_size;
+            if (function.GetStartAddress().GetOffset() <= start
+                && end <= function.GetEndAddress().GetOffset()) {
+                auto line_entry = sc.GetLineEntry();
+                location_info =
+                  LLVMDIBuilderCreateDebugLocation(
+                    comp_ctx->context, line_entry.GetLine(),
+                    line_entry.GetColumn(), func_ctx->debug_func, NULL);
+                 //LOG_VERBOSE("Gen the location l:%d, c:%d at %lx", line_entry.GetLine(), line_entry.GetColumn(), vm_offset);
+            } else 
+                LOG_WARNING("the offset and function is not matched");
+        }
+    }
+    return location_info;
+}
+
+LLVMMetadataRef
+dwarf_gen_func_ret_location(AOTCompContext *comp_ctx, AOTFuncContext *func_ctx)
+{
+    LLVMMetadataRef func_info = NULL;
+    dwar_extractor *extractor;
+    uint64_t vm_offset;
+    AOTFunc *func = func_ctx->aot_func;
+    LLVMMetadataRef location_info = NULL;
+
+    if (!(extractor = TO_EXTACTOR(comp_ctx->comp_data->extractor)))
+        return NULL;
+
+    // A code address in DWARF for WebAssembly is the offset of an
+    // instruction relative within the Code section of the WebAssembly file.
+    // For this reason Section::GetFileAddress() must return zero for the
+    // Code section. (refert to ObjectFileWasm.cpp)
+    vm_offset = (func->code + func->code_size -1) - comp_ctx->comp_data->wasm_module->buf_code;
+    location_info =  dwarf_gen_location(comp_ctx, func_ctx, vm_offset);
+
+    return location_info;
+}

+ 58 - 0
core/iwasm/compilation/debug/dwarf_extractor.h

@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2021 Ant Group.  All rights reserved.
+ * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+ */
+
+#ifndef _DWARF_EXTRACTOR_H_
+#define _DWARF_EXTRACTOR_H_
+
+#include "llvm-c/DebugInfo.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+typedef unsigned int LLDBLangType;
+#define LLDB_TO_LLVM_LANG_TYPE(lldb_lang_type)                                \
+    (LLVMDWARFSourceLanguage)(((lldb_lang_type) > 0 ? (lldb_lang_type)-1 : 1))
+
+struct AOTCompData;
+typedef struct AOTCompData *aot_comp_data_t;
+typedef void *dwar_extractor_handle_t;
+
+struct AOTCompContext;
+typedef struct AOTCompContext AOTCompContext;
+
+struct AOTFuncContext;
+
+typedef struct AOTFuncContext AOTFuncContext;
+dwar_extractor_handle_t
+create_dwarf_extractor(aot_comp_data_t comp_data, char *file_name);
+
+LLVMMetadataRef
+dwarf_gen_file_info(AOTCompContext *comp_ctx);
+
+LLVMMetadataRef
+dwarf_gen_comp_unit_info(AOTCompContext *comp_ctx);
+
+LLVMMetadataRef
+dwarf_gen_func_info(AOTCompContext *comp_ctx, AOTFuncContext  *func_ctx);
+
+LLVMMetadataRef
+dwarf_gen_location(AOTCompContext *comp_ctx,
+                   AOTFuncContext *func_ctx,
+                   uint64_t vm_offset);
+
+LLVMMetadataRef
+dwarf_gen_func_ret_location(AOTCompContext *comp_ctx,
+                            AOTFuncContext *func_ctx);
+
+void
+dwarf_get_func_name(AOTCompContext *comp_ctx,
+                    AOTFuncContext *func_ctx,
+                    char *name,
+                    int len);
+#ifdef __cplusplus
+}
+#endif
+
+#endif

+ 11 - 3
core/iwasm/compilation/iwasm_compl.cmake

@@ -2,9 +2,17 @@ set (IWASM_COMPL_DIR ${CMAKE_CURRENT_LIST_DIR})
 
 include_directories(${IWASM_COMPL_DIR})
 
-file (GLOB_RECURSE source_all
-                   ${IWASM_COMPL_DIR}/*.c
-                   ${IWASM_COMPL_DIR}/*.cpp)
+if (WAMR_BUILD_DEBUG_AOT EQUAL 1)
+    file (GLOB_RECURSE source_all
+                    ${IWASM_COMPL_DIR}/*.c
+                    ${IWASM_COMPL_DIR}/*.cpp)
+else()
+    file (GLOB source_all
+                    ${IWASM_COMPL_DIR}/simd/*.c
+                    ${IWASM_COMPL_DIR}/simd/*.cpp
+                    ${IWASM_COMPL_DIR}/*.c
+                    ${IWASM_COMPL_DIR}/*.cpp)
+endif()
 
 set (IWASM_COMPL_SOURCE ${source_all})
 

+ 6 - 0
core/iwasm/include/aot_export.h

@@ -26,6 +26,12 @@ aot_create_comp_data(void *wasm_module);
 void
 aot_destroy_comp_data(aot_comp_data_t comp_data);
 
+#if WASM_ENABLE_DEBUG_AOT != 0
+typedef void * dwar_extractor_handle_t;
+dwar_extractor_handle_t
+create_dwarf_extractor(aot_comp_data_t comp_data, char * file_name);
+#endif
+
 enum {
     AOT_FORMAT_FILE,
     AOT_OBJECT_FILE,

+ 5 - 0
core/iwasm/include/wasm_export.h

@@ -133,6 +133,11 @@ typedef struct RuntimeInitArgs {
     /* maximum thread number, only used when
         WASM_ENABLE_THREAD_MGR is defined */
     uint32_t max_thread_num;
+#if WASM_ENABLE_DEBUG_INTERP != 0
+    char ip_addr[128];
+    int platform_port;
+    int instance_port;
+#endif
 } RuntimeInitArgs;
 
 #ifndef WASM_VALKIND_T_DEFINED

+ 14 - 0
core/iwasm/interpreter/wasm.h

@@ -316,6 +316,13 @@ typedef struct StringNode {
     char *str;
 } StringNode, *StringList;
 
+#if WASM_ENABLE_DEBUG_INTERP != 0
+typedef struct WASMFastOPCodeNode {
+    struct WASMFastOPCodeNode *next;
+    uint64 offset;
+    uint8 orig_op;
+} WASMFastOPCodeNode;
+#endif
 struct WASMModule {
     /* Module type, for module loaded from WASM bytecode binary,
        this field is Wasm_Module_Bytecode;
@@ -404,6 +411,13 @@ struct WASMModule {
     bh_list import_module_list_head;
     bh_list *import_module_list;
 #endif
+#if WASM_ENABLE_DEBUG_INTERP != 0 || WASM_ENABLE_DEBUG_AOT != 0
+    bh_list fast_opcode_list;
+    uint8 *buf_code;
+    uint8 *load_addr;
+    uint64 load_size;
+    uint64 buf_code_size;
+#endif
 };
 
 typedef struct BlockType {

+ 76 - 5
core/iwasm/interpreter/wasm_interp_classic.c

@@ -12,6 +12,9 @@
 #if WASM_ENABLE_SHARED_MEMORY != 0
 #include "../common/wasm_shared_memory.h"
 #endif
+#if WASM_ENABLE_THREAD_MGR != 0 && WASM_ENABLE_DEBUG_INTERP != 0
+#include "../libraries/thread-mgr/thread_manager.h"
+#endif
 
 typedef int32 CellType_I32;
 typedef int64 CellType_I64;
@@ -848,27 +851,70 @@ wasm_interp_call_func_import(WASMModuleInstance *module_inst,
 #endif
 
 #if WASM_ENABLE_THREAD_MGR != 0
+#if WASM_ENABLE_DEBUG_INTERP != 0
+#define CHECK_SUSPEND_FLAGS() do {                                  \
+    if (IS_WAMR_TERM_SIG(exec_env->current_status->signal_flag)) {  \
+      return;                                                       \
+    }                                                               \
+    if (IS_WAMR_STOP_SIG(exec_env->current_status->signal_flag)) {  \
+      SYNC_ALL_TO_FRAME();                                          \
+      wasm_cluster_thread_stopped(exec_env);                        \
+      wasm_cluster_thread_waiting_run(exec_env);                    \
+    }                                                               \
+  } while (0)
+#else
 #define CHECK_SUSPEND_FLAGS() do {                      \
     if (exec_env->suspend_flags.flags != 0) {           \
         if (exec_env->suspend_flags.flags & 0x01) {     \
             /* terminate current thread */              \
             return;                                     \
         }                                               \
-        /* TODO: support suspend and breakpoint */      \
+        while (exec_env->suspend_flags.flags & 0x02){   \
+            /* suspend current thread */                \
+            os_cond_wait(&exec_env->wait_cond,          \
+                         &exec_env->wait_lock);         \
+        }                                               \
     }                                                   \
   } while (0)
 #endif
+#endif
 
 #if WASM_ENABLE_LABELS_AS_VALUES != 0
 
 #define HANDLE_OP(opcode) HANDLE_##opcode
 #define FETCH_OPCODE_AND_DISPATCH() goto *handle_table[*frame_ip++]
+
+#if WASM_ENABLE_THREAD_MGR != 0 && WASM_ENABLE_DEBUG_INTERP != 0
+#define HANDLE_OP_END()                                                       \
+    do {                                                                      \
+        while (exec_env->current_status->signal_flag == WAMR_SIG_SINGSTEP     \
+               && exec_env->current_status->step_count++ == 1) {              \
+            exec_env->current_status->step_count = 0;                         \
+            SYNC_ALL_TO_FRAME();                                              \
+            wasm_cluster_thread_stopped(exec_env);                            \
+            wasm_cluster_thread_waiting_run(exec_env);                        \
+        }                                                                     \
+        goto *handle_table[*frame_ip++];                                      \
+    } while (0)
+#else
 #define HANDLE_OP_END() FETCH_OPCODE_AND_DISPATCH()
+#endif
 
 #else   /* else of WASM_ENABLE_LABELS_AS_VALUES */
-
 #define HANDLE_OP(opcode) case opcode
+#if WASM_ENABLE_THREAD_MGR != 0 && WASM_ENABLE_DEBUG_INTERP != 0
+#define HANDLE_OP_END()                                                       \
+    if (exec_env->current_status->signal_flag == WAMR_SIG_SINGSTEP            \
+        && exec_env->current_status->step_count++ == 2) {                     \
+        exec_env->current_status->step_count = 0;                             \
+        SYNC_ALL_TO_FRAME();                                                  \
+        wasm_cluster_thread_stopped(exec_env);                                \
+        wasm_cluster_thread_waiting_run(exec_env);                            \
+    }                                                                         \
+    continue
+#else
 #define HANDLE_OP_END() continue
+#endif
 
 #endif  /* end of WASM_ENABLE_LABELS_AS_VALUES */
 
@@ -941,6 +987,16 @@ handle_op_block:
         else if (cache_items[1].start_addr == frame_ip) {
           end_addr = cache_items[1].end_addr;
         }
+#if WASM_ENABLE_DEBUG_INTERP != 0
+        else if (!wasm_loader_find_block_addr(exec_env,
+                                              (BlockAddr*)exec_env->block_addr_cache,
+                                              frame_ip, (uint8*)-1,
+                                              LABEL_TYPE_BLOCK,
+                                              &else_addr, &end_addr)) {
+          wasm_set_exception(module, "find block address failed");
+          goto got_exception;
+        }
+#endif
         else {
           end_addr = NULL;
         }
@@ -978,7 +1034,8 @@ handle_op_if:
           else_addr = cache_items[1].else_addr;
           end_addr = cache_items[1].end_addr;
         }
-        else if (!wasm_loader_find_block_addr((BlockAddr*)exec_env->block_addr_cache,
+        else if (!wasm_loader_find_block_addr(exec_env,
+                                              (BlockAddr*)exec_env->block_addr_cache,
                                               frame_ip, (uint8*)-1,
                                               LABEL_TYPE_IF,
                                               &else_addr, &end_addr)) {
@@ -1030,7 +1087,8 @@ handle_op_if:
 label_pop_csp_n:
         POP_CSP_N(depth);
         if (!frame_ip) { /* must be label pushed by WASM_OP_BLOCK */
-          if (!wasm_loader_find_block_addr((BlockAddr*)exec_env->block_addr_cache,
+          if (!wasm_loader_find_block_addr(exec_env,
+                                           (BlockAddr*)exec_env->block_addr_cache,
                                            (frame_csp - 1)->begin_addr, (uint8*)-1,
                                            LABEL_TYPE_BLOCK,
                                            &else_addr, &end_addr)) {
@@ -3178,7 +3236,17 @@ label_pop_csp_n:
         frame_sp = frame->sp;
         frame_csp = frame->csp;
         goto call_func_from_entry;
-
+#if WASM_ENABLE_DEBUG_INTERP != 0
+      HANDLE_OP (DEBUG_OP_BREAK):
+      {
+        wasm_cluster_thread_send_signal(exec_env, WAMR_SIG_TRAP);
+        exec_env->suspend_flags.flags |= 2;
+        frame_ip--;
+        SYNC_ALL_TO_FRAME();
+        CHECK_SUSPEND_FLAGS();
+        HANDLE_OP_END ();
+      }
+#endif
 #if WASM_ENABLE_LABELS_AS_VALUES == 0
       default:
         wasm_set_exception(module, "unsupported opcode");
@@ -3320,6 +3388,9 @@ label_pop_csp_n:
         PUSH_CSP(LABEL_TYPE_FUNCTION, cell_num, frame_ip_end - 1);
 
         wasm_exec_env_set_cur_frame(exec_env, (WASMRuntimeFrame*)frame);
+#if WASM_ENABLE_THREAD_MGR != 0
+        CHECK_SUSPEND_FLAGS();
+#endif
       }
       HANDLE_OP_END ();
     }

+ 133 - 13
core/iwasm/interpreter/wasm_loader.c

@@ -10,6 +10,9 @@
 #include "wasm_opcode.h"
 #include "wasm_runtime.h"
 #include "../common/wasm_native.h"
+#if WASM_ENABLE_DEBUG_INTERP != 0
+#include "../libraries/debug-engine/debug_engine.h"
+#endif
 
 /* Read a value of given type from the address pointed to by the given
    pointer and increase the pointer to the position just after the
@@ -2934,6 +2937,10 @@ load_from_sections(WASMModule *module, WASMSection *sections,
         if (section->section_type == SECTION_TYPE_CODE) {
             buf_code = section->section_body;
             buf_code_end = buf_code + section->section_body_size;
+#if WASM_ENABLE_DEBUG_INTERP != 0 || WASM_ENABLE_DEBUG_AOT != 0
+            module->buf_code = (uint8 *)buf_code;
+            module->buf_code_size = section->section_body_size;
+#endif
         }
         else if (section->section_type == SECTION_TYPE_FUNC) {
             buf_func = section->section_body;
@@ -3296,10 +3303,26 @@ create_module(char *error_buf, uint32 error_buf_size)
 
 #if WASM_ENABLE_MULTI_MODULE != 0
     module->import_module_list = &module->import_module_list_head;
+#endif
+#if WASM_ENABLE_DEBUG_INTERP != 0
+    bh_list_init(&module->fast_opcode_list);
 #endif
     return module;
 }
 
+#if WASM_ENABLE_DEBUG_INTERP != 0
+static void
+record_fast_op(WASMModule *module, uint8 * pos, uint8 orig_op)
+{
+    WASMFastOPCodeNode *fast_op = wasm_runtime_malloc(sizeof(WASMFastOPCodeNode));
+    if (fast_op) {
+        fast_op->offset = pos - module->load_addr;
+        fast_op->orig_op = orig_op;
+        bh_list_insert(&module->fast_opcode_list, fast_op);
+    }
+}
+#endif
+
 WASMModule *
 wasm_loader_load_from_sections(WASMSection *section_list,
                                char *error_buf, uint32 error_buf_size)
@@ -3493,6 +3516,11 @@ wasm_loader_load(const uint8 *buf, uint32 size, char *error_buf, uint32 error_bu
         return NULL;
     }
 
+#if WASM_ENABLE_DEBUG_INTERP != 0
+    module->load_addr = (uint8 *)buf;
+    module->load_size = size;
+#endif
+
     if (!load(buf, size, module, error_buf, error_buf_size)) {
         goto fail;
     }
@@ -3593,7 +3621,6 @@ wasm_loader_unload(WASMModule *module)
              */
             wasm_runtime_free(node);
             /*
-             *
              * the module file reading buffer will be released
              * in runtime_destroy()
              */
@@ -3601,12 +3628,21 @@ wasm_loader_unload(WASMModule *module)
         }
     }
 #endif
-
+#if WASM_ENABLE_DEBUG_INTERP != 0
+    WASMFastOPCodeNode *fast_opcode =
+        bh_list_first_elem(&module->fast_opcode_list);
+    while(fast_opcode) {
+        WASMFastOPCodeNode * next = bh_list_elem_next(fast_opcode);
+        wasm_runtime_free(fast_opcode);
+        fast_opcode = next;
+    }
+#endif
     wasm_runtime_free(module);
 }
 
 bool
-wasm_loader_find_block_addr(BlockAddr *block_addr_cache,
+wasm_loader_find_block_addr(WASMExecEnv *exec_env,
+                            BlockAddr *block_addr_cache,
                             const uint8 *start_addr,
                             const uint8 *code_end_addr,
                             uint8 label_type,
@@ -3638,7 +3674,9 @@ wasm_loader_find_block_addr(BlockAddr *block_addr_cache,
 
     while (p < code_end_addr) {
         opcode = *p++;
-
+#if WASM_ENABLE_DEBUG_INTERP != 0
+op_break_retry:
+#endif
         switch (opcode) {
             case WASM_OP_UNREACHABLE:
             case WASM_OP_NOP:
@@ -4156,6 +4194,31 @@ wasm_loader_find_block_addr(BlockAddr *block_addr_cache,
                 break;
             }
 #endif
+#if WASM_ENABLE_DEBUG_INTERP != 0
+            case DEBUG_OP_BREAK: {
+                WASMDebugInstance *debug_instance =
+                  wasm_exec_env_get_instance(exec_env);
+                char orignal_opcode[1];
+                uint64 size = 1;
+                WASMModuleInstance *module_inst =
+                  (WASMModuleInstance *)exec_env->module_inst;
+                uint64 offset = (p - 1) >= module_inst->module->load_addr
+                                  ? (p - 1) - module_inst->module->load_addr
+                                  : ~0;
+                if (debug_instance) {
+                    if (wasm_debug_instance_get_obj_mem(
+                          debug_instance, offset, orignal_opcode, &size)
+                        && size == 1) {
+                        LOG_VERBOSE("WASM loader find OP_BREAK , recover it "
+                                    "with  %02x: ",
+                                    orignal_opcode[0]);
+                        opcode = orignal_opcode[0];
+                        goto op_break_retry;
+                    }
+                }
+                break;
+            }
+#endif
 
             default:
                 return false;
@@ -6317,6 +6380,9 @@ handle_op_block_and_loop:
                      * to new extended opcode so that interpreter can resolve the
                      * block quickly.
                      */
+#if WASM_ENABLE_DEBUG_INTERP != 0
+                    record_fast_op(module, p - 2, *(p - 2));
+#endif
                     *(p - 2) = EXT_OP_BLOCK + (opcode - WASM_OP_BLOCK);
 #endif
                 }
@@ -7196,13 +7262,28 @@ handle_op_block_and_loop:
 #else
 #if (WASM_ENABLE_WAMR_COMPILER == 0) && (WASM_ENABLE_JIT == 0)
                 if (local_offset < 0x80) {
+#if WASM_ENABLE_DEBUG_INTERP != 0
+                    record_fast_op(module, p_org, *p_org);
+#endif
                     *p_org++ = EXT_OP_GET_LOCAL_FAST;
-                    if (is_32bit_type(local_type))
+                    if (is_32bit_type(local_type)) {
+#if WASM_ENABLE_DEBUG_INTERP != 0
+                        record_fast_op(module, p_org, *p_org);
+#endif
                         *p_org++ = (uint8)local_offset;
-                    else
+                    }
+                    else {
+#if WASM_ENABLE_DEBUG_INTERP != 0
+                        record_fast_op(module, p_org, *p_org);
+#endif
                         *p_org++ = (uint8)(local_offset | 0x80);
-                    while (p_org < p)
+                    }
+                    while (p_org < p) {
+#if WASM_ENABLE_DEBUG_INTERP != 0
+                        record_fast_op(module, p_org, *p_org);
+#endif
                         *p_org++ = WASM_OP_NOP;
+                    }
                 }
 #endif
 #endif
@@ -7254,13 +7335,28 @@ handle_op_block_and_loop:
 #else
 #if (WASM_ENABLE_WAMR_COMPILER == 0) && (WASM_ENABLE_JIT == 0)
                 if (local_offset < 0x80) {
+#if WASM_ENABLE_DEBUG_INTERP != 0
+                    record_fast_op(module, p_org, *p_org);
+#endif
                     *p_org++ = EXT_OP_SET_LOCAL_FAST;
-                    if (is_32bit_type(local_type))
+                    if (is_32bit_type(local_type)) {
+#if WASM_ENABLE_DEBUG_INTERP != 0
+                        record_fast_op(module, p_org, *p_org);
+#endif
                         *p_org++ = (uint8)local_offset;
-                    else
+                    }
+                    else {
+#if WASM_ENABLE_DEBUG_INTERP != 0
+                        record_fast_op(module, p_org, *p_org);
+#endif
                         *p_org++ = (uint8)(local_offset | 0x80);
-                    while (p_org < p)
+                    }
+                    while (p_org < p) {
+#if WASM_ENABLE_DEBUG_INTERP != 0
+                        record_fast_op(module, p_org, *p_org);
+#endif
                         *p_org++ = WASM_OP_NOP;
+                    }
                 }
 #endif
 #endif
@@ -7309,13 +7405,28 @@ handle_op_block_and_loop:
 #else
 #if (WASM_ENABLE_WAMR_COMPILER == 0) && (WASM_ENABLE_JIT == 0)
                 if (local_offset < 0x80) {
+#if WASM_ENABLE_DEBUG_INTERP != 0
+                    record_fast_op(module, p_org, *p_org);
+#endif
                     *p_org++ = EXT_OP_TEE_LOCAL_FAST;
-                    if (is_32bit_type(local_type))
+                    if (is_32bit_type(local_type)) {
+#if WASM_ENABLE_DEBUG_INTERP != 0
+                        record_fast_op(module, p_org, *p_org);
+#endif
                         *p_org++ = (uint8)local_offset;
-                    else
+                    }
+                    else {
+#if WASM_ENABLE_DEBUG_INTERP != 0
+                        record_fast_op(module, p_org, *p_org);
+#endif
                         *p_org++ = (uint8)(local_offset | 0x80);
-                    while (p_org < p)
+                    }
+                    while (p_org < p) {
+#if WASM_ENABLE_DEBUG_INTERP != 0
+                        record_fast_op(module, p_org, *p_org);
+#endif
                         *p_org++ = WASM_OP_NOP;
+                    }
                 }
 #endif
 #endif
@@ -7344,6 +7455,9 @@ handle_op_block_and_loop:
 #if (WASM_ENABLE_WAMR_COMPILER == 0) && (WASM_ENABLE_JIT == 0)
                 if (global_type == VALUE_TYPE_I64
                     || global_type == VALUE_TYPE_F64) {
+#if WASM_ENABLE_DEBUG_INTERP != 0
+                    record_fast_op(module, p_org, *p_org);
+#endif
                     *p_org = WASM_OP_GET_GLOBAL_64;
                 }
 #endif
@@ -7393,10 +7507,16 @@ handle_op_block_and_loop:
 #if WASM_ENABLE_FAST_INTERP == 0
                 if (global_type == VALUE_TYPE_I64
                     || global_type == VALUE_TYPE_F64) {
+#if WASM_ENABLE_DEBUG_INTERP != 0
+                    record_fast_op(module, p_org, *p_org);
+#endif
                     *p_org = WASM_OP_SET_GLOBAL_64;
                 }
                 else if (module->aux_stack_size > 0
                          && global_idx == module->aux_stack_top_global_index) {
+#if WASM_ENABLE_DEBUG_INTERP != 0
+                    record_fast_op(module, p_org, *p_org);
+#endif
                     *p_org = WASM_OP_SET_GLOBAL_AUX_STACK;
                 }
 #else /* else of WASM_ENABLE_FAST_INTERP */

+ 3 - 1
core/iwasm/interpreter/wasm_loader.h

@@ -62,8 +62,10 @@ wasm_loader_unload(WASMModule *module);
  *
  * @return true if success, false otherwise
  */
+
 bool
-wasm_loader_find_block_addr(BlockAddr *block_addr_cache,
+wasm_loader_find_block_addr(WASMExecEnv *exec_env,
+                            BlockAddr *block_addr_cache,
                             const uint8 *start_addr,
                             const uint8 *code_end_addr,
                             uint8 block_type,

+ 13 - 0
core/iwasm/interpreter/wasm_opcode.h

@@ -267,6 +267,10 @@ typedef enum WASMOpcode {
     EXT_OP_LOOP                   = 0xd4, /* loop with blocktype */
     EXT_OP_IF                     = 0xd5, /* if with blocktype */
 
+#if WASM_ENABLE_DEBUG_INTERP != 0
+    DEBUG_OP_BREAK                = 0xd6, /* debug break point */
+#endif
+
     /* Post-MVP extend op prefix */
     WASM_OP_MISC_PREFIX           = 0xfc,
     WASM_OP_SIMD_PREFIX           = 0xfd,
@@ -673,6 +677,14 @@ typedef enum WASMAtomicEXTOpcode {
 }
 #endif
 
+#if WASM_ENABLE_DEBUG_INTERP != 0
+#define DEF_DEBUG_BREAK_HANDLE(_name)                       \
+  _name[DEBUG_OP_BREAK] =                                   \
+    HANDLE_OPCODE (DEBUG_OP_BREAK);     /* 0xd6 */
+#else
+#define DEF_DEBUG_BREAK_HANDLE(_name)
+#endif
+
 /*
  * Macro used to generate computed goto tables for the C interpreter.
  */
@@ -900,6 +912,7 @@ do {                                                         \
     HANDLE_OPCODE (WASM_OP_MISC_PREFIX);     /* 0xfc */      \
   _name[WASM_OP_ATOMIC_PREFIX] =                             \
     HANDLE_OPCODE (WASM_OP_ATOMIC_PREFIX);   /* 0xfe */      \
+  DEF_DEBUG_BREAK_HANDLE(_name)                              \
 } while (0)
 #endif /* end of _WASM_OPCODE_H */
 

+ 30 - 1
core/iwasm/interpreter/wasm_runtime.c

@@ -16,6 +16,9 @@
 #if WASM_ENABLE_THREAD_MGR != 0
 #include "../libraries/thread-mgr/thread_manager.h"
 #endif
+#if WASM_ENABLE_DEBUG_INTERP != 0
+#include "../libraries/debug-engine/debug_engine.h"
+#endif
 
 static void
 set_error_buf(char *error_buf, uint32 error_buf_size, const char *string)
@@ -1826,14 +1829,30 @@ wasm_module_malloc(WASMModuleInstance *module_inst, uint32 size,
     }
     else if (module_inst->malloc_function
              && module_inst->free_function) {
+#if WASM_ENABLE_DEBUG_INTERP != 0
+        /* TODO: obviously, we can not create debug instance for
+         * module malloc here, so, just disable the engine here,
+         * it is strange, but we now are lack of ways to indicate
+         * which calls should not be debugged. And we have other
+         * execute_xxx_function may need to be taken care of
+         */
+        bool active = wasm_debug_get_engine_active();
+        wasm_debug_set_engine_active(false);
+#endif
         if (!execute_malloc_function(module_inst,
                                      module_inst->malloc_function,
                                      module_inst->retain_function,
                                      size, &offset)) {
+#if WASM_ENABLE_DEBUG_INTERP != 0
+            wasm_debug_set_engine_active(active);
+#endif
             return 0;
         }
+#if WASM_ENABLE_DEBUG_INTERP != 0
+        wasm_debug_set_engine_active(active);
+#endif
         /* If we use app's malloc function,
-            the default memory may be changed while memory growing */
+           the default memory may be changed while memory growing */
         memory = module_inst->default_memory;
         addr = offset ? memory->memory_data + offset : NULL;
     }
@@ -1915,9 +1934,19 @@ wasm_module_free(WASMModuleInstance *module_inst, uint32 ptr)
                  && module_inst->free_function
                  && memory->memory_data <= addr
                  && addr < memory->memory_data_end) {
+#if WASM_ENABLE_DEBUG_INTERP != 0
+            /*TODO: obviously, we can not create debug instance for module malloc here,
+            so, just disable the engine here, it is strange. the wasm's call should be
+            marshed to its own thread */
+            bool active = wasm_debug_get_engine_active();
+            wasm_debug_set_engine_active(false);
+#endif
             execute_free_function(module_inst,
                                   module_inst->free_function,
                                   ptr);
+#if WASM_ENABLE_DEBUG_INTERP != 0
+            wasm_debug_set_engine_active(active);
+#endif
         }
     }
 }

+ 1011 - 0
core/iwasm/libraries/debug-engine/debug_engine.c

@@ -0,0 +1,1011 @@
+/*
+ * Copyright (C) 2021 Ant Group.  All rights reserved.
+ * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+ */
+
+#include "debug_engine.h"
+
+#include "bh_log.h"
+#include "gdbserver.h"
+#include "platform_api_extension.h"
+#include "wasm_interp.h"
+#include "wasm_opcode.h"
+#include "wasm_runtime.h"
+
+static uint8 break_instr[] = { DEBUG_OP_BREAK };
+
+typedef struct WASMDebugEngine {
+    struct WASMDebugEngine *next;
+    WASMDebugControlThread *control_thread;
+    char ip_addr[128];
+    int platform_port;
+    int process_base_port;
+    bh_list debug_instance_list;
+    bool active;
+} WASMDebugEngine;
+
+static bool
+should_stop(WASMDebugControlThread *control_thread)
+{
+    return control_thread->status != RUNNING;
+}
+
+static void *
+control_thread_routine(void *arg)
+{
+    WASMDebugObject *debug_object = (WASMDebugObject *)arg;
+
+    LOG_WARNING("control thread of debug object %p start at %s:%d\n",
+                debug_object, debug_object->control_thread->ip_addr,
+                debug_object->control_thread->port);
+
+    debug_object->control_thread->server =
+      wasm_launch_gdbserver(debug_object->control_thread->ip_addr,
+                            debug_object->control_thread->port);
+    if (!debug_object->control_thread->server) {
+        LOG_ERROR("Failed to create debug server\n");
+        return NULL;
+    }
+
+    debug_object->control_thread->server->thread =
+      debug_object->control_thread;
+
+    while (true) {
+        os_mutex_lock(&debug_object->control_thread->wait_lock);
+        if (!should_stop(debug_object->control_thread)) {
+            if (!wasm_gdbserver_handle_packet(
+                  debug_object->control_thread->server))
+                debug_object->control_thread->status = STOPPED;
+        }
+        else {
+            os_mutex_unlock(&debug_object->control_thread->wait_lock);
+            break;
+        }
+        os_mutex_unlock(&debug_object->control_thread->wait_lock);
+    }
+
+    LOG_VERBOSE("control thread of debug object %p stop\n", debug_object);
+    return NULL;
+}
+
+static WASMDebugControlThread *
+wasm_debug_control_thread_create(WASMDebugObject *debug_object)
+{
+    WASMDebugControlThread *control_thread;
+
+    if (!(control_thread =
+            wasm_runtime_malloc(sizeof(WASMDebugControlThread)))) {
+        LOG_ERROR("WASM Debug Engine error: failed to allocate memory");
+        return NULL;
+    }
+
+    if (os_mutex_init(&control_thread->wait_lock) != 0)
+        goto fail;
+
+    if (os_cond_init(&control_thread->wait_cond) != 0)
+        goto fail1;
+
+    control_thread->status = RUNNING;
+
+    if (0 != os_thread_create(&control_thread->tid, control_thread_routine,
+                              debug_object, APP_THREAD_STACK_SIZE_MAX)) {
+        goto fail2;
+    }
+
+    return control_thread;
+
+fail2:
+    os_cond_destroy(&control_thread->wait_cond);
+fail1:
+    os_mutex_destroy(&control_thread->wait_lock);
+fail:
+    wasm_runtime_free(control_thread);
+    return NULL;
+}
+
+static void
+wasm_debug_control_thread_destroy(WASMDebugObject *debug_object)
+{
+    WASMDebugControlThread *control_thread = debug_object->control_thread;
+    LOG_VERBOSE("control thread of debug object %p stop at %s:%d\n",
+                debug_object, debug_object->control_thread->ip_addr,
+                debug_object->control_thread->port);
+    os_mutex_lock(&control_thread->wait_lock);
+    control_thread->status = STOPPED;
+    wasm_close_gdbserver(control_thread->server);
+    os_mutex_unlock(&control_thread->wait_lock);
+    os_cond_signal(&control_thread->wait_cond);
+    os_thread_join(control_thread->tid, NULL);
+    wasm_runtime_free(control_thread->server);
+
+    os_mutex_destroy(&control_thread->wait_lock);
+    os_cond_destroy(&control_thread->wait_cond);
+    wasm_runtime_free(control_thread);
+}
+
+static WASMDebugEngine *g_debug_engine;
+
+static WASMDebugEngine *
+wasm_debug_engine_create()
+{
+    WASMDebugEngine *engine;
+
+    if (!(engine = wasm_runtime_malloc(sizeof(WASMDebugEngine)))) {
+        LOG_ERROR("WASM Debug Engine error: failed to allocate memory");
+        return NULL;
+    }
+    memset(engine, 0, sizeof(WASMDebugEngine));
+
+    /* TODO: support Wasm platform in LLDB */
+    /*
+    engine->control_thread =
+        wasm_debug_control_thread_create((WASMDebugObject *)engine);
+    engine->control_thread->debug_engine = (WASMDebugObject *)engine;
+    engine->control_thread->debug_instance = NULL;
+    sprintf(engine->control_thread->ip_addr, "127.0.0.1");
+    engine->control_thread->port = 1234;
+    */
+
+    bh_list_init(&engine->debug_instance_list);
+    return engine;
+}
+
+bool
+wasm_debug_engine_init(char *ip_addr, int platform_port, int process_port)
+{
+    if (g_debug_engine == NULL)
+        g_debug_engine = wasm_debug_engine_create();
+
+    if (g_debug_engine) {
+        process_port -= 1;
+        g_debug_engine->platform_port =
+          platform_port > 0 ? platform_port : 1234;
+        g_debug_engine->process_base_port =
+          process_port > 0 ? process_port : 6169;
+        if (ip_addr)
+            sprintf(g_debug_engine->ip_addr, "%s", ip_addr);
+        else
+            sprintf(g_debug_engine->ip_addr, "%s", "127.0.0.1");
+        g_debug_engine->active = true;
+    }
+
+    return g_debug_engine != NULL ? true : false;
+}
+
+void
+wasm_debug_set_engine_active(bool active)
+{
+    if (g_debug_engine) {
+        g_debug_engine->active = active;
+    }
+}
+
+bool
+wasm_debug_get_engine_active(void)
+{
+    if (g_debug_engine) {
+        return g_debug_engine->active;
+    }
+    return false;
+}
+
+void
+wasm_debug_engine_destroy()
+{
+    if (g_debug_engine) {
+        wasm_runtime_free(g_debug_engine);
+        g_debug_engine = NULL;
+    }
+}
+
+/* A debug Instance is a debug "process" in gdb remote protocol
+   and bound to a runtime cluster */
+WASMDebugInstance *
+wasm_debug_instance_create(WASMCluster *cluster)
+{
+    WASMDebugInstance *instance;
+    WASMExecEnv *exec_env;
+
+    if (!g_debug_engine || !g_debug_engine->active) {
+        return NULL;
+    }
+
+    if (!(instance = wasm_runtime_malloc(sizeof(WASMDebugInstance)))) {
+        LOG_ERROR("WASM Debug Engine error: failed to allocate memory");
+        return NULL;
+    }
+    memset(instance, 0, sizeof(WASMDebugInstance));
+    instance->cluster = cluster;
+    instance->control_thread =
+      wasm_debug_control_thread_create((WASMDebugObject *)instance);
+    instance->control_thread->debug_engine = (WASMDebugObject *)g_debug_engine;
+    instance->control_thread->debug_instance = (WASMDebugObject *)instance;
+    strcpy(instance->control_thread->ip_addr, g_debug_engine->ip_addr);
+    instance->id = g_debug_engine->debug_instance_list.len + 1;
+
+    exec_env = bh_list_first_elem(&cluster->exec_env_list);
+
+    /* exec_evn is created, but handle may not be set yet. */
+    instance->current_tid = exec_env ? exec_env->handle : 0;
+
+    instance->control_thread->port =
+      g_debug_engine->process_base_port + instance->id;
+    bh_list_init(&instance->break_point_list);
+    bh_list_insert(&g_debug_engine->debug_instance_list, instance);
+    wasm_cluster_send_signal_all(instance->cluster, WAMR_SIG_STOP);
+    return instance;
+}
+
+static WASMDebugInstance *
+wasm_cluster_get_debug_instance(WASMDebugEngine *engine, WASMCluster *cluster)
+{
+    WASMDebugInstance *instance =
+      bh_list_first_elem(&engine->debug_instance_list);
+    while (instance) {
+        if (instance->cluster == cluster)
+            return instance;
+        instance = bh_list_elem_next(instance);
+    }
+    return instance;
+}
+
+void
+wasm_debug_instance_destroy(WASMCluster *cluster)
+{
+    WASMDebugInstance *instance = NULL;
+
+    if (!g_debug_engine) {
+        return;
+    }
+
+    instance = wasm_cluster_get_debug_instance(g_debug_engine, cluster);
+    if (instance) {
+        wasm_debug_control_thread_destroy((WASMDebugObject *)instance);
+        bh_list_remove(&g_debug_engine->debug_instance_list, instance);
+        wasm_runtime_free(instance);
+    }
+}
+
+static WASMExecEnv *
+wasm_debug_instance_get_current_env(WASMDebugInstance *instance)
+{
+    WASMExecEnv *exec_env = NULL;
+
+    if (instance) {
+        exec_env = bh_list_first_elem(&instance->cluster->exec_env_list);
+        while (exec_env) {
+            if (exec_env->handle == instance->current_tid)
+                break;
+            exec_env = bh_list_elem_next(exec_env);
+        }
+    }
+    return exec_env;
+}
+
+#if WASM_ENABLE_LIBC_WASI != 0
+bool
+wasm_debug_instance_get_current_object_name(WASMDebugInstance *instance,
+                                            char name_buffer[],
+                                            int len)
+{
+    WASMExecEnv *exec_env;
+    WASIArguments *wasi_args;
+    WASMModuleInstance *module_inst;
+
+    if (!instance)
+        return false;
+
+    exec_env = bh_list_first_elem(&instance->cluster->exec_env_list);
+    if (!exec_env)
+        return false;
+
+    module_inst = (WASMModuleInstance *)exec_env->module_inst;
+    wasi_args = &module_inst->module->wasi_args;
+    if (wasi_args && wasi_args->argc > 0) {
+        char *argv_name = wasi_args->argv[0];
+        int name_len = strlen(argv_name);
+        printf("the module name is %s\n", argv_name);
+        if (len - 1 >= name_len)
+            strcpy(name_buffer, argv_name);
+        else
+            strcpy(name_buffer, argv_name + (name_len + 1 - len));
+        return true;
+    }
+    return false;
+}
+#endif
+
+uint64
+wasm_debug_instance_get_pid(WASMDebugInstance *instance)
+{
+    if (instance != NULL) {
+        return (uint64)instance->id;
+    }
+    return (uint64)0;
+}
+
+uint64
+wasm_debug_instance_get_tid(WASMDebugInstance *instance)
+{
+    if (instance != NULL) {
+        return (uint64)instance->current_tid;
+    }
+    return (uint64)0;
+}
+
+int
+wasm_debug_instance_get_tids(WASMDebugInstance *instance,
+                             uint64 tids[], int len)
+{
+    WASMExecEnv *exec_env;
+    int i = 0;
+
+    if (!instance)
+        return 0;
+
+    exec_env = bh_list_first_elem(&instance->cluster->exec_env_list);
+    while (exec_env && i < len) {
+        tids[i++] = exec_env->handle;
+        exec_env = bh_list_elem_next(exec_env);
+    }
+    LOG_VERBOSE("find %d tids\n", i);
+    return i;
+}
+
+uint64
+wasm_debug_instance_wait_thread(WASMDebugInstance *instance,
+                                uint64 tid, uint32 *status)
+{
+    WASMExecEnv *exec_env;
+    WASMExecEnv *last_exec_env = NULL;
+
+    exec_env = bh_list_first_elem(&instance->cluster->exec_env_list);
+    while (exec_env) {
+        last_exec_env = exec_env;
+        if (instance->current_tid != 0
+            && last_exec_env->handle == instance->current_tid) {
+            break;
+        }
+        exec_env = bh_list_elem_next(exec_env);
+    }
+
+    if (last_exec_env) {
+        wasm_cluster_wait_thread_status(last_exec_env, status);
+        if (instance->current_tid == 0)
+            instance->current_tid = last_exec_env->handle;
+        return last_exec_env->handle;
+    }
+    else {
+        *status = ~0;
+        return 0;
+    }
+}
+
+void
+wasm_debug_instance_set_cur_thread(WASMDebugInstance *instance, uint64 tid)
+{
+    instance->current_tid = tid;
+}
+
+uint64
+wasm_debug_instance_get_pc(WASMDebugInstance *instance)
+{
+    WASMExecEnv *exec_env;
+
+    if (!instance)
+        return 0;
+
+    exec_env = wasm_debug_instance_get_current_env(instance);
+    if ((exec_env->cur_frame != NULL)
+        && (exec_env->cur_frame->ip != NULL)) {
+        WASMModuleInstance *module_inst =
+          (WASMModuleInstance *)exec_env->module_inst;
+        return WASM_ADDR(
+          WasmObj, instance->id,
+          (exec_env->cur_frame->ip - module_inst->module->load_addr));
+    }
+    return 0;
+}
+
+uint64
+wasm_debug_instance_get_load_addr(WASMDebugInstance *instance)
+{
+    WASMExecEnv *exec_env;
+
+    if (!instance)
+        return WASM_ADDR(WasmInvalid, 0, 0);
+
+    exec_env = bh_list_first_elem(&instance->cluster->exec_env_list);
+    if (exec_env) {
+        return WASM_ADDR(WasmObj, instance->id, 0);
+    }
+
+    return WASM_ADDR(WasmInvalid, 0, 0);
+}
+
+WASMDebugMemoryInfo *
+wasm_debug_instance_get_memregion(WASMDebugInstance *instance, uint64 addr)
+{
+    WASMDebugMemoryInfo *mem_info;
+    WASMExecEnv *exec_env;
+    WASMModuleInstance *module_inst;
+    WASMMemoryInstance *memory;
+    uint32 num_bytes_per_page;
+    uint32 linear_mem_size = 0;
+
+    if (!instance)
+        return NULL;
+
+    exec_env = bh_list_first_elem(&instance->cluster->exec_env_list);
+    if (!exec_env)
+        return NULL;
+
+    if (!(mem_info = wasm_runtime_malloc(sizeof(WASMDebugMemoryInfo)))) {
+        LOG_ERROR("WASM Debug Engine error: failed to allocate memory");
+        return NULL;
+    }
+    mem_info->start = WASM_ADDR(WasmInvalid, 0, 0);
+    mem_info->size = 0;
+    mem_info->name[0] = '\0';
+    mem_info->permisson[0] = '\0';
+
+    module_inst = (WASMModuleInstance *)exec_env->module_inst;
+
+    switch (WASM_ADDR_TYPE(addr)) {
+        case WasmObj:
+            if (WASM_ADDR_OFFSET(addr) < module_inst->module->load_size) {
+                mem_info->start = WASM_ADDR(WasmObj, instance->id, 0);
+                mem_info->size = module_inst->module->load_size;
+                sprintf(mem_info->name, "%s", "module");
+                sprintf(mem_info->permisson, "%s", "rx");
+            }
+            break;
+        case WasmMemory: {
+            memory = module_inst->default_memory;
+
+            if (memory) {
+                num_bytes_per_page = memory->num_bytes_per_page;
+                linear_mem_size = num_bytes_per_page * memory->cur_page_count;
+            }
+            if (WASM_ADDR_OFFSET(addr) < linear_mem_size) {
+                mem_info->start = WASM_ADDR(WasmMemory, instance->id, 0);
+                mem_info->size = linear_mem_size;
+                sprintf(mem_info->name, "%s", "memory");
+                sprintf(mem_info->permisson, "%s", "rw");
+            }
+            break;
+        }
+        default:
+            mem_info->start = WASM_ADDR(WasmInvalid, 0, 0);
+            mem_info->size = 0;
+    }
+    return mem_info;
+}
+
+void
+wasm_debug_instance_destroy_memregion(WASMDebugInstance *instance,
+                                      WASMDebugMemoryInfo *mem_info)
+{
+    wasm_runtime_free(mem_info);
+}
+
+bool
+wasm_debug_instance_get_obj_mem(WASMDebugInstance *instance,
+                                uint64 offset, char *buf, uint64 *size)
+{
+    WASMExecEnv *exec_env;
+    WASMModuleInstance *module_inst;
+
+    if (!instance)
+        return false;
+
+    exec_env = bh_list_first_elem(&instance->cluster->exec_env_list);
+    if (!exec_env)
+        return false;
+
+    module_inst = (WASMModuleInstance *)exec_env->module_inst;
+
+    if (offset + *size > module_inst->module->load_size) {
+        LOG_VERBOSE("wasm_debug_instance_get_data_mem size over flow!\n");
+        *size = module_inst->module->load_size >= offset
+                  ? module_inst->module->load_size - offset
+                  : 0;
+    }
+
+    bh_memcpy_s(buf, *size, module_inst->module->load_addr + offset, *size);
+
+    WASMDebugBreakPoint *breakpoint =
+      bh_list_first_elem(&instance->break_point_list);
+
+    while (breakpoint) {
+        if (offset <= breakpoint->addr && breakpoint->addr < offset + *size) {
+            bh_memcpy_s(buf + (breakpoint->addr - offset), sizeof(break_instr),
+                        &breakpoint->orignal_data, sizeof(break_instr));
+        }
+        breakpoint = bh_list_elem_next(breakpoint);
+    }
+
+    WASMFastOPCodeNode *fast_opcode =
+      bh_list_first_elem(&module_inst->module->fast_opcode_list);
+    while (fast_opcode) {
+        if (offset <= fast_opcode->offset
+            && fast_opcode->offset < offset + *size) {
+            *(uint8 *)(buf + (fast_opcode->offset - offset)) =
+              fast_opcode->orig_op;
+        }
+        fast_opcode = bh_list_elem_next(fast_opcode);
+    }
+
+    return true;
+}
+
+bool
+wasm_debug_instance_get_linear_mem(WASMDebugInstance *instance,
+                                   uint64 offset, char *buf, uint64 *size)
+{
+    WASMExecEnv *exec_env;
+    WASMModuleInstance *module_inst;
+    WASMMemoryInstance *memory;
+    uint32 num_bytes_per_page;
+    uint32 linear_mem_size;
+
+    if (!instance)
+        return false;
+
+    exec_env = wasm_debug_instance_get_current_env(instance);
+    if (!exec_env)
+        return false;
+
+    module_inst = (WASMModuleInstance *)exec_env->module_inst;
+    memory = module_inst->default_memory;
+    if (memory) {
+        num_bytes_per_page = memory->num_bytes_per_page;
+        linear_mem_size = num_bytes_per_page * memory->cur_page_count;
+        if (offset + *size > linear_mem_size) {
+            LOG_VERBOSE(
+              "wasm_debug_instance_get_linear_mem size over flow!\n");
+            *size = linear_mem_size >= offset ? linear_mem_size - offset : 0;
+        }
+        bh_memcpy_s(buf, *size, memory->memory_data + offset, *size);
+        return true;
+    }
+    return false;
+}
+
+bool
+wasm_debug_instance_set_linear_mem(WASMDebugInstance *instance,
+                                   uint64 offset, char *buf, uint64 *size)
+{
+    WASMExecEnv *exec_env;
+    WASMModuleInstance *module_inst;
+    WASMMemoryInstance *memory;
+    uint32 num_bytes_per_page;
+    uint32 linear_mem_size;
+
+    if (!instance)
+        return false;
+
+    exec_env = wasm_debug_instance_get_current_env(instance);
+    if (!exec_env)
+        return false;
+
+    module_inst = (WASMModuleInstance *)exec_env->module_inst;
+    memory = module_inst->default_memory;
+    if (memory) {
+        num_bytes_per_page = memory->num_bytes_per_page;
+        linear_mem_size = num_bytes_per_page * memory->cur_page_count;
+        if (offset + *size > linear_mem_size) {
+            LOG_VERBOSE(
+              "wasm_debug_instance_get_linear_mem size over flow!\n");
+            *size = linear_mem_size >= offset ? linear_mem_size - offset : 0;
+        }
+        bh_memcpy_s(memory->memory_data + offset, *size, buf, *size);
+        return true;
+    }
+    return false;
+}
+
+bool
+wasm_debug_instance_get_mem(WASMDebugInstance *instance,
+                            uint64 addr, char *buf, uint64 *size)
+{
+    switch (WASM_ADDR_TYPE(addr)) {
+        case WasmMemory:
+            return wasm_debug_instance_get_linear_mem(
+              instance, WASM_ADDR_OFFSET(addr), buf, size);
+            break;
+        case WasmObj:
+            return wasm_debug_instance_get_obj_mem(
+              instance, WASM_ADDR_OFFSET(addr), buf, size);
+            break;
+        default:
+            return false;
+    }
+}
+
+bool
+wasm_debug_instance_set_mem(WASMDebugInstance *instance,
+                            uint64 addr, char *buf, uint64 *size)
+{
+    switch (WASM_ADDR_TYPE(addr)) {
+        case WasmMemory:
+            return wasm_debug_instance_set_linear_mem(
+              instance, WASM_ADDR_OFFSET(addr), buf, size);
+            break;
+        case WasmObj:
+        default:
+            return false;
+    }
+}
+
+WASMDebugInstance *
+wasm_exec_env_get_instance(WASMExecEnv *exec_env)
+{
+    WASMDebugInstance *instance = NULL;
+    bh_assert(g_debug_engine);
+
+    instance = bh_list_first_elem(&g_debug_engine->debug_instance_list);
+    while (instance) {
+        if (instance->cluster == exec_env->cluster)
+            break;
+        instance = bh_list_elem_next(instance);
+    }
+    return instance;
+}
+
+int
+wasm_debug_instance_get_call_stack_pcs(WASMDebugInstance *instance,
+                                       uint64 tid, uint64 buf[], uint64 size)
+{
+    WASMExecEnv *exec_env;
+    struct WASMInterpFrame *frame;
+    uint64 i = 0;
+
+    if (!instance)
+        return 0;
+
+    exec_env = bh_list_first_elem(&instance->cluster->exec_env_list);
+    while (exec_env) {
+        if (exec_env->handle == tid) {
+            WASMModuleInstance *module_inst =
+              (WASMModuleInstance *)exec_env->module_inst;
+            frame = exec_env->cur_frame;
+            while (frame && i < size) {
+                if (frame->ip != NULL) {
+                    buf[i++] =
+                      WASM_ADDR(WasmObj, instance->id,
+                                (frame->ip - module_inst->module->load_addr));
+                }
+                frame = frame->prev_frame;
+            }
+            return i;
+        }
+        exec_env = bh_list_elem_next(exec_env);
+    }
+    return 0;
+}
+
+bool
+wasm_debug_instance_add_breakpoint(WASMDebugInstance *instance,
+                                   uint64 addr, uint64 length)
+{
+    WASMExecEnv *exec_env;
+    WASMModuleInstance *module_inst;
+    uint64 offset;
+
+    if (!instance)
+        return false;
+
+    exec_env = bh_list_first_elem(&instance->cluster->exec_env_list);
+    if (!exec_env)
+        return false;
+
+    module_inst = (WASMModuleInstance *)exec_env->module_inst;
+    if (WASM_ADDR_TYPE(addr) != WasmObj)
+        return false;
+
+    offset = WASM_ADDR_OFFSET(addr);
+
+    if (length >= sizeof(break_instr)) {
+        if (offset + sizeof(break_instr) <= module_inst->module->load_size) {
+            WASMDebugBreakPoint *breakpoint;
+            if (!(breakpoint =
+                    wasm_runtime_malloc(sizeof(WASMDebugBreakPoint)))) {
+                LOG_ERROR(
+                  "WASM Debug Engine error: failed to allocate memory");
+                return false;
+            }
+            breakpoint->addr = offset;
+            /* TODO: how to if more than one breakpoints are set
+                     at the same addr? */
+            bh_memcpy_s(&breakpoint->orignal_data,
+                        (uint32)sizeof(break_instr),
+                        module_inst->module->load_addr + offset,
+                        (uint32)sizeof(break_instr));
+
+            bh_memcpy_s(module_inst->module->load_addr + offset,
+                        (uint32)sizeof(break_instr),
+                        break_instr,
+                        (uint32)sizeof(break_instr));
+
+            bh_list_insert(&instance->break_point_list, breakpoint);
+            return true;
+        }
+    }
+    return false;
+}
+
+bool
+wasm_debug_instance_remove_breakpoint(WASMDebugInstance *instance,
+                                      uint64 addr, uint64 length)
+{
+    WASMExecEnv *exec_env;
+    WASMModuleInstance *module_inst;
+    uint64 offset;
+
+    if (!instance)
+        return false;
+
+    exec_env = bh_list_first_elem(&instance->cluster->exec_env_list);
+    if (!exec_env)
+        return false;
+
+    module_inst = (WASMModuleInstance *)exec_env->module_inst;
+
+    if (WASM_ADDR_TYPE(addr) != WasmObj)
+        return false;
+    offset = WASM_ADDR_OFFSET(addr);
+
+    if (length >= sizeof(break_instr)) {
+        if (offset + sizeof(break_instr) <= module_inst->module->load_size) {
+            WASMDebugBreakPoint *breakpoint =
+              bh_list_first_elem(&instance->break_point_list);
+            while (breakpoint) {
+                WASMDebugBreakPoint *next_break =
+                  bh_list_elem_next(breakpoint);
+                if (breakpoint->addr == offset) {
+                    /* TODO: how to if more than one breakpoints are set
+                       at the same addr? */
+                    bh_memcpy_s(module_inst->module->load_addr + offset,
+                                (uint32)sizeof(break_instr),
+                                &breakpoint->orignal_data,
+                                (uint32)sizeof(break_instr));
+                    bh_list_remove(&instance->break_point_list, breakpoint);
+                    wasm_runtime_free(breakpoint);
+                }
+                breakpoint = next_break;
+            }
+        }
+    }
+    return true;
+}
+
+bool
+wasm_debug_instance_continue(WASMDebugInstance *instance)
+{
+    WASMExecEnv *exec_env;
+
+    if (!instance)
+        return false;
+
+    exec_env = bh_list_first_elem(&instance->cluster->exec_env_list);
+    if (!exec_env)
+        return false;
+
+    while (exec_env) {
+        wasm_cluster_thread_continue(exec_env);
+        exec_env = bh_list_elem_next(exec_env);
+    }
+    return true;
+}
+
+bool
+wasm_debug_instance_kill(WASMDebugInstance *instance)
+{
+    WASMExecEnv *exec_env;
+
+    if (!instance)
+        return false;
+
+    exec_env = bh_list_first_elem(&instance->cluster->exec_env_list);
+    if (!exec_env)
+        return false;
+
+    while (exec_env) {
+        wasm_cluster_thread_send_signal(exec_env, WAMR_SIG_TERM);
+        exec_env = bh_list_elem_next(exec_env);
+    }
+    return true;
+}
+
+bool
+wasm_debug_instance_singlestep(WASMDebugInstance *instance, uint64 tid)
+{
+    WASMExecEnv *exec_env;
+
+    if (!instance)
+        return false;
+
+    exec_env = bh_list_first_elem(&instance->cluster->exec_env_list);
+    if (!exec_env)
+        return false;
+
+    while (exec_env) {
+        if (exec_env->handle == tid || tid == (uint64)~0) {
+            wasm_cluster_thread_send_signal(exec_env, WAMR_SIG_SINGSTEP);
+            wasm_cluster_thread_step(exec_env);
+        }
+        exec_env = bh_list_elem_next(exec_env);
+    }
+    return true;
+}
+
+bool
+wasm_debug_instance_get_local(WASMDebugInstance *instance,
+                              int frame_index, int local_index,
+                              char buf[], int *size)
+{
+    WASMExecEnv *exec_env;
+    struct WASMInterpFrame *frame;
+    WASMFunctionInstance *cur_func;
+    uint8 local_type = 0xFF;
+    uint32 local_offset;
+    int param_count;
+    int fi = 0;
+
+    if (!instance)
+        return false;
+
+    exec_env = wasm_debug_instance_get_current_env(instance);
+    if (!exec_env)
+        return false;
+
+    frame = exec_env->cur_frame;
+    while (frame && fi++ != frame_index) {
+        frame = frame->prev_frame;
+    }
+
+    if (!frame)
+        return false;
+    cur_func = frame->function;
+    if (!cur_func)
+        return false;
+
+    param_count = cur_func->param_count;
+    local_offset = cur_func->local_offsets[local_index];
+    if (local_index < param_count)
+        local_type = cur_func->param_types[local_index];
+    else if (local_index < cur_func->local_count + param_count)
+        local_type = cur_func->local_types[local_index - param_count];
+
+    switch (local_type) {
+        case VALUE_TYPE_I32:
+        case VALUE_TYPE_F32:
+            *size = 4;
+            bh_memcpy_s(buf, 4, (char *)(frame->lp + local_offset), 4);
+            break;
+        case VALUE_TYPE_I64:
+        case VALUE_TYPE_F64:
+            *size = 8;
+            bh_memcpy_s(buf, 8, (char *)(frame->lp + local_offset), 8);
+            break;
+        default:
+            *size = 0;
+            break;
+    }
+    return true;
+}
+
+bool
+wasm_debug_instance_get_global(WASMDebugInstance *instance,
+                               int frame_index, int global_index,
+                               char buf[], int *size)
+{
+    WASMExecEnv *exec_env;
+    struct WASMInterpFrame *frame;
+    WASMModuleInstance *module_inst;
+    WASMGlobalInstance *globals, *global;
+    uint8 *global_addr;
+    uint8 global_type = 0xFF;
+    uint8 *global_data;
+    int fi = 0;
+
+    if (!instance)
+        return false;
+
+    exec_env = wasm_debug_instance_get_current_env(instance);
+    if (!exec_env)
+        return false;
+
+    frame = exec_env->cur_frame;
+    while (frame && fi++ != frame_index) {
+        frame = frame->prev_frame;
+    }
+
+    if (!frame)
+        return false;
+
+    module_inst = (WASMModuleInstance *)exec_env->module_inst;
+    global_data = module_inst->global_data;
+    globals = module_inst->globals;
+    global = globals + global_index;
+
+#if WASM_ENABLE_MULTI_MODULE == 0
+    global_addr = global_data + global->data_offset;
+#else
+    global_addr = global->import_global_inst
+                    ? global->import_module_inst->global_data
+                        + global->import_global_inst->data_offset
+                    : global_data + global->data_offset;
+#endif
+    global_type = global->type;
+
+    switch (global_type) {
+        case VALUE_TYPE_I32:
+        case VALUE_TYPE_F32:
+            *size = 4;
+            bh_memcpy_s(buf, 4, (char *)(global_addr), 4);
+            break;
+        case VALUE_TYPE_I64:
+        case VALUE_TYPE_F64:
+            *size = 8;
+            bh_memcpy_s(buf, 8, (char *)(global_addr), 8);
+            break;
+        default:
+            *size = 0;
+            break;
+    }
+    return true;
+}
+
+uint64
+wasm_debug_instance_mmap(WASMDebugInstance *instance,
+                         uint32 size, int map_port)
+{
+    WASMExecEnv *exec_env;
+    WASMModuleInstance *module_inst;
+    uint32 offset;
+    void *native_addr;
+    (void)map_port;
+
+    if (!instance)
+        return 0;
+
+    exec_env = wasm_debug_instance_get_current_env(instance);
+    if (!exec_env)
+        return 0;
+
+    module_inst = (WASMModuleInstance *)exec_env->module_inst;
+
+    /* TODO: malloc in wasi libc maybe not be thread safe, we hope LLDB will
+             always ask for memory when threads stopped */
+    offset = wasm_runtime_module_malloc((wasm_module_inst_t)module_inst, size,
+                                        &native_addr);
+    if (!offset)
+        LOG_WARNING("the memory may be not enough for debug, try use larger "
+                    "--heap-size");
+    return WASM_ADDR(WasmMemory, 0, offset);
+}
+
+bool
+wasm_debug_instance_ummap(WASMDebugInstance *instance, uint64 addr)
+{
+    WASMExecEnv *exec_env;
+    WASMModuleInstance *module_inst;
+    uint32 offset;
+
+    if (!instance)
+        return false;
+
+    exec_env = wasm_debug_instance_get_current_env(instance);
+    if (!exec_env)
+        return false;
+
+    module_inst = (WASMModuleInstance *)exec_env->module_inst;
+    if (WASM_ADDR_TYPE(addr) == WasmMemory) {
+        offset = WASM_ADDR_OFFSET(addr);
+        wasm_runtime_module_free((wasm_module_inst_t)module_inst, offset);
+        return true;
+    }
+    return false;
+}

+ 12 - 0
core/iwasm/libraries/debug-engine/debug_engine.cmake

@@ -0,0 +1,12 @@
+# Copyright (C) 2021 Ant Group.  All rights reserved.
+# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+
+set (DEBUG_ENGINE_DIR ${CMAKE_CURRENT_LIST_DIR})
+
+add_definitions (-DWASM_ENABLE_DEBUG_INTERP=1)
+
+include_directories(${DEBUG_ENGINE_DIR})
+
+file (GLOB source_all ${DEBUG_ENGINE_DIR}/*.c)
+
+set (DEBUG_ENGINE_SOURCE ${source_all})

+ 193 - 0
core/iwasm/libraries/debug-engine/debug_engine.h

@@ -0,0 +1,193 @@
+/*
+ * Copyright (C) 2021 Ant Group.  All rights reserved.
+ * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+ */
+
+#ifndef _DEBUG_ENGINE_H
+#define _DEBUG_ENGINE_H
+
+#include "bh_list.h"
+#include "gdbserver.h"
+#include "thread_manager.h"
+
+typedef enum WASMDebugControlThreadStatus {
+    RUNNING,
+    STOPPED,
+} WASMDebugControlThreadStatus;
+
+struct WASMDebugObject;
+typedef struct WASMDebugControlThread {
+    WASMGDBServer *server;
+    korp_tid tid;
+    korp_mutex wait_lock;
+    korp_cond wait_cond;
+    char ip_addr[128];
+    int port;
+    WASMDebugControlThreadStatus status;
+    struct WASMDebugObject *debug_engine;
+    struct WASMDebugObject *debug_instance;
+} WASMDebugControlThread;
+
+typedef struct WASMDebugObject {
+    struct WASMDebugObject *next;
+    WASMDebugControlThread *control_thread;
+} WASMDebugObject;
+
+typedef struct WASMDebugBreakPoint {
+    struct WASMDebugBreakPoint *next;
+    uint64 addr;
+    uint64 orignal_data;
+} WASMDebugBreakPoint;
+
+typedef struct WASMDebugInstance {
+    struct WASMDebugInstance *next;
+    WASMDebugControlThread *control_thread;
+    bh_list break_point_list;
+    WASMCluster *cluster;
+    uint32 id;
+    korp_tid current_tid;
+} WASMDebugInstance;
+
+typedef enum WASMDebugEventKind {
+    BREAK_POINT_ADD,
+    BREAK_POINT_REMOVE
+} WASMDebugEventKind;
+
+typedef struct WASMDebugEvent {
+    WASMDebugEventKind kind;
+    unsigned char metadata[0];
+} WASMDebugEvent;
+
+typedef struct WASMDebugMemoryInfo {
+    uint64 start;
+    uint64 size;
+    char name[128];
+    char permisson[4];
+} WASMDebugMemoryInfo;
+
+typedef enum WasmAddressType {
+    WasmMemory = 0x00,
+    WasmObj = 0x01,
+    WasmInvalid = 0x03
+} WasmAddressType;
+
+#define WASM_ADDR(type, id, offset)                                         \
+    (((uint64)type << 62) | ((uint64)0 << 32) | ((uint64)offset << 0))
+
+#define WASM_ADDR_TYPE(addr)   (((addr)&0xC000000000000000) >> 62)
+#define WASM_ADDR_OFFSET(addr) (((addr)&0x00000000FFFFFFFF))
+
+#define INVALIED_ADDR (0xFFFFFFFFFFFFFFFF)
+
+WASMDebugInstance *
+wasm_debug_instance_create(WASMCluster *cluster);
+
+void
+wasm_debug_instance_destroy(WASMCluster *cluster);
+
+WASMDebugInstance *
+wasm_exec_env_get_instance(WASMExecEnv *exec_env);
+
+bool
+wasm_debug_engine_init(char *ip_addr, int platform_port, int process_port);
+
+void
+wasm_debug_engine_destroy();
+
+void
+wasm_debug_set_engine_active(bool active);
+
+bool
+wasm_debug_get_engine_active(void);
+
+
+uint64
+wasm_debug_instance_get_pid(WASMDebugInstance *instance);
+
+uint64
+wasm_debug_instance_get_tid(WASMDebugInstance *instance);
+
+int
+wasm_debug_instance_get_tids(WASMDebugInstance *instance,
+                             uint64 tids[], int len);
+
+void
+wasm_debug_instance_set_cur_thread(WASMDebugInstance *instance, uint64 tid);
+
+uint64
+wasm_debug_instance_get_pc(WASMDebugInstance *instance);
+
+uint64
+wasm_debug_instance_get_load_addr(WASMDebugInstance *instance);
+
+WASMDebugMemoryInfo *
+wasm_debug_instance_get_memregion(WASMDebugInstance *instance, uint64 addr);
+
+void
+wasm_debug_instance_destroy_memregion(WASMDebugInstance *instance,
+                                      WASMDebugMemoryInfo *mem_info);
+
+bool
+wasm_debug_instance_get_obj_mem(WASMDebugInstance *instance,
+                                uint64 addr, char *buf, uint64 *size);
+
+bool
+wasm_debug_instance_get_linear_mem(WASMDebugInstance *instance,
+                                   uint64 addr, char *buf, uint64 *size);
+
+bool
+wasm_debug_instance_get_mem(WASMDebugInstance *instance,
+                            uint64 addr, char *buf, uint64 *size);
+
+bool
+wasm_debug_instance_set_mem(WASMDebugInstance *instance,
+                            uint64 addr, char *buf, uint64 *size);
+
+int
+wasm_debug_instance_get_call_stack_pcs(WASMDebugInstance *instance,
+                                       uint64 tid, uint64 buf[], uint64 size);
+
+bool
+wasm_debug_instance_add_breakpoint(WASMDebugInstance *instance,
+                                   uint64 addr, uint64 length);
+
+bool
+wasm_debug_instance_remove_breakpoint(WASMDebugInstance *instance,
+                                      uint64 addr, uint64 length);
+
+bool
+wasm_debug_instance_continue(WASMDebugInstance *instance);
+
+bool
+wasm_debug_instance_kill(WASMDebugInstance *instance);
+
+uint64
+wasm_debug_instance_wait_thread(WASMDebugInstance *instance,
+                                uint64 tid, uint32 *status);
+
+bool
+wasm_debug_instance_singlestep(WASMDebugInstance *instance, uint64 tid);
+
+bool
+wasm_debug_instance_get_local(WASMDebugInstance *instance,
+                              int frame_index, int local_index,
+                              char buf[], int *size);
+
+bool
+wasm_debug_instance_get_global(WASMDebugInstance *instance,
+                               int frame_index, int global_index,
+                               char buf[], int *size);
+
+#if WASM_ENABLE_LIBC_WASI != 0
+bool
+wasm_debug_instance_get_current_object_name(WASMDebugInstance *instance,
+                                            char name_buffer[], int len);
+#endif
+
+uint64
+wasm_debug_instance_mmap(WASMDebugInstance *instance,
+                         uint32 size, int map_port);
+
+bool
+wasm_debug_instance_ummap(WASMDebugInstance *instance, uint64 addr);
+#endif

+ 177 - 0
core/iwasm/libraries/debug-engine/gdbserver.c

@@ -0,0 +1,177 @@
+/*
+ * Copyright (C) 2021 Ant Group.  All rights reserved.
+ * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+ */
+
+#include "gdbserver.h"
+
+#include <arpa/inet.h>
+#include <fcntl.h>
+#include <netinet/in.h>
+#include <netinet/tcp.h>
+#include <signal.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/poll.h>
+#include <sys/socket.h>
+#include <unistd.h>
+
+#include "bh_log.h"
+#include "handler.h"
+#include "packets.h"
+#include "utils.h"
+
+typedef void (*PacketHandler)(WASMGDBServer *server, char *payload);
+
+struct packet_handler_elem {
+    char request;
+    PacketHandler handler;
+};
+
+#define DEL_HANDLER(r, h) [r] = { .request = r, .handler = h }
+
+static struct packet_handler_elem packet_handler_table[255] = {
+    DEL_HANDLER('Q', handle_generay_set),
+    DEL_HANDLER('q', handle_generay_query),
+    DEL_HANDLER('v', handle_v_packet),
+    DEL_HANDLER('?', handle_threadstop_request),
+    DEL_HANDLER('H', handle_set_current_thread),
+    DEL_HANDLER('p', handle_get_register),
+    DEL_HANDLER('j', handle_get_json_request),
+    DEL_HANDLER('m', handle_get_read_memory),
+    DEL_HANDLER('M', handle_get_write_memory),
+    DEL_HANDLER('x', handle_get_read_binary_memory),
+    DEL_HANDLER('Z', handle_add_break),
+    DEL_HANDLER('z', handle_remove_break),
+    DEL_HANDLER('c', handle_continue_request),
+    DEL_HANDLER('k', handle_kill_request),
+    DEL_HANDLER('_', handle____request),
+};
+
+WASMGDBServer *
+wasm_launch_gdbserver(char *host, int port)
+{
+    int listen_fd = -1;
+    const int one = 1;
+    struct sockaddr_in addr;
+    int ret;
+    int sockt_fd = 0;
+
+    WASMGDBServer *server;
+
+    if (!(server = wasm_runtime_malloc(sizeof(WASMGDBServer)))) {
+        LOG_ERROR("wasm gdb server error: failed to allocate memory");
+        return NULL;
+    }
+
+    listen_fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
+    if (listen_fd < 0) {
+        LOG_ERROR("wasm gdb server error: socket() failed");
+        goto fail;
+    }
+
+    ret = fcntl(listen_fd, F_SETFD, FD_CLOEXEC);
+    if(ret < 0) {
+        LOG_ERROR("wasm gdb server error: fcntl() failed on setting FD_CLOEXEC");
+        goto fail;
+    }
+
+    ret = setsockopt(listen_fd, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one));
+    if (ret < 0) {
+        LOG_ERROR("wasm gdb server error: setsockopt() failed");
+        goto fail;
+    }
+
+    LOG_VERBOSE("Listening on %s:%d\n", host, port);
+    addr.sin_family = AF_INET;
+    addr.sin_addr.s_addr = inet_addr(host);
+    addr.sin_port = htons(port);
+
+    ret = bind(listen_fd, (struct sockaddr *)&addr, sizeof(addr));
+    if (ret < 0) {
+        LOG_ERROR("wasm gdb server error: bind() failed");
+        goto fail;
+    }
+
+    ret = listen(listen_fd, 1);
+    if (ret < 0) {
+        LOG_ERROR("wasm gdb server error: listen() failed");
+        goto fail;
+    }
+
+    server->listen_fd = listen_fd;
+
+    sockt_fd = accept(listen_fd, NULL, NULL);
+    if (sockt_fd < 0) {
+        LOG_ERROR("wasm gdb server error: accept() failed");
+        goto fail;
+    }
+    LOG_VERBOSE("accept gdb client");
+    server->socket_fd = sockt_fd;
+    server->noack = false;
+    return server;
+
+fail:
+    if (listen_fd > 0) {
+        shutdown(listen_fd, SHUT_RDWR);
+        close(listen_fd);
+    }
+    if (server)
+        wasm_runtime_free(server);
+    return NULL;
+}
+
+void
+wasm_close_gdbserver(WASMGDBServer *server)
+{
+    if (server->socket_fd > 0) {
+        shutdown(server->socket_fd, SHUT_RDWR);
+        close(server->socket_fd);
+    }
+    if (server->listen_fd > 0) {
+        shutdown(server->listen_fd, SHUT_RDWR);
+        close(server->listen_fd);
+    }
+}
+
+static inline void
+handler_packet(WASMGDBServer *server, char request, char *payload)
+{
+    if (packet_handler_table[(int)request].handler != NULL)
+        packet_handler_table[(int)request].handler(server, payload);
+}
+
+static void
+process_packet(WASMGDBServer *server)
+{
+    uint8_t *inbuf = server->pkt.buf;
+    int inbuf_size = server->pkt.end;
+    uint8_t *packetend_ptr = (uint8_t *)memchr(inbuf, '#', inbuf_size);
+    int packetend = packetend_ptr - inbuf;
+    bh_assert('$' == inbuf[0]);
+    char request = inbuf[1];
+    char *payload = (char *)&inbuf[2];
+    inbuf[packetend] = '\0';
+
+    uint8_t checksum = 0;
+    for (int i = 1; i < packetend; i++)
+        checksum += inbuf[i];
+    bh_assert(checksum
+              == (hex(inbuf[packetend + 1]) << 4 | hex(inbuf[packetend + 2])));
+
+    LOG_VERBOSE("receive request:%c %s\n", request, payload);
+    handler_packet(server, request, payload);
+    inbuf_erase_head(server, packetend + 3);
+}
+
+bool
+wasm_gdbserver_handle_packet(WASMGDBServer *server)
+{
+    bool ret;
+    ret = read_packet(server);
+    if (ret)
+        process_packet(server);
+    return ret;
+}

+ 43 - 0
core/iwasm/libraries/debug-engine/gdbserver.h

@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2021 Ant Group.  All rights reserved.
+ * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+ */
+
+#ifndef _GDB_SERVER_H
+#define _GDB_SERVER_H
+
+#include <stdbool.h>
+
+#define PACKET_BUF_SIZE 0x8000
+
+enum GDBStoppointType {
+    eStoppointInvalid = -1,
+    eBreakpointSoftware = 0,
+    eBreakpointHardware,
+    eWatchpointWrite,
+    eWatchpointRead,
+    eWatchpointReadWrite
+};
+typedef struct WasmDebugPacket {
+    unsigned char buf[PACKET_BUF_SIZE];
+    unsigned int end;
+} WasmDebugPacket;
+
+struct WASMDebugControlThread;
+typedef struct WASMGDBServer {
+    int listen_fd;
+    int socket_fd;
+    WasmDebugPacket pkt;
+    bool noack;
+    struct WASMDebugControlThread *thread;
+} WASMGDBServer;
+
+WASMGDBServer *
+wasm_launch_gdbserver(char *addr, int port);
+
+void
+wasm_close_gdbserver(WASMGDBServer *server);
+
+bool
+wasm_gdbserver_handle_packet(WASMGDBServer *server);
+#endif

+ 598 - 0
core/iwasm/libraries/debug-engine/handler.c

@@ -0,0 +1,598 @@
+/*
+ * Copyright (C) 2021 Ant Group.  All rights reserved.
+ * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+ */
+
+#include <bh_log.h>
+#include <handler.h>
+#include <limits.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "debug_engine.h"
+#include "packets.h"
+#include "utils.h"
+#include "wasm_runtime.h"
+
+#define MAX_PACKET_SIZE (0x20000)
+static char tmpbuf[MAX_PACKET_SIZE];
+
+void
+handle_generay_set(WASMGDBServer *server, char *payload)
+{
+    const char *name;
+    char *args;
+
+    args = strchr(payload, ':');
+    if (args)
+        *args++ = '\0';
+
+    name = payload;
+    LOG_VERBOSE("%s:%s\n", __FUNCTION__, payload);
+
+    if (!strcmp(name, "StartNoAckMode")) {
+        server->noack = true;
+        write_packet(server, "OK");
+    }
+    if (!strcmp(name, "ThreadSuffixSupported")) {
+        write_packet(server, "");
+    }
+    if (!strcmp(name, "ListThreadsInStopReply")) {
+        write_packet(server, "");
+    }
+    if (!strcmp(name, "EnableErrorStrings")) {
+        write_packet(server, "OK");
+    }
+}
+
+static void
+process_xfer(WASMGDBServer *server, const char *name, char *args)
+{
+    const char *mode = args;
+
+    args = strchr(args, ':');
+    *args++ = '\0';
+
+    if (!strcmp(name, "libraries") && !strcmp(mode, "read")) {
+        //TODO: how to get current wasm file name?
+        uint64_t addr = wasm_debug_instance_get_load_addr(
+          (WASMDebugInstance *)server->thread->debug_instance);
+#if WASM_ENABLE_LIBC_WASI != 0
+        char objname[128];
+        wasm_debug_instance_get_current_object_name(
+          (WASMDebugInstance *)server->thread->debug_instance, objname, 128);
+        sprintf(tmpbuf,
+                "l<library-list><library name=\"%s\"><section "
+                "address=\"0x%lx\"/></library></library-list>",
+                objname, addr);
+#else
+        sprintf(tmpbuf,
+                "l<library-list><library name=\"%s\"><section "
+                "address=\"0x%lx\"/></library></library-list>",
+                "nobody.wasm", addr);
+#endif
+        write_packet(server, tmpbuf);
+    }
+}
+
+void
+porcess_wasm_local(WASMGDBServer *server, char *args)
+{
+    int frame_index;
+    int local_index;
+    char buf[16];
+    int size = 16;
+    bool ret;
+
+    sprintf(tmpbuf, "E01");
+    if (sscanf(args, "%d;%d", &frame_index, &local_index) == 2) {
+        ret = wasm_debug_instance_get_local(
+          (WASMDebugInstance *)server->thread->debug_instance, frame_index,
+          local_index, buf, &size);
+        if (ret && size > 0) {
+            mem2hex(buf, tmpbuf, size);
+        }
+    }
+    write_packet(server, tmpbuf);
+}
+
+void
+porcess_wasm_global(WASMGDBServer *server, char *args)
+{
+    int frame_index;
+    int global_index;
+    char buf[16];
+    int size = 16;
+    bool ret;
+
+    sprintf(tmpbuf, "E01");
+    if (sscanf(args, "%d;%d", &frame_index, &global_index) == 2) {
+        ret = wasm_debug_instance_get_global(
+          (WASMDebugInstance *)server->thread->debug_instance, frame_index,
+          global_index, buf, &size);
+        if (ret && size > 0) {
+            mem2hex(buf, tmpbuf, size);
+        }
+    }
+    write_packet(server, tmpbuf);
+}
+
+void
+handle_generay_query(WASMGDBServer *server, char *payload)
+{
+    const char *name;
+    char *args;
+
+    args = strchr(payload, ':');
+    if (args)
+        *args++ = '\0';
+    name = payload;
+    LOG_VERBOSE("%s:%s\n", __FUNCTION__, payload);
+
+    if (!strcmp(name, "C")) {
+        uint64_t pid, tid;
+        pid = wasm_debug_instance_get_pid(
+          (WASMDebugInstance *)server->thread->debug_instance);
+        tid = wasm_debug_instance_get_tid(
+          (WASMDebugInstance *)server->thread->debug_instance);
+        snprintf(tmpbuf, sizeof(tmpbuf), "QCp%lx.%lx", pid, tid);
+        write_packet(server, tmpbuf);
+    }
+    if (!strcmp(name, "Supported")) {
+        sprintf(tmpbuf, "qXfer:libraries:read+;PacketSize=%x;", MAX_PACKET_SIZE);
+        write_packet(server, tmpbuf);
+    }
+
+    if (!strcmp(name, "Xfer")) {
+        name = args;
+        args = strchr(args, ':');
+        *args++ = '\0';
+        process_xfer(server, name, args);
+    }
+
+    if (!strcmp(name, "HostInfo")) {
+        //Todo: change vendor to Intel for outside tree?
+        char triple[256];
+        mem2hex("wasm32-Ant-wasi-wasm", triple,
+                strlen("wasm32-Ant-wasi-wasm"));
+        sprintf(tmpbuf,
+                "vendor:Ant;ostype:wasi;arch:wasm32;"
+                "triple:%s;endian:little;ptrsize:4;",
+                triple);
+
+        write_packet(server, tmpbuf);
+    }
+    if (!strcmp(name, "GetWorkingDir")) {
+        if (getcwd(tmpbuf, PATH_MAX))
+            write_packet(server, tmpbuf);
+    }
+    if (!strcmp(name, "QueryGDBServer")) {
+        write_packet(server, "");
+    }
+    if (!strcmp(name, "VAttachOrWaitSupported")) {
+        write_packet(server, "");
+    }
+    if (!strcmp(name, "ProcessInfo")) {
+        //Todo: process id parent-pid
+        uint64_t pid;
+        pid = wasm_debug_instance_get_pid(
+          (WASMDebugInstance *)server->thread->debug_instance);
+        char triple[256];
+        //arch-vendor-os-env(format)
+        mem2hex("wasm32-Ant-wasi-wasm", triple,
+                strlen("wasm32-Ant-wasi-wasm"));
+        sprintf(tmpbuf,
+                "pid:%lx;parent-pid:%lx;vendor:Ant;ostype:wasi;arch:wasm32;"
+                "triple:%s;endian:little;ptrsize:4;",
+                pid, pid, triple);
+
+        write_packet(server, tmpbuf);
+    }
+    if (!strcmp(name, "RegisterInfo0")) {
+        sprintf(
+          tmpbuf,
+          "name:pc;alt-name:pc;bitsize:64;offset:0;encoding:uint;format:hex;"
+          "set:General Purpose Registers;gcc:16;dwarf:16;generic:pc;");
+        write_packet(server, tmpbuf);
+    }
+    else if (!strncmp(name, "RegisterInfo", strlen("RegisterInfo"))) {
+        write_packet(server, "E45");
+    }
+    if (!strcmp(name, "StructuredDataPlugins")) {
+        write_packet(server, "");
+    }
+
+    if (!strcmp(name, "MemoryRegionInfo")) {
+        uint64_t addr = strtol(args, NULL, 16);
+        WASMDebugMemoryInfo *mem_info = wasm_debug_instance_get_memregion(
+          (WASMDebugInstance *)server->thread->debug_instance, addr);
+        if (mem_info) {
+            char name[256];
+            mem2hex(mem_info->name, name, strlen(mem_info->name));
+            sprintf(tmpbuf, "start:%lx;size:%lx;permissions:%s;name:%s;",
+                    (uint64)mem_info->start, mem_info->size, mem_info->permisson, name);
+            write_packet(server, tmpbuf);
+            wasm_debug_instance_destroy_memregion(
+            (WASMDebugInstance *)server->thread->debug_instance, mem_info);
+        }
+    }
+
+    if (!strcmp(name, "WasmData")) {
+
+    }
+
+    if (!strcmp(name, "WasmMem")) {
+
+    }
+
+    if (!strcmp(name, "Symbol")) {
+        write_packet(server, "");
+    }
+
+    if (!strcmp(name, "WasmCallStack")) {
+        uint64_t tid = strtol(args, NULL, 16);
+        uint64_t buf[1024 / sizeof(uint64_t)];
+        uint64_t count = wasm_debug_instance_get_call_stack_pcs(
+          (WASMDebugInstance *)server->thread->debug_instance, tid, buf,
+          1024 / sizeof(uint64_t));
+        if (count > 0) {
+            mem2hex((char *)buf, tmpbuf, count * sizeof(uint64_t));
+            write_packet(server, tmpbuf);
+        }
+        else
+            write_packet(server, "");
+    }
+
+    if (!strcmp(name, "WasmLocal")) {
+        porcess_wasm_local(server, args);
+    }
+
+    if (!strcmp(name, "WasmGlobal")) {
+        porcess_wasm_global(server, args);
+    }
+}
+
+static void
+send_thread_stop_status(WASMGDBServer *server, uint32_t status, uint64_t tid)
+{
+    int tids_number, len = 0, i = 0;
+    uint64_t tids[20];
+    char pc_string[17];
+    uint32_t gdb_status = status;
+
+    if (status == 0) {
+        sprintf(tmpbuf, "W%02x", status);
+        write_packet(server, tmpbuf);
+        return;
+    }
+    tids_number = wasm_debug_instance_get_tids(
+      (WASMDebugInstance *)server->thread->debug_instance, tids, 20);
+    uint64_t pc = wasm_debug_instance_get_pc(
+      (WASMDebugInstance *)server->thread->debug_instance);
+
+    if (status == WAMR_SIG_SINGSTEP) {
+        gdb_status = WAMR_SIG_TRAP;
+    }
+
+    //TODO: how name a wasm thread?
+    len += sprintf(tmpbuf, "T%02xthread:%lx;name:%s;", gdb_status, tid, "nobody");
+    if (tids_number > 0) {
+        len += sprintf(tmpbuf + len, "threads:");
+        while (i < tids_number) {
+            if (i == tids_number - 1)
+                len += sprintf(tmpbuf + len, "%lx;", tids[i]);
+            else
+                len += sprintf(tmpbuf + len, "%lx,", tids[i]);
+            i++;
+        }
+    }
+    mem2hex((void *)&pc, pc_string, 8);
+    pc_string[8 * 2] = '\0';
+
+    if (status == WAMR_SIG_TRAP) {
+        len += sprintf(tmpbuf + len, "thread-pcs:%lx;00:%s,reason:%s;", pc,
+                       pc_string, "breakpoint");
+    }
+    else if (status == WAMR_SIG_SINGSTEP) {
+        len += sprintf(tmpbuf + len, "thread-pcs:%lx;00:%s,reason:%s;", pc,
+                       pc_string, "trace");
+    }
+    else if (status > 0) {
+        len += sprintf(tmpbuf + len, "thread-pcs:%lx;00:%s,reason:%s;", pc,
+                       pc_string, "signal");
+    }
+    write_packet(server, tmpbuf);
+}
+
+void
+handle_v_packet(WASMGDBServer *server, char *payload)
+{
+    const char *name;
+    char *args;
+    uint32_t status;
+    args = strchr(payload, ';');
+    if (args)
+        *args++ = '\0';
+    name = payload;
+    LOG_VERBOSE("%s:%s\n", __FUNCTION__, payload);
+
+    if (!strcmp("Cont?", name))
+        write_packet(server, "vCont;c;C;s;S;");
+
+    if (!strcmp("Cont", name)) {
+        if (args[0] == 's') {
+            char *numstring = strchr(args, ':');
+            if (numstring) {
+                *numstring++ = '\0';
+                uint64_t tid = strtol(numstring, NULL, 16);
+                wasm_debug_instance_set_cur_thread(
+                  (WASMDebugInstance *)server->thread->debug_instance, tid);
+                wasm_debug_instance_singlestep(
+                  (WASMDebugInstance *)server->thread->debug_instance, tid);
+                tid = wasm_debug_instance_wait_thread(
+                  (WASMDebugInstance *)server->thread->debug_instance, tid,
+                  &status);
+                send_thread_stop_status(server, status, tid);
+            }
+        }
+    }
+}
+
+void
+handle_threadstop_request(WASMGDBServer *server, char *payload)
+{
+    uint64_t tid = wasm_debug_instance_get_tid(
+      (WASMDebugInstance *)server->thread->debug_instance);
+    uint32_t status;
+
+    tid = wasm_debug_instance_wait_thread(
+      (WASMDebugInstance *)server->thread->debug_instance, tid, &status);
+
+    send_thread_stop_status(server, status, tid);
+}
+
+void
+handle_set_current_thread(WASMGDBServer *server, char *payload)
+{
+    LOG_VERBOSE("%s:%s\n", __FUNCTION__, payload, payload);
+    if ('g' == *payload++) {
+        uint64_t tid;
+        tid = strtol(payload, NULL, 16);
+        if (tid > 0)
+            wasm_debug_instance_set_cur_thread(
+              (WASMDebugInstance *)server->thread->debug_instance, tid);
+    }
+    write_packet(server, "OK");
+}
+
+void
+handle_get_register(WASMGDBServer *server, char *payload)
+{
+    int i = strtol(payload, NULL, 16);
+
+    if (i != 0) {
+        write_packet(server, "E01");
+        return;
+    }
+    uint64_t regdata = wasm_debug_instance_get_pc(
+      (WASMDebugInstance *)server->thread->debug_instance);
+    mem2hex((void *)&regdata, tmpbuf, 8);
+    tmpbuf[8 * 2] = '\0';
+    write_packet(server, tmpbuf);
+}
+
+void
+handle_get_json_request(WASMGDBServer *server, char *payload)
+{
+    char *args;
+
+    args = strchr(payload, ':');
+    if (args)
+        *args++ = '\0';
+    write_packet(server, "");
+}
+
+void
+handle_get_read_binary_memory(WASMGDBServer *server, char *payload)
+{
+    write_packet(server, "");
+}
+
+void
+handle_get_read_memory(WASMGDBServer *server, char *payload)
+{
+    size_t maddr, mlen;
+    bool ret;
+
+    sprintf(tmpbuf, "%s", "");
+    if (sscanf(payload, "%zx,%zx", &maddr, &mlen) == 2) {
+        if (mlen * 2 > MAX_PACKET_SIZE) {
+            LOG_ERROR("Buffer overflow!");
+            mlen = MAX_PACKET_SIZE / 2;
+        }
+        char *buff = wasm_runtime_malloc(mlen);
+        if (buff) {
+            ret = wasm_debug_instance_get_mem(
+              (WASMDebugInstance *)server->thread->debug_instance, maddr, buff,
+              &mlen);
+            if (ret) {
+                mem2hex(buff, tmpbuf, mlen);
+            }
+            wasm_runtime_free(buff);
+        }
+    }
+    write_packet(server, tmpbuf);
+}
+
+void
+handle_get_write_memory(WASMGDBServer *server, char *payload)
+{
+    size_t maddr, mlen, hex_len;
+    int offset, act_len;
+    char *buff;
+    bool ret;
+
+    sprintf(tmpbuf, "%s", "");
+    if (sscanf(payload, "%zx,%zx:%n", &maddr, &mlen, &offset) == 2) {
+        payload += offset;
+        hex_len = strlen(payload);
+        act_len = hex_len / 2 < mlen ? hex_len / 2 : mlen;
+        buff = wasm_runtime_malloc(act_len);
+        if (buff) {
+            hex2mem(payload, buff, act_len);
+            ret = wasm_debug_instance_set_mem(
+              (WASMDebugInstance *)server->thread->debug_instance, maddr, buff,
+              &mlen);
+            if (ret) {
+                sprintf(tmpbuf, "%s", "OK");
+            }
+            wasm_runtime_free(buff);
+        }
+    }
+    write_packet(server, tmpbuf);
+}
+
+void
+handle_add_break(WASMGDBServer *server, char *payload)
+{
+    size_t type, addr, length;
+
+    if (sscanf(payload, "%zx,%zx,%zx", &type, &addr, &length) == 3) {
+        if (type == eBreakpointSoftware) {
+            bool ret = wasm_debug_instance_add_breakpoint(
+              (WASMDebugInstance *)server->thread->debug_instance, addr,
+              length);
+            if (ret)
+                write_packet(server, "OK");
+            else
+                write_packet(server, "E01");
+            return;
+        }
+    }
+    write_packet(server, "");
+}
+
+void
+handle_remove_break(WASMGDBServer *server, char *payload)
+{
+    size_t type, addr, length;
+
+    if (sscanf(payload, "%zx,%zx,%zx", &type, &addr, &length) == 3) {
+        if (type == eBreakpointSoftware) {
+            bool ret = wasm_debug_instance_remove_breakpoint(
+              (WASMDebugInstance *)server->thread->debug_instance, addr,
+              length);
+            if (ret)
+                write_packet(server, "OK");
+            else
+                write_packet(server, "E01");
+            return;
+        }
+    }
+    write_packet(server, "");
+}
+
+void
+handle_continue_request(WASMGDBServer *server, char *payload)
+{
+    uint64_t tid;
+    uint32_t status;
+
+    wasm_debug_instance_continue(
+      (WASMDebugInstance *)server->thread->debug_instance);
+
+    tid = wasm_debug_instance_get_tid(
+      (WASMDebugInstance *)server->thread->debug_instance);
+
+    tid = wasm_debug_instance_wait_thread(
+      (WASMDebugInstance *)server->thread->debug_instance, tid, &status);
+
+    send_thread_stop_status(server, status, tid);
+}
+
+void
+handle_kill_request(WASMGDBServer *server, char *payload)
+{
+    uint64_t tid;
+    uint32_t status;
+
+    wasm_debug_instance_kill(
+      (WASMDebugInstance *)server->thread->debug_instance);
+
+    tid = wasm_debug_instance_get_tid(
+      (WASMDebugInstance *)server->thread->debug_instance);
+
+    tid = wasm_debug_instance_wait_thread(
+      (WASMDebugInstance *)server->thread->debug_instance, tid, &status);
+
+    send_thread_stop_status(server, status, tid);
+}
+
+static void
+handle_malloc(WASMGDBServer *server, char *payload)
+{
+    char *args;
+    uint64_t size;
+    int map_port = MMAP_PROT_NONE;
+    uint64_t addr;
+
+    sprintf(tmpbuf, "%s", "E03");
+
+    args = strstr(payload, ",");
+    if (args)
+        *args++ = '\0';
+
+    size = strtol(payload, NULL, 16);
+    if (size > 0) {
+        while (*args) {
+            if (*args == 'r') {
+                map_port |= MMAP_PROT_READ;
+            }
+            if (*args == 'w') {
+                map_port |= MMAP_PROT_WRITE;
+            }
+            if (*args == 'x') {
+                map_port |= MMAP_PROT_EXEC;
+            }
+            args++;
+        }
+        addr = wasm_debug_instance_mmap(
+          (WASMDebugInstance *)server->thread->debug_instance, size, map_port);
+        if (addr) {
+            sprintf(tmpbuf, "%lx", addr);
+        }
+    }
+    write_packet(server, tmpbuf);
+}
+
+static void
+handle_free(WASMGDBServer *server, char *payload)
+{
+    uint64_t addr;
+    bool ret;
+
+    sprintf(tmpbuf, "%s", "E03");
+    addr = strtol(payload, NULL, 16);
+
+    ret = wasm_debug_instance_ummap(
+      (WASMDebugInstance *)server->thread->debug_instance, addr);
+    if (ret) {
+        sprintf(tmpbuf, "%s", "OK");
+    }
+    write_packet(server, tmpbuf);
+}
+
+void
+handle____request(WASMGDBServer *server, char *payload)
+{
+    char *args;
+
+    if (payload[0] == 'M') {
+        args = payload + 1;
+        handle_malloc(server, args);
+    }
+    if (payload[0] == 'm') {
+        args = payload + 1;
+        handle_free(server, args);
+    }
+}

+ 55 - 0
core/iwasm/libraries/debug-engine/handler.h

@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2021 Ant Group.  All rights reserved.
+ * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+ */
+
+#ifndef HANDLER_H
+#define HANDLER_H
+
+#include "gdbserver.h"
+
+void
+handle_generay_set(WASMGDBServer *server, char *payload);
+
+void
+handle_generay_query(WASMGDBServer *server, char *payload);
+
+void
+handle_v_packet(WASMGDBServer *server, char *payload);
+
+void
+handle_threadstop_request(WASMGDBServer *server, char *payload);
+
+void
+handle_set_current_thread(WASMGDBServer *server, char *payload);
+
+void
+handle_get_register(WASMGDBServer *server, char *payload);
+
+void
+handle_get_json_request(WASMGDBServer *server, char *payload);
+
+void
+handle_get_read_binary_memory(WASMGDBServer *server, char *payload);
+
+void
+handle_get_read_memory(WASMGDBServer *server, char *payload);
+
+void
+handle_get_write_memory(WASMGDBServer *server, char *payload);
+
+void
+handle_add_break(WASMGDBServer *server, char *payload);
+
+void
+handle_remove_break(WASMGDBServer *server, char *payload);
+
+void
+handle_continue_request(WASMGDBServer *server, char *payload);
+
+void
+handle_kill_request(WASMGDBServer *server, char *payload);
+
+void
+handle____request(WASMGDBServer *server, char *payload);
+#endif

+ 173 - 0
core/iwasm/libraries/debug-engine/packets.c

@@ -0,0 +1,173 @@
+/*
+ * Copyright (C) 2021 Ant Group.  All rights reserved.
+ * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+ */
+
+#include "packets.h"
+
+#include <stdbool.h>
+
+#include "bh_log.h"
+#include "gdbserver.h"
+
+void
+pktbuf_insert(WASMGDBServer *gdbserver, const uint8_t *buf, ssize_t len)
+{
+    WasmDebugPacket *pkt = &gdbserver->pkt;
+
+    if ((unsigned long)(pkt->end + len) >= sizeof(pkt->buf)) {
+        LOG_ERROR("Packet buffer overflow");
+        exit(-2);
+    }
+    memcpy(pkt->buf + pkt->end, buf, len);
+    pkt->end += len;
+}
+
+void
+pktbuf_erase_head(WASMGDBServer *gdbserver, ssize_t end)
+{
+    WasmDebugPacket *pkt = &gdbserver->pkt;
+    memmove(pkt->buf, pkt->buf + end, pkt->end - end);
+    pkt->end -= end;
+}
+
+void
+inbuf_erase_head(WASMGDBServer *gdbserver, ssize_t end)
+{
+    pktbuf_erase_head(gdbserver, end);
+}
+
+void
+pktbuf_clear(WASMGDBServer *gdbserver)
+{
+    WasmDebugPacket *pkt = &gdbserver->pkt;
+    pkt->end = 0;
+}
+
+int
+read_data_once(WASMGDBServer *gdbserver)
+{
+    ssize_t nread;
+    uint8_t buf[4096];
+
+    nread = read(gdbserver->socket_fd, buf, sizeof(buf));
+    if (nread <= 0) {
+        LOG_ERROR("Connection closed");
+        return -1;
+    }
+    pktbuf_insert(gdbserver, buf, nread);
+    return nread;
+}
+
+void
+write_data_raw(WASMGDBServer *gdbserver, const uint8_t *data, ssize_t len)
+{
+    ssize_t nwritten;
+
+    nwritten = write(gdbserver->socket_fd, data, len);
+    if (nwritten < 0) {
+        LOG_ERROR("Write error\n");
+        exit(-2);
+    }
+}
+
+void
+write_hex(WASMGDBServer *gdbserver, unsigned long hex)
+{
+    char buf[32];
+    size_t len;
+
+    len = snprintf(buf, sizeof(buf) - 1, "%02lx", hex);
+    write_data_raw(gdbserver, (uint8_t *)buf, len);
+}
+
+void
+write_packet_bytes(WASMGDBServer *gdbserver,
+                   const uint8_t *data,
+                   size_t num_bytes)
+{
+    uint8_t checksum;
+    size_t i;
+
+    write_data_raw(gdbserver, (uint8_t *)"$", 1);
+    for (i = 0, checksum = 0; i < num_bytes; ++i)
+        checksum += data[i];
+    write_data_raw(gdbserver, (uint8_t *)data, num_bytes);
+    write_data_raw(gdbserver, (uint8_t *)"#", 1);
+    write_hex(gdbserver, checksum);
+}
+
+void
+write_packet(WASMGDBServer *gdbserver, const char *data)
+{
+    LOG_VERBOSE("send replay:%s", data);
+    write_packet_bytes(gdbserver, (const uint8_t *)data, strlen(data));
+}
+
+void
+write_binary_packet(WASMGDBServer *gdbserver,
+                    const char *pfx,
+                    const uint8_t *data,
+                    ssize_t num_bytes)
+{
+    uint8_t *buf;
+    ssize_t pfx_num_chars = strlen(pfx);
+    ssize_t buf_num_bytes = 0;
+    int i;
+
+    buf = malloc(2 * num_bytes + pfx_num_chars);
+    memcpy(buf, pfx, pfx_num_chars);
+    buf_num_bytes += pfx_num_chars;
+
+    for (i = 0; i < num_bytes; ++i) {
+        uint8_t b = data[i];
+        switch (b) {
+            case '#':
+            case '$':
+            case '}':
+            case '*':
+                buf[buf_num_bytes++] = '}';
+                buf[buf_num_bytes++] = b ^ 0x20;
+                break;
+            default:
+                buf[buf_num_bytes++] = b;
+                break;
+        }
+    }
+    write_packet_bytes(gdbserver, buf, buf_num_bytes);
+    free(buf);
+}
+
+bool
+skip_to_packet_start(WASMGDBServer *gdbserver)
+{
+    ssize_t end = -1;
+
+    for (size_t i = 0; i < gdbserver->pkt.end; ++i)
+        if (gdbserver->pkt.buf[i] == '$') {
+            end = i;
+            break;
+        }
+
+    if (end < 0) {
+        pktbuf_clear(gdbserver);
+        return false;
+    }
+
+    pktbuf_erase_head(gdbserver, end);
+    bh_assert(1 <= gdbserver->pkt.end);
+    bh_assert('$' == gdbserver->pkt.buf[0]);
+    return true;
+}
+
+bool
+read_packet(WASMGDBServer *gdbserver)
+{
+    while (!skip_to_packet_start(gdbserver)) {
+        if(read_data_once(gdbserver) < 0)
+            return false;
+    }
+    if (!gdbserver->noack)
+        write_data_raw(gdbserver, (uint8_t *)"+", 1);
+    return true;
+}

+ 22 - 0
core/iwasm/libraries/debug-engine/packets.h

@@ -0,0 +1,22 @@
+/*
+ * Copyright (C) 2021 Ant Group.  All rights reserved.
+ * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+ */
+
+#ifndef PACKETS_H
+#define PACKETS_H
+
+#include <stdint.h>
+#include <unistd.h>
+#include "gdbserver.h"
+
+bool
+read_packet(WASMGDBServer *gdbserver);
+
+void
+write_packet(WASMGDBServer *gdbserver, const char *data);
+
+void
+inbuf_erase_head(WASMGDBServer *gdbserver, ssize_t end);
+
+#endif

+ 45 - 0
core/iwasm/libraries/debug-engine/utils.c

@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2021 Ant Group.  All rights reserved.
+ * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+ */
+
+#include "utils.h"
+
+int
+hex(char ch)
+{
+    if ((ch >= 'a') && (ch <= 'f'))
+        return (ch - 'a' + 10);
+    if ((ch >= '0') && (ch <= '9'))
+        return (ch - '0');
+    if ((ch >= 'A') && (ch <= 'F'))
+        return (ch - 'A' + 10);
+    return (-1);
+}
+
+char *
+mem2hex(char *mem, char *buf, int count)
+{
+    unsigned char ch;
+
+    for (int i = 0; i < count; i++) {
+        ch = *(mem++);
+        *buf++ = hexchars[ch >> 4];
+        *buf++ = hexchars[ch % 16];
+    }
+    *buf = 0;
+    return (buf);
+}
+
+char *
+hex2mem(char *buf, char *mem, int count)
+{
+    unsigned char ch;
+
+    for (int i = 0; i < count; i++) {
+        ch = hex(*buf++) << 4;
+        ch = ch + hex(*buf++);
+        *(mem++) = ch;
+    }
+    return (mem);
+}

+ 23 - 0
core/iwasm/libraries/debug-engine/utils.h

@@ -0,0 +1,23 @@
+/*
+ * Copyright (C) 2021 Ant Group.  All rights reserved.
+ * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+ */
+
+#ifndef UTILS_H
+#define UTILS_H
+
+static const char hexchars[] = "0123456789abcdef";
+
+int
+hex(char ch);
+
+char *
+mem2hex(char *mem, char *buf, int count);
+
+char *
+hex2mem(char *buf, char *mem, int count);
+
+int
+unescape(char *msg, int len);
+
+#endif /* UTILS_H */

+ 124 - 2
core/iwasm/libraries/thread-mgr/thread_manager.c

@@ -484,6 +484,119 @@ fail1:
     return -1;
 }
 
+#if WASM_ENABLE_DEBUG_INTERP != 0
+WASMCurrentEnvStatus *
+wasm_cluster_create_exenv_status()
+{
+    WASMCurrentEnvStatus *status;
+
+    if (!(status = wasm_runtime_malloc(sizeof(WASMCurrentEnvStatus)))) {
+        goto fail;
+    }
+    if (os_mutex_init(&status->wait_lock) != 0)
+        goto fail1;
+
+    if (os_cond_init(&status->wait_cond) != 0)
+        goto fail2;
+    status->step_count = 0;
+    status->signal_flag = 0;
+    status->running_status = 0;
+    return status;
+
+fail2:
+    os_mutex_destroy(&status->wait_lock);
+fail1:
+    wasm_runtime_free(status);
+fail:
+    return NULL;
+}
+
+void
+wasm_cluster_destroy_exenv_status(WASMCurrentEnvStatus *status)
+{
+    os_mutex_destroy(&status->wait_lock);
+    os_cond_destroy(&status->wait_cond);
+    wasm_runtime_free(status);
+}
+
+inline static bool
+wasm_cluster_thread_is_running(WASMExecEnv *exec_env) {
+   return exec_env->current_status->running_status == STATUS_RUNNING
+          || exec_env->current_status->running_status == STATUS_STEP;
+}
+
+void
+wasm_cluster_clear_thread_signal(WASMExecEnv *exec_env)
+{
+    exec_env->current_status->signal_flag = 0;
+}
+
+void
+wasm_cluster_wait_thread_status(WASMExecEnv *exec_env, uint32 * status)
+{
+    os_mutex_lock(&exec_env->current_status->wait_lock);
+    while (wasm_cluster_thread_is_running(exec_env)) {
+        os_cond_wait(&exec_env->current_status->wait_cond,
+                     &exec_env->current_status->wait_lock);
+    }
+    *status = exec_env->current_status->signal_flag;
+    os_mutex_unlock(&exec_env->current_status->wait_lock);
+}
+
+void
+wasm_cluster_thread_send_signal(WASMExecEnv *exec_env, uint32 signo)
+{
+    exec_env->current_status->signal_flag = signo;
+}
+
+void
+wasm_cluster_thread_stopped(WASMExecEnv *exec_env)
+{
+    exec_env->current_status->running_status = STATUS_STOP;
+    os_cond_signal(&exec_env->current_status->wait_cond);
+}
+
+void
+wasm_cluster_thread_waiting_run(WASMExecEnv *exec_env)
+{
+    os_mutex_lock(&exec_env->wait_lock);
+    while (!wasm_cluster_thread_is_running(exec_env)) {
+        os_cond_wait(&exec_env->wait_cond, &exec_env->wait_lock);
+    }
+    os_mutex_unlock(&exec_env->wait_lock);
+}
+
+void
+wasm_cluster_send_signal_all(WASMCluster *cluster, uint32 signo)
+{
+    WASMExecEnv *exec_env = bh_list_first_elem(&cluster->exec_env_list);
+    while (exec_env) {
+        wasm_cluster_thread_send_signal(exec_env, signo);
+        exec_env = bh_list_elem_next(exec_env);
+    }
+}
+
+void wasm_cluster_thread_exited(WASMExecEnv *exec_env)
+{
+    exec_env->current_status->running_status = STATUS_EXIT;
+    os_cond_signal(&exec_env->current_status->wait_cond);
+
+}
+
+void wasm_cluster_thread_continue(WASMExecEnv *exec_env)
+{
+    wasm_cluster_clear_thread_signal(exec_env);
+    exec_env->current_status->running_status = STATUS_RUNNING;
+    os_cond_signal(&exec_env->wait_cond);
+}
+
+void wasm_cluster_thread_step(WASMExecEnv *exec_env)
+{
+    exec_env->current_status->running_status = STATUS_STEP;
+    os_cond_signal(&exec_env->wait_cond);
+}
+#endif
+
 int32
 wasm_cluster_join_thread(WASMExecEnv *exec_env, void **ret_val)
 {
@@ -520,7 +633,10 @@ wasm_cluster_exit_thread(WASMExecEnv *exec_env, void *retval)
 
     cluster = wasm_exec_env_get_cluster(exec_env);
     bh_assert(cluster);
-
+#if WASM_ENABLE_DEBUG_INTERP != 0
+    wasm_cluster_clear_thread_signal(exec_env);
+    wasm_cluster_thread_exited(exec_env);
+#endif
     /* App exit the thread, free the resources before exit native thread */
     /* Free aux stack space */
     free_aux_stack(cluster, exec_env->aux_stack_bottom.bottom);
@@ -537,8 +653,13 @@ int32
 wasm_cluster_cancel_thread(WASMExecEnv *exec_env)
 {
     /* Set the termination flag */
+#if WASM_ENABLE_DEBUG_INTERP != 0
+   wasm_cluster_thread_send_signal(exec_env, WAMR_SIG_TERM);
+   wasm_cluster_thread_exited(exec_env);
+#else
     exec_env->suspend_flags.flags |= 0x01;
-    return 0;
+#endif
+   return 0;
 }
 
 static void
@@ -621,6 +742,7 @@ void
 wasm_cluster_resume_thread(WASMExecEnv *exec_env)
 {
     exec_env->suspend_flags.flags &= ~0x02;
+    os_cond_signal(&exec_env->wait_cond);
 }
 
 static void

+ 56 - 0
core/iwasm/libraries/thread-mgr/thread_manager.h

@@ -15,7 +15,63 @@
 #ifdef __cplusplus
 extern "C" {
 #endif
+#if WASM_ENABLE_DEBUG_INTERP != 0
+#define WAMR_SIG_TRAP  (5)
+#define WAMR_SIG_STOP  (19)
+#define WAMR_SIG_TERM  (15)
+#define WAMR_SIG_SINGSTEP  (0x1ff)
 
+#define STATUS_RUNNING (0)
+#define STATUS_STOP    (1)
+#define STATUS_EXIT   (2)
+#define STATUS_STEP   (3)
+
+#define IS_WAMR_TERM_SIG(signo) \
+    ((signo) == WAMR_SIG_TERM)
+
+#define IS_WAMR_STOP_SIG(signo) \
+    ((signo) == WAMR_SIG_STOP || (signo) == WAMR_SIG_TRAP)
+
+typedef struct WASMCurrentEnvStatus
+{
+    uint64 signal_flag:32;
+    uint64 step_count:16;
+    uint64 running_status:16;
+    korp_mutex wait_lock;
+    korp_cond wait_cond;
+}WASMCurrentEnvStatus;
+
+WASMCurrentEnvStatus *
+wasm_cluster_create_exenv_status();
+
+void
+wasm_cluster_destroy_exenv_status(WASMCurrentEnvStatus *status);
+
+void
+wasm_cluster_send_signal_all(WASMCluster *cluster, uint32 signo);
+
+void
+wasm_cluster_thread_stopped(WASMExecEnv *exec_env);
+
+void
+wasm_cluster_thread_waiting_run(WASMExecEnv *exec_env);
+
+void
+wasm_cluster_wait_thread_status(WASMExecEnv *exec_env, uint32 * status);
+
+void
+wasm_cluster_thread_exited(WASMExecEnv *exec_env);
+
+void
+wasm_cluster_thread_continue(WASMExecEnv *exec_env);
+
+void
+wasm_cluster_thread_send_signal(WASMExecEnv *exec_env, uint32 signo);
+
+void
+wasm_cluster_thread_step(WASMExecEnv *exec_env);
+
+#endif
 typedef struct WASMCluster
 {
     struct WASMCluster *next;

+ 86 - 0
doc/source_debugging.md

@@ -0,0 +1,86 @@
+# WAMR source debugging
+
+WAMR supports source level debugging based on DWARF (normally used in C/C++/Rust), source map (normally used in AssemblyScript) is not supported.
+
+## Build wasm application with debug information
+To debug your application, you need to compile them with debug information. You can use `-g` option when compiling the source code if you are using wasi-sdk (also work for emcc and rustc):
+``` bash
+/opt/wasi-sdk/bin/clang -g test.c -o test.wasm
+```
+
+Then you will get `test.wasm` which is a WebAssembly module with embedded DWARF sections. Further, you can use `llvm-dwarfdump` to check if the generated wasm file contains DWARF information:
+``` bash
+llvm-dwarfdump-12 test.wasm
+```
+
+## Debugging with interpreter
+
+1. Build iwasm with source debugging feature
+``` bash
+cd ${WAMR_ROOT}/product-mini/platforms/linux
+mkdir build && cd build
+cmake .. -DWAMR_BUILD_DEBUG_INTERP=1
+make
+```
+
+2. Execute iwasm with debug engine enabled
+``` bash
+iwasm -g=127.0.0.1:1234 test.wasm
+```
+
+3. Build customized lldb (assume you have already built llvm)
+``` bash
+cd ${WAMR_ROOT}/core/deps/llvm
+git apply ../../../../build-scripts/lldb-wasm.patch
+mkdir build && cd build
+cmake ../llvm -DLLVM_ENABLE_PROJECTS="clang,lldb" -DLLVM_TARGETS_TO_BUILD:STRING="X86;WebAssembly"
+make -j $(nproc)
+```
+
+4. Launch customized lldb and connect to iwasm
+``` bash
+lldb
+(lldb) process connect -p wasm connect://127.0.0.1:1234
+```
+Then you can use lldb commands to debug your applications. Please refer to [lldb document](https://lldb.llvm.org/use/tutorial.html) for command usage.
+
+> Known issue: `step over` on some function may be treated as `step in`, it will be fixed later.
+
+## Debugging with AoT
+
+> Note: AoT debugging is experimental and only a few debugging capabilities are supported.
+
+1. Build lldb (assume you have already built llvm)
+``` bash
+cd ${WAMR_ROOT}/core/deps/llvm/build
+cmake . -DLLVM_ENABLE_PROJECTS="clang;lldb"
+make -j $(nproc)
+```
+
+2. Build wamrc with debugging feature
+``` bash
+cd ${WAMR_ROOT}/wamr-compiler
+mkdir build && cd build
+cmake .. -DWAMR_BUILD_DEBUG_AOT=1
+make -j $(nproc)
+```
+
+3. Build iwasm with debugging feature
+``` bash
+cd ${WAMR_ROOT}/product-mini/platforms/linux
+mkdir build && cd build
+cmake .. -DWAMR_BUILD_DEBUG_AOT=1
+make
+```
+
+4. Compile wasm module to AoT module
+``` bash
+wamrc -o test.aot test.wasm
+```
+
+5. Execute iwasm using lldb
+``` bash
+lldb-12 iwasm -- test.aot
+```
+
+Then you can use lldb commands to debug both wamr runtime and your wasm application in ***current terminal***

+ 11 - 0
product-mini/platforms/darwin/CMakeLists.txt

@@ -82,6 +82,17 @@ if (NOT DEFINED WAMR_BUILD_SIMD)
   set (WAMR_BUILD_SIMD 1)
 endif ()
 
+if (NOT DEFINED WAMR_BUILD_DEBUG_INTERP)
+  # Disable Debug feature by default
+  set (WAMR_BUILD_DEBUG_INTERP 0)
+endif ()
+
+if (WAMR_BUILD_DEBUG_INTERP EQUAL 1)
+  set (WAMR_BUILD_FAST_INTERP 0)
+  set (WAMR_BUILD_MINI_LOADER 0)
+  set (WAMR_BUILD_SIMD 0)
+endif ()
+
 set (CMAKE_SHARED_LINKER_FLAGS "-Wl,-U,_get_ext_lib_export_apis")
 set (CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS}")
 

+ 11 - 0
product-mini/platforms/linux/CMakeLists.txt

@@ -90,6 +90,17 @@ if (NOT DEFINED WAMR_BUILD_REF_TYPES)
   set (WAMR_BUILD_REF_TYPES 0)
 endif ()
 
+if (NOT DEFINED WAMR_BUILD_DEBUG_INTERP)
+  # Disable Debug feature by default
+  set (WAMR_BUILD_DEBUG_INTERP 0)
+endif ()
+
+if (WAMR_BUILD_DEBUG_INTERP EQUAL 1)
+  set (WAMR_BUILD_FAST_INTERP 0)
+  set (WAMR_BUILD_MINI_LOADER 0)
+  set (WAMR_BUILD_SIMD 0)
+endif ()
+
 if (COLLECT_CODE_COVERAGE EQUAL 1)
   set (CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fprofile-arcs -ftest-coverage")
 endif ()

+ 28 - 0
product-mini/platforms/posix/main.c

@@ -49,6 +49,9 @@ print_help()
 #endif
 #if WASM_ENABLE_LIB_PTHREAD != 0
     printf("  --max-threads=n        Set maximum thread number per cluster, default is 4\n");
+#endif
+#if WASM_ENABLE_DEBUG_INTERP != 0
+    printf("  -g=ip:port             Set the debug sever address, default is debug disabled\n");
 #endif
     return 1;
 }
@@ -241,6 +244,11 @@ main(int argc, char *argv[])
     const char *env_list[8] = { NULL };
     uint32 env_list_size = 0;
 #endif
+#if WASM_ENABLE_DEBUG_INTERP != 0
+    char * ip_addr = NULL;
+    //int platform_port = 0;
+    int instance_port = 0;
+#endif
 
     /* Process options.  */
     for (argc--, argv++; argc > 0 && argv[0][0] == '-'; argc--, argv++) {
@@ -321,6 +329,19 @@ main(int argc, char *argv[])
                 return print_help();
             wasm_runtime_set_max_thread_num(atoi(argv[0] + 14));
         }
+#endif
+#if WASM_ENABLE_DEBUG_INTERP != 0
+        else if (!strncmp(argv[0], "-g=", 3)) {
+            char * port_str = strchr(argv[0] + 3, ':');
+            char *port_end;
+            if (port_str == NULL)
+                return print_help();
+            *port_str = '\0';
+            instance_port = strtoul(port_str + 1, &port_end, 10);
+            if (port_str[1] == '\0' || *port_end != '\0')
+                return print_help();
+            ip_addr = argv[0] + 3;
+        }
 #endif
         else
             return print_help();
@@ -346,6 +367,13 @@ main(int argc, char *argv[])
     init_args.mem_alloc_option.allocator.free_func = free;
 #endif
 
+#if WASM_ENABLE_DEBUG_INTERP != 0
+    init_args.platform_port = 0;
+    init_args.instance_port = instance_port;
+    if (ip_addr)
+        strcpy(init_args.ip_addr, ip_addr);
+#endif
+
     /* initialize runtime environment */
     if (!wasm_runtime_full_init(&init_args)) {
         printf("Init runtime environment failed.\n");

+ 11 - 0
product-mini/platforms/vxworks/CMakeLists.txt

@@ -59,6 +59,17 @@ if (NOT DEFINED WAMR_BUILD_LIBC_WASI)
   set (WAMR_BUILD_LIBC_WASI 0)
 endif ()
 
+if (NOT DEFINED WAMR_BUILD_DEBUG_INTERP)
+  # Disable Debug feature by default
+  set (WAMR_BUILD_DEBUG_INTERP 0)
+endif ()
+
+if (WAMR_BUILD_DEBUG_INTERP EQUAL 1)
+  set (WAMR_BUILD_FAST_INTERP 0)
+  set (WAMR_BUILD_MINI_LOADER 0)
+  set (WAMR_BUILD_SIMD 0)
+endif ()
+
 set (CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,--gc-sections")
 
 set (WAMR_ROOT_DIR ${CMAKE_CURRENT_SOURCE_DIR}/../../..)

+ 15 - 2
wamr-compiler/CMakeLists.txt

@@ -109,6 +109,9 @@ message ("-- CMAKE_BUILD_TYPE = " ${CMAKE_BUILD_TYPE})
 if (CMAKE_BUILD_TYPE STREQUAL "Debug")
   add_definitions(-DBH_DEBUG=1)
 endif ()
+if (WAMR_BUILD_DEBUG_AOT EQUAL 1)
+  add_definitions(-DWASM_ENABLE_DEBUG_AOT=1)
+endif()
 
 # Enable LLVM
 if (NOT WAMR_BUILD_WITH_CUSTOM_LLVM)
@@ -133,6 +136,16 @@ add_definitions(${LLVM_DEFINITIONS})
 message(STATUS "Found LLVM ${LLVM_PACKAGE_VERSION}")
 message(STATUS "Using LLVMConfig.cmake in: ${LLVM_DIR}")
 
+if (WAMR_BUILD_DEBUG_AOT EQUAL 1)
+  if(LLVM_BUILD_MAIN_SRC_DIR)
+    include_directories(${LLVM_BUILD_MAIN_SRC_DIR}/../lldb/include)
+    include_directories(${LLVM_BUILD_BINARY_DIR}/tools/lldb/include)
+  endif()
+  link_directories(${LLVM_LIBRARY_DIRS})
+  find_library(lib_lldb NAMES lldb HINTS ${LLVM_LIBRARY_DIRS})
+  message(STATUS "find lldb ${LLDB_ALL_PLUGINS} in: ${LLVM_LIBRARY_DIRS}")
+endif()
+
 if (NOT (CMAKE_C_COMPILER MATCHES ".*clang.*" OR CMAKE_C_COMPILER_ID MATCHES ".*Clang"))
   if(NOT MSVC)
     set (CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,--gc-sections")
@@ -212,7 +225,7 @@ add_library (aotclib ${IWASM_COMPL_SOURCE})
 add_executable (wamrc main.c)
 
 if (NOT MSVC)
-  target_link_libraries (wamrc aotclib vmlib LLVMDemangle ${LLVM_AVAILABLE_LIBS} -lm -ldl -lpthread)
+  target_link_libraries (wamrc aotclib vmlib LLVMDemangle ${LLVM_AVAILABLE_LIBS} -lm -ldl -lpthread ${lib_lldb})
 else()
-  target_link_libraries (wamrc aotclib vmlib  ${LLVM_AVAILABLE_LIBS})
+  target_link_libraries (wamrc aotclib vmlib  ${lib_lldb} ${LLVM_AVAILABLE_LIBS})
 endif()

+ 6 - 0
wamr-compiler/main.c

@@ -248,6 +248,12 @@ main(int argc, char *argv[])
     goto fail3;
   }
 
+#if WASM_ENABLE_DEBUG_AOT != 0
+  if (!create_dwarf_extractor(comp_data, wasm_file_name)) {
+    printf("%s:create dwarf extractor failed\n", wasm_file_name);
+  }
+#endif
+
   bh_print_time("Begin to create compile context");
 
   if (!(comp_ctx = aot_create_comp_context(comp_data,