1 // Copyright (C) 2014 The Android Open Source Project
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14
15 #include "base/SharedLibrary.h"
16 #include "base/PathUtils.h"
17
18 #include <functional>
19 #include <vector>
20
21 #include <stddef.h>
22 #include <string.h>
23 #include <stdio.h>
24
25 #ifndef _WIN32
26 #include <dlfcn.h>
27 #include <stdlib.h>
28 #endif
29
30 #define GL_LOG(fmt,...) fprintf(stderr, "%s:%d " fmt, __func__, __LINE__, ##__VA_ARGS__);
31
32 using android::base::PathUtils;
33
34 namespace android {
35 namespace base {
36
37 class LibrarySearchPaths {
38 public:
39 LibrarySearchPaths() = default;
40
addPath(const char * path)41 void addPath(const char* path) {
42 mPaths.push_back(path);
43 }
44
forEachPath(std::function<void (const std::string &)> func)45 void forEachPath(std::function<void(const std::string&)> func) {
46 for (const auto& path: mPaths) {
47 func(path);
48 }
49 }
50
51 private:
52 std::vector<std::string> mPaths;
53 };
54
sSearchPaths()55 LibrarySearchPaths* sSearchPaths() {
56 static LibrarySearchPaths* paths = new LibrarySearchPaths;
57 return paths;
58 }
59
60 static SharedLibrary::LibraryMap s_libraryMap;
61
62 // static
open(const char * libraryName)63 SharedLibrary* SharedLibrary::open(const char* libraryName) {
64 GL_LOG("SharedLibrary::open for [%s]\n", libraryName);
65 char error[1];
66 return open(libraryName, error, sizeof(error));
67 }
68
open(const char * libraryName,char * error,size_t errorSize)69 SharedLibrary* SharedLibrary::open(const char* libraryName,
70 char* error,
71 size_t errorSize) {
72 auto lib = s_libraryMap.find(libraryName);
73
74 if (lib == s_libraryMap.end()) {
75 GL_LOG("SharedLibrary::open for [%s]: not found in map, open for the first time\n", libraryName);
76 SharedLibrary* load = do_open(libraryName, error, errorSize);
77 if (load != nullptr) {
78 s_libraryMap[libraryName] =
79 std::unique_ptr<SharedLibrary, SharedLibrary::Deleter>(load);
80 }
81 return load;
82 }
83
84 return lib->second.get();
85 }
86
87 #ifdef _WIN32
88
89 // static
do_open(const char * libraryName,char * error,size_t errorSize)90 SharedLibrary* SharedLibrary::do_open(const char* libraryName,
91 char* error,
92 size_t errorSize) {
93 GL_LOG("SharedLibrary::open for [%s] (win32): call LoadLibrary\n", libraryName);
94 HMODULE lib = LoadLibraryA(libraryName);
95
96 // Try a bit harder to find the shared library if we cannot find it.
97 if (!lib) {
98 GL_LOG("SharedLibrary::open for [%s] can't find in default path. Searching alternatives...\n",
99 libraryName);
100 sSearchPaths()->forEachPath([&lib, libraryName](const std::string& path) {
101 if (!lib) {
102 auto libName = PathUtils::join(path, libraryName);
103 GL_LOG("SharedLibrary::open for [%s]: trying [%s]\n",
104 libraryName, libName.c_str());
105 lib = LoadLibraryA(libName.c_str());
106 GL_LOG("SharedLibrary::open for [%s]: trying [%s]. found? %d\n",
107 libraryName, libName.c_str(), lib != nullptr);
108 }
109 });
110 }
111
112 if (lib) {
113 constexpr size_t kMaxPathLength = 2048;
114 char fullPath[kMaxPathLength];
115 GetModuleFileNameA(lib, fullPath, kMaxPathLength);
116 GL_LOG("SharedLibrary::open succeeded for [%s]. File name: [%s]\n",
117 libraryName, fullPath);
118 return new SharedLibrary(lib);
119 }
120
121 if (errorSize == 0) {
122 GL_LOG("SharedLibrary::open for [%s] failed, but no error\n",
123 libraryName);
124 return NULL;
125 }
126
127 // Convert error into human-readable message.
128 DWORD errorCode = ::GetLastError();
129 LPSTR message = NULL;
130 size_t messageLen = FormatMessageA(
131 FORMAT_MESSAGE_ALLOCATE_BUFFER |
132 FORMAT_MESSAGE_FROM_SYSTEM |
133 FORMAT_MESSAGE_IGNORE_INSERTS,
134 NULL,
135 errorCode,
136 MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
137 (LPSTR) &message,
138 0,
139 NULL);
140
141 int ret = snprintf(error, errorSize, "%.*s", (int)messageLen, message);
142 if (ret < 0 || ret == static_cast<int>(errorSize)) {
143 // snprintf() on Windows doesn't behave as expected by C99,
144 // this path is to ensure that the result is always properly
145 // zero-terminated.
146 ret = static_cast<int>(errorSize - 1);
147 error[ret] = '\0';
148 }
149 // Remove any trailing \r\n added by FormatMessage
150 if (ret > 0 && error[ret - 1] == '\n') {
151 error[--ret] = '\0';
152 }
153 if (ret > 0 && error[ret - 1] == '\r') {
154 error[--ret] = '\0';
155 }
156 GL_LOG("Failed to load [%s]. Error string: [%s]\n",
157 libraryName, error);
158
159 return NULL;
160 }
161
SharedLibrary(HandleType lib)162 SharedLibrary::SharedLibrary(HandleType lib) : mLib(lib) {}
163
~SharedLibrary()164 SharedLibrary::~SharedLibrary() {
165 if (mLib) {
166 // BUG: 66013149
167 // In windows it sometimes hang on exit when destroying s_libraryMap.
168 // Let's skip freeing the library, since pretty much the only situation
169 // we need to do it, is on exit.
170 //FreeLibrary(mLib);
171 }
172 }
173
findSymbol(const char * symbolName) const174 SharedLibrary::FunctionPtr SharedLibrary::findSymbol(
175 const char* symbolName) const {
176 if (!mLib || !symbolName) {
177 return NULL;
178 }
179 return reinterpret_cast<FunctionPtr>(
180 GetProcAddress(mLib, symbolName));
181 }
182
183 #else // !_WIN32
184
185 // static
do_open(const char * libraryName,char * error,size_t errorSize)186 SharedLibrary* SharedLibrary::do_open(const char* libraryName,
187 char* error,
188 size_t errorSize) {
189 GL_LOG("SharedLibrary::open for [%s] (posix): begin\n", libraryName);
190
191 const char* libPath = libraryName;
192 char* path = NULL;
193
194 const char* libBaseName = strrchr(libraryName, '/');
195 if (!libBaseName) {
196 libBaseName = libraryName;
197 }
198
199 if (!strchr(libBaseName, '.')) {
200 // There is no extension in this library name, so append one.
201 #ifdef __APPLE__
202 static const char kDllExtension[] = ".dylib";
203 #else
204 static const char kDllExtension[] = ".so";
205 #endif
206 size_t pathLen = strlen(libraryName) + sizeof(kDllExtension);
207 path = static_cast<char*>(malloc(pathLen));
208 snprintf(path, pathLen, "%s%s", libraryName, kDllExtension);
209 libPath = path;
210 }
211
212 dlerror(); // clear error.
213
214 #ifdef __APPLE__
215 // On OSX, some libraries don't include an extension (notably OpenGL)
216 // On OSX we try to open |libraryName| first. If that doesn't exist,
217 // we try |libraryName|.dylib
218 GL_LOG("SharedLibrary::open for [%s] (posix,darwin): call dlopen\n", libraryName);
219 void* lib = dlopen(libraryName, RTLD_NOW);
220 if (lib == NULL) {
221 GL_LOG("SharedLibrary::open for [%s] (posix,darwin): failed, "
222 "try again with [%s]\n", libraryName, libPath);
223 lib = dlopen(libPath, RTLD_NOW);
224
225 sSearchPaths()->forEachPath([&lib, libraryName, libPath](const std::string& path) {
226 if (!lib) {
227 auto libName = PathUtils::join(path, libraryName);
228 GL_LOG("SharedLibrary::open for [%s] (posix,darwin): still failed, "
229 "try [%s]\n", libraryName, libName.c_str());
230 lib = dlopen(libName.c_str(), RTLD_NOW);
231 if (!lib) {
232 auto libPathName = PathUtils::join(path, libPath);
233 GL_LOG("SharedLibrary::open for [%s] (posix,darwin): still failed, "
234 "try [%s]\n", libraryName, libPathName.c_str());
235 lib = dlopen(libPathName.c_str(), RTLD_NOW);
236 }
237 }
238 });
239 }
240 #else
241 GL_LOG("SharedLibrary::open for [%s] (posix,linux): call dlopen on [%s]\n",
242 libraryName, libPath);
243 void* lib = dlopen(libPath, RTLD_NOW);
244 #endif
245
246 sSearchPaths()->forEachPath([&lib, libPath, libraryName](const std::string& path) {
247 if (!lib) {
248 auto libPathName = PathUtils::join(path, libPath);
249 GL_LOG("SharedLibrary::open for [%s] (posix): try again with %s\n",
250 libraryName,
251 libPathName.c_str());
252 lib = dlopen(libPathName.c_str(), RTLD_NOW);
253 }
254 });
255
256 if (path) {
257 free(path);
258 }
259
260 if (lib) {
261 return new SharedLibrary(lib);
262 }
263
264 snprintf(error, errorSize, "%s", dlerror());
265 GL_LOG("SharedLibrary::open for [%s] failed (posix). dlerror: [%s]\n",
266 libraryName, error);
267 return NULL;
268 }
269
SharedLibrary(HandleType lib)270 SharedLibrary::SharedLibrary(HandleType lib) : mLib(lib) {}
271
~SharedLibrary()272 SharedLibrary::~SharedLibrary() {
273 if (mLib) {
274 dlclose(mLib);
275 }
276 }
277
findSymbol(const char * symbolName) const278 SharedLibrary::FunctionPtr SharedLibrary::findSymbol(
279 const char* symbolName) const {
280 if (!mLib || !symbolName) {
281 return NULL;
282 }
283 return reinterpret_cast<FunctionPtr>(dlsym(mLib, symbolName));
284 }
285
286 #endif // !_WIN32
287
288 // static
addLibrarySearchPath(const char * path)289 void SharedLibrary::addLibrarySearchPath(const char* path) {
290 sSearchPaths()->addPath(path);
291 }
292
293 } // namespace base
294 } // namespace android
295