From fa22956a8616c34482b10bb3ae1aed76ad017c3e Mon Sep 17 00:00:00 2001 From: Luke Channings <461449+LukeChannings@users.noreply.github.com> Date: Sat, 15 Oct 2022 16:21:04 +0100 Subject: [PATCH] refactor(build): better handle old glibc (#16238) Follow-up to #16208. - Refactors build.rs behaviour to use `-exported_symbols_list` / `--export-dynamic-symbol-list` - Since all build systems now rely on a symbols list file, I have added `generate_exported_symbols_list`, which derives the symbol list file depending on the platform, which makes `tools/napi/generate_link_win.js` redundant. - Fixes a missed instance of `i8` being used instead of `c_char` Co-authored-by: Divy Srivastava --- Cargo.lock | 10 ++ cli/Cargo.toml | 1 + cli/build.rs | 62 +++----- cli/generated_symbol_exports_list_linux.def | 144 ++++++++++++++++++ cli/generated_symbol_exports_list_macos.def | 144 ++++++++++++++++++ ...generated_symbol_exports_list_windows.def} | 0 cli/napi/README.md | 6 +- cli/napi/mod.rs | 4 +- cli/napi_sym/README.md | 4 +- test_napi/src/object_wrap.rs | 2 +- tools/napi/generate_link_win.js | 14 -- tools/napi/generate_symbols_lists.js | 20 +++ 12 files changed, 350 insertions(+), 61 deletions(-) create mode 100644 cli/generated_symbol_exports_list_linux.def create mode 100644 cli/generated_symbol_exports_list_macos.def rename cli/{exports.def => generated_symbol_exports_list_windows.def} (100%) delete mode 100755 tools/napi/generate_link_win.js create mode 100755 tools/napi/generate_symbols_lists.js diff --git a/Cargo.lock b/Cargo.lock index 1799a10bb3..5e4913561e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -843,6 +843,7 @@ dependencies = [ "flaky_test", "flate2", "fwdansi", + "glibc_version", "google-storage1", "http", "import_map", @@ -2047,6 +2048,15 @@ dependencies = [ "polyval", ] +[[package]] +name = "glibc_version" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "803ff7635f1ab4e2c064b68a0c60da917d3d18dc8d086130f689d62ce4f1c33e" +dependencies = [ + "regex", +] + [[package]] name = "glob" version = "0.3.0" diff --git a/cli/Cargo.toml b/cli/Cargo.toml index 267048b271..094a32d53b 100644 --- a/cli/Cargo.toml +++ b/cli/Cargo.toml @@ -43,6 +43,7 @@ regex = "=1.6.0" serde = { version = "=1.0.144", features = ["derive"] } serde_json = "1.0.64" zstd = '=0.11.2' +glibc_version = "0.1.2" [target.'cfg(windows)'.build-dependencies] winapi = "=0.3.9" diff --git a/cli/build.rs b/cli/build.rs index 0d683780f2..98d044a3e9 100644 --- a/cli/build.rs +++ b/cli/build.rs @@ -332,56 +332,40 @@ fn main() { panic!("Cross compiling with snapshot is not supported."); } + let symbols_path = std::path::Path::new( + format!("generated_symbol_exports_list_{}.def", env::consts::OS).as_str(), + ) + .canonicalize() + .expect( + "Missing symbols list! Generate using tools/napi/generate_symbols_lists.js", + ); + #[cfg(target_os = "windows")] println!( "cargo:rustc-link-arg-bin=deno=/DEF:{}", - std::path::Path::new("exports.def") - .canonicalize() - .expect( - "Missing exports.def! Generate using tools/napi/generate_link_win.js" - ) - .display(), + symbols_path.display() ); - #[cfg(all( - not(target_os = "windows"), - not(all(target_os = "linux", target_arch = "aarch64")) - ))] - { - // Load the symbols file generated by the `napi_sym` macro. - #[derive(serde::Deserialize)] - struct Symbols { - symbols: Vec, - } - let symbols_json = - std::fs::read_to_string("./napi_sym/symbol_exports.json").expect( - "Missing ./napi_sym/symbol_exports.json! This is a bug in napi_sym", - ); - let symbols: Symbols = serde_json::from_str(&symbols_json) - .expect("./napi_sym/symbol_exports.json is not valid JSON"); + #[cfg(target_os = "macos")] + println!( + "cargo:rustc-link-arg-bin=deno=-Wl,-exported_symbols_list,{}", + symbols_path.display() + ); - // Don't export all symbols into the dynamic symbol table. -rdynamic exports *all* symbols introducing binary bloat. - // We only need to export Node API symbols. - for symbol in symbols.symbols { - // TODO(@littledivy): We _might_ hit an argument size limit? - // Maybe use `--export-dynamic-symbol-list` / `--exported_symbols_list` instead? https://reviews.llvm.org/D107317 - #[cfg(target_os = "macos")] + #[cfg(target_os = "linux")] + { + let ver = glibc_version::get_version().unwrap(); + if ver.major <= 2 && ver.minor < 35 { + println!("cargo:warning=Compiling with all symbols exported, this will result in a larger binary. Please use glibc 2.35 or later for an optimised build."); + println!("cargo:rustc-link-arg-bin=deno=-rdynamic"); + } else { println!( - "cargo:rustc-link-arg-bin=deno=-Wl,-exported_symbol,_{}", - symbol - ); - #[cfg(target_os = "linux")] - println!( - "cargo:rustc-link-arg-bin=deno=-Wl,--export-dynamic-symbol={}", - symbol + "cargo:rustc-link-arg-bin=deno=-Wl,--export-dynamic-symbol-list={}", + symbols_path.display() ); } } - // Linux + aarch64 does not support a glibc version that supports `--export-dynamic-symbol`. - #[cfg(all(target_os = "linux", target_arch = "aarch64"))] - println!("cargo:rustc-link-arg-bin=deno=-rdynamic"); - // To debug snapshot issues uncomment: // op_fetch_asset::trace_serializer(); diff --git a/cli/generated_symbol_exports_list_linux.def b/cli/generated_symbol_exports_list_linux.def new file mode 100644 index 0000000000..0adba1dcef --- /dev/null +++ b/cli/generated_symbol_exports_list_linux.def @@ -0,0 +1,144 @@ +node_api_create_syntax_error +napi_make_callback +napi_has_named_property +napi_async_destroy +napi_coerce_to_object +napi_get_arraybuffer_info +napi_detach_arraybuffer +napi_get_undefined +napi_reference_unref +napi_fatal_error +napi_open_callback_scope +napi_close_callback_scope +napi_get_value_uint32 +napi_create_function +napi_create_arraybuffer +napi_get_value_int64 +napi_get_all_property_names +napi_resolve_deferred +napi_is_detached_arraybuffer +napi_create_string_utf8 +napi_create_threadsafe_function +node_api_throw_syntax_error +napi_create_bigint_int64 +napi_wrap +napi_set_property +napi_get_value_bigint_int64 +napi_open_handle_scope +napi_create_error +napi_create_buffer +napi_cancel_async_work +napi_is_exception_pending +napi_acquire_threadsafe_function +napi_create_external +napi_get_threadsafe_function_context +napi_get_null +napi_create_string_utf16 +napi_get_value_bigint_uint64 +napi_module_register +napi_is_typedarray +napi_create_external_buffer +napi_get_new_target +napi_get_instance_data +napi_close_handle_scope +napi_get_value_string_utf16 +napi_get_property_names +napi_is_arraybuffer +napi_get_cb_info +napi_define_properties +napi_add_env_cleanup_hook +node_api_get_module_file_name +napi_get_node_version +napi_create_int64 +napi_create_double +napi_get_and_clear_last_exception +napi_create_reference +napi_get_typedarray_info +napi_call_threadsafe_function +napi_get_last_error_info +napi_create_array_with_length +napi_coerce_to_number +napi_get_global +napi_is_error +napi_set_instance_data +napi_create_typedarray +napi_throw_type_error +napi_has_property +napi_get_value_external +napi_create_range_error +napi_typeof +napi_ref_threadsafe_function +napi_create_bigint_uint64 +napi_get_prototype +napi_adjust_external_memory +napi_release_threadsafe_function +napi_delete_async_work +napi_create_string_latin1 +napi_is_array +napi_unref_threadsafe_function +napi_throw_error +napi_has_own_property +napi_get_reference_value +napi_remove_env_cleanup_hook +napi_get_value_string_utf8 +napi_is_promise +napi_get_boolean +napi_run_script +napi_get_element +napi_get_named_property +napi_get_buffer_info +napi_get_value_bool +napi_reference_ref +napi_create_object +napi_create_promise +napi_create_int32 +napi_escape_handle +napi_open_escapable_handle_scope +napi_throw +napi_get_value_double +napi_set_named_property +napi_call_function +napi_create_date +napi_object_freeze +napi_get_uv_event_loop +napi_get_value_string_latin1 +napi_reject_deferred +napi_add_finalizer +napi_create_array +napi_delete_reference +napi_get_date_value +napi_create_dataview +napi_get_version +napi_define_class +napi_is_date +napi_remove_wrap +napi_delete_property +napi_instanceof +napi_create_buffer_copy +napi_delete_element +napi_object_seal +napi_queue_async_work +napi_get_value_bigint_words +napi_is_buffer +napi_get_array_length +napi_get_property +napi_new_instance +napi_set_element +napi_create_bigint_words +napi_strict_equals +napi_is_dataview +napi_close_escapable_handle_scope +napi_get_dataview_info +napi_get_value_int32 +napi_unwrap +napi_throw_range_error +napi_coerce_to_bool +napi_create_uint32 +napi_has_element +napi_create_external_arraybuffer +napi_create_symbol +napi_coerce_to_string +napi_create_type_error +napi_fatal_exception +napi_create_async_work +napi_async_init diff --git a/cli/generated_symbol_exports_list_macos.def b/cli/generated_symbol_exports_list_macos.def new file mode 100644 index 0000000000..7c588ea2fd --- /dev/null +++ b/cli/generated_symbol_exports_list_macos.def @@ -0,0 +1,144 @@ +_node_api_create_syntax_error +_napi_make_callback +_napi_has_named_property +_napi_async_destroy +_napi_coerce_to_object +_napi_get_arraybuffer_info +_napi_detach_arraybuffer +_napi_get_undefined +_napi_reference_unref +_napi_fatal_error +_napi_open_callback_scope +_napi_close_callback_scope +_napi_get_value_uint32 +_napi_create_function +_napi_create_arraybuffer +_napi_get_value_int64 +_napi_get_all_property_names +_napi_resolve_deferred +_napi_is_detached_arraybuffer +_napi_create_string_utf8 +_napi_create_threadsafe_function +_node_api_throw_syntax_error +_napi_create_bigint_int64 +_napi_wrap +_napi_set_property +_napi_get_value_bigint_int64 +_napi_open_handle_scope +_napi_create_error +_napi_create_buffer +_napi_cancel_async_work +_napi_is_exception_pending +_napi_acquire_threadsafe_function +_napi_create_external +_napi_get_threadsafe_function_context +_napi_get_null +_napi_create_string_utf16 +_napi_get_value_bigint_uint64 +_napi_module_register +_napi_is_typedarray +_napi_create_external_buffer +_napi_get_new_target +_napi_get_instance_data +_napi_close_handle_scope +_napi_get_value_string_utf16 +_napi_get_property_names +_napi_is_arraybuffer +_napi_get_cb_info +_napi_define_properties +_napi_add_env_cleanup_hook +_node_api_get_module_file_name +_napi_get_node_version +_napi_create_int64 +_napi_create_double +_napi_get_and_clear_last_exception +_napi_create_reference +_napi_get_typedarray_info +_napi_call_threadsafe_function +_napi_get_last_error_info +_napi_create_array_with_length +_napi_coerce_to_number +_napi_get_global +_napi_is_error +_napi_set_instance_data +_napi_create_typedarray +_napi_throw_type_error +_napi_has_property +_napi_get_value_external +_napi_create_range_error +_napi_typeof +_napi_ref_threadsafe_function +_napi_create_bigint_uint64 +_napi_get_prototype +_napi_adjust_external_memory +_napi_release_threadsafe_function +_napi_delete_async_work +_napi_create_string_latin1 +_napi_is_array +_napi_unref_threadsafe_function +_napi_throw_error +_napi_has_own_property +_napi_get_reference_value +_napi_remove_env_cleanup_hook +_napi_get_value_string_utf8 +_napi_is_promise +_napi_get_boolean +_napi_run_script +_napi_get_element +_napi_get_named_property +_napi_get_buffer_info +_napi_get_value_bool +_napi_reference_ref +_napi_create_object +_napi_create_promise +_napi_create_int32 +_napi_escape_handle +_napi_open_escapable_handle_scope +_napi_throw +_napi_get_value_double +_napi_set_named_property +_napi_call_function +_napi_create_date +_napi_object_freeze +_napi_get_uv_event_loop +_napi_get_value_string_latin1 +_napi_reject_deferred +_napi_add_finalizer +_napi_create_array +_napi_delete_reference +_napi_get_date_value +_napi_create_dataview +_napi_get_version +_napi_define_class +_napi_is_date +_napi_remove_wrap +_napi_delete_property +_napi_instanceof +_napi_create_buffer_copy +_napi_delete_element +_napi_object_seal +_napi_queue_async_work +_napi_get_value_bigint_words +_napi_is_buffer +_napi_get_array_length +_napi_get_property +_napi_new_instance +_napi_set_element +_napi_create_bigint_words +_napi_strict_equals +_napi_is_dataview +_napi_close_escapable_handle_scope +_napi_get_dataview_info +_napi_get_value_int32 +_napi_unwrap +_napi_throw_range_error +_napi_coerce_to_bool +_napi_create_uint32 +_napi_has_element +_napi_create_external_arraybuffer +_napi_create_symbol +_napi_coerce_to_string +_napi_create_type_error +_napi_fatal_exception +_napi_create_async_work +_napi_async_init diff --git a/cli/exports.def b/cli/generated_symbol_exports_list_windows.def similarity index 100% rename from cli/exports.def rename to cli/generated_symbol_exports_list_windows.def diff --git a/cli/napi/README.md b/cli/napi/README.md index 7f46d97973..210d89b180 100644 --- a/cli/napi/README.md +++ b/cli/napi/README.md @@ -44,10 +44,10 @@ pub fn napi_get_boolean( } ``` -Update the Windows `.def` file using the script: +Update the generated symbol lists using the script: ``` -deno run --allow-write tools/napi/generate_link_win.js +deno run --allow-write tools/napi/generate_symbols_lists.js ``` Add a test in [`/test_napi`](../../test_napi/). You can also refer to Node.js @@ -109,7 +109,7 @@ unsafe extern "C" fn napi_register_module_v1( ) -> napi_value { ... + boolean::init(env, exports); - + exports } ``` diff --git a/cli/napi/mod.rs b/cli/napi/mod.rs index 8982a732a5..1712632a5c 100644 --- a/cli/napi/mod.rs +++ b/cli/napi/mod.rs @@ -7,13 +7,13 @@ //! Symbols to be exported are now defined in this JSON file. //! The `#[napi_sym]` macro checks for missing entries and panics. //! -//! `./tools/napi/generate_link_win.js` is used to generate the LINK `cli/exports.def` on Windows, +//! `./tools/napi/generate_symbols_list.js` is used to generate the LINK `cli/exports.def` on Windows, //! which is also checked into git. //! //! To add a new napi function: //! 1. Place `#[napi_sym]` on top of your implementation. //! 2. Add the function's identifier to this JSON list. -//! 3. Finally, run `./tools/napi/generate_link_win.js` to update `cli/exports.def`. +//! 3. Finally, run `./tools/napi/generate_symbols_list.js` to update `cli/generated_symbol_exports_list_*.def`. pub mod r#async; pub mod env; diff --git a/cli/napi_sym/README.md b/cli/napi_sym/README.md index 80bb2be0f7..b3e2ab43bc 100644 --- a/cli/napi_sym/README.md +++ b/cli/napi_sym/README.md @@ -24,11 +24,11 @@ fn napi_get_boolean( ### `symbol_exports.json` -A file containing the symbols that need to be put into the exectable's dynamic +A file containing the symbols that need to be put into the executable's dynamic symbol table at link-time. This is done using `/DEF:` on Windows, `-exported_symbol,_` on macOS and `--export-dynamic-symbol=` on Linux. See [`cli/build.rs`](../build.rs). On Windows, you need to generate the `.def` file by running -[`tools/napi/generate_link_win.js`](../../tools/napi/generate_link_win.js). +[`tools/napi/generate_symbols_lists.js`](../../tools/napi/generate_symbols_lists.js). diff --git a/test_napi/src/object_wrap.rs b/test_napi/src/object_wrap.rs index a38f97c9cd..40189e01c5 100644 --- a/test_napi/src/object_wrap.rs +++ b/test_napi/src/object_wrap.rs @@ -130,7 +130,7 @@ pub fn init(env: napi_env, exports: napi_value) { unsafe { napi_define_class( env, - "NapiObject\0".as_ptr() as *mut i8, + "NapiObject\0".as_ptr() as *mut c_char, usize::MAX, Some(NapiObject::new), ptr::null_mut(), diff --git a/tools/napi/generate_link_win.js b/tools/napi/generate_link_win.js deleted file mode 100755 index 7d16f81c3e..0000000000 --- a/tools/napi/generate_link_win.js +++ /dev/null @@ -1,14 +0,0 @@ -#!/usr/bin/env -S deno run --unstable --allow-read --allow-write -// Copyright 2018-2022 the Deno authors. All rights reserved. MIT license. - -import exports from "../../cli/napi_sym/symbol_exports.json" assert { - type: "json", -}; - -let def = "LIBRARY\nEXPORTS\n"; -for (const symbol of exports.symbols) { - def += ` ${symbol}\n`; -} - -const defUrl = new URL("../../cli/exports.def", import.meta.url); -await Deno.writeTextFile(defUrl.pathname, def, { create: true }); diff --git a/tools/napi/generate_symbols_lists.js b/tools/napi/generate_symbols_lists.js new file mode 100755 index 0000000000..3e41c3f065 --- /dev/null +++ b/tools/napi/generate_symbols_lists.js @@ -0,0 +1,20 @@ +#!/usr/bin/env -S deno run --unstable --allow-read --allow-write +// Copyright 2018-2022 the Deno authors. All rights reserved. MIT license. + +import exports from "../../cli/napi_sym/symbol_exports.json" assert { + type: "json", +}; + +for await (const os of ["linux", "macos", "windows"]) { + let def = os === "windows" ? "LIBRARY\nEXPORTS\n" : ""; + const prefix = os === "windows" ? " " : os === "macos" ? "_" : ""; + for (const symbol of exports.symbols) { + def += `${prefix}${symbol}\n`; + } + + const defUrl = new URL( + `../../cli/generated_symbol_exports_list_${os}.def`, + import.meta.url, + ); + await Deno.writeTextFile(defUrl.pathname, def, { create: true }); +}