• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 //! Helpers for code generation that need struct layout
2 
3 use super::helpers;
4 
5 use crate::ir::comp::CompInfo;
6 use crate::ir::context::BindgenContext;
7 use crate::ir::layout::Layout;
8 use crate::ir::ty::{Type, TypeKind};
9 use proc_macro2::{self, Ident, Span};
10 use std::cmp;
11 
12 const MAX_GUARANTEED_ALIGN: usize = 8;
13 
14 /// Trace the layout of struct.
15 #[derive(Debug)]
16 pub struct StructLayoutTracker<'a> {
17     name: &'a str,
18     ctx: &'a BindgenContext,
19     comp: &'a CompInfo,
20     is_packed: bool,
21     known_type_layout: Option<Layout>,
22     is_rust_union: bool,
23     can_copy_union_fields: bool,
24     latest_offset: usize,
25     padding_count: usize,
26     latest_field_layout: Option<Layout>,
27     max_field_align: usize,
28     last_field_was_bitfield: bool,
29 }
30 
31 /// Returns a size aligned to a given value.
align_to(size: usize, align: usize) -> usize32 pub fn align_to(size: usize, align: usize) -> usize {
33     if align == 0 {
34         return size;
35     }
36 
37     let rem = size % align;
38     if rem == 0 {
39         return size;
40     }
41 
42     size + align - rem
43 }
44 
45 /// Returns the lower power of two byte count that can hold at most n bits.
bytes_from_bits_pow2(mut n: usize) -> usize46 pub fn bytes_from_bits_pow2(mut n: usize) -> usize {
47     if n == 0 {
48         return 0;
49     }
50 
51     if n <= 8 {
52         return 1;
53     }
54 
55     if !n.is_power_of_two() {
56         n = n.next_power_of_two();
57     }
58 
59     n / 8
60 }
61 
62 #[test]
test_align_to()63 fn test_align_to() {
64     assert_eq!(align_to(1, 1), 1);
65     assert_eq!(align_to(1, 2), 2);
66     assert_eq!(align_to(1, 4), 4);
67     assert_eq!(align_to(5, 1), 5);
68     assert_eq!(align_to(17, 4), 20);
69 }
70 
71 #[test]
test_bytes_from_bits_pow2()72 fn test_bytes_from_bits_pow2() {
73     assert_eq!(bytes_from_bits_pow2(0), 0);
74     for i in 1..9 {
75         assert_eq!(bytes_from_bits_pow2(i), 1);
76     }
77     for i in 9..17 {
78         assert_eq!(bytes_from_bits_pow2(i), 2);
79     }
80     for i in 17..33 {
81         assert_eq!(bytes_from_bits_pow2(i), 4);
82     }
83 }
84 
85 impl<'a> StructLayoutTracker<'a> {
new( ctx: &'a BindgenContext, comp: &'a CompInfo, ty: &'a Type, name: &'a str, ) -> Self86     pub fn new(
87         ctx: &'a BindgenContext,
88         comp: &'a CompInfo,
89         ty: &'a Type,
90         name: &'a str,
91     ) -> Self {
92         let known_type_layout = ty.layout(ctx);
93         let is_packed = comp.is_packed(ctx, known_type_layout.as_ref());
94         let (is_rust_union, can_copy_union_fields) =
95             comp.is_rust_union(ctx, known_type_layout.as_ref(), name);
96         StructLayoutTracker {
97             name,
98             ctx,
99             comp,
100             is_packed,
101             known_type_layout,
102             is_rust_union,
103             can_copy_union_fields,
104             latest_offset: 0,
105             padding_count: 0,
106             latest_field_layout: None,
107             max_field_align: 0,
108             last_field_was_bitfield: false,
109         }
110     }
111 
can_copy_union_fields(&self) -> bool112     pub fn can_copy_union_fields(&self) -> bool {
113         self.can_copy_union_fields
114     }
115 
is_rust_union(&self) -> bool116     pub fn is_rust_union(&self) -> bool {
117         self.is_rust_union
118     }
119 
saw_vtable(&mut self)120     pub fn saw_vtable(&mut self) {
121         debug!("saw vtable for {}", self.name);
122 
123         let ptr_size = self.ctx.target_pointer_size();
124         self.latest_offset += ptr_size;
125         self.latest_field_layout = Some(Layout::new(ptr_size, ptr_size));
126         self.max_field_align = ptr_size;
127     }
128 
saw_base(&mut self, base_ty: &Type)129     pub fn saw_base(&mut self, base_ty: &Type) {
130         debug!("saw base for {}", self.name);
131         if let Some(layout) = base_ty.layout(self.ctx) {
132             self.align_to_latest_field(layout);
133 
134             self.latest_offset += self.padding_bytes(layout) + layout.size;
135             self.latest_field_layout = Some(layout);
136             self.max_field_align = cmp::max(self.max_field_align, layout.align);
137         }
138     }
139 
saw_bitfield_unit(&mut self, layout: Layout)140     pub fn saw_bitfield_unit(&mut self, layout: Layout) {
141         debug!("saw bitfield unit for {}: {:?}", self.name, layout);
142 
143         self.align_to_latest_field(layout);
144 
145         self.latest_offset += layout.size;
146 
147         debug!(
148             "Offset: <bitfield>: {} -> {}",
149             self.latest_offset - layout.size,
150             self.latest_offset
151         );
152 
153         self.latest_field_layout = Some(layout);
154         self.last_field_was_bitfield = true;
155         // NB: We intentionally don't update the max_field_align here, since our
156         // bitfields code doesn't necessarily guarantee it, so we need to
157         // actually generate the dummy alignment.
158     }
159 
160     /// Returns a padding field if necessary for a given new field _before_
161     /// adding that field.
saw_field( &mut self, field_name: &str, field_ty: &Type, field_offset: Option<usize>, ) -> Option<proc_macro2::TokenStream>162     pub fn saw_field(
163         &mut self,
164         field_name: &str,
165         field_ty: &Type,
166         field_offset: Option<usize>,
167     ) -> Option<proc_macro2::TokenStream> {
168         let mut field_layout = field_ty.layout(self.ctx)?;
169 
170         if let TypeKind::Array(inner, len) =
171             *field_ty.canonical_type(self.ctx).kind()
172         {
173             // FIXME(emilio): As an _ultra_ hack, we correct the layout returned
174             // by arrays of structs that have a bigger alignment than what we
175             // can support.
176             //
177             // This means that the structs in the array are super-unsafe to
178             // access, since they won't be properly aligned, but there's not too
179             // much we can do about it.
180             if let Some(layout) = self.ctx.resolve_type(inner).layout(self.ctx)
181             {
182                 if layout.align > MAX_GUARANTEED_ALIGN {
183                     field_layout.size =
184                         align_to(layout.size, layout.align) * len;
185                     field_layout.align = MAX_GUARANTEED_ALIGN;
186                 }
187             }
188         }
189         self.saw_field_with_layout(field_name, field_layout, field_offset)
190     }
191 
saw_field_with_layout( &mut self, field_name: &str, field_layout: Layout, field_offset: Option<usize>, ) -> Option<proc_macro2::TokenStream>192     pub fn saw_field_with_layout(
193         &mut self,
194         field_name: &str,
195         field_layout: Layout,
196         field_offset: Option<usize>,
197     ) -> Option<proc_macro2::TokenStream> {
198         let will_merge_with_bitfield = self.align_to_latest_field(field_layout);
199 
200         let is_union = self.comp.is_union();
201         let padding_bytes = match field_offset {
202             Some(offset) if offset / 8 > self.latest_offset => {
203                 offset / 8 - self.latest_offset
204             }
205             _ => {
206                 if will_merge_with_bitfield ||
207                     field_layout.align == 0 ||
208                     is_union
209                 {
210                     0
211                 } else if !self.is_packed {
212                     self.padding_bytes(field_layout)
213                 } else if let Some(l) = self.known_type_layout {
214                     self.padding_bytes(l)
215                 } else {
216                     0
217                 }
218             }
219         };
220 
221         self.latest_offset += padding_bytes;
222 
223         let padding_layout = if self.is_packed || is_union {
224             None
225         } else {
226             let force_padding = self.ctx.options().force_explicit_padding;
227 
228             // Otherwise the padding is useless.
229             let need_padding = force_padding ||
230                 padding_bytes >= field_layout.align ||
231                 field_layout.align > MAX_GUARANTEED_ALIGN;
232 
233             debug!(
234                 "Offset: <padding>: {} -> {}",
235                 self.latest_offset - padding_bytes,
236                 self.latest_offset
237             );
238 
239             debug!(
240                 "align field {} to {}/{} with {} padding bytes {:?}",
241                 field_name,
242                 self.latest_offset,
243                 field_offset.unwrap_or(0) / 8,
244                 padding_bytes,
245                 field_layout
246             );
247 
248             let padding_align = if force_padding {
249                 1
250             } else {
251                 cmp::min(field_layout.align, MAX_GUARANTEED_ALIGN)
252             };
253 
254             if need_padding && padding_bytes != 0 {
255                 Some(Layout::new(padding_bytes, padding_align))
256             } else {
257                 None
258             }
259         };
260 
261         self.latest_offset += field_layout.size;
262         self.latest_field_layout = Some(field_layout);
263         self.max_field_align =
264             cmp::max(self.max_field_align, field_layout.align);
265         self.last_field_was_bitfield = false;
266 
267         debug!(
268             "Offset: {}: {} -> {}",
269             field_name,
270             self.latest_offset - field_layout.size,
271             self.latest_offset
272         );
273 
274         padding_layout.map(|layout| self.padding_field(layout))
275     }
276 
add_tail_padding( &mut self, comp_name: &str, comp_layout: Layout, ) -> Option<proc_macro2::TokenStream>277     pub fn add_tail_padding(
278         &mut self,
279         comp_name: &str,
280         comp_layout: Layout,
281     ) -> Option<proc_macro2::TokenStream> {
282         // Only emit an padding field at the end of a struct if the
283         // user configures explicit padding.
284         if !self.ctx.options().force_explicit_padding {
285             return None;
286         }
287 
288         // Padding doesn't make sense for rust unions.
289         if self.is_rust_union {
290             return None;
291         }
292 
293         if self.latest_offset == comp_layout.size {
294             // This struct does not contain tail padding.
295             return None;
296         }
297 
298         trace!(
299             "need a tail padding field for {}: offset {} -> size {}",
300             comp_name,
301             self.latest_offset,
302             comp_layout.size
303         );
304         let size = comp_layout.size - self.latest_offset;
305         Some(self.padding_field(Layout::new(size, 0)))
306     }
307 
pad_struct( &mut self, layout: Layout, ) -> Option<proc_macro2::TokenStream>308     pub fn pad_struct(
309         &mut self,
310         layout: Layout,
311     ) -> Option<proc_macro2::TokenStream> {
312         debug!(
313             "pad_struct:\n\tself = {:#?}\n\tlayout = {:#?}",
314             self, layout
315         );
316 
317         if layout.size < self.latest_offset {
318             warn!(
319                 "Calculated wrong layout for {}, too more {} bytes",
320                 self.name,
321                 self.latest_offset - layout.size
322             );
323             return None;
324         }
325 
326         let padding_bytes = layout.size - self.latest_offset;
327         if padding_bytes == 0 {
328             return None;
329         }
330 
331         let repr_align = self.ctx.options().rust_features().repr_align;
332 
333         // We always pad to get to the correct size if the struct is one of
334         // those we can't align properly.
335         //
336         // Note that if the last field we saw was a bitfield, we may need to pad
337         // regardless, because bitfields don't respect alignment as strictly as
338         // other fields.
339         if padding_bytes >= layout.align ||
340             (self.last_field_was_bitfield &&
341                 padding_bytes >= self.latest_field_layout.unwrap().align) ||
342             (!repr_align && layout.align > MAX_GUARANTEED_ALIGN)
343         {
344             let layout = if self.is_packed {
345                 Layout::new(padding_bytes, 1)
346             } else if self.last_field_was_bitfield ||
347                 layout.align > MAX_GUARANTEED_ALIGN
348             {
349                 // We've already given up on alignment here.
350                 Layout::for_size(self.ctx, padding_bytes)
351             } else {
352                 Layout::new(padding_bytes, layout.align)
353             };
354 
355             debug!("pad bytes to struct {}, {:?}", self.name, layout);
356 
357             Some(self.padding_field(layout))
358         } else {
359             None
360         }
361     }
362 
requires_explicit_align(&self, layout: Layout) -> bool363     pub fn requires_explicit_align(&self, layout: Layout) -> bool {
364         let repr_align = self.ctx.options().rust_features().repr_align;
365 
366         // Always force explicit repr(align) for stuff more than 16-byte aligned
367         // to work-around https://github.com/rust-lang/rust/issues/54341.
368         //
369         // Worst-case this just generates redundant alignment attributes.
370         if repr_align && self.max_field_align >= 16 {
371             return true;
372         }
373 
374         if self.max_field_align >= layout.align {
375             return false;
376         }
377 
378         // We can only generate up-to a 8-bytes of alignment unless we support
379         // repr(align).
380         repr_align || layout.align <= MAX_GUARANTEED_ALIGN
381     }
382 
padding_bytes(&self, layout: Layout) -> usize383     fn padding_bytes(&self, layout: Layout) -> usize {
384         align_to(self.latest_offset, layout.align) - self.latest_offset
385     }
386 
padding_field(&mut self, layout: Layout) -> proc_macro2::TokenStream387     fn padding_field(&mut self, layout: Layout) -> proc_macro2::TokenStream {
388         let ty = helpers::blob(self.ctx, layout);
389         let padding_count = self.padding_count;
390 
391         self.padding_count += 1;
392 
393         let padding_field_name = Ident::new(
394             &format!("__bindgen_padding_{}", padding_count),
395             Span::call_site(),
396         );
397 
398         self.max_field_align = cmp::max(self.max_field_align, layout.align);
399 
400         quote! {
401             pub #padding_field_name : #ty ,
402         }
403     }
404 
405     /// Returns whether the new field is known to merge with a bitfield.
406     ///
407     /// This is just to avoid doing the same check also in pad_field.
align_to_latest_field(&mut self, new_field_layout: Layout) -> bool408     fn align_to_latest_field(&mut self, new_field_layout: Layout) -> bool {
409         if self.is_packed {
410             // Skip to align fields when packed.
411             return false;
412         }
413 
414         let layout = match self.latest_field_layout {
415             Some(l) => l,
416             None => return false,
417         };
418 
419         // If it was, we may or may not need to align, depending on what the
420         // current field alignment and the bitfield size and alignment are.
421         debug!(
422             "align_to_bitfield? {}: {:?} {:?}",
423             self.last_field_was_bitfield, layout, new_field_layout
424         );
425 
426         // Avoid divide-by-zero errors if align is 0.
427         let align = cmp::max(1, layout.align);
428 
429         if self.last_field_was_bitfield &&
430             new_field_layout.align <= layout.size % align &&
431             new_field_layout.size <= layout.size % align
432         {
433             // The new field will be coalesced into some of the remaining bits.
434             //
435             // FIXME(emilio): I think this may not catch everything?
436             debug!("Will merge with bitfield");
437             return true;
438         }
439 
440         // Else, just align the obvious way.
441         self.latest_offset += self.padding_bytes(layout);
442         false
443     }
444 }
445