| // 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 |