From 8bdbb08ccf7a9d1e645063b5fd2e615873261996 Mon Sep 17 00:00:00 2001 From: Benedikt Reinartz Date: Wed, 29 May 2024 10:53:14 +0200 Subject: [PATCH 1/8] Initial commit for NIF inspection tool --- rustler_tool/.cargo/config.toml | 2 + rustler_tool/Cargo.toml | 9 ++ rustler_tool/src/fake_symbols.rs | 148 +++++++++++++++++++++++++++++++ rustler_tool/src/main.rs | 42 +++++++++ rustler_tool/src/nif.rs | 67 ++++++++++++++ 5 files changed, 268 insertions(+) create mode 100644 rustler_tool/.cargo/config.toml create mode 100644 rustler_tool/Cargo.toml create mode 100644 rustler_tool/src/fake_symbols.rs create mode 100644 rustler_tool/src/main.rs create mode 100644 rustler_tool/src/nif.rs diff --git a/rustler_tool/.cargo/config.toml b/rustler_tool/.cargo/config.toml new file mode 100644 index 00000000..e2bab392 --- /dev/null +++ b/rustler_tool/.cargo/config.toml @@ -0,0 +1,2 @@ +[target.x86_64-unknown-linux-gnu] +rustflags = [ "-C", "link-args=-Wl,-export-dynamic" ] diff --git a/rustler_tool/Cargo.toml b/rustler_tool/Cargo.toml new file mode 100644 index 00000000..14f40e0e --- /dev/null +++ b/rustler_tool/Cargo.toml @@ -0,0 +1,9 @@ +[package] +name = "rustler_tool" +version = "0.1.0" +edition = "2021" + +[dependencies] +clap = { version = "4.5", features = [ "derive" ] } +libloading = "0.8" +rustler_sys = { version = "2.4.0", path = "../rustler_sys" } diff --git a/rustler_tool/src/fake_symbols.rs b/rustler_tool/src/fake_symbols.rs new file mode 100644 index 00000000..d385e072 --- /dev/null +++ b/rustler_tool/src/fake_symbols.rs @@ -0,0 +1,148 @@ +#[no_mangle] +pub static enif_alloc: usize = 0; +#[no_mangle] +pub static enif_alloc_binary: usize = 0; +#[no_mangle] +pub static enif_alloc_env: usize = 0; +#[no_mangle] +pub static enif_binary_to_term: usize = 0; +#[no_mangle] +pub static enif_clear_env: usize = 0; +#[no_mangle] +pub static enif_compare: usize = 0; +#[no_mangle] +pub static enif_consume_timeslice: usize = 0; +#[no_mangle] +pub static enif_free: usize = 0; +#[no_mangle] +pub static enif_free_env: usize = 0; +#[no_mangle] +pub static enif_get_atom: usize = 0; +#[no_mangle] +pub static enif_get_atom_length: usize = 0; +#[no_mangle] +pub static enif_get_double: usize = 0; +#[no_mangle] +pub static enif_get_int: usize = 0; +#[no_mangle] +pub static enif_get_list_cell: usize = 0; +#[no_mangle] +pub static enif_get_list_length: usize = 0; +#[no_mangle] +pub static enif_get_local_pid: usize = 0; +#[no_mangle] +pub static enif_get_long: usize = 0; +#[no_mangle] +pub static enif_get_map_size: usize = 0; +#[no_mangle] +pub static enif_get_map_value: usize = 0; +#[no_mangle] +pub static enif_get_resource: usize = 0; +#[no_mangle] +pub static enif_get_tuple: usize = 0; +#[no_mangle] +pub static enif_get_uint: usize = 0; +#[no_mangle] +pub static enif_get_ulong: usize = 0; +#[no_mangle] +pub static enif_hash: usize = 0; +#[no_mangle] +pub static enif_inspect_binary: usize = 0; +#[no_mangle] +pub static enif_inspect_iolist_as_binary: usize = 0; +#[no_mangle] +pub static enif_is_atom: usize = 0; +#[no_mangle] +pub static enif_is_binary: usize = 0; +#[no_mangle] +pub static enif_is_empty_list: usize = 0; +#[no_mangle] +pub static enif_is_fun: usize = 0; +#[no_mangle] +pub static enif_is_identical: usize = 0; +#[no_mangle] +pub static enif_is_list: usize = 0; +#[no_mangle] +pub static enif_is_map: usize = 0; +#[no_mangle] +pub static enif_is_number: usize = 0; +#[no_mangle] +pub static enif_is_pid: usize = 0; +#[no_mangle] +pub static enif_is_port: usize = 0; +#[no_mangle] +pub static enif_is_process_alive: usize = 0; +#[no_mangle] +pub static enif_is_ref: usize = 0; +#[no_mangle] +pub static enif_is_tuple: usize = 0; +#[no_mangle] +pub static enif_make_atom_len: usize = 0; +#[no_mangle] +pub static enif_make_badarg: usize = 0; +#[no_mangle] +pub static enif_make_binary: usize = 0; +#[no_mangle] +pub static enif_make_copy: usize = 0; +#[no_mangle] +pub static enif_make_double: usize = 0; +#[no_mangle] +pub static enif_make_existing_atom_len: usize = 0; +#[no_mangle] +pub static enif_make_int: usize = 0; +#[no_mangle] +pub static enif_make_list_cell: usize = 0; +#[no_mangle] +pub static enif_make_list_from_array: usize = 0; +#[no_mangle] +pub static enif_make_long: usize = 0; +#[no_mangle] +pub static enif_make_map_from_arrays: usize = 0; +#[no_mangle] +pub static enif_make_map_put: usize = 0; +#[no_mangle] +pub static enif_make_map_remove: usize = 0; +#[no_mangle] +pub static enif_make_map_update: usize = 0; +#[no_mangle] +pub static enif_make_new_binary: usize = 0; +#[no_mangle] +pub static enif_make_new_map: usize = 0; +#[no_mangle] +pub static enif_make_reverse_list: usize = 0; +#[no_mangle] +pub static enif_make_sub_binary: usize = 0; +#[no_mangle] +pub static enif_make_tuple_from_array: usize = 0; +#[no_mangle] +pub static enif_make_uint: usize = 0; +#[no_mangle] +pub static enif_make_ulong: usize = 0; +#[no_mangle] +pub static enif_map_iterator_create: usize = 0; +#[no_mangle] +pub static enif_map_iterator_destroy: usize = 0; +#[no_mangle] +pub static enif_map_iterator_get_pair: usize = 0; +#[no_mangle] +pub static enif_map_iterator_next: usize = 0; +#[no_mangle] +pub static enif_map_iterator_prev: usize = 0; +#[no_mangle] +pub static enif_open_resource_type: usize = 0; +#[no_mangle] +pub static enif_raise_exception: usize = 0; +#[no_mangle] +pub static enif_realloc_binary: usize = 0; +#[no_mangle] +pub static enif_release_binary: usize = 0; +#[no_mangle] +pub static enif_schedule_nif: usize = 0; +#[no_mangle] +pub static enif_self: usize = 0; +#[no_mangle] +pub static enif_snprintf: usize = 0; +#[no_mangle] +pub static enif_term_to_binary: usize = 0; +#[no_mangle] +pub static enif_term_type: usize = 0; diff --git a/rustler_tool/src/main.rs b/rustler_tool/src/main.rs new file mode 100644 index 00000000..b977e80c --- /dev/null +++ b/rustler_tool/src/main.rs @@ -0,0 +1,42 @@ +#[cfg(unix)] +mod fake_symbols; +mod nif; + +use std::path::PathBuf; + +use clap::{Parser, Subcommand}; + +use crate::nif::NifLibrary; + +#[derive(Parser)] +#[command(version, about, long_about = None)] +struct Cli { + #[command(subcommand)] + command: Option, +} + +#[derive(Subcommand)] +enum Commands { + /// does testing things + Nif { path: PathBuf }, +} + +fn main() { + let cli = Cli::parse(); + + match &cli.command { + Some(Commands::Nif { path }) => { + println!("Extracting nifs from {:?}", path); + + let lib = NifLibrary::load(&path).unwrap(); + + println!("Found library {} with nifs", lib.name); + for nif in lib.nifs { + println!(" {}/{}", nif.name, nif.arity); + } + } + None => { + panic!("No command given") + } + } +} diff --git a/rustler_tool/src/nif.rs b/rustler_tool/src/nif.rs new file mode 100644 index 00000000..673e1204 --- /dev/null +++ b/rustler_tool/src/nif.rs @@ -0,0 +1,67 @@ +use libloading::{Library, Symbol}; +use rustler_sys::ErlNifEntry; +use std::ffi::CStr; +use std::path::{Path, PathBuf}; + +pub struct Nif { + pub name: String, + pub arity: usize, + pub flags: usize, +} + +pub struct NifLibrary { + pub path: PathBuf, + pub name: String, + pub nifs: Vec, +} + +#[cfg(unix)] +unsafe fn maybe_call_nif_init( + lib: &Library, +) -> Result<*const ErlNifEntry, Box> { + let func: Symbol *const ErlNifEntry> = lib.get(b"nif_init")?; + + Ok(func()) +} + +#[cfg(windows)] +unsafe fn maybe_call_nif_init( + lib: &Library, +) -> Result<*const ErlNifEntry, Box> { + use rustler_sys::TWinDynNifCallbacks; + static NULL_CALLBACKS: TWinDynNifCallbacks = TWinDynNifCallbacks {}; + let func: Symbol *const ErlNifEntry> = + lib.get(b"nif_init")?; + + func(&NULL_CALLBACKS) +} + +impl NifLibrary { + pub fn load(path: &Path) -> Result> { + unsafe { + let lib = Library::new(&path)?; + let entry = maybe_call_nif_init(&lib)?; + + let name = CStr::from_ptr((*entry).name).to_str()?.to_string(); + let nif_array = + std::slice::from_raw_parts((*entry).funcs, (*entry).num_of_funcs as usize); + + let nifs = nif_array + .into_iter() + .filter_map(|f| { + Some(Nif { + name: CStr::from_ptr((*f).name).to_str().ok()?.to_string(), + arity: (*f).arity as usize, + flags: (*f).flags as usize, + }) + }) + .collect(); + + Ok(NifLibrary { + path: path.to_path_buf(), + name, + nifs, + }) + } + } +} From 9e7ecdb1f38dfaa5e7d0d4f854b7f3f93ac32774 Mon Sep 17 00:00:00 2001 From: Benedikt Reinartz Date: Thu, 6 Jun 2024 19:25:28 +0200 Subject: [PATCH 2/8] Implement enif_alloc/free in case these are used by the lib's allocator --- rustler_tool/src/fake_symbols.rs | 29 +++++++++++++++++++++++++---- 1 file changed, 25 insertions(+), 4 deletions(-) diff --git a/rustler_tool/src/fake_symbols.rs b/rustler_tool/src/fake_symbols.rs index d385e072..f445d799 100644 --- a/rustler_tool/src/fake_symbols.rs +++ b/rustler_tool/src/fake_symbols.rs @@ -1,5 +1,28 @@ -#[no_mangle] -pub static enif_alloc: usize = 0; +use std::alloc::{Layout, alloc, dealloc}; + +const HEADER: usize = 8; +const ALIGNMENT: usize = 8; + +#[no_mangle] +pub unsafe extern "C" fn enif_alloc(size: usize) -> *mut u8 { + if let Ok(layout) = Layout::from_size_align(size + HEADER, ALIGNMENT) { + let ptr = alloc(layout); + *(ptr as *mut usize) = size; + return ptr.wrapping_add(HEADER); + } + + std::ptr::null_mut() +} + +#[no_mangle] +pub unsafe extern "C" fn enif_free(ptr: *mut u8) { + let real_ptr = ptr.wrapping_sub(HEADER); + let size = *(real_ptr as *const usize); + if let Ok(layout) = Layout::from_size_align(size + HEADER, ALIGNMENT) { + dealloc(real_ptr, layout); + } +} + #[no_mangle] pub static enif_alloc_binary: usize = 0; #[no_mangle] @@ -13,8 +36,6 @@ pub static enif_compare: usize = 0; #[no_mangle] pub static enif_consume_timeslice: usize = 0; #[no_mangle] -pub static enif_free: usize = 0; -#[no_mangle] pub static enif_free_env: usize = 0; #[no_mangle] pub static enif_get_atom: usize = 0; From 46a601a2327a1f3b5fe19afba2f6e90a149e7c05 Mon Sep 17 00:00:00 2001 From: Benedikt Reinartz Date: Thu, 6 Jun 2024 20:45:55 +0200 Subject: [PATCH 3/8] Fix allocator usage and implement simple module formatting --- rustler_tool/src/fake_symbols.rs | 18 +++++++- rustler_tool/src/main.rs | 38 +++++++++++++---- rustler_tool/src/nif.rs | 14 ++++--- rustler_tool/src/nif_elixir.rs | 62 +++++++++++++++++++++++++++ rustler_tool/src/nif_erlang.rs | 72 ++++++++++++++++++++++++++++++++ 5 files changed, 189 insertions(+), 15 deletions(-) create mode 100644 rustler_tool/src/nif_elixir.rs create mode 100644 rustler_tool/src/nif_erlang.rs diff --git a/rustler_tool/src/fake_symbols.rs b/rustler_tool/src/fake_symbols.rs index f445d799..e77c866d 100644 --- a/rustler_tool/src/fake_symbols.rs +++ b/rustler_tool/src/fake_symbols.rs @@ -1,4 +1,4 @@ -use std::alloc::{Layout, alloc, dealloc}; +use std::alloc::{alloc, dealloc, Layout}; const HEADER: usize = 8; const ALIGNMENT: usize = 8; @@ -26,6 +26,8 @@ pub unsafe extern "C" fn enif_free(ptr: *mut u8) { #[no_mangle] pub static enif_alloc_binary: usize = 0; #[no_mangle] +pub static enif_alloc_resource: usize = 0; +#[no_mangle] pub static enif_alloc_env: usize = 0; #[no_mangle] pub static enif_binary_to_term: usize = 0; @@ -98,6 +100,8 @@ pub static enif_is_ref: usize = 0; #[no_mangle] pub static enif_is_tuple: usize = 0; #[no_mangle] +pub static enif_keep_resource: usize = 0; +#[no_mangle] pub static enif_make_atom_len: usize = 0; #[no_mangle] pub static enif_make_badarg: usize = 0; @@ -132,6 +136,10 @@ pub static enif_make_new_map: usize = 0; #[no_mangle] pub static enif_make_reverse_list: usize = 0; #[no_mangle] +pub static enif_make_resource: usize = 0; +#[no_mangle] +pub static enif_make_resource_binary: usize = 0; +#[no_mangle] pub static enif_make_sub_binary: usize = 0; #[no_mangle] pub static enif_make_tuple_from_array: usize = 0; @@ -158,12 +166,20 @@ pub static enif_realloc_binary: usize = 0; #[no_mangle] pub static enif_release_binary: usize = 0; #[no_mangle] +pub static enif_release_resource: usize = 0; +#[no_mangle] pub static enif_schedule_nif: usize = 0; #[no_mangle] pub static enif_self: usize = 0; #[no_mangle] +pub static enif_send: usize = 0; +#[no_mangle] pub static enif_snprintf: usize = 0; #[no_mangle] pub static enif_term_to_binary: usize = 0; #[no_mangle] pub static enif_term_type: usize = 0; +#[no_mangle] +pub static enif_thread_type: usize = 0; +#[no_mangle] +pub static enif_whereis_pid: usize = 0; diff --git a/rustler_tool/src/main.rs b/rustler_tool/src/main.rs index b977e80c..62d92011 100644 --- a/rustler_tool/src/main.rs +++ b/rustler_tool/src/main.rs @@ -1,6 +1,8 @@ #[cfg(unix)] mod fake_symbols; mod nif; +mod nif_elixir; +mod nif_erlang; use std::path::PathBuf; @@ -15,24 +17,44 @@ struct Cli { command: Option, } +#[derive(clap::ValueEnum, Clone, Default, Debug)] +enum OutputFormat { + #[default] + Bare, + Erlang, + Elixir, +} + #[derive(Subcommand)] enum Commands { /// does testing things - Nif { path: PathBuf }, + Nif { + path: PathBuf, + #[arg(short, long, default_value_t, value_enum)] + format: OutputFormat, + }, } fn main() { let cli = Cli::parse(); match &cli.command { - Some(Commands::Nif { path }) => { - println!("Extracting nifs from {:?}", path); - - let lib = NifLibrary::load(&path).unwrap(); + Some(Commands::Nif { path, format }) => { + let lib = NifLibrary::load(path).unwrap(); - println!("Found library {} with nifs", lib.name); - for nif in lib.nifs { - println!(" {}/{}", nif.name, nif.arity); + match format { + OutputFormat::Bare => { + println!("{}", lib.name); + for nif in lib.nifs { + println!(" {}/{}", nif.name, nif.arity); + } + } + OutputFormat::Erlang => { + println!("{}", nif_erlang::LibAsErlang(lib)) + } + OutputFormat::Elixir => { + println!("{}", nif_elixir::LibAsElixir(lib)) + } } } None => { diff --git a/rustler_tool/src/nif.rs b/rustler_tool/src/nif.rs index 673e1204..443a710f 100644 --- a/rustler_tool/src/nif.rs +++ b/rustler_tool/src/nif.rs @@ -39,24 +39,26 @@ unsafe fn maybe_call_nif_init( impl NifLibrary { pub fn load(path: &Path) -> Result> { unsafe { - let lib = Library::new(&path)?; + let lib = Library::new(path)?; let entry = maybe_call_nif_init(&lib)?; let name = CStr::from_ptr((*entry).name).to_str()?.to_string(); let nif_array = std::slice::from_raw_parts((*entry).funcs, (*entry).num_of_funcs as usize); - let nifs = nif_array - .into_iter() + let mut nifs: Vec<_> = nif_array + .iter() .filter_map(|f| { Some(Nif { - name: CStr::from_ptr((*f).name).to_str().ok()?.to_string(), - arity: (*f).arity as usize, - flags: (*f).flags as usize, + name: CStr::from_ptr(f.name).to_str().ok()?.to_string(), + arity: f.arity as usize, + flags: f.flags as usize, }) }) .collect(); + nifs.sort_by_key(|x| x.name.clone()); + Ok(NifLibrary { path: path.to_path_buf(), name, diff --git a/rustler_tool/src/nif_elixir.rs b/rustler_tool/src/nif_elixir.rs new file mode 100644 index 00000000..db106af8 --- /dev/null +++ b/rustler_tool/src/nif_elixir.rs @@ -0,0 +1,62 @@ +use crate::NifLibrary; +use std::fmt; + +pub struct LibAsElixir(pub NifLibrary); + +impl fmt::Display for LibAsElixir { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + writeln!( + f, + "defmodule {} do", + string_to_elixir_atom(&self.0.name, false) + )?; + + for nif in &self.0.nifs { + write!(f, " def {}(", string_to_elixir_atom(&nif.name, true))?; + for i in 0..nif.arity { + if i > 0 { + write!(f, ", ")?; + } + write!(f, "_")?; + } + writeln!(f, "), do: :erlang.nif_error(not_loaded)")?; + } + writeln!(f, "end") + } +} + +fn string_to_elixir_atom(s: &str, func: bool) -> String { + match s { + "false" | "true" | "nil" => s.to_string(), + _ if s.starts_with("Elixir.") => s[7..].to_string(), + _ => { + let mut output = String::new(); + let mut needs_quotes = false; + for c in s.chars() { + match c { + 'a'..='z' | 'A'..='Z' | '0'..='9' | '@' | '_' => output.push(c), + '"' => { + needs_quotes = true; + output.push_str("\\\""); + } + '\\' => { + needs_quotes = true; + output.push_str(r"\\"); + } + _ => { + needs_quotes = true; + output.push(c); + } + } + } + + if needs_quotes { + format!(":\"{}\"", output).to_string() + } else if !func { + format!(":{}", output).to_string() + } else { + output + } + } + } +} diff --git a/rustler_tool/src/nif_erlang.rs b/rustler_tool/src/nif_erlang.rs new file mode 100644 index 00000000..8f0aa968 --- /dev/null +++ b/rustler_tool/src/nif_erlang.rs @@ -0,0 +1,72 @@ +use crate::NifLibrary; +use std::fmt; + +pub struct LibAsErlang(pub NifLibrary); + +impl fmt::Display for LibAsErlang { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "-module({}).\n\n", string_to_erlang_atom(&self.0.name))?; + writeln!(f, "-export([")?; + let count = self.0.nifs.len(); + for (n, nif) in self.0.nifs.iter().enumerate() { + write!(f, " {}/{}", string_to_erlang_atom(&nif.name), nif.arity)?; + if n == count - 1 { + write!(f, ",")?; + } + writeln!(f)?; + } + write!(f, "]).\n\n")?; + + // TODO: On Load function + + for nif in &self.0.nifs { + write!(f, "{}(", string_to_erlang_atom(&nif.name))?; + for i in 0..nif.arity { + if i > 0 { + write!(f, ", ")?; + } + write!(f, "_")?; + } + write!(f, ") ->\n erlang:nif_error(not_loaded).\n\n")?; + } + + Ok(()) + } +} + +fn string_to_erlang_atom(input: &str) -> String { + let mut output = String::with_capacity(input.len()); + let mut needs_quotes = false; + + let mut first = true; + + for c in input.chars() { + match c { + 'A'..='Z' if first => { + needs_quotes = true; + output.push(c); + } + 'a'..='z' | 'A'..='Z' | '0'..='9' | '@' | '_' => output.push(c), + '\'' => { + needs_quotes = true; + output.push_str(r"\'"); + } + '\\' => { + needs_quotes = true; + output.push_str(r"\\"); + } + _ => { + needs_quotes = true; + output.push(c); + } + } + + first = false; + } + + if needs_quotes { + format!("'{}'", output).to_string() + } else { + output + } +} From b9cb3ded19278bcda90b37066b824e2493776301 Mon Sep 17 00:00:00 2001 From: Benedikt Reinartz Date: Thu, 24 Apr 2025 10:16:05 +0200 Subject: [PATCH 4/8] Move, update, complete fake symbol list --- Cargo.toml | 1 + .../.cargo/config.toml | 0 {rustler_tool => cargo-rustler}/Cargo.toml | 4 +- .../src/fake_symbols.rs | 212 ++++++++++++------ {rustler_tool => cargo-rustler}/src/main.rs | 0 {rustler_tool => cargo-rustler}/src/nif.rs | 2 +- .../src/nif_elixir.rs | 0 .../src/nif_erlang.rs | 0 8 files changed, 149 insertions(+), 70 deletions(-) rename {rustler_tool => cargo-rustler}/.cargo/config.toml (100%) rename {rustler_tool => cargo-rustler}/Cargo.toml (61%) rename {rustler_tool => cargo-rustler}/src/fake_symbols.rs (70%) rename {rustler_tool => cargo-rustler}/src/main.rs (100%) rename {rustler_tool => cargo-rustler}/src/nif.rs (98%) rename {rustler_tool => cargo-rustler}/src/nif_elixir.rs (100%) rename {rustler_tool => cargo-rustler}/src/nif_erlang.rs (100%) diff --git a/Cargo.toml b/Cargo.toml index f81d1d6c..07d3e381 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -11,6 +11,7 @@ members = [ "rustler_tests/native/rustler_compile_tests", "rustler_tests/native/resource_dyncall", "rustler_benchmarks/native/benchmark", + "cargo-rustler", ] default-members = [ "rustler", diff --git a/rustler_tool/.cargo/config.toml b/cargo-rustler/.cargo/config.toml similarity index 100% rename from rustler_tool/.cargo/config.toml rename to cargo-rustler/.cargo/config.toml diff --git a/rustler_tool/Cargo.toml b/cargo-rustler/Cargo.toml similarity index 61% rename from rustler_tool/Cargo.toml rename to cargo-rustler/Cargo.toml index 14f40e0e..effc00db 100644 --- a/rustler_tool/Cargo.toml +++ b/cargo-rustler/Cargo.toml @@ -1,9 +1,9 @@ [package] -name = "rustler_tool" +name = "cargo-rustler" version = "0.1.0" edition = "2021" [dependencies] clap = { version = "4.5", features = [ "derive" ] } libloading = "0.8" -rustler_sys = { version = "2.4.0", path = "../rustler_sys" } +rustler = { version = "0.36.1", path = "../rustler" } diff --git a/rustler_tool/src/fake_symbols.rs b/cargo-rustler/src/fake_symbols.rs similarity index 70% rename from rustler_tool/src/fake_symbols.rs rename to cargo-rustler/src/fake_symbols.rs index e77c866d..9af9e0b3 100644 --- a/rustler_tool/src/fake_symbols.rs +++ b/cargo-rustler/src/fake_symbols.rs @@ -24,162 +24,240 @@ pub unsafe extern "C" fn enif_free(ptr: *mut u8) { } #[no_mangle] -pub static enif_alloc_binary: usize = 0; +pub static enif_priv_data: usize = 0; #[no_mangle] -pub static enif_alloc_resource: usize = 0; +pub static enif_is_atom: usize = 0; #[no_mangle] -pub static enif_alloc_env: usize = 0; +pub static enif_is_binary: usize = 0; #[no_mangle] -pub static enif_binary_to_term: usize = 0; +pub static enif_is_ref: usize = 0; #[no_mangle] -pub static enif_clear_env: usize = 0; +pub static enif_inspect_binary: usize = 0; #[no_mangle] -pub static enif_compare: usize = 0; +pub static enif_alloc_binary: usize = 0; #[no_mangle] -pub static enif_consume_timeslice: usize = 0; +pub static enif_realloc_binary: usize = 0; #[no_mangle] -pub static enif_free_env: usize = 0; +pub static enif_release_binary: usize = 0; #[no_mangle] -pub static enif_get_atom: usize = 0; +pub static enif_get_int: usize = 0; #[no_mangle] -pub static enif_get_atom_length: usize = 0; +pub static enif_get_ulong: usize = 0; #[no_mangle] pub static enif_get_double: usize = 0; #[no_mangle] -pub static enif_get_int: usize = 0; -#[no_mangle] pub static enif_get_list_cell: usize = 0; #[no_mangle] -pub static enif_get_list_length: usize = 0; +pub static enif_get_tuple: usize = 0; #[no_mangle] -pub static enif_get_local_pid: usize = 0; +pub static enif_is_identical: usize = 0; #[no_mangle] -pub static enif_get_long: usize = 0; +pub static enif_compare: usize = 0; #[no_mangle] -pub static enif_get_map_size: usize = 0; +pub static enif_make_binary: usize = 0; #[no_mangle] -pub static enif_get_map_value: usize = 0; +pub static enif_make_badarg: usize = 0; #[no_mangle] -pub static enif_get_resource: usize = 0; +pub static enif_make_int: usize = 0; #[no_mangle] -pub static enif_get_tuple: usize = 0; +pub static enif_make_ulong: usize = 0; #[no_mangle] -pub static enif_get_uint: usize = 0; +pub static enif_make_double: usize = 0; #[no_mangle] -pub static enif_get_ulong: usize = 0; +pub static enif_make_atom: usize = 0; #[no_mangle] -pub static enif_hash: usize = 0; +pub static enif_make_existing_atom: usize = 0; #[no_mangle] -pub static enif_inspect_binary: usize = 0; +pub static enif_make_tuple: usize = 0; #[no_mangle] -pub static enif_inspect_iolist_as_binary: usize = 0; +pub static enif_make_list: usize = 0; #[no_mangle] -pub static enif_is_atom: usize = 0; +pub static enif_make_list_cell: usize = 0; #[no_mangle] -pub static enif_is_binary: usize = 0; +pub static enif_make_string: usize = 0; #[no_mangle] -pub static enif_is_empty_list: usize = 0; +pub static enif_make_ref: usize = 0; #[no_mangle] -pub static enif_is_fun: usize = 0; +pub static enif_realloc: usize = 0; #[no_mangle] -pub static enif_is_identical: usize = 0; +pub static enif_system_info: usize = 0; #[no_mangle] -pub static enif_is_list: usize = 0; +pub static enif_fprintf: usize = 0; #[no_mangle] -pub static enif_is_map: usize = 0; +pub static enif_inspect_iolist_as_binary: usize = 0; #[no_mangle] -pub static enif_is_number: usize = 0; +pub static enif_make_sub_binary: usize = 0; +#[no_mangle] +pub static enif_get_string: usize = 0; +#[no_mangle] +pub static enif_get_atom: usize = 0; +#[no_mangle] +pub static enif_is_fun: usize = 0; #[no_mangle] pub static enif_is_pid: usize = 0; #[no_mangle] pub static enif_is_port: usize = 0; #[no_mangle] -pub static enif_is_process_alive: usize = 0; +pub static enif_get_uint: usize = 0; #[no_mangle] -pub static enif_is_ref: usize = 0; +pub static enif_get_long: usize = 0; #[no_mangle] -pub static enif_is_tuple: usize = 0; +pub static enif_make_uint: usize = 0; #[no_mangle] -pub static enif_keep_resource: usize = 0; +pub static enif_make_long: usize = 0; #[no_mangle] -pub static enif_make_atom_len: usize = 0; +pub static enif_make_tuple_from_array: usize = 0; #[no_mangle] -pub static enif_make_badarg: usize = 0; +pub static enif_make_list_from_array: usize = 0; #[no_mangle] -pub static enif_make_binary: usize = 0; +pub static enif_is_empty_list: usize = 0; #[no_mangle] -pub static enif_make_copy: usize = 0; +pub static enif_open_resource_type: usize = 0; #[no_mangle] -pub static enif_make_double: usize = 0; +pub static enif_alloc_resource: usize = 0; +#[no_mangle] +pub static enif_release_resource: usize = 0; +#[no_mangle] +pub static enif_make_resource: usize = 0; +#[no_mangle] +pub static enif_get_resource: usize = 0; +#[no_mangle] +pub static enif_sizeof_resource: usize = 0; +#[no_mangle] +pub static enif_make_new_binary: usize = 0; +#[no_mangle] +pub static enif_is_list: usize = 0; +#[no_mangle] +pub static enif_is_tuple: usize = 0; +#[no_mangle] +pub static enif_get_atom_length: usize = 0; +#[no_mangle] +pub static enif_get_list_length: usize = 0; +#[no_mangle] +pub static enif_make_atom_len: usize = 0; #[no_mangle] pub static enif_make_existing_atom_len: usize = 0; #[no_mangle] -pub static enif_make_int: usize = 0; +pub static enif_make_string_len: usize = 0; #[no_mangle] -pub static enif_make_list_cell: usize = 0; +pub static enif_alloc_env: usize = 0; #[no_mangle] -pub static enif_make_list_from_array: usize = 0; +pub static enif_free_env: usize = 0; #[no_mangle] -pub static enif_make_long: usize = 0; +pub static enif_clear_env: usize = 0; #[no_mangle] -pub static enif_make_map_from_arrays: usize = 0; +pub static enif_send: usize = 0; #[no_mangle] -pub static enif_make_map_put: usize = 0; +pub static enif_make_copy: usize = 0; #[no_mangle] -pub static enif_make_map_remove: usize = 0; +pub static enif_self: usize = 0; #[no_mangle] -pub static enif_make_map_update: usize = 0; +pub static enif_get_local_pid: usize = 0; #[no_mangle] -pub static enif_make_new_binary: usize = 0; +pub static enif_keep_resource: usize = 0; #[no_mangle] -pub static enif_make_new_map: usize = 0; +pub static enif_make_resource_binary: usize = 0; +#[no_mangle] +pub static enif_is_exception: usize = 0; #[no_mangle] pub static enif_make_reverse_list: usize = 0; #[no_mangle] -pub static enif_make_resource: usize = 0; +pub static enif_is_number: usize = 0; #[no_mangle] -pub static enif_make_resource_binary: usize = 0; +pub static enif_dlopen: usize = 0; #[no_mangle] -pub static enif_make_sub_binary: usize = 0; +pub static enif_dlsym: usize = 0; #[no_mangle] -pub static enif_make_tuple_from_array: usize = 0; +pub static enif_consume_timeslice: usize = 0; #[no_mangle] -pub static enif_make_uint: usize = 0; +pub static enif_is_map: usize = 0; #[no_mangle] -pub static enif_make_ulong: usize = 0; +pub static enif_get_map_size: usize = 0; +#[no_mangle] +pub static enif_make_new_map: usize = 0; +#[no_mangle] +pub static enif_make_map_put: usize = 0; +#[no_mangle] +pub static enif_get_map_value: usize = 0; +#[no_mangle] +pub static enif_make_map_update: usize = 0; +#[no_mangle] +pub static enif_make_map_remove: usize = 0; #[no_mangle] pub static enif_map_iterator_create: usize = 0; #[no_mangle] pub static enif_map_iterator_destroy: usize = 0; #[no_mangle] -pub static enif_map_iterator_get_pair: usize = 0; +pub static enif_map_iterator_is_head: usize = 0; +#[no_mangle] +pub static enif_map_iterator_is_tail: usize = 0; #[no_mangle] pub static enif_map_iterator_next: usize = 0; #[no_mangle] pub static enif_map_iterator_prev: usize = 0; #[no_mangle] -pub static enif_open_resource_type: usize = 0; +pub static enif_map_iterator_get_pair: usize = 0; +#[no_mangle] +pub static enif_schedule_nif: usize = 0; +#[no_mangle] +pub static enif_has_pending_exception: usize = 0; #[no_mangle] pub static enif_raise_exception: usize = 0; #[no_mangle] -pub static enif_realloc_binary: usize = 0; +pub static enif_getenv: usize = 0; #[no_mangle] -pub static enif_release_binary: usize = 0; +pub static enif_monotonic_time: usize = 0; #[no_mangle] -pub static enif_release_resource: usize = 0; +pub static enif_time_offset: usize = 0; #[no_mangle] -pub static enif_schedule_nif: usize = 0; +pub static enif_convert_time_unit: usize = 0; #[no_mangle] -pub static enif_self: usize = 0; +pub static enif_now_time: usize = 0; #[no_mangle] -pub static enif_send: usize = 0; +pub static enif_cpu_time: usize = 0; #[no_mangle] -pub static enif_snprintf: usize = 0; +pub static enif_make_unique_integer: usize = 0; +#[no_mangle] +pub static enif_is_current_process_alive: usize = 0; +#[no_mangle] +pub static enif_is_process_alive: usize = 0; +#[no_mangle] +pub static enif_is_port_alive: usize = 0; +#[no_mangle] +pub static enif_get_local_port: usize = 0; #[no_mangle] pub static enif_term_to_binary: usize = 0; #[no_mangle] -pub static enif_term_type: usize = 0; +pub static enif_binary_to_term: usize = 0; +#[no_mangle] +pub static enif_port_command: usize = 0; #[no_mangle] pub static enif_thread_type: usize = 0; #[no_mangle] +pub static enif_snprintf: usize = 0; +#[no_mangle] +pub static enif_select: usize = 0; +#[no_mangle] +pub static enif_open_resource_type_x: usize = 0; +#[no_mangle] +pub static enif_monitor_process: usize = 0; +#[no_mangle] +pub static enif_demonitor_process: usize = 0; +#[no_mangle] +pub static enif_compare_monitors: usize = 0; +#[no_mangle] +pub static enif_hash: usize = 0; +#[no_mangle] pub static enif_whereis_pid: usize = 0; +#[no_mangle] +pub static enif_whereis_port: usize = 0; +#[no_mangle] +pub static enif_make_map_from_arrays: usize = 0; +#[no_mangle] +pub static enif_make_monitor_term: usize = 0; +#[no_mangle] +pub static enif_is_pid_undefined: usize = 0; +#[no_mangle] +pub static enif_set_pid_undefined: usize = 0; +#[no_mangle] +pub static enif_term_type: usize = 0; diff --git a/rustler_tool/src/main.rs b/cargo-rustler/src/main.rs similarity index 100% rename from rustler_tool/src/main.rs rename to cargo-rustler/src/main.rs diff --git a/rustler_tool/src/nif.rs b/cargo-rustler/src/nif.rs similarity index 98% rename from rustler_tool/src/nif.rs rename to cargo-rustler/src/nif.rs index 443a710f..5d86774a 100644 --- a/rustler_tool/src/nif.rs +++ b/cargo-rustler/src/nif.rs @@ -1,5 +1,5 @@ use libloading::{Library, Symbol}; -use rustler_sys::ErlNifEntry; +use rustler::sys::ErlNifEntry; use std::ffi::CStr; use std::path::{Path, PathBuf}; diff --git a/rustler_tool/src/nif_elixir.rs b/cargo-rustler/src/nif_elixir.rs similarity index 100% rename from rustler_tool/src/nif_elixir.rs rename to cargo-rustler/src/nif_elixir.rs diff --git a/rustler_tool/src/nif_erlang.rs b/cargo-rustler/src/nif_erlang.rs similarity index 100% rename from rustler_tool/src/nif_erlang.rs rename to cargo-rustler/src/nif_erlang.rs From 6b531f5f18977d24b215076dde6a1a41b3230676 Mon Sep 17 00:00:00 2001 From: Benedikt Reinartz Date: Fri, 25 Apr 2025 21:53:19 +0200 Subject: [PATCH 5/8] Implement simple build command --- cargo-rustler/Cargo.toml | 5 ++ cargo-rustler/snippets/on_load.erl | 19 +++++++ cargo-rustler/src/erl_build.rs | 36 ++++++++++++++ cargo-rustler/src/main.rs | 25 ++++++++-- cargo-rustler/src/nif.rs | 4 +- cargo-rustler/src/nif_erlang.rs | 49 ++++++++++++++---- cargo-rustler/src/rust_build.rs | 79 ++++++++++++++++++++++++++++++ 7 files changed, 202 insertions(+), 15 deletions(-) create mode 100644 cargo-rustler/snippets/on_load.erl create mode 100644 cargo-rustler/src/erl_build.rs create mode 100644 cargo-rustler/src/rust_build.rs diff --git a/cargo-rustler/Cargo.toml b/cargo-rustler/Cargo.toml index effc00db..0dc8905e 100644 --- a/cargo-rustler/Cargo.toml +++ b/cargo-rustler/Cargo.toml @@ -3,7 +3,12 @@ name = "cargo-rustler" version = "0.1.0" edition = "2021" +metadata = { msrv = "1.77" } + + [dependencies] +cargo_metadata = "0.19.2" clap = { version = "4.5", features = [ "derive" ] } libloading = "0.8" rustler = { version = "0.36.1", path = "../rustler" } +tempfile = "3.19.1" diff --git a/cargo-rustler/snippets/on_load.erl b/cargo-rustler/snippets/on_load.erl new file mode 100644 index 00000000..2e2f79d6 --- /dev/null +++ b/cargo-rustler/snippets/on_load.erl @@ -0,0 +1,19 @@ +-on_load(on_load/0). + +on_load() -> + PrivDir = case application:get_application(?MODULE) of + {ok, App} -> + code:priv_dir(App); + undefined -> + case code:which(?MODULE) of + non_existing -> + error(nif_module_not_found); + BeamPath -> + AbsDir = filename:dirname(filename:absname(BeamPath)), + filename:flatten(filename:join([AbsDir, "..", "priv"])) + end + end, + + Filename = filename:join([PrivDir, "native", ?NIF_NAME]), + erlang:load_nif(Filename, ?NIF_LOAD_INFO). + diff --git a/cargo-rustler/src/erl_build.rs b/cargo-rustler/src/erl_build.rs new file mode 100644 index 00000000..33be7994 --- /dev/null +++ b/cargo-rustler/src/erl_build.rs @@ -0,0 +1,36 @@ +use std::fs::{self, File}; +use std::io::Write; +use std::path::Path; +use std::process::Command; + +use tempfile::TempDir; + +pub fn build(module_name: &str, module: &str, output: &Path) { + let dir = TempDir::new().expect("Failed to create temp dir"); + let module_filename = dir.path().join(format!("{module_name}.erl")); + let out_dir = output.join("ebin"); + fs::create_dir_all(&out_dir).expect("Failed to create output directory"); + + // println!("Writing module to: {module_filename:?}\n\n====\n{module}\n==="); + + { + let mut f = File::create_new(&module_filename).expect("Failed to create temp file"); + f.write_all(module.as_bytes()) + .expect("Failed to write to temp file"); + } + + let command = Command::new("erlc") + .arg("-o") + .arg(out_dir) + .arg(&module_filename) + .output() + .expect("Failed to execute erlc"); + + if !command.status.success() { + let stderr = String::from_utf8_lossy(&command.stdout); + panic!( + "Erlang compilation failed: {}", + stderr.trim_end_matches('\n') + ); + } +} diff --git a/cargo-rustler/src/main.rs b/cargo-rustler/src/main.rs index 62d92011..ec7cebfd 100644 --- a/cargo-rustler/src/main.rs +++ b/cargo-rustler/src/main.rs @@ -1,12 +1,15 @@ +mod erl_build; #[cfg(unix)] mod fake_symbols; mod nif; mod nif_elixir; mod nif_erlang; +mod rust_build; use std::path::PathBuf; use clap::{Parser, Subcommand}; +use rust_build::BuildArgs; use crate::nif::NifLibrary; @@ -14,7 +17,7 @@ use crate::nif::NifLibrary; #[command(version, about, long_about = None)] struct Cli { #[command(subcommand)] - command: Option, + command: Commands, } #[derive(clap::ValueEnum, Clone, Default, Debug)] @@ -33,13 +36,15 @@ enum Commands { #[arg(short, long, default_value_t, value_enum)] format: OutputFormat, }, + + Build(BuildArgs), } fn main() { let cli = Cli::parse(); match &cli.command { - Some(Commands::Nif { path, format }) => { + Commands::Nif { path, format } => { let lib = NifLibrary::load(path).unwrap(); match format { @@ -57,8 +62,20 @@ fn main() { } } } - None => { - panic!("No command given") + Commands::Build(args) => { + println!("Building NIFs in {:?}", args.path); + let paths = rust_build::build(args); + + println!("Got NIFs: {paths:?}"); + + for path in paths { + let lib = NifLibrary::load(&path).unwrap(); + let erlang = nif_erlang::LibAsErlang(lib); + let module = format!("{erlang}"); + let module_name = erlang.0.name; + + erl_build::build(&module_name, &module, &args.out); + } } } } diff --git a/cargo-rustler/src/nif.rs b/cargo-rustler/src/nif.rs index 5d86774a..44f3a89d 100644 --- a/cargo-rustler/src/nif.rs +++ b/cargo-rustler/src/nif.rs @@ -6,7 +6,7 @@ use std::path::{Path, PathBuf}; pub struct Nif { pub name: String, pub arity: usize, - pub flags: usize, + pub _flags: usize, } pub struct NifLibrary { @@ -52,7 +52,7 @@ impl NifLibrary { Some(Nif { name: CStr::from_ptr(f.name).to_str().ok()?.to_string(), arity: f.arity as usize, - flags: f.flags as usize, + _flags: f.flags as usize, }) }) .collect(); diff --git a/cargo-rustler/src/nif_erlang.rs b/cargo-rustler/src/nif_erlang.rs index 8f0aa968..5a106de4 100644 --- a/cargo-rustler/src/nif_erlang.rs +++ b/cargo-rustler/src/nif_erlang.rs @@ -5,23 +5,54 @@ pub struct LibAsErlang(pub NifLibrary); impl fmt::Display for LibAsErlang { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "-module({}).\n\n", string_to_erlang_atom(&self.0.name))?; + let nif_lib = &self.0; + + let module_name = string_to_erlang_atom(&nif_lib.name); + + let nifs: Vec<_> = nif_lib + .nifs + .iter() + .enumerate() + .map(|(n, nif)| (n, string_to_erlang_atom(&nif.name), nif.arity)) + .collect(); + + let nif_name = nif_lib + .path + .with_extension("") + .file_name() + .unwrap() + .to_string_lossy() + .to_string(); + + writeln!(f, "-module({module_name}).\n")?; writeln!(f, "-export([")?; - let count = self.0.nifs.len(); - for (n, nif) in self.0.nifs.iter().enumerate() { - write!(f, " {}/{}", string_to_erlang_atom(&nif.name), nif.arity)?; - if n == count - 1 { + let count = nifs.len(); + for (n, name, arity) in &nifs { + write!(f, " {name}/{arity}")?; + if *n != count - 1 { + write!(f, ",")?; + } + writeln!(f)?; + } + write!(f, "]).\n\n")?; + + writeln!(f, "-nifs([")?; + for (n, name, arity) in &nifs { + write!(f, " {name}/{arity}")?; + if *n != count - 1 { write!(f, ",")?; } writeln!(f)?; } write!(f, "]).\n\n")?; - // TODO: On Load function + writeln!(f, "-define(NIF_LOAD_INFO, 0).")?; + writeln!(f, "-define(NIF_NAME, \"{}\").", &nif_name)?; + write!(f, "{}", include_str!("../snippets/on_load.erl"))?; - for nif in &self.0.nifs { - write!(f, "{}(", string_to_erlang_atom(&nif.name))?; - for i in 0..nif.arity { + for (_, name, arity) in &nifs { + write!(f, "{name}(")?; + for i in 0..*arity { if i > 0 { write!(f, ", ")?; } diff --git a/cargo-rustler/src/rust_build.rs b/cargo-rustler/src/rust_build.rs new file mode 100644 index 00000000..0df199de --- /dev/null +++ b/cargo-rustler/src/rust_build.rs @@ -0,0 +1,79 @@ +use std::fs; +use std::io::BufReader; +use std::path::PathBuf; +use std::process::{Command, Stdio}; + +use cargo_metadata::Message; +use clap::Args; + +#[derive(Args)] +pub struct BuildArgs { + #[arg(default_value = ".")] + pub path: PathBuf, + + #[arg(default_value = "out")] + pub out: PathBuf, +} + +pub fn build(args: &BuildArgs) -> Vec { + let path = args.path.clone(); + + // Run `cargo build` in the specified directory + let mut command = Command::new("cargo") + .arg("build") + .arg("--release") + .arg("--message-format=json") + .current_dir(&path) + .stdout(Stdio::piped()) + .spawn() + .expect("Failed to execute cargo build"); + + let reader = BufReader::new(command.stdout.take().unwrap()); + + let mut artifacts = vec![]; + + for message in Message::parse_stream(reader) { + if let Message::CompilerArtifact(artifact) = message.unwrap() { + // Check if the artifact is a library + if artifact.target.is_dylib() || artifact.target.is_cdylib() { + artifacts.push(artifact); + } + } + } + + dbg!(&artifacts); + + let output = command.wait().expect("Couldn't get cargo's exit status"); + + if !output.success() { + panic!("Cargo build failed with status: {}", output); + } + + artifacts + .iter() + .map(|artifact| { + // Ensure the output directory exists + let out_dir = args.out.join("priv/native/"); + fs::create_dir_all(&out_dir).expect("Failed to create output directory"); + + let name = &artifact.target.name; + let filename = &artifact.filenames[0]; + let mut ext = filename.extension().unwrap(); + // Erlang expects .so on macOS + if ext == "dylib" { + ext = "so"; + } + + // Stripping the "lib" prefix simplifies the load_nif call as it can be the same on all platforms + let output_name = format!("{}.{}", name, ext); + let destination = out_dir.join(&output_name); + + println!("Copying artifact from {:?} to {:?}", filename, destination,); + + // Copy the artifact to the output directory + fs::copy(filename, &destination).expect("Failed to copy artifact"); + + destination + }) + .collect() +} From 164dc0457e136ec124cc0232d88f5502d6b283b5 Mon Sep 17 00:00:00 2001 From: Benedikt Reinartz Date: Fri, 25 Apr 2025 22:13:54 +0200 Subject: [PATCH 6/8] Make independent of rustler itself and fix Windows --- cargo-rustler/Cargo.toml | 1 - cargo-rustler/src/main.rs | 1 + cargo-rustler/src/nif.rs | 16 ++++--- cargo-rustler/src/nif_types.rs | 80 ++++++++++++++++++++++++++++++++++ 4 files changed, 92 insertions(+), 6 deletions(-) create mode 100644 cargo-rustler/src/nif_types.rs diff --git a/cargo-rustler/Cargo.toml b/cargo-rustler/Cargo.toml index 0dc8905e..c177c0eb 100644 --- a/cargo-rustler/Cargo.toml +++ b/cargo-rustler/Cargo.toml @@ -10,5 +10,4 @@ metadata = { msrv = "1.77" } cargo_metadata = "0.19.2" clap = { version = "4.5", features = [ "derive" ] } libloading = "0.8" -rustler = { version = "0.36.1", path = "../rustler" } tempfile = "3.19.1" diff --git a/cargo-rustler/src/main.rs b/cargo-rustler/src/main.rs index ec7cebfd..3ddd6b91 100644 --- a/cargo-rustler/src/main.rs +++ b/cargo-rustler/src/main.rs @@ -4,6 +4,7 @@ mod fake_symbols; mod nif; mod nif_elixir; mod nif_erlang; +mod nif_types; mod rust_build; use std::path::PathBuf; diff --git a/cargo-rustler/src/nif.rs b/cargo-rustler/src/nif.rs index 44f3a89d..ed6cd7fe 100644 --- a/cargo-rustler/src/nif.rs +++ b/cargo-rustler/src/nif.rs @@ -1,5 +1,5 @@ +use crate::nif_types::ErlNifEntry; use libloading::{Library, Symbol}; -use rustler::sys::ErlNifEntry; use std::ffi::CStr; use std::path::{Path, PathBuf}; @@ -28,12 +28,18 @@ unsafe fn maybe_call_nif_init( unsafe fn maybe_call_nif_init( lib: &Library, ) -> Result<*const ErlNifEntry, Box> { - use rustler_sys::TWinDynNifCallbacks; - static NULL_CALLBACKS: TWinDynNifCallbacks = TWinDynNifCallbacks {}; - let func: Symbol *const ErlNifEntry> = + static mut NULL_CALLBACKS: [usize; 1024] = [0; 1024]; + // enif_priv_data + NULL_CALLBACKS[0] = 0; + // enif_alloc + NULL_CALLBACKS[1] = crate::fake_symbols::enif_alloc as *mut c_void as usize; + // enif_free + NULL_CALLBACKS[2] = crate::fake_symbols::enif_free as *mut c_void as usize; + + let func: Symbol *const ErlNifEntry> = lib.get(b"nif_init")?; - func(&NULL_CALLBACKS) + Ok(func(std::ptr::addr_of_mut!(NULL_CALLBACKS))) } impl NifLibrary { diff --git a/cargo-rustler/src/nif_types.rs b/cargo-rustler/src/nif_types.rs new file mode 100644 index 00000000..3d5f8236 --- /dev/null +++ b/cargo-rustler/src/nif_types.rs @@ -0,0 +1,80 @@ +#![allow(clippy::missing_safety_doc)] +#![allow(clippy::upper_case_acronyms)] + +pub use std::ffi::{c_char, c_int, c_uint, c_void}; + +#[allow(non_camel_case_types)] +pub type size_t = usize; + +#[allow(non_camel_case_types)] +pub type ERL_NIF_UINT = size_t; + +#[allow(non_camel_case_types)] +pub type ERL_NIF_TERM = ERL_NIF_UINT; + +/// See [ErlNifEnv](http://www.erlang.org/doc/man/erl_nif.html#ErlNifEnv) in the Erlang docs. +#[derive(Debug)] +#[allow(missing_copy_implementations)] +#[repr(C)] +pub struct ErlNifEnv { + dummy: *mut c_void, // block automatic Send and Sync traits. Ref https://doc.rust-lang.org/beta/nomicon/send-and-sync.html +} + +// Ownership of an env may be safely transfers between threads, therefore ErlNifEnv is Send. +// This is the common use case for process independent environments created with enif_alloc_env(). +// ErlNifEnv is NOT Sync because it is thread unsafe. +unsafe impl Send for ErlNifEnv {} + +/// See [ErlNifFunc](http://www.erlang.org/doc/man/erl_nif.html#ErlNifFunc) in the Erlang docs. +// #[allow(missing_copy_implementations)] +#[derive(Debug)] +#[repr(C)] +pub struct ErlNifFunc { + pub name: *const c_char, + pub arity: c_uint, + pub function: unsafe extern "C" fn( + env: *mut ErlNifEnv, + argc: c_int, + argv: *const ERL_NIF_TERM, + ) -> ERL_NIF_TERM, + pub flags: c_uint, +} + +// #[allow(missing_copy_implementations)] +#[doc(hidden)] +#[derive(Debug)] +#[repr(C)] +#[allow(non_snake_case)] +pub struct ErlNifEntry { + pub major: c_int, + pub minor: c_int, + pub name: *const c_char, + pub num_of_funcs: c_int, + pub funcs: *const ErlNifFunc, + pub load: Option< + unsafe extern "C" fn( + env: *mut ErlNifEnv, + priv_data: *mut *mut c_void, + load_info: ERL_NIF_TERM, + ) -> c_int, + >, + pub reload: Option< + unsafe extern "C" fn( + env: *mut ErlNifEnv, + priv_data: *mut *mut c_void, + load_info: ERL_NIF_TERM, + ) -> c_int, + >, + pub upgrade: Option< + unsafe extern "C" fn( + env: *mut ErlNifEnv, + priv_data: *mut *mut c_void, + old_priv_data: *mut *mut c_void, + load_info: ERL_NIF_TERM, + ) -> c_int, + >, + pub unload: Option ()>, + pub vm_variant: *const c_char, + pub options: c_uint, // added in 2.7 + pub sizeof_ErlNifResourceTypeInit: usize, // added in 2.12 +} From 62e7c1fe7ce185e961bec06c81762cc20f21fbc2 Mon Sep 17 00:00:00 2001 From: Benedikt Reinartz Date: Tue, 22 Jul 2025 20:31:29 +0200 Subject: [PATCH 7/8] Fix clippy lints --- cargo-rustler/Cargo.toml | 3 +-- cargo-rustler/src/nif_elixir.rs | 4 ++-- cargo-rustler/src/nif_erlang.rs | 2 +- cargo-rustler/src/rust_build.rs | 6 +++--- 4 files changed, 7 insertions(+), 8 deletions(-) diff --git a/cargo-rustler/Cargo.toml b/cargo-rustler/Cargo.toml index c177c0eb..9c6add78 100644 --- a/cargo-rustler/Cargo.toml +++ b/cargo-rustler/Cargo.toml @@ -5,9 +5,8 @@ edition = "2021" metadata = { msrv = "1.77" } - [dependencies] -cargo_metadata = "0.19.2" +cargo_metadata = "0.21.0" clap = { version = "4.5", features = [ "derive" ] } libloading = "0.8" tempfile = "3.19.1" diff --git a/cargo-rustler/src/nif_elixir.rs b/cargo-rustler/src/nif_elixir.rs index db106af8..19cc8037 100644 --- a/cargo-rustler/src/nif_elixir.rs +++ b/cargo-rustler/src/nif_elixir.rs @@ -51,9 +51,9 @@ fn string_to_elixir_atom(s: &str, func: bool) -> String { } if needs_quotes { - format!(":\"{}\"", output).to_string() + format!(":\"{output}\"").to_string() } else if !func { - format!(":{}", output).to_string() + format!(":{output}").to_string() } else { output } diff --git a/cargo-rustler/src/nif_erlang.rs b/cargo-rustler/src/nif_erlang.rs index 5a106de4..2f368996 100644 --- a/cargo-rustler/src/nif_erlang.rs +++ b/cargo-rustler/src/nif_erlang.rs @@ -96,7 +96,7 @@ fn string_to_erlang_atom(input: &str) -> String { } if needs_quotes { - format!("'{}'", output).to_string() + format!("'{output}'").to_string() } else { output } diff --git a/cargo-rustler/src/rust_build.rs b/cargo-rustler/src/rust_build.rs index 0df199de..a74ce354 100644 --- a/cargo-rustler/src/rust_build.rs +++ b/cargo-rustler/src/rust_build.rs @@ -46,7 +46,7 @@ pub fn build(args: &BuildArgs) -> Vec { let output = command.wait().expect("Couldn't get cargo's exit status"); if !output.success() { - panic!("Cargo build failed with status: {}", output); + panic!("Cargo build failed with status: {output}"); } artifacts @@ -65,10 +65,10 @@ pub fn build(args: &BuildArgs) -> Vec { } // Stripping the "lib" prefix simplifies the load_nif call as it can be the same on all platforms - let output_name = format!("{}.{}", name, ext); + let output_name = format!("{name}.{ext}"); let destination = out_dir.join(&output_name); - println!("Copying artifact from {:?} to {:?}", filename, destination,); + println!("Copying artifact from {filename:?} to {destination:?}",); // Copy the artifact to the output directory fs::copy(filename, &destination).expect("Failed to copy artifact"); From c737084d8d7f63617e4fe2465f39e180891bb69c Mon Sep 17 00:00:00 2001 From: Benedikt Reinartz Date: Tue, 22 Jul 2025 20:35:40 +0200 Subject: [PATCH 8/8] Adjust msrv setting --- .clippy.toml | 1 - cargo-rustler/Cargo.toml | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) delete mode 100644 .clippy.toml diff --git a/.clippy.toml b/.clippy.toml deleted file mode 100644 index 5d37e5d8..00000000 --- a/.clippy.toml +++ /dev/null @@ -1 +0,0 @@ -msrv = "1.70" diff --git a/cargo-rustler/Cargo.toml b/cargo-rustler/Cargo.toml index 9c6add78..4ed4571d 100644 --- a/cargo-rustler/Cargo.toml +++ b/cargo-rustler/Cargo.toml @@ -2,8 +2,8 @@ name = "cargo-rustler" version = "0.1.0" edition = "2021" +rust-version = "1.77" -metadata = { msrv = "1.77" } [dependencies] cargo_metadata = "0.21.0"