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: usize = 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 {}: {:?}", 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.
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 {} 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
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
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
379 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
399 fn padding_bytes(&self, layout: Layout) -> usize {
400 align_to(self.latest_offset, layout.align) - self.latest_offset
401 }
402
403 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.
426 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