• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2012 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 <dlfcn.h>
20 #include <libgen.h>
21 #include <limits.h>
22 #include <stdio.h>
23 #include <stdint.h>
24 
25 #include <string>
26 
27 #define ASSERT_SUBSTR(needle, haystack) \
28     ASSERT_PRED_FORMAT2(::testing::IsSubstring, needle, haystack)
29 
30 static bool gCalled = false;
DlSymTestFunction()31 extern "C" void DlSymTestFunction() {
32   gCalled = true;
33 }
34 
TEST(dlfcn,dlsym_in_self)35 TEST(dlfcn, dlsym_in_self) {
36   dlerror(); // Clear any pending errors.
37   void* self = dlopen(NULL, RTLD_NOW);
38   ASSERT_TRUE(self != NULL);
39   ASSERT_TRUE(dlerror() == NULL);
40 
41   void* sym = dlsym(self, "DlSymTestFunction");
42   ASSERT_TRUE(sym != NULL);
43 
44   void (*function)() = reinterpret_cast<void(*)()>(sym);
45 
46   gCalled = false;
47   function();
48   ASSERT_TRUE(gCalled);
49 
50   ASSERT_EQ(0, dlclose(self));
51 }
52 
TEST(dlfcn,dlopen_failure)53 TEST(dlfcn, dlopen_failure) {
54   void* self = dlopen("/does/not/exist", RTLD_NOW);
55   ASSERT_TRUE(self == NULL);
56 #if __BIONIC__
57   ASSERT_STREQ("dlopen failed: library \"/does/not/exist\" not found", dlerror());
58 #else
59   ASSERT_STREQ("/does/not/exist: cannot open shared object file: No such file or directory", dlerror());
60 #endif
61 }
62 
ConcurrentDlErrorFn(void *)63 static void* ConcurrentDlErrorFn(void*) {
64   dlopen("/child/thread", RTLD_NOW);
65   return reinterpret_cast<void*>(strdup(dlerror()));
66 }
67 
TEST(dlfcn,dlerror_concurrent)68 TEST(dlfcn, dlerror_concurrent) {
69   dlopen("/main/thread", RTLD_NOW);
70   const char* main_thread_error = dlerror();
71   ASSERT_SUBSTR("/main/thread", main_thread_error);
72 
73   pthread_t t;
74   ASSERT_EQ(0, pthread_create(&t, NULL, ConcurrentDlErrorFn, NULL));
75   void* result;
76   ASSERT_EQ(0, pthread_join(t, &result));
77   char* child_thread_error = static_cast<char*>(result);
78   ASSERT_SUBSTR("/child/thread", child_thread_error);
79   free(child_thread_error);
80 
81   ASSERT_SUBSTR("/main/thread", main_thread_error);
82 }
83 
TEST(dlfcn,dlsym_failures)84 TEST(dlfcn, dlsym_failures) {
85   dlerror(); // Clear any pending errors.
86   void* self = dlopen(NULL, RTLD_NOW);
87   ASSERT_TRUE(self != NULL);
88   ASSERT_TRUE(dlerror() == NULL);
89 
90   void* sym;
91 
92   // NULL handle.
93   sym = dlsym(NULL, "test");
94   ASSERT_TRUE(sym == NULL);
95 #if __BIONIC__
96   ASSERT_SUBSTR("dlsym library handle is null", dlerror());
97 #else
98   ASSERT_SUBSTR("undefined symbol: test", dlerror()); // glibc isn't specific about the failure.
99 #endif
100 
101   // NULL symbol name.
102 #if __BIONIC__
103   // glibc marks this parameter non-null and SEGVs if you cheat.
104   sym = dlsym(self, NULL);
105   ASSERT_TRUE(sym == NULL);
106   ASSERT_SUBSTR("", dlerror());
107 #endif
108 
109   // Symbol that doesn't exist.
110   sym = dlsym(self, "ThisSymbolDoesNotExist");
111   ASSERT_TRUE(sym == NULL);
112   ASSERT_SUBSTR("undefined symbol: ThisSymbolDoesNotExist", dlerror());
113 
114   ASSERT_EQ(0, dlclose(self));
115 }
116 
TEST(dlfcn,dladdr)117 TEST(dlfcn, dladdr) {
118   dlerror(); // Clear any pending errors.
119   void* self = dlopen(NULL, RTLD_NOW);
120   ASSERT_TRUE(self != NULL);
121   ASSERT_TRUE(dlerror() == NULL);
122 
123   void* sym = dlsym(self, "DlSymTestFunction");
124   ASSERT_TRUE(sym != NULL);
125 
126   // Deliberately ask dladdr for an address inside a symbol, rather than the symbol base address.
127   void* addr = reinterpret_cast<void*>(reinterpret_cast<uintptr_t>(sym) + 2);
128 
129   Dl_info info;
130   int rc = dladdr(addr, &info);
131   ASSERT_NE(rc, 0); // Zero on error, non-zero on success.
132 
133   // Get the name of this executable.
134   char executable_path[PATH_MAX];
135   rc = readlink("/proc/self/exe", executable_path, sizeof(executable_path));
136   ASSERT_NE(rc, -1);
137   executable_path[rc] = '\0';
138   std::string executable_name(basename(executable_path));
139 
140   // The filename should be that of this executable.
141   // Note that we don't know whether or not we have the full path, so we want an "ends_with" test.
142   std::string dli_fname(info.dli_fname);
143   dli_fname = basename(&dli_fname[0]);
144   ASSERT_EQ(dli_fname, executable_name);
145 
146   // The symbol name should be the symbol we looked up.
147   ASSERT_STREQ(info.dli_sname, "DlSymTestFunction");
148 
149   // The address should be the exact address of the symbol.
150   ASSERT_EQ(info.dli_saddr, sym);
151 
152   // Look in /proc/pid/maps to find out what address we were loaded at.
153   // TODO: factor /proc/pid/maps parsing out into a class and reuse all over bionic.
154   void* base_address = NULL;
155   char path[PATH_MAX];
156   snprintf(path, sizeof(path), "/proc/%d/maps", getpid());
157   char line[BUFSIZ];
158   FILE* fp = fopen(path, "r");
159   ASSERT_TRUE(fp != NULL);
160   while (fgets(line, sizeof(line), fp) != NULL) {
161     uintptr_t start = strtoul(line, 0, 16);
162     line[strlen(line) - 1] = '\0'; // Chomp the '\n'.
163     char* path = strchr(line, '/');
164     if (path != NULL && strcmp(executable_path, path) == 0) {
165       base_address = reinterpret_cast<void*>(start);
166       break;
167     }
168   }
169   fclose(fp);
170 
171   // The base address should be the address we were loaded at.
172   ASSERT_EQ(info.dli_fbase, base_address);
173 
174   ASSERT_EQ(0, dlclose(self));
175 }
176 
TEST(dlfcn,dladdr_invalid)177 TEST(dlfcn, dladdr_invalid) {
178   Dl_info info;
179 
180   dlerror(); // Clear any pending errors.
181 
182   // No symbol corresponding to NULL.
183   ASSERT_EQ(dladdr(NULL, &info), 0); // Zero on error, non-zero on success.
184   ASSERT_TRUE(dlerror() == NULL); // dladdr(3) doesn't set dlerror(3).
185 
186   // No symbol corresponding to a stack address.
187   ASSERT_EQ(dladdr(&info, &info), 0); // Zero on error, non-zero on success.
188   ASSERT_TRUE(dlerror() == NULL); // dladdr(3) doesn't set dlerror(3).
189 }
190 
191 // Our dynamic linker doesn't support GNU hash tables.
192 #if defined(__BIONIC__)
193 // GNU-style ELF hash tables are incompatible with the MIPS ABI.
194 // MIPS requires .dynsym to be sorted to match the GOT but GNU-style requires sorting by hash code.
195 #if !defined(__mips__)
TEST(dlfcn,dlopen_library_with_only_gnu_hash)196 TEST(dlfcn, dlopen_library_with_only_gnu_hash) {
197   dlerror(); // Clear any pending errors.
198   void* handle = dlopen("no-elf-hash-table-library.so", RTLD_NOW);
199   ASSERT_TRUE(handle == NULL);
200   ASSERT_STREQ("dlopen failed: empty/missing DT_HASH in \"no-elf-hash-table-library.so\" (built with --hash-style=gnu?)", dlerror());
201 }
202 #endif
203 #endif
204 
TEST(dlfcn,dlopen_bad_flags)205 TEST(dlfcn, dlopen_bad_flags) {
206   dlerror(); // Clear any pending errors.
207   void* handle;
208 
209 #ifdef __GLIBC__
210   // glibc was smart enough not to define RTLD_NOW as 0, so it can detect missing flags.
211   handle = dlopen(NULL, 0);
212   ASSERT_TRUE(handle == NULL);
213   ASSERT_SUBSTR("invalid", dlerror());
214 #endif
215 
216   handle = dlopen(NULL, 0xffffffff);
217   ASSERT_TRUE(handle == NULL);
218   ASSERT_SUBSTR("invalid", dlerror());
219 
220   // glibc actually allows you to choose both RTLD_NOW and RTLD_LAZY at the same time, and so do we.
221   handle = dlopen(NULL, RTLD_NOW|RTLD_LAZY);
222   ASSERT_TRUE(handle != NULL);
223   ASSERT_SUBSTR(NULL, dlerror());
224 }
225