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 <cstddef>
22 #include <cstdint>
23 #include <string>
24 
25 #include <sys/user.h>
26 
27 #include "berberis/base/file.h"
28 #include "berberis/base/page_size.h"
29 #include "berberis/base/stringprintf.h"
30 
31 namespace {
32 
33 const constexpr char* kTestSymbolName = "tiny_symbol";
34 const constexpr char* kTestLibInvalidElfClassName = "libtinytest_invalid_elf_class.so";
35 const constexpr char* kTestLibGnuName = "libtinytest.so";
36 const constexpr char* kTestLibSysvName = "libtinytest_sysv.so";
37 const constexpr char* kTestExecutableName = "tiny_static_executable";
38 constexpr size_t kTestLibGnuLoadSize = 0x3000;
39 
40 #if defined(__LP64__)
41 constexpr uintptr_t kStaticExecutableEntryPoint = 0x1ce00;
42 constexpr const char* kTestFilesDir = "/tiny_loader/tests/files/64/";
43 #else
44 constexpr uintptr_t kStaticExecutableEntryPoint = 0x410f30;
45 constexpr const char* kTestFilesDir = "/tiny_loader/tests/files/32/";
46 #endif
47 
AssertLoadedElfFilesEqual(const LoadedElfFile & actual,const LoadedElfFile & expected)48 void AssertLoadedElfFilesEqual(const LoadedElfFile& actual, const LoadedElfFile& expected) {
49   ASSERT_EQ(actual.e_type(), expected.e_type());
50   ASSERT_EQ(actual.base_addr(), expected.base_addr());
51   ASSERT_EQ(actual.load_bias(), expected.load_bias());
52   ASSERT_EQ(actual.entry_point(), expected.entry_point());
53   ASSERT_EQ(actual.phdr_table(), expected.phdr_table());
54   ASSERT_EQ(actual.phdr_count(), expected.phdr_count());
55 }
56 
GetTestElfFilepath(const char * name,std::string * real_path,std::string * error_msg)57 bool GetTestElfFilepath(const char* name, std::string* real_path, std::string* error_msg) {
58   std::string path = berberis::GetExecutableDirectory();
59   path += kTestFilesDir;
60 
61   std::string out_path;
62   if (!berberis::Realpath(path.c_str(), &out_path)) {
63     *error_msg = berberis::StringPrintf("Failed to get realpath for \"%s\"", path.c_str());
64     return false;
65   }
66 
67   out_path += "/";
68   out_path += name;
69 
70   if (!berberis::Realpath(out_path, real_path)) {
71     *error_msg = berberis::StringPrintf("\"%s\": does not exist", out_path.c_str());
72     return false;
73   }
74 
75   return true;
76 }
77 
TestLoadLibrary(const char * test_library_name)78 void TestLoadLibrary(const char* test_library_name) {
79   LoadedElfFile loaded_elf_file;
80   std::string error_msg;
81   std::string elf_filepath;
82   ASSERT_TRUE(GetTestElfFilepath(test_library_name, &elf_filepath, &error_msg)) << error_msg;
83   ASSERT_TRUE(TinyLoader::LoadFromFile(elf_filepath.c_str(), &loaded_elf_file, &error_msg))
84       << error_msg;
85 
86   // Get AT_BASE -> note that even though linker does not use
87   // AT_BASE this is needed for dynamic vdso and passed to the linker
88   // as AT_SYSINFO_EHDR
89   void* base_addr = loaded_elf_file.base_addr();
90   ElfAddr load_bias = loaded_elf_file.load_bias();
91   ASSERT_TRUE(base_addr != nullptr);
92   ASSERT_TRUE(reinterpret_cast<void*>(load_bias) == base_addr);
93   ASSERT_TRUE(loaded_elf_file.phdr_table() != nullptr);
94   ASSERT_EQ(loaded_elf_file.phdr_count(), 9U);
95   void* symbol_addr = loaded_elf_file.FindSymbol(kTestSymbolName);
96   ASSERT_TRUE(symbol_addr != nullptr);
97   ASSERT_TRUE(static_cast<uint8_t*>(symbol_addr) > static_cast<uint8_t*>(base_addr));
98 
99   std::vector<std::pair<std::string, void*>> symbols;
100   loaded_elf_file.ForEachSymbol([&symbols](const char* name, void* address, const ElfSym* s) {
101     if (s->st_size != 0) {
102       symbols.emplace_back(std::string(name), address);
103     }
104   });
105 
106   ASSERT_EQ(1U, symbols.size());
107   ASSERT_EQ(kTestSymbolName, symbols.begin()->first);
108   ASSERT_EQ(symbol_addr, symbols.begin()->second);
109 
110   // AT_ENTRY for this file is 0
111   ASSERT_TRUE(loaded_elf_file.entry_point() == nullptr);
112 
113   ASSERT_EQ(ET_DYN, loaded_elf_file.e_type());
114 
115   ASSERT_NE(nullptr, loaded_elf_file.dynamic());
116 
117   // The second part of the test - to Load this file from already mapped memory.
118   // Check that resulted loaded_elf_file is effectively the same
119   LoadedElfFile memory_elf_file;
120   ASSERT_TRUE(TinyLoader::LoadFromMemory(
121       elf_filepath.c_str(), base_addr, berberis::kPageSize, &memory_elf_file, &error_msg))
122       << error_msg;
123   AssertLoadedElfFilesEqual(memory_elf_file, loaded_elf_file);
124   void* memory_symbol_addr = memory_elf_file.FindSymbol(kTestSymbolName);
125   ASSERT_EQ(symbol_addr, memory_symbol_addr);
126 }
127 
128 }  // namespace
129 
TEST(tiny_loader,library_gnu_hash)130 TEST(tiny_loader, library_gnu_hash) {
131   TestLoadLibrary(kTestLibGnuName);
132 }
133 
TEST(tiny_loader,library_sysv_hash)134 TEST(tiny_loader, library_sysv_hash) {
135   TestLoadLibrary(kTestLibSysvName);
136 }
137 
TEST(tiny_loader,CalculateLoadSize)138 TEST(tiny_loader, CalculateLoadSize) {
139   std::string error_msg;
140   std::string elf_filepath;
141   ASSERT_TRUE(GetTestElfFilepath(kTestLibGnuName, &elf_filepath, &error_msg)) << error_msg;
142   size_t size = TinyLoader::CalculateLoadSize(elf_filepath.c_str(), &error_msg);
143   EXPECT_EQ(size, kTestLibGnuLoadSize);
144 }
145 
TEST(tiny_loader,library_invalid_elf_class)146 TEST(tiny_loader, library_invalid_elf_class) {
147   LoadedElfFile loaded_elf_file;
148   std::string error_msg;
149   std::string elf_filepath;
150   ASSERT_TRUE(GetTestElfFilepath(kTestLibInvalidElfClassName, &elf_filepath, &error_msg))
151       << error_msg;
152   ASSERT_FALSE(TinyLoader::LoadFromFile(elf_filepath.c_str(), &loaded_elf_file, &error_msg));
153 #if defined(__LP64__)
154   std::string expected_error_msg =
155       "\"" + elf_filepath + "\" ELFCLASS32 is not supported, expected ELFCLASS64.";
156 #else
157   std::string expected_error_msg =
158       "\"" + elf_filepath + "\" ELFCLASS64 is not supported, expected ELFCLASS32.";
159 #endif
160   ASSERT_EQ(expected_error_msg, error_msg);
161 }
162 
TEST(tiny_loader,binary)163 TEST(tiny_loader, binary) {
164   LoadedElfFile loaded_elf_file;
165   std::string error_msg;
166 
167   std::string elf_filepath;
168   ASSERT_TRUE(GetTestElfFilepath(kTestExecutableName, &elf_filepath, &error_msg)) << error_msg;
169   ASSERT_TRUE(TinyLoader::LoadFromFile(elf_filepath.c_str(), &loaded_elf_file, &error_msg))
170       << error_msg;
171 
172   ASSERT_EQ(reinterpret_cast<void*>(kStaticExecutableEntryPoint), loaded_elf_file.entry_point());
173   ASSERT_EQ(ET_EXEC, loaded_elf_file.e_type());
174 
175   ASSERT_NE(nullptr, loaded_elf_file.phdr_table());
176 
177   ASSERT_EQ(nullptr, loaded_elf_file.dynamic());
178 
179   // The second part of the test - to Load this file from already mapped memory.
180   // Check that resulted loaded_elf_file is effectively the same
181   LoadedElfFile memory_elf_file;
182   ASSERT_TRUE(TinyLoader::LoadFromMemory(elf_filepath.c_str(),
183                                          loaded_elf_file.base_addr(),
184                                          berberis::kPageSize,
185                                          &memory_elf_file,
186                                          &error_msg))
187       << error_msg;
188   AssertLoadedElfFilesEqual(memory_elf_file, loaded_elf_file);
189 }
190