| //===----------------------------------------------------------------------===// |
| // |
| // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
| // See https://llvm.org/LICENSE.txt for license information. |
| // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
| // |
| //===----------------------------------------------------------------------===// |
| |
| #include "Plugins/ObjectFile/ELF/ObjectFileELF.h" |
| #include "Plugins/Platform/Linux/PlatformLinux.h" |
| #include "Plugins/ScriptInterpreter/None/ScriptInterpreterNone.h" |
| #include "TestingSupport/SubsystemRAII.h" |
| #include "TestingSupport/TestUtilities.h" |
| #include "lldb/Core/Debugger.h" |
| #include "lldb/Core/Module.h" |
| #include "lldb/Host/FileSystem.h" |
| #include "lldb/Host/HostInfo.h" |
| #include "lldb/Target/Platform.h" |
| #include "lldb/Target/Target.h" |
| #include "llvm/Testing/Support/Error.h" |
| #include "gtest/gtest.h" |
| |
| #include <optional> |
| |
| using namespace lldb_private; |
| using namespace lldb; |
| |
| namespace { |
| |
| // An ELF shared library. A PIE executable is one too. |
| const char *k_shared_library_yaml = R"( |
| --- !ELF |
| FileHeader: |
| Class: ELFCLASS64 |
| Data: ELFDATA2LSB |
| Type: ET_DYN |
| Machine: EM_X86_64 |
| Sections: |
| - Name: .text |
| Type: SHT_PROGBITS |
| Flags: [ SHF_ALLOC, SHF_EXECINSTR ] |
| Address: 0x1000 |
| AddressAlign: 0x10 |
| Content: C3 |
| ... |
| )"; |
| |
| const char *k_executable_yaml = R"( |
| --- !ELF |
| FileHeader: |
| Class: ELFCLASS64 |
| Data: ELFDATA2LSB |
| Type: ET_EXEC |
| Machine: EM_X86_64 |
| Sections: |
| - Name: .text |
| Type: SHT_PROGBITS |
| Flags: [ SHF_ALLOC, SHF_EXECINSTR ] |
| Address: 0x1000 |
| AddressAlign: 0x10 |
| Content: C3 |
| ... |
| )"; |
| |
| class ExecutableModuleTest : public testing::Test { |
| public: |
| void SetUp() override { |
| llvm::Expected<TestFile> shared_library = |
| TestFile::fromYaml(k_shared_library_yaml); |
| ASSERT_THAT_EXPECTED(shared_library, llvm::Succeeded()); |
| m_shared_library.emplace(std::move(*shared_library)); |
| llvm::Expected<TestFile> executable = TestFile::fromYaml(k_executable_yaml); |
| ASSERT_THAT_EXPECTED(executable, llvm::Succeeded()); |
| m_executable.emplace(std::move(*executable)); |
| |
| ArchSpec arch("x86_64-pc-linux"); |
| std::call_once(TestUtilities::g_debugger_initialize_flag, |
| []() { Debugger::Initialize(nullptr); }); |
| Platform::SetHostPlatform( |
| platform_linux::PlatformLinux::CreateInstance(true, &arch)); |
| m_debugger_sp = Debugger::CreateInstance(); |
| ASSERT_TRUE(m_debugger_sp); |
| PlatformSP platform_sp; |
| m_debugger_sp->GetTargetList().CreateTarget( |
| *m_debugger_sp, "", arch, eLoadDependentsNo, platform_sp, m_target_sp); |
| ASSERT_TRUE(m_target_sp); |
| } |
| |
| void TearDown() override { |
| m_target_sp.reset(); |
| if (m_debugger_sp) |
| Debugger::Destroy(m_debugger_sp); |
| } |
| |
| ModuleSP CreateSharedLibrary() { |
| return std::make_shared<Module>(m_shared_library->moduleSpec()); |
| } |
| |
| ModuleSP CreateExecutable() { |
| return std::make_shared<Module>(m_executable->moduleSpec()); |
| } |
| |
| SubsystemRAII<FileSystem, HostInfo, ObjectFileELF, |
| platform_linux::PlatformLinux, ScriptInterpreterNone> |
| subsystems; |
| // These back the target's modules and must outlive them. |
| std::optional<TestFile> m_shared_library; |
| std::optional<TestFile> m_executable; |
| DebuggerSP m_debugger_sp; |
| TargetSP m_target_sp; |
| }; |
| |
| } // namespace |
| |
| TEST_F(ExecutableModuleTest, NoExecutable) { |
| m_target_sp->GetImages().Append(CreateSharedLibrary(), /*notify=*/false); |
| EXPECT_EQ(m_target_sp->GetExecutableModule(), nullptr); |
| } |
| |
| TEST_F(ExecutableModuleTest, SharedLibraryExecutable) { |
| ModuleSP executable_sp = CreateSharedLibrary(); |
| m_target_sp->RebuildModuleListWithExecutable(executable_sp, |
| eLoadDependentsNo); |
| m_target_sp->GetImages().Append(CreateSharedLibrary(), /*notify=*/false); |
| EXPECT_EQ(m_target_sp->GetExecutableModule(), executable_sp); |
| } |
| |
| TEST_F(ExecutableModuleTest, MarkKeepsImages) { |
| ModuleList &images = m_target_sp->GetImages(); |
| images.Append(CreateSharedLibrary(), /*notify=*/false); |
| ModuleSP executable_sp = CreateSharedLibrary(); |
| images.Append(executable_sp, /*notify=*/false); |
| |
| m_target_sp->MarkExecutableModule(executable_sp); |
| EXPECT_EQ(m_target_sp->GetExecutableModule(), executable_sp); |
| EXPECT_EQ(images.GetSize(), 2u); |
| } |
| |
| TEST_F(ExecutableModuleTest, RemovedExecutable) { |
| ModuleSP executable_sp = CreateSharedLibrary(); |
| m_target_sp->RebuildModuleListWithExecutable(executable_sp, |
| eLoadDependentsNo); |
| ModuleList &images = m_target_sp->GetImages(); |
| images.Append(CreateSharedLibrary(), /*notify=*/false); |
| |
| images.Remove(executable_sp); |
| EXPECT_EQ(m_target_sp->GetExecutableModule(), nullptr); |
| } |
| |
| TEST_F(ExecutableModuleTest, ReplacedExecutable) { |
| ModuleSP executable_sp = CreateSharedLibrary(); |
| m_target_sp->RebuildModuleListWithExecutable(executable_sp, |
| eLoadDependentsNo); |
| m_target_sp->GetImages().Append(CreateSharedLibrary(), /*notify=*/false); |
| |
| // A replacement only becomes the executable once the target is rebuilt |
| // around it, which callers decide by comparing it against the executable. |
| ModuleSP replacement_sp = CreateSharedLibrary(); |
| m_target_sp->GetImages().ReplaceModule(executable_sp, replacement_sp); |
| EXPECT_EQ(m_target_sp->GetExecutableModule(), nullptr); |
| } |
| |
| TEST_F(ExecutableModuleTest, ExecutableTypeFirst) { |
| ModuleSP library_sp = CreateSharedLibrary(); |
| m_target_sp->RebuildModuleListWithExecutable(library_sp, eLoadDependentsNo); |
| ModuleSP executable_sp = CreateExecutable(); |
| m_target_sp->GetImages().Append(executable_sp, /*notify=*/false); |
| EXPECT_EQ(m_target_sp->GetExecutableModule(), executable_sp); |
| } |