1 /*
2 * Copyright (C) 2017 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 #include "gtest/gtest.h"
18
19 #include "berberis/tiny_loader/tiny_loader.h"
20
21 #include <string>
22
23 #include <sys/user.h>
24
25 #include "berberis/base/file.h"
26 #include "berberis/base/page_size.h"
27 #include "berberis/base/stringprintf.h"
28
29 namespace {
30
31 const constexpr char* kTestSymbolName = "tiny_symbol";
32 const constexpr char* kTestLibInvalidElfClassName = "libtinytest_invalid_elf_class.so";
33 const constexpr char* kTestLibGnuName = "libtinytest.so";
34 const constexpr char* kTestLibSysvName = "libtinytest_sysv.so";
35 const constexpr char* kTestExecutableName = "tiny_static_executable";
36
37 #if defined(__LP64__)
38 constexpr uintptr_t kStaticExecutableEntryPoint = 0x1ce00;
39 constexpr const char* kTestFilesDir = "/tiny_loader/tests/files/64/";
40 #else
41 constexpr uintptr_t kStaticExecutableEntryPoint = 0x410f30;
42 constexpr const char* kTestFilesDir = "/tiny_loader/tests/files/32/";
43 #endif
44
AssertLoadedElfFilesEqual(const LoadedElfFile & actual,const LoadedElfFile & expected)45 void AssertLoadedElfFilesEqual(const LoadedElfFile& actual, const LoadedElfFile& expected) {
46 ASSERT_EQ(actual.e_type(), expected.e_type());
47 ASSERT_EQ(actual.base_addr(), expected.base_addr());
48 ASSERT_EQ(actual.load_bias(), expected.load_bias());
49 ASSERT_EQ(actual.entry_point(), expected.entry_point());
50 ASSERT_EQ(actual.phdr_table(), expected.phdr_table());
51 ASSERT_EQ(actual.phdr_count(), expected.phdr_count());
52 }
53
GetTestElfFilepath(const char * name,std::string * real_path,std::string * error_msg)54 bool GetTestElfFilepath(const char* name, std::string* real_path, std::string* error_msg) {
55 std::string path = berberis::GetExecutableDirectory();
56 path += kTestFilesDir;
57
58 std::string out_path;
59 if (!berberis::Realpath(path.c_str(), &out_path)) {
60 *error_msg = berberis::StringPrintf("Failed to get realpath for \"%s\"", path.c_str());
61 return false;
62 }
63
64 out_path += "/";
65 out_path += name;
66
67 if (!berberis::Realpath(out_path, real_path)) {
68 *error_msg = berberis::StringPrintf("\"%s\": does not exist", out_path.c_str());
69 return false;
70 }
71
72 return true;
73 }
74
TestLoadLibrary(const char * test_library_name)75 void TestLoadLibrary(const char* test_library_name) {
76 LoadedElfFile loaded_elf_file;
77 std::string error_msg;
78 std::string elf_filepath;
79 ASSERT_TRUE(GetTestElfFilepath(test_library_name, &elf_filepath, &error_msg)) << error_msg;
80 ASSERT_TRUE(TinyLoader::LoadFromFile(elf_filepath.c_str(), &loaded_elf_file, &error_msg))
81 << error_msg;
82
83 // Get AT_BASE -> note that even though linker does not use
84 // AT_BASE this is needed for dynamic vdso and passed to the linker
85 // as AT_SYSINFO_EHDR
86 void* base_addr = loaded_elf_file.base_addr();
87 ElfAddr load_bias = loaded_elf_file.load_bias();
88 ASSERT_TRUE(base_addr != nullptr);
89 ASSERT_TRUE(reinterpret_cast<void*>(load_bias) == base_addr);
90 ASSERT_TRUE(loaded_elf_file.phdr_table() != nullptr);
91 ASSERT_EQ(loaded_elf_file.phdr_count(), 9U);
92 void* symbol_addr = loaded_elf_file.FindSymbol(kTestSymbolName);
93 ASSERT_TRUE(symbol_addr != nullptr);
94 ASSERT_TRUE(static_cast<uint8_t*>(symbol_addr) > static_cast<uint8_t*>(base_addr));
95
96 std::vector<std::pair<std::string, void*>> symbols;
97 loaded_elf_file.ForEachSymbol([&symbols](const char* name, void* address, const ElfSym* s) {
98 if (s->st_size != 0) {
99 symbols.emplace_back(std::string(name), address);
100 }
101 });
102
103 ASSERT_EQ(1U, symbols.size());
104 ASSERT_EQ(kTestSymbolName, symbols.begin()->first);
105 ASSERT_EQ(symbol_addr, symbols.begin()->second);
106
107 // AT_ENTRY for this file is 0
108 ASSERT_TRUE(loaded_elf_file.entry_point() == nullptr);
109
110 ASSERT_EQ(ET_DYN, loaded_elf_file.e_type());
111
112 ASSERT_NE(nullptr, loaded_elf_file.dynamic());
113
114 // The second part of the test - to Load this file from already mapped memory.
115 // Check that resulted loaded_elf_file is effectively the same
116 LoadedElfFile memory_elf_file;
117 ASSERT_TRUE(TinyLoader::LoadFromMemory(
118 elf_filepath.c_str(), base_addr, berberis::kPageSize, &memory_elf_file, &error_msg))
119 << error_msg;
120 AssertLoadedElfFilesEqual(memory_elf_file, loaded_elf_file);
121 void* memory_symbol_addr = memory_elf_file.FindSymbol(kTestSymbolName);
122 ASSERT_EQ(symbol_addr, memory_symbol_addr);
123 }
124
125 } // namespace
126
TEST(tiny_loader,library_gnu_hash)127 TEST(tiny_loader, library_gnu_hash) {
128 TestLoadLibrary(kTestLibGnuName);
129 }
130
TEST(tiny_loader,library_sysv_hash)131 TEST(tiny_loader, library_sysv_hash) {
132 TestLoadLibrary(kTestLibSysvName);
133 }
134
TEST(tiny_loader,library_invalid_elf_class)135 TEST(tiny_loader, library_invalid_elf_class) {
136 LoadedElfFile loaded_elf_file;
137 std::string error_msg;
138 std::string elf_filepath;
139 ASSERT_TRUE(GetTestElfFilepath(kTestLibInvalidElfClassName, &elf_filepath, &error_msg))
140 << error_msg;
141 ASSERT_FALSE(TinyLoader::LoadFromFile(elf_filepath.c_str(), &loaded_elf_file, &error_msg));
142 #if defined(__LP64__)
143 std::string expected_error_msg =
144 "\"" + elf_filepath + "\" ELFCLASS32 is not supported, expected ELFCLASS64.";
145 #else
146 std::string expected_error_msg =
147 "\"" + elf_filepath + "\" ELFCLASS64 is not supported, expected ELFCLASS32.";
148 #endif
149 ASSERT_EQ(expected_error_msg, error_msg);
150 }
151
TEST(tiny_loader,binary)152 TEST(tiny_loader, binary) {
153 LoadedElfFile loaded_elf_file;
154 std::string error_msg;
155
156 std::string elf_filepath;
157 ASSERT_TRUE(GetTestElfFilepath(kTestExecutableName, &elf_filepath, &error_msg)) << error_msg;
158 ASSERT_TRUE(TinyLoader::LoadFromFile(elf_filepath.c_str(), &loaded_elf_file, &error_msg))
159 << error_msg;
160
161 ASSERT_EQ(reinterpret_cast<void*>(kStaticExecutableEntryPoint), loaded_elf_file.entry_point());
162 ASSERT_EQ(ET_EXEC, loaded_elf_file.e_type());
163
164 ASSERT_NE(nullptr, loaded_elf_file.phdr_table());
165
166 ASSERT_EQ(nullptr, loaded_elf_file.dynamic());
167
168 // The second part of the test - to Load this file from already mapped memory.
169 // Check that resulted loaded_elf_file is effectively the same
170 LoadedElfFile memory_elf_file;
171 ASSERT_TRUE(TinyLoader::LoadFromMemory(elf_filepath.c_str(),
172 loaded_elf_file.base_addr(),
173 berberis::kPageSize,
174 &memory_elf_file,
175 &error_msg))
176 << error_msg;
177 AssertLoadedElfFilesEqual(memory_elf_file, loaded_elf_file);
178 }
179