| // Copyright (c) 2024, the Dart project authors. Please see the AUTHORS file | |
| // for details. All rights reserved. Use of this source code is governed by a | |
| // BSD-style license that can be found in the LICENSE file. | |
| #include "include/bin/native_assets_api.h" | |
| #include <cstring> | |
| #include "platform/globals.h" | |
| #include "platform/utils.h" | |
| #if defined(DART_HOST_OS_WINDOWS) | |
| #include <Psapi.h> | |
| #include <Windows.h> | |
| #include <combaseapi.h> | |
| #include <stdio.h> | |
| #include <tchar.h> | |
| #endif | |
| #if defined(DART_HOST_OS_LINUX) || defined(DART_HOST_OS_MACOS) || \ | |
| defined(DART_HOST_OS_ANDROID) || defined(DART_HOST_OS_FUCHSIA) | |
| #include <dlfcn.h> | |
| #endif | |
| #include "bin/uri.h" | |
| namespace dart { | |
| namespace bin { | |
| #define SET_ERROR_MSG(error_msg, format, ...) \ | |
| intptr_t len = snprintf(nullptr, 0, format, ##__VA_ARGS__); \ | |
| char* msg = reinterpret_cast<char*>(malloc(len + 1)); \ | |
| snprintf(msg, len + 1, format, ##__VA_ARGS__); \ | |
| *error_msg = msg | |
| #if defined(DART_HOST_OS_WINDOWS) | |
| // Replaces back slashes with forward slashes in place. | |
| static void ReplaceBackSlashes(char* cstr) { | |
| const intptr_t length = strlen(cstr); | |
| for (int i = 0; i < length; i++) { | |
| cstr[i] = cstr[i] == '\\' ? '/' : cstr[i]; | |
| } | |
| } | |
| // Replaces forward slashes with back slashes in place. | |
| static void ReplaceForwardSlashes(char* cstr) { | |
| const intptr_t length = strlen(cstr); | |
| for (int i = 0; i < length; i++) { | |
| cstr[i] = cstr[i] == '/' ? '\\' : cstr[i]; | |
| } | |
| } | |
| #endif | |
| // If an error occurs populates |error| (if provided) with an error message | |
| // (caller must free this message when it is no longer needed). | |
| static void* LoadDynamicLibrary(const char* library_file, | |
| bool search_dll_load_dir = false, | |
| char** error = nullptr) { | |
| char* utils_error = nullptr; | |
| void* handle = Utils::LoadDynamicLibrary(library_file, search_dll_load_dir, | |
| &utils_error); | |
| if (utils_error != nullptr) { | |
| if (error != nullptr) { | |
| SET_ERROR_MSG(error, "Failed to load dynamic library '%s': %s", | |
| library_file != nullptr ? library_file : "<process>", | |
| utils_error); | |
| } | |
| free(utils_error); | |
| } | |
| return handle; | |
| } | |
| #if defined(DART_HOST_OS_WINDOWS) | |
| // On windows, nullptr signals trying a lookup in all loaded modules. | |
| const nullptr_t kWindowsDynamicLibraryProcessPtr = nullptr; | |
| #endif | |
| static void WrapError(const char* path, char** error) { | |
| if (*error != nullptr) { | |
| char* inner_error = *error; | |
| SET_ERROR_MSG(error, "Failed to load dynamic library '%s': %s", path, | |
| inner_error); | |
| free(inner_error); | |
| } | |
| } | |
| static void WrapErrorRelative(const char* path, | |
| const char* base_path, | |
| char** error) { | |
| if (*error != nullptr) { | |
| char* inner_error = *error; | |
| SET_ERROR_MSG(error, | |
| "Failed to load dynamic library '%s' relative to '%s': %s", | |
| path, base_path, inner_error); | |
| free(inner_error); | |
| } | |
| } | |
| void* NativeAssets::DlopenAbsolute(const char* path, char** error) { | |
| // If we'd want to be strict, it should not take into account include paths. | |
| void* handle = | |
| LoadDynamicLibrary(path, /* search_dll_load_dir= */ true, error); | |
| WrapError(path, error); | |
| return handle; | |
| } | |
| void* NativeAssets::DlopenRelative(const char* path, | |
| const char* base_path, | |
| char** error) { | |
| if (base_path == nullptr) { | |
| SET_ERROR_MSG( | |
| error, "Failed to resolve relative path '%s', no base path provided.", | |
| path); | |
| return nullptr; | |
| } | |
| void* handle = nullptr; | |
| #if defined(DART_HOST_OS_WINDOWS) | |
| char* path_copy = strdup(path); | |
| char* base_path_copy = strdup(base_path); | |
| ReplaceBackSlashes(path_copy); | |
| ReplaceBackSlashes(base_path_copy); | |
| auto target_path = ResolvePath(path_copy, base_path_copy); | |
| free(path_copy); | |
| free(base_path_copy); | |
| #else | |
| auto target_path = ResolvePath(path, base_path); | |
| #endif | |
| if (!target_path) { | |
| SET_ERROR_MSG(error, "Failed to resolve '%s' relative to '%s'.", path, | |
| base_path); | |
| } else { | |
| #if defined(DART_HOST_OS_WINDOWS) | |
| ReplaceForwardSlashes(target_path.get()); | |
| #endif | |
| handle = LoadDynamicLibrary(target_path.get(), | |
| /* search_dll_load_dir= */ true, error); | |
| } | |
| WrapErrorRelative(path, base_path, error); | |
| return handle; | |
| } | |
| void* NativeAssets::DlopenSystem(const char* path, char** error) { | |
| // Should take into account LD_PATH etc. | |
| void* handle = | |
| LoadDynamicLibrary(path, /* search_dll_load_dir= */ false, error); | |
| WrapError(path, error); | |
| return handle; | |
| } | |
| void* NativeAssets::DlopenProcess(char** error) { | |
| #if defined(DART_HOST_OS_LINUX) || defined(DART_HOST_OS_MACOS) || \ | |
| defined(DART_HOST_OS_ANDROID) || defined(DART_HOST_OS_FUCHSIA) | |
| return RTLD_DEFAULT; | |
| #else | |
| return kWindowsDynamicLibraryProcessPtr; | |
| #endif | |
| } | |
| void* NativeAssets::DlopenExecutable(char** error) { | |
| return LoadDynamicLibrary(nullptr, /* search_dll_load_dir= */ false, error); | |
| } | |
| #if defined(DART_HOST_OS_WINDOWS) | |
| void* co_task_mem_allocated = nullptr; | |
| // If an error occurs populates |error| with an error message | |
| // (caller must free this message when it is no longer needed). | |
| void* LookupSymbolInProcess(const char* symbol, char** error) { | |
| // Force loading ole32.dll. | |
| if (co_task_mem_allocated == nullptr) { | |
| co_task_mem_allocated = CoTaskMemAlloc(sizeof(intptr_t)); | |
| CoTaskMemFree(co_task_mem_allocated); | |
| } | |
| HANDLE current_process = | |
| OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, | |
| GetCurrentProcessId()); | |
| if (current_process == nullptr) { | |
| SET_ERROR_MSG(error, "Failed to open current process."); | |
| return nullptr; | |
| } | |
| HMODULE modules[1024]; | |
| DWORD cb_needed; | |
| if (EnumProcessModules(current_process, modules, sizeof(modules), | |
| &cb_needed) != 0) { | |
| for (intptr_t i = 0; i < (cb_needed / sizeof(HMODULE)); i++) { | |
| if (auto result = | |
| reinterpret_cast<void*>(GetProcAddress(modules[i], symbol))) { | |
| CloseHandle(current_process); | |
| return result; | |
| } | |
| } | |
| } | |
| CloseHandle(current_process); | |
| SET_ERROR_MSG( | |
| error, "None of the loaded modules contained the requested symbol '%s'.", | |
| symbol); | |
| return nullptr; | |
| } | |
| #endif | |
| // If an error occurs populates |error| with an error message | |
| // (caller must free this message when it is no longer needed). | |
| static void* ResolveSymbol(void* handle, const char* symbol, char** error) { | |
| #if defined(DART_HOST_OS_WINDOWS) | |
| if (handle == kWindowsDynamicLibraryProcessPtr) { | |
| return LookupSymbolInProcess(symbol, error); | |
| } | |
| #endif | |
| return Utils::ResolveSymbolInDynamicLibrary(handle, symbol, error); | |
| } | |
| void* NativeAssets::Dlsym(void* handle, const char* symbol, char** error) { | |
| void* const result = ResolveSymbol(handle, symbol, error); | |
| if (*error != nullptr) { | |
| char* inner_error = *error; | |
| SET_ERROR_MSG(error, "Failed to lookup symbol '%s': %s", symbol, | |
| inner_error); | |
| free(inner_error); | |
| } | |
| return result; | |
| } | |
| void NativeAssets::Dlclose(void* handle, char** error) { | |
| Utils::UnloadDynamicLibrary(handle, error); | |
| } | |
| } // namespace bin | |
| } // namespace dart |