Skip to content

Commit cf15f41

Browse files
authored
rust: Add back in workdaround to call ctors once (#895)
* rust: Add back in workdaround to call ctors once This commit partially reverts some work in #868 where the pieces used to run libc ctors once was removed by default. I erroneously thought that this was no longer necessary but I believe it is still necessary given how everything works today. I've additionally updated comments accordingly. Closes #894 * Fix CI build
1 parent be059b7 commit cf15f41

File tree

8 files changed

+64
-91
lines changed

8 files changed

+64
-91
lines changed

crates/guest-rust/Cargo.toml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,10 +13,10 @@ Used when compiling Rust programs to the component model.
1313

1414
[dependencies]
1515
wit-bindgen-rust-macro = { path = "./macro", optional = true, version = "0.22.0" }
16-
wit-bindgen-rt = { path = "./rt", optional = true, version = "0.22.0" }
16+
wit-bindgen-rt = { path = "./rt", version = "0.22.0" }
1717
bitflags = { workspace = true }
1818

1919
[features]
2020
default = ["macros", "realloc"]
2121
macros = ["dep:wit-bindgen-rust-macro"]
22-
realloc = ["dep:wit-bindgen-rt"]
22+
realloc = []

crates/guest-rust/macro/src/lib.rs

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -101,8 +101,8 @@ impl Parse for Config {
101101
Opt::TypeSectionSuffix(suffix) => {
102102
opts.type_section_suffix = Some(suffix.value());
103103
}
104-
Opt::RunCtorsOnceWorkaround(enable) => {
105-
opts.run_ctors_once_workaround = enable.value();
104+
Opt::DisableRunCtorsOnceWorkaround(enable) => {
105+
opts.disable_run_ctors_once_workaround = enable.value();
106106
}
107107
Opt::DefaultBindingsModule(enable) => {
108108
opts.default_bindings_module = Some(enable.value());
@@ -227,7 +227,7 @@ mod kw {
227227
syn::custom_keyword!(additional_derives);
228228
syn::custom_keyword!(with);
229229
syn::custom_keyword!(type_section_suffix);
230-
syn::custom_keyword!(run_ctors_once_workaround);
230+
syn::custom_keyword!(disable_run_ctors_once_workaround);
231231
syn::custom_keyword!(default_bindings_module);
232232
syn::custom_keyword!(export_macro_name);
233233
syn::custom_keyword!(pub_export_macro);
@@ -276,7 +276,7 @@ enum Opt {
276276
AdditionalDerives(Vec<syn::Path>),
277277
With(HashMap<String, String>),
278278
TypeSectionSuffix(syn::LitStr),
279-
RunCtorsOnceWorkaround(syn::LitBool),
279+
DisableRunCtorsOnceWorkaround(syn::LitBool),
280280
DefaultBindingsModule(syn::LitStr),
281281
ExportMacroName(syn::LitStr),
282282
PubExportMacro(syn::LitBool),
@@ -382,10 +382,10 @@ impl Parse for Opt {
382382
input.parse::<kw::type_section_suffix>()?;
383383
input.parse::<Token![:]>()?;
384384
Ok(Opt::TypeSectionSuffix(input.parse()?))
385-
} else if l.peek(kw::run_ctors_once_workaround) {
386-
input.parse::<kw::run_ctors_once_workaround>()?;
385+
} else if l.peek(kw::disable_run_ctors_once_workaround) {
386+
input.parse::<kw::disable_run_ctors_once_workaround>()?;
387387
input.parse::<Token![:]>()?;
388-
Ok(Opt::RunCtorsOnceWorkaround(input.parse()?))
388+
Ok(Opt::DisableRunCtorsOnceWorkaround(input.parse()?))
389389
} else if l.peek(kw::default_bindings_module) {
390390
input.parse::<kw::default_bindings_module>()?;
391391
input.parse::<Token![:]>()?;

crates/guest-rust/rt/src/lib.rs

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,3 +79,29 @@ pub unsafe fn cabi_realloc(
7979
}
8080
return ptr;
8181
}
82+
83+
/// Provide a hook for generated export functions to run static constructors at
84+
/// most once.
85+
///
86+
/// wit-bindgen-rust generates a call to this function at the start of all
87+
/// component export functions. Importantly, it is not called as part of
88+
/// `cabi_realloc`, which is a *core* export func, but should not execute ctors.
89+
#[cfg(target_arch = "wasm32")]
90+
pub fn run_ctors_once() {
91+
static mut RUN: bool = false;
92+
unsafe {
93+
if !RUN {
94+
// This function is synthesized by `wasm-ld` to run all static
95+
// constructors. wasm-ld will either provide an implementation
96+
// of this symbol, or synthesize a wrapper around each
97+
// exported function to (unconditionally) run ctors. By using
98+
// this function, the linked module is opting into "manually"
99+
// running ctors.
100+
extern "C" {
101+
fn __wasm_call_ctors();
102+
}
103+
__wasm_call_ctors();
104+
RUN = true;
105+
}
106+
}
107+
}

crates/guest-rust/src/lib.rs

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -782,9 +782,9 @@
782782
/// // support the standard library itself depending on this crate one day.
783783
/// std_feature,
784784
///
785-
/// // Force a workaround to be emitted for pre-Rust-1.69.0 modules to
786-
/// // ensure that libc ctors run only once.
787-
/// run_ctors_once_workaround: true,
785+
/// // Disable a workaround to force wasm constructors to be run only once
786+
/// // when exported functions are called.
787+
/// disable_run_ctors_once_workaround: false,
788788
/// });
789789
/// ```
790790
///
@@ -803,6 +803,9 @@ pub mod examples;
803803

804804
#[doc(hidden)]
805805
pub mod rt {
806+
#[cfg(target_arch = "wasm32")]
807+
pub use wit_bindgen_rt::run_ctors_once;
808+
806809
pub fn maybe_link_cabi_realloc() {
807810
#[cfg(feature = "realloc")]
808811
wit_bindgen_rt::maybe_link_cabi_realloc();

crates/guest-rust/src/pre_wit_bindgen_0_20_0.rs

Lines changed: 0 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -19,35 +19,6 @@ use core::sync::atomic::{AtomicU32, Ordering::Relaxed};
1919

2020
pub use alloc_crate::{alloc, boxed, string, vec};
2121

22-
/// Provide a hook for generated export functions to run static
23-
/// constructors at most once. wit-bindgen-rust generates a call to this
24-
/// function at the start of all component export functions. Importantly,
25-
/// it is not called as part of `cabi_realloc`, which is a *core* export
26-
/// func, but may not execute ctors, because the environment ctor in
27-
/// wasi-libc (before rust 1.69.0) calls an import func, which is not
28-
/// permitted by the Component Model when inside realloc.
29-
///
30-
/// We intend to remove this once rust 1.69.0 stabilizes.
31-
#[cfg(target_arch = "wasm32")]
32-
pub fn run_ctors_once() {
33-
static mut RUN: bool = false;
34-
unsafe {
35-
if !RUN {
36-
// This function is synthesized by `wasm-ld` to run all static
37-
// constructors. wasm-ld will either provide an implementation
38-
// of this symbol, or synthesize a wrapper around each
39-
// exported function to (unconditionally) run ctors. By using
40-
// this function, the linked module is opting into "manually"
41-
// running ctors.
42-
extern "C" {
43-
fn __wasm_call_ctors();
44-
}
45-
__wasm_call_ctors();
46-
RUN = true;
47-
}
48-
}
49-
}
50-
5122
pub unsafe fn dealloc(ptr: i32, size: usize, align: usize) {
5223
if size == 0 {
5324
return;

crates/rust/src/interface.rs

Lines changed: 11 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -519,25 +519,19 @@ macro_rules! {macro_name} {{
519519
let params = self.print_export_sig(func);
520520
self.push_str(" {");
521521

522-
if self.gen.opts.run_ctors_once_workaround {
522+
if !self.gen.opts.disable_run_ctors_once_workaround {
523523
let run_ctors_once = self.path_to_run_ctors_once();
524+
// Before executing any other code, use this function to run all
525+
// static constructors, if they have not yet been run. This is a
526+
// hack required to work around wasi-libc ctors calling import
527+
// functions to initialize the environment.
528+
//
529+
// See
530+
// https://github.com/bytecodealliance/preview2-prototyping/issues/99
531+
// for more details.
524532
uwrite!(
525533
self.src,
526-
"\
527-
// Before executing any other code, use this function to run all static
528-
// constructors, if they have not yet been run. This is a hack required
529-
// to work around wasi-libc ctors calling import functions to initialize
530-
// the environment.
531-
//
532-
// This functionality will be removed once rust 1.69.0 is stable, at which
533-
// point wasi-libc will no longer have this behavior.
534-
//
535-
// See
536-
// https://github.com/bytecodealliance/preview2-prototyping/issues/99
537-
// for more details.
538-
#[cfg(target_arch=\"wasm32\")]
539-
{run_ctors_once}();
540-
",
534+
"#[cfg(target_arch=\"wasm32\")]\n{run_ctors_once}();",
541535
);
542536
}
543537

@@ -1917,7 +1911,7 @@ macro_rules! {macro_name} {{
19171911
}
19181912

19191913
fn path_to_run_ctors_once(&mut self) -> String {
1920-
self.path_from_runtime_module(RuntimeItem::RunCtorsOnce, "bool_lift")
1914+
self.path_from_runtime_module(RuntimeItem::RunCtorsOnce, "run_ctors_once")
19211915
}
19221916

19231917
pub fn path_to_vec(&mut self) -> String {

crates/rust/src/lib.rs

Lines changed: 11 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -164,10 +164,10 @@ pub struct Opts {
164164
#[cfg_attr(feature = "clap", arg(long))]
165165
pub type_section_suffix: Option<String>,
166166

167-
/// Apply a workaround required before Rust 1.69 to run wasm ctors only
168-
/// once.
167+
/// Disable a workaround used to prevent libc ctors/dtors from being invoked
168+
/// too much.
169169
#[cfg_attr(feature = "clap", arg(long))]
170-
pub run_ctors_once_workaround: bool,
170+
pub disable_run_ctors_once_workaround: bool,
171171

172172
/// Changes the default module used in the generated `export!` macro to
173173
/// something other than `self`.
@@ -413,36 +413,15 @@ pub unsafe fn bool_lift(val: u8) -> bool {
413413
}
414414

415415
RuntimeItem::RunCtorsOnce => {
416-
self.src.push_str(
416+
let rt = self.runtime_path();
417+
self.src.push_str(&format!(
417418
r#"
418-
/// Provide a hook for generated export functions to run static
419-
/// constructors at most once. wit-bindgen-rust generates a call to this
420-
/// function at the start of all component export functions. Importantly,
421-
/// it is not called as part of `cabi_realloc`, which is a *core* export
422-
/// func, but may not execute ctors, because the environment ctor in
423-
/// wasi-libc (before rust 1.69.0) calls an import func, which is not
424-
/// permitted by the Component Model when inside realloc.
425419
#[cfg(target_arch = "wasm32")]
426-
pub fn run_ctors_once() {
427-
static mut RUN: bool = false;
428-
unsafe {
429-
if !RUN {
430-
// This function is synthesized by `wasm-ld` to run all static
431-
// constructors. wasm-ld will either provide an implementation
432-
// of this symbol, or synthesize a wrapper around each
433-
// exported function to (unconditionally) run ctors. By using
434-
// this function, the linked module is opting into "manually"
435-
// running ctors.
436-
extern "C" {
437-
fn __wasm_call_ctors();
438-
}
439-
__wasm_call_ctors();
440-
RUN = true;
441-
}
442-
}
443-
}
420+
pub fn run_ctors_once() {{
421+
{rt}::run_ctors_once();
422+
}}
444423
"#,
445-
);
424+
));
446425
}
447426

448427
RuntimeItem::AsI32 => {
@@ -868,8 +847,8 @@ impl WorldGenerator for RustWasm {
868847
if let Some(default) = &self.opts.default_bindings_module {
869848
uwriteln!(self.src, "// * default-bindings-module: {default:?}");
870849
}
871-
if self.opts.run_ctors_once_workaround {
872-
uwriteln!(self.src, "// * run-ctors-once-workaround");
850+
if self.opts.disable_run_ctors_once_workaround {
851+
uwriteln!(self.src, "// * disable-run-ctors-once-workaround");
873852
}
874853
if let Some(s) = &self.opts.export_macro_name {
875854
uwriteln!(self.src, "// * export-macro-name: {s}");

crates/rust/tests/codegen.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,7 @@ mod run_ctors_once_workaround {
8686
export apply-the-workaround: func();
8787
}
8888
",
89-
run_ctors_once_workaround: true,
89+
disable_run_ctors_once_workaround: true,
9090
stubs,
9191
});
9292
}

0 commit comments

Comments
 (0)