| /* |
| * 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 <sys/types.h> |
| |
| #include <memory> |
| #include <string> |
| #include <string_view> |
| |
| #include <android-base/file.h> |
| #include <dex/dex_file.h> |
| #include <gtest/gtest.h> |
| |
| #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<DexFile> GetTestDexData() { |
| size_t size = sizeof(kDexData); |
| std::string error_msg; |
| std::unique_ptr<DexFile> 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<size_t>( |
| 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<size_t>( |
| 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<size_t>(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<size_t>(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<DexFile> 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.<init>"); |
| |
| 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.<init>"); |
| } |
| |
| TEST(DexFileTest, get_method_info_for_offset_with_signature) { |
| std::unique_ptr<DexFile> 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.<init>()"); |
| |
| 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.<init>()"); |
| |
| // 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.<init>"); |
| } |
| |
| TEST(DexFileTest, get_method_info_for_offset_boundaries) { |
| std::unique_ptr<DexFile> 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<DexFile> dex_file = GetTestDexData(); |
| ASSERT_NE(dex_file, nullptr); |
| |
| std::vector<MethodInfo> infos; |
| infos.emplace_back(MethodInfo{0x100, 8, DexString("Main.<init>")}); |
| 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<DexFile> dex_file = GetTestDexData(); |
| ASSERT_NE(dex_file, nullptr); |
| |
| std::vector<MethodInfo> infos; |
| infos.emplace_back(MethodInfo{0x100, 8, DexString("void Main.<init>()")}); |
| 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<DexFile> 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<DexFile> 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 |