blob: 72f0219ecd22fe90ce62aaa8eee1e4dc6d0118a4 [file] [log] [blame]
// Copyright 2021 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 <lib/arch/nop.h>
#include <lib/code-patching/code-patching.h>
#include <lib/fitx/result.h>
#include <lib/zbitl/error-stdio.h>
#include <ktl/move.h>
#include <ktl/string_view.h>
#include <ktl/enforce.h>
namespace code_patching {
fitx::result<Patcher::Error> Patcher::Init(Bootfs bootfs, ktl::string_view directory) {
ZX_ASSERT(!directory.empty());
bootfs_ = ktl::move(bootfs);
dir_ = directory;
auto it = bootfs_.find({dir_, kPatchesBin});
if (auto result = bootfs_.take_error(); result.is_error()) {
return result;
}
if (it == bootfs_.end()) {
return fitx::error{Error{.reason = "failed to find patch directives"sv}};
}
if (it->data.size() % sizeof(Directive) != 0) {
fitx::error{Error{
.reason = "patch directive payload has bad size"sv,
.filename = it->name,
.entry_offset = it.dirent_offset(),
}};
}
patches_ = {
reinterpret_cast<const Directive*>(it->data.data()),
it->data.size() / sizeof(Directive),
};
return fitx::ok();
}
fitx::result<Patcher::Error> Patcher::PatchWithAlternative(ktl::span<ktl::byte> instructions,
ktl::string_view alternative) {
auto result = GetPatchAlternative(alternative);
if (result.is_error()) {
return result.take_error();
}
Bytes bytes = ktl::move(result).value();
ZX_ASSERT_MSG(
instructions.size() >= bytes.size(),
"instruction range (%zu bytes) is too small for patch alternative \"%.*s\" (%zu bytes)",
instructions.size(), static_cast<int>(alternative.size()), alternative.data(), bytes.size());
memcpy(instructions.data(), bytes.data(), bytes.size());
PrepareToSync(instructions);
return fitx::ok();
}
void Patcher::NopFill(ktl::span<ktl::byte> instructions) {
arch::NopFill(instructions);
PrepareToSync(instructions);
}
fitx::result<Patcher::Error, Patcher::Bytes> Patcher::GetPatchAlternative(ktl::string_view name) {
auto it = bootfs_.find({dir_, kPatchAlternativeDir, name});
if (auto result = bootfs_.take_error(); result.is_error()) {
return result.take_error();
}
if (it == bootfs_.end()) {
return fitx::error{Error{.reason = "failed to find patch alternative"sv}};
}
return fitx::ok(it->data);
}
void PrintPatcherError(const Patcher::Error& error, FILE* f) {
return zbitl::PrintBootfsError(error, f);
}
} // namespace code_patching