Skip to content

Commit

Permalink
[vm/ffi] Support DynamicLibrary.process() on Windows
Browse files Browse the repository at this point in the history
Support looking up a symbol in the process on Windows by using the
Windows Process Status API to iterate over all loaded Modules.

TEST=tests/ffi/has_symbol_test.dart
TEST=tests/ffi/vmspecific_dynamic_library_test.dart

Change-Id: I1029f1c7dae9a193b662d942388affb681842c90
Cq-Include-Trybots: luci.dart.try:vm-kernel-win-debug-x64c-try,vm-kernel-win-debug-x64-try,vm-kernel-nnbd-win-debug-x64-try,vm-kernel-precomp-win-debug-x64c-try,dart-sdk-win-try,vm-kernel-win-release-x64-try,vm-kernel-win-release-ia32-try,vm-kernel-precomp-win-product-x64-try
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/260760
Commit-Queue: Daco Harkes <dacoharkes@google.com>
Reviewed-by: Tess Strickland <sstrickl@google.com>
Reviewed-by: Martin Kustermann <kustermann@google.com>
  • Loading branch information
dcharkes authored and Commit Queue committed Sep 27, 2022
1 parent d6d33e6 commit c04673f
Show file tree
Hide file tree
Showing 6 changed files with 103 additions and 37 deletions.
3 changes: 3 additions & 0 deletions runtime/bin/BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -787,6 +787,9 @@ template("dart_executable") {

if (is_win) {
libs = [
# ole32.dll contains CoTaskMemAlloc. Here so that package:ffi can look
# CoTaskMemAlloc up with `DynamicLibrary.process()`.
"ole32.lib",
"iphlpapi.lib",
"psapi.lib",
"ws2_32.lib",
Expand Down
77 changes: 72 additions & 5 deletions runtime/lib/ffi_dynamic_library.cc
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,15 @@
// 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 "platform/globals.h"
#if defined(DART_HOST_OS_WINDOWS)
#include <Psapi.h>
#include <Windows.h>
#include <combaseapi.h>
#include <stdio.h>
#include <tchar.h>
#endif

#include "include/dart_api.h"
#include "vm/bootstrap_natives.h"
#include "vm/exceptions.h"
Expand All @@ -15,7 +24,7 @@

namespace dart {

#if defined(USING_SIMULATOR)
#if defined(USING_SIMULATOR) || defined(DART_PRECOMPILER)

DART_NORETURN static void SimulatorUnsupported() {
Exceptions::ThrowUnsupportedError(
Expand All @@ -41,7 +50,7 @@ DEFINE_NATIVE_ENTRY(Ffi_dl_providesSymbol, 0, 2) {
SimulatorUnsupported();
}

#else // defined(USING_SIMULATOR)
#else // defined(USING_SIMULATOR) || defined(DART_PRECOMPILER)

static void* LoadDynamicLibrary(const char* library_file) {
char* error = nullptr;
Expand All @@ -56,9 +65,60 @@ static void* LoadDynamicLibrary(const char* library_file) {
return handle;
}

#if defined(DART_HOST_OS_WINDOWS)
// On windows, nullptr signals trying a lookup in all loaded modules.
const nullptr_t kWindowsDynamicLibraryProcessPtr = nullptr;

void* co_task_mem_alloced = nullptr;

void* LookupSymbolInProcess(const char* symbol, char** error) {
// Force loading ole32.dll.
if (co_task_mem_alloced == nullptr) {
co_task_mem_alloced = CoTaskMemAlloc(sizeof(intptr_t));
CoTaskMemFree(co_task_mem_alloced);
}

HANDLE current_process =
OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE,
GetCurrentProcessId());
if (current_process == nullptr) {
*error = OS::SCreate(nullptr, "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);

*error = OS::SCreate(
nullptr,
"None of the loaded modules contained the requested symbol '%s'.",
symbol);
return nullptr;
}
#endif

static void* ResolveSymbol(void* handle, const char* symbol) {
char* error = nullptr;
void* result = Utils::ResolveSymbolInDynamicLibrary(handle, symbol, &error);
#if !defined(DART_HOST_OS_WINDOWS)
void* const result =
Utils::ResolveSymbolInDynamicLibrary(handle, symbol, &error);
#else
void* const result =
handle == kWindowsDynamicLibraryProcessPtr
? LookupSymbolInProcess(symbol, &error)
: Utils::ResolveSymbolInDynamicLibrary(handle, symbol, &error);
#endif
if (error != nullptr) {
const String& msg = String::Handle(String::NewFormatted(
"Failed to lookup symbol '%s': %s", symbol, error));
Expand All @@ -70,7 +130,15 @@ static void* ResolveSymbol(void* handle, const char* symbol) {

static bool SymbolExists(void* handle, const char* symbol) {
char* error = nullptr;
#if !defined(DART_HOST_OS_WINDOWS)
Utils::ResolveSymbolInDynamicLibrary(handle, symbol, &error);
#else
if (handle == nullptr) {
LookupSymbolInProcess(symbol, &error);
} else {
Utils::ResolveSymbolInDynamicLibrary(handle, symbol, &error);
}
#endif
if (error != nullptr) {
free(error);
return false;
Expand All @@ -91,8 +159,7 @@ DEFINE_NATIVE_ENTRY(Ffi_dl_processLibrary, 0, 0) {
defined(DART_HOST_OS_ANDROID) || defined(DART_HOST_OS_FUCHSIA)
return DynamicLibrary::New(RTLD_DEFAULT);
#else
Exceptions::ThrowUnsupportedError(
"DynamicLibrary.process is not available on this platform.");
return DynamicLibrary::New(kWindowsDynamicLibraryProcessPtr);
#endif
}

Expand Down
14 changes: 7 additions & 7 deletions tests/ffi/has_symbol_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -19,16 +19,16 @@ void testHasSymbol() {
Expect.isTrue(ffiTestFunctions.providesSymbol('ReturnMaxUint8'));
Expect.isFalse(ffiTestFunctions.providesSymbol('SymbolNotInLibrary'));

if (Platform.isMacOS ||
Platform.isIOS ||
Platform.isAndroid ||
Platform.isLinux) {
DynamicLibrary p = DynamicLibrary.process();
final p = DynamicLibrary.process();
Expect.isFalse(p.providesSymbol('symbol_that_does_not_exist_in_process'));
if (Platform.isWindows) {
Expect.isTrue(p.providesSymbol('HeapAlloc'));
Expect.isTrue(p.providesSymbol('CoTaskMemAlloc'));
} else {
Expect.isTrue(p.providesSymbol('dlopen'));
Expect.isFalse(p.providesSymbol('symbol_that_does_not_exist_in_process'));
}

DynamicLibrary e = DynamicLibrary.executable();
final e = DynamicLibrary.executable();
Expect.isTrue(e.providesSymbol('Dart_Invoke'));
Expect.isFalse(e.providesSymbol('symbol_that_does_not_exist_in_executable'));
}
16 changes: 7 additions & 9 deletions tests/ffi/vmspecific_dynamic_library_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -37,22 +37,20 @@ typedef NativeDoubleUnOp = Double Function(Double);
typedef DoubleUnOp = double Function(double);

void testLookup() {
DynamicLibrary l = dlopenPlatformSpecific("ffi_test_dynamic_library");
final l = dlopenPlatformSpecific("ffi_test_dynamic_library");
var timesFour = l.lookupFunction<NativeDoubleUnOp, DoubleUnOp>("timesFour");
Expect.approxEquals(12.0, timesFour(3));

if (Platform.isMacOS ||
Platform.isIOS ||
Platform.isAndroid ||
Platform.isLinux) {
final p = DynamicLibrary.process();
if (Platform.isWindows) {
Expect.isTrue(p.lookup<Void>("HeapAlloc") != nullptr);
Expect.isTrue(p.lookup<Void>("CoTaskMemAlloc") != nullptr);
} else {
// Lookup a symbol from 'libc' since it's loaded with global visibility.
DynamicLibrary p = DynamicLibrary.process();
Expect.isTrue(p.lookup<Void>("strcmp") != nullptr);
} else {
Expect.throws<UnsupportedError>(() => DynamicLibrary.process());
}

DynamicLibrary e = DynamicLibrary.executable();
final e = DynamicLibrary.executable();
Expect.isTrue(e.lookup("Dart_Invoke") != nullptr);
}

Expand Down
14 changes: 7 additions & 7 deletions tests/ffi_2/has_symbol_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -21,16 +21,16 @@ void testHasSymbol() {
Expect.isTrue(ffiTestFunctions.providesSymbol('ReturnMaxUint8'));
Expect.isFalse(ffiTestFunctions.providesSymbol('SymbolNotInLibrary'));

if (Platform.isMacOS ||
Platform.isIOS ||
Platform.isAndroid ||
Platform.isLinux) {
DynamicLibrary p = DynamicLibrary.process();
final p = DynamicLibrary.process();
Expect.isFalse(p.providesSymbol('symbol_that_does_not_exist_in_process'));
if (Platform.isWindows) {
Expect.isTrue(p.providesSymbol('HeapAlloc'));
Expect.isTrue(p.providesSymbol('CoTaskMemAlloc'));
} else {
Expect.isTrue(p.providesSymbol('dlopen'));
Expect.isFalse(p.providesSymbol('symbol_that_does_not_exist_in_process'));
}

DynamicLibrary e = DynamicLibrary.executable();
final e = DynamicLibrary.executable();
Expect.isTrue(e.providesSymbol('Dart_Invoke'));
Expect.isFalse(e.providesSymbol('symbol_that_does_not_exist_in_executable'));
}
16 changes: 7 additions & 9 deletions tests/ffi_2/vmspecific_dynamic_library_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -39,22 +39,20 @@ typedef NativeDoubleUnOp = Double Function(Double);
typedef DoubleUnOp = double Function(double);

void testLookup() {
DynamicLibrary l = dlopenPlatformSpecific("ffi_test_dynamic_library");
final l = dlopenPlatformSpecific("ffi_test_dynamic_library");
var timesFour = l.lookupFunction<NativeDoubleUnOp, DoubleUnOp>("timesFour");
Expect.approxEquals(12.0, timesFour(3));

if (Platform.isMacOS ||
Platform.isIOS ||
Platform.isAndroid ||
Platform.isLinux) {
final p = DynamicLibrary.process();
if (Platform.isWindows) {
Expect.isTrue(p.lookup<Void>("HeapAlloc") != nullptr);
Expect.isTrue(p.lookup<Void>("CoTaskMemAlloc") != nullptr);
} else {
// Lookup a symbol from 'libc' since it's loaded with global visibility.
DynamicLibrary p = DynamicLibrary.process();
Expect.isTrue(p.lookup<Void>("strcmp") != nullptr);
} else {
Expect.throws<UnsupportedError>(() => DynamicLibrary.process());
}

DynamicLibrary e = DynamicLibrary.executable();
final e = DynamicLibrary.executable();
Expect.isTrue(e.lookup("Dart_Invoke") != nullptr);
}

Expand Down

0 comments on commit c04673f

Please sign in to comment.