Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 16 additions & 5 deletions crates/wasm_split_cli/src/emit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ use crate::{
split_point::{SplitModuleIdentifier, SplitProgramInfo},
util::{wasm_data_len, wasm_data_start},
};
use eyre::{anyhow, bail, Context, Result};
use eyre::{bail, Context, Result};
use tracing::{trace, warn};
use wasm_encoder::{reencode::Reencode, ConstExpr, EntityType, ProducersField, ProducersSection};
use wasmparser::{
Expand Down Expand Up @@ -374,10 +374,21 @@ impl DataEmitInfo {
..
} = input_module.reloc_info.symbols[symbol_index]
else {
// Not sure how to emit an *undefined* data symbol
return Some(Err(anyhow!(
"Expected data symbol dep node to ref to defined data symbol"
)));
// Undefined data symbol: nothing defines it, so there is
// no definition to place and no address to relocate.
// The linker already resolved every reference to it
// (to 0 under --allow-undefined), and `reloc_value`
// leaves such references untouched, so the symbol
// simply has no place in the emit state. Toolchains
// produce these in the wild, e.g. rustc incremental
// builds whose reused objects still reference renamed
// promoted anonymous globals (rust-lang/rust#81280).
if let SymbolInfo::Data { name, .. } =
input_module.reloc_info.symbols[symbol_index]
{
trace!("undefined data symbol {name:?} in included set; references keep their linker value");
}
return None;
};
if def_data.size == 0 {
// We don't care about zero-sized symbols.
Expand Down
15 changes: 13 additions & 2 deletions integration/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 3 additions & 0 deletions integration/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ members = [
"callback-in-split",
"optimize_splits",
"static_mut",
"undefined_data",
]
resolver = "2"

Expand All @@ -22,3 +23,5 @@ wasm-bindgen = "=0.2.127"
wasm-bindgen-futures = "0.4.55"
wasm-bindgen-test = "0.3.55"
tokio = { version = "1.48", features = ["rt", "macros"] }
autocfg = "1"
rustversion = "1"
19 changes: 19 additions & 0 deletions integration/undefined_data/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
[package]
name = "undefined_data"
version.workspace = true
edition.workspace = true
publish.workspace = true

[lib]
crate-type = ["cdylib"]

[dependencies]
wasm-bindgen.workspace = true
wasm_split_helpers.workspace = true

[dev-dependencies]
wasm-bindgen-test.workspace = true

[build-dependencies]
autocfg.workspace = true
rustversion.workspace = true
31 changes: 31 additions & 0 deletions integration/undefined_data/build.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
const WEAK_LINKAGE_PROBE: &str = r#"
extern "C" {
#[linkage = "extern_weak"]
static _OPTIONAL_VARIABLE: Option<&'static i32>;
}
"#;

fn main() {
println!("cargo:rerun-if-changed=build.rs");

let is_nightly = rustversion::cfg!(nightly);
let ac = autocfg::new();
let has_weak_linkage = ac.probe_raw(WEAK_LINKAGE_PROBE).is_ok();
let has_weak_linkage_needs_feature = !has_weak_linkage
&& ac
.probe_raw(&format!("#![feature(linkage)]\n{WEAK_LINKAGE_PROBE}"))
.is_ok();
assert!(
!is_nightly || (has_weak_linkage || has_weak_linkage_needs_feature),
"expected weak linkage to work on nightly"
);

autocfg::emit_possibility("has_weak_linkage");
autocfg::emit_possibility("has_weak_linkage_needs_feature");
if has_weak_linkage || has_weak_linkage_needs_feature {
autocfg::emit("has_weak_linkage");
}
if has_weak_linkage_needs_feature {
autocfg::emit("has_weak_linkage_needs_feature");
}
}
60 changes: 60 additions & 0 deletions integration/undefined_data/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
//! The main module references a data symbol with weak linkage. This will
//! resolves every reference to address 0, but will still leaves a
//! `R_WASM_MEMORY_ADDR_*` relocation against it. The splitter must accept
//! such a module.
//!
//! This is to mock a bug (rust-lang/rust#81280) occuring in the wild. In
//! this case, rustc incremental builds produce a similar shape. Here, an
//! undefined reference to a promoted anonymous global whose name changed
//! gets resolves to address 0 with a linker arg, `--allow-undefined`,
//! passed to wasm-ld as a workaround.
#![cfg_attr(has_weak_linkage_needs_feature, feature(linkage))]

use wasm_split_helpers::wasm_split;

#[cfg(has_weak_linkage)]
pub mod weak {
unsafe extern "C" {
#[linkage = "extern_weak"]
safe static UNDEFINED_DATA: Option<&'static i32>;
}

/// The address the linker resolved for the undefined data symbol.
pub fn undefined_data_address() -> usize {
// Taking the address, not reading through it, keeps the relocation
// against the undefined symbol without touching address 0.
UNDEFINED_DATA
.map(|data| (data as *const i32).addr())
.unwrap_or(0)
}
}

#[wasm_split(split)]
fn lazy() -> u32 {
42
}

#[cfg(test)]
mod tests {
use wasm_bindgen_test::wasm_bindgen_test as test;

// This pulls in the magic marker which we assert in all tests
const _: () = {
let _ = wasm_split_helpers::rt::ensure_loaded;
};

#[cfg(has_weak_linkage)]
#[test]
fn undefined_data_keeps_the_linker_address() {
assert_eq!(
super::weak::undefined_data_address(),
0,
"an undefined data symbol resolves to address 0 and must not be relocated"
);
}

#[test]
async fn split_still_loads() {
assert_eq!(super::lazy().await, 42);
}
}