blob: d1fcef53a203f6a4589486582e57605c32e43789 [file] [log] [blame]
// Copyright 2023 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef EXTENSIONS_CXX_DEBUGGING_WASMVENDORPLUGINS_H_
#define EXTENSIONS_CXX_DEBUGGING_WASMVENDORPLUGINS_H_
#include "ApiContext.h"
#include "Plugins/SymbolFile/DWARF/DWARFDeclContext.h"
#include "Plugins/SymbolFile/DWARF/SymbolFileDWARF.h"
#include "Plugins/TypeSystem/Clang/TypeSystemClang.h"
#include "lldb/Core/Debugger.h"
#include "lldb/Target/RegisterContext.h"
#include "lldb/Target/Unwind.h"
#include "llvm/BinaryFormat/Dwarf.h"
class SymbolVendorWASM;
class SymbolFileDWARF;
namespace lldb_private {
class FileSystem;
class CPlusPlusLanguage;
class ClangASTContext;
namespace wasm {
class ObjectFileWasm;
class SymbolVendorWasm;
} // namespace wasm
} // namespace lldb_private
namespace symbols_backend {
template <typename T>
static void initialize() { // NOLINT
T::Initialize();
}
template <typename T>
static void terminate() { // NOLINT
T::Terminate();
}
template <typename T, typename FirstT, typename... MoreT>
static void initialize() { // NOLINT
T::Initialize();
initialize<FirstT, MoreT...>();
}
template <typename T, typename FirstT, typename... MoreT>
static void terminate() { // NOLINT
terminate<FirstT, MoreT...>();
T::Terminate();
}
template <typename... SystemT>
struct PluginRegistryContext {
PluginRegistryContext() { initialize<SystemT...>(); }
~PluginRegistryContext() { terminate<SystemT...>(); }
};
class WasmPlatform : public lldb_private::Platform {
public:
using lldb_private::Platform::Platform;
static void Initialize();
static void Terminate();
class Resolver : public lldb_private::UserIDResolver {
llvm::Optional<std::string> DoGetUserName(id_t uid) final { return {}; }
llvm::Optional<std::string> DoGetGroupName(id_t gid) final { return {}; }
};
Resolver resolver_;
llvm::StringRef GetPluginName() final { return "wasm32"; }
llvm::StringRef GetDescription() final { return "wasm32"; }
lldb_private::UserIDResolver& GetUserIDResolver() final { return resolver_; }
std::vector<lldb_private::ArchSpec> GetSupportedArchitectures(
const lldb_private::ArchSpec& process_host_arch) final {
return {{lldb_private::ArchSpec("wasm32-unknown-unknown")}};
}
void CalculateTrapHandlerSymbolNames() final {}
lldb::ProcessSP Attach(lldb_private::ProcessAttachInfo& attach_info,
lldb_private::Debugger& debugger,
lldb_private::Target* target,
lldb_private::Status& error) final {
error.SetErrorString("Cannot attach to processes");
return {};
}
};
class WasmRegisters : public lldb_private::RegisterContext {
using lldb_private::RegisterContext::RegisterContext;
lldb_private::RegisterInfo fake_pc_register_ = {"PC",
nullptr,
4,
0,
lldb::Encoding::eEncodingUint,
lldb::Format::eFormatDefault,
{0},
nullptr,
nullptr};
size_t frame_offset_;
public:
WasmRegisters(lldb_private::Thread& thread, size_t frame_offset);
void InvalidateAllRegisters() override{};
size_t GetRegisterCount() override { return 1; }
const lldb_private::RegisterInfo* GetRegisterInfoAtIndex(size_t reg) override;
size_t GetRegisterSetCount() override { return 0; }
const lldb_private::RegisterSet* GetRegisterSet(size_t reg_set) override {
return nullptr;
}
lldb::ByteOrder GetByteOrder() override { return lldb::eByteOrderLittle; }
bool ReadRegister(const lldb_private::RegisterInfo* reg_info,
lldb_private::RegisterValue& reg_value) override;
bool WriteRegister(const lldb_private::RegisterInfo* reg_info,
const lldb_private::RegisterValue& reg_value) override {
return false;
}
friend class WasmThread;
};
class WasmUnwind : public lldb_private::Unwind {
size_t frame_offset_;
public:
explicit WasmUnwind(lldb_private::Thread& thread, size_t frame_offset)
: Unwind(thread), frame_offset_(frame_offset) {}
void DoClear() final{};
uint32_t DoGetFrameCount() final { return 1; }
bool DoGetFrameInfoAtIndex(uint32_t frame_idx,
lldb::addr_t& cfa,
lldb::addr_t& pc,
bool& behaves_like_zeroth_frame) final;
lldb::RegisterContextSP DoCreateRegisterContextForFrame(
lldb_private::StackFrame* frame) final {
return GetRegisterContext();
}
lldb::RegisterContextSP GetRegisterContext() {
return lldb::RegisterContextSP{
new WasmRegisters(GetThread(), frame_offset_)};
}
};
class WasmThread : public lldb_private::Thread {
friend class WasmUnwind;
lldb::StackFrameSP stack_frame_;
WasmUnwind unwind_;
public:
WasmThread(lldb_private::Process& process, size_t frame_offset)
: Thread(process, 0), unwind_(*this, frame_offset) {}
void RefreshStateAfterStop() override {}
bool CalculateStopInfo() override { return false; }
WasmUnwind& GetUnwinder() final { return unwind_; }
lldb::RegisterContextSP CreateRegisterContextForFrame(
lldb_private::StackFrame* frame) override;
lldb::RegisterContextSP GetRegisterContext() override;
lldb::StackFrameSP GetFrame();
};
class WasmProcess : public lldb_private::Process {
using lldb_private::Process::Process;
const api::DebuggerProxy* proxy_ = nullptr;
size_t frame_offset_ = 0;
public:
static void Initialize();
static void Terminate();
static lldb::ProcessSP CreateInstance(
lldb::TargetSP target_sp,
lldb::ListenerSP listener_sp,
const lldb_private::FileSpec* crash_file_path,
bool can_connect);
static llvm::StringRef GetPluginDescriptionStatic() {
return "wasm32 proces";
}
static llvm::StringRef GetPluginNameStatic() { return "wasm32"; }
void SetProxyAndFrameOffset(const api::DebuggerProxy& proxy,
size_t frame_offset);
bool CanDebug(lldb::TargetSP target, bool plugin_specified_by_name) override;
lldb_private::Status DoDestroy() override { return {}; }
void RefreshStateAfterStop() override {}
bool DoUpdateThreadList(lldb_private::ThreadList& old_thread_list,
lldb_private::ThreadList& new_thread_list) override;
size_t DoReadMemory(lldb::addr_t vm_addr,
void* buf,
size_t size,
lldb_private::Status& error) override;
llvm::StringRef GetPluginName() override { return GetPluginNameStatic(); }
};
class SymbolFileWasmDWARF : public ::SymbolFileDWARF {
static char ID;
public:
using SymbolFileDWARF::SymbolFileDWARF;
bool isA(const void* ClassID) const override {
return ClassID == &ID || SymbolFileDWARF::isA(ClassID);
}
static bool classof(const SymbolFile* obj) { return obj->isA(&ID); }
static void Initialize();
static void Terminate();
static llvm::StringRef GetPluginNameStatic() { return "wasm_dwarf"; }
llvm::StringRef GetPluginName() final { return GetPluginNameStatic(); }
static llvm::StringRef GetPluginDescriptionStatic();
static lldb_private::SymbolFile* CreateInstance(
lldb::ObjectFileSP objfile_sp);
struct WasmValueLoader {
SymbolFileWasmDWARF& symbol_file;
explicit WasmValueLoader(SymbolFileWasmDWARF& symbol_file)
: symbol_file(symbol_file) {
assert(!symbol_file.current_value_loader_ &&
"Cannot nest wasm eval contexts");
symbol_file.current_value_loader_ = this;
}
~WasmValueLoader() { symbol_file.current_value_loader_ = nullptr; }
virtual llvm::Expected<api::DebuggerProxy::WasmValue> LoadWASMValue(
uint8_t storage_type,
const lldb_private::DataExtractor& data,
lldb::offset_t& offset) = 0;
};
lldb::offset_t GetVendorDWARFOpcodeSize(
const lldb_private::DataExtractor& data,
const lldb::offset_t data_offset,
const uint8_t op) const final {
return LLDB_INVALID_OFFSET;
}
bool ParseVendorDWARFOpcode(
uint8_t op,
const lldb_private::DataExtractor& opcodes,
lldb::offset_t& offset,
std::vector<lldb_private::Value>& stack) const final {
if (!current_value_loader_) {
return false;
}
switch (op) {
case llvm::dwarf::DW_OP_WASM_location_int:
case llvm::dwarf::DW_OP_WASM_location: {
uint8_t storage_type = opcodes.GetU8(&offset);
auto value =
current_value_loader_->LoadWASMValue(storage_type, opcodes, offset);
if (!value) {
llvm::errs() << llvm::toString(value.takeError());
return false;
}
auto stack_value = std::visit(
[](auto v) { return lldb_private::Scalar(v); }, value->value);
stack.push_back(stack_value);
stack.back().SetValueType(lldb_private::Value::ValueType::Scalar);
return true;
}
}
return false;
}
std::shared_ptr<lldb_private::Type> externref_type_sp;
lldb::TypeSP FindDefinitionTypeForDWARFDeclContext(
const DWARFDeclContext& dwarf_decl_ctx) override {
// We define type externref_t as a 32-bit integer, so as to be
// able to transfer some information through the interpreter
const uint32_t dwarf_decl_ctx_count = dwarf_decl_ctx.GetSize();
if (dwarf_decl_ctx_count > 0) {
const lldb_private::ConstString type_name(dwarf_decl_ctx[0].name);
if (type_name == "externref_t") {
if (!externref_type_sp) {
const lldb::LanguageType language = dwarf_decl_ctx.GetLanguage();
auto type_system = GetTypeSystemForLanguage(language);
bool ok = !type_system.takeError();
assert(ok);
auto ast = clang::dyn_cast<lldb_private::TypeSystemClang>(
type_system->get());
lldb_private::CompilerType clang_type =
ast->GetBasicType(lldb::eBasicTypeUnsignedLongLong);
externref_type_sp = std::make_shared<lldb_private::Type>(
lldb::user_id_t(0), this, type_name, 4, nullptr, LLDB_INVALID_UID,
lldb_private::Type::eEncodingIsUID, lldb_private::Declaration(),
clang_type, lldb_private::Type::ResolveState::Forward);
}
return externref_type_sp;
}
}
return SymbolFileDWARF::FindDefinitionTypeForDWARFDeclContext(
dwarf_decl_ctx);
}
private:
WasmValueLoader* current_value_loader_ = nullptr;
};
} // namespace symbols_backend
#endif // EXTENSIONS_CXX_DEBUGGING_WASMVENDORPLUGINS_H_