| // Copyright 2022 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_PHYS_INCLUDE_PHYS_ELF_IMAGE_H_ |
| #define ZIRCON_KERNEL_PHYS_INCLUDE_PHYS_ELF_IMAGE_H_ |
| |
| #include <inttypes.h> |
| #include <lib/code-patching/code-patching.h> |
| #include <lib/elfldltl/load.h> |
| #include <lib/elfldltl/memory.h> |
| #include <lib/elfldltl/note.h> |
| #include <lib/elfldltl/static-vector.h> |
| #include <lib/fit/function.h> |
| #include <lib/fit/result.h> |
| #include <lib/zbitl/items/bootfs.h> |
| #include <lib/zbitl/view.h> |
| #include <zircon/assert.h> |
| #include <zircon/limits.h> |
| |
| #include <ktl/array.h> |
| #include <ktl/byte.h> |
| #include <ktl/initializer_list.h> |
| #include <ktl/move.h> |
| #include <ktl/optional.h> |
| #include <ktl/span.h> |
| #include <ktl/string_view.h> |
| #include <ktl/type_traits.h> |
| |
| #include "address-space.h" |
| #include "allocation.h" |
| |
| class ElfImage { |
| public: |
| static constexpr ktl::string_view kImageName = "image.elf"; |
| |
| static constexpr size_t kMaxLoad = 4; // RODATA, CODE, RELRO, DATA |
| |
| static constexpr size_t kMaxBuildIdLen = 32; |
| |
| using LoadInfo = elfldltl::LoadInfo<elfldltl::Elf<>, elfldltl::StaticVector<kMaxLoad>::Container, |
| elfldltl::PhdrLoadPolicy::kContiguous>; |
| |
| using BootfsDir = zbitl::BootfsView<ktl::span<ktl::byte>>; |
| using Error = BootfsDir::Error; |
| |
| using PublishDebugdataFunction = fit::inline_function<ktl::span<ktl::byte>( |
| ktl::string_view sink_name, ktl::string_view vmo_name, ktl::string_view suffix, |
| size_t content_size)>; |
| |
| using PrintPatchFunction = fit::inline_function<void(ktl::initializer_list<ktl::string_view>)>; |
| |
| // An ELF image is found at "dir/name". That can be an ELF file or a subtree. |
| // The subtree should contain "image.elf", "code-patches.bin", etc. A |
| // singleton file will be treated as the image with no patches to apply. |
| fit::result<Error> Init(BootfsDir dir, ktl::string_view name, bool relocated); |
| |
| ktl::string_view name() const { return name_; } |
| |
| LoadInfo& load_info() { return load_info_; } |
| const LoadInfo& load_info() const { return load_info_; } |
| |
| uint64_t load_bias() const { |
| ZX_DEBUG_ASSERT(load_bias_); |
| return *load_bias_; |
| } |
| |
| // Return the memory image within the current address space. Must be called |
| // after Init(). |
| cpp20::span<const std::byte> memory_image() const { return image_.image(); } |
| |
| uint64_t entry() const { return entry_ + load_bias(); } |
| |
| ktl::optional<ktl::string_view> interp() const { return interp_; } |
| |
| const ktl::optional<elfldltl::ElfNote>& build_id() const { return build_id_; } |
| |
| bool has_patches() const { return !patches().empty(); } |
| |
| size_t patch_count() const { return patches().size(); } |
| |
| // The template parameter must be an `enum class Id : uint32_t` type. Calls |
| // the callback as fit::result<Error>(code_patching::Patcher&, Id, |
| // ktl::span<ktl::byte>, PrintPatchFunction) for each patch in the file |
| // (Print is as for ArchCodePatch in <lib/code-patching/code-patches.h>). |
| // Before Load() this patches the BOOTFS file image in place. After Load() |
| // this patches the load image (which could sometimes still be using the file |
| // image in place). |
| template <typename Id, typename Callback> |
| fit::result<Error> ForEachPatch(Callback&& callback) { |
| static_assert(ktl::is_enum_v<Id>); |
| static_assert(ktl::is_same_v<uint32_t, ktl::underlying_type_t<Id>>); |
| static_assert(ktl::is_invocable_r_v<fit::result<Error>, Callback, code_patching::Patcher&, Id, |
| ktl::span<ktl::byte>, PrintPatchFunction>); |
| fit::result<Error> result = fit::ok(); |
| for (const code_patching::Directive& patch : patches()) { |
| ktl::span<ktl::byte> bytes = GetBytesToPatch(patch); |
| auto print = [this, &patch](ktl::initializer_list<ktl::string_view> strings) { |
| PrintPatch(patch, strings); |
| }; |
| result = callback(patcher_, static_cast<Id>(patch.id), bytes, print); |
| if (result.is_error()) { |
| break; |
| } |
| } |
| return result; |
| } |
| |
| // Return true if the memory within the BOOTFS image for this file is |
| // sufficient to be used in place as the load image. |
| bool CanLoadInPlace() const { |
| return load_info_.vaddr_size() <= ZBI_BOOTFS_PAGE_ALIGN(image_.image().size_bytes()); |
| } |
| |
| // Load in place if possible, or else copy into a new Allocation |
| // A virtual load address at which relocation is expected to occur may be |
| // provided; if not, the image will be loaded within the current address |
| // space. |
| // The Allocation returned is null if LoadInPlace was used; otherwise, it |
| // owns the memory backing the new load image and should be kept alive for |
| // the lifetime of the ElfImage. In general, the returned allocation should |
| // not be consulted for addresses within the load image; that is what |
| // memory_image() is for. |
| Allocation Load(ktl::optional<uint64_t> relocation_address = {}, bool in_place_ok = true); |
| |
| size_t vaddr_size() const { return load_info_.vaddr_size(); } |
| |
| // Returns the virtual address where the image will be loaded. Must be called |
| // after Load(). |
| uintptr_t load_address() const { |
| ZX_DEBUG_ASSERT(load_bias_); |
| return static_cast<uintptr_t>(load_info_.vaddr_start() + *load_bias_); |
| } |
| |
| // Set the virtual address where the image will be loaded. |
| // This is the address Relocate() adjusts things for. |
| void set_load_address(uint64_t address) { |
| ZX_ASSERT(address % ZX_PAGE_SIZE == 0); |
| load_bias_ = address - load_info_.vaddr_start(); |
| } |
| |
| // Returns the physical address where the image will be loaded. Must be |
| // called after Load(). |
| uintptr_t physical_load_address() const { |
| return reinterpret_cast<uintptr_t>(memory_image().data()); |
| } |
| |
| // Apply relocations to the image in place after setting the load address. |
| void Relocate(); |
| |
| // Maps the image at its loaded address, mapping each of its load segments |
| // with appropriate access permissions (modulo the execute-only exception of |
| // AddressSpace::Map()). Must be called after Load(). |
| fit::result<AddressSpace::MapError> MapInto(AddressSpace& aspace); |
| |
| // Panic if the loaded file doesn't have a PT_INTERP matching the hex string |
| // corresponding to this build ID note; the prefix is used in panic messages. |
| void AssertInterpMatchesBuildId(ktl::string_view prefix, const elfldltl::ElfNote& build_id); |
| |
| // Set up state to describe the running phys executable. |
| void InitSelf(ktl::string_view name, elfldltl::DirectMemory& memory, uintptr_t load_bias, |
| const elfldltl::Elf<>::Phdr& load_segment, |
| ktl::span<const ktl::byte> build_id_note); |
| |
| // This uses the symbolizer_markup::Writer API to emit the contextual |
| // elements describing this ELF module. The ID number should be unique among |
| // modules in the same address space, i.e. since the last Reset() in the same |
| // markup output stream. |
| template <class Writer> |
| Writer& SymbolizerContext(Writer& writer, unsigned int id, ktl::string_view prefix = {}) const { |
| return load_info_.SymbolizerContext(writer, id, name(), build_id_->desc, load_address(), |
| prefix); |
| } |
| |
| // Publish instrumentation VMOs for this module. The argument is similar |
| // called like HandoffPrep::PublishVmo, which see; PublishDebugdataFunction |
| // takes different arguments to name the data, so it can be used to compose |
| // either a ZBI_TYPE_DEBUGDATA payload or a named VMO. |
| void PublishDebugdata(PublishDebugdataFunction publish_debugdata) const; |
| |
| // Call the image's entry point as a function type F. |
| template <typename F, typename... Args> |
| decltype(auto) Call(Args&&... args) const { |
| static_assert(ktl::is_function_v<F>); |
| F* fnptr = reinterpret_cast<F*>(static_cast<uintptr_t>(entry())); |
| return (*fnptr)(ktl::forward<Args>(args)...); |
| } |
| |
| // Call the image's entry point as a [[noreturn]] function type F. |
| template <typename F, typename... Args> |
| [[noreturn]] void Handoff(Args&&... args) const { |
| static_assert(ktl::is_function_v<F>); |
| Call<F>(ktl::forward<Args>(args)...); |
| ZX_PANIC("ELF image entry point returned!"); |
| } |
| |
| // A function called by Handoff calls this on its own module to provide code |
| // that must be instantiated separately in each module to publish per-module |
| // instrumentation data. |
| void OnHandoff() { publish_self_ = PublishSelf; } |
| |
| private: |
| // PublishSelf takes one of these for each particular kind of per-module |
| // instrumentation VMO supported. The callback knows what kind of data it's |
| // for and deals with calling a PublishDebugdataFunction with the right |
| // arguments. That code exists in the module where PublishDebugdata is |
| // called. PublishSelf exists separately in each module and deals with |
| // filling the buffer with the right per-module data. |
| using PublishSelfCallback = fit::inline_function<ktl::span<ktl::byte>(size_t content_size)>; |
| |
| // publish_self_ is set to point to this by the module itself, so it points |
| // to the instantiation of this function inside that one module. The first |
| // argument is always with the module that contains the code being called. |
| static void PublishSelf(const ElfImage& module, PublishSelfCallback llvmprofdata); |
| |
| // Subroutine of PublishDebugdata only used inside elf-image-vmos.cc. |
| template <const ktl::string_view& kSinkName, const ktl::string_view& kSuffix, |
| const ktl::string_view& kAnnounce> |
| PublishSelfCallback MakePublishSelfCallback(PublishDebugdataFunction& publish_debugdata) const; |
| |
| // This is only called from PublishSelf so it will always be the |
| // instantiation inside this module. |
| void PublishSelfLlvmProfdata(PublishSelfCallback publish) const; |
| |
| ktl::span<const code_patching::Directive> patches() const { return patcher_.patches(); } |
| |
| ktl::span<ktl::byte> GetBytesToPatch(const code_patching::Directive& patch); |
| |
| void PrintPatch(const code_patching::Directive& patch, |
| ktl::initializer_list<ktl::string_view> strings) const; |
| |
| ktl::string_view name_; |
| elfldltl::DirectMemory image_{{}}; |
| LoadInfo load_info_; |
| uint64_t entry_ = 0; |
| ktl::span<const elfldltl::Elf<>::Dyn> dynamic_; |
| ktl::optional<elfldltl::ElfNote> build_id_; |
| ktl::optional<ktl::string_view> interp_; |
| code_patching::Patcher patcher_; |
| ktl::optional<uintptr_t> load_bias_; |
| decltype(PublishSelf)* publish_self_ = nullptr; |
| }; |
| |
| #endif // ZIRCON_KERNEL_PHYS_INCLUDE_PHYS_ELF_IMAGE_H_ |