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 36PlatformShim platform_shim; 37extern "C" { 38#if defined(__linux__) || defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__GNU__) 39PlatformShim* get_platform_shim(std::vector<fs::FolderManager>* folders) { 40 platform_shim = PlatformShim(folders); 41 return &platform_shim; 42} 43#elif defined(__APPLE__) 44FRAMEWORK_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__) 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#elif defined(__APPLE__) 67#define OPENDIR_FUNC_NAME my_opendir 68#define READDIR_FUNC_NAME my_readdir 69#define CLOSEDIR_FUNC_NAME my_closedir 70#define ACCESS_FUNC_NAME my_access 71#define FOPEN_FUNC_NAME my_fopen 72#define DLOPEN_FUNC_NAME my_dlopen 73#define GETEUID_FUNC_NAME my_geteuid 74#define GETEGID_FUNC_NAME my_getegid 75#if !defined(TARGET_OS_IPHONE) 76#if defined(HAVE_SECURE_GETENV) 77#define SECURE_GETENV_FUNC_NAME my_secure_getenv 78#endif 79#if defined(HAVE___SECURE_GETENV) 80#define __SECURE_GETENV_FUNC_NAME my__secure_getenv 81#endif 82#endif 83#endif 84 85using PFN_OPENDIR = DIR* (*)(const char* path_name); 86using PFN_READDIR = struct dirent* (*)(DIR* dir_stream); 87using PFN_CLOSEDIR = int (*)(DIR* dir_stream); 88using PFN_ACCESS = int (*)(const char* pathname, int mode); 89using PFN_FOPEN = FILE* (*)(const char* filename, const char* mode); 90using PFN_DLOPEN = void* (*)(const char* in_filename, int flags); 91using PFN_GETEUID = uid_t (*)(void); 92using PFN_GETEGID = gid_t (*)(void); 93#if defined(HAVE_SECURE_GETENV) || defined(HAVE___SECURE_GETENV) 94using PFN_SEC_GETENV = char* (*)(const char* name); 95#endif 96 97#if defined(__APPLE__) 98#define real_opendir opendir 99#define real_readdir readdir 100#define real_closedir closedir 101#define real_access access 102#define real_fopen fopen 103#define real_dlopen dlopen 104#define real_geteuid geteuid 105#define real_getegid getegid 106#if defined(HAVE_SECURE_GETENV) 107#define real_secure_getenv secure_getenv 108#endif 109#if defined(HAVE___SECURE_GETENV) 110#define real__secure_getenv __secure_getenv 111#endif 112#else 113PFN_OPENDIR real_opendir = nullptr; 114PFN_READDIR real_readdir = nullptr; 115PFN_CLOSEDIR real_closedir = nullptr; 116PFN_ACCESS real_access = nullptr; 117PFN_FOPEN real_fopen = nullptr; 118PFN_DLOPEN real_dlopen = nullptr; 119PFN_GETEUID real_geteuid = nullptr; 120PFN_GETEGID real_getegid = nullptr; 121#if defined(HAVE_SECURE_GETENV) 122PFN_SEC_GETENV real_secure_getenv = nullptr; 123#endif 124#if defined(HAVE___SECURE_GETENV) 125PFN_SEC_GETENV real__secure_getenv = nullptr; 126#endif 127#endif 128 129FRAMEWORK_EXPORT DIR* OPENDIR_FUNC_NAME(const char* path_name) { 130#if !defined(__APPLE__) 131 if (!real_opendir) real_opendir = (PFN_OPENDIR)dlsym(RTLD_NEXT, "opendir"); 132#endif 133 if (platform_shim.is_during_destruction) { 134 return real_opendir(path_name); 135 } 136 DIR* dir; 137 if (platform_shim.is_fake_path(path_name)) { 138 auto real_path_name = platform_shim.get_real_path_from_fake_path(fs::path(path_name)); 139 dir = real_opendir(real_path_name.c_str()); 140 platform_shim.dir_entries.push_back(DirEntry{dir, std::string(path_name), {}, 0, true}); 141 } else if (platform_shim.is_known_path(path_name)) { 142 dir = real_opendir(path_name); 143 platform_shim.dir_entries.push_back(DirEntry{dir, std::string(path_name), {}, 0, false}); 144 } else { 145 dir = real_opendir(path_name); 146 } 147 148 return dir; 149} 150 151FRAMEWORK_EXPORT struct dirent* READDIR_FUNC_NAME(DIR* dir_stream) { 152#if !defined(__APPLE__) 153 if (!real_readdir) real_readdir = (PFN_READDIR)dlsym(RTLD_NEXT, "readdir"); 154#endif 155 if (platform_shim.is_during_destruction) { 156 return real_readdir(dir_stream); 157 } 158 auto it = std::find_if(platform_shim.dir_entries.begin(), platform_shim.dir_entries.end(), 159 [dir_stream](DirEntry const& entry) { return entry.directory == dir_stream; }); 160 161 if (it == platform_shim.dir_entries.end()) { 162 return real_readdir(dir_stream); 163 } 164 // Folder was found but this is the first file to be read from it 165 if (it->current_index == 0) { 166 std::vector<struct dirent*> folder_contents; 167 std::vector<std::string> dirent_filenames; 168 while (true) { 169 struct dirent* dir_entry = real_readdir(dir_stream); 170 if (NULL == dir_entry) { 171 break; 172 } 173 folder_contents.push_back(dir_entry); 174 dirent_filenames.push_back(&dir_entry->d_name[0]); 175 } 176 auto real_path = it->folder_path; 177 if (it->is_fake_path) { 178 real_path = platform_shim.redirection_map.at(it->folder_path).str(); 179 } 180 auto filenames = get_folder_contents(platform_shim.folders, real_path); 181 182 // Add the dirent structures in the order they appear in the FolderManager 183 // Ignore anything which wasn't in the FolderManager 184 for (auto const& file : filenames) { 185 for (size_t i = 0; i < dirent_filenames.size(); i++) { 186 if (file == dirent_filenames.at(i)) { 187 it->contents.push_back(folder_contents.at(i)); 188 break; 189 } 190 } 191 } 192 } 193 if (it->current_index >= it->contents.size()) return nullptr; 194 return it->contents.at(it->current_index++); 195} 196 197FRAMEWORK_EXPORT int CLOSEDIR_FUNC_NAME(DIR* dir_stream) { 198#if !defined(__APPLE__) 199 if (!real_closedir) real_closedir = (PFN_CLOSEDIR)dlsym(RTLD_NEXT, "closedir"); 200#endif 201 if (platform_shim.is_during_destruction) { 202 return real_closedir(dir_stream); 203 } 204 auto it = std::find_if(platform_shim.dir_entries.begin(), platform_shim.dir_entries.end(), 205 [dir_stream](DirEntry const& entry) { return entry.directory == dir_stream; }); 206 207 if (it != platform_shim.dir_entries.end()) { 208 platform_shim.dir_entries.erase(it); 209 } 210 211 return real_closedir(dir_stream); 212} 213 214FRAMEWORK_EXPORT int ACCESS_FUNC_NAME(const char* in_pathname, int mode) { 215#if !defined(__APPLE__) 216 if (!real_access) real_access = (PFN_ACCESS)dlsym(RTLD_NEXT, "access"); 217#endif 218 fs::path path{in_pathname}; 219 if (!path.has_parent_path()) { 220 return real_access(in_pathname, mode); 221 } 222 223 if (platform_shim.is_fake_path(path.parent_path())) { 224 fs::path real_path = platform_shim.get_real_path_from_fake_path(path.parent_path()); 225 real_path /= path.filename(); 226 return real_access(real_path.c_str(), mode); 227 } 228 return real_access(in_pathname, mode); 229} 230 231FRAMEWORK_EXPORT FILE* FOPEN_FUNC_NAME(const char* in_filename, const char* mode) { 232#if !defined(__APPLE__) 233 if (!real_fopen) real_fopen = (PFN_FOPEN)dlsym(RTLD_NEXT, "fopen"); 234#endif 235 fs::path path{in_filename}; 236 if (!path.has_parent_path()) { 237 return real_fopen(in_filename, mode); 238 } 239 240 FILE* f_ptr; 241 if (platform_shim.is_fake_path(path.parent_path())) { 242 auto real_path = platform_shim.get_real_path_from_fake_path(path.parent_path()) / path.filename(); 243 f_ptr = real_fopen(real_path.c_str(), mode); 244 } else { 245 f_ptr = real_fopen(in_filename, mode); 246 } 247 248 return f_ptr; 249} 250 251FRAMEWORK_EXPORT void* DLOPEN_FUNC_NAME(const char* in_filename, int flags) { 252#if !defined(__APPLE__) 253 if (!real_dlopen) real_dlopen = (PFN_DLOPEN)dlsym(RTLD_NEXT, "dlopen"); 254#endif 255 256 if (platform_shim.is_dlopen_redirect_name(in_filename)) { 257 return real_dlopen(platform_shim.dlopen_redirection_map[in_filename].c_str(), flags); 258 } 259 return real_dlopen(in_filename, flags); 260} 261 262FRAMEWORK_EXPORT uid_t GETEUID_FUNC_NAME(void) { 263#if !defined(__APPLE__) 264 if (!real_geteuid) real_geteuid = (PFN_GETEUID)dlsym(RTLD_NEXT, "geteuid"); 265#endif 266 if (platform_shim.use_fake_elevation) { 267 // Root on linux is 0, so force pretending like we're root 268 return 0; 269 } else { 270 return real_geteuid(); 271 } 272} 273 274FRAMEWORK_EXPORT gid_t GETEGID_FUNC_NAME(void) { 275#if !defined(__APPLE__) 276 if (!real_getegid) real_getegid = (PFN_GETEGID)dlsym(RTLD_NEXT, "getegid"); 277#endif 278 if (platform_shim.use_fake_elevation) { 279 // Root on linux is 0, so force pretending like we're root 280 return 0; 281 } else { 282 return real_getegid(); 283 } 284} 285 286#if !defined(TARGET_OS_IPHONE) 287#if defined(HAVE_SECURE_GETENV) 288FRAMEWORK_EXPORT char* SECURE_GETENV_FUNC_NAME(const char* name) { 289#if !defined(__APPLE__) 290 if (!real_secure_getenv) real_secure_getenv = (PFN_SEC_GETENV)dlsym(RTLD_NEXT, "secure_getenv"); 291#endif 292 if (platform_shim.use_fake_elevation) { 293 return NULL; 294 } else { 295 return real_secure_getenv(name); 296 } 297} 298#endif 299#if defined(HAVE___SECURE_GETENV) 300FRAMEWORK_EXPORT char* __SECURE_GETENV_FUNC_NAME(const char* name) { 301#if !defined(__APPLE__) 302 if (!real__secure_getenv) real__secure_getenv = (PFN_SEC_GETENV)dlsym(RTLD_NEXT, "__secure_getenv"); 303#endif 304 305 if (platform_shim.use_fake_elevation) { 306 return NULL; 307 } else { 308 return real__secure_getenv(name); 309 } 310} 311#endif 312#endif 313#if defined(__APPLE__) 314FRAMEWORK_EXPORT CFBundleRef my_CFBundleGetMainBundle() { 315 static CFBundleRef global_bundle{}; 316 return reinterpret_cast<CFBundleRef>(&global_bundle); 317} 318FRAMEWORK_EXPORT CFURLRef my_CFBundleCopyResourcesDirectoryURL([[maybe_unused]] CFBundleRef bundle) { 319 static CFURLRef global_url{}; 320 return reinterpret_cast<CFURLRef>(&global_url); 321} 322FRAMEWORK_EXPORT Boolean my_CFURLGetFileSystemRepresentation([[maybe_unused]] CFURLRef url, 323 [[maybe_unused]] Boolean resolveAgainstBase, UInt8* buffer, 324 CFIndex maxBufLen) { 325 if (!platform_shim.bundle_contents.empty()) { 326 CFIndex copy_len = (CFIndex)platform_shim.bundle_contents.size(); 327 if (copy_len > maxBufLen) { 328 copy_len = maxBufLen; 329 } 330 strncpy(reinterpret_cast<char*>(buffer), platform_shim.bundle_contents.c_str(), copy_len); 331 return TRUE; 332 } 333 return FALSE; 334} 335#endif 336 337/* Shiming functions on apple is limited by the linker prefering to not use functions in the 338 * executable in loaded dylibs. By adding an interposer, we redirect the linker to use our 339 * version of the function over the real one, thus shimming the system function. 340 */ 341#if defined(__APPLE__) 342#define MACOS_ATTRIB __attribute__((section("__DATA,__interpose"))) 343#define VOIDP_CAST(_func) reinterpret_cast<const void*>(&_func) 344 345struct Interposer { 346 const void* shim_function; 347 const void* underlying_function; 348}; 349 350__attribute__((used)) static Interposer _interpose_opendir MACOS_ATTRIB = {VOIDP_CAST(my_opendir), VOIDP_CAST(opendir)}; 351// don't intercept readdir as it crashes when using ASAN with macOS 352// __attribute__((used)) static Interposer _interpose_readdir MACOS_ATTRIB = {VOIDP_CAST(my_readdir), VOIDP_CAST(readdir)}; 353__attribute__((used)) static Interposer _interpose_closedir MACOS_ATTRIB = {VOIDP_CAST(my_closedir), VOIDP_CAST(closedir)}; 354__attribute__((used)) static Interposer _interpose_access MACOS_ATTRIB = {VOIDP_CAST(my_access), VOIDP_CAST(access)}; 355__attribute__((used)) static Interposer _interpose_fopen MACOS_ATTRIB = {VOIDP_CAST(my_fopen), VOIDP_CAST(fopen)}; 356__attribute__((used)) static Interposer _interpose_dlopen MACOS_ATTRIB = {VOIDP_CAST(my_dlopen), VOIDP_CAST(dlopen)}; 357__attribute__((used)) static Interposer _interpose_euid MACOS_ATTRIB = {VOIDP_CAST(my_geteuid), VOIDP_CAST(geteuid)}; 358__attribute__((used)) static Interposer _interpose_egid MACOS_ATTRIB = {VOIDP_CAST(my_getegid), VOIDP_CAST(getegid)}; 359#if !defined(TARGET_OS_IPHONE) 360#if defined(HAVE_SECURE_GETENV) 361__attribute__((used)) static Interposer _interpose_secure_getenv MACOS_ATTRIB = {VOIDP_CAST(my_secure_getenv), 362 VOIDP_CAST(secure_getenv)}; 363#endif 364#if defined(HAVE___SECURE_GETENV) 365__attribute__((used)) static Interposer _interpose__secure_getenv MACOS_ATTRIB = {VOIDP_CAST(my__secure_getenv), 366 VOIDP_CAST(__secure_getenv)}; 367#endif 368#endif 369__attribute__((used)) static Interposer _interpose_CFBundleGetMainBundle MACOS_ATTRIB = {VOIDP_CAST(my_CFBundleGetMainBundle), 370 VOIDP_CAST(CFBundleGetMainBundle)}; 371__attribute__((used)) static Interposer _interpose_CFBundleCopyResourcesDirectoryURL MACOS_ATTRIB = { 372 VOIDP_CAST(my_CFBundleCopyResourcesDirectoryURL), VOIDP_CAST(CFBundleCopyResourcesDirectoryURL)}; 373__attribute__((used)) static Interposer _interpose_CFURLGetFileSystemRepresentation MACOS_ATTRIB = { 374 VOIDP_CAST(my_CFURLGetFileSystemRepresentation), VOIDP_CAST(CFURLGetFileSystemRepresentation)}; 375 376#endif 377} // extern "C" 378