/* * Copyright (C) 2019 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 "art_api/dex_file_support.h" namespace art_api { namespace dex { static constexpr uint32_t kDexData[] = { 0x0a786564, 0x00383330, 0xc98b3ab8, 0xf3749d94, 0xaecca4d8, 0xffc7b09a, 0xdca9ca7f, 0x5be5deab, 0x00000220, 0x00000070, 0x12345678, 0x00000000, 0x00000000, 0x0000018c, 0x00000008, 0x00000070, 0x00000004, 0x00000090, 0x00000002, 0x000000a0, 0x00000000, 0x00000000, 0x00000003, 0x000000b8, 0x00000001, 0x000000d0, 0x00000130, 0x000000f0, 0x00000122, 0x0000012a, 0x00000132, 0x00000146, 0x00000151, 0x00000154, 0x00000158, 0x0000016d, 0x00000001, 0x00000002, 0x00000004, 0x00000006, 0x00000004, 0x00000002, 0x00000000, 0x00000005, 0x00000002, 0x0000011c, 0x00000000, 0x00000000, 0x00010000, 0x00000007, 0x00000001, 0x00000000, 0x00000000, 0x00000001, 0x00000001, 0x00000000, 0x00000003, 0x00000000, 0x0000017e, 0x00000000, 0x00010001, 0x00000001, 0x00000173, 0x00000004, 0x00021070, 0x000e0000, 0x00010001, 0x00000000, 0x00000178, 0x00000001, 0x0000000e, 0x00000001, 0x3c060003, 0x74696e69, 0x4c06003e, 0x6e69614d, 0x4c12003b, 0x6176616a, 0x6e616c2f, 0x624f2f67, 0x7463656a, 0x4d09003b, 0x2e6e6961, 0x6176616a, 0x00560100, 0x004c5602, 0x6a4c5b13, 0x2f617661, 0x676e616c, 0x7274532f, 0x3b676e69, 0x616d0400, 0x01006e69, 0x000e0700, 0x07000103, 0x0000000e, 0x81000002, 0x01f00480, 0x02880901, 0x0000000c, 0x00000000, 0x00000001, 0x00000000, 0x00000001, 0x00000008, 0x00000070, 0x00000002, 0x00000004, 0x00000090, 0x00000003, 0x00000002, 0x000000a0, 0x00000005, 0x00000003, 0x000000b8, 0x00000006, 0x00000001, 0x000000d0, 0x00002001, 0x00000002, 0x000000f0, 0x00001001, 0x00000001, 0x0000011c, 0x00002002, 0x00000008, 0x00000122, 0x00002003, 0x00000002, 0x00000173, 0x00002000, 0x00000001, 0x0000017e, 0x00001000, 0x00000001, 0x0000018c, }; TEST(DexStringTest, alloc_string) { auto s = DexString("123"); EXPECT_EQ(std::string_view(s), "123"); } TEST(DexStringTest, alloc_empty_string) { auto s = DexString(""); EXPECT_TRUE(std::string_view(s).empty()); } TEST(DexStringTest, move_construct) { auto s1 = DexString("foo"); auto s2 = DexString(std::move(s1)); EXPECT_TRUE(std::string_view(s1).empty()); // NOLINT bugprone-use-after-move EXPECT_EQ(std::string_view(s2), "foo"); } TEST(DexStringTest, move_assign) { auto s1 = DexString("foo"); DexString s2; EXPECT_TRUE(std::string_view(s2).empty()); s2 = std::move(s1); EXPECT_TRUE(std::string_view(s1).empty()); // NOLINT bugprone-use-after-move EXPECT_EQ(std::string_view(s2), "foo"); } TEST(DexStringTest, reassign) { auto s = DexString("foo"); s = DexString("bar"); EXPECT_EQ(std::string_view(s), "bar"); } TEST(DexStringTest, data_access) { auto s = DexString("foo"); EXPECT_STREQ(s.data(), "foo"); EXPECT_STREQ(s.c_str(), "foo"); } TEST(DexStringTest, size_access) { auto s = DexString("foo"); EXPECT_EQ(s.size(), size_t{3}); EXPECT_EQ(s.length(), size_t{3}); } TEST(DexStringTest, equality) { auto s = DexString("foo"); EXPECT_EQ(s, DexString("foo")); EXPECT_FALSE(s == DexString("bar")); } TEST(DexStringTest, equality_with_nul) { auto s = DexString(std::string("foo\0bar", 7)); EXPECT_EQ(s.size(), size_t{7}); EXPECT_EQ(s, DexString(std::string("foo\0bar", 7))); EXPECT_FALSE(s == DexString(std::string("foo\0baz", 7))); } TEST(DexFileTest, from_memory_header_too_small) { size_t size = sizeof(art::DexFile::Header) - 1; std::string error_msg; EXPECT_EQ(DexFile::OpenFromMemory(kDexData, &size, "", &error_msg), nullptr); EXPECT_EQ(size, sizeof(art::DexFile::Header)); EXPECT_TRUE(error_msg.empty()); } TEST(DexFileTest, from_memory_file_too_small) { size_t size = sizeof(art::DexFile::Header); std::string error_msg; EXPECT_EQ(DexFile::OpenFromMemory(kDexData, &size, "", &error_msg), nullptr); EXPECT_EQ(size, sizeof(kDexData)); EXPECT_TRUE(error_msg.empty()); } static std::unique_ptr GetTestDexData() { size_t size = sizeof(kDexData); std::string error_msg; std::unique_ptr dex_file = DexFile::OpenFromMemory(kDexData, &size, "", &error_msg); EXPECT_TRUE(error_msg.empty()); return dex_file; } TEST(DexFileTest, from_memory) { EXPECT_NE(GetTestDexData(), nullptr); } TEST(DexFileTest, from_fd_header_too_small) { TemporaryFile tf; ASSERT_NE(tf.fd, -1); ASSERT_EQ(sizeof(art::DexFile::Header) - 1, static_cast( TEMP_FAILURE_RETRY(write(tf.fd, kDexData, sizeof(art::DexFile::Header) - 1)))); std::string error_msg; EXPECT_EQ(DexFile::OpenFromFd(tf.fd, 0, tf.path, &error_msg), nullptr); EXPECT_FALSE(error_msg.empty()); } TEST(DexFileTest, from_fd_file_too_small) { TemporaryFile tf; ASSERT_NE(tf.fd, -1); ASSERT_EQ(sizeof(art::DexFile::Header), static_cast( TEMP_FAILURE_RETRY(write(tf.fd, kDexData, sizeof(art::DexFile::Header))))); std::string error_msg; EXPECT_EQ(DexFile::OpenFromFd(tf.fd, 0, tf.path, &error_msg), nullptr); EXPECT_FALSE(error_msg.empty()); } TEST(DexFileTest, from_fd) { TemporaryFile tf; ASSERT_NE(tf.fd, -1); ASSERT_EQ(sizeof(kDexData), static_cast(TEMP_FAILURE_RETRY(write(tf.fd, kDexData, sizeof(kDexData))))); std::string error_msg; EXPECT_NE(DexFile::OpenFromFd(tf.fd, 0, tf.path, &error_msg), nullptr); EXPECT_TRUE(error_msg.empty()); } TEST(DexFileTest, from_fd_non_zero_offset) { TemporaryFile tf; ASSERT_NE(tf.fd, -1); ASSERT_EQ(0x100, lseek(tf.fd, 0x100, SEEK_SET)); ASSERT_EQ(sizeof(kDexData), static_cast(TEMP_FAILURE_RETRY(write(tf.fd, kDexData, sizeof(kDexData))))); std::string error_msg; EXPECT_NE(DexFile::OpenFromFd(tf.fd, 0x100, tf.path, &error_msg), nullptr); EXPECT_TRUE(error_msg.empty()); } TEST(DexFileTest, get_method_info_for_offset_without_signature) { std::unique_ptr dex_file = GetTestDexData(); ASSERT_NE(dex_file, nullptr); MethodInfo info = dex_file->GetMethodInfoForOffset(0x102, false); EXPECT_EQ(info.offset, int32_t{0x100}); EXPECT_EQ(info.len, int32_t{8}); EXPECT_STREQ(info.name.data(), "Main."); info = dex_file->GetMethodInfoForOffset(0x118, false); EXPECT_EQ(info.offset, int32_t{0x118}); EXPECT_EQ(info.len, int32_t{2}); EXPECT_STREQ(info.name.data(), "Main.main"); // Retrieve a cached result. info = dex_file->GetMethodInfoForOffset(0x104, false); EXPECT_EQ(info.offset, int32_t{0x100}); EXPECT_EQ(info.len, int32_t{8}); EXPECT_STREQ(info.name.data(), "Main."); } TEST(DexFileTest, get_method_info_for_offset_with_signature) { std::unique_ptr dex_file = GetTestDexData(); ASSERT_NE(dex_file, nullptr); MethodInfo info = dex_file->GetMethodInfoForOffset(0x102, true); EXPECT_EQ(info.offset, int32_t{0x100}); EXPECT_EQ(info.len, int32_t{8}); EXPECT_STREQ(info.name.data(), "void Main.()"); info = dex_file->GetMethodInfoForOffset(0x118, true); EXPECT_EQ(info.offset, int32_t{0x118}); EXPECT_EQ(info.len, int32_t{2}); EXPECT_STREQ(info.name.data(), "void Main.main(java.lang.String[])"); // Retrieve a cached result. info = dex_file->GetMethodInfoForOffset(0x104, true); EXPECT_EQ(info.offset, int32_t{0x100}); EXPECT_EQ(info.len, int32_t{8}); EXPECT_STREQ(info.name.data(), "void Main.()"); // with_signature doesn't affect the cache. info = dex_file->GetMethodInfoForOffset(0x104, false); EXPECT_EQ(info.offset, int32_t{0x100}); EXPECT_EQ(info.len, int32_t{8}); EXPECT_STREQ(info.name.data(), "Main."); } TEST(DexFileTest, get_method_info_for_offset_boundaries) { std::unique_ptr dex_file = GetTestDexData(); ASSERT_NE(dex_file, nullptr); MethodInfo info = dex_file->GetMethodInfoForOffset(0x100000, false); EXPECT_EQ(info.offset, int32_t{0}); info = dex_file->GetMethodInfoForOffset(0x99, false); EXPECT_EQ(info.offset, int32_t{0}); info = dex_file->GetMethodInfoForOffset(0x100, false); EXPECT_EQ(info.offset, int32_t{0x100}); info = dex_file->GetMethodInfoForOffset(0x107, false); EXPECT_EQ(info.offset, int32_t{0x100}); info = dex_file->GetMethodInfoForOffset(0x108, false); EXPECT_EQ(info.offset, int32_t{0}); // Make sure that once the whole dex file has been cached, no problems occur. info = dex_file->GetMethodInfoForOffset(0x98, false); EXPECT_EQ(info.offset, int32_t{0}); // Choose a value that is in the cached map, but not in a valid method. info = dex_file->GetMethodInfoForOffset(0x110, false); EXPECT_EQ(info.offset, int32_t{0}); } TEST(DexFileTest, get_all_method_infos_without_signature) { std::unique_ptr dex_file = GetTestDexData(); ASSERT_NE(dex_file, nullptr); std::vector infos; infos.emplace_back(MethodInfo{0x100, 8, DexString("Main.")}); infos.emplace_back(MethodInfo{0x118, 2, DexString("Main.main")}); ASSERT_EQ(dex_file->GetAllMethodInfos(false), infos); } TEST(DexFileTest, get_all_method_infos_with_signature) { std::unique_ptr dex_file = GetTestDexData(); ASSERT_NE(dex_file, nullptr); std::vector infos; infos.emplace_back(MethodInfo{0x100, 8, DexString("void Main.()")}); infos.emplace_back(MethodInfo{0x118, 2, DexString("void Main.main(java.lang.String[])")}); ASSERT_EQ(dex_file->GetAllMethodInfos(true), infos); } TEST(DexFileTest, move_construct) { std::unique_ptr dex_file = GetTestDexData(); ASSERT_NE(dex_file, nullptr); auto df1 = DexFile(std::move(*dex_file)); auto df2 = DexFile(std::move(df1)); MethodInfo info = df2.GetMethodInfoForOffset(0x100, false); EXPECT_EQ(info.offset, int32_t{0x100}); } TEST(DexFileTest, pointer_construct) { std::unique_ptr dex_file = GetTestDexData(); ASSERT_NE(dex_file, nullptr); auto new_dex = DexFile(dex_file); ASSERT_TRUE(dex_file.get() == nullptr); MethodInfo info = new_dex.GetMethodInfoForOffset(0x100, false); EXPECT_EQ(info.offset, int32_t{0x100}); } } // namespace dex } // namespace art_api