Skip to content
This repository was archived by the owner on Feb 25, 2025. It is now read-only.

Commit f524ec7

Browse files
dcharkesCommit Queue
authored andcommitted
[vm/ffi] FfiNative process lookup
This CL makes `FfiNative`s use `DynamicLibrary.process()` lookup if resolving with the resolver set by `Dart_SetFfiNativeResolver` fails. Also moves the implementation over from ffi.cc to ffi_dynamic_library.cc so the implementation can be shared with `DynamicLibrary.process()`. Moves the implementation behind non-simulator and non-precompiler. However, the implementation is tested in vm/cc tests which are in precompiler mode. So enables the implementation if TESTED is defined. This CL massages the build files so that TESTED is properly defined when compiling the runtime for the vm/cc tests, and links the ole32 symbols on windows for vm/cc tests. (And some unrelated small cleanup changes here and there.) TEST=tests/ffi/native_assets/process_test.dart Change-Id: I25395d381db1d9b4b7a5759171a798a1140a6140 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,vm-kernel-reload-linux-debug-x64-try,vm-kernel-reload-rollback-linux-debug-x64-try Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/264982 Reviewed-by: Martin Kustermann <[email protected]> Commit-Queue: Daco Harkes <[email protected]>
1 parent 89d7697 commit f524ec7

File tree

10 files changed

+253
-85
lines changed

10 files changed

+253
-85
lines changed

runtime/BUILD.gn

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -256,6 +256,10 @@ config("dart_config") {
256256
}
257257
}
258258

259+
config("dart_testing_config") {
260+
defines = [ "TESTING" ]
261+
}
262+
259263
config("dart_shared_lib") {
260264
if (dart_lib_export_symbols) {
261265
defines = [ "DART_SHARED_LIB" ]

runtime/bin/BUILD.gn

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -956,7 +956,7 @@ executable("run_vm_tests") {
956956
":dart_kernel_platform_cc",
957957
":dart_snapshot_cc",
958958
":standalone_dart_io",
959-
"..:libdart_precompiler",
959+
"..:libdart_precompiler_testing",
960960
"//third_party/boringssl", # for secure_socket_utils_test
961961
"//third_party/zlib",
962962
]
@@ -1015,6 +1015,9 @@ executable("run_vm_tests") {
10151015

10161016
if (is_win) {
10171017
libs = [
1018+
# ole32.dll contains CoTaskMemAlloc. Here so that package:ffi can look
1019+
# CoTaskMemAlloc up with `DynamicLibrary.process()`.
1020+
"ole32.lib",
10181021
"iphlpapi.lib",
10191022
"psapi.lib",
10201023
"ws2_32.lib",

runtime/bin/dartutils.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -152,6 +152,10 @@ class DartUtils {
152152
static bool IsDartBuiltinLibURL(const char* url_name);
153153
static bool IsHttpSchemeURL(const char* url_name);
154154
static const char* RemoveScheme(const char* url);
155+
156+
// Retuns directory name including the last path separtor.
157+
//
158+
// The return value must be `free`d by the caller.
155159
static char* DirName(const char* url);
156160
static void* MapExecutable(const char* name, intptr_t* file_len);
157161
static void* OpenFile(const char* name, bool write);

runtime/configs.gni

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,9 @@ _precompiler_base = [ "$_dart_runtime:dart_precompiler_config" ]
4646

4747
_precompiler_config = _base_config + _precompiler_base + _maybe_product
4848

49+
_precompiler_testing_config =
50+
_precompiler_config + [ "$_dart_runtime:dart_testing_config" ]
51+
4952
_precompiler_product_config = _base_config + _precompiler_base + _product
5053

5154
_precompiler_fuchsia_config =
@@ -96,6 +99,13 @@ _all_configs = [
9699
compiler = true
97100
is_product = false
98101
},
102+
{
103+
suffix = "_precompiler_testing"
104+
configs = _precompiler_testing_config
105+
snapshot = false
106+
compiler = true
107+
is_product = false
108+
},
99109
{
100110
suffix = "_precompiler_product"
101111
configs = _precompiler_product_config

runtime/lib/ffi.cc

Lines changed: 0 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,6 @@
1212
#include "vm/class_finalizer.h"
1313
#include "vm/class_id.h"
1414
#include "vm/compiler/ffi/native_type.h"
15-
#include "vm/dart_api_impl.h"
1615
#include "vm/exceptions.h"
1716
#include "vm/flags.h"
1817
#include "vm/heap/gc_shared.h"
@@ -206,45 +205,6 @@ DEFINE_NATIVE_ENTRY(DartApiDLInitializeData, 0, 0) {
206205
return Integer::New(reinterpret_cast<intptr_t>(&dart_api_data));
207206
}
208207

209-
// FFI native C function pointer resolver.
210-
static intptr_t FfiResolve(Dart_Handle lib_url,
211-
Dart_Handle name,
212-
uintptr_t args_n) {
213-
DARTSCOPE(Thread::Current());
214-
215-
const String& lib_url_str = Api::UnwrapStringHandle(T->zone(), lib_url);
216-
const String& function_name = Api::UnwrapStringHandle(T->zone(), name);
217-
218-
// Find the corresponding library's native function resolver (if set).
219-
const Library& lib = Library::Handle(Library::LookupLibrary(T, lib_url_str));
220-
if (lib.IsNull()) {
221-
const String& error = String::Handle(String::NewFormatted(
222-
"Unknown library: '%s'.", lib_url_str.ToCString()));
223-
Exceptions::ThrowArgumentError(error);
224-
}
225-
auto resolver = lib.ffi_native_resolver();
226-
if (resolver == nullptr) {
227-
const String& error = String::Handle(String::NewFormatted(
228-
"Library has no handler: '%s'.", lib_url_str.ToCString()));
229-
Exceptions::ThrowArgumentError(error);
230-
}
231-
232-
auto* f = resolver(function_name.ToCString(), args_n);
233-
if (f == nullptr) {
234-
const String& error = String::Handle(String::NewFormatted(
235-
"Couldn't resolve function: '%s'.", function_name.ToCString()));
236-
Exceptions::ThrowArgumentError(error);
237-
}
238-
239-
return reinterpret_cast<intptr_t>(f);
240-
}
241-
242-
// Bootstrap to get the FFI Native resolver through a `native` call.
243-
DEFINE_NATIVE_ENTRY(Ffi_GetFfiNativeResolver, 1, 0) {
244-
GET_NATIVE_TYPE_ARGUMENT(type_arg, arguments->NativeTypeArgAt(0));
245-
return Pointer::New(type_arg, reinterpret_cast<intptr_t>(FfiResolve));
246-
}
247-
248208
DEFINE_FFI_NATIVE_ENTRY(FinalizerEntry_SetExternalSize,
249209
void,
250210
(Dart_Handle entry_handle, intptr_t external_size)) {

runtime/lib/ffi_dynamic_library.cc

Lines changed: 134 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -11,11 +11,12 @@
1111
#include <tchar.h>
1212
#endif
1313

14-
#include "include/dart_api.h"
1514
#include "vm/bootstrap_natives.h"
15+
#include "vm/dart_api_impl.h"
1616
#include "vm/exceptions.h"
1717
#include "vm/globals.h"
1818
#include "vm/native_entry.h"
19+
#include "vm/object_store.h"
1920

2021
#if defined(DART_HOST_OS_LINUX) || defined(DART_HOST_OS_MACOS) || \
2122
defined(DART_HOST_OS_ANDROID) || defined(DART_HOST_OS_FUCHSIA)
@@ -24,11 +25,15 @@
2425

2526
namespace dart {
2627

27-
#if defined(USING_SIMULATOR) || defined(DART_PRECOMPILER)
28+
#if defined(USING_SIMULATOR) || (defined(DART_PRECOMPILER) && !defined(TESTING))
2829

2930
DART_NORETURN static void SimulatorUnsupported() {
31+
#if defined(USING_SIMULATOR)
3032
Exceptions::ThrowUnsupportedError(
3133
"Not supported on simulated architectures.");
34+
#else
35+
Exceptions::ThrowUnsupportedError("Not supported in precompiler.");
36+
#endif
3237
}
3338

3439
DEFINE_NATIVE_ENTRY(Ffi_dl_open, 0, 1) {
@@ -50,17 +55,26 @@ DEFINE_NATIVE_ENTRY(Ffi_dl_providesSymbol, 0, 2) {
5055
SimulatorUnsupported();
5156
}
5257

53-
#else // defined(USING_SIMULATOR) || defined(DART_PRECOMPILER)
58+
DEFINE_NATIVE_ENTRY(Ffi_GetFfiNativeResolver, 1, 0) {
59+
SimulatorUnsupported();
60+
}
5461

55-
static void* LoadDynamicLibrary(const char* library_file) {
56-
char* error = nullptr;
57-
void* handle = Utils::LoadDynamicLibrary(library_file, &error);
58-
if (error != nullptr) {
59-
const String& msg = String::Handle(String::NewFormatted(
60-
"Failed to load dynamic library '%s': %s",
61-
library_file != nullptr ? library_file : "<process>", error));
62-
free(error);
63-
Exceptions::ThrowArgumentError(msg);
62+
#else // defined(USING_SIMULATOR) || \
63+
// (defined(DART_PRECOMPILER) && !defined(TESTING))
64+
65+
// If an error occurs populates |error| (if provided) with an error message
66+
// (caller must free this message when it is no longer needed).
67+
static void* LoadDynamicLibrary(const char* library_file,
68+
char** error = nullptr) {
69+
char* utils_error = nullptr;
70+
void* handle = Utils::LoadDynamicLibrary(library_file, &utils_error);
71+
if (utils_error != nullptr) {
72+
if (error != nullptr) {
73+
*error = OS::SCreate(
74+
/*use malloc*/ nullptr, "Failed to load dynamic library '%s': %s",
75+
library_file != nullptr ? library_file : "<process>", utils_error);
76+
}
77+
free(utils_error);
6478
}
6579
return handle;
6680
}
@@ -71,6 +85,8 @@ const nullptr_t kWindowsDynamicLibraryProcessPtr = nullptr;
7185

7286
void* co_task_mem_alloced = nullptr;
7387

88+
// If an error occurs populates |error| with an error message
89+
// (caller must free this message when it is no longer needed).
7490
void* LookupSymbolInProcess(const char* symbol, char** error) {
7591
// Force loading ole32.dll.
7692
if (co_task_mem_alloced == nullptr) {
@@ -101,31 +117,22 @@ void* LookupSymbolInProcess(const char* symbol, char** error) {
101117
CloseHandle(current_process);
102118

103119
*error = OS::SCreate(
104-
nullptr,
120+
nullptr, // Use `malloc`.
105121
"None of the loaded modules contained the requested symbol '%s'.",
106122
symbol);
107123
return nullptr;
108124
}
109125
#endif
110126

111-
static void* ResolveSymbol(void* handle, const char* symbol) {
112-
char* error = nullptr;
113-
#if !defined(DART_HOST_OS_WINDOWS)
114-
void* const result =
115-
Utils::ResolveSymbolInDynamicLibrary(handle, symbol, &error);
116-
#else
117-
void* const result =
118-
handle == kWindowsDynamicLibraryProcessPtr
119-
? LookupSymbolInProcess(symbol, &error)
120-
: Utils::ResolveSymbolInDynamicLibrary(handle, symbol, &error);
121-
#endif
122-
if (error != nullptr) {
123-
const String& msg = String::Handle(String::NewFormatted(
124-
"Failed to lookup symbol '%s': %s", symbol, error));
125-
free(error);
126-
Exceptions::ThrowArgumentError(msg);
127+
// If an error occurs populates |error| with an error message
128+
// (caller must free this message when it is no longer needed).
129+
static void* ResolveSymbol(void* handle, const char* symbol, char** error) {
130+
#if defined(DART_HOST_OS_WINDOWS)
131+
if (handle == kWindowsDynamicLibraryProcessPtr) {
132+
return LookupSymbolInProcess(symbol, error);
127133
}
128-
return result;
134+
#endif
135+
return Utils::ResolveSymbolInDynamicLibrary(handle, symbol, error);
129136
}
130137

131138
static bool SymbolExists(void* handle, const char* symbol) {
@@ -149,8 +156,13 @@ static bool SymbolExists(void* handle, const char* symbol) {
149156
DEFINE_NATIVE_ENTRY(Ffi_dl_open, 0, 1) {
150157
GET_NON_NULL_NATIVE_ARGUMENT(String, lib_path, arguments->NativeArgAt(0));
151158

152-
void* handle = LoadDynamicLibrary(lib_path.ToCString());
153-
159+
char* error = nullptr;
160+
void* handle = LoadDynamicLibrary(lib_path.ToCString(), &error);
161+
if (error != nullptr) {
162+
const String& msg = String::Handle(String::New(error));
163+
free(error);
164+
Exceptions::ThrowArgumentError(msg);
165+
}
154166
return DynamicLibrary::New(handle);
155167
}
156168

@@ -176,8 +188,15 @@ DEFINE_NATIVE_ENTRY(Ffi_dl_lookup, 1, 2) {
176188

177189
void* handle = dlib.GetHandle();
178190

179-
const uword pointer =
180-
reinterpret_cast<uword>(ResolveSymbol(handle, argSymbolName.ToCString()));
191+
char* error = nullptr;
192+
const uword pointer = reinterpret_cast<uword>(
193+
ResolveSymbol(handle, argSymbolName.ToCString(), &error));
194+
if (error != nullptr) {
195+
const String& msg = String::Handle(String::NewFormatted(
196+
"Failed to lookup symbol '%s': %s", argSymbolName.ToCString(), error));
197+
free(error);
198+
Exceptions::ThrowArgumentError(msg);
199+
}
181200
return Pointer::New(type_arg, pointer);
182201
}
183202

@@ -197,6 +216,86 @@ DEFINE_NATIVE_ENTRY(Ffi_dl_providesSymbol, 0, 2) {
197216
return Bool::Get(SymbolExists(handle, argSymbolName.ToCString())).ptr();
198217
}
199218

200-
#endif // defined(USING_SIMULATOR)
219+
// nullptr if no native resolver is installed.
220+
static Dart_FfiNativeResolver GetFfiNativeResolver(Thread* const thread,
221+
const String& lib_url_str) {
222+
const Library& lib =
223+
Library::Handle(Library::LookupLibrary(thread, lib_url_str));
224+
if (lib.IsNull()) {
225+
// It is not an error to not have a native resolver installed.
226+
return nullptr;
227+
}
228+
return lib.ffi_native_resolver();
229+
}
230+
231+
// If an error occurs populates |error| with an error message
232+
// (caller must free this message when it is no longer needed).
233+
static void* FfiResolveWithFfiNativeResolver(Thread* const thread,
234+
Dart_FfiNativeResolver resolver,
235+
const String& symbol,
236+
intptr_t args_n,
237+
char** error) {
238+
auto* result = resolver(symbol.ToCString(), args_n);
239+
if (result == nullptr) {
240+
*error = OS::SCreate(/*use malloc*/ nullptr,
241+
"Couldn't resolve function: '%s'", symbol.ToCString());
242+
}
243+
return result;
244+
}
245+
246+
// Frees |error|.
247+
static void ThrowFfiResolveError(const String& symbol,
248+
const String& asset,
249+
char* error) {
250+
const String& error_message = String::Handle(String::NewFormatted(
251+
"Couldn't resolve native function '%s' in '%s' : %s.\n",
252+
symbol.ToCString(), asset.ToCString(), error));
253+
free(error);
254+
Exceptions::ThrowArgumentError(error_message);
255+
}
256+
257+
// FFI native C function pointer resolver.
258+
static intptr_t FfiResolve(Dart_Handle asset_handle,
259+
Dart_Handle symbol_handle,
260+
uintptr_t args_n) {
261+
auto* const thread = Thread::Current();
262+
DARTSCOPE(thread);
263+
auto* const zone = thread->zone();
264+
const String& asset = Api::UnwrapStringHandle(zone, asset_handle);
265+
const String& symbol = Api::UnwrapStringHandle(zone, symbol_handle);
266+
char* error = nullptr;
267+
268+
// Resolver resolution.
269+
auto resolver = GetFfiNativeResolver(thread, asset);
270+
if (resolver != nullptr) {
271+
void* ffi_native_result = FfiResolveWithFfiNativeResolver(
272+
thread, resolver, symbol, args_n, &error);
273+
if (error != nullptr) {
274+
ThrowFfiResolveError(symbol, asset, error);
275+
}
276+
return reinterpret_cast<intptr_t>(ffi_native_result);
277+
}
278+
279+
// Resolution in current process.
280+
#if !defined(DART_HOST_OS_WINDOWS)
281+
void* const result = Utils::ResolveSymbolInDynamicLibrary(
282+
RTLD_DEFAULT, symbol.ToCString(), &error);
283+
#else
284+
void* const result = LookupSymbolInProcess(symbol.ToCString(), &error);
285+
#endif
286+
if (error != nullptr) {
287+
ThrowFfiResolveError(symbol, asset, error);
288+
}
289+
return reinterpret_cast<intptr_t>(result);
290+
}
291+
292+
// Bootstrap to get the FFI Native resolver through a `native` call.
293+
DEFINE_NATIVE_ENTRY(Ffi_GetFfiNativeResolver, 1, 0) {
294+
GET_NATIVE_TYPE_ARGUMENT(type_arg, arguments->NativeTypeArgAt(0));
295+
return Pointer::New(type_arg, reinterpret_cast<intptr_t>(FfiResolve));
296+
}
297+
298+
#endif // defined(USING_SIMULATOR) || \
299+
// (defined(DART_PRECOMPILER) && !defined(TESTING))
201300

202301
} // namespace dart

runtime/vm/dart_api_impl.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -176,7 +176,7 @@ class Api : AllStatic {
176176

177177
// Casts the internal IsolateGroup* type to the external Dart_IsolateGroup
178178
// type.
179-
static Dart_IsolateGroup CastIsolateGroup(IsolateGroup* isolate);
179+
static Dart_IsolateGroup CastIsolateGroup(IsolateGroup* isolate_group);
180180

181181
// Gets the handle used to designate successful return.
182182
static Dart_Handle Success() { return Api::True(); }

runtime/vm/dart_api_impl_test.cc

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -10392,9 +10392,10 @@ TEST_CASE(Dart_SetFfiNativeResolver_MissingResolver) {
1039210392
EXPECT_VALID(lib);
1039310393

1039410394
Dart_Handle result = Dart_Invoke(lib, NewString("main"), 0, nullptr);
10395-
EXPECT_ERROR(
10396-
result,
10397-
"Invalid argument(s): Library has no handler: 'file:///test-lib'.");
10395+
10396+
// With no resolver, we resolve in process. Expect to not find the symbol
10397+
// in processes. Error message depends on architecture.
10398+
EXPECT_ERROR(result, "Invalid argument(s):");
1039810399
}
1039910400

1040010401
static void* NopResolver(const char* name, uintptr_t args_n) {
@@ -10415,9 +10416,7 @@ TEST_CASE(Dart_SetFfiNativeResolver_DoesNotResolve) {
1041510416
EXPECT_VALID(result);
1041610417

1041710418
result = Dart_Invoke(lib, NewString("main"), 0, nullptr);
10418-
EXPECT_ERROR(
10419-
result,
10420-
"Invalid argument(s): Couldn't resolve function: 'DoesNotResolve'");
10419+
EXPECT_ERROR(result, "Couldn't resolve function: 'DoesNotResolve'");
1042110420
}
1042210421

1042310422
TEST_CASE(DartAPI_UserTags) {

0 commit comments

Comments
 (0)