Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Packed structures indirectly containing aligned structures aren't supported #136

Open
ahomescu opened this issue Aug 5, 2019 · 0 comments

Comments

@ahomescu
Copy link
Contributor

ahomescu commented Aug 5, 2019

Commit 49d2b57 adds support for packed structures that are also aligned, and also packed structures that directly contain aligned structure fields. However, packed structures that indirectly contain aligned structures aren't supported by the current implementation, e.g.

struct A {
  int x;
  struct B b;
} __attribute__((packed));

struct B {
  int x;
  struct C c;
}

struct C {
  int x;
} __attribute__((aligned(16)));

I have some ideas on how to support this, but the implementation would be somewhat complex.
Basically, we need to produce X_Inner and X_PADDING for all types that are directly or indirectly included in a packed structure, and manually lay out the fields of the X_Inner structure to perfectly match what C does.
This is tricky because, in addition to the post-field padding that the current implementation adds, we'd need to insert pre-field padding inside non-packed structures, e.g., between the B::x and B::c fields above.
We cannot rely on rustc inserting that padding by itself, since we're not using the #[repr(align(N))] attribute for alignment.

We need to control the exact layout of every structure ourselves using padding fields.
To start, we will distinguish between manually-aligned structures, i.e., structures whose layouts are directly or indirectly controlled by an aligned(N) attribute, and rustc-aligned ones.
I propose the following algorithm to emit a manually-aligned structure in Rust:

  1. Find the last manually-aligned field in the structure
  2. Split the structure, e.g., Foo, into the following components:
    a. The sub-structure containing all fields before the manually-aligned one (called Foo_Pre below)
    b. The pre-field padding
    c. The field itself (of type T)
    d. The post-field padding
    e. The substructure with all the following fields, if any (all rustc-managed), e.g.
struct Foo {
  pre: Foo_Pre,
  pre_padding: [u8; field_PREPADDING],
  field: T,
  post_padding: [u8; field_POSTPADDING],
  post: Foo_Post,
}
  1. Compute the alignment T_ALIGNMENT of T as the maximum between the manually-specified alignment and align_of::<T>() from Rust.
  2. Compute field_PREPADDING as align_to(size_of::<Pre>(), T_ALIGNMENT) after computing T_ALIGNMENT.
  3. Further split the first 3 fields into a separate substructure, in order to compute the post-padding size:
struct Foo_PreField {
  pre: Foo_Pre,
  pre_padding: [u8; field_PREPADDING],
  field: T,
}
struct Foo {
  pre_field: Foo_PreField,
  post_padding: [u8; field_POSTPADDING],
  post: Foo_Post,
}
  1. Compute field_POSTPADDING as align_to(size_of::<Foo_PreField>, T_ALIGNMENT).
  2. If Foo_Pre contains other manually-aligned fields, go back to step 1 with Foo_Pre as the new structure. Otherwise, stop.

The align_to function needs to be implemented as a const fn, and the actual names of Foo_Pre, Foo_PreField, Foo_Post and the padding fields and constants should be produced by the renamer.

For example, the generated structures corresponding to struct B above would look something like

struct B_Pre {
  x: u32,
}
struct B_PreField {
  pre: B_Pre,
  pre_padding: [u8; C_PREPADDING],
  c: C,
}
struct B_Post {
}
struct B {
  pre_field: B_PreField,
  post_padding: [u8; C_POSTPADDING],
  post: B_Post,
}
const C_ALIGNMENT = max(16, std::mem::align_of(C));
const C_PREPADDING = align_to(size_of::<B_Pre>(), C_ALIGNMENT);
const C_POSTPADDING = align_to(size_of::<B_PreField>(), C_ALIGNMENT);

So far, we haven't run into any code that needs this, so the implementation work will be done later.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant