| // 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 |
| |
| #include <inttypes.h> |
| #include <lib/elfldltl/diagnostics.h> |
| #include <lib/elfldltl/dynamic.h> |
| #include <lib/elfldltl/layout.h> |
| #include <lib/elfldltl/link.h> |
| #include <lib/elfldltl/memory.h> |
| #include <lib/elfldltl/phdr.h> |
| #include <lib/memalloc/range.h> |
| #include <lib/zbitl/error-stdio.h> |
| #include <lib/zbitl/items/bootfs.h> |
| #include <lib/zbitl/view.h> |
| #include <stdio.h> |
| #include <zircon/limits.h> |
| |
| #include <fbl/alloc_checker.h> |
| #include <ktl/atomic.h> |
| #include <ktl/byte.h> |
| #include <ktl/limits.h> |
| #include <ktl/optional.h> |
| #include <ktl/span.h> |
| #include <ktl/string_view.h> |
| #include <phys/kernel-package.h> |
| #include <phys/symbolize.h> |
| #include <phys/zbitl-allocation.h> |
| |
| #include "../test-main.h" |
| |
| #include <ktl/enforce.h> |
| |
| const char Symbolize::kProgramName_[] = "basic-elf-loading-test"; |
| |
| namespace { |
| |
| using BootfsView = zbitl::BootfsView<ktl::span<const ktl::byte>>; |
| |
| using Elf = elfldltl::Elf<elfldltl::ElfClass::kNative>; |
| using Dyn = typename Elf::Dyn; |
| using Ehdr = typename Elf::Ehdr; |
| using Phdr = typename Elf::Phdr; |
| |
| // The name of ELF module to be loaded. |
| constexpr ktl::string_view kGetInt = "get-int"; |
| |
| // The BOOTFS namespace under which kGetInt lives. |
| constexpr ktl::string_view kNamespace = "loadables"; |
| |
| } // namespace |
| |
| int TestMain(void* zbi_ptr, arch::EarlyTicks) { |
| // Initialize memory for allocation/free. |
| InitMemory(zbi_ptr); |
| |
| zbitl::View zbi( |
| zbitl::StorageFromRawHeader<ktl::span<ktl::byte>>(static_cast<zbi_header_t*>(zbi_ptr))); |
| KernelStorage kernelfs; |
| kernelfs.Init(zbi); |
| |
| BootfsView bootfs = kernelfs.GetBootfs(); |
| auto it = bootfs.find({kNamespace, kGetInt}); |
| if (auto result = bootfs.take_error(); result.is_error()) { |
| zbitl::PrintBootfsError(result.error_value()); |
| return 1; |
| } |
| if (it == bootfs.end()) { |
| printf("FAILED: Cannot find %.*s/%.*s in BOOTFS\n", // |
| static_cast<int>(kNamespace.size()), kNamespace.data(), // |
| static_cast<int>(kGetInt.size()), kGetInt.data()); |
| return 1; |
| } |
| |
| // Now that we've found the module, we can load it. |
| |
| ktl::span<ktl::byte> elf_bytes = {const_cast<ktl::byte*>(it->data.data()), it->data.size()}; |
| |
| // We are just reading from the file and so don't worry about the base address. |
| elfldltl::DirectMemory file{elf_bytes}; |
| |
| auto ehdr_result = file.ReadFromFile<Ehdr>(0); |
| if (!ehdr_result) { |
| printf("FAILED: to read ELF header\n"); |
| return 1; |
| } |
| const Ehdr& ehdr = *ehdr_result; |
| ZX_ASSERT(ehdr.Loadable()); |
| |
| // TODO(fxbug.dev/91400): Handle this case. |
| ZX_ASSERT(ehdr.phnum != Ehdr::kPnXnum); |
| |
| auto phdr_allocator = elfldltl::NoArrayFromFile<Phdr>(); |
| ktl::span<const Phdr> phdrs; |
| if (auto result = file.ReadArrayFromFile<Phdr>(ehdr.phoff(), phdr_allocator, |
| ehdr.phnum() * ehdr.phentsize() / sizeof(Phdr))) { |
| phdrs = *result; |
| } else { |
| printf("FAILED: to read phdrs\n"); |
| return 1; |
| } |
| |
| // Since `bootfs`'s underlying buffer is ZBI_BOOTFS_PAGE_SIZE-aligned (a |
| // KernelStorage guarantee), so too will the ELF payload (a BOOTFS |
| // guarantee). Assert that this implies runtime page size alignment as well. |
| // |
| // TODO(mcgrathr): Using `ZX_PAGE_SIZE` here as the runtime page size for ELF |
| // purposes in physboot is the right thing to do now, but should be |
| // revisited. |
| static_assert(ZX_PAGE_SIZE <= ZBI_BOOTFS_PAGE_SIZE); |
| |
| auto diag = elfldltl::PanicDiagnostics("FAILED: "); |
| |
| // Parse phdrs to find the dynamic sections and to validate that the load |
| // segments comprise a contiguous layout. A contiguous layout - paired with |
| // the fact that the file is already appropriately aligned - implies that |
| // the file in memory is already suitable as a load image. |
| ktl::optional<Phdr> dyn_phdr; |
| uint64_t vaddr_start, vaddr_size; |
| elfldltl::DecodePhdrs(diag, phdrs, elfldltl::PhdrDynamicObserver<Elf>(dyn_phdr), |
| elfldltl::PhdrLoadObserver<Elf, elfldltl::PhdrLoadPolicy::kContiguous>( |
| ZX_PAGE_SIZE, vaddr_start, vaddr_size)); |
| file.set_base(vaddr_start); |
| |
| if (!dyn_phdr) { |
| printf("FAILED: no dynamic sections found\n"); |
| return 1; |
| } |
| |
| auto dyn_allocator = elfldltl::NoArrayFromFile<Dyn>(); |
| ktl::span<const Dyn> dyn; |
| if (auto result = file.ReadArrayFromFile<Dyn>(dyn_phdr->offset(), dyn_allocator, |
| dyn_phdr->filesz() / sizeof(Dyn)); |
| !result) { |
| printf("FAILED: to read dynamic sections\n"); |
| return 1; |
| } else { |
| dyn = *result; |
| } |
| |
| // Parse the dynamic sections for relocation info. |
| elfldltl::RelocationInfo<Elf> reloc_info; |
| elfldltl::DecodeDynamic(diag, file, dyn, elfldltl::DynamicRelocationInfoObserver(reloc_info)); |
| |
| // Apply relocations. |
| auto runtime_load_addr = reinterpret_cast<uint64_t>(elf_bytes.data()); |
| uint64_t load_bias = runtime_load_addr - vaddr_start; |
| if (!elfldltl::RelocateRelative(file, reloc_info, load_bias)) { |
| printf("FAILED: relocation failed\n"); |
| return 1; |
| } |
| ktl::atomic_signal_fence(ktl::memory_order_seq_cst); |
| |
| // We should now be able to access GetInt()! |
| auto GetInt = reinterpret_cast<int (*)()>(ehdr.entry() + load_bias); |
| constexpr int kExpected = 42; |
| if (int actual = GetInt(); actual != kExpected) { |
| printf("FAILED: Expected %d; got %d\n", kExpected, actual); |
| return 1; |
| } |
| |
| return 0; |
| } |