diff --git a/libunwindstack/Android.bp b/libunwindstack/Android.bp index 8776db7a2..5445f6ec9 100644 --- a/libunwindstack/Android.bp +++ b/libunwindstack/Android.bp @@ -98,8 +98,10 @@ cc_defaults { "tests/ElfInterfaceArmTest.cpp", "tests/ElfInterfaceTest.cpp", "tests/ElfTest.cpp", + "tests/ElfTestUtils.cpp", "tests/LogFake.cpp", - "tests/MapInfoTest.cpp", + "tests/MapInfoCreateMemoryTest.cpp", + "tests/MapInfoGetElfTest.cpp", "tests/MapsTest.cpp", "tests/MemoryFake.cpp", "tests/MemoryFileTest.cpp", @@ -144,8 +146,8 @@ cc_test { ], data: [ - "tests/elf32.xz", - "tests/elf64.xz", + "tests/files/elf32.xz", + "tests/files/elf64.xz", ], } diff --git a/libunwindstack/MapInfo.cpp b/libunwindstack/MapInfo.cpp index 051f70061..d7b483de6 100644 --- a/libunwindstack/MapInfo.cpp +++ b/libunwindstack/MapInfo.cpp @@ -73,13 +73,15 @@ Memory* MapInfo::CreateMemory(pid_t pid) { return new MemoryRange(memory, start, end); } -Elf* MapInfo::GetElf(pid_t pid, bool) { +Elf* MapInfo::GetElf(pid_t pid, bool init_gnu_debugdata) { if (elf) { return elf; } elf = new Elf(CreateMemory(pid)); - elf->Init(); + if (elf->Init() && init_gnu_debugdata) { + elf->InitGnuDebugdata(); + } // If the init fails, keep the elf around as an invalid object so we // don't try to reinit the object. return elf; diff --git a/libunwindstack/tests/ElfTest.cpp b/libunwindstack/tests/ElfTest.cpp index 22cade25c..bf0823bb4 100644 --- a/libunwindstack/tests/ElfTest.cpp +++ b/libunwindstack/tests/ElfTest.cpp @@ -24,6 +24,7 @@ #include "Elf.h" +#include "ElfTestUtils.h" #include "MemoryFake.h" #if !defined(PT_ARM_EXIDX) @@ -36,36 +37,16 @@ class ElfTest : public ::testing::Test { memory_ = new MemoryFake; } - template - void InitEhdr(Ehdr* ehdr) { - memset(ehdr, 0, sizeof(Ehdr)); - memcpy(&ehdr->e_ident[0], ELFMAG, SELFMAG); - ehdr->e_ident[EI_DATA] = ELFDATA2LSB; - ehdr->e_ident[EI_VERSION] = EV_CURRENT; - ehdr->e_ident[EI_OSABI] = ELFOSABI_SYSV; - - ehdr->e_type = ET_DYN; - ehdr->e_version = EV_CURRENT; - } - - void InitElf32(uint32_t machine) { + void InitElf32(uint32_t machine_type) { Elf32_Ehdr ehdr; + TestInitEhdr(&ehdr, ELFCLASS32, machine_type); - InitEhdr(&ehdr); - ehdr.e_ident[EI_CLASS] = ELFCLASS32; - - ehdr.e_machine = machine; - ehdr.e_entry = 0; ehdr.e_phoff = 0x100; - ehdr.e_shoff = 0; - ehdr.e_flags = 0; ehdr.e_ehsize = sizeof(ehdr); ehdr.e_phentsize = sizeof(Elf32_Phdr); ehdr.e_phnum = 1; ehdr.e_shentsize = sizeof(Elf32_Shdr); - ehdr.e_shnum = 0; - ehdr.e_shstrndx = 0; - if (machine == EM_ARM) { + if (machine_type == EM_ARM) { ehdr.e_flags = 0x5000200; ehdr.e_phnum = 2; } @@ -74,16 +55,13 @@ class ElfTest : public ::testing::Test { Elf32_Phdr phdr; memset(&phdr, 0, sizeof(phdr)); phdr.p_type = PT_LOAD; - phdr.p_offset = 0; - phdr.p_vaddr = 0; - phdr.p_paddr = 0; phdr.p_filesz = 0x10000; phdr.p_memsz = 0x10000; phdr.p_flags = PF_R | PF_X; phdr.p_align = 0x1000; memory_->SetMemory(0x100, &phdr, sizeof(phdr)); - if (machine == EM_ARM) { + if (machine_type == EM_ARM) { memset(&phdr, 0, sizeof(phdr)); phdr.p_type = PT_ARM_EXIDX; phdr.p_offset = 0x30000; @@ -97,31 +75,21 @@ class ElfTest : public ::testing::Test { } } - void InitElf64(uint32_t machine) { + void InitElf64(uint32_t machine_type) { Elf64_Ehdr ehdr; + TestInitEhdr(&ehdr, ELFCLASS64, machine_type); - InitEhdr(&ehdr); - ehdr.e_ident[EI_CLASS] = ELFCLASS64; - - ehdr.e_machine = machine; - ehdr.e_entry = 0; ehdr.e_phoff = 0x100; - ehdr.e_shoff = 0; ehdr.e_flags = 0x5000200; ehdr.e_ehsize = sizeof(ehdr); ehdr.e_phentsize = sizeof(Elf64_Phdr); ehdr.e_phnum = 1; ehdr.e_shentsize = sizeof(Elf64_Shdr); - ehdr.e_shnum = 0; - ehdr.e_shstrndx = 0; memory_->SetMemory(0, &ehdr, sizeof(ehdr)); Elf64_Phdr phdr; memset(&phdr, 0, sizeof(phdr)); phdr.p_type = PT_LOAD; - phdr.p_offset = 0; - phdr.p_vaddr = 0; - phdr.p_paddr = 0; phdr.p_filesz = 0x10000; phdr.p_memsz = 0x10000; phdr.p_flags = PF_R | PF_X; @@ -129,12 +97,6 @@ class ElfTest : public ::testing::Test { memory_->SetMemory(0x100, &phdr, sizeof(phdr)); } - template - void GnuDebugdataInitFail(Ehdr* ehdr); - - template - void GnuDebugdataInit(Ehdr* ehdr); - MemoryFake* memory_; }; @@ -214,153 +176,64 @@ TEST_F(ElfTest, elf_x86_64) { ASSERT_TRUE(elf.interface() != nullptr); } -template -void ElfTest::GnuDebugdataInitFail(Ehdr* ehdr) { +TEST_F(ElfTest, gnu_debugdata_init_fail32) { + TestInitGnuDebugdata(ELFCLASS32, EM_ARM, false, + [&](uint64_t offset, const void* ptr, size_t size) { + memory_->SetMemory(offset, ptr, size); + }); + Elf elf(memory_); - - uint64_t offset = 0x2000; - - ehdr->e_shoff = offset; - ehdr->e_shnum = 3; - ehdr->e_shentsize = sizeof(Shdr); - ehdr->e_shstrndx = 2; - memory_->SetMemory(0, ehdr, sizeof(*ehdr)); - - Shdr shdr; - memset(&shdr, 0, sizeof(shdr)); - shdr.sh_type = SHT_NULL; - memory_->SetMemory(offset, &shdr, sizeof(shdr)); - offset += ehdr->e_shentsize; - - memset(&shdr, 0, sizeof(shdr)); - shdr.sh_type = SHT_PROGBITS; - shdr.sh_name = 0x100; - shdr.sh_addr = 0x5000; - shdr.sh_offset = 0x5000; - shdr.sh_entsize = 0x100; - shdr.sh_size = 0x800; - memory_->SetMemory(offset, &shdr, sizeof(shdr)); - offset += ehdr->e_shentsize; - - memset(&shdr, 0, sizeof(shdr)); - shdr.sh_type = SHT_STRTAB; - shdr.sh_name = 0x200000; - shdr.sh_offset = 0xf000; - shdr.sh_size = 0x1000; - memory_->SetMemory(offset, &shdr, sizeof(shdr)); - offset += ehdr->e_shentsize; - - memory_->SetMemory(0xf100, ".gnu_debugdata", sizeof(".gnu_debugdata")); - ASSERT_TRUE(elf.Init()); ASSERT_TRUE(elf.interface() != nullptr); ASSERT_TRUE(elf.gnu_debugdata_interface() == nullptr); - EXPECT_EQ(0x5000U, elf.interface()->gnu_debugdata_offset()); - EXPECT_EQ(0x800U, elf.interface()->gnu_debugdata_size()); - - elf.InitGnuDebugdata(); -} - -TEST_F(ElfTest, gnu_debugdata_init_fail32) { - Elf32_Ehdr ehdr; - InitEhdr(&ehdr); - ehdr.e_ident[EI_CLASS] = ELFCLASS32; - ehdr.e_machine = EM_ARM; - - GnuDebugdataInitFail(&ehdr); + EXPECT_EQ(0x1acU, elf.interface()->gnu_debugdata_offset()); + EXPECT_EQ(0x100U, elf.interface()->gnu_debugdata_size()); } TEST_F(ElfTest, gnu_debugdata_init_fail64) { - Elf64_Ehdr ehdr; - InitEhdr(&ehdr); - ehdr.e_ident[EI_CLASS] = ELFCLASS64; - ehdr.e_machine = EM_AARCH64; + TestInitGnuDebugdata(ELFCLASS64, EM_AARCH64, false, + [&](uint64_t offset, const void* ptr, size_t size) { + memory_->SetMemory(offset, ptr, size); + }); - GnuDebugdataInitFail(&ehdr); -} - -template -void ElfTest::GnuDebugdataInit(Ehdr* ehdr) { Elf elf(memory_); - - uint64_t offset = 0x2000; - - ehdr->e_shoff = offset; - ehdr->e_shnum = 3; - ehdr->e_shentsize = sizeof(Shdr); - ehdr->e_shstrndx = 2; - memory_->SetMemory(0, ehdr, sizeof(*ehdr)); - - Shdr shdr; - memset(&shdr, 0, sizeof(shdr)); - shdr.sh_type = SHT_NULL; - memory_->SetMemory(offset, &shdr, sizeof(shdr)); - offset += ehdr->e_shentsize; - - uint64_t gnu_offset = offset; - offset += ehdr->e_shentsize; - - memset(&shdr, 0, sizeof(shdr)); - shdr.sh_type = SHT_STRTAB; - shdr.sh_name = 0x200000; - shdr.sh_offset = 0xf000; - shdr.sh_size = 0x1000; - memory_->SetMemory(offset, &shdr, sizeof(shdr)); - offset += ehdr->e_shentsize; - - memory_->SetMemory(0xf100, ".gnu_debugdata", sizeof(".gnu_debugdata")); - - // Read in the compressed elf data and put it in our fake memory. - std::string name("tests/"); - if (sizeof(Ehdr) == sizeof(Elf32_Ehdr)) { - name += "elf32.xz"; - } else { - name += "elf64.xz"; - } - int fd = TEMP_FAILURE_RETRY(open(name.c_str(), O_RDONLY)); - ASSERT_NE(-1, fd) << "Cannot open " + name; - // Assumes the file is less than 1024 bytes. - std::vector buf(1024); - ssize_t bytes = TEMP_FAILURE_RETRY(read(fd, buf.data(), buf.size())); - ASSERT_GT(bytes, 0); - // Make sure the file isn't too big. - ASSERT_NE(static_cast(bytes), buf.size()) - << "File " + name + " is too big, increase buffer size."; - close(fd); - buf.resize(bytes); - memory_->SetMemory(0x5000, buf); - - memset(&shdr, 0, sizeof(shdr)); - shdr.sh_type = SHT_PROGBITS; - shdr.sh_name = 0x100; - shdr.sh_addr = 0x5000; - shdr.sh_offset = 0x5000; - shdr.sh_size = bytes; - memory_->SetMemory(gnu_offset, &shdr, sizeof(shdr)); - ASSERT_TRUE(elf.Init()); ASSERT_TRUE(elf.interface() != nullptr); ASSERT_TRUE(elf.gnu_debugdata_interface() == nullptr); - EXPECT_EQ(0x5000U, elf.interface()->gnu_debugdata_offset()); + EXPECT_EQ(0x200U, elf.interface()->gnu_debugdata_offset()); + EXPECT_EQ(0x100U, elf.interface()->gnu_debugdata_size()); +} + +TEST_F(ElfTest, gnu_debugdata_init32) { + TestInitGnuDebugdata(ELFCLASS32, EM_ARM, true, + [&](uint64_t offset, const void* ptr, size_t size) { + memory_->SetMemory(offset, ptr, size); + }); + + Elf elf(memory_); + ASSERT_TRUE(elf.Init()); + ASSERT_TRUE(elf.interface() != nullptr); + ASSERT_TRUE(elf.gnu_debugdata_interface() == nullptr); + EXPECT_EQ(0x1acU, elf.interface()->gnu_debugdata_offset()); + EXPECT_EQ(0x8cU, elf.interface()->gnu_debugdata_size()); elf.InitGnuDebugdata(); ASSERT_TRUE(elf.gnu_debugdata_interface() != nullptr); } -TEST_F(ElfTest, gnu_debugdata_init32) { - Elf32_Ehdr ehdr; - InitEhdr(&ehdr); - ehdr.e_ident[EI_CLASS] = ELFCLASS32; - ehdr.e_machine = EM_ARM; - - GnuDebugdataInit(&ehdr); -} - TEST_F(ElfTest, gnu_debugdata_init64) { - Elf64_Ehdr ehdr; - InitEhdr(&ehdr); - ehdr.e_ident[EI_CLASS] = ELFCLASS64; - ehdr.e_machine = EM_AARCH64; + TestInitGnuDebugdata(ELFCLASS64, EM_AARCH64, true, + [&](uint64_t offset, const void* ptr, size_t size) { + memory_->SetMemory(offset, ptr, size); + }); - GnuDebugdataInit(&ehdr); + Elf elf(memory_); + ASSERT_TRUE(elf.Init()); + ASSERT_TRUE(elf.interface() != nullptr); + ASSERT_TRUE(elf.gnu_debugdata_interface() == nullptr); + EXPECT_EQ(0x200U, elf.interface()->gnu_debugdata_offset()); + EXPECT_EQ(0x90U, elf.interface()->gnu_debugdata_size()); + + elf.InitGnuDebugdata(); + ASSERT_TRUE(elf.gnu_debugdata_interface() != nullptr); } diff --git a/libunwindstack/tests/ElfTestUtils.cpp b/libunwindstack/tests/ElfTestUtils.cpp new file mode 100644 index 000000000..8755f163b --- /dev/null +++ b/libunwindstack/tests/ElfTestUtils.cpp @@ -0,0 +1,129 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include + +#include "ElfTestUtils.h" + +template +void TestInitEhdr(Ehdr* ehdr, uint32_t elf_class, uint32_t machine_type) { + memset(ehdr, 0, sizeof(Ehdr)); + memcpy(&ehdr->e_ident[0], ELFMAG, SELFMAG); + ehdr->e_ident[EI_DATA] = ELFDATA2LSB; + ehdr->e_ident[EI_VERSION] = EV_CURRENT; + ehdr->e_ident[EI_OSABI] = ELFOSABI_SYSV; + ehdr->e_ident[EI_CLASS] = elf_class; + ehdr->e_type = ET_DYN; + ehdr->e_machine = machine_type; + ehdr->e_version = EV_CURRENT; + ehdr->e_ehsize = sizeof(Ehdr); +} + +template +void TestInitGnuDebugdata(uint32_t elf_class, uint32_t machine, bool init_gnu_debugdata, + TestCopyFuncType copy_func) { + Ehdr ehdr; + + TestInitEhdr(&ehdr, elf_class, machine); + + uint64_t offset = sizeof(Ehdr); + ehdr.e_shoff = offset; + ehdr.e_shnum = 3; + ehdr.e_shentsize = sizeof(Shdr); + ehdr.e_shstrndx = 2; + copy_func(0, &ehdr, sizeof(ehdr)); + + Shdr shdr; + memset(&shdr, 0, sizeof(shdr)); + shdr.sh_type = SHT_NULL; + copy_func(offset, &shdr, sizeof(shdr)); + offset += ehdr.e_shentsize; + + // Skip this header, it will contain the gnu_debugdata information. + uint64_t gnu_offset = offset; + offset += ehdr.e_shentsize; + + uint64_t symtab_offset = sizeof(ehdr) + ehdr.e_shnum * ehdr.e_shentsize; + memset(&shdr, 0, sizeof(shdr)); + shdr.sh_name = 1; + shdr.sh_type = SHT_STRTAB; + shdr.sh_offset = symtab_offset; + shdr.sh_size = 0x100; + copy_func(offset, &shdr, sizeof(shdr)); + + char value = '\0'; + uint64_t symname_offset = symtab_offset; + copy_func(symname_offset, &value, 1); + symname_offset++; + std::string name(".shstrtab"); + copy_func(symname_offset, name.c_str(), name.size() + 1); + symname_offset += name.size() + 1; + name = ".gnu_debugdata"; + copy_func(symname_offset, name.c_str(), name.size() + 1); + + ssize_t bytes = 0x100; + offset = symtab_offset + 0x100; + if (init_gnu_debugdata) { + // Read in the compressed elf data and copy it in. + name = "tests/files/"; + if (elf_class == ELFCLASS32) { + name += "elf32.xz"; + } else { + name += "elf64.xz"; + } + int fd = TEMP_FAILURE_RETRY(open(name.c_str(), O_RDONLY)); + ASSERT_NE(-1, fd) << "Cannot open " + name; + // Assumes the file is less than 1024 bytes. + std::vector buf(1024); + bytes = TEMP_FAILURE_RETRY(read(fd, buf.data(), buf.size())); + ASSERT_GT(bytes, 0); + // Make sure the file isn't too big. + ASSERT_NE(static_cast(bytes), buf.size()) + << "File " + name + " is too big, increase buffer size."; + close(fd); + buf.resize(bytes); + copy_func(offset, buf.data(), buf.size()); + } + + memset(&shdr, 0, sizeof(shdr)); + shdr.sh_type = SHT_PROGBITS; + shdr.sh_name = symname_offset - symtab_offset; + shdr.sh_addr = offset; + shdr.sh_offset = offset; + shdr.sh_size = bytes; + copy_func(gnu_offset, &shdr, sizeof(shdr)); +} + +template void TestInitEhdr(Elf32_Ehdr*, uint32_t, uint32_t); +template void TestInitEhdr(Elf64_Ehdr*, uint32_t, uint32_t); + +template void TestInitGnuDebugdata(uint32_t, uint32_t, bool, + TestCopyFuncType); +template void TestInitGnuDebugdata(uint32_t, uint32_t, bool, + TestCopyFuncType); diff --git a/libunwindstack/tests/ElfTestUtils.h b/libunwindstack/tests/ElfTestUtils.h new file mode 100644 index 000000000..4fd3bbca1 --- /dev/null +++ b/libunwindstack/tests/ElfTestUtils.h @@ -0,0 +1,31 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * 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 _LIBUNWINDSTACK_TESTS_ELF_TEST_UTILS_H +#define _LIBUNWINDSTACK_TESTS_ELF_TEST_UTILS_H + +#include + +typedef std::function TestCopyFuncType; + +template +void TestInitEhdr(Ehdr* ehdr, uint32_t elf_class, uint32_t machine_type); + +template +void TestInitGnuDebugdata(uint32_t elf_class, uint32_t machine_type, bool init_gnu_debudata, + TestCopyFuncType copy_func); + +#endif // _LIBUNWINDSTACK_TESTS_ELF_TEST_UTILS_H diff --git a/libunwindstack/tests/MapInfoTest.cpp b/libunwindstack/tests/MapInfoCreateMemoryTest.cpp similarity index 85% rename from libunwindstack/tests/MapInfoTest.cpp rename to libunwindstack/tests/MapInfoCreateMemoryTest.cpp index 6e47dc01d..d7433e19a 100644 --- a/libunwindstack/tests/MapInfoTest.cpp +++ b/libunwindstack/tests/MapInfoCreateMemoryTest.cpp @@ -34,7 +34,7 @@ #include "MapInfo.h" #include "Memory.h" -class MapInfoTest : public ::testing::Test { +class MapInfoCreateMemoryTest : public ::testing::Test { protected: static void SetUpTestCase() { std::vector buffer(1024); @@ -58,10 +58,10 @@ class MapInfoTest : public ::testing::Test { static TemporaryFile elf_at_100_; }; -TemporaryFile MapInfoTest::elf_; -TemporaryFile MapInfoTest::elf_at_100_; +TemporaryFile MapInfoCreateMemoryTest::elf_; +TemporaryFile MapInfoCreateMemoryTest::elf_at_100_; -TEST_F(MapInfoTest, end_le_start) { +TEST_F(MapInfoCreateMemoryTest, end_le_start) { MapInfo info{.start = 0x100, .end = 0x100, .offset = 0, .name = elf_.path}; std::unique_ptr memory; @@ -80,7 +80,7 @@ TEST_F(MapInfoTest, end_le_start) { // Verify that if the offset is non-zero but there is no elf at the offset, // that the full file is used. -TEST_F(MapInfoTest, create_memory_file_backed_non_zero_offset_full_file) { +TEST_F(MapInfoCreateMemoryTest, file_backed_non_zero_offset_full_file) { MapInfo info{.start = 0x100, .end = 0x200, .offset = 0x100, .name = elf_.path}; std::unique_ptr memory(info.CreateMemory(getpid())); @@ -100,7 +100,7 @@ TEST_F(MapInfoTest, create_memory_file_backed_non_zero_offset_full_file) { // Verify that if the offset is non-zero and there is an elf at that // offset, that only part of the file is used. -TEST_F(MapInfoTest, create_memory_file_backed_non_zero_offset_partial_file) { +TEST_F(MapInfoCreateMemoryTest, file_backed_non_zero_offset_partial_file) { MapInfo info{.start = 0x100, .end = 0x200, .offset = 0x100, .name = elf_at_100_.path}; std::unique_ptr memory(info.CreateMemory(getpid())); @@ -119,7 +119,7 @@ TEST_F(MapInfoTest, create_memory_file_backed_non_zero_offset_partial_file) { } // Verify that device file names will never result in Memory object creation. -TEST_F(MapInfoTest, create_memory_check_device_maps) { +TEST_F(MapInfoCreateMemoryTest, check_device_maps) { // Set up some memory so that a valid local memory object would // be returned if the file mapping fails, but the device check is incorrect. std::vector buffer(1024); @@ -135,7 +135,7 @@ TEST_F(MapInfoTest, create_memory_check_device_maps) { ASSERT_TRUE(memory.get() == nullptr); } -TEST_F(MapInfoTest, create_memory_local_memory) { +TEST_F(MapInfoCreateMemoryTest, local_memory) { // Set up some memory for a valid local memory object. std::vector buffer(1024); for (size_t i = 0; i < buffer.size(); i++) { @@ -160,7 +160,7 @@ TEST_F(MapInfoTest, create_memory_local_memory) { ASSERT_FALSE(memory->Read(read_buffer.size(), read_buffer.data(), 1)); } -TEST_F(MapInfoTest, create_memory_remote_memory) { +TEST_F(MapInfoCreateMemoryTest, remote_memory) { std::vector buffer(1024); memset(buffer.data(), 0xa, buffer.size()); @@ -202,19 +202,3 @@ TEST_F(MapInfoTest, create_memory_remote_memory) { kill(pid, SIGKILL); } - -TEST_F(MapInfoTest, get_elf) { - // Create a map to use as initialization data. - void* map = mmap(nullptr, 1024, PROT_READ, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); - ASSERT_NE(MAP_FAILED, map); - - uint64_t start = reinterpret_cast(map); - MapInfo info{.start = start, .end = start + 1024, .offset = 0, .name = ""}; - - // The map contains garbage, but this should still produce an elf object. - std::unique_ptr elf(info.GetElf(getpid(), false)); - ASSERT_TRUE(elf.get() != nullptr); - ASSERT_FALSE(elf->valid()); - - ASSERT_EQ(0, munmap(map, 1024)); -} diff --git a/libunwindstack/tests/MapInfoGetElfTest.cpp b/libunwindstack/tests/MapInfoGetElfTest.cpp new file mode 100644 index 000000000..fc36cc6c2 --- /dev/null +++ b/libunwindstack/tests/MapInfoGetElfTest.cpp @@ -0,0 +1,140 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * 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 +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include + +#include "Elf.h" +#include "ElfTestUtils.h" +#include "MapInfo.h" + +class MapInfoGetElfTest : public ::testing::Test { + protected: + void SetUp() override { + map_ = mmap(nullptr, kMapSize, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); + ASSERT_NE(MAP_FAILED, map_); + + uint64_t start = reinterpret_cast(map_); + info_.reset(new MapInfo{.start = start, .end = start + 1024, .offset = 0, .name = ""}); + } + + void TearDown() override { munmap(map_, kMapSize); } + + const size_t kMapSize = 4096; + + void* map_ = nullptr; + std::unique_ptr info_; +}; + +TEST_F(MapInfoGetElfTest, invalid) { + // The map is empty, but this should still create an invalid elf object. + std::unique_ptr elf(info_->GetElf(getpid(), false)); + ASSERT_TRUE(elf.get() != nullptr); + ASSERT_FALSE(elf->valid()); +} + +TEST_F(MapInfoGetElfTest, valid32) { + Elf32_Ehdr ehdr; + TestInitEhdr(&ehdr, ELFCLASS32, EM_ARM); + memcpy(map_, &ehdr, sizeof(ehdr)); + + std::unique_ptr elf(info_->GetElf(getpid(), false)); + ASSERT_TRUE(elf.get() != nullptr); + ASSERT_TRUE(elf->valid()); + EXPECT_EQ(static_cast(EM_ARM), elf->machine_type()); + EXPECT_EQ(ELFCLASS32, elf->class_type()); +} + +TEST_F(MapInfoGetElfTest, valid64) { + Elf64_Ehdr ehdr; + TestInitEhdr(&ehdr, ELFCLASS64, EM_AARCH64); + memcpy(map_, &ehdr, sizeof(ehdr)); + + std::unique_ptr elf(info_->GetElf(getpid(), false)); + ASSERT_TRUE(elf.get() != nullptr); + ASSERT_TRUE(elf->valid()); + EXPECT_EQ(static_cast(EM_AARCH64), elf->machine_type()); + EXPECT_EQ(ELFCLASS64, elf->class_type()); +} + +TEST_F(MapInfoGetElfTest, gnu_debugdata_do_not_init32) { + TestInitGnuDebugdata( + ELFCLASS32, EM_ARM, false, [&](uint64_t offset, const void* ptr, size_t size) { + memcpy(&reinterpret_cast(map_)[offset], ptr, size); + }); + + std::unique_ptr elf(info_->GetElf(getpid(), false)); + ASSERT_TRUE(elf.get() != nullptr); + ASSERT_TRUE(elf->valid()); + EXPECT_EQ(static_cast(EM_ARM), elf->machine_type()); + EXPECT_EQ(ELFCLASS32, elf->class_type()); + EXPECT_TRUE(elf->gnu_debugdata_interface() == nullptr); +} + +TEST_F(MapInfoGetElfTest, gnu_debugdata_do_not_init64) { + TestInitGnuDebugdata( + ELFCLASS64, EM_AARCH64, false, [&](uint64_t offset, const void* ptr, size_t size) { + memcpy(&reinterpret_cast(map_)[offset], ptr, size); + }); + + std::unique_ptr elf(info_->GetElf(getpid(), false)); + ASSERT_TRUE(elf.get() != nullptr); + ASSERT_TRUE(elf->valid()); + EXPECT_EQ(static_cast(EM_AARCH64), elf->machine_type()); + EXPECT_EQ(ELFCLASS64, elf->class_type()); + EXPECT_TRUE(elf->gnu_debugdata_interface() == nullptr); +} + +TEST_F(MapInfoGetElfTest, gnu_debugdata_init32) { + TestInitGnuDebugdata( + ELFCLASS32, EM_ARM, true, [&](uint64_t offset, const void* ptr, size_t size) { + memcpy(&reinterpret_cast(map_)[offset], ptr, size); + }); + + std::unique_ptr elf(info_->GetElf(getpid(), true)); + ASSERT_TRUE(elf.get() != nullptr); + ASSERT_TRUE(elf->valid()); + EXPECT_EQ(static_cast(EM_ARM), elf->machine_type()); + EXPECT_EQ(ELFCLASS32, elf->class_type()); + EXPECT_TRUE(elf->gnu_debugdata_interface() != nullptr); +} + +TEST_F(MapInfoGetElfTest, gnu_debugdata_init64) { + TestInitGnuDebugdata( + ELFCLASS64, EM_AARCH64, true, [&](uint64_t offset, const void* ptr, size_t size) { + memcpy(&reinterpret_cast(map_)[offset], ptr, size); + }); + + std::unique_ptr elf(info_->GetElf(getpid(), true)); + ASSERT_TRUE(elf.get() != nullptr); + ASSERT_TRUE(elf->valid()); + EXPECT_EQ(static_cast(EM_AARCH64), elf->machine_type()); + EXPECT_EQ(ELFCLASS64, elf->class_type()); + EXPECT_TRUE(elf->gnu_debugdata_interface() != nullptr); +} diff --git a/libunwindstack/tests/elf32.xz b/libunwindstack/tests/files/elf32.xz similarity index 100% rename from libunwindstack/tests/elf32.xz rename to libunwindstack/tests/files/elf32.xz diff --git a/libunwindstack/tests/elf64.xz b/libunwindstack/tests/files/elf64.xz similarity index 100% rename from libunwindstack/tests/elf64.xz rename to libunwindstack/tests/files/elf64.xz