|
34 | 34 | #define WIN32_LEAN_AND_MEAN |
35 | 35 | #include <windows.h> |
36 | 36 | typedef HMODULE lib_handle_t; |
37 | | - #define LIB_OPEN(path) LoadLibraryA(path) |
| 37 | + /* Library loads on Windows go through open_lib_from_napi(), which uses |
| 38 | + * LoadLibraryW. LoadLibraryA would mangle path bytes that lie outside |
| 39 | + * the process's active ANSI code page and fail with ERROR_MOD_NOT_FOUND. |
| 40 | + */ |
38 | 41 | #define LIB_SYM(handle, sym) GetProcAddress(handle, sym) |
39 | 42 | #define LIB_CLOSE(handle) FreeLibrary(handle) |
40 | 43 | #else |
41 | 44 | #include <dlfcn.h> |
42 | 45 | typedef void* lib_handle_t; |
43 | | - #define LIB_OPEN(path) dlopen(path, RTLD_NOW | RTLD_LOCAL) |
44 | 46 | #define LIB_SYM(handle, sym) dlsym(handle, sym) |
45 | 47 | #define LIB_CLOSE(handle) dlclose(handle) |
46 | 48 | #endif |
@@ -219,6 +221,77 @@ static void preload_isolated_openssl(void) { |
219 | 221 | #endif |
220 | 222 | } |
221 | 223 |
|
| 224 | +/* ── Helper: open a library from a napi string path ────────────────────── |
| 225 | + * |
| 226 | + * On Windows the load uses LoadLibraryW with a UTF-16 path so that paths |
| 227 | + * containing any character outside the process's active ANSI code page |
| 228 | + * resolve correctly. |
| 229 | + * |
| 230 | + * Returns the loaded library handle on success. On any failure (bad |
| 231 | + * argument, allocation failure, or loader rejection), throws a JS error |
| 232 | + * and returns NULL. `error_prefix` scopes the loader error message |
| 233 | + * (e.g. "Failed to load core library"). |
| 234 | + */ |
| 235 | +static lib_handle_t open_lib_from_napi(napi_env env, napi_value path_val, |
| 236 | + const char* error_prefix) { |
| 237 | + size_t len8 = 0; |
| 238 | + if (napi_get_value_string_utf8(env, path_val, NULL, 0, &len8) != napi_ok) { |
| 239 | + napi_throw_type_error(env, NULL, "library path must be a string"); |
| 240 | + return NULL; |
| 241 | + } |
| 242 | + char* path_utf8 = (char*)malloc(len8 + 1); |
| 243 | + if (!path_utf8) { |
| 244 | + napi_throw_error(env, NULL, "Out of memory"); |
| 245 | + return NULL; |
| 246 | + } |
| 247 | + if (napi_get_value_string_utf8(env, path_val, path_utf8, len8 + 1, &len8) != napi_ok) { |
| 248 | + free(path_utf8); |
| 249 | + napi_throw_error(env, NULL, "Failed to read library path"); |
| 250 | + return NULL; |
| 251 | + } |
| 252 | + |
| 253 | + lib_handle_t handle = NULL; |
| 254 | +#ifdef _WIN32 |
| 255 | + size_t len16 = 0; |
| 256 | + if (napi_get_value_string_utf16(env, path_val, NULL, 0, &len16) != napi_ok) { |
| 257 | + free(path_utf8); |
| 258 | + napi_throw_error(env, NULL, "Failed to read library path"); |
| 259 | + return NULL; |
| 260 | + } |
| 261 | + char16_t* path_utf16 = (char16_t*)malloc((len16 + 1) * sizeof(char16_t)); |
| 262 | + if (!path_utf16) { |
| 263 | + free(path_utf8); |
| 264 | + napi_throw_error(env, NULL, "Out of memory"); |
| 265 | + return NULL; |
| 266 | + } |
| 267 | + if (napi_get_value_string_utf16(env, path_val, path_utf16, len16 + 1, &len16) != napi_ok) { |
| 268 | + free(path_utf16); |
| 269 | + free(path_utf8); |
| 270 | + napi_throw_error(env, NULL, "Failed to read library path"); |
| 271 | + return NULL; |
| 272 | + } |
| 273 | + /* char16_t and Windows wchar_t are both 16-bit; cast only at the OS boundary. */ |
| 274 | + handle = LoadLibraryW((LPCWSTR)path_utf16); |
| 275 | + free(path_utf16); |
| 276 | +#else |
| 277 | + handle = dlopen(path_utf8, RTLD_NOW | RTLD_LOCAL); |
| 278 | +#endif |
| 279 | + |
| 280 | + if (!handle) { |
| 281 | + char err_msg[1024]; |
| 282 | +#ifdef _WIN32 |
| 283 | + DWORD win_err = GetLastError(); |
| 284 | + snprintf(err_msg, sizeof(err_msg), "%s: %s (LoadLibraryW error %lu)", error_prefix, path_utf8, (unsigned long)win_err); |
| 285 | +#else |
| 286 | + const char* dl_err = dlerror(); |
| 287 | + snprintf(err_msg, sizeof(err_msg), "%s: %s (%s)", error_prefix, path_utf8, dl_err ? dl_err : "dlopen failed"); |
| 288 | +#endif |
| 289 | + napi_throw_error(env, NULL, err_msg); |
| 290 | + } |
| 291 | + free(path_utf8); |
| 292 | + return handle; |
| 293 | +} |
| 294 | + |
222 | 295 | /* ── Helper: clean up loaded libraries on error ───────────────────────── */ |
223 | 296 |
|
224 | 297 | static void cleanup_loaded_libs(void) { |
@@ -324,54 +397,23 @@ static napi_value napi_load_library(napi_env env, napi_callback_info info) { |
324 | 397 | napi_value elem; |
325 | 398 | NAPI_CALL(env, napi_get_element(env, argv[1], i, &elem)); |
326 | 399 |
|
327 | | - size_t len = 0; |
328 | | - NAPI_CALL(env, napi_get_value_string_utf8(env, elem, NULL, 0, &len)); |
329 | | - char* dep_path = (char*)malloc(len + 1); |
330 | | - if (!dep_path) { |
331 | | - cleanup_loaded_libs(); |
332 | | - napi_throw_error(env, NULL, "Out of memory"); |
333 | | - return NULL; |
334 | | - } |
335 | | - NAPI_CALL(env, napi_get_value_string_utf8(env, elem, dep_path, len + 1, &len)); |
336 | | - |
337 | | - g_dep_libs[i] = LIB_OPEN(dep_path); |
| 400 | + g_dep_libs[i] = open_lib_from_napi( |
| 401 | + env, elem, "Failed to load dependency library"); |
338 | 402 | if (!g_dep_libs[i]) { |
339 | | - char err_msg[512]; |
340 | | - snprintf(err_msg, sizeof(err_msg), |
341 | | - "Failed to load dependency library: %s", dep_path); |
342 | | - free(dep_path); |
343 | 403 | cleanup_loaded_libs(); |
344 | | - napi_throw_error(env, NULL, err_msg); |
345 | 404 | return NULL; |
346 | 405 | } |
347 | | - free(dep_path); |
348 | 406 | } |
349 | 407 | } |
350 | 408 | } |
351 | 409 | } |
352 | 410 |
|
353 | 411 | /* Load the core library */ |
354 | | - size_t core_len = 0; |
355 | | - NAPI_CALL(env, napi_get_value_string_utf8(env, argv[0], NULL, 0, &core_len)); |
356 | | - char* core_path = (char*)malloc(core_len + 1); |
357 | | - if (!core_path) { |
358 | | - cleanup_loaded_libs(); |
359 | | - napi_throw_error(env, NULL, "Out of memory"); |
360 | | - return NULL; |
361 | | - } |
362 | | - NAPI_CALL(env, napi_get_value_string_utf8(env, argv[0], core_path, core_len + 1, &core_len)); |
363 | | - |
364 | | - g_core_lib = LIB_OPEN(core_path); |
| 412 | + g_core_lib = open_lib_from_napi(env, argv[0], "Failed to load core library"); |
365 | 413 | if (!g_core_lib) { |
366 | | - char err_msg[512]; |
367 | | - snprintf(err_msg, sizeof(err_msg), |
368 | | - "Failed to load core library: %s", core_path); |
369 | | - free(core_path); |
370 | 414 | cleanup_loaded_libs(); |
371 | | - napi_throw_error(env, NULL, err_msg); |
372 | 415 | return NULL; |
373 | 416 | } |
374 | | - free(core_path); |
375 | 417 |
|
376 | 418 | /* Resolve function pointers */ |
377 | 419 | g_execute_command = (ExecuteCommandFn)LIB_SYM(g_core_lib, "execute_command"); |
|
0 commit comments