blob: a4998a98c0445f6eaaeb57b761f5962bb6fc4604 [file]
// Copyright 2026 The Fuchsia Authors
//
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file or at
// https://opensource.org/licenses/MIT
#include "phys/runtime/self.h"
#include <lib/elfldltl/diagnostics.h>
#include <lib/elfldltl/self.h>
#include <zircon/assert.h>
#include <fbl/no_destructor.h>
#include <ktl/byte.h>
#include <ktl/string_view.h>
#include <ktl/variant.h>
#include <phys/elf/image.h>
#include <ktl/enforce.h>
// These are defined by the phys.ld linker script.
extern "C" ktl::byte __executable_start[], _edata[];
extern "C" const ktl::byte __start_note_gnu_build_id[];
extern "C" const ktl::byte __stop_note_gnu_build_id[];
namespace {
using LoadInfo = ElfImage::LoadInfo;
using size_type = ElfImage::Elf::size_type;
// This is function-local so it can be non-constinit. It's never destroyed.
ElfImage& MutableSelf() {
static fbl::NoDestructor<ElfImage> gSelf;
return *gSelf;
}
auto PanicDiagnostics(ktl::string_view name) {
return elfldltl::Diagnostics{
elfldltl::PrintfDiagnosticsReport(__zx_panic, name, ": "),
elfldltl::DiagnosticsPanicFlags(),
};
}
void PruneSegment(LoadInfo::ZeroFillSegment& segment, size_type memsz) {
segment = LoadInfo::ZeroFillSegment{segment.vaddr(), memsz};
}
void PruneSegment(LoadInfo::DataWithZeroFillSegment& segment, size_type memsz) {
ZX_DEBUG_ASSERT(memsz >= segment.filesz());
segment = LoadInfo::DataWithZeroFillSegment{
segment.offset(),
segment.vaddr(),
memsz,
segment.filesz(),
};
}
void PruneSegment(auto& segment, size_type memsz) {
// The final segment being pruned should only ever be either
// DataWithZeroFillSegment or pure ZeroFillSegment.
ZX_PANIC("impossible segment type for PruneFromSelf!");
}
} // namespace
void InitSelf(const char* name) {
using Phdr = ElfImage::Elf::Phdr;
auto memory = elfldltl::Self<>::Memory(__executable_start, _end);
auto bias = elfldltl::Self<>::LoadBias();
Phdr load_segment = {
.type = elfldltl::ElfPhdrType::kLoad,
.vaddr = memory.base(),
.filesz = _edata - __executable_start,
.memsz = memory.image().size_bytes(),
};
load_segment.flags = Phdr::kRead | Phdr::kWrite | Phdr::kExecute;
MutableSelf().InitSelf(name, memory, bias, load_segment,
{__start_note_gnu_build_id, __stop_note_gnu_build_id});
}
const ElfImage& Self() { return MutableSelf(); }
void PruneFromSelf(uint64_t start, uint64_t end) {
ElfImage& self = MutableSelf();
// Modify the ZeroFillSegment or DataWithZeroFillSegment to prune the range.
auto adjust_memsz = [start, end, &self](auto& segment) {
[[maybe_unused]] auto abs = segment.vaddr() + self.load_bias();
ZX_DEBUG_ASSERT(start >= abs);
ZX_DEBUG_ASSERT(end >= start);
ZX_DEBUG_ASSERT(end - abs <= segment.memsz());
auto rel_end = static_cast<size_type>(start - self.load_bias());
size_type new_memsz = rel_end - segment.vaddr();
ZX_DEBUG_ASSERT(new_memsz >= segment.filesz());
PruneSegment(segment, new_memsz);
};
// Adjusting the last segment in LoadInfo is actually done by removing the
// existing segment and then adding back its replacement.
auto& load_info = self.load_info();
auto segment = load_info.RemoveLastSegment();
ktl::visit(adjust_memsz, segment);
auto diag = PanicDiagnostics(self.name());
load_info.AddSegment(diag, segment);
}