Skip to content

Commit

Permalink
Merge pull request #27 from GabrielMajeri/manual-bindings
Browse files Browse the repository at this point in the history
Use libc for `libdl` bindings on Linux
  • Loading branch information
fitzgen authored Sep 4, 2018
2 parents 01916f2 + f44ce9b commit adf1c11
Show file tree
Hide file tree
Showing 6 changed files with 113 additions and 77 deletions.
9 changes: 6 additions & 3 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,16 @@ repository = "gimli-rs/findshlibs"
[badges.travis-ci]
repository = "gimli-rs/findshlibs"

[build-dependencies.bindgen]
default-features = false
version = "0.38.0"
[build-dependencies]
cfg-if = "0.1.2"

[target.'cfg(target_os = "macos")'.build-dependencies]
bindgen = { version = "0.39.0", default-features = false }

[dependencies]
cfg-if = "0.1.2"
lazy_static = "1.0.0"
libc = "0.2.43"

[features]
nightly = []
72 changes: 31 additions & 41 deletions build.rs
Original file line number Diff line number Diff line change
@@ -1,47 +1,37 @@
extern crate bindgen;
#[macro_use]
extern crate cfg_if;

use std::env;
use std::path::PathBuf;
cfg_if! {
if #[cfg(target_os = "macos")] {
extern crate bindgen;

fn main() {
if cfg!(target_os = "linux") {
generate_linux_bindings();
} else if cfg!(target_os = "macos") {
generate_macos_bindings();
}
}
use std::env;
use std::path::PathBuf;

fn generate_linux_bindings() {
let bindings = bindgen::Builder::default()
.header("./src/linux/bindings.h")
.whitelist_function("dl_iterate_phdr")
.whitelist_type(r#"Elf\d*.*"#)
.whitelist_var("PT_.*")
.generate()
.expect("Should generate linux FFI bindings OK");
fn main() {
generate_macos_bindings();
}

let out_path = PathBuf::from(env::var("OUT_DIR").unwrap());
bindings
.write_to_file(out_path.join("linux_bindings.rs"))
.expect("Should write linux_bindings.rs OK");
}
fn generate_macos_bindings() {
let bindings = bindgen::Builder::default()
.header("./src/macos/bindings.h")
.whitelist_function("_dyld_.*")
.whitelist_type("mach_header.*")
.whitelist_type("load_command.*")
.whitelist_type("uuid_command.*")
.whitelist_type("segment_command.*")
.whitelist_var("MH_MAGIC.*")
.whitelist_var("LC_SEGMENT.*")
.whitelist_var("LC_UUID.*")
.generate()
.expect("Should generate macOS FFI bindings OK");

fn generate_macos_bindings() {
let bindings = bindgen::Builder::default()
.header("./src/macos/bindings.h")
.whitelist_function("_dyld_.*")
.whitelist_type("mach_header.*")
.whitelist_type("load_command.*")
.whitelist_type("uuid_command.*")
.whitelist_type("segment_command.*")
.whitelist_var("MH_MAGIC.*")
.whitelist_var("LC_SEGMENT.*")
.whitelist_var("LC_UUID.*")
.generate()
.expect("Should generate macOS FFI bindings OK");

let out_path = PathBuf::from(env::var("OUT_DIR").unwrap());
bindings
.write_to_file(out_path.join("macos_bindings.rs"))
.expect("Should write macos_bindings.rs OK");
let out_path = PathBuf::from(env::var("OUT_DIR").unwrap());
bindings
.write_to_file(out_path.join("macos_bindings.rs"))
.expect("Should write macos_bindings.rs OK");
}
} else {
fn main() {}
}
}
3 changes: 3 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,9 @@ extern crate cfg_if;
#[macro_use]
extern crate lazy_static;

#[cfg(target_os = "linux")]
extern crate libc;

use std::ffi::CStr;
use std::fmt::{self, Debug};
use std::ptr;
Expand Down
2 changes: 0 additions & 2 deletions src/linux/bindings.h

This file was deleted.

5 changes: 0 additions & 5 deletions src/linux/bindings.rs

This file was deleted.

99 changes: 73 additions & 26 deletions src/linux/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,19 +6,19 @@ use super::SharedLibrary as SharedLibraryTrait;

use std::any::Any;
use std::ffi::CStr;
use std::fmt;
use std::isize;
use std::marker::PhantomData;
use std::os::raw;
use std::panic;
use std::slice;

mod bindings;
use libc;

cfg_if! {
if #[cfg(target_pointer_width = "32")] {
type Phdr = bindings::Elf32_Phdr;
type Phdr = libc::Elf32_Phdr;
} else if #[cfg(target_pointer_width = "64")] {
type Phdr = bindings::Elf64_Phdr;
type Phdr = libc::Elf64_Phdr;
} else {
// Unsupported.
}
Expand All @@ -37,19 +37,19 @@ impl<'a> SegmentTrait for Segment<'a> {
fn name(&self) -> &CStr {
unsafe {
match self.phdr.as_ref().unwrap().p_type {
bindings::PT_NULL => CStr::from_ptr("NULL\0".as_ptr() as _),
bindings::PT_LOAD => CStr::from_ptr("LOAD\0".as_ptr() as _),
bindings::PT_DYNAMIC => CStr::from_ptr("DYNAMIC\0".as_ptr() as _),
bindings::PT_INTERP => CStr::from_ptr("INTERP\0".as_ptr() as _),
bindings::PT_NOTE => CStr::from_ptr("NOTE\0".as_ptr() as _),
bindings::PT_SHLIB => CStr::from_ptr("SHLI\0".as_ptr() as _),
bindings::PT_PHDR => CStr::from_ptr("PHDR\0".as_ptr() as _),
bindings::PT_TLS => CStr::from_ptr("TLS\0".as_ptr() as _),
bindings::PT_NUM => CStr::from_ptr("NUM\0".as_ptr() as _),
bindings::PT_LOOS => CStr::from_ptr("LOOS\0".as_ptr() as _),
bindings::PT_GNU_EH_FRAME => CStr::from_ptr("GNU_EH_FRAME\0".as_ptr() as _),
bindings::PT_GNU_STACK => CStr::from_ptr("GNU_STACK\0".as_ptr() as _),
bindings::PT_GNU_RELRO => CStr::from_ptr("GNU_RELRO\0".as_ptr() as _),
libc::PT_NULL => CStr::from_ptr("NULL\0".as_ptr() as _),
libc::PT_LOAD => CStr::from_ptr("LOAD\0".as_ptr() as _),
libc::PT_DYNAMIC => CStr::from_ptr("DYNAMIC\0".as_ptr() as _),
libc::PT_INTERP => CStr::from_ptr("INTERP\0".as_ptr() as _),
libc::PT_NOTE => CStr::from_ptr("NOTE\0".as_ptr() as _),
libc::PT_SHLIB => CStr::from_ptr("SHLI\0".as_ptr() as _),
libc::PT_PHDR => CStr::from_ptr("PHDR\0".as_ptr() as _),
libc::PT_TLS => CStr::from_ptr("TLS\0".as_ptr() as _),
libc::PT_NUM => CStr::from_ptr("NUM\0".as_ptr() as _),
libc::PT_LOOS => CStr::from_ptr("LOOS\0".as_ptr() as _),
libc::PT_GNU_EH_FRAME => CStr::from_ptr("GNU_EH_FRAME\0".as_ptr() as _),
libc::PT_GNU_STACK => CStr::from_ptr("GNU_STACK\0".as_ptr() as _),
libc::PT_GNU_RELRO => CStr::from_ptr("GNU_RELRO\0".as_ptr() as _),
_ => CStr::from_ptr("(unknown segment type)\0".as_ptr() as _),
}
}
Expand All @@ -71,7 +71,6 @@ impl<'a> SegmentTrait for Segment<'a> {
}

/// An iterator of mapped segments in a shared library.
#[derive(Debug)]
pub struct SegmentIter<'a> {
inner: ::std::slice::Iter<'a, Phdr>,
}
Expand All @@ -88,8 +87,16 @@ impl<'a> Iterator for SegmentIter<'a> {
}
}

impl<'a> fmt::Debug for SegmentIter<'a> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let ref phdr = self.inner.as_slice()[0];

f.debug_struct("SegmentIter").field("phdr", &DebugPhdr(phdr)).finish()
}
}

/// A shared library on Linux.
#[derive(Debug, Clone, Copy)]
#[derive(Clone, Copy)]
pub struct SharedLibrary<'a> {
size: usize,
addr: *const u8,
Expand All @@ -102,11 +109,11 @@ struct IterState<F> {
panic: Option<Box<Any + Send>>,
}

const CONTINUE: raw::c_int = 0;
const BREAK: raw::c_int = 1;
const CONTINUE: libc::c_int = 0;
const BREAK: libc::c_int = 1;

impl<'a> SharedLibrary<'a> {
unsafe fn new(info: &'a bindings::dl_phdr_info, size: usize) -> Self {
unsafe fn new(info: &'a libc::dl_phdr_info, size: usize) -> Self {
SharedLibrary {
size: size,
addr: info.dlpi_addr as usize as *const _,
Expand All @@ -115,10 +122,10 @@ impl<'a> SharedLibrary<'a> {
}
}

unsafe extern "C" fn callback<F, C>(info: *mut bindings::dl_phdr_info,
unsafe extern "C" fn callback<F, C>(info: *mut libc::dl_phdr_info,
size: usize,
state: *mut raw::c_void)
-> raw::c_int
state: *mut libc::c_void)
-> libc::c_int
where F: FnMut(&Self) -> C,
C: Into<IterationControl>
{
Expand Down Expand Up @@ -176,7 +183,7 @@ impl<'a> SharedLibraryTrait for SharedLibrary<'a> {
};

unsafe {
bindings::dl_iterate_phdr(Some(Self::callback::<F, C>), &mut state as *mut _ as *mut _);
libc::dl_iterate_phdr(Some(Self::callback::<F, C>), &mut state as *mut _ as *mut _);
}

if let Some(panic) = state.panic {
Expand All @@ -185,6 +192,45 @@ impl<'a> SharedLibraryTrait for SharedLibrary<'a> {
}
}

impl<'a> fmt::Debug for SharedLibrary<'a> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "SharedLibrary {{ size: {:?}, addr: {:?}, ", self.size, self.addr)?;
write!(f, "name: {:?}, headers: [", self.name)?;

// Debug does not usually have a trailing comma in the list,
// last element must be formatted separately.
let l = self.headers.len();
self.headers[..(l - 1)].into_iter()
.map(|phdr| write!(f, "{:?}, ", &DebugPhdr(phdr)))
.collect::<fmt::Result>()?;

write!(f, "{:?}", &DebugPhdr(&self.headers[l - 1]))?;

write!(f, "] }}")
}
}

struct DebugPhdr<'a>(&'a Phdr);

impl<'a> fmt::Debug for DebugPhdr<'a> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let phdr = self.0;

// The layout is different for 32-bit vs 64-bit,
// but since the fields are the same, it shouldn't matter much.
f.debug_struct("Phdr")
.field("p_type", &phdr.p_type)
.field("p_flags", &phdr.p_flags)
.field("p_offset", &phdr.p_offset)
.field("p_vaddr", &phdr.p_vaddr)
.field("p_paddr", &phdr.p_paddr)
.field("p_filesz", &phdr.p_filesz)
.field("p_memsz", &phdr.p_memsz)
.field("p_align", &phdr.p_align)
.finish()
}
}

#[cfg(test)]
mod tests {
use linux;
Expand Down Expand Up @@ -227,6 +273,7 @@ mod tests {
#[test]
fn get_name() {
linux::SharedLibrary::each(|shlib| {
println!("{:?}", shlib);
let _ = shlib.name();
});
}
Expand Down

0 comments on commit adf1c11

Please sign in to comment.