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