| // Copyright 2023 The Fuchsia Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "ld-load-zircon-process-tests-base.h" |
| |
| #include <lib/elfldltl/machine.h> |
| |
| #include <gtest/gtest.h> |
| |
| namespace ld::testing { |
| |
| const char* LdLoadZirconProcessTestsBase::process_name() const { |
| return ::testing::UnitTest::GetInstance()->current_test_info()->name(); |
| } |
| |
| void LdLoadZirconProcessTestsBase::set_process(zx::process process) { |
| ASSERT_FALSE(process_); |
| process_ = std::move(process); |
| } |
| |
| int64_t LdLoadZirconProcessTestsBase::Wait() { |
| int64_t result = -1; |
| |
| auto wait_for_termination = [this, &result]() { |
| zx_signals_t signals; |
| ASSERT_EQ(process_.wait_one(ZX_PROCESS_TERMINATED, zx::time::infinite(), &signals), ZX_OK); |
| ASSERT_TRUE(signals & ZX_PROCESS_TERMINATED); |
| zx_info_process_t info; |
| ASSERT_EQ(process_.get_info(ZX_INFO_PROCESS, &info, sizeof(info), nullptr, nullptr), ZX_OK); |
| ASSERT_TRUE(info.flags & ZX_INFO_PROCESS_FLAG_STARTED); |
| ASSERT_TRUE(info.flags & ZX_INFO_PROCESS_FLAG_EXITED); |
| result = info.return_code; |
| }; |
| wait_for_termination(); |
| |
| return result; |
| } |
| |
| int64_t LdLoadZirconProcessTestsBase::Run(TestProcessArgs* bootstrap, |
| std::optional<size_t> stack_size, |
| const zx::thread& thread, uintptr_t entry, |
| uintptr_t vdso_base, const zx::vmar& root_vmar) { |
| // Allocate the stack. This is delayed until here in case the test uses |
| // bootstrap() methods after Init() that affect bootstrap().GetStackSize(). |
| zx::vmo stack_vmo; |
| uintptr_t sp; |
| auto allocate_stack = [&]() { |
| size_t bootstrap_stack_size; |
| if (!bootstrap) { |
| ASSERT_TRUE(stack_size); |
| bootstrap_stack_size = *stack_size; |
| } else { |
| // TODO(mcgrathr): stack use too big for procargs piddly default |
| // bootstrap_stack_size = bootstrap.GetStackSize(); |
| bootstrap_stack_size = 64 << 10; |
| } |
| |
| const size_t page_size = zx_system_get_page_size(); |
| const size_t stack_vmo_size = (bootstrap_stack_size + page_size - 1) & -page_size; |
| const size_t stack_vmar_size = stack_vmo_size + page_size; |
| |
| ASSERT_EQ(zx::vmo::create(stack_vmo_size, 0, &stack_vmo), ZX_OK); |
| |
| zx::vmar stack_vmar; |
| uintptr_t stack_vmar_base; |
| ASSERT_EQ(root_vmar.allocate(ZX_VM_CAN_MAP_SPECIFIC | ZX_VM_CAN_MAP_READ | ZX_VM_CAN_MAP_WRITE, |
| 0, stack_vmar_size, &stack_vmar, &stack_vmar_base), |
| ZX_OK); |
| |
| zx_vaddr_t stack_base; |
| ASSERT_EQ( |
| stack_vmar.map(ZX_VM_PERM_READ | ZX_VM_PERM_WRITE | ZX_VM_SPECIFIC | ZX_VM_ALLOW_FAULTS, |
| page_size, stack_vmo, 0, stack_vmo_size, &stack_base), |
| ZX_OK); |
| |
| if (bootstrap) { |
| ASSERT_NO_FATAL_FAILURE(bootstrap->AddStackVmo(std::move(stack_vmo))); |
| } |
| |
| sp = elfldltl::AbiTraits<>::InitialStackPointer(stack_base, stack_vmo_size); |
| }; |
| |
| allocate_stack(); |
| if (::testing::Test::HasFailure()) { |
| return -1; |
| } |
| |
| // Pack up the bootstrap message and start the process running. |
| auto start_process = [&]() { |
| zx::channel bootstrap_receiver = |
| bootstrap ? bootstrap->PackBootstrap() : zx::channel(ZX_HANDLE_INVALID); |
| |
| ASSERT_EQ(this->process().start(thread, entry, sp, std::move(bootstrap_receiver), vdso_base), |
| ZX_OK); |
| }; |
| |
| start_process(); |
| if (::testing::Test::HasFailure()) { |
| return -1; |
| } |
| |
| return Wait(); |
| } |
| |
| LdLoadZirconProcessTestsBase::~LdLoadZirconProcessTestsBase() { |
| if (process_) { |
| EXPECT_EQ(process_.kill(), ZX_OK); |
| } |
| } |
| |
| } // namespace ld::testing |