blob: cdc9d641c3cb33bcd79b71122c95810a1c809dc8 [file] [log] [blame] [edit]
// Copyright 2025 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.
#ifndef LIB_C_STARTUP_PROCESSARGS_H_
#define LIB_C_STARTUP_PROCESSARGS_H_
#include <lib/ld/processargs.h>
#include <lib/zx/handle.h>
#include <zircon/startup.h>
#include <zircon/types.h>
#include <cstdint>
#include <ranges>
#include <span>
#include "../asm-linkage.h"
#include "src/__support/macros/config.h"
namespace LIBC_NAMESPACE_DECL {
// This saves the data from the <zircon/processargs.h> bootstrap message. It's
// allocated in its own GuardedPageBlock. It's actually variable-sized, with a
// flexible array member at the end.
class Processargs {
public:
using Buffer = ld::ProcessargsBuffer<0, 0>;
// This is the implementation function aliased to _zx_startup_get_handles.
//
// It's not really meant to be used as a weak symbol per se--it will always
// be defined and won't ever be overridden by another definition. However,
// declaring it as weak prevents the compiler's language-level logic from
// presuming that it cannot possibly compare equal to another function symbol
// (as aliases are outside the C/C++ linkage model) without also hiding their
// equality from LTO constant propagation and dead-code elimination (as
// laundering either pointer through an empty asm would, for example).
[[gnu::weak]] static zx_startup_handles_t GetHandles(zx_handle_t bootstrap_handle)
LIBC_ASM_LINKAGE_DECLARE(ProcessargsGetHandles);
explicit Processargs(const Buffer& msg, std::span<const zx_handle_t> msg_handles,
Buffer::Actual actual, uint32_t strtab_off);
int argc() const { return static_cast<int>(argc_); }
std::span<char*> argv() { return {&arrays_[0], argc_}; }
std::span<char*> envp() {
// There is a nullptr terminator after argv.last().
return {&arrays_[argc_ + 1], envc_};
}
// The handles and their info must be kept alive for zx_take_startup_handle.
std::span<uint32_t> handle_info() { return handle_arrays().subspan(0, handlec_); }
std::span<zx_handle_t> handles() { return handle_arrays().subspan(handlec_); }
// Returns a range of {uint32_t info, zx::handle take()} pairs such that
// calling take() consumes the handle that matches info while calling
// take(std::in_place) just borrows it.
static auto HandleTakers(std::span<uint32_t> handle_info, std::span<zx_handle_t> handles) {
auto taker = [handle_info, handles](size_t i) {
struct Take {
zx::handle operator()() {
info = 0;
return zx::handle{std::exchange(handle, ZX_HANDLE_INVALID)};
}
zx::unowned_handle operator()(std::in_place_t) const { return zx::unowned_handle{handle}; }
uint32_t& info;
zx_handle_t& handle;
};
Take take = {
.info = handle_info[i],
.handle = handles[i],
};
return std::make_pair(handle_info[i], take);
};
return std::views::transform(std::ranges::iota_view(size_t{0}, handle_info.size()), taker);
}
// The names table is only used by fdio startup (__libc_extensions_init),
// which doesn't save pointers into it. But it has to be allocated somewhere
// without using the normal heap, and it's not likely to bump up the size of
// the GuardedPageBlock so it's not worth the juggling to trim it when dead.
std::span<char*> names() { return {&arrays_[argc_ + 1 + envc_ + 1 + 2], namec_}; }
private:
std::span<uint32_t> handle_arrays() {
return {
reinterpret_cast<uint32_t*>(&arrays_[argc_ + 1 + envc_ + 3 + namec_]),
static_cast<size_t>(handlec_) * 2,
};
}
uint32_t argc_ = 0, envc_ = 0, namec_ = 0, handlec_ = 0, strtab_size_ = 0;
char* arrays_[];
};
} // namespace LIBC_NAMESPACE_DECL
#endif // LIB_C_STARTUP_PROCESSARGS_H_