Skip to content

Commit

Permalink
Parses object size
Browse files Browse the repository at this point in the history
  • Loading branch information
ultimaweapon committed Oct 16, 2024
1 parent 13f86f8 commit dddae2b
Show file tree
Hide file tree
Showing 4 changed files with 149 additions and 48 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# cppbind

This is a Rust crate to generate binding to C++ functions and methods with zero overhead. It work by generating a FFI function with a symbol matching with the C++ side. Thus, allows you to call those C++ functions and methods directly from Rust without any overhead the same as C++ call themself.
This is a Rust crate to generate binding to C++ functions and methods with **zero overhead**. It works by generate FFI functions with symbol matching with the C++ side. Thus, allows you to call those C++ functions and methods directly from Rust without any overhead the same as C++ call themself. It also **allow you to construct a C++ object directly on Rust stack**.

The goal of this crate is to allows efficient integration between Rust and C++, which mean most of the generated code will be unsafe. You need to know how the C++ code you are going to use is working otherwise you will be hit by undefined behaviors.

Expand Down
5 changes: 4 additions & 1 deletion example/src/main.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
use cppbind::cpp;

fn main() {}
fn main() {
let mut v1 = class1_memory::new();
let v1 = unsafe { class1::new1(&mut v1) };
}

cpp! {
class class1 {
Expand Down
47 changes: 45 additions & 2 deletions macros/src/cpp.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,26 @@ pub fn render(items: Declarations) -> syn::Result<TokenStream> {
fn render_class(item: Class) -> syn::Result<TokenStream> {
// Get metadata.
let class = item.name;
let meta = match META.get_type(class.to_string()) {
let name = class.to_string();
let meta = match META.get_type(&name) {
Some(v) => v,
None => return Err(Error::new_spanned(class, "type_info not found")),
None => {
return Err(Error::new_spanned(
class,
format_args!("cppbind::type_info<{name}> not found"),
))
}
};

// Get object size.
let size = match meta.size {
Some(v) => v,
None => {
return Err(Error::new_spanned(
class,
format_args!("cppbind::type_info<{name}>::size not found"),
));
}
};

// Render constructors.
Expand All @@ -46,6 +63,12 @@ fn render_class(item: Class) -> syn::Result<TokenStream> {
}

// Compose.
let mem = if name.chars().next().unwrap().is_uppercase() {
format_ident!("{name}Memory")
} else {
format_ident!("{name}_memory")
};

Ok(quote! {
#[allow(non_camel_case_types)]
pub struct #class<T> {
Expand All @@ -56,6 +79,26 @@ fn render_class(item: Class) -> syn::Result<TokenStream> {
impl<T: ::cppbind::Memory<Class = Self>> #class<T> {
#impls
}

#[allow(non_camel_case_types)]
#[repr(transparent)]
pub struct #mem([::std::mem::MaybeUninit<u8>; #size]);

impl #mem {
pub const fn new() -> Self {
Self([const { ::std::mem::MaybeUninit::uninit() }; #size])
}
}

impl ::cppbind::Memory for &mut #mem {
type Class = #class<Self>;
}

impl Default for #mem {
fn default() -> Self {
Self::new()
}
}
})
}

Expand Down
143 changes: 99 additions & 44 deletions macros/src/meta.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ use memmap2::Mmap;
use object::read::archive::ArchiveFile;
use object::read::elf::ElfFile64;
use object::read::macho::MachOFile64;
use object::{Endianness, LittleEndian, Object, ObjectSymbol, SymbolIndex};
use object::{Endianness, LittleEndian, Object, ObjectSection, ObjectSymbol, SymbolIndex};
use std::collections::HashMap;
use std::fs::File;
use std::path::Path;
Expand Down Expand Up @@ -61,7 +61,7 @@ impl Metadata {
};

if let Err(e) = r {
return Err(MetadataError::ParseSymbolFailed(name.into_owned(), e));
return Err(MetadataError::ParseObjectFailed(name.into_owned(), e));
}
}

Expand All @@ -72,55 +72,94 @@ impl Metadata {
self.types.get(name.as_ref())
}

fn parse_obj<'a>(&mut self, obj: impl Object<'a>) -> Result<(), SymbolError> {
fn parse_obj<'a>(&mut self, obj: impl Object<'a>) -> Result<(), ObjectError> {
// Parse symbols.
for sym in obj.symbols() {
use std::collections::hash_map::Entry;
let index = sym.index();

// Get symbol name.
let raw = match sym.name_bytes() {
Ok(v) => v,
Err(e) => return Err(SymbolError::GetNameFailed(sym.index(), e)),
};
self.parse_sym(&obj, sym)
.map_err(|e| ObjectError::ParseSymbolFailed(index, e))?;
}

// Parse name.
let addr = sym.address();
let size = sym.size();
let sym = match Symbol::parse(raw) {
Ok(v) => v,
Err(_) => continue, // Ignore unknown symbol.
};
Ok(())
}

// Check namespace.
let mut iter = sym.name().iter();
fn parse_sym<'a>(
&mut self,
obj: &impl Object<'a>,
sym: impl ObjectSymbol<'a>,
) -> Result<(), SymbolError> {
use std::collections::hash_map::Entry;

// Get symbol name.
let index = sym.index();
let raw = match sym.name_bytes() {
Ok(v) => v,
Err(e) => return Err(SymbolError::GetNameFailed(index, e)),
};

if !iter
.next()
.is_some_and(|v| *v == Segment::Ident("cppbind".into()))
{
continue;
}
// Get section index.
let section = match sym.section_index() {
Some(v) => v,
None => return Ok(()),
};

// Check if type_info.
if !iter
.next()
.is_some_and(|v| *v == Segment::Ident("type_info".into()))
{
return Err(SymbolError::UnknownCppbindSymbol);
}
// Parse name.
let off: usize = sym.address().try_into().unwrap();
let len: usize = sym.size().try_into().unwrap();
let sym = match Symbol::parse(raw) {
Ok(v) => v,
Err(_) => return Ok(()), // Ignore unknown symbol.
};

// Get class name.
let class = iter.next().ok_or(SymbolError::UnknownCppbindSymbol)?;
let class = match class {
Segment::TemplateArg(TemplateArg::Ident(v)) => v,
_ => return Err(SymbolError::UnknownCppbindSymbol),
};
// Check namespace.
let mut iter = sym.name().iter();

// Get TypeInfo.
let info = match self.types.entry(class.as_ref().to_owned()) {
Entry::Occupied(e) => e.into_mut(),
Entry::Vacant(e) => e.insert(TypeInfo::default()),
};
if !iter
.next()
.is_some_and(|v| *v == Segment::Ident("cppbind".into()))
{
return Ok(());
}

// Check if type_info.
if !iter
.next()
.is_some_and(|v| *v == Segment::Ident("type_info".into()))
{
return Err(SymbolError::UnknownCppbindSymbol);
}

// Get class name.
let class = iter.next().ok_or(SymbolError::UnknownCppbindSymbol)?;
let class = match class {
Segment::TemplateArg(TemplateArg::Ident(v)) => v,
_ => return Err(SymbolError::UnknownCppbindSymbol),
};

// Get TypeInfo.
let info = match self.types.entry(class.as_ref().to_owned()) {
Entry::Occupied(e) => e.into_mut(),
Entry::Vacant(e) => e.insert(TypeInfo::default()),
};

// Check info type.
let ty = iter.next().ok_or(SymbolError::UnknownCppbindSymbol)?;
let section = obj
.section_by_index(section)
.map_err(|e| SymbolError::GetSectionFailed(index, e))?;
let section = section
.data()
.map_err(|e| SymbolError::GetSectionDataFailed(index, e))?;

if *ty == Segment::Ident("size".into()) {
info.size = section
.get(off..(off + len))
.map(|v| usize::from_ne_bytes(v.try_into().unwrap()))
.ok_or_else(|| SymbolError::GetDataFailed(index))
.map(Some)?;
} else {
return Err(SymbolError::UnknownCppbindSymbol);
}

Ok(())
Expand Down Expand Up @@ -151,8 +190,15 @@ pub enum MetadataError {
#[error("couldn't parse {0}")]
ParseMemberFailed(String, #[source] object::read::Error),

#[error("couldn't parse a symbol on {0}")]
ParseSymbolFailed(String, #[source] SymbolError),
#[error("couldn't parse {0}")]
ParseObjectFailed(String, #[source] ObjectError),
}

/// Represents an error when [`Metadata`] fails to parse an object file.
#[derive(Debug, Error)]
pub enum ObjectError {
#[error("couldn't parse a symbol #{0}")]
ParseSymbolFailed(SymbolIndex, #[source] SymbolError),
}

/// Represents an error when [`Metadata`] fails to parse a symbol.
Expand All @@ -163,4 +209,13 @@ pub enum SymbolError {

#[error("unknown symbol on cppbind namespace")]
UnknownCppbindSymbol,

#[error("couldn't get section for symbol #{0}")]
GetSectionFailed(SymbolIndex, #[source] object::read::Error),

#[error("couldn't get section data for symbol #{0}")]
GetSectionDataFailed(SymbolIndex, #[source] object::read::Error),

#[error("couldn't get data of symbol #{0}")]
GetDataFailed(SymbolIndex),
}

0 comments on commit dddae2b

Please sign in to comment.