-
Notifications
You must be signed in to change notification settings - Fork 352
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
Evaluate global constructors (life before main) #450
Comments
🤮 @eddyb! |
Are you sure about the "lawful" part? :P More seriously though -- shouldn't we instead lobby for some way to express this in Rust proper? I already feel bad about that TLS emulation we are doing... |
No, Rust as a language is uninterested in life-before-main. The only reason any of this works is because it's an OS feature we can't really stop you from opting into. |
See rust-lang/rust#66862 (comment) for more information on these sections. |
You might find the following C program useful for experimenting with init section order: #include <stdio.h>
typedef void (*FN)(void);
#define MKINIT(function, section) \
void function(void) { puts(section); } \
__attribute__((__section__(section))) FN function##_section[] = { function };
MKINIT(init_array, ".init_array")
MKINIT(init_array_00001, ".init_array.00001")
MKINIT(init_array_00002, ".init_array.00002")
MKINIT(init_array_65533, ".init_array.65533")
MKINIT(init_array_65534, ".init_array.65534")
MKINIT(fini_array, ".fini_array")
MKINIT(fini_array_00001, ".fini_array.00001")
MKINIT(fini_array_00002, ".fini_array.00002")
MKINIT(fini_array_65533, ".fini_array.65533")
MKINIT(fini_array_65534, ".fini_array.65534")
MKINIT(ctors, ".ctors")
MKINIT(ctors_00001, ".ctors.00001")
MKINIT(ctors_00002, ".ctors.00002")
MKINIT(ctors_65533, ".ctors.65533")
MKINIT(ctors_65534, ".ctors.65534")
MKINIT(dtors, ".dtors")
MKINIT(dtors_00001, ".dtors.00001")
MKINIT(dtors_00002, ".dtors.00002")
MKINIT(dtors_65533, ".dtors.65533")
MKINIT(dtors_65534, ".dtors.65534")
int main(void)
{
puts("main");
return 0;
} This prints:
Note that weird things happen if you try using |
Thanks! We'll need to turn this into a Rust program as a test for this feature, but that shouldn't be too hard I guess.
Sounds like it would be best to just make Miri error in those cases. |
We also need to bail if two entries for the same link section exist |
@oli-obk No, you can have arbitrarily many entries in a section; it's an array of function pointers. Entries for the same section get concatenated in link order. |
How does one detect the length of the array? Do link sections have a notion of "length"? |
I don't think miri can figure out link order. For now we can bail out on multiple entries for the same section, but in the future we can check that they are order independent instead |
That sounds like fantasy to me. ;) I don't see a feasible implementation strategy for this. |
@RalfJung yes, in some sense. AFAIK, linker scripts can define a symbol before and one after, so that code could iterate between those two symbols. When you're operating before linking (as miri would need to do), any That is, ignoring the Rust type of the I don't think link order is a serious concern, and other than the numeric priorities which @joshtriplett explained, you can just pick some visiting order (and maybe lint the fact that a linker might differ?). |
@oli-obk @bjorn3 do you know a good way to get "all statics that are in a particular linker section"? I suspect this issue may be responsible for rust-lang/rust#123583. Maybe it's time to stop using this ad-hoc hack for our Windows thread-local destructors. |
I implemented this in rust-lang/rust@850e1e3, but it requires a hack in the |
Oh, neat!
Does the regular query do that and Miri just lost it? Or do we somehow have to do something the regular query does not do? I assume regular rustc also has to preserve these statics for codegen as well... |
The regular query doesn't do that.
Statics are codegened in the crate that defines them and as such don't need to end up in |
I think it'd be better for the compiler to have some basic support for platform specific linker magic. The library code is already a bit too hacky for my liking so it'd be an improvement there too. |
Yeah it would be better but also a lot more work to implement. I won't stop anyone who tries to do that but I don't have the time to do it.
The static in question is not marked #[link_section = ".CRT$XLB"]
pub static p_thread_callback: unsafe extern "system" fn(c::LPVOID, c::DWORD, c::LPVOID) =
on_tls_callback; So there's something here I don't understand.
The query we override runs in that crate, so why is that relevant? For finding extern functions with a link_name we also just do exactly what codegen does, I think. So it seems strange to me that here we'd have to do more than what codegen does. |
See rust-lang/rust#121596 for why the |
Which is one reason why I'd prefer proper compiler support for TLS destructors 😉 But it is just an optimization. Considering it to be always called is fine. |
What Miri should be doing is randomly sometimes call it and sometimes not. That reflects the fact that it is unspecified whether the static ends up in the final binary or not.
I don't disagree with that. But I strongly disagree with using hacks relying on unspecified behavior to work around language limitations. The fix for a missing language feature is to add the feature. If something can't be done properly then doing it improperly is not a viable alternative IMO. In this case, using |
In this case I think the worst thing that can happen is memory leaks. Or can unsafe code somehow rely on TLS destructors being called? If yes then I would say rust-lang/rust#121596 introduced a soundness issue, as we no longer can guarantee that TLS destructors are called. |
I'm not sure I understand your point. If there is ever a TLS dtor then it will be called. Always. If there is not then it's a no-op in any case so is eligible for removal. |
You are relying on the completely unspecified assumption that these volatile reads "pull in" the static. |
This comment was marked as off-topic.
This comment was marked as off-topic.
This comment was marked as off-topic.
This comment was marked as off-topic.
|
That sounds like a bug in But fixing |
It's documented behaviour. https://doc.rust-lang.org/reference/abi.html#the-used-attribute
But my point is that without compiler support there are no right options for including the global destructor. |
How is that unspecified? |
This comment was marked as off-topic.
This comment was marked as off-topic.
This comment was marked as off-topic.
This comment was marked as off-topic.
This comment was marked as off-topic.
This comment was marked as off-topic.
This comment was marked as off-topic.
This comment was marked as off-topic.
This comment was marked as off-topic.
This comment was marked as off-topic.
This comment was marked as off-topic.
This comment was marked as off-topic.
This comment was marked as off-topic.
This comment was marked as off-topic.
This comment was marked as off-topic.
This comment was marked as off-topic.
This comment was marked as off-topic.
This comment was marked as off-topic.
This comment was marked as off-topic.
This comment was marked as off-topic.
This comment was marked as off-topic.
This comment was marked as off-topic.
This comment was marked as off-topic.
This comment was marked as off-topic.
This comment was marked as off-topic.
This comment was marked as off-topic.
This comment was marked as off-topic.
This comment was marked as off-topic.
Unfortunately I don't think one can move individual comments. I opened a new issue: rust-lang/unsafe-code-guidelines#504. Would be great if you could summarize the relevant part of the discussion there. :) I'll make some of our previous comments here as off-topic for the Miri side. When/if rust-lang/unsafe-code-guidelines#504 reaches a conclusion, we can try to implement that in Miri (but the proposal you made is unimplementable for Miri); meanwhile we can either keep using our current hack in Miri or go with a different hack, or with the best possible approximation of the unimplementable spec you proposed. (I'll explain in rust-lang/unsafe-code-guidelines#504 why I claim this is unimplementable. I already explained it above, too.) |
There are options that are different degrees of wrong, and IMO rust-lang/rust#121596 made it more wrong than before. Now you're not only relying on whatever happens between the object file and the final binary keeping the symbol around, you are also relying on this IOW, there are two stages where the symbol can get lost, and your argument seems to be "because the second stage is lossy, it's okay to not use the attribute that makes the first stage definitely not-lossy". That's not a valid argument; we have been discussing all along what happens in the first stage (whether the symbol makes it to the object file) so the second stage (whether the symbol makes it from the object file to the binary) is orthogonal and entirely irrelevant. |
FWIW, I agree with this statement. |
With rust-lang/rust#123937 now has the basic infrastructure to deal with link_sections that are actually arrays, which should make it a lot easier to implement support for constructors as well. |
@eddyb is lawful evil https://github.com/neon-bindings/neon/blame/master/src/lib.rs#L49
basically we should look in platform specific link sections for statics and run their value before anything else
The text was updated successfully, but these errors were encountered: