blob: 3fe1ca5f1d19caf4a22ecd7a6fbebe4c0b822c6c [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.
#include "Expressions.h"
#include "ApiContext.h"
#include "WasmModule.h"
#include "WasmVendorPlugins.h"
#include "lldb-eval/ast.h"
#include "lldb-eval/context.h"
#include "lldb-eval/eval.h"
#include "lldb-eval/parser.h"
#include "lldb-eval/parser_context.h"
#include "lldb-eval/value.h"
#include "lldb/Core/Module.h"
#include "lldb/Core/Section.h"
#include "lldb/Symbol/CompilerType.h"
#include "lldb/Symbol/SymbolContext.h"
#include "lldb/Symbol/Type.h"
#include "lldb/Symbol/TypeSystem.h"
#include "lldb/Target/Process.h"
#include "lldb/Utility/ArchSpec.h"
#include "lldb/Utility/Listener.h"
#include "llvm/ADT/StringRef.h"
#include <cstddef>
#include <iterator>
#include <memory>
#include <string>
#include <type_traits>
namespace symbols_backend {
static lldb_private::CompilerType GetTopType(lldb_private::CompilerType t) {
if (!t.IsValid() || !t.IsTypedefType()) {
return t;
}
return GetTopType(t.GetTypedefedType());
}
struct WasmValueLoaderContext : SymbolFileWasmDWARF::WasmValueLoader {
const api::DebuggerProxy& proxy;
explicit WasmValueLoaderContext(const api::DebuggerProxy& proxy,
SymbolFileWasmDWARF& symbol_file)
: SymbolFileWasmDWARF::WasmValueLoader(symbol_file), proxy(proxy) {}
llvm::Expected<api::DebuggerProxy::WasmValue> LoadWASMValue(
uint8_t storage_type,
const lldb_private::DataExtractor& data,
lldb::offset_t& offset) final {
uint64_t index = 0;
switch (storage_type) {
case 0x00: // local
case 0x01: // global
case 0x02: // operand
index = data.GetULEB128(&offset);
break;
case 0x03: // global i32
index = data.GetU32(&offset);
break;
default:
return llvm::createStringError(llvm::inconvertibleErrorCode(),
"Invalid WASM value storage type");
}
switch (storage_type) {
case 0x00: // local
return proxy.GetLocal(index);
case 0x02: // operand
return proxy.GetOperand(index);
case 0x01: // global
case 0x03: // global i32
return proxy.GetGlobal(index);
}
return llvm::createStringError(llvm::inconvertibleErrorCode(),
"Invalid WASM value storage type");
}
};
lldb_private::CompilerType GetCompilerType(lldb::SBType t) {
struct Derived : lldb::SBType {
explicit Derived(const lldb::SBType& t) : SBType(t) {}
auto Get() { return ref().GetCompilerType(false); }
};
return Derived{t}.Get();
}
llvm::Optional<llvm::Error> CheckError(lldb::SBValue value) {
auto e = value.GetError();
if (!e.IsValid() || e.Success()) {
return {};
}
auto message = e.GetCString();
return llvm::createStringError(llvm::inconvertibleErrorCode(), message);
}
llvm::Expected<ExpressionResult> InterpretExpression(
const WasmModule& module,
lldb_private::TypeSystem& type_system,
lldb_private::SymbolContext& sc,
size_t frame_offset,
size_t inline_frame_index,
lldb_private::Address addr,
llvm::StringRef expression,
const api::DebuggerProxy& proxy) {
auto target = module.Target()->shared_from_this();
lldb::ListenerSP listener = lldb_private::Listener::MakeListener("wasm32");
lldb::ProcessSP process =
target->CreateProcess(listener, "wasm32", nullptr, false);
if (!process) {
return llvm::createStringError(llvm::inconvertibleErrorCode(),
"Failed to create process");
}
lldb::SectionSP code_section =
module.Module()->GetObjectFile()->GetSectionList()->FindSectionByType(
lldb::eSectionTypeCode, false);
target->SetSectionLoadAddress(code_section, 0);
static_cast<WasmProcess*>(process.get())
->SetProxyAndFrameOffset(proxy, frame_offset);
process->UpdateThreadListIfNeeded();
process->GetThreadList().SetSelectedThreadByID(0);
auto thread = std::static_pointer_cast<WasmThread>(
process->GetThreadList().GetSelectedThread());
WasmValueLoaderContext loader(proxy, *llvm::cast<SymbolFileWasmDWARF>(
module.Module()->GetSymbolFile()));
auto sm = lldb_eval::SourceManager::Create(expression.str());
auto ctx = lldb_eval::Context::Create(sm, lldb::SBFrame{thread->GetFrame()});
lldb_eval::Parser parser(ctx);
lldb_eval::Error e;
auto tree = parser.Run(e);
if (e || !tree || tree->is_error()) {
return llvm::createStringError(llvm::inconvertibleErrorCode(),
e.message().c_str());
}
lldb_eval::Interpreter interpreter(target, sm);
auto result = interpreter.Eval(tree.get(), e);
if (!result.IsValid() || e) {
return llvm::createStringError(llvm::inconvertibleErrorCode(),
e.message().c_str());
}
if (auto e = CheckError(result.inner_value())) {
return std::move(*e);
}
auto type = GetCompilerType(ToSBType(result.type()));
auto top_type = GetTopType(type);
auto val = result.inner_value();
llvm::Optional<size_t> address;
if (auto address_of = val.AddressOf()) {
address = address_of.GetValueAsUnsigned();
}
switch (top_type.GetBasicTypeEnumeration()) {
case lldb::BasicType::eBasicTypeChar:
case lldb::BasicType::eBasicTypeSignedChar:
case lldb::BasicType::eBasicTypeChar8:
case lldb::BasicType::eBasicTypeChar16:
case lldb::BasicType::eBasicTypeChar32:
case lldb::BasicType::eBasicTypeShort:
case lldb::BasicType::eBasicTypeInt:
case lldb::BasicType::eBasicTypeLong:
case lldb::BasicType::eBasicTypeWChar:
case lldb::BasicType::eBasicTypeSignedWChar:
case lldb::BasicType::eBasicTypeLongLong: {
auto integer = result.GetValueAsSigned();
if (auto integer_size = type.GetByteSize(nullptr)) {
switch (*integer_size) {
case 1:
return ExpressionResult{type, static_cast<int8_t>(integer),
address};
case 2:
return ExpressionResult{type, static_cast<int16_t>(integer),
address};
case 4:
return ExpressionResult{type, static_cast<int32_t>(integer),
address};
case 8:
return ExpressionResult{type, static_cast<int64_t>(integer),
address};
}
break;
}
}
case lldb::BasicType::eBasicTypeUnsignedChar:
case lldb::BasicType::eBasicTypeUnsignedShort:
case lldb::BasicType::eBasicTypeUnsignedWChar:
case lldb::BasicType::eBasicTypeUnsignedInt:
case lldb::BasicType::eBasicTypeUnsignedLong:
case lldb::BasicType::eBasicTypeUnsignedLongLong: {
auto integer = result.GetUInt64();
if (auto integer_size = type.GetByteSize(nullptr)) {
switch (*integer_size) {
case 1:
return ExpressionResult{type, static_cast<uint8_t>(integer),
address};
case 2:
return ExpressionResult{type, static_cast<uint16_t>(integer),
address};
case 4:
return ExpressionResult{type, static_cast<uint32_t>(integer),
address};
case 8:
return ExpressionResult{type, static_cast<uint64_t>(integer),
address};
}
}
break;
}
case lldb::BasicType::eBasicTypeBool:
return ExpressionResult{type, result.GetBool(), address};
case lldb::BasicType::eBasicTypeFloat:
return ExpressionResult{type, result.GetFloat().convertToFloat(),
address};
case lldb::BasicType::eBasicTypeDouble:
return ExpressionResult{type, result.GetFloat().convertToDouble(),
address};
case lldb::BasicType::eBasicTypeVoid:
return ExpressionResult{type, std::monostate{}, address};
case lldb::BasicType::eBasicTypeNullPtr:
return ExpressionResult{type, nullptr, address};
case lldb::BasicType::eBasicTypeHalf:
case lldb::BasicType::eBasicTypeLongDouble:
case lldb::BasicType::eBasicTypeFloatComplex:
case lldb::BasicType::eBasicTypeDoubleComplex:
case lldb::BasicType::eBasicTypeLongDoubleComplex:
case lldb::BasicType::eBasicTypeObjCID:
case lldb::BasicType::eBasicTypeObjCClass:
case lldb::BasicType::eBasicTypeObjCSel:
case lldb::BasicType::eBasicTypeInt128:
case lldb::BasicType::eBasicTypeUnsignedInt128:
case lldb::BasicType::eBasicTypeOther:
return llvm::createStringError(llvm::inconvertibleErrorCode(),
"Invalid basic type %s",
result.type()->GetName().str().c_str());
case lldb::BasicType::eBasicTypeInvalid:
break;
}
bool is_signed = false;
if (top_type.IsEnumerationType(is_signed)) {
if (is_signed) {
auto integer = result.GetValueAsSigned();
if (auto integer_size = type.GetByteSize(nullptr)) {
switch (*integer_size) {
case 1:
return ExpressionResult{type, static_cast<int8_t>(integer),
address};
case 2:
return ExpressionResult{type, static_cast<int16_t>(integer),
address};
case 4:
return ExpressionResult{type, static_cast<int32_t>(integer),
address};
case 8:
return ExpressionResult{type, static_cast<int64_t>(integer),
address};
}
}
}
auto integer = result.GetUInt64();
if (auto integer_size = type.GetByteSize(nullptr)) {
switch (*integer_size) {
case 1:
return ExpressionResult{type, static_cast<uint8_t>(integer), address};
case 2:
return ExpressionResult{type, static_cast<uint16_t>(integer),
address};
case 4:
return ExpressionResult{type, static_cast<uint32_t>(integer),
address};
case 8:
return ExpressionResult{type, static_cast<uint64_t>(integer),
address};
}
}
}
if (result.IsPointer()) {
if (module.Module()->GetArchitecture().GetAddressByteSize() == 4) {
return ExpressionResult{type, static_cast<uint32_t>(result.GetUInt64()),
address};
} else {
return ExpressionResult{type, static_cast<uint64_t>(result.GetUInt64()),
address};
}
}
auto ptr = result.AddressOf();
if (ptr.IsValid()) {
return ExpressionResult{type, reinterpret_cast<void*>(ptr.GetUInt64()),
address};
}
return llvm::createStringError(llvm::inconvertibleErrorCode(),
"Cannot evaluate in-memory value with type %s",
result.type()->GetName().str().c_str());
}
} // namespace symbols_backend