Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

NIF Inspection Tool #614

Draft
wants to merge 3 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ members = [
"rustler_tests/native/rustler_serde_test",
"rustler_tests/native/dynamic_load",
"rustler_tests/native/rustler_compile_tests",
"rustler_benchmarks/native/benchmark",
"rustler_benchmarks/native/benchmark", "rustler_tool",
]
default-members = [
"rustler",
Expand Down
2 changes: 2 additions & 0 deletions rustler_tool/.cargo/config.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
[target.x86_64-unknown-linux-gnu]
rustflags = [ "-C", "link-args=-Wl,-export-dynamic" ]
9 changes: 9 additions & 0 deletions rustler_tool/Cargo.toml
Original file line number Diff line number Diff line change
@@ -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" }
185 changes: 185 additions & 0 deletions rustler_tool/src/fake_symbols.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,185 @@
use std::alloc::{alloc, dealloc, Layout};

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]
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;
#[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_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_keep_resource: 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_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;
#[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_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;
64 changes: 64 additions & 0 deletions rustler_tool/src/main.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
#[cfg(unix)]
mod fake_symbols;
mod nif;
mod nif_elixir;
mod nif_erlang;

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<Commands>,
}

#[derive(clap::ValueEnum, Clone, Default, Debug)]
enum OutputFormat {
#[default]
Bare,
Erlang,
Elixir,
}

#[derive(Subcommand)]
enum Commands {
/// does testing things
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, format }) => {
let lib = NifLibrary::load(path).unwrap();

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 => {
panic!("No command given")
}
}
}
69 changes: 69 additions & 0 deletions rustler_tool/src/nif.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
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<Nif>,
}

#[cfg(unix)]
unsafe fn maybe_call_nif_init(
lib: &Library,
) -> Result<*const ErlNifEntry, Box<dyn std::error::Error>> {
let func: Symbol<unsafe extern "C" fn() -> *const ErlNifEntry> = lib.get(b"nif_init")?;

Ok(func())
}

#[cfg(windows)]
unsafe fn maybe_call_nif_init(
lib: &Library,
) -> Result<*const ErlNifEntry, Box<dyn std::error::Error>> {
use rustler_sys::TWinDynNifCallbacks;
static NULL_CALLBACKS: TWinDynNifCallbacks = TWinDynNifCallbacks {};
let func: Symbol<unsafe extern "C" fn(*mut TWinDynNifCallbacks) -> *const ErlNifEntry> =
lib.get(b"nif_init")?;

func(&NULL_CALLBACKS)
}

impl NifLibrary {
pub fn load(path: &Path) -> Result<NifLibrary, Box<dyn std::error::Error>> {
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 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,
})
})
.collect();

nifs.sort_by_key(|x| x.name.clone());

Ok(NifLibrary {
path: path.to_path_buf(),
name,
nifs,
})
}
}
}
Loading
Loading