From 6c270cf239dfcbde2ce60fea3cc9d61f3800902d Mon Sep 17 00:00:00 2001 From: Joshua Peraza Date: Thu, 3 Dec 2020 13:24:06 -0800 Subject: [PATCH] linux: Refactor test modules This patch moves LoadModule() to it's own file from process_reader_linux_test.cc so that it may be used in other tests that interact with loaded modules. Change-Id: Ie4f7932d65710fc3e20b6e2488e497c5aab27cdd Reviewed-on: https://chromium-review.googlesource.com/c/crashpad/crashpad/+/2569882 Reviewed-by: Mark Mentovai Commit-Queue: Joshua Peraza --- snapshot/BUILD.gn | 2 + snapshot/linux/process_reader_linux_test.cc | 213 +---------------- snapshot/linux/test_modules.cc | 249 ++++++++++++++++++++ snapshot/linux/test_modules.h | 37 +++ snapshot/snapshot_test.gyp | 2 + 5 files changed, 291 insertions(+), 212 deletions(-) create mode 100644 snapshot/linux/test_modules.cc create mode 100644 snapshot/linux/test_modules.h diff --git a/snapshot/BUILD.gn b/snapshot/BUILD.gn index 6ca9bd40..79f5eeb1 100644 --- a/snapshot/BUILD.gn +++ b/snapshot/BUILD.gn @@ -368,6 +368,8 @@ source_set("snapshot_test") { "linux/exception_snapshot_linux_test.cc", "linux/process_reader_linux_test.cc", "linux/system_snapshot_linux_test.cc", + "linux/test_modules.cc", + "linux/test_modules.h", "sanitized/process_snapshot_sanitized_test.cc", "sanitized/sanitization_information_test.cc", ] diff --git a/snapshot/linux/process_reader_linux_test.cc b/snapshot/linux/process_reader_linux_test.cc index 98b99e65..c3b8723b 100644 --- a/snapshot/linux/process_reader_linux_test.cc +++ b/snapshot/linux/process_reader_linux_test.cc @@ -37,6 +37,7 @@ #include "base/strings/stringprintf.h" #include "build/build_config.h" #include "gtest/gtest.h" +#include "snapshot/linux/test_modules.h" #include "test/errors.h" #include "test/linux/fake_ptrace_connection.h" #include "test/linux/get_tls.h" @@ -541,218 +542,6 @@ void ExpectModulesFromSelf( #endif // !OS_ANDROID || !ARCH_CPU_ARMEL || __ANDROID_API__ >= 21 } -bool WriteTestModule(const base::FilePath& module_path, - const std::string& soname) { -#if defined(ARCH_CPU_64_BITS) - using Ehdr = Elf64_Ehdr; - using Phdr = Elf64_Phdr; - using Shdr = Elf64_Shdr; - using Dyn = Elf64_Dyn; - using Sym = Elf64_Sym; - unsigned char elf_class = ELFCLASS64; -#else - using Ehdr = Elf32_Ehdr; - using Phdr = Elf32_Phdr; - using Shdr = Elf32_Shdr; - using Dyn = Elf32_Dyn; - using Sym = Elf32_Sym; - unsigned char elf_class = ELFCLASS32; -#endif - - struct { - Ehdr ehdr; - struct { - Phdr load1; - Phdr load2; - Phdr dynamic; - } phdr_table; - struct { - Dyn hash; - Dyn strtab; - Dyn symtab; - Dyn strsz; - Dyn syment; - Dyn soname; - Dyn null; - } dynamic_array; - struct { - Elf32_Word nbucket; - Elf32_Word nchain; - Elf32_Word bucket; - Elf32_Word chain; - } hash_table; - char string_table[32]; - struct { - } section_header_string_table; - struct { - Sym und_symbol; - } symbol_table; - struct { - Shdr null; - Shdr dynamic; - Shdr string_table; - Shdr section_header_string_table; - } shdr_table; - } module = {}; - - module.ehdr.e_ident[EI_MAG0] = ELFMAG0; - module.ehdr.e_ident[EI_MAG1] = ELFMAG1; - module.ehdr.e_ident[EI_MAG2] = ELFMAG2; - module.ehdr.e_ident[EI_MAG3] = ELFMAG3; - - module.ehdr.e_ident[EI_CLASS] = elf_class; - -#if defined(ARCH_CPU_LITTLE_ENDIAN) - module.ehdr.e_ident[EI_DATA] = ELFDATA2LSB; -#else - module.ehdr.e_ident[EI_DATA] = ELFDATA2MSB; -#endif // ARCH_CPU_LITTLE_ENDIAN - - module.ehdr.e_ident[EI_VERSION] = EV_CURRENT; - - module.ehdr.e_type = ET_DYN; - -#if defined(ARCH_CPU_X86) - module.ehdr.e_machine = EM_386; -#elif defined(ARCH_CPU_X86_64) - module.ehdr.e_machine = EM_X86_64; -#elif defined(ARCH_CPU_ARMEL) - module.ehdr.e_machine = EM_ARM; -#elif defined(ARCH_CPU_ARM64) - module.ehdr.e_machine = EM_AARCH64; -#elif defined(ARCH_CPU_MIPSEL) || defined(ARCH_CPU_MIPS64EL) - module.ehdr.e_machine = EM_MIPS; -#endif - - module.ehdr.e_version = EV_CURRENT; - module.ehdr.e_ehsize = sizeof(module.ehdr); - - module.ehdr.e_phoff = offsetof(decltype(module), phdr_table); - module.ehdr.e_phnum = sizeof(module.phdr_table) / sizeof(Phdr); - module.ehdr.e_phentsize = sizeof(Phdr); - - module.ehdr.e_shoff = offsetof(decltype(module), shdr_table); - module.ehdr.e_shentsize = sizeof(Shdr); - module.ehdr.e_shnum = sizeof(module.shdr_table) / sizeof(Shdr); - module.ehdr.e_shstrndx = - offsetof(decltype(module.shdr_table), section_header_string_table) / - sizeof(Shdr); - - constexpr size_t load2_vaddr = 0x200000; - - module.phdr_table.load1.p_type = PT_LOAD; - module.phdr_table.load1.p_offset = 0; - module.phdr_table.load1.p_vaddr = 0; - module.phdr_table.load1.p_filesz = offsetof(decltype(module), shdr_table); - module.phdr_table.load1.p_memsz = offsetof(decltype(module), shdr_table); - module.phdr_table.load1.p_flags = PF_R; - module.phdr_table.load1.p_align = load2_vaddr; - - module.phdr_table.load2.p_type = PT_LOAD; - module.phdr_table.load2.p_offset = 0; - module.phdr_table.load2.p_vaddr = load2_vaddr; - module.phdr_table.load2.p_filesz = offsetof(decltype(module), shdr_table); - module.phdr_table.load2.p_memsz = offsetof(decltype(module), shdr_table); - module.phdr_table.load2.p_flags = PF_R | PF_W; - module.phdr_table.load2.p_align = load2_vaddr; - - module.phdr_table.dynamic.p_type = PT_DYNAMIC; - module.phdr_table.dynamic.p_offset = - offsetof(decltype(module), dynamic_array); - module.phdr_table.dynamic.p_vaddr = - load2_vaddr + module.phdr_table.dynamic.p_offset; - module.phdr_table.dynamic.p_filesz = sizeof(module.dynamic_array); - module.phdr_table.dynamic.p_memsz = sizeof(module.dynamic_array); - module.phdr_table.dynamic.p_flags = PF_R | PF_W; - module.phdr_table.dynamic.p_align = 8; - - module.dynamic_array.hash.d_tag = DT_HASH; - module.dynamic_array.hash.d_un.d_ptr = offsetof(decltype(module), hash_table); - module.dynamic_array.strtab.d_tag = DT_STRTAB; - module.dynamic_array.strtab.d_un.d_ptr = - offsetof(decltype(module), string_table); - module.dynamic_array.symtab.d_tag = DT_SYMTAB; - module.dynamic_array.symtab.d_un.d_ptr = - offsetof(decltype(module), symbol_table); - module.dynamic_array.strsz.d_tag = DT_STRSZ; - module.dynamic_array.strsz.d_un.d_val = sizeof(module.string_table); - module.dynamic_array.syment.d_tag = DT_SYMENT; - module.dynamic_array.syment.d_un.d_val = sizeof(Sym); - constexpr size_t kSonameOffset = 1; - module.dynamic_array.soname.d_tag = DT_SONAME; - module.dynamic_array.soname.d_un.d_val = kSonameOffset; - - module.dynamic_array.null.d_tag = DT_NULL; - - module.hash_table.nbucket = 1; - module.hash_table.nchain = 1; - module.hash_table.bucket = 0; - module.hash_table.chain = 0; - - CHECK_GE(sizeof(module.string_table), soname.size() + 2); - module.string_table[0] = '\0'; - memcpy(&module.string_table[kSonameOffset], soname.c_str(), soname.size()); - - module.shdr_table.null.sh_type = SHT_NULL; - - module.shdr_table.dynamic.sh_name = 0; - module.shdr_table.dynamic.sh_type = SHT_DYNAMIC; - module.shdr_table.dynamic.sh_flags = SHF_WRITE | SHF_ALLOC; - module.shdr_table.dynamic.sh_addr = module.phdr_table.dynamic.p_vaddr; - module.shdr_table.dynamic.sh_offset = module.phdr_table.dynamic.p_offset; - module.shdr_table.dynamic.sh_size = module.phdr_table.dynamic.p_filesz; - module.shdr_table.dynamic.sh_link = - offsetof(decltype(module.shdr_table), string_table) / sizeof(Shdr); - - module.shdr_table.string_table.sh_name = 0; - module.shdr_table.string_table.sh_type = SHT_STRTAB; - module.shdr_table.string_table.sh_offset = - offsetof(decltype(module), string_table); - module.shdr_table.string_table.sh_size = sizeof(module.string_table); - - module.shdr_table.section_header_string_table.sh_name = 0; - module.shdr_table.section_header_string_table.sh_type = SHT_STRTAB; - module.shdr_table.section_header_string_table.sh_offset = - offsetof(decltype(module), section_header_string_table); - module.shdr_table.section_header_string_table.sh_size = - sizeof(module.section_header_string_table); - - FileWriter writer; - if (!writer.Open(module_path, - FileWriteMode::kCreateOrFail, - FilePermissions::kWorldReadable)) { - ADD_FAILURE(); - return false; - } - - if (!writer.Write(&module, sizeof(module))) { - ADD_FAILURE(); - return false; - } - - return true; -} - -ScopedModuleHandle LoadTestModule(const std::string& module_name, - const std::string& module_soname) { - base::FilePath module_path( - TestPaths::Executable().DirName().Append(module_name)); - - if (!WriteTestModule(module_path, module_soname)) { - return ScopedModuleHandle(nullptr); - } - EXPECT_TRUE(IsRegularFile(module_path)); - - ScopedModuleHandle handle( - dlopen(module_path.value().c_str(), RTLD_LAZY | RTLD_LOCAL)); - EXPECT_TRUE(handle.valid()) - << "dlopen: " << module_path.value() << " " << dlerror(); - - EXPECT_TRUE(LoggingRemoveFile(module_path)); - - return handle; -} - void ExpectTestModule(ProcessReaderLinux* reader, const std::string& module_name) { for (const auto& module : reader->Modules()) { diff --git a/snapshot/linux/test_modules.cc b/snapshot/linux/test_modules.cc new file mode 100644 index 00000000..f4743212 --- /dev/null +++ b/snapshot/linux/test_modules.cc @@ -0,0 +1,249 @@ +// Copyright 2020 The Crashpad Authors. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "snapshot/linux/test_modules.h" + +#include + +#include + +#include "base/check_op.h" +#include "base/files/file_path.h" +#include "build/build_config.h" +#include "gtest/gtest.h" +#include "test/test_paths.h" +#include "util/file/filesystem.h" +#include "util/file/file_writer.h" + +namespace crashpad { +namespace test { + +bool WriteTestModule(const base::FilePath& module_path, + const std::string& soname) { +#if defined(ARCH_CPU_64_BITS) + using Ehdr = Elf64_Ehdr; + using Phdr = Elf64_Phdr; + using Shdr = Elf64_Shdr; + using Dyn = Elf64_Dyn; + using Sym = Elf64_Sym; + unsigned char elf_class = ELFCLASS64; +#else + using Ehdr = Elf32_Ehdr; + using Phdr = Elf32_Phdr; + using Shdr = Elf32_Shdr; + using Dyn = Elf32_Dyn; + using Sym = Elf32_Sym; + unsigned char elf_class = ELFCLASS32; +#endif + + struct { + Ehdr ehdr; + struct { + Phdr load1; + Phdr load2; + Phdr dynamic; + } phdr_table; + struct { + Dyn hash; + Dyn strtab; + Dyn symtab; + Dyn strsz; + Dyn syment; + Dyn soname; + Dyn null; + } dynamic_array; + struct { + Elf32_Word nbucket; + Elf32_Word nchain; + Elf32_Word bucket; + Elf32_Word chain; + } hash_table; + char string_table[32]; + struct { + } section_header_string_table; + struct { + Sym und_symbol; + } symbol_table; + struct { + Shdr null; + Shdr dynamic; + Shdr string_table; + Shdr section_header_string_table; + } shdr_table; + } module = {}; + + module.ehdr.e_ident[EI_MAG0] = ELFMAG0; + module.ehdr.e_ident[EI_MAG1] = ELFMAG1; + module.ehdr.e_ident[EI_MAG2] = ELFMAG2; + module.ehdr.e_ident[EI_MAG3] = ELFMAG3; + + module.ehdr.e_ident[EI_CLASS] = elf_class; + +#if defined(ARCH_CPU_LITTLE_ENDIAN) + module.ehdr.e_ident[EI_DATA] = ELFDATA2LSB; +#else + module.ehdr.e_ident[EI_DATA] = ELFDATA2MSB; +#endif // ARCH_CPU_LITTLE_ENDIAN + + module.ehdr.e_ident[EI_VERSION] = EV_CURRENT; + + module.ehdr.e_type = ET_DYN; + +#if defined(ARCH_CPU_X86) + module.ehdr.e_machine = EM_386; +#elif defined(ARCH_CPU_X86_64) + module.ehdr.e_machine = EM_X86_64; +#elif defined(ARCH_CPU_ARMEL) + module.ehdr.e_machine = EM_ARM; +#elif defined(ARCH_CPU_ARM64) + module.ehdr.e_machine = EM_AARCH64; +#elif defined(ARCH_CPU_MIPSEL) || defined(ARCH_CPU_MIPS64EL) + module.ehdr.e_machine = EM_MIPS; +#endif + + module.ehdr.e_version = EV_CURRENT; + module.ehdr.e_ehsize = sizeof(module.ehdr); + + module.ehdr.e_phoff = offsetof(decltype(module), phdr_table); + module.ehdr.e_phnum = sizeof(module.phdr_table) / sizeof(Phdr); + module.ehdr.e_phentsize = sizeof(Phdr); + + module.ehdr.e_shoff = offsetof(decltype(module), shdr_table); + module.ehdr.e_shentsize = sizeof(Shdr); + module.ehdr.e_shnum = sizeof(module.shdr_table) / sizeof(Shdr); + module.ehdr.e_shstrndx = + offsetof(decltype(module.shdr_table), section_header_string_table) / + sizeof(Shdr); + + constexpr size_t load2_vaddr = 0x200000; + + module.phdr_table.load1.p_type = PT_LOAD; + module.phdr_table.load1.p_offset = 0; + module.phdr_table.load1.p_vaddr = 0; + module.phdr_table.load1.p_filesz = offsetof(decltype(module), shdr_table); + module.phdr_table.load1.p_memsz = offsetof(decltype(module), shdr_table); + module.phdr_table.load1.p_flags = PF_R; + module.phdr_table.load1.p_align = load2_vaddr; + + module.phdr_table.load2.p_type = PT_LOAD; + module.phdr_table.load2.p_offset = 0; + module.phdr_table.load2.p_vaddr = load2_vaddr; + module.phdr_table.load2.p_filesz = offsetof(decltype(module), shdr_table); + module.phdr_table.load2.p_memsz = offsetof(decltype(module), shdr_table); + module.phdr_table.load2.p_flags = PF_R | PF_W; + module.phdr_table.load2.p_align = load2_vaddr; + + module.phdr_table.dynamic.p_type = PT_DYNAMIC; + module.phdr_table.dynamic.p_offset = + offsetof(decltype(module), dynamic_array); + module.phdr_table.dynamic.p_vaddr = + load2_vaddr + module.phdr_table.dynamic.p_offset; + module.phdr_table.dynamic.p_filesz = sizeof(module.dynamic_array); + module.phdr_table.dynamic.p_memsz = sizeof(module.dynamic_array); + module.phdr_table.dynamic.p_flags = PF_R | PF_W; + module.phdr_table.dynamic.p_align = 8; + + module.dynamic_array.hash.d_tag = DT_HASH; + module.dynamic_array.hash.d_un.d_ptr = offsetof(decltype(module), hash_table); + module.dynamic_array.strtab.d_tag = DT_STRTAB; + module.dynamic_array.strtab.d_un.d_ptr = + offsetof(decltype(module), string_table); + module.dynamic_array.symtab.d_tag = DT_SYMTAB; + module.dynamic_array.symtab.d_un.d_ptr = + offsetof(decltype(module), symbol_table); + module.dynamic_array.strsz.d_tag = DT_STRSZ; + module.dynamic_array.strsz.d_un.d_val = sizeof(module.string_table); + module.dynamic_array.syment.d_tag = DT_SYMENT; + module.dynamic_array.syment.d_un.d_val = sizeof(Sym); + constexpr size_t kSonameOffset = 1; + module.dynamic_array.soname.d_tag = DT_SONAME; + module.dynamic_array.soname.d_un.d_val = kSonameOffset; + + module.dynamic_array.null.d_tag = DT_NULL; + + module.hash_table.nbucket = 1; + module.hash_table.nchain = 1; + module.hash_table.bucket = 0; + module.hash_table.chain = 0; + + if (sizeof(module.string_table) < soname.size() + 2) { + ADD_FAILURE() << "string table too small"; + return false; + } + module.string_table[0] = '\0'; + memcpy(&module.string_table[kSonameOffset], soname.c_str(), soname.size()); + + module.shdr_table.null.sh_type = SHT_NULL; + + module.shdr_table.dynamic.sh_name = 0; + module.shdr_table.dynamic.sh_type = SHT_DYNAMIC; + module.shdr_table.dynamic.sh_flags = SHF_WRITE | SHF_ALLOC; + module.shdr_table.dynamic.sh_addr = module.phdr_table.dynamic.p_vaddr; + module.shdr_table.dynamic.sh_offset = module.phdr_table.dynamic.p_offset; + module.shdr_table.dynamic.sh_size = module.phdr_table.dynamic.p_filesz; + module.shdr_table.dynamic.sh_link = + offsetof(decltype(module.shdr_table), string_table) / sizeof(Shdr); + + module.shdr_table.string_table.sh_name = 0; + module.shdr_table.string_table.sh_type = SHT_STRTAB; + module.shdr_table.string_table.sh_offset = + offsetof(decltype(module), string_table); + module.shdr_table.string_table.sh_size = sizeof(module.string_table); + + module.shdr_table.section_header_string_table.sh_name = 0; + module.shdr_table.section_header_string_table.sh_type = SHT_STRTAB; + module.shdr_table.section_header_string_table.sh_offset = + offsetof(decltype(module), section_header_string_table); + module.shdr_table.section_header_string_table.sh_size = + sizeof(module.section_header_string_table); + + FileWriter writer; + if (!writer.Open(module_path, + FileWriteMode::kCreateOrFail, + FilePermissions::kWorldReadable)) { + ADD_FAILURE(); + return false; + } + + if (!writer.Write(&module, sizeof(module))) { + ADD_FAILURE(); + LoggingRemoveFile(module_path); + return false; + } + + return true; +} + +ScopedModuleHandle LoadTestModule(const std::string& module_name, + const std::string& module_soname) { + base::FilePath module_path( + TestPaths::Executable().DirName().Append(module_name)); + + if (!WriteTestModule(module_path, module_soname)) { + return ScopedModuleHandle(nullptr); + } + EXPECT_TRUE(IsRegularFile(module_path)); + + ScopedModuleHandle handle( + dlopen(module_path.value().c_str(), RTLD_LAZY | RTLD_LOCAL)); + EXPECT_TRUE(handle.valid()) + << "dlopen: " << module_path.value() << " " << dlerror(); + + EXPECT_TRUE(LoggingRemoveFile(module_path)); + + return handle; +} + +} // namespace test +} // namespace crashpad diff --git a/snapshot/linux/test_modules.h b/snapshot/linux/test_modules.h new file mode 100644 index 00000000..b791480a --- /dev/null +++ b/snapshot/linux/test_modules.h @@ -0,0 +1,37 @@ +// Copyright 2020 The Crashpad Authors. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef CRASHPAD_SNAPSHOT_LINUX_TEST_MODULES_H_ +#define CRASHPAD_SNAPSHOT_LINUX_TEST_MODULES_H_ + +#include + +#include "test/scoped_module_handle.h" + +namespace crashpad { +namespace test { + +//! \brief Constructs and loads a test module. +//! +//! \param module_name The filename of the mdoule. +//! \param module_soname The SONAME for the module. +//! \return a handle to the loaded module on success. On failure, the handle +//! will be invalid and a message will be logged. +ScopedModuleHandle LoadTestModule(const std::string& module_name, + const std::string& module_soname); + +} // namespace test +} // namespace crashpad + +#endif // CRASHPAD_SNAPSHOT_LINUX_DEBUG_RENDEZVOUS_H_ diff --git a/snapshot/snapshot_test.gyp b/snapshot/snapshot_test.gyp index 43de7254..a4d4d9e0 100644 --- a/snapshot/snapshot_test.gyp +++ b/snapshot/snapshot_test.gyp @@ -80,6 +80,8 @@ 'linux/exception_snapshot_linux_test.cc', 'linux/process_reader_linux_test.cc', 'linux/system_snapshot_linux_test.cc', + 'linux/test_modules.cc', + 'linux/test_modules.h', 'mac/cpu_context_mac_test.cc', 'mac/mach_o_image_annotations_reader_test.cc', 'mac/mach_o_image_reader_test.cc',