Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[lldb] Implement WebAssembly debugging #77949

Open
wants to merge 9 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 40 additions & 0 deletions lldb/include/lldb/Target/Process.h
Original file line number Diff line number Diff line change
Expand Up @@ -1548,6 +1548,46 @@ class Process : public std::enable_shared_from_this<Process>,
virtual size_t ReadMemory(lldb::addr_t vm_addr, void *buf, size_t size,
Status &error);

/// Read of memory from a process.
///
/// This function will read memory from the current process's address space
/// and remove any traps that may have been inserted into the memory.
///
/// This overloads accepts an ExecutionContext as additional argument. By
/// default, it calls the previous overload without the ExecutionContext
/// argument, but it can be overridden by Process subclasses.
///
/// \param[in] vm_addr
/// A virtual load address that indicates where to start reading
/// memory from.
///
/// \param[out] buf
/// A byte buffer that is at least \a size bytes long that
/// will receive the memory bytes.
///
/// \param[in] size
/// The number of bytes to read.
///
/// \param[in] exe_ctx
/// The current execution context, if available.
///
/// \param[out] error
/// An error that indicates the success or failure of this
/// operation. If error indicates success (error.Success()),
/// then the value returned can be trusted, otherwise zero
/// will be returned.
///
/// \return
/// The number of bytes that were actually read into \a buf. If
/// the returned number is greater than zero, yet less than \a
/// size, then this function will get called again with \a
/// 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,
ExecutionContext *exe_ctx, Status &error) {
return ReadMemory(vm_addr, buf, size, error);
}

/// Read of memory from a process.
///
/// This function has the same semantics of ReadMemory except that it
Expand Down
2 changes: 1 addition & 1 deletion lldb/source/Core/Value.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -552,7 +552,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, exe_ctx, error);
if (bytes_read != byte_size)
error.SetErrorStringWithFormat(
"read memory from 0x%" PRIx64 " failed (%u of %u bytes read)",
Expand Down
41 changes: 41 additions & 0 deletions lldb/source/Expression/DWARFExpression.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -346,6 +346,16 @@ static offset_t GetOpcodeDataSize(const DataExtractor &data,
return (offset - data_offset) + subexpr_len;
}

case DW_OP_WASM_location: {
uint8_t wasm_op = data.GetU8(&offset);
if (wasm_op == 3) {
data.GetU32(&offset);
} else {
data.GetULEB128(&offset);
}
paolosevMSFT marked this conversation as resolved.
Show resolved Hide resolved
return offset - data_offset;
}

default:
if (!dwarf_cu) {
return LLDB_INVALID_OFFSET;
Expand Down Expand Up @@ -2595,6 +2605,37 @@ bool DWARFExpression::Evaluate(
break;
}

case DW_OP_WASM_location: {
uint8_t wasm_op = opcodes.GetU8(&offset);
uint32_t index;

/* LLDB doesn't have an address space to represents WebAssembly locals,
* globals and operand stacks.
* We encode these elements into virtual registers:
* | tag: 2 bits | index: 30 bits |
* where tag is:
* 0: Not a WebAssembly location
* 1: Local
* 2: Global
* 3: Operand stack value
paolosevMSFT marked this conversation as resolved.
Show resolved Hide resolved
*/
if (wasm_op == 3) {
paolosevMSFT marked this conversation as resolved.
Show resolved Hide resolved
index = opcodes.GetU32(&offset);
wasm_op = 1;
} else {
index = opcodes.GetULEB128(&offset);
}

reg_num = (((wasm_op + 1) & 0x03) << 30) | (index & 0x3fffffff);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why are we adding 1 to the wasm_op? Can't we just set it correctly above?

Also it would be nice to have an enum or constant for 0x03 like kWasmOpMask, and for 30 like `kWasmOpShift'


if (ReadRegisterValueAsScalar(reg_ctx, reg_kind, reg_num, error_ptr, tmp))
stack.push_back(tmp);
else
return false;

break;
}

default:
if (dwarf_cu) {
if (dwarf_cu->GetSymbolFileDWARF().ParseVendorDWARFOpcode(
Expand Down
18 changes: 18 additions & 0 deletions lldb/source/Interpreter/CommandInterpreter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -794,6 +794,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>", 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",
Expand Down
1 change: 1 addition & 0 deletions lldb/source/Plugins/Process/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -19,3 +19,4 @@ add_subdirectory(elf-core)
add_subdirectory(mach-core)
add_subdirectory(minidump)
add_subdirectory(FreeBSDKernel)
add_subdirectory(wasm)
Original file line number Diff line number Diff line change
Expand Up @@ -1628,6 +1628,11 @@ void ProcessGDBRemote::ParseExpeditedRegisters(
}
}

std::shared_ptr<ThreadGDBRemote>
ProcessGDBRemote::CreateThread(lldb::tid_t tid) {
return std::make_shared<ThreadGDBRemote>(*this, tid);
}

ThreadSP ProcessGDBRemote::SetThreadStopInfo(
lldb::tid_t tid, ExpeditedRegisterMap &expedited_register_map,
uint8_t signo, const std::string &thread_name, const std::string &reason,
Expand All @@ -1652,7 +1657,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);
}
}
Expand Down
2 changes: 2 additions & 0 deletions lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.h
Original file line number Diff line number Diff line change
Expand Up @@ -355,6 +355,8 @@ class ProcessGDBRemote : public Process,
MonitorDebugserverProcess(std::weak_ptr<ProcessGDBRemote> process_wp,
lldb::pid_t pid, int signo, int exit_status);

virtual std::shared_ptr<ThreadGDBRemote> CreateThread(lldb::tid_t tid);

lldb::StateType SetThreadStopInfo(StringExtractor &stop_packet);

bool
Expand Down
12 changes: 12 additions & 0 deletions lldb/source/Plugins/Process/wasm/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
add_lldb_library(lldbPluginProcessWasm PLUGIN
ProcessWasm.cpp
ThreadWasm.cpp
UnwindWasm.cpp
wasmRegisterContext.cpp

LINK_LIBS
lldbCore
${LLDB_PLUGINS}
LINK_COMPONENTS
Support
)
Loading