Skip to content

Commit 986d6bf

Browse files
authored
Rollup merge of #121533 - ratmice:wasm_init_fini_array, r=nnethercote
Handle .init_array link_section specially on wasm Given that wasm-ld now has support for [.init_array](https://github.com/llvm/llvm-project/blob/8f2bd8ae68883592a333f4bdbed9798d66e68630/llvm/lib/MC/WasmObjectWriter.cpp#L1852), it appears we can easily implement that section by falling through to the normal path rather than taking the typical custom_section path for wasm. The wasm-ld appears to have a bunch of limitations. Only one static with the `link_section` in a crate or else you hit the fatal error in the link above "only one .init_array section fragment supported". They do not get merged. You can still call multiple constructors by setting it to an array. ``` unsafe extern "C" fn ctor() { println!("foo"); } #[used] #[link_section = ".init_array"] static FOO: [unsafe extern "C" fn(); 2] = [ctor, ctor]; ``` Another issue appears to be that if crate *A* depends on crate *B*, but *A* doesn't call any symbols from *B* and *B* doesn't `#[export_name = ...]` any symbols, then crate *B*'s constructor will not be called. The workaround to this is to provide an exported symbol in crate *B*.
2 parents 3d68afc + 1de046f commit 986d6bf

File tree

2 files changed

+36
-9
lines changed

2 files changed

+36
-9
lines changed

compiler/rustc_codegen_llvm/src/consts.rs

+8-2
Original file line numberDiff line numberDiff line change
@@ -495,8 +495,14 @@ impl<'ll> CodegenCx<'ll, '_> {
495495
}
496496

497497
// Wasm statics with custom link sections get special treatment as they
498-
// go into custom sections of the wasm executable.
499-
if self.tcx.sess.target.is_like_wasm {
498+
// go into custom sections of the wasm executable. The exception to this
499+
// is the `.init_array` section which are treated specially by the wasm linker.
500+
if self.tcx.sess.target.is_like_wasm
501+
&& attrs
502+
.link_section
503+
.map(|link_section| !link_section.as_str().starts_with(".init_array"))
504+
.unwrap_or(true)
505+
{
500506
if let Some(section) = attrs.link_section {
501507
let section = llvm::LLVMMDStringInContext2(
502508
self.llcx,

compiler/rustc_hir_analysis/src/check/mod.rs

+28-7
Original file line numberDiff line numberDiff line change
@@ -166,21 +166,42 @@ fn maybe_check_static_with_link_section(tcx: TyCtxt<'_>, id: LocalDefId) {
166166
return;
167167
}
168168

169-
// For the wasm32 target statics with `#[link_section]` are placed into custom
170-
// sections of the final output file, but this isn't link custom sections of
171-
// other executable formats. Namely we can only embed a list of bytes,
172-
// nothing with provenance (pointers to anything else). If any provenance
173-
// show up, reject it here.
169+
// For the wasm32 target statics with `#[link_section]` other than `.init_array`
170+
// are placed into custom sections of the final output file, but this isn't like
171+
// custom sections of other executable formats. Namely we can only embed a list
172+
// of bytes, nothing with provenance (pointers to anything else). If any
173+
// provenance show up, reject it here.
174174
// `#[link_section]` may contain arbitrary, or even undefined bytes, but it is
175175
// the consumer's responsibility to ensure all bytes that have been read
176176
// have defined values.
177+
//
178+
// The `.init_array` section is left to go through the normal custom section code path.
179+
// When dealing with `.init_array` wasm-ld currently has several limitations. This manifests
180+
// in workarounds in user-code.
181+
//
182+
// * The linker fails to merge multiple items in a crate into the .init_array section.
183+
// To work around this, a single array can be used placing multiple items in the array.
184+
// #[link_section = ".init_array"]
185+
// static FOO: [unsafe extern "C" fn(); 2] = [ctor, ctor];
186+
// * Even symbols marked used get gc'd from dependant crates unless at least one symbol
187+
// in the crate is marked with an `#[export_name]`
188+
//
189+
// Once `.init_array` support in wasm-ld is complete, the user code workarounds should
190+
// continue to work, but would no longer be necessary.
191+
177192
if let Ok(alloc) = tcx.eval_static_initializer(id.to_def_id())
178193
&& alloc.inner().provenance().ptrs().len() != 0
179194
{
180-
let msg = "statics with a custom `#[link_section]` must be a \
195+
if attrs
196+
.link_section
197+
.map(|link_section| !link_section.as_str().starts_with(".init_array"))
198+
.unwrap()
199+
{
200+
let msg = "statics with a custom `#[link_section]` must be a \
181201
simple list of bytes on the wasm target with no \
182202
extra levels of indirection such as references";
183-
tcx.dcx().span_err(tcx.def_span(id), msg);
203+
tcx.dcx().span_err(tcx.def_span(id), msg);
204+
}
184205
}
185206
}
186207

0 commit comments

Comments
 (0)