bindgen/codegen/
struct_layout.rs

1//! Helpers for code generation that need struct layout
2
3use super::helpers;
4
5use crate::ir::comp::CompInfo;
6use crate::ir::context::BindgenContext;
7use crate::ir::layout::Layout;
8use crate::ir::ty::{Type, TypeKind};
9use crate::FieldVisibilityKind;
10use proc_macro2::{Ident, Span};
11use std::cmp;
12
13const MAX_GUARANTEED_ALIGN: usize = 8;
14
15/// Trace the layout of struct.
16#[derive(Debug)]
17pub(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.
35pub(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.
49pub(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]
66fn 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]
75fn 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
88impl<'a> StructLayoutTracker<'a> {
89    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
118    pub(crate) fn can_copy_union_fields(&self) -> bool {
119        self.can_copy_union_fields
120    }
121
122    pub(crate) fn is_rust_union(&self) -> bool {
123        self.is_rust_union
124    }
125
126    pub(crate) fn saw_flexible_array(&mut self) {
127        self.last_field_was_flexible_array = true;
128    }
129
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
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
150    pub(crate) fn saw_bitfield_unit(&mut self, layout: Layout) {
151        debug!("saw bitfield unit for {}: {layout:?}", self.name);
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.
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
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 {field_name} to {}/{} with {padding_bytes} padding bytes {field_layout:?}",
252                self.latest_offset,
253                field_offset.unwrap_or(0) / 8,
254            );
255
256            let padding_align = if force_padding {
257                1
258            } else {
259                cmp::min(field_layout.align, MAX_GUARANTEED_ALIGN)
260            };
261
262            if need_padding && padding_bytes != 0 {
263                Some(Layout::new(padding_bytes, padding_align))
264            } else {
265                None
266            }
267        };
268
269        self.latest_offset += field_layout.size;
270        self.latest_field_layout = Some(field_layout);
271        self.max_field_align =
272            cmp::max(self.max_field_align, field_layout.align);
273        self.last_field_was_bitfield = false;
274
275        debug!(
276            "Offset: {field_name}: {} -> {}",
277            self.latest_offset - field_layout.size,
278            self.latest_offset
279        );
280
281        padding_layout.map(|layout| self.padding_field(layout))
282    }
283
284    pub(crate) fn add_tail_padding(
285        &mut self,
286        comp_name: &str,
287        comp_layout: Layout,
288    ) -> Option<proc_macro2::TokenStream> {
289        // Only emit an padding field at the end of a struct if the
290        // user configures explicit padding.
291        if !self.ctx.options().force_explicit_padding {
292            return None;
293        }
294
295        // Padding doesn't make sense for rust unions.
296        if self.is_rust_union {
297            return None;
298        }
299
300        // Also doesn't make sense for structs with flexible array members
301        if self.last_field_was_flexible_array {
302            return None;
303        }
304
305        if self.latest_offset == comp_layout.size {
306            // This struct does not contain tail padding.
307            return None;
308        }
309
310        trace!(
311            "need a tail padding field for {comp_name}: offset {} -> size {}",
312            self.latest_offset,
313            comp_layout.size
314        );
315        let size = comp_layout.size - self.latest_offset;
316        Some(self.padding_field(Layout::new(size, 0)))
317    }
318
319    pub(crate) fn pad_struct(
320        &mut self,
321        layout: Layout,
322    ) -> Option<proc_macro2::TokenStream> {
323        debug!("pad_struct:\n\tself = {self:#?}\n\tlayout = {layout:#?}");
324
325        if layout.size < self.latest_offset {
326            warn!(
327                "Calculated wrong layout for {}, too more {} bytes",
328                self.name,
329                self.latest_offset - layout.size
330            );
331            return None;
332        }
333
334        let padding_bytes = layout.size - self.latest_offset;
335        if padding_bytes == 0 {
336            return None;
337        }
338
339        let repr_align = true;
340
341        // We always pad to get to the correct size if the struct is one of
342        // those we can't align properly.
343        //
344        // Note that if the last field we saw was a bitfield, we may need to pad
345        // regardless, because bitfields don't respect alignment as strictly as
346        // other fields.
347        if padding_bytes >= layout.align ||
348            (self.last_field_was_bitfield &&
349                padding_bytes >= self.latest_field_layout.unwrap().align) ||
350            (!repr_align && layout.align > MAX_GUARANTEED_ALIGN)
351        {
352            let layout = if self.is_packed {
353                Layout::new(padding_bytes, 1)
354            } else if self.last_field_was_bitfield ||
355                layout.align > MAX_GUARANTEED_ALIGN
356            {
357                // We've already given up on alignment here.
358                Layout::for_size(self.ctx, padding_bytes)
359            } else {
360                Layout::new(padding_bytes, layout.align)
361            };
362
363            debug!("pad bytes to struct {}, {layout:?}", self.name);
364
365            Some(self.padding_field(layout))
366        } else {
367            None
368        }
369    }
370
371    pub(crate) fn requires_explicit_align(&self, layout: Layout) -> bool {
372        let repr_align = true;
373
374        // Always force explicit repr(align) for stuff more than 16-byte aligned
375        // to work-around https://github.com/rust-lang/rust/issues/54341.
376        //
377        // Worst-case this just generates redundant alignment attributes.
378        if repr_align && self.max_field_align >= 16 {
379            return true;
380        }
381
382        if self.max_field_align >= layout.align {
383            return false;
384        }
385
386        // We can only generate up-to a 8-bytes of alignment unless we support
387        // repr(align).
388        repr_align || layout.align <= MAX_GUARANTEED_ALIGN
389    }
390
391    fn padding_bytes(&self, layout: Layout) -> usize {
392        align_to(self.latest_offset, layout.align) - self.latest_offset
393    }
394
395    fn padding_field(&mut self, layout: Layout) -> proc_macro2::TokenStream {
396        let ty = helpers::blob(self.ctx, layout, false);
397        let padding_count = self.padding_count;
398
399        self.padding_count += 1;
400
401        let padding_field_name = Ident::new(
402            &format!("__bindgen_padding_{padding_count}"),
403            Span::call_site(),
404        );
405
406        self.max_field_align = cmp::max(self.max_field_align, layout.align);
407
408        let vis = super::access_specifier(self.visibility);
409
410        quote! {
411            #vis #padding_field_name : #ty ,
412        }
413    }
414
415    /// Returns whether the new field is known to merge with a bitfield.
416    ///
417    /// This is just to avoid doing the same check also in `pad_field`.
418    fn align_to_latest_field(&mut self, new_field_layout: Layout) -> bool {
419        if self.is_packed {
420            // Skip to align fields when packed.
421            return false;
422        }
423
424        let Some(layout) = self.latest_field_layout else {
425            return false;
426        };
427
428        // If it was, we may or may not need to align, depending on what the
429        // current field alignment and the bitfield size and alignment are.
430        debug!(
431            "align_to_bitfield? {}: {layout:?} {new_field_layout:?}",
432            self.last_field_was_bitfield,
433        );
434
435        // Avoid divide-by-zero errors if align is 0.
436        let align = cmp::max(1, layout.align);
437
438        if self.last_field_was_bitfield &&
439            new_field_layout.align <= layout.size % align &&
440            new_field_layout.size <= layout.size % align
441        {
442            // The new field will be coalesced into some of the remaining bits.
443            //
444            // FIXME(emilio): I think this may not catch everything?
445            debug!("Will merge with bitfield");
446            return true;
447        }
448
449        // Else, just align the obvious way.
450        self.latest_offset += self.padding_bytes(layout);
451        false
452    }
453}