blob: 76cb28e8858e14d547e145a4a50399c0f34a76be [file] [log] [blame] [edit]
// Copyright 2024 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 "lib/ld/fuchsia-debugdata.h"
#include <lib/fidl/txn_header.h>
#include <zircon/fidl.h>
#include <array>
#include <string_view>
#include <type_traits>
namespace ld {
// These are minimal hand-rolled marshalling functions for the one-way FIDL
// fuchsia.debugdata.Publisher/Publish, fuchsia.io.Directory/Open, and
// fuchsia.unknown.Cloneable/Clone messages.
//
// This code marshals FIDL wire format just as FIDL bindings would, but avoids
// the generated FIDL C++ bindings code because it is too heavy-weight for use
// in low-level environments. This code uses direct marshalling code in local
// stack buffers of minimal size, with no dynamic allocation.
// TODO(https://fxbug.dev/42072760): This should be generated by Zither.
namespace {
constexpr uint64_t fuchsia_io_MAX_PATH_LENGTH = 4095;
constexpr uint64_t fuchsia_io_MAX_NAME_LENGTH = 255;
constexpr uint64_t fuchsia_io_Flags_PROTOCOL_SERVICE = 0x000100000000;
constexpr uint64_t fuchsia_io_DirectoryOpen_Ordinal = 0x568ddcb9a9cbb6d9;
constexpr std::string_view fuchsia_debugdata_Publisher_Name = "fuchsia.debugdata.Publisher";
static_assert(fuchsia_debugdata_Publisher_Name.size() <= fuchsia_io_MAX_PATH_LENGTH);
constexpr uint64_t fuchsia_debugdata_PublisherPublish_Ordinal = 0xf52f8806121e066;
constexpr uint64_t fuchsia_unknown_CloneableClone_Ordinal = 0x20d8a7aba2168a79;
fidl_message_header_t TxnHeader(uint64_t ordinal) {
fidl_message_header_t hdr;
fidl_init_txn_header(&hdr, 0, ordinal, 0);
return hdr;
}
// To use no more stack space than needed for the request, this is specifically
// initialized with the exact constant path string that will be needed.
struct alignas(FIDL_ALIGNMENT) fuchsia_io_DirectoryOpenRequestForDebugdata {
fuchsia_io_DirectoryOpenRequestForDebugdata() {
fuchsia_debugdata_Publisher_Name.copy(path_buffer.data(), path_buffer.size());
}
size_t size_bytes() const { return FIDL_ALIGN(sizeof(fixed) + fixed.path.size); }
struct alignas(FIDL_ALIGNMENT) {
fidl_message_header_t hdr = TxnHeader(fuchsia_io_DirectoryOpen_Ordinal);
fidl_string_t path = {
.size = fuchsia_debugdata_Publisher_Name.size(),
.data = reinterpret_cast<char*>(FIDL_ALLOC_PRESENT),
};
uint64_t flags = fuchsia_io_Flags_PROTOCOL_SERVICE;
fidl_table_t options = {fidl_vector_t{
.count = 0,
.data = reinterpret_cast<void*>(FIDL_ALLOC_PRESENT),
}};
zx_handle_t object = FIDL_HANDLE_PRESENT;
uint32_t padding = 0;
} fixed;
std::array<char, fuchsia_debugdata_Publisher_Name.size()> path_buffer;
const std::byte padding[FIDL_ALIGN(fuchsia_debugdata_Publisher_Name.size()) -
fuchsia_debugdata_Publisher_Name.size()] = {};
};
struct alignas(FIDL_ALIGNMENT) fuchsia_debugdata_PublisherPublishRequestMessage {
size_t size_bytes() const { return FIDL_ALIGN(sizeof(fixed) + fixed.data_sink.size); }
struct alignas(FIDL_ALIGNMENT) {
fidl_message_header_t hdr;
fidl_string_t data_sink;
zx_handle_t data;
zx_handle_t vmo_token;
} fixed;
std::array<char, fuchsia_io_MAX_NAME_LENGTH> data_sink_buffer{};
const std::byte padding[FIDL_ALIGN(fuchsia_io_MAX_NAME_LENGTH) - fuchsia_io_MAX_NAME_LENGTH] = {};
};
struct alignas(FIDL_ALIGNMENT) fuchsia_unknown_CloneableCloneRequest {
static constexpr size_t size_bytes() { return sizeof(fuchsia_unknown_CloneableCloneRequest); }
fidl_message_header_t hdr = TxnHeader(fuchsia_unknown_CloneableClone_Ordinal);
zx_handle_t request = FIDL_HANDLE_PRESENT;
uint32_t padding = 0;
};
template <class Request, size_t N>
zx::result<> SendMessage(zx::unowned_channel client_end, const Request& request,
const std::array<zx_handle_t, N>& handles)
requires std::has_unique_object_representations_v<Request>
{
const uint32_t nbytes = static_cast<uint32_t>(request.size_bytes());
const uint32_t nhandles = static_cast<uint32_t>(handles.size());
return zx::make_result(client_end->write(0u, &request, nbytes, handles.data(), nhandles));
}
zx::result<> fuchsia_io_DirectoryOpen_fuchsia_debugdata_Publisher(zx::unowned_channel client_end,
zx::channel server_end) {
return SendMessage(client_end->borrow(), fuchsia_io_DirectoryOpenRequestForDebugdata{},
std::array{server_end.release()});
}
zx::result<> fuchsia_debugdata_PublisherPublish(zx::unowned_channel client_end,
std::string_view data_sink, zx::vmo data,
zx::eventpair vmo_token) {
fuchsia_debugdata_PublisherPublishRequestMessage request = {
.fixed = {
.hdr = TxnHeader(fuchsia_debugdata_PublisherPublish_Ordinal),
.data_sink =
{
.size = data_sink.size(),
.data = reinterpret_cast<char*>(FIDL_ALLOC_PRESENT),
},
.data = FIDL_HANDLE_PRESENT,
.vmo_token = FIDL_HANDLE_PRESENT,
}};
if (data_sink.size() > request.data_sink_buffer.size()) [[unlikely]] {
return zx::error{ZX_ERR_INVALID_ARGS};
}
data_sink.copy(request.data_sink_buffer.data(), request.data_sink_buffer.size());
return SendMessage(client_end->borrow(), request,
std::array{data.release(), vmo_token.release()});
}
zx::result<> fuchsia_unknown_CloneableClone(zx::unowned_channel client_end,
zx::channel server_end) {
return SendMessage(client_end->borrow(), fuchsia_unknown_CloneableCloneRequest{},
std::array{server_end.release()});
}
} // namespace
zx::result<zx::eventpair> Debugdata::Publish(zx::unowned_channel svc_client_end) && {
zx::channel debugdata_client_end, debugdata_server_end;
if (zx_status_t status = zx::channel::create(0, &debugdata_client_end, &debugdata_server_end);
status != ZX_OK) [[unlikely]] {
return zx::error{status};
}
zx::eventpair vmo_token_client, vmo_token_server;
if (zx_status_t status = zx::eventpair::create(0, &vmo_token_client, &vmo_token_server);
status != ZX_OK) {
return zx::error{status};
}
if (zx::result result = fuchsia_debugdata_PublisherPublish(
debugdata_client_end.borrow(), data_sink_, std::move(data_), std::move(vmo_token_server));
result.is_error()) [[unlikely]] {
return result.take_error();
}
if (zx::result result = fuchsia_io_DirectoryOpen_fuchsia_debugdata_Publisher(
svc_client_end->borrow(), std::move(debugdata_server_end));
result.is_error()) {
return result.take_error();
}
return zx::ok(std::move(vmo_token_client));
}
zx::result<Debugdata::Deferred> Debugdata::DeferredPublish() && {
zx::channel svc_client_end, svc_server_end;
if (zx_status_t status = zx::channel::create(0, &svc_client_end, &svc_server_end);
status != ZX_OK) [[unlikely]] {
return zx::error{status};
}
zx::result token = std::move(*this).Publish(svc_client_end.borrow());
if (token.is_error()) [[unlikely]] {
return token.take_error();
}
return zx::ok(Deferred{
.vmo_token = *std::move(token),
.svc_server_end = std::move(svc_server_end),
});
}
zx::result<> Debugdata::Forward(zx::unowned_channel svc_client_end, zx::channel svc_server_end) {
return fuchsia_unknown_CloneableClone(std::move(svc_client_end), std::move(svc_server_end));
}
} // namespace ld