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