• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2021-2022 The Khronos Group Inc.
3  * Copyright (c) 2021-2022 Valve Corporation
4  * Copyright (c) 2021-2022 LunarG, Inc.
5  *
6  * Permission is hereby granted, free of charge, to any person obtaining a copy
7  * of this software and/or associated documentation files (the "Materials"), to
8  * deal in the Materials without restriction, including without limitation the
9  * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
10  * sell copies of the Materials, and to permit persons to whom the Materials are
11  * furnished to do so, subject to the following conditions:
12  *
13  * The above copyright notice(s) and this permission notice shall be included in
14  * all copies or substantial portions of the Materials.
15  *
16  * THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
19  *
20  * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
21  * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
22  * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE MATERIALS OR THE
23  * USE OR OTHER DEALINGS IN THE MATERIALS.
24  *
25  * Author: Charles Giessen <charles@lunarg.com>
26  */
27 
28 #include "shim.h"
29 
30 #include <algorithm>
31 
32 #if defined(__APPLE__)
33 #include <CoreFoundation/CoreFoundation.h>
34 #endif
35 
36 PlatformShim platform_shim;
37 extern "C" {
38 #if defined(__linux__) || defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__GNU__) || defined(__QNX__)
get_platform_shim(std::vector<fs::FolderManager> * folders)39 PlatformShim* get_platform_shim(std::vector<fs::FolderManager>* folders) {
40     platform_shim = PlatformShim(folders);
41     return &platform_shim;
42 }
43 #elif defined(__APPLE__)
44 FRAMEWORK_EXPORT PlatformShim* get_platform_shim(std::vector<fs::FolderManager>* folders) {
45     platform_shim = PlatformShim(folders);
46     return &platform_shim;
47 }
48 #endif
49 
50 // Necessary for MacOS function shimming
51 #if defined(__linux__) || defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__GNU__) || defined(__QNX__)
52 #define OPENDIR_FUNC_NAME opendir
53 #define READDIR_FUNC_NAME readdir
54 #define CLOSEDIR_FUNC_NAME closedir
55 #define ACCESS_FUNC_NAME access
56 #define FOPEN_FUNC_NAME fopen
57 #define DLOPEN_FUNC_NAME dlopen
58 #define GETEUID_FUNC_NAME geteuid
59 #define GETEGID_FUNC_NAME getegid
60 #if defined(HAVE_SECURE_GETENV)
61 #define SECURE_GETENV_FUNC_NAME secure_getenv
62 #endif
63 #if defined(HAVE___SECURE_GETENV)
64 #define __SECURE_GETENV_FUNC_NAME __secure_getenv
65 #endif
66 #define PRINTF_FUNC_NAME printf
67 #define FPUTS_FUNC_NAME fputs
68 #define FPUTC_FUNC_NAME fputc
69 #elif defined(__APPLE__)
70 #define OPENDIR_FUNC_NAME my_opendir
71 #define READDIR_FUNC_NAME my_readdir
72 #define CLOSEDIR_FUNC_NAME my_closedir
73 #define ACCESS_FUNC_NAME my_access
74 #define FOPEN_FUNC_NAME my_fopen
75 #define DLOPEN_FUNC_NAME my_dlopen
76 #define GETEUID_FUNC_NAME my_geteuid
77 #define GETEGID_FUNC_NAME my_getegid
78 #if !defined(TARGET_OS_IPHONE)
79 #if defined(HAVE_SECURE_GETENV)
80 #define SECURE_GETENV_FUNC_NAME my_secure_getenv
81 #endif
82 #if defined(HAVE___SECURE_GETENV)
83 #define __SECURE_GETENV_FUNC_NAME my__secure_getenv
84 #endif
85 #endif
86 #define FPUTS_FUNC_NAME my_fputs
87 #define FPUTC_FUNC_NAME my_fputc
88 #endif
89 
90 using PFN_OPENDIR = DIR* (*)(const char* path_name);
91 using PFN_READDIR = struct dirent* (*)(DIR* dir_stream);
92 using PFN_CLOSEDIR = int (*)(DIR* dir_stream);
93 using PFN_ACCESS = int (*)(const char* pathname, int mode);
94 using PFN_FOPEN = FILE* (*)(const char* filename, const char* mode);
95 using PFN_DLOPEN = void* (*)(const char* in_filename, int flags);
96 using PFN_GETEUID = uid_t (*)(void);
97 using PFN_GETEGID = gid_t (*)(void);
98 #if defined(HAVE_SECURE_GETENV) || defined(HAVE___SECURE_GETENV)
99 using PFN_SEC_GETENV = char* (*)(const char* name);
100 #endif
101 using PFN_FPUTS = int (*)(const char* str, FILE* stream);
102 using PFN_FPUTC = int (*)(int c, FILE* stream);
103 
104 #if defined(__APPLE__)
105 #define real_opendir opendir
106 #define real_readdir readdir
107 #define real_closedir closedir
108 #define real_access access
109 #define real_fopen fopen
110 #define real_dlopen dlopen
111 #define real_geteuid geteuid
112 #define real_getegid getegid
113 #if defined(HAVE_SECURE_GETENV)
114 #define real_secure_getenv secure_getenv
115 #endif
116 #if defined(HAVE___SECURE_GETENV)
117 #define real__secure_getenv __secure_getenv
118 #endif
119 #define real_fputs fputs
120 #define real_fputc fputc
121 #else
122 PFN_OPENDIR real_opendir = nullptr;
123 PFN_READDIR real_readdir = nullptr;
124 PFN_CLOSEDIR real_closedir = nullptr;
125 PFN_ACCESS real_access = nullptr;
126 PFN_FOPEN real_fopen = nullptr;
127 PFN_DLOPEN real_dlopen = nullptr;
128 PFN_GETEUID real_geteuid = nullptr;
129 PFN_GETEGID real_getegid = nullptr;
130 #if defined(HAVE_SECURE_GETENV)
131 PFN_SEC_GETENV real_secure_getenv = nullptr;
132 #endif
133 #if defined(HAVE___SECURE_GETENV)
134 PFN_SEC_GETENV real__secure_getenv = nullptr;
135 #endif
136 PFN_FPUTS real_fputs = nullptr;
137 PFN_FPUTC real_fputc = nullptr;
138 #endif
139 
OPENDIR_FUNC_NAME(const char * path_name)140 FRAMEWORK_EXPORT DIR* OPENDIR_FUNC_NAME(const char* path_name) {
141 #if !defined(__APPLE__)
142     if (!real_opendir) real_opendir = (PFN_OPENDIR)dlsym(RTLD_NEXT, "opendir");
143 #endif
144     if (platform_shim.is_during_destruction) {
145         return real_opendir(path_name);
146     }
147     DIR* dir;
148     if (platform_shim.is_fake_path(path_name)) {
149         auto real_path_name = platform_shim.get_real_path_from_fake_path(std::filesystem::path(path_name));
150         dir = real_opendir(real_path_name.c_str());
151         platform_shim.dir_entries.push_back(DirEntry{dir, std::string(path_name), {}, 0, true});
152     } else if (platform_shim.is_known_path(path_name)) {
153         dir = real_opendir(path_name);
154         platform_shim.dir_entries.push_back(DirEntry{dir, std::string(path_name), {}, 0, false});
155     } else {
156         dir = real_opendir(path_name);
157     }
158 
159     return dir;
160 }
161 
READDIR_FUNC_NAME(DIR * dir_stream)162 FRAMEWORK_EXPORT struct dirent* READDIR_FUNC_NAME(DIR* dir_stream) {
163 #if !defined(__APPLE__)
164     if (!real_readdir) {
165         if (sizeof(void*) == 8) {
166             real_readdir = (PFN_READDIR)dlsym(RTLD_NEXT, "readdir");
167         } else {
168             // Necessary to specify the 64 bit readdir version since that is what is linked in when using _FILE_OFFSET_BITS
169             real_readdir = (PFN_READDIR)dlsym(RTLD_NEXT, "readdir64");
170         }
171     }
172 
173 #endif
174     if (platform_shim.is_during_destruction) {
175         return real_readdir(dir_stream);
176     }
177     auto it = std::find_if(platform_shim.dir_entries.begin(), platform_shim.dir_entries.end(),
178                            [dir_stream](DirEntry const& entry) { return entry.directory == dir_stream; });
179 
180     if (it == platform_shim.dir_entries.end()) {
181         return real_readdir(dir_stream);
182     }
183     // Folder was found but this is the first file to be read from it
184     if (it->current_index == 0) {
185         std::vector<struct dirent*> folder_contents;
186         std::vector<std::string> dirent_filenames;
187         while (true) {
188             errno = 0;
189             struct dirent* dir_entry = real_readdir(dir_stream);
190             if (errno != 0) {
191                 std::cerr << "Readdir failed: errno has value of " << std::to_string(errno) << "\n";
192             }
193             if (NULL == dir_entry) {
194                 break;
195             }
196             // skip . and .. entries
197             if ((dir_entry->d_name[0] == '.' && dir_entry->d_name[1] == '.' && dir_entry->d_name[2] == '\0') ||
198                 (dir_entry->d_name[0] == '.' && dir_entry->d_name[1] == '\0')) {
199                 continue;
200             }
201             folder_contents.push_back(dir_entry);
202             dirent_filenames.push_back(&dir_entry->d_name[0]);
203         }
204         auto real_path = it->folder_path;
205         if (it->is_fake_path) {
206             real_path = platform_shim.redirection_map.at(it->folder_path);
207         }
208         auto filenames = get_folder_contents(platform_shim.folders, real_path);
209 
210         // Add the dirent structures in the order they appear in the FolderManager
211         // Ignore anything which wasn't in the FolderManager
212         for (auto const& file : filenames) {
213             for (size_t i = 0; i < dirent_filenames.size(); i++) {
214                 if (file == dirent_filenames.at(i)) {
215                     it->contents.push_back(folder_contents.at(i));
216                     break;
217                 }
218             }
219         }
220     }
221     if (it->current_index >= it->contents.size()) return nullptr;
222     return it->contents.at(it->current_index++);
223 }
224 
CLOSEDIR_FUNC_NAME(DIR * dir_stream)225 FRAMEWORK_EXPORT int CLOSEDIR_FUNC_NAME(DIR* dir_stream) {
226 #if !defined(__APPLE__)
227     if (!real_closedir) real_closedir = (PFN_CLOSEDIR)dlsym(RTLD_NEXT, "closedir");
228 #endif
229     if (platform_shim.is_during_destruction) {
230         return real_closedir(dir_stream);
231     }
232     auto it = std::find_if(platform_shim.dir_entries.begin(), platform_shim.dir_entries.end(),
233                            [dir_stream](DirEntry const& entry) { return entry.directory == dir_stream; });
234 
235     if (it != platform_shim.dir_entries.end()) {
236         platform_shim.dir_entries.erase(it);
237     }
238 
239     return real_closedir(dir_stream);
240 }
241 
ACCESS_FUNC_NAME(const char * in_pathname,int mode)242 FRAMEWORK_EXPORT int ACCESS_FUNC_NAME(const char* in_pathname, int mode) {
243 #if !defined(__APPLE__)
244     if (!real_access) real_access = (PFN_ACCESS)dlsym(RTLD_NEXT, "access");
245 #endif
246     std::filesystem::path path{in_pathname};
247     if (!path.has_parent_path()) {
248         return real_access(in_pathname, mode);
249     }
250 
251     if (platform_shim.is_fake_path(path.parent_path())) {
252         std::filesystem::path real_path = platform_shim.get_real_path_from_fake_path(path.parent_path());
253         real_path /= path.filename();
254         return real_access(real_path.c_str(), mode);
255     }
256     return real_access(in_pathname, mode);
257 }
258 
FOPEN_FUNC_NAME(const char * in_filename,const char * mode)259 FRAMEWORK_EXPORT FILE* FOPEN_FUNC_NAME(const char* in_filename, const char* mode) {
260 #if !defined(__APPLE__)
261     if (!real_fopen) real_fopen = (PFN_FOPEN)dlsym(RTLD_NEXT, "fopen");
262 #endif
263     std::filesystem::path path{in_filename};
264     if (!path.has_parent_path()) {
265         return real_fopen(in_filename, mode);
266     }
267 
268     FILE* f_ptr;
269     if (platform_shim.is_fake_path(path.parent_path())) {
270         auto real_path = platform_shim.get_real_path_from_fake_path(path.parent_path()) / path.filename();
271         f_ptr = real_fopen(real_path.c_str(), mode);
272     } else {
273         f_ptr = real_fopen(in_filename, mode);
274     }
275 
276     return f_ptr;
277 }
278 
DLOPEN_FUNC_NAME(const char * in_filename,int flags)279 FRAMEWORK_EXPORT void* DLOPEN_FUNC_NAME(const char* in_filename, int flags) {
280 #if !defined(__APPLE__)
281     if (!real_dlopen) real_dlopen = (PFN_DLOPEN)dlsym(RTLD_NEXT, "dlopen");
282 #endif
283 
284     if (platform_shim.is_dlopen_redirect_name(in_filename)) {
285         return real_dlopen(platform_shim.dlopen_redirection_map[in_filename].c_str(), flags);
286     }
287     return real_dlopen(in_filename, flags);
288 }
289 
GETEUID_FUNC_NAME(void)290 FRAMEWORK_EXPORT uid_t GETEUID_FUNC_NAME(void) {
291 #if !defined(__APPLE__)
292     if (!real_geteuid) real_geteuid = (PFN_GETEUID)dlsym(RTLD_NEXT, "geteuid");
293 #endif
294     if (platform_shim.use_fake_elevation) {
295         // Root on linux is 0, so force pretending like we're root
296         return 0;
297     } else {
298         return real_geteuid();
299     }
300 }
301 
GETEGID_FUNC_NAME(void)302 FRAMEWORK_EXPORT gid_t GETEGID_FUNC_NAME(void) {
303 #if !defined(__APPLE__)
304     if (!real_getegid) real_getegid = (PFN_GETEGID)dlsym(RTLD_NEXT, "getegid");
305 #endif
306     if (platform_shim.use_fake_elevation) {
307         // Root on linux is 0, so force pretending like we're root
308         return 0;
309     } else {
310         return real_getegid();
311     }
312 }
313 
314 #if !defined(TARGET_OS_IPHONE)
315 #if defined(HAVE_SECURE_GETENV)
SECURE_GETENV_FUNC_NAME(const char * name)316 FRAMEWORK_EXPORT char* SECURE_GETENV_FUNC_NAME(const char* name) {
317 #if !defined(__APPLE__)
318     if (!real_secure_getenv) real_secure_getenv = (PFN_SEC_GETENV)dlsym(RTLD_NEXT, "secure_getenv");
319 #endif
320     if (platform_shim.use_fake_elevation) {
321         return NULL;
322     } else {
323         return real_secure_getenv(name);
324     }
325 }
326 #endif
327 #if defined(HAVE___SECURE_GETENV)
__SECURE_GETENV_FUNC_NAME(const char * name)328 FRAMEWORK_EXPORT char* __SECURE_GETENV_FUNC_NAME(const char* name) {
329 #if !defined(__APPLE__)
330     if (!real__secure_getenv) real__secure_getenv = (PFN_SEC_GETENV)dlsym(RTLD_NEXT, "__secure_getenv");
331 #endif
332 
333     if (platform_shim.use_fake_elevation) {
334         return NULL;
335     } else {
336         return real__secure_getenv(name);
337     }
338 }
339 #endif
340 #endif
341 
FPUTS_FUNC_NAME(const char * str,FILE * stream)342 FRAMEWORK_EXPORT int FPUTS_FUNC_NAME(const char* str, FILE* stream) {
343 #if !defined(__APPLE__)
344     if (!real_fputs) real_fputs = (PFN_FPUTS)dlsym(RTLD_NEXT, "fputs");
345 #endif
346     if (stream == stderr) {
347         platform_shim.fputs_stderr_log += str;
348     }
349     return real_fputs(str, stream);
350 }
351 
FPUTC_FUNC_NAME(int ch,FILE * stream)352 FRAMEWORK_EXPORT int FPUTC_FUNC_NAME(int ch, FILE* stream) {
353 #if !defined(__APPLE__)
354     if (!real_fputc) real_fputc = (PFN_FPUTC)dlsym(RTLD_NEXT, "fputc");
355 #endif
356     if (stream == stderr) {
357         platform_shim.fputs_stderr_log += ch;
358     }
359     return real_fputc(ch, stream);
360 }
361 
362 #if defined(__APPLE__)
my_CFBundleGetMainBundle()363 FRAMEWORK_EXPORT CFBundleRef my_CFBundleGetMainBundle() {
364     static CFBundleRef global_bundle{};
365     return reinterpret_cast<CFBundleRef>(&global_bundle);
366 }
my_CFBundleCopyResourcesDirectoryURL(CFBundleRef bundle)367 FRAMEWORK_EXPORT CFURLRef my_CFBundleCopyResourcesDirectoryURL([[maybe_unused]] CFBundleRef bundle) {
368     static CFURLRef global_url{};
369     return reinterpret_cast<CFURLRef>(&global_url);
370 }
my_CFURLGetFileSystemRepresentation(CFURLRef url,Boolean resolveAgainstBase,UInt8 * buffer,CFIndex maxBufLen)371 FRAMEWORK_EXPORT Boolean my_CFURLGetFileSystemRepresentation([[maybe_unused]] CFURLRef url,
372                                                              [[maybe_unused]] Boolean resolveAgainstBase, UInt8* buffer,
373                                                              CFIndex maxBufLen) {
374     if (!platform_shim.bundle_contents.empty()) {
375         CFIndex copy_len = (CFIndex)platform_shim.bundle_contents.size();
376         if (copy_len > maxBufLen) {
377             copy_len = maxBufLen;
378         }
379         strncpy(reinterpret_cast<char*>(buffer), platform_shim.bundle_contents.c_str(), copy_len);
380         return TRUE;
381     }
382     return FALSE;
383 }
384 #endif
385 
386 /* Shiming functions on apple is limited by the linker prefering to not use functions in the
387  * executable in loaded dylibs. By adding an interposer, we redirect the linker to use our
388  * version of the function over the real one, thus shimming the system function.
389  */
390 #if defined(__APPLE__)
391 #define MACOS_ATTRIB __attribute__((section("__DATA,__interpose")))
392 #define VOIDP_CAST(_func) reinterpret_cast<const void*>(&_func)
393 
394 struct Interposer {
395     const void* shim_function;
396     const void* underlying_function;
397 };
398 
399 __attribute__((used)) static Interposer _interpose_opendir MACOS_ATTRIB = {VOIDP_CAST(my_opendir), VOIDP_CAST(opendir)};
400 __attribute__((used)) static Interposer _interpose_readdir MACOS_ATTRIB = {VOIDP_CAST(my_readdir), VOIDP_CAST(readdir)};
401 __attribute__((used)) static Interposer _interpose_closedir MACOS_ATTRIB = {VOIDP_CAST(my_closedir), VOIDP_CAST(closedir)};
402 __attribute__((used)) static Interposer _interpose_access MACOS_ATTRIB = {VOIDP_CAST(my_access), VOIDP_CAST(access)};
403 __attribute__((used)) static Interposer _interpose_fopen MACOS_ATTRIB = {VOIDP_CAST(my_fopen), VOIDP_CAST(fopen)};
404 __attribute__((used)) static Interposer _interpose_dlopen MACOS_ATTRIB = {VOIDP_CAST(my_dlopen), VOIDP_CAST(dlopen)};
405 __attribute__((used)) static Interposer _interpose_euid MACOS_ATTRIB = {VOIDP_CAST(my_geteuid), VOIDP_CAST(geteuid)};
406 __attribute__((used)) static Interposer _interpose_egid MACOS_ATTRIB = {VOIDP_CAST(my_getegid), VOIDP_CAST(getegid)};
407 #if !defined(TARGET_OS_IPHONE)
408 #if defined(HAVE_SECURE_GETENV)
409 __attribute__((used)) static Interposer _interpose_secure_getenv MACOS_ATTRIB = {VOIDP_CAST(my_secure_getenv),
410                                                                                  VOIDP_CAST(secure_getenv)};
411 #endif
412 #if defined(HAVE___SECURE_GETENV)
413 __attribute__((used)) static Interposer _interpose__secure_getenv MACOS_ATTRIB = {VOIDP_CAST(my__secure_getenv),
414                                                                                   VOIDP_CAST(__secure_getenv)};
415 #endif
416 #endif
417 __attribute__((used)) static Interposer _interpose_fputs MACOS_ATTRIB = {VOIDP_CAST(my_fputs), VOIDP_CAST(fputs)};
418 __attribute__((used)) static Interposer _interpose_fputc MACOS_ATTRIB = {VOIDP_CAST(my_fputc), VOIDP_CAST(fputc)};
419 __attribute__((used)) static Interposer _interpose_CFBundleGetMainBundle MACOS_ATTRIB = {VOIDP_CAST(my_CFBundleGetMainBundle),
420                                                                                          VOIDP_CAST(CFBundleGetMainBundle)};
421 __attribute__((used)) static Interposer _interpose_CFBundleCopyResourcesDirectoryURL MACOS_ATTRIB = {
422     VOIDP_CAST(my_CFBundleCopyResourcesDirectoryURL), VOIDP_CAST(CFBundleCopyResourcesDirectoryURL)};
423 __attribute__((used)) static Interposer _interpose_CFURLGetFileSystemRepresentation MACOS_ATTRIB = {
424     VOIDP_CAST(my_CFURLGetFileSystemRepresentation), VOIDP_CAST(CFURLGetFileSystemRepresentation)};
425 
426 #endif
427 }  // extern "C"
428