Skip to content

Commit e0c07eb

Browse files
committed
Add AbstractType derive macro
1 parent a1de5ca commit e0c07eb

File tree

5 files changed

+144
-0
lines changed

5 files changed

+144
-0
lines changed

rust/Cargo.lock

Lines changed: 23 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

rust/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ log = "0.4"
1313
libc = "0.2"
1414
rayon = { version = "1.8", optional = true }
1515
binaryninjacore-sys = { path = "binaryninjacore-sys" }
16+
binaryninja-derive = { path = "binaryninja-derive" }
1617

1718
[patch.crates-io]
1819
# Patched pdb crate to implement some extra structures

rust/binaryninja-derive/Cargo.toml

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
[package]
2+
name = "binaryninja-derive"
3+
version = "0.1.0"
4+
edition = "2021"
5+
6+
[dependencies]
7+
syn = "2.0"
8+
quote = "1"
9+
proc-macro2 = "1.0"
10+
proc-macro2-diagnostics = { version = "0.10", default-features = false }
11+
12+
[lib]
13+
proc-macro = true

rust/binaryninja-derive/src/lib.rs

Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
use proc_macro2::TokenStream;
2+
use proc_macro2_diagnostics::{Diagnostic, SpanDiagnosticExt};
3+
use quote::quote;
4+
use syn::spanned::Spanned;
5+
use syn::{parse_macro_input, Data, DeriveInput, Fields, FieldsNamed, Ident};
6+
7+
type Result<T> = std::result::Result<T, Diagnostic>;
8+
9+
#[proc_macro_derive(AbstractType)]
10+
pub fn abstract_type_derive(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
11+
let input = parse_macro_input!(input as DeriveInput);
12+
match impl_abstract_type(input) {
13+
Ok(tokens) => tokens.into(),
14+
Err(diag) => diag.emit_as_item_tokens().into(),
15+
}
16+
}
17+
18+
fn impl_abstract_type(ast: DeriveInput) -> Result<TokenStream> {
19+
let mut repr_c = false;
20+
for attr in ast.attrs {
21+
if attr.path().is_ident("repr") {
22+
let _ = attr.parse_nested_meta(|meta| {
23+
if meta.path.is_ident("c") {
24+
repr_c = true;
25+
}
26+
Ok(())
27+
});
28+
}
29+
}
30+
31+
if !repr_c {
32+
return Err(ast.ident.span().error("type must be `repr(C)`"));
33+
}
34+
35+
if !ast.generics.params.is_empty() {
36+
return Err(ast.generics.span().error("type must not be generic"));
37+
}
38+
39+
let ident = ast.ident;
40+
match ast.data {
41+
Data::Struct(s) => match s.fields {
42+
Fields::Named(fields) => Ok(impl_abstract_struct_type(ident, fields)),
43+
Fields::Unnamed(_) => Err(s
44+
.fields
45+
.span()
46+
.error("tuple structs are unsupported; struct must have named fields")),
47+
Fields::Unit => Err(ident
48+
.span()
49+
.error("unit structs are unsupported; provide at least one named field")),
50+
},
51+
Data::Enum(_) => todo!(),
52+
Data::Union(u) => Ok(impl_abstract_union_type(ident, u.fields)),
53+
}
54+
}
55+
56+
fn field_arguments(name: &Ident, fields: FieldsNamed) -> Vec<TokenStream> {
57+
fields
58+
.named
59+
.iter()
60+
.map(|field| {
61+
let ident = field.ident.as_ref().unwrap();
62+
let ty = &field.ty;
63+
quote! {
64+
&<#ty as ::binaryninja::types::AbstractType>::resolve_type(),
65+
stringify!(#ident),
66+
::std::mem::offset_of!(#name, #ident) as u64,
67+
false,
68+
::binaryninja::types::MemberAccess::NoAccess,
69+
::binaryninja::types::MemberScope::NoScope,
70+
}
71+
})
72+
.collect()
73+
}
74+
75+
fn impl_abstract_struct_type(name: Ident, fields: FieldsNamed) -> TokenStream {
76+
let args = field_arguments(&name, fields);
77+
quote! {
78+
impl ::binaryninja::types::AbstractType for #name {
79+
fn resolve_type() -> ::binaryninja::rc::Ref<::binaryninja::types::Type> {
80+
::binaryninja::types::Type::structure(
81+
&::binaryninja::types::Structure::builder()
82+
#(.insert(#args))*
83+
.finalize()
84+
)
85+
}
86+
}
87+
}
88+
}
89+
90+
fn impl_abstract_union_type(name: Ident, fields: FieldsNamed) -> TokenStream {
91+
let args = field_arguments(&name, fields);
92+
quote! {
93+
impl ::binaryninja::types::AbstractType for #name {
94+
fn resolve_type() -> ::binaryninja::rc::Ref<::binaryninja::types::Type> {
95+
::binaryninja::types::Type::structure(
96+
&::binaryninja::types::Structure::builder()
97+
#(.insert(#args))*
98+
.set_structure_type(
99+
::binaryninja::types::StructureType::UnionStructureType
100+
)
101+
.finalize()
102+
)
103+
}
104+
}
105+
}
106+
}

rust/src/types.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -691,6 +691,7 @@ impl Drop for TypeBuilder {
691691
//////////
692692
// Type
693693

694+
pub use binaryninja_derive::*;
694695
pub trait AbstractType {
695696
fn resolve_type() -> Ref<Type>;
696697
}

0 commit comments

Comments
 (0)