11use proc_macro2:: TokenStream ;
22use proc_macro2_diagnostics:: { Diagnostic , SpanDiagnosticExt } ;
3- use quote:: quote;
3+ use quote:: { format_ident , quote} ;
44use syn:: spanned:: Spanned ;
55use syn:: {
6- parenthesized, parse_macro_input, token, Attribute , Data , DeriveInput , Field , Fields ,
7- FieldsNamed , Ident , LitInt , Path , Type , Variant ,
6+ parenthesized, parse_macro_input, token, Attribute , Data , DeriveInput , Expr , Field , Fields ,
7+ FieldsNamed , Ident , Lit , LitInt , Path , Type , Variant ,
88} ;
99
1010type Result < T > = std:: result:: Result < T , Diagnostic > ;
1111
1212struct AbstractField {
1313 ty : Type ,
14+ width : Option < Type > ,
1415 ident : Ident ,
1516 named : bool ,
16- pointer : bool ,
1717}
1818
1919impl AbstractField {
@@ -22,16 +22,38 @@ impl AbstractField {
2222 return Err ( field. span ( ) . error ( "field must be named" ) ) ;
2323 } ;
2424 let named = field. attrs . iter ( ) . any ( |attr| attr. path ( ) . is_ident ( "named" ) ) ;
25- let ( ty, pointer) = match field. ty {
26- Type :: Ptr ( ty) => ( * ty. elem , true ) ,
27- _ => ( field. ty , false ) ,
28- } ;
29- Ok ( Self {
30- ty,
31- ident,
32- named,
33- pointer,
34- } )
25+ let width = field
26+ . attrs
27+ . iter ( )
28+ . find ( |attr| attr. path ( ) . is_ident ( "width" ) ) ;
29+ if let Type :: Ptr ( ty) = field. ty {
30+ if let Some ( attr) = width {
31+ if let Expr :: Lit ( expr) = & attr. meta . require_name_value ( ) ?. value {
32+ if let Lit :: Str ( lit_str) = & expr. lit {
33+ return Ok ( Self {
34+ ty : * ty. elem ,
35+ width : Some ( lit_str. parse ( ) ?) ,
36+ ident,
37+ named,
38+ } ) ;
39+ }
40+ }
41+ }
42+ Err ( ident. span ( )
43+ . error ( "pointer field must have explicit `#[width = \" <type>\" ]` attribute, for example: `u64`" ) )
44+ } else {
45+ match width {
46+ Some ( attr) => Err ( attr
47+ . span ( )
48+ . error ( "`#[width]` attribute can only be applied to pointer fields" ) ) ,
49+ None => Ok ( Self {
50+ ty : field. ty ,
51+ width : None ,
52+ ident,
53+ named,
54+ } ) ,
55+ }
56+ }
3557 }
3658
3759 fn resolved_ty ( & self ) -> TokenStream {
@@ -45,9 +67,15 @@ impl AbstractField {
4567 )
4668 } ;
4769 }
48- if self . pointer {
70+ if let Some ( width ) = & self . width {
4971 resolved = quote ! {
50- :: binaryninja:: types:: Type :: pointer_of_width( & #resolved, 8 , false , false , None )
72+ :: binaryninja:: types:: Type :: pointer_of_width(
73+ & #resolved,
74+ :: std:: mem:: size_of:: <#width>( ) ,
75+ false ,
76+ false ,
77+ None
78+ )
5179 }
5280 }
5381 resolved
@@ -56,8 +84,8 @@ impl AbstractField {
5684
5785struct Repr {
5886 c : bool ,
59- packed : Option < usize > ,
60- align : Option < usize > ,
87+ packed : Option < Option < LitInt > > ,
88+ align : Option < LitInt > ,
6189 primitive : Option < ( Path , bool ) > ,
6290}
6391
@@ -75,6 +103,10 @@ impl Repr {
75103 return Err ( attr
76104 . span ( )
77105 . error ( "`#[named]` attribute can only be applied to fields" ) ) ;
106+ } else if ident == "width" {
107+ return Err ( attr
108+ . span ( )
109+ . error ( "`#[width]` attribute can only be applied to pointer fields" ) ) ;
78110 } else if ident == "repr" {
79111 attr. parse_nested_meta ( |meta| {
80112 if let Some ( ident) = meta. path . get_ident ( ) {
@@ -84,14 +116,14 @@ impl Repr {
84116 if meta. input . peek ( token:: Paren ) {
85117 let content;
86118 parenthesized ! ( content in meta. input) ;
87- packed = Some ( content. parse :: < LitInt > ( ) ?. base10_parse ( ) ? ) ;
119+ packed = Some ( Some ( content. parse ( ) ?) ) ;
88120 } else {
89- packed = Some ( 1 ) ;
121+ packed = Some ( None ) ;
90122 }
91123 } else if ident == "align" {
92124 let content;
93125 parenthesized ! ( content in meta. input) ;
94- align = Some ( content. parse :: < LitInt > ( ) ? . base10_parse ( ) ?) ;
126+ align = Some ( content. parse ( ) ?) ;
95127 } else if ident_in_list ( ident, [ "u8" , "u16" , "u32" , "u64" ] ) {
96128 primitive = Some ( ( meta. path . clone ( ) , false ) ) ;
97129 } else if ident_in_list ( ident, [ "i8" , "i16" , "i32" , "i64" ] ) {
@@ -121,7 +153,7 @@ fn ident_in_list<const N: usize>(ident: &Ident, list: [&'static str; N]) -> bool
121153 list. iter ( ) . any ( |id| ident == id)
122154}
123155
124- #[ proc_macro_derive( AbstractType , attributes( named) ) ]
156+ #[ proc_macro_derive( AbstractType , attributes( named, width ) ) ]
125157pub fn abstract_type_derive ( input : proc_macro:: TokenStream ) -> proc_macro:: TokenStream {
126158 let input = parse_macro_input ! ( input as DeriveInput ) ;
127159 match impl_abstract_type ( input) {
@@ -175,43 +207,92 @@ fn impl_abstract_structure_type(
175207 return Err ( name. span ( ) . error ( msg) ) ;
176208 }
177209
178- let fields = fields
210+ let abstract_fields = fields
179211 . named
180212 . into_iter ( )
181213 . map ( AbstractField :: from_field)
182214 . collect :: < Result < Vec < _ > > > ( ) ?;
183- let args = fields
215+ let layout_name = format_ident ! ( "__{name}_layout" ) ;
216+ let field_wrapper = format_ident ! ( "__{name}_field_wrapper" ) ;
217+ let layout_fields = abstract_fields
218+ . iter ( )
219+ . map ( |field| {
220+ let ident = & field. ident ;
221+ let layout_ty = field. width . as_ref ( ) . unwrap_or ( & field. ty ) ;
222+ quote ! {
223+ #ident: #field_wrapper<
224+ [ u8 ; <#layout_ty as :: binaryninja:: types:: AbstractType >:: SIZE ] ,
225+ { <#layout_ty as :: binaryninja:: types:: AbstractType >:: ALIGN } ,
226+ >
227+ }
228+ } )
229+ . collect :: < Vec < _ > > ( ) ;
230+ let args = abstract_fields
184231 . iter ( )
185232 . map ( |field| {
186233 let ident = & field. ident ;
187234 let resolved_ty = field. resolved_ty ( ) ;
188235 quote ! {
189236 & #resolved_ty,
190237 stringify!( #ident) ,
191- :: std:: mem:: offset_of!( #name , #ident) as u64 ,
238+ :: std:: mem:: offset_of!( #layout_name , #ident) as u64 ,
192239 false ,
193240 :: binaryninja:: types:: MemberAccess :: NoAccess ,
194241 :: binaryninja:: types:: MemberScope :: NoScope ,
195242 }
196243 } )
197244 . collect :: < Vec < _ > > ( ) ;
198245 let is_packed = repr. packed . is_some ( ) ;
199- let set_alignment = repr. align . map ( |align| quote ! { . set_alignment( #align) } ) ;
200- let set_union = match kind {
201- StructureKind :: Struct => None ,
202- StructureKind :: Union => Some ( quote ! {
203- . set_structure_type(
204- :: binaryninja:: types:: StructureType :: UnionStructureType
246+ let packed = repr. packed . map ( |size| match size {
247+ Some ( n) => quote ! { #[ repr( packed( #n) ) ] } ,
248+ None => quote ! { #[ repr( packed) ] } ,
249+ } ) ;
250+ let ( align, set_alignment) = repr
251+ . align
252+ . map ( |n| {
253+ (
254+ quote ! { #[ repr( align( #n) ) ] } ,
255+ quote ! { . set_alignment( Self :: ALIGN ) } ,
205256 )
206- } ) ,
257+ } )
258+ . unzip ( ) ;
259+ let ( kind, set_union) = match kind {
260+ StructureKind :: Struct => ( quote ! { struct } , None ) ,
261+ StructureKind :: Union => (
262+ quote ! { union } ,
263+ Some ( quote ! {
264+ . set_structure_type(
265+ :: binaryninja:: types:: StructureType :: UnionStructureType
266+ )
267+ } ) ,
268+ ) ,
207269 } ;
208270 Ok ( quote ! {
271+ #[ repr( C ) ]
272+ #[ derive( Copy , Clone ) ]
273+ struct #field_wrapper<T , const N : usize >
274+ where
275+ :: binaryninja:: elain:: Align <N >: :: binaryninja:: elain:: Alignment
276+ {
277+ t: T ,
278+ _align: :: binaryninja:: elain:: Align <N >,
279+ }
280+
281+ #[ repr( C ) ]
282+ #packed
283+ #align
284+ #kind #layout_name {
285+ #( #layout_fields) , *
286+ }
287+
209288 impl :: binaryninja:: types:: AbstractType for #name {
289+ const SIZE : usize = :: std:: mem:: size_of:: <#layout_name>( ) ;
290+ const ALIGN : usize = :: std:: mem:: align_of:: <#layout_name>( ) ;
210291 fn resolve_type( ) -> :: binaryninja:: rc:: Ref <:: binaryninja:: types:: Type > {
211292 :: binaryninja:: types:: Type :: structure(
212293 & :: binaryninja:: types:: Structure :: builder( )
213294 #( . insert( #args) ) *
214- . set_width( :: std :: mem :: size_of :: <#name> ( ) as u64 )
295+ . set_width( Self :: SIZE as u64 )
215296 . set_packed( #is_packed)
216297 #set_alignment
217298 #set_union
0 commit comments