blob: 36d6281ef631766d423533c2740f458144dd8cfe [file] [log] [blame]
// 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_TARGET_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_TARGET_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_TARGET_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;
}
} // namespace bin
} // namespace dart