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