93ca44b048
Adds a new API `DynamicLibrary.openFromAssetId` to `dart:ffi` that allows opening dynamically loaded libraries using their native asset IDs instead of physical file paths. Closes https://github.com/dart-lang/sdk/issues/63295 TEST=./tools/test.py -n vm-aot-linux-debug-x64 'ffi/native_assets/asset_*' Cq-Include-Trybots: dart/try:vm-aot-linux-debug-arm64-try,vm-aot-linux-debug-x64-try,vm-aot-linux-debug-x64c-try,vm-aot-mac-debug-arm64-try,vm-aot-mac-debug-x64-try,vm-aot-obfuscate-linux-release-x64-try,vm-aot-optimization-level-linux-release-x64-try,vm-aot-win-debug-arm64-try,vm-aot-win-debug-x64-try,vm-aot-win-debug-x64c-try,vm-asan-linux-release-arm64-try,vm-asan-linux-release-x64-try,vm-asan-mac-release-arm64-try,vm-asan-win-release-x64-try,vm-dyn-linux-debug-x64-try,vm-dyn-mac-debug-arm64-try,vm-ffi-dyn-mac-debug-simarm64_arm64-try,vm-msan-linux-release-arm64-try,vm-msan-linux-release-x64-try,vm-tsan-linux-release-arm64-try,vm-tsan-linux-release-x64-try,vm-tsan-mac-release-arm64-try,vm-ubsan-linux-release-arm64-try,vm-ubsan-linux-release-x64-try,vm-ubsan-mac-release-arm64-try,vm-ubsan-win-release-x64-try R=vegorov@google.com Change-Id: Ic617cb0906d1688d2d080dae7d1e08ee58b4c8d6 CoreLibraryReviewExempt: VM-only Change-Id: Ic617cb0906d1688d2d080dae7d1e08ee58b4c8d6 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/507180 Auto-Submit: Shikhar Soni <shikharsoni@google.com> Reviewed-by: Daco Harkes <dacoharkes@google.com> Commit-Queue: Daco Harkes <dacoharkes@google.com>
228 lines
7.1 KiB
C++
228 lines
7.1 KiB
C++
// 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;
|
|
}
|
|
|
|
} // namespace bin
|
|
} // namespace dart
|