1
1
use proc_macro2:: TokenStream ;
2
2
use proc_macro2_diagnostics:: { Diagnostic , SpanDiagnosticExt } ;
3
- use quote:: quote;
3
+ use quote:: { format_ident , quote} ;
4
4
use syn:: spanned:: Spanned ;
5
5
use 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 ,
8
8
} ;
9
9
10
10
type Result < T > = std:: result:: Result < T , Diagnostic > ;
11
11
12
12
struct AbstractField {
13
13
ty : Type ,
14
+ width : Option < Type > ,
14
15
ident : Ident ,
15
16
named : bool ,
16
- pointer : bool ,
17
17
}
18
18
19
19
impl AbstractField {
@@ -22,16 +22,38 @@ impl AbstractField {
22
22
return Err ( field. span ( ) . error ( "field must be named" ) ) ;
23
23
} ;
24
24
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
+ }
35
57
}
36
58
37
59
fn resolved_ty ( & self ) -> TokenStream {
@@ -45,9 +67,15 @@ impl AbstractField {
45
67
)
46
68
} ;
47
69
}
48
- if self . pointer {
70
+ if let Some ( width ) = & self . width {
49
71
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
+ )
51
79
}
52
80
}
53
81
resolved
@@ -56,8 +84,8 @@ impl AbstractField {
56
84
57
85
struct Repr {
58
86
c : bool ,
59
- packed : Option < usize > ,
60
- align : Option < usize > ,
87
+ packed : Option < Option < LitInt > > ,
88
+ align : Option < LitInt > ,
61
89
primitive : Option < ( Path , bool ) > ,
62
90
}
63
91
@@ -75,6 +103,10 @@ impl Repr {
75
103
return Err ( attr
76
104
. span ( )
77
105
. 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" ) ) ;
78
110
} else if ident == "repr" {
79
111
attr. parse_nested_meta ( |meta| {
80
112
if let Some ( ident) = meta. path . get_ident ( ) {
@@ -84,14 +116,14 @@ impl Repr {
84
116
if meta. input . peek ( token:: Paren ) {
85
117
let content;
86
118
parenthesized ! ( content in meta. input) ;
87
- packed = Some ( content. parse :: < LitInt > ( ) ?. base10_parse ( ) ? ) ;
119
+ packed = Some ( Some ( content. parse :: < LitInt > ( ) ?) ) ;
88
120
} else {
89
- packed = Some ( 1 ) ;
121
+ packed = Some ( None ) ;
90
122
}
91
123
} else if ident == "align" {
92
124
let content;
93
125
parenthesized ! ( content in meta. input) ;
94
- align = Some ( content. parse :: < LitInt > ( ) ?. base10_parse ( ) ? ) ;
126
+ align = Some ( content. parse :: < LitInt > ( ) ?) ;
95
127
} else if ident_in_list ( ident, [ "u8" , "u16" , "u32" , "u64" ] ) {
96
128
primitive = Some ( ( meta. path . clone ( ) , false ) ) ;
97
129
} 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
121
153
list. iter ( ) . any ( |id| ident == id)
122
154
}
123
155
124
- #[ proc_macro_derive( AbstractType , attributes( named) ) ]
156
+ #[ proc_macro_derive( AbstractType , attributes( named, width ) ) ]
125
157
pub fn abstract_type_derive ( input : proc_macro:: TokenStream ) -> proc_macro:: TokenStream {
126
158
let input = parse_macro_input ! ( input as DeriveInput ) ;
127
159
match impl_abstract_type ( input) {
@@ -175,43 +207,91 @@ fn impl_abstract_structure_type(
175
207
return Err ( name. span ( ) . error ( msg) ) ;
176
208
}
177
209
178
- let fields = fields
210
+ let abstract_fields = fields
179
211
. named
180
212
. into_iter ( )
181
213
. map ( AbstractField :: from_field)
182
214
. 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
184
231
. iter ( )
185
232
. map ( |field| {
186
233
let ident = & field. ident ;
187
234
let resolved_ty = field. resolved_ty ( ) ;
188
235
quote ! {
189
236
& #resolved_ty,
190
237
stringify!( #ident) ,
191
- :: std:: mem:: offset_of!( #name , #ident) as u64 ,
238
+ :: std:: mem:: offset_of!( #layout_name , #ident) as u64 ,
192
239
false ,
193
240
:: binaryninja:: types:: MemberAccess :: NoAccess ,
194
241
:: binaryninja:: types:: MemberScope :: NoScope ,
195
242
}
196
243
} )
197
244
. collect :: < Vec < _ > > ( ) ;
198
245
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 ) } ,
205
256
)
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
+ ) ,
207
269
} ;
208
270
Ok ( quote ! {
271
+ #[ derive( Copy , Clone ) ]
272
+ struct #field_wrapper<T , const N : usize >
273
+ where
274
+ :: binaryninja:: elain:: Align <N >: :: binaryninja:: elain:: Alignment
275
+ {
276
+ t: T ,
277
+ _align: :: binaryninja:: elain:: Align <N >,
278
+ }
279
+
280
+ #[ repr( C ) ]
281
+ #packed
282
+ #align
283
+ #kind #layout_name {
284
+ #( #layout_fields) , *
285
+ }
286
+
209
287
impl :: binaryninja:: types:: AbstractType for #name {
288
+ const SIZE : usize = :: std:: mem:: size_of:: <#layout_name>( ) ;
289
+ const ALIGN : usize = :: std:: mem:: align_of:: <#layout_name>( ) ;
210
290
fn resolve_type( ) -> :: binaryninja:: rc:: Ref <:: binaryninja:: types:: Type > {
211
291
:: binaryninja:: types:: Type :: structure(
212
292
& :: binaryninja:: types:: Structure :: builder( )
213
293
#( . insert( #args) ) *
214
- . set_width( :: std :: mem :: size_of :: <#name> ( ) as u64 )
294
+ . set_width( Self :: SIZE as u64 )
215
295
. set_packed( #is_packed)
216
296
#set_alignment
217
297
#set_union
0 commit comments