summaryrefslogtreecommitdiff
path: root/libunwindstack/tests/DexFilesTest.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'libunwindstack/tests/DexFilesTest.cpp')
-rw-r--r--libunwindstack/tests/DexFilesTest.cpp356
1 files changed, 356 insertions, 0 deletions
diff --git a/libunwindstack/tests/DexFilesTest.cpp b/libunwindstack/tests/DexFilesTest.cpp
new file mode 100644
index 000000000..477cf8efe
--- /dev/null
+++ b/libunwindstack/tests/DexFilesTest.cpp
@@ -0,0 +1,356 @@
+/*
+ * Copyright (C) 2018 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 <elf.h>
+#include <string.h>
+
+#include <memory>
+#include <vector>
+
+#include <gtest/gtest.h>
+
+#include <unwindstack/DexFiles.h>
+#include <unwindstack/Elf.h>
+#include <unwindstack/MapInfo.h>
+#include <unwindstack/Maps.h>
+#include <unwindstack/Memory.h>
+
+#include "DexFileData.h"
+#include "ElfFake.h"
+#include "MemoryFake.h"
+
+namespace unwindstack {
+
+class DexFilesTest : public ::testing::Test {
+ protected:
+ void CreateFakeElf(MapInfo* map_info, uint64_t global_offset, uint64_t data_offset,
+ uint64_t data_vaddr, uint64_t data_size) {
+ MemoryFake* memory = new MemoryFake;
+ ElfFake* elf = new ElfFake(memory);
+ elf->FakeSetValid(true);
+ ElfInterfaceFake* interface = new ElfInterfaceFake(memory);
+ elf->FakeSetInterface(interface);
+
+ interface->FakeSetGlobalVariable("__dex_debug_descriptor", global_offset);
+ interface->FakeSetDataOffset(data_offset);
+ interface->FakeSetDataVaddrStart(data_vaddr);
+ interface->FakeSetDataVaddrEnd(data_vaddr + data_size);
+ map_info->elf.reset(elf);
+ }
+
+ void Init(ArchEnum arch) {
+ dex_files_.reset(new DexFiles(process_memory_));
+ dex_files_->SetArch(arch);
+
+ maps_.reset(
+ new BufferMaps("1000-4000 ---s 00000000 00:00 0 /fake/elf\n"
+ "4000-6000 r--s 00000000 00:00 0 /fake/elf\n"
+ "6000-8000 -wxs 00002000 00:00 0 /fake/elf\n"
+ "a000-c000 r--p 00000000 00:00 0 /fake/elf2\n"
+ "c000-f000 rw-p 00002000 00:00 0 /fake/elf2\n"
+ "f000-11000 r--p 00000000 00:00 0 /fake/elf3\n"
+ "100000-110000 rw-p 00f1000 00:00 0 /fake/elf3\n"
+ "200000-210000 rw-p 0002000 00:00 0 /fake/elf3\n"
+ "300000-400000 rw-p 0003000 00:00 0 /fake/elf3\n"
+ "500000-501000 r--p 0000000 00:00 0 /fake/elf4\n"
+ "501000-502000 ---p 0000000 00:00 0\n"
+ "503000-510000 rw-p 0003000 00:00 0 /fake/elf4\n"
+ "510000-520000 rw-p 0010000 00:00 0 /fake/elf4\n"));
+ ASSERT_TRUE(maps_->Parse());
+
+ // Global variable in a section that is not readable.
+ MapInfo* map_info = maps_->Get(kMapGlobalNonReadable);
+ ASSERT_TRUE(map_info != nullptr);
+ CreateFakeElf(map_info, 0x2800, 0x2000, 0x2000, 0x3000);
+
+ // Global variable not set by default.
+ map_info = maps_->Get(kMapGlobalSetToZero);
+ ASSERT_TRUE(map_info != nullptr);
+ CreateFakeElf(map_info, 0x2800, 0x2000, 0x2000, 0x3000);
+
+ // Global variable set in this map.
+ map_info = maps_->Get(kMapGlobal);
+ ASSERT_TRUE(map_info != nullptr);
+ CreateFakeElf(map_info, 0xf1800, 0xf1000, 0xf1000, 0x10000);
+
+ // Global variable set in this map, but there is an empty map before rw map.
+ map_info = maps_->Get(kMapGlobalAfterEmpty);
+ ASSERT_TRUE(map_info != nullptr);
+ CreateFakeElf(map_info, 0x3800, 0x3000, 0x3000, 0xd000);
+ }
+
+ void SetUp() override {
+ memory_ = new MemoryFake;
+ process_memory_.reset(memory_);
+
+ Init(ARCH_ARM);
+ }
+
+ void WriteDescriptor32(uint64_t addr, uint32_t head);
+ void WriteDescriptor64(uint64_t addr, uint64_t head);
+ void WriteEntry32(uint64_t entry_addr, uint32_t next, uint32_t prev, uint32_t dex_file);
+ void WriteEntry64(uint64_t entry_addr, uint64_t next, uint64_t prev, uint64_t dex_file);
+ void WriteDex(uint64_t dex_file);
+
+ static constexpr size_t kMapGlobalNonReadable = 2;
+ static constexpr size_t kMapGlobalSetToZero = 3;
+ static constexpr size_t kMapGlobal = 5;
+ static constexpr size_t kMapGlobalRw = 6;
+ static constexpr size_t kMapDexFileEntries = 7;
+ static constexpr size_t kMapDexFiles = 8;
+ static constexpr size_t kMapGlobalAfterEmpty = 9;
+ static constexpr size_t kMapDexFilesAfterEmpty = 12;
+
+ std::shared_ptr<Memory> process_memory_;
+ MemoryFake* memory_;
+ std::unique_ptr<DexFiles> dex_files_;
+ std::unique_ptr<BufferMaps> maps_;
+};
+
+void DexFilesTest::WriteDescriptor32(uint64_t addr, uint32_t head) {
+ // void* first_entry_
+ memory_->SetData32(addr + 12, head);
+}
+
+void DexFilesTest::WriteDescriptor64(uint64_t addr, uint64_t head) {
+ // void* first_entry_
+ memory_->SetData64(addr + 16, head);
+}
+
+void DexFilesTest::WriteEntry32(uint64_t entry_addr, uint32_t next, uint32_t prev,
+ uint32_t dex_file) {
+ // Format of the 32 bit DEXFileEntry structure:
+ // uint32_t next
+ memory_->SetData32(entry_addr, next);
+ // uint32_t prev
+ memory_->SetData32(entry_addr + 4, prev);
+ // uint32_t dex_file
+ memory_->SetData32(entry_addr + 8, dex_file);
+}
+
+void DexFilesTest::WriteEntry64(uint64_t entry_addr, uint64_t next, uint64_t prev,
+ uint64_t dex_file) {
+ // Format of the 64 bit DEXFileEntry structure:
+ // uint64_t next
+ memory_->SetData64(entry_addr, next);
+ // uint64_t prev
+ memory_->SetData64(entry_addr + 8, prev);
+ // uint64_t dex_file
+ memory_->SetData64(entry_addr + 16, dex_file);
+}
+
+void DexFilesTest::WriteDex(uint64_t dex_file) {
+ memory_->SetMemory(dex_file, kDexData, sizeof(kDexData) * sizeof(uint32_t));
+}
+
+TEST_F(DexFilesTest, get_method_information_invalid) {
+ std::string method_name = "nothing";
+ uint64_t method_offset = 0x124;
+ MapInfo* info = maps_->Get(kMapDexFileEntries);
+
+ dex_files_->GetMethodInformation(maps_.get(), info, 0, &method_name, &method_offset);
+ EXPECT_EQ("nothing", method_name);
+ EXPECT_EQ(0x124U, method_offset);
+}
+
+TEST_F(DexFilesTest, get_method_information_32) {
+ std::string method_name = "nothing";
+ uint64_t method_offset = 0x124;
+ MapInfo* info = maps_->Get(kMapDexFiles);
+
+ WriteDescriptor32(0x100800, 0x200000);
+ WriteEntry32(0x200000, 0, 0, 0x300000);
+ WriteDex(0x300000);
+
+ dex_files_->GetMethodInformation(maps_.get(), info, 0x300100, &method_name, &method_offset);
+ EXPECT_EQ("Main.<init>", method_name);
+ EXPECT_EQ(0U, method_offset);
+}
+
+TEST_F(DexFilesTest, get_method_information_64) {
+ Init(ARCH_ARM64);
+
+ std::string method_name = "nothing";
+ uint64_t method_offset = 0x124;
+ MapInfo* info = maps_->Get(kMapDexFiles);
+
+ WriteDescriptor64(0x100800, 0x200000);
+ WriteEntry64(0x200000, 0, 0, 0x301000);
+ WriteDex(0x301000);
+
+ dex_files_->GetMethodInformation(maps_.get(), info, 0x301102, &method_name, &method_offset);
+ EXPECT_EQ("Main.<init>", method_name);
+ EXPECT_EQ(2U, method_offset);
+}
+
+TEST_F(DexFilesTest, get_method_information_not_first_entry_32) {
+ std::string method_name = "nothing";
+ uint64_t method_offset = 0x124;
+ MapInfo* info = maps_->Get(kMapDexFiles);
+
+ WriteDescriptor32(0x100800, 0x200000);
+ WriteEntry32(0x200000, 0x200100, 0, 0x100000);
+ WriteEntry32(0x200100, 0, 0x200000, 0x300000);
+ WriteDex(0x300000);
+
+ dex_files_->GetMethodInformation(maps_.get(), info, 0x300104, &method_name, &method_offset);
+ EXPECT_EQ("Main.<init>", method_name);
+ EXPECT_EQ(4U, method_offset);
+}
+
+TEST_F(DexFilesTest, get_method_information_not_first_entry_64) {
+ Init(ARCH_ARM64);
+
+ std::string method_name = "nothing";
+ uint64_t method_offset = 0x124;
+ MapInfo* info = maps_->Get(kMapDexFiles);
+
+ WriteDescriptor64(0x100800, 0x200000);
+ WriteEntry64(0x200000, 0x200100, 0, 0x100000);
+ WriteEntry64(0x200100, 0, 0x200000, 0x300000);
+ WriteDex(0x300000);
+
+ dex_files_->GetMethodInformation(maps_.get(), info, 0x300106, &method_name, &method_offset);
+ EXPECT_EQ("Main.<init>", method_name);
+ EXPECT_EQ(6U, method_offset);
+}
+
+TEST_F(DexFilesTest, get_method_information_cached) {
+ std::string method_name = "nothing";
+ uint64_t method_offset = 0x124;
+ MapInfo* info = maps_->Get(kMapDexFiles);
+
+ WriteDescriptor32(0x100800, 0x200000);
+ WriteEntry32(0x200000, 0, 0, 0x300000);
+ WriteDex(0x300000);
+
+ dex_files_->GetMethodInformation(maps_.get(), info, 0x300100, &method_name, &method_offset);
+ EXPECT_EQ("Main.<init>", method_name);
+ EXPECT_EQ(0U, method_offset);
+
+ // Clear all memory and make sure that data is acquired from the cache.
+ memory_->Clear();
+ dex_files_->GetMethodInformation(maps_.get(), info, 0x300100, &method_name, &method_offset);
+ EXPECT_EQ("Main.<init>", method_name);
+ EXPECT_EQ(0U, method_offset);
+}
+
+TEST_F(DexFilesTest, get_method_information_search_libs) {
+ std::string method_name = "nothing";
+ uint64_t method_offset = 0x124;
+ MapInfo* info = maps_->Get(kMapDexFiles);
+
+ WriteDescriptor32(0x100800, 0x200000);
+ WriteEntry32(0x200000, 0x200100, 0, 0x100000);
+ WriteEntry32(0x200100, 0, 0x200000, 0x300000);
+ WriteDex(0x300000);
+
+ // Only search a given named list of libs.
+ std::vector<std::string> libs{"libart.so"};
+ dex_files_.reset(new DexFiles(process_memory_, libs));
+ dex_files_->SetArch(ARCH_ARM);
+
+ dex_files_->GetMethodInformation(maps_.get(), info, 0x300104, &method_name, &method_offset);
+ EXPECT_EQ("nothing", method_name);
+ EXPECT_EQ(0x124U, method_offset);
+
+ MapInfo* map_info = maps_->Get(kMapGlobal);
+ map_info->name = "/system/lib/libart.so";
+ dex_files_.reset(new DexFiles(process_memory_, libs));
+ dex_files_->SetArch(ARCH_ARM);
+ // Set the rw map to the same name or this will not scan this entry.
+ map_info = maps_->Get(kMapGlobalRw);
+ map_info->name = "/system/lib/libart.so";
+ // Make sure that clearing out copy of the libs doesn't affect the
+ // DexFiles object.
+ libs.clear();
+
+ dex_files_->GetMethodInformation(maps_.get(), info, 0x300104, &method_name, &method_offset);
+ EXPECT_EQ("Main.<init>", method_name);
+ EXPECT_EQ(4U, method_offset);
+}
+
+TEST_F(DexFilesTest, get_method_information_global_skip_zero_32) {
+ std::string method_name = "nothing";
+ uint64_t method_offset = 0x124;
+ MapInfo* info = maps_->Get(kMapDexFiles);
+
+ // First global variable found, but value is zero.
+ WriteDescriptor32(0xc800, 0);
+
+ WriteDescriptor32(0x100800, 0x200000);
+ WriteEntry32(0x200000, 0, 0, 0x300000);
+ WriteDex(0x300000);
+
+ dex_files_->GetMethodInformation(maps_.get(), info, 0x300100, &method_name, &method_offset);
+ EXPECT_EQ("Main.<init>", method_name);
+ EXPECT_EQ(0U, method_offset);
+
+ // Verify that second is ignored when first is set to non-zero
+ dex_files_.reset(new DexFiles(process_memory_));
+ dex_files_->SetArch(ARCH_ARM);
+ method_name = "fail";
+ method_offset = 0x123;
+ WriteDescriptor32(0xc800, 0x100000);
+ dex_files_->GetMethodInformation(maps_.get(), info, 0x300100, &method_name, &method_offset);
+ EXPECT_EQ("fail", method_name);
+ EXPECT_EQ(0x123U, method_offset);
+}
+
+TEST_F(DexFilesTest, get_method_information_global_skip_zero_64) {
+ Init(ARCH_ARM64);
+
+ std::string method_name = "nothing";
+ uint64_t method_offset = 0x124;
+ MapInfo* info = maps_->Get(kMapDexFiles);
+
+ // First global variable found, but value is zero.
+ WriteDescriptor64(0xc800, 0);
+
+ WriteDescriptor64(0x100800, 0x200000);
+ WriteEntry64(0x200000, 0, 0, 0x300000);
+ WriteDex(0x300000);
+
+ dex_files_->GetMethodInformation(maps_.get(), info, 0x300100, &method_name, &method_offset);
+ EXPECT_EQ("Main.<init>", method_name);
+ EXPECT_EQ(0U, method_offset);
+
+ // Verify that second is ignored when first is set to non-zero
+ dex_files_.reset(new DexFiles(process_memory_));
+ dex_files_->SetArch(ARCH_ARM64);
+ method_name = "fail";
+ method_offset = 0x123;
+ WriteDescriptor64(0xc800, 0x100000);
+ dex_files_->GetMethodInformation(maps_.get(), info, 0x300100, &method_name, &method_offset);
+ EXPECT_EQ("fail", method_name);
+ EXPECT_EQ(0x123U, method_offset);
+}
+
+TEST_F(DexFilesTest, get_method_information_with_empty_map) {
+ std::string method_name = "nothing";
+ uint64_t method_offset = 0x124;
+ MapInfo* info = maps_->Get(kMapDexFilesAfterEmpty);
+
+ WriteDescriptor32(0x503800, 0x506000);
+ WriteEntry32(0x506000, 0, 0, 0x510000);
+ WriteDex(0x510000);
+
+ dex_files_->GetMethodInformation(maps_.get(), info, 0x510100, &method_name, &method_offset);
+ EXPECT_EQ("Main.<init>", method_name);
+ EXPECT_EQ(0U, method_offset);
+}
+
+} // namespace unwindstack