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](8f2bd8ae68/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*.
This commit is contained in:
commit
986d6bf9fb
2 changed files with 36 additions and 9 deletions
|
@ -495,8 +495,14 @@ impl<'ll> CodegenCx<'ll, '_> {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Wasm statics with custom link sections get special treatment as they
|
// Wasm statics with custom link sections get special treatment as they
|
||||||
// go into custom sections of the wasm executable.
|
// go into custom sections of the wasm executable. The exception to this
|
||||||
if self.tcx.sess.target.is_like_wasm {
|
// is the `.init_array` section which are treated specially by the wasm linker.
|
||||||
|
if self.tcx.sess.target.is_like_wasm
|
||||||
|
&& attrs
|
||||||
|
.link_section
|
||||||
|
.map(|link_section| !link_section.as_str().starts_with(".init_array"))
|
||||||
|
.unwrap_or(true)
|
||||||
|
{
|
||||||
if let Some(section) = attrs.link_section {
|
if let Some(section) = attrs.link_section {
|
||||||
let section = llvm::LLVMMDStringInContext2(
|
let section = llvm::LLVMMDStringInContext2(
|
||||||
self.llcx,
|
self.llcx,
|
||||||
|
|
|
@ -166,16 +166,36 @@ fn maybe_check_static_with_link_section(tcx: TyCtxt<'_>, id: LocalDefId) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// For the wasm32 target statics with `#[link_section]` are placed into custom
|
// For the wasm32 target statics with `#[link_section]` other than `.init_array`
|
||||||
// sections of the final output file, but this isn't link custom sections of
|
// are placed into custom sections of the final output file, but this isn't like
|
||||||
// other executable formats. Namely we can only embed a list of bytes,
|
// custom sections of other executable formats. Namely we can only embed a list
|
||||||
// nothing with provenance (pointers to anything else). If any provenance
|
// of bytes, nothing with provenance (pointers to anything else). If any
|
||||||
// show up, reject it here.
|
// provenance show up, reject it here.
|
||||||
// `#[link_section]` may contain arbitrary, or even undefined bytes, but it is
|
// `#[link_section]` may contain arbitrary, or even undefined bytes, but it is
|
||||||
// the consumer's responsibility to ensure all bytes that have been read
|
// the consumer's responsibility to ensure all bytes that have been read
|
||||||
// have defined values.
|
// have defined values.
|
||||||
|
//
|
||||||
|
// The `.init_array` section is left to go through the normal custom section code path.
|
||||||
|
// When dealing with `.init_array` wasm-ld currently has several limitations. This manifests
|
||||||
|
// in workarounds in user-code.
|
||||||
|
//
|
||||||
|
// * The linker fails to merge multiple items in a crate into the .init_array section.
|
||||||
|
// To work around this, a single array can be used placing multiple items in the array.
|
||||||
|
// #[link_section = ".init_array"]
|
||||||
|
// static FOO: [unsafe extern "C" fn(); 2] = [ctor, ctor];
|
||||||
|
// * Even symbols marked used get gc'd from dependant crates unless at least one symbol
|
||||||
|
// in the crate is marked with an `#[export_name]`
|
||||||
|
//
|
||||||
|
// Once `.init_array` support in wasm-ld is complete, the user code workarounds should
|
||||||
|
// continue to work, but would no longer be necessary.
|
||||||
|
|
||||||
if let Ok(alloc) = tcx.eval_static_initializer(id.to_def_id())
|
if let Ok(alloc) = tcx.eval_static_initializer(id.to_def_id())
|
||||||
&& alloc.inner().provenance().ptrs().len() != 0
|
&& alloc.inner().provenance().ptrs().len() != 0
|
||||||
|
{
|
||||||
|
if attrs
|
||||||
|
.link_section
|
||||||
|
.map(|link_section| !link_section.as_str().starts_with(".init_array"))
|
||||||
|
.unwrap()
|
||||||
{
|
{
|
||||||
let msg = "statics with a custom `#[link_section]` must be a \
|
let msg = "statics with a custom `#[link_section]` must be a \
|
||||||
simple list of bytes on the wasm target with no \
|
simple list of bytes on the wasm target with no \
|
||||||
|
@ -183,6 +203,7 @@ fn maybe_check_static_with_link_section(tcx: TyCtxt<'_>, id: LocalDefId) {
|
||||||
tcx.dcx().span_err(tcx.def_span(id), msg);
|
tcx.dcx().span_err(tcx.def_span(id), msg);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn report_forbidden_specialization(tcx: TyCtxt<'_>, impl_item: DefId, parent_impl: DefId) {
|
fn report_forbidden_specialization(tcx: TyCtxt<'_>, impl_item: DefId, parent_impl: DefId) {
|
||||||
let span = tcx.def_span(impl_item);
|
let span = tcx.def_span(impl_item);
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue