blob: 4e61559ad4428ae3c1c4768a7a99f9880acaa4a4 [file] [log] [blame]
// Copyright 2019 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "zx_channel_params.h"
#include <vector>
#include "src/developer/debug/ipc/register_desc.h"
#include "src/developer/debug/shared/message_loop.h"
#include "src/developer/debug/zxdb/client/memory_dump.h"
#include "src/developer/debug/zxdb/client/process.h"
#include "src/lib/fxl/logging.h"
namespace fidlcat {
namespace {
// Helper function to convert a vector of bytes to a T.
template <typename T>
T GetValueFromBytes(const std::vector<uint8_t>& bytes) {
T ret = 0;
for (uint64_t i = 0; i < sizeof(ret) && i < bytes.size(); i++) {
ret |= ((uint64_t)(bytes[i])) << (i * 8);
}
return ret;
}
// Helper function to convert the value in a register to a T
template <typename T>
T GetRegisterValue(const std::vector<zxdb::Register>& regs,
const debug_ipc::RegisterID id) {
for (const zxdb::Register& reg : regs) {
if (reg.id() == id) {
return GetValueFromBytes<T>(reg.data());
}
}
return 0;
}
// Grovels through the |dump| and constructs a local copy of the bytes into an
// array of type |T|, starting at |bytes_address| and continuing for |count|
// elements.
template <typename T>
std::unique_ptr<T> MemoryDumpToArray(uint64_t bytes_address, uint32_t count,
const zxdb::MemoryDump& dump) {
static_assert(std::is_array<T>::value || std::is_pointer<T>::value,
"MemoryDump can only be used for pointer types");
std::unique_ptr<T> output_buffer = std::make_unique<T>(count);
// replace with memset, or poison pattern.
for (uint32_t i = 0; i < count; i++) {
output_buffer[i] = 0;
}
uint8_t* buffer_as_bytes = reinterpret_cast<uint8_t*>(output_buffer.get());
size_t output_offset = 0;
for (const debug_ipc::MemoryBlock& block : dump.blocks()) {
if (!block.valid) {
continue;
}
size_t block_offset = 0;
if (block.address < bytes_address) {
if (block.address + block.size < bytes_address) {
continue;
}
block_offset = bytes_address - block.address;
}
while (block_offset < block.size &&
output_offset < (sizeof(output_buffer[0]) * count)) {
buffer_as_bytes[output_offset] = block.data[block_offset];
output_offset++;
block_offset++;
}
}
return output_buffer;
}
} // namespace
void ZxChannelWriteParams::BuildZxChannelWriteParamsAndContinue(
fxl::WeakPtr<zxdb::Thread> thread, const zxdb::RegisterSet& registers,
ZxChannelWriteCallback&& fn) {
if (registers.arch() == debug_ipc::Arch::kX64) {
auto regs_it = registers.category_map().find(
debug_ipc::RegisterCategory::Type::kGeneral);
if (regs_it == registers.category_map().end()) {
zxdb::Err err(zxdb::ErrType::kInput,
"zx_channel_write params not found?");
ZxChannelWriteParams params;
fn(err, params);
return;
}
ZxChannelWriteParams::BuildX86AndContinue(thread, regs_it->second,
std::move(fn));
return;
} else if (registers.arch() == debug_ipc::Arch::kArm64) {
zxdb::Err err(zxdb::ErrType::kCanceled, "ARM64 not supported yet");
ZxChannelWriteParams params;
fn(err, params);
} else {
zxdb::Err err(zxdb::ErrType::kCanceled, "Unknown arch");
ZxChannelWriteParams params;
fn(err, params);
}
return;
}
// Assuming that |thread| is stopped in a zx_channel_write, and that |regs| is
// the set of registers for that thread, and that both are on a connected x64
// device, do what is necessary to populate |params| and pass them to |fn|.
//
// This remains pretty brittle WRT the order of paramaters to zx_channel_write
// and the x86 calling conventions. Those things aren't likely to change, but
// if they did, we'd have to update this.
void ZxChannelWriteParams::BuildX86AndContinue(
fxl::WeakPtr<zxdb::Thread> thread, const std::vector<zxdb::Register>& regs,
ZxChannelWriteCallback&& fn) {
// The order of parameters in the System V AMD64 ABI we use, according to
// Wikipedia:
zx_handle_t handle =
GetRegisterValue<zx_handle_t>(regs, debug_ipc::RegisterID::kX64_rdi);
uint32_t options =
GetRegisterValue<uint32_t>(regs, debug_ipc::RegisterID::kX64_rsi);
uint64_t bytes_address =
GetRegisterValue<uint64_t>(regs, debug_ipc::RegisterID::kX64_rdx);
uint32_t num_bytes =
GetRegisterValue<uint32_t>(regs, debug_ipc::RegisterID::kX64_rcx);
uint64_t handles_address =
GetRegisterValue<uint64_t>(regs, debug_ipc::RegisterID::kX64_r8);
uint32_t num_handles =
GetRegisterValue<uint32_t>(regs, debug_ipc::RegisterID::kX64_r9);
struct ParamStore {
// The params, which will move from partially-constructed to fully
// constructed.
ZxChannelWriteParams params;
// Any errs that are propagated from the memory reads.
zxdb::Err err;
// The last callback to run, which invokes the ZxChannelWriteCallback
fit::function<void()> final_cb;
};
ParamStore* store = new ParamStore();
ZxChannelWriteParams new_params(handle, options, nullptr, num_bytes, nullptr,
num_handles);
store->params = std::move(new_params);
store->final_cb = [store, fn = std::move(fn)]() {
if (!store->err.ok()) {
ZxChannelWriteParams p;
fn(store->err, p);
} else {
zxdb::Err err;
fn(err, store->params);
}
delete store;
};
if (num_bytes != 0) {
thread->GetProcess()->ReadMemory(
bytes_address, num_bytes,
[store, num_bytes, bytes_address](const zxdb::Err& err,
zxdb::MemoryDump dump) {
std::unique_ptr<uint8_t[]> bytes;
auto handles = std::move(store->params.handles_);
if (err.ok()) {
bytes =
MemoryDumpToArray<uint8_t[]>(bytes_address, num_bytes, dump);
} else {
std::string msg = "Failed to build zx_channel_write params: ";
msg.append(err.msg());
store->err = zxdb::Err(err.type(), msg);
}
store->params = ZxChannelWriteParams(
store->params.GetHandle(), store->params.GetOptions(),
std::move(bytes), num_bytes, std::move(handles),
store->params.GetNumHandles());
if (store->params.IsComplete() || !store->err.ok()) {
store->final_cb();
}
});
}
if (num_handles != 0) {
thread->GetProcess()->ReadMemory(
handles_address, num_handles * sizeof(zx_handle_t),
[store, num_handles, handles_address](const zxdb::Err& err,
zxdb::MemoryDump dump) {
std::unique_ptr<zx_handle_t[]> handles;
auto bytes = std::move(store->params.bytes_);
if (err.ok()) {
handles = MemoryDumpToArray<zx_handle_t[]>(handles_address,
num_handles, dump);
} else {
std::string msg = "Failed to build zx_channel_write params: ";
msg.append(err.msg());
store->err = zxdb::Err(err.type(), msg);
}
store->params = ZxChannelWriteParams(
store->params.GetHandle(), store->params.GetOptions(),
std::move(bytes), store->params.GetNumBytes(), std::move(handles),
store->params.GetNumHandles());
if (store->params.IsComplete() || !store->err.ok()) {
store->final_cb();
}
});
}
if (num_handles == 0 && num_bytes == 0) {
store->final_cb();
}
}
} // namespace fidlcat