-
Notifications
You must be signed in to change notification settings - Fork 694
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
Incorrect layout with large bitfield #1007
Comments
Here is where we allocate logical bitfields into physical units: https://github.com/rust-lang-nursery/rust-bindgen/blob/master/src/ir/comp.rs#L432-L617 If we discover the root cause of the issue, and it doesn't appear to be a simple fix, we should investigate falling back to making the whole struct opaque. Perhaps extending self.fields().iter().any(|f| match *f {
Field::Bitfields(ref unit) => unit.bitfields().iter().any(|b| b.width() > 64),
_ => false,
}) which would make any bitfield that is bigger than a cc @aeleos |
@highfive: assign me |
Hey @aeleos! Thanks for your interest in working on this issue. It's now assigned to you! |
@fitzgen so when the Item info for the bitfield is being printed out, it outputs this.
So there are 2 different layouts being generated, one with size 80, align 8 and the other with size 128, align 128. Do you know where each of them are generated and what each of them means? Another question I have is that when the header in question is compiled with clang++ it gives this warning
If clang is truncating the value to 32 bits, does that mean the correct behavior would be to treat it as a 32 bit bitfield? |
The former is the type's layout we get directly from
It sounds like we should be performing that same truncation inside the I expect this is the root cause and performing the truncation is the correct fix. Does |
Here is the warning from gcc
It doesn't explicitly say that it is truncating, so I am not 100% sure, but I think that is what it is doing.
|
Looks like
|
I think we can ignore this because |
@fitzgen Another interesting thing I just found, the actual type assigned to the anonymous bitfield doesn't change the size of the struct.
g++ also has the same behavior. Considering that it specifically says that the value is being truncated, I find it odd that the memory is being allocated to fit the entire bitfield. I tried a few different combinations of bitfield sizes are the memory size pattern seems to be 0x40, 0x48, 0x50, 0x58, etc. Is it possible to just check whatever memory size libclang outputs and use that as the memory size? But does that mean we are just allocating extra memory for no reason? |
The number one priority is to end up with the same physical representation as the C code. If we don't, then passing these structs across FFI boundaries will lead to memory unsafety. That certainly is odd behavior. I think the best thing to do is to treat the whole struct as opaque if any bitfields are wider than its type. So we would add something like this to self.field().iter().any(|f| match *f {
Field::DataMember(_) => false,
Field::Bitfields(ref unit) => unit.bitfields().iter().any(|bf| {
bf.width() / 8 > ctx.resolve_type(bf.ty()).layout().size()
})
}) (Probably with a comment explaining why we're checking bitfield widths there and linking back to this issue) |
@fitzgen sorry about getting back to you so late, I have been busy the past few days. Do you mean add it to |
Yep!
|
@fitzgen so I tried to implement something, but there are a few issues with it. This is my new is_opaque function
Right now when it runs with the failing test case, I get this as the output
I am not sure how the BitfieldUnit struct is supposed to work, but there is nothing inside the bitfields array if in this case. Since we can't see how large the bitfield is in any way other than the layout according to this, what do you think is the best thing to to do? I did also notice that this line is output |
That looks like what I'd expect, although if self.has_non_type_template_params {
return self.has_non_type_template_params
} should be if self.has_non_type_template_params {
return true;
} and then we don't need to check
Pretty much what it says on the tin: /// A contiguous set of logical bitfields that live within the same physical
/// allocation unit. See 9.2.4 [class.bit] in the C++ standard and [section
/// 2.4.II.1 in the Itanium C++
/// ABI](http://itanium-cxx-abi.github.io/cxx-abi/abi.html#class-types). Logical bitfields get allocated into physical units. The units end up as a field in the resulting struct, and the bitfields become getters/setters that are masking out everything but their own relevant bits from the unit.
And I think this is because we eagerly filter out anonymous bitfields when computing which bitfields end up in which units. We're going to have to stop doing that filtering to make these changes work. https://github.com/rust-lang-nursery/rust-bindgen/blob/master/src/ir/comp.rs#L583-L589 Sorry I didn't remember this earlier. Removing the eager filtering, removing the
That would be desirable, but is a bit orthogonal to this issue, IMO. It also depends on #959. If you want to help out, it would be very appreciated :) But I don't think it needs to block this issue. Does that help clear things up? Anything not make sense? Feel like you know where to go from here? Cheers! 😸 |
@fitzgen I think I get whats happening, here is what I am thinking are the next steps
That seems like everything does that sound right to you? |
That sounds 💯 ! |
@fitzgen so the first issue I have run into is with this line |
I code generation, we can simply skip bitfields that don't have names. The allocation units are always emitted, so skipping the bitfield won't cause the struct layout to go wrong, unlike normal data members. |
Follow up to #1001
Input C/C++ Header
Bindgen Invocation
Actual Results
Panic when running the layout tests: our generated struct ends up with the wrong size.
Expected Results
We generate a struct with the correct layout, and it passes its layout tests.
The text was updated successfully, but these errors were encountered: