├── .gitignore ├── .travis.yml ├── Cargo.toml ├── README.md ├── build.rs ├── netmap_test ├── Cargo.toml ├── build.rs └── src │ └── main.rs └── src ├── lib.rs ├── netmap.rs ├── netmap_user.rs ├── netmap_util.rs └── netmap_with_libs.rs /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | /Cargo.lock 3 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: rust 2 | cache: 3 | directories: 4 | - $HOME/.cargo 5 | rust: 6 | - stable 7 | - beta 8 | - nightly 9 | notifications: 10 | irc: 11 | channels: 12 | - "chat.freenode.net#libpnet" 13 | use_notice: true 14 | script: 15 | - | 16 | git clone --depth=1 https://github.com/luigirizzo/netmap && 17 | cargo build --verbose && cargo test --verbose && 18 | cargo build --features netmap_with_libs && cargo test --features netmap_with_libs 19 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | 3 | name = "netmap_sys" 4 | version = "0.1.4" 5 | authors = ["Robert Clipsham "] 6 | homepage = "http://info.iet.unipi.it/~luigi/netmap/" 7 | repository = "https://github.com/libpnet/rust-netmap" 8 | license = "BSD-2-Clause" 9 | description = "Bindings to netmap - the fast packet I/O framework" 10 | readme = "README.md" 11 | keywords = ["networking", "packet", "bindings"] 12 | links = "rust_netmap_user" 13 | build = "build.rs" 14 | 15 | [lib] 16 | 17 | name = "netmap_sys" 18 | crate_type = [ "rlib", "dylib" ] 19 | 20 | [features] 21 | 22 | default = [] 23 | netmap_with_libs = [] 24 | 25 | [dependencies] 26 | libc = "0.2" 27 | 28 | [build-dependencies] 29 | cc = "1.0.35" 30 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # netmap_sys 2 | 3 | Rust bindings to [netmap](http://info.iet.unipi.it/~luigi/netmap/), currently 4 | at version 3.17 (Linux). 5 | 6 | ## Usage 7 | 8 | To use within your own project, add: 9 | 10 | ``` 11 | [dependencies.netmap_sys] 12 | version = "0.1.4" 13 | # Uncomment this line where you wish to use features guarded by the 14 | # NETMAP_WITH_LIBS macro in C. 15 | #features = "netmap_with_libs" 16 | ``` 17 | 18 | To your Cargo.toml. 19 | 20 | ## Troubleshooting 21 | 22 | ### missing rust_netmap_user 23 | 24 | If you get an error containing the message: 25 | 26 | ``` 27 | error: could not find native static library `rust_netmap_user`, perhaps an -L flag is missing? 28 | ``` 29 | 30 | You should check the following things: 31 | 32 | 1. You have gcc / clang installed 33 | 2. Make sure that `/usr/include/net/netmap.h` and 34 | `/usr/include/net/netmap_user.h` both exist. If they do not, you should 35 | check your netmap installation. You can either manually add these files or 36 | symlinks to the, or change the paths searched in `build.rs`. 37 | 3. If you still have issues, please file an issue in the bug tracker, along 38 | with the output of `cargo build -v`, your operating system and distribution, 39 | how you installed netmap, and the output of `clang -DNETMAP_WITH_LIBS 40 | -Dstatic= -Dinline= -x c -fPIC -O2 -c /usr/include/net/netmap_user.h -o 41 | $(mktemp)`. 42 | -------------------------------------------------------------------------------- /build.rs: -------------------------------------------------------------------------------- 1 | // netmap doesn't provide these functions as a library, so we cheat, to save porting them manually 2 | // to Rust. This is a very ugly hack. 3 | extern crate cc; 4 | use std::env; 5 | use std::io::prelude::*; 6 | use std::fs; 7 | use std::path::Path; 8 | 9 | fn main() { 10 | if let Some(_) = env::var_os("CARGO_FEATURE_NETMAP_WITH_LIBS") { 11 | let out_dir = env::var("OUT_DIR").unwrap(); 12 | let tmp_path = Path::new(&out_dir).join("netmap.c"); 13 | let mut tmp = fs::File::create(&tmp_path).unwrap(); 14 | 15 | tmp.write_all(b"#include \n\ 16 | #include \n\ 17 | typedef unsigned int u_int; 18 | typedef unsigned long u_long; 19 | typedef unsigned char u_char; 20 | #include \n").unwrap(); 21 | cc::Build::new() 22 | .file(&tmp_path) 23 | .define("NETMAP_WITH_LIBS", None) 24 | .define("static", Some("")) 25 | .define("inline", Some("")) 26 | .include("netmap/sys") 27 | .compile("librust_netmap_user.a"); 28 | fs::remove_file(&tmp_path).unwrap(); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /netmap_test/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "netmap_test" 3 | version = "0.1.0" 4 | authors = ["Alexander Polyakov "] 5 | build = "build.rs" 6 | 7 | [dependencies.netmap_sys] 8 | path = ".." 9 | features = [ "netmap_with_libs" ] 10 | 11 | [dependencies] 12 | libc = "0.2" 13 | 14 | [build-dependencies] 15 | ctest = { git = "https://github.com/libpnet/ctest", branch = "nowerror" } 16 | -------------------------------------------------------------------------------- /netmap_test/build.rs: -------------------------------------------------------------------------------- 1 | extern crate ctest; 2 | 3 | fn main() { 4 | let mut cfg = ctest::TestGenerator::new(); 5 | 6 | cfg.header("sys/time.h") 7 | .header("sys/ioctl.h") 8 | .header("net/if.h") 9 | .header("net/netmap.h") 10 | .header("net/netmap_user.h"); 11 | 12 | cfg.type_name(|ty, is_struct| { 13 | if is_struct || ty == "timeval" { 14 | format!("struct {}", ty) 15 | } else { 16 | ty.to_string() 17 | } 18 | }); 19 | 20 | cfg.include("netmap/sys"); 21 | cfg.include("/usr/include"); 22 | 23 | cfg.generate("../src/lib.rs", "all.rs"); 24 | } 25 | -------------------------------------------------------------------------------- /netmap_test/src/main.rs: -------------------------------------------------------------------------------- 1 | #![allow(bad_style)] 2 | 3 | extern crate netmap_sys; 4 | extern crate libc; 5 | 6 | //use netmap_sys::*; 7 | use netmap_sys::netmap::*; 8 | use netmap_sys::netmap_user::*; 9 | use libc::*; 10 | 11 | include!(concat!(env!("OUT_DIR"), "/all.rs")); 12 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | #![allow(bad_style)] 2 | extern crate libc; 3 | 4 | pub mod netmap; 5 | pub mod netmap_user; 6 | mod netmap_util; 7 | 8 | #[cfg(feature = "netmap_with_libs")] 9 | mod netmap_with_libs; 10 | -------------------------------------------------------------------------------- /src/netmap.rs: -------------------------------------------------------------------------------- 1 | use libc::{c_int, c_uint, c_ulong, c_char, timeval, ssize_t, size_t, IF_NAMESIZE}; 2 | 3 | pub const IFNAMSIZ: usize = IF_NAMESIZE; 4 | 5 | pub const NETMAP_API: c_int = 11; 6 | pub const NETMAP_MIN_API: c_int = 11; 7 | pub const NETMAP_MAX_API: c_int = 15; 8 | 9 | pub const NM_CACHE_ALIGN: c_int = 128; 10 | 11 | #[repr(C)] 12 | #[derive(Clone, Copy)] 13 | pub struct netmap_slot { 14 | pub buf_idx: u32, 15 | pub len: u16, 16 | pub flags: u16, 17 | pub ptr: u64, 18 | } 19 | 20 | pub const NS_BUF_CHANGED: u16 = 0x0001; 21 | pub const NS_REPORT: u16 = 0x0002; 22 | pub const NS_FORWARD: u16 = 0x0004; 23 | pub const NS_NO_LEARN: u16 = 0x0008; 24 | pub const NS_INDIRECT: u16 = 0x0010; 25 | pub const NS_MOREFRAG: u16 = 0x0020; 26 | 27 | pub const NS_PORT_SHIFT: c_int = 8; 28 | pub const NS_PORT_MASK: c_int = (0xff << NS_PORT_SHIFT); 29 | 30 | // FIXME NS_RFRAGS 31 | 32 | #[repr(C)] 33 | #[derive(Copy)] 34 | pub struct netmap_ring { 35 | pub buf_ofs: i64, 36 | pub num_slots: u32, 37 | pub nr_buf_size: u32, 38 | pub ringid: u16, 39 | pub dir: u16, 40 | 41 | pub head: u32, 42 | pub cur: u32, 43 | pub tail: u32, 44 | 45 | pub flags: u32, 46 | 47 | pub ts: timeval, 48 | 49 | _padding: [u8; 72], 50 | pub sem: [u8; 128], // FIXME __attribute__((__aligned__(NM_CACHE_ALIGN))) 51 | 52 | pub slot: [netmap_slot; 0], // FIXME Check struct size/field alignment 53 | } 54 | 55 | impl Clone for netmap_ring { 56 | fn clone(&self) -> netmap_ring { 57 | *self 58 | } 59 | } 60 | 61 | pub const NR_TIMESTAMP: u32 = 0x0002; 62 | pub const NR_FORWARD: u32 = 0x0004; 63 | 64 | #[repr(C)] 65 | #[derive(Clone, Copy)] 66 | pub struct netmap_if { 67 | pub ni_name: [c_char; IFNAMSIZ], 68 | pub ni_version: u32, 69 | pub ni_flags: u32, 70 | 71 | pub ni_tx_rings: u32, 72 | pub ni_rx_rings: u32, 73 | 74 | pub ni_bufs_head: u32, 75 | pub ni_host_tx_rings: u32, /* number of SW tx rings */ 76 | pub ni_host_rx_rings: u32, /* number of SW tx rings */ 77 | pub ni_spare1: [u32; 3], 78 | 79 | pub ring_ofs: [ssize_t; 0], // FIXME Check this is right, see above 80 | } 81 | 82 | pub const NI_PRIV_MEM: c_int = 0x1; 83 | 84 | #[repr(C)] 85 | #[derive(Clone, Copy)] 86 | pub struct nmreq { 87 | pub nr_name: [c_char; IFNAMSIZ], 88 | pub nr_version: u32, 89 | pub nr_offset: u32, 90 | pub nr_memsize: u32, 91 | pub nr_tx_slots: u32, 92 | pub nr_rx_slots: u32, 93 | pub nr_tx_rings: u16, 94 | pub nr_rx_rings: u16, 95 | 96 | pub nr_ringid: u16, 97 | 98 | pub nr_cmd: u16, 99 | pub nr_arg1: u16, 100 | pub nr_arg2: u16, 101 | pub nr_arg3: u32, 102 | pub nr_flags: u32, 103 | 104 | pub spare2: [u32; 1], 105 | } 106 | 107 | pub const NETMAP_HW_RING: c_int = 0x4000; 108 | pub const NETMAP_SW_RING: c_int = 0x2000; 109 | 110 | pub const NETMAP_RING_MASK: c_int = 0x0fff; 111 | 112 | pub const NETMAP_NO_TX_POLL: c_int = 0x1000; 113 | 114 | pub const NETMAP_DO_RX_POLL: c_int = 0x8000; 115 | 116 | pub const NETMAP_BDG_ATTACH: c_int = 1; 117 | pub const NETMAP_BDG_DETACH: c_int = 2; 118 | pub const NETMAP_BDG_REGOPS: c_int = 3; 119 | pub const NETMAP_BDG_LIST: c_int = 4; 120 | pub const NETMAP_BDG_VNET_HDR: c_int = 5; 121 | pub const NETMAP_BDG_OFFSET: c_int = NETMAP_BDG_VNET_HDR; 122 | pub const NETMAP_BDG_NEWIF: c_int = 6; 123 | pub const NETMAP_BDG_DELIF: c_int = 7; 124 | 125 | pub const NETMAP_BDG_HOST: c_int = 1; 126 | 127 | pub const NR_REG_MASK: c_int = 0xf; 128 | 129 | pub const NR_REG_DEFAULT: u32 = 0; 130 | pub const NR_REG_ALL_NIC: u32 = 1; 131 | pub const NR_REG_SW: u32 = 2; 132 | pub const NR_REG_NIC_SW: u32 = 3; 133 | pub const NR_REG_ONE_NIC: u32 = 4; 134 | pub const NR_REG_PIPE_MASTER: u32 = 5; 135 | pub const NR_REG_PIPE_SLAVE: u32 = 6; 136 | 137 | pub const NR_MONITOR_TX: u32 = 0x100; 138 | pub const NR_MONITOR_RX: u32 = 0x200; 139 | pub const NR_ZCOPY_MON: u32 = 0x400; 140 | pub const NR_EXCLUSIVE: u32 = 0x800; 141 | pub const NR_PTNETMAP_HOST: u32 = 0x1000; 142 | pub const NR_RX_RINGS_ONLY: u32 = 0x2000; 143 | pub const NR_TX_RINGS_ONLY: u32 = 0x4000; 144 | pub const NR_ACCEPT_VNET_HDR: u32 = 0x8000; 145 | 146 | #[cfg(target_os = "linux")] 147 | pub const NIOCGINFO: c_ulong = 3225184657; 148 | #[cfg(target_os = "linux")] 149 | pub const NIOCREGIF: c_ulong = 3225184658; 150 | #[cfg(target_os = "linux")] 151 | pub const NIOCTXSYNC: c_uint = 27028; 152 | #[cfg(target_os = "linux")] 153 | pub const NIOCRXSYNC: c_uint = 27029; 154 | #[cfg(target_os = "linux")] 155 | pub const NIOCCONFIG: c_ulong = 3239078294; 156 | 157 | #[cfg(target_os = "freebsd")] 158 | pub const NIOCGINFO: c_ulong = 3225184657; 159 | #[cfg(target_os = "freebsd")] 160 | pub const NIOCREGIF: c_ulong = 3225184658; 161 | #[cfg(target_os = "freebsd")] 162 | pub const NIOCTXSYNC: c_uint = 536897940; 163 | #[cfg(target_os = "freebsd")] 164 | pub const NIOCRXSYNC: c_uint = 536897941; 165 | #[cfg(target_os = "freebsd")] 166 | pub const NIOCCONFIG: c_ulong = 3239078294; 167 | 168 | #[inline(always)] 169 | pub unsafe fn nm_ring_empty(ring: *mut netmap_ring) -> bool { 170 | (*ring).head == (*ring).tail 171 | } 172 | 173 | pub const NM_IFRDATA_LEN: usize = 256; 174 | 175 | #[repr(C)] 176 | #[derive(Copy)] 177 | pub struct nm_ifreq { 178 | pub nifr_name: [c_char; IFNAMSIZ], 179 | pub data: [c_char; NM_IFRDATA_LEN], 180 | } 181 | 182 | impl Clone for nm_ifreq { 183 | fn clone(&self) -> nm_ifreq { 184 | *self 185 | } 186 | } 187 | 188 | -------------------------------------------------------------------------------- /src/netmap_user.rs: -------------------------------------------------------------------------------- 1 | extern crate libc; 2 | use libc::{c_int, c_char, c_void}; 3 | 4 | use netmap::*; 5 | use netmap_util::{unlikely}; 6 | 7 | #[cfg(feature = "netmap_with_libs")] 8 | pub use netmap_with_libs::{nm_pkthdr, nm_stat, nm_desc, P2NMD, IS_NETMAP_DESC, NETMAP_FD, 9 | nm_pkt_copy, nm_cb_t, NM_OPEN_NO_MMAP, NM_OPEN_IFNAME, NM_OPEN_ARG1, 10 | NM_OPEN_ARG2, NM_OPEN_ARG3, NM_OPEN_RING_CFG, nm_open, nm_close, 11 | nm_inject, nm_dispatch, nm_nextpkt, nm_mmap}; 12 | 13 | 14 | #[inline(always)] 15 | pub unsafe fn _NETMAP_OFFSET(ptr: *mut U, offset: isize) -> *mut T { 16 | (((ptr as *mut c_char).offset(offset)) as *mut c_void) as *mut T 17 | } 18 | 19 | #[inline(always)] 20 | pub unsafe fn NETMAP_IF(_base: *mut U, _ofs: isize) -> *mut netmap_if { 21 | _NETMAP_OFFSET(_base, _ofs) 22 | } 23 | 24 | // FIXME It's possible the pointer arithmetic here uses the wrong integer types. 25 | #[inline(always)] 26 | pub unsafe fn NETMAP_TXRING(nifp: *mut netmap_if, index: isize) -> *mut netmap_ring { 27 | let ptr = (&mut (*nifp).ring_ofs as *mut [isize; 0]) as *mut isize; 28 | _NETMAP_OFFSET(nifp, *(ptr.offset(index) as *mut isize)) 29 | } 30 | 31 | #[inline(always)] 32 | pub unsafe fn NETMAP_RXRING(nifp: *mut netmap_if, index: isize) -> *mut netmap_ring { 33 | let ptr = (&mut (*nifp).ring_ofs as *mut [isize; 0]) as *mut isize; 34 | let rr = &mut (*nifp).ni_tx_rings; 35 | let r = *rr as isize; 36 | let a = index + r + 1; 37 | _NETMAP_OFFSET(nifp, *(ptr.offset(a) as *mut isize)) 38 | } 39 | 40 | #[inline(always)] 41 | pub unsafe fn NETMAP_BUF(ring: *mut netmap_ring, index: isize) -> *mut c_char { 42 | (ring as *mut c_char).offset((*ring).buf_ofs as isize + (index as isize * (*ring).nr_buf_size as isize)) 43 | } 44 | 45 | #[inline(always)] 46 | pub unsafe fn NETMAP_BUF_IDX(ring: *mut netmap_ring, buf: *mut c_char) -> usize { 47 | ((buf as *mut c_char).offset( -((ring as *mut c_char) as isize) ) 48 | .offset((*ring).buf_ofs as isize) as usize / (*ring).nr_buf_size as usize) 49 | } 50 | 51 | #[inline(always)] 52 | pub unsafe fn nm_ring_next(r: *mut netmap_ring, i: u32) -> u32 { 53 | if unlikely(i + 1 == (*r).num_slots) { 54 | 0 55 | } else { 56 | i + 1 57 | } 58 | } 59 | 60 | #[inline(always)] 61 | pub unsafe fn nm_ring_space(ring: *mut netmap_ring) -> u32 { 62 | let mut ret: c_int = ((*ring).tail - (*ring).head) as c_int; 63 | if ret < 0 { 64 | ret += (*ring).num_slots as c_int; 65 | } 66 | return ret as u32; 67 | } 68 | 69 | -------------------------------------------------------------------------------- /src/netmap_util.rs: -------------------------------------------------------------------------------- 1 | // FIXME replace with intrinsics 2 | #[cfg(feature = "netmap_with_libs")] 3 | #[inline(always)] 4 | pub fn likely(t: t) -> t { 5 | t 6 | } 7 | #[inline(always)] 8 | pub fn unlikely(t: t) -> t { 9 | t 10 | } 11 | 12 | -------------------------------------------------------------------------------- /src/netmap_with_libs.rs: -------------------------------------------------------------------------------- 1 | use libc::{c_int, c_uint, c_char, c_uchar, c_void, size_t, timeval}; 2 | use netmap::*; 3 | use netmap_util::{likely, unlikely}; 4 | 5 | extern { 6 | fn memcpy(dest: *mut c_void, src: *mut c_void, n: size_t) -> *mut c_void; 7 | } 8 | 9 | #[repr(C)] 10 | #[derive(Clone, Copy)] 11 | pub struct nm_pkthdr { 12 | pub ts: timeval, 13 | pub caplen: u32, 14 | pub len: u32, 15 | } 16 | 17 | #[repr(C)] 18 | #[derive(Clone, Copy)] 19 | pub struct nm_stat { 20 | pub ps_recv: c_uint, 21 | pub ps_drop: c_uint, 22 | pub ps_ifdrop: c_uint, 23 | } 24 | 25 | pub const NM_ERRBUF_SIZE: usize = 512; 26 | 27 | #[repr(C)] 28 | #[derive(Copy)] 29 | pub struct nm_desc { 30 | pub self_: *mut nm_desc, 31 | pub fd: c_int, 32 | pub mem: *mut c_void, 33 | pub memsize: u64, 34 | pub done_mmap: c_int, 35 | pub nifp: *mut netmap_if, 36 | pub first_tx_ring: u16, 37 | pub last_tx_ring: u16, 38 | pub cur_tx_ring: u16, 39 | pub first_rx_ring: u16, 40 | pub last_rx_ring: u16, 41 | pub cur_rx_ring: u16, 42 | pub req: nmreq, 43 | pub hdr: nm_pkthdr, 44 | 45 | pub some_ring: *const netmap_ring, 46 | pub buf_start: *const c_void, 47 | pub buf_end: *const c_void, 48 | pub snaplen: c_int, 49 | pub promisc: c_int, 50 | pub to_ms: c_int, 51 | pub errbuf: *mut c_char, 52 | 53 | pub if_flags: u32, 54 | pub if_reqcap: u32, 55 | pub if_curcap: u32, 56 | 57 | pub st: nm_stat, 58 | pub msg: [c_char; NM_ERRBUF_SIZE], 59 | } 60 | 61 | impl Clone for nm_desc { 62 | fn clone(&self) -> nm_desc { 63 | *self 64 | } 65 | } 66 | 67 | #[inline(always)] 68 | pub unsafe fn P2NMD(p: *mut T) -> *mut nm_desc { 69 | p as *mut nm_desc 70 | } 71 | 72 | #[inline(always)] 73 | pub unsafe fn IS_NETMAP_DESC(d: *mut nm_desc) -> bool { 74 | !d.is_null() && (*P2NMD(d)).self_ == P2NMD(d) 75 | } 76 | 77 | #[inline(always)] 78 | pub unsafe fn NETMAP_FD(d: *mut nm_desc) -> c_int { 79 | (*P2NMD(d)).fd 80 | } 81 | 82 | #[inline(always)] 83 | pub unsafe fn nm_pkt_copy(_src: *const c_void, _dst: *mut c_void, mut l: c_int) { 84 | let mut src = _src as *const u64; 85 | let mut dst = _dst as *mut u64; 86 | 87 | if unlikely(l > 1024) { 88 | memcpy(dst as *mut c_void, src as *mut c_void, l as usize); 89 | return; 90 | } 91 | 92 | while likely(l > 0) { 93 | *dst = *src; dst = dst.offset(1); src = src.offset(1); 94 | *dst = *src; dst = dst.offset(1); src = src.offset(1); 95 | *dst = *src; dst = dst.offset(1); src = src.offset(1); 96 | *dst = *src; dst = dst.offset(1); src = src.offset(1); 97 | *dst = *src; dst = dst.offset(1); src = src.offset(1); 98 | *dst = *src; dst = dst.offset(1); src = src.offset(1); 99 | *dst = *src; dst = dst.offset(1); src = src.offset(1); 100 | *dst = *src; dst = dst.offset(1); src = src.offset(1); 101 | l -= 64; 102 | } 103 | } 104 | 105 | pub type nm_cb_t = extern fn(*mut c_uchar, *const nm_pkthdr, *const c_uchar) -> c_void; 106 | 107 | pub const NM_OPEN_NO_MMAP: c_int = 0x040000; 108 | pub const NM_OPEN_IFNAME: c_int = 0x080000; 109 | pub const NM_OPEN_ARG1: c_int = 0x100000; 110 | pub const NM_OPEN_ARG2: c_int = 0x200000; 111 | pub const NM_OPEN_ARG3: c_int = 0x400000; 112 | pub const NM_OPEN_RING_CFG: c_int = 0x800000; 113 | 114 | extern { 115 | pub fn nm_open(ifname: *const c_char, req: *const nmreq, 116 | new_flags: u64, arg: *const nm_desc) -> *mut nm_desc; 117 | pub fn nm_close(d: *mut nm_desc) -> c_int; 118 | pub fn nm_inject(d: *mut nm_desc, buf: *const c_void, size: size_t) -> c_int; 119 | pub fn nm_dispatch(d: *mut nm_desc, cnt: c_int, cb: nm_cb_t, arg: *mut c_uchar) -> c_int; 120 | pub fn nm_nextpkt(d: *mut nm_desc, hdr: *mut nm_pkthdr) -> *mut c_uchar; 121 | pub fn nm_mmap(d: *mut nm_desc, p: *const nm_desc) -> c_int; 122 | } 123 | 124 | #[test] 125 | fn check_linking() { 126 | use std::ptr::null; 127 | unsafe { nm_open(b"lo\0" as *const u8 as *const i8, null(), 0, null()) }; 128 | } 129 | --------------------------------------------------------------------------------