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