blob: f3cf677f778b8bdae83bc236c8fe4ea3fd24f0bc [file] [log] [blame]
// Copyright 2020 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
#ifndef ZIRCON_KERNEL_LIB_CODE_PATCHING_INCLUDE_LIB_CODE_PATCHING_CODE_PATCHING_H_
#define ZIRCON_KERNEL_LIB_CODE_PATCHING_INCLUDE_LIB_CODE_PATCHING_CODE_PATCHING_H_
#include <lib/arch/cache.h>
#include <lib/fitx/result.h>
#include <lib/zbitl/items/bootfs.h>
#include <stdio.h>
#include <zircon/assert.h>
#include <zircon/compiler.h>
#include <cstddef>
#include <cstring>
#include <ktl/byte.h>
#include <ktl/span.h>
#include <ktl/string_view.h>
// This file is concerned with the facilities for code-patching.
namespace code_patching {
// A patch directive, giving the 'what' of an instruction range and the 'how'
// and 'when' of a patch case identifier.
struct Directive {
uint64_t range_start;
uint32_t range_size;
// A patch case identifier, corresponding to particular hard-coded details on
// how and when code should be the replaced.
uint32_t id;
};
// Ensures against alignment padding.
static_assert(std::has_unique_object_representations_v<Directive>);
// Patcher helps to facilitate code patching. It is constructed from a BOOTFS
// that expects the following entries to be present for some directory
// namespace:
//
// * ${NAMESPACE}/code-patches.bin
// This is raw binary comprised of an array of patch directives (in practice,
// removed as a section from the executable to patch).
//
// * ${NAMESPACE}/code-patches/
// A directory under which patch alternatives are found.
//
// Patcher provides methods for patching provided instruction ranges in the
// supported ways (e.g., nop-fill or wholesale replacement by an alternative).
// Instruction-cache coherence among the modified ranges is also managed by the
// class: it will be effected on destruction or once Commit() is called.
//
class Patcher {
public:
using Bytes = ktl::span<const ktl::byte>;
using Bootfs = zbitl::BootfsView<Bytes>;
using Error = Bootfs::Error;
using iterator = ktl::span<const Directive>::iterator;
// The file containing the Directives.
static constexpr ktl::string_view kPatchesBin = "code-patches.bin";
// A directory under which patch alternatives are found.
static constexpr ktl::string_view kPatchAlternativeDir = "code-patches";
// Initializes the Patcher. The provided directory namespace must be
// nonempty. Must be called before any other method.
fitx::result<Error> Init(Bootfs bootfs, ktl::string_view directory);
// The associated patch directives.
ktl::span<const Directive> patches() const { return patches_; }
// Replaces a range of instructions with the given patch alternative.
fitx::result<Error> PatchWithAlternative(ktl::span<ktl::byte> instructions,
ktl::string_view alternative);
// Overwrites a range of instuctions with the minimal number of `nop`
// instructions.
void NopFill(ktl::span<ktl::byte> instructions);
// Forces instruction-data cache consistency among the modified ranges since
// construction or when this method was last called. In general, it is not
// required that this method be called; consistency will also be reached upon
// destruction of Patcher.
void Commit() { sync_ctx_ = {}; }
private:
fitx::result<Error, Bytes> GetPatchAlternative(ktl::string_view name);
void PrepareToSync(ktl::span<ktl::byte> instructions) {
sync_ctx_.SyncRange(reinterpret_cast<uintptr_t>(instructions.data()), instructions.size());
}
Bootfs bootfs_;
ktl::string_view dir_;
ktl::span<const Directive> patches_;
arch::GlobalCacheConsistencyContext sync_ctx_;
};
void PrintPatcherError(const Patcher::Error& error, FILE* f = stdout);
} // namespace code_patching
#endif // ZIRCON_KERNEL_LIB_CODE_PATCHING_INCLUDE_LIB_CODE_PATCHING_CODE_PATCHING_H_