Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
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
150 changes: 143 additions & 7 deletions libwild/src/wasm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1672,6 +1672,9 @@ fn build_name_section<'data>(
if let Some(idx) = indices.tls_base_global {
set_name_first_wins(&mut global_names, idx, "__tls_base");
}
for &(known, idx) in &indices.data_address_globals {
set_name_first_wins(&mut global_names, idx, <&str>::from(known));
}
if let Some(got_base) = indices.got_mem_global_base {
got_mem_names.reserve(got_mem.entries.len());
for (i, entry) in got_mem.entries.iter().enumerate() {
Expand Down Expand Up @@ -3945,6 +3948,7 @@ struct LinkerDefinedIndices {
/// First module global index for GOT.func entries.
got_func_global_base: Option<u32>,
got_func_count: u32,
data_address_globals: Vec<(WasmLinkerSymbol, u32)>,
}

/// Where a GOT.mem slot's final linear-memory address comes from.
Expand Down Expand Up @@ -4061,8 +4065,10 @@ fn setup_got_mem_and_indices<'data>(
shared_imports.function_count(),
shared_imports.global_count(),
weak_undef_stubs,
LinkerDefinedIndexRequest {
&LinkerDefinedIndexRequest {
has_init_funcs,
export_symbols: requested_linker_export_symbols(symbol_db.args),
has_memory: any_object_needs_linker_memory(layout_inputs),
wrap_entry,
got_mem_count: scan.got_mem.len(),
got_func_count: scan.got_func.len(),
Expand Down Expand Up @@ -4740,6 +4746,39 @@ fn fill_got_mem_inits(
Ok(())
}

fn fill_exported_data_global_inits(
layout: &mut WasmLayout<'_>,
indices: &LinkerDefinedIndices,
data_start: u32,
data_end: u32,
stack_size: u32,
heap_end: Option<u32>,
stack_first: bool,
) -> Result {
for &(known, global_index) in &indices.data_address_globals {
let addr = known
.data_address(data_start, data_end, stack_size, heap_end, stack_first)?
.ok_or_else(|| {
crate::error!(
"linker-defined symbol `{}` has no address to export",
std::str::from_utf8(known.name()).unwrap_or("?")
)
})?;
let defined_slot = (global_index - indices.global_import_count) as usize;
let global = layout.globals.get_mut(defined_slot).ok_or_else(|| {
crate::error!("exported data global slot {defined_slot} out of range")
})?;
let addr_i32 = i32::try_from(addr).map_err(|_| {
Comment thread
lapla-cogito marked this conversation as resolved.
Outdated
crate::error!(
"exported data address for `{}` out of i32 range",
std::str::from_utf8(known.name()).unwrap_or("?")
)
})?;
global.init_expr_body = Cow::Owned(encode_i32_const_body(addr_i32));
}
Ok(())
}

/// Fill GOT.func globals with table indices. Requires the indirect function table first. Undefined
/// weak targets resolve through `function_indices` to unreachable stubs.
fn fill_got_func_inits(
Expand Down Expand Up @@ -4828,9 +4867,11 @@ fn entry_is_defined_function(
!sym.is_undefined() && sym.kind == WasmSymbolKind::Func
}

#[derive(Clone, Copy)]
struct LinkerDefinedIndexRequest {
has_init_funcs: bool,
// Linker symbols named by `--export` / `--export-if-defined`.
export_symbols: Vec<WasmLinkerSymbol>,
has_memory: bool,
wrap_entry: bool,
got_mem_count: u32,
got_func_count: u32,
Expand All @@ -4845,13 +4886,37 @@ impl LinkerDefinedIndices {
function_import_count: u32,
global_import_count: u32,
mut weak_undef_stubs: Vec<WeakUndefFunctionStub>,
request: LinkerDefinedIndexRequest,
request: &LinkerDefinedIndexRequest,
) -> Result<Self> {
let mut needs_memory_base = request.needs_memory_base;
let mut needs_table_base = request.needs_table_base;
let mut needs_stack_pointer = false;
let mut needs_tls_base = false;
let mut needs_ctors = request.has_init_funcs;
let mut export_data = Vec::new();
for &sym in &request.export_symbols {
if !sym.materialize_on_export() {
continue;
}
match sym {
WasmLinkerSymbol::CallCtors => needs_ctors = true,
Comment thread
lapla-cogito marked this conversation as resolved.
Outdated
WasmLinkerSymbol::MemoryBase => needs_memory_base = true,
WasmLinkerSymbol::TableBase => needs_table_base = true,
WasmLinkerSymbol::StackPointer => needs_stack_pointer = true,
WasmLinkerSymbol::TlsBase => {}
WasmLinkerSymbol::HeapEnd if !request.has_memory => {}
WasmLinkerSymbol::DataEnd
| WasmLinkerSymbol::GlobalBase
| WasmLinkerSymbol::HeapBase
| WasmLinkerSymbol::HeapEnd
| WasmLinkerSymbol::WasmFirstPageEnd
| WasmLinkerSymbol::DsoHandle => {
if !export_data.contains(&sym) {
export_data.push(sym);
}
}
}
}

for (input, resolutions) in layout_inputs.iter().zip(import_resolutions.iter()) {
let absorption = LinkerImportAbsorption::from_resolutions(
Expand Down Expand Up @@ -4890,6 +4955,13 @@ impl LinkerDefinedIndices {
next_global += 1;
idx
});
let mut data_address_globals = Vec::with_capacity(export_data.len());
for known in export_data {
data_address_globals.push((known, next_global));
next_global = next_global
.checked_add(1)
.ok_or_else(|| crate::error!("Wasm global index overflow"))?;
}
let got_mem_global_base = if request.got_mem_count > 0 {
let base = next_global;
next_global = next_global
Expand Down Expand Up @@ -4945,6 +5017,7 @@ impl LinkerDefinedIndices {
got_mem_count: request.got_mem_count,
got_func_global_base,
got_func_count: request.got_func_count,
data_address_globals,
})
}

Expand All @@ -4954,7 +5027,11 @@ impl LinkerDefinedIndices {
WasmLinkerSymbol::TableBase => self.table_base_global,
WasmLinkerSymbol::StackPointer => self.stack_pointer_global,
WasmLinkerSymbol::TlsBase => self.tls_base_global,
_ => None,
other => self
.data_address_globals
.iter()
.find(|(sym, _)| *sym == other)
.map(|(_, idx)| *idx),
}
}

Expand Down Expand Up @@ -5200,6 +5277,16 @@ fn emit_reserved_linker_definitions(
init_expr_body: Cow::Borrowed(ZERO_I32_INIT_EXPR),
});
}
for _ in &indices.data_address_globals {
linker_globals.push(OutputGlobal {
ty: GlobalType {
content_type: wasmparser::ValType::I32,
mutable: false,
shared: false,
},
init_expr_body: Cow::Borrowed(ZERO_I32_INIT_EXPR),
});
}
// GOT.mem placeholders. wasm-ld emits static GOT.data.internal.* as immutable i32 for
// freestanding executables.
for _ in 0..indices.got_mem_count {
Expand Down Expand Up @@ -5499,17 +5586,52 @@ fn ensure_entry_export<'data>(
});
}

fn requested_linker_export_symbols(args: &WasmArgs) -> Vec<WasmLinkerSymbol> {
let mut symbols = Vec::new();
for name in args.force_export_symbol_names() {
let Some(sym) = WasmLinkerSymbol::parse(name) else {
Comment thread
lapla-cogito marked this conversation as resolved.
continue;
};
if !symbols.contains(&sym) {
symbols.push(sym);
}
}
symbols
}

fn try_export_linker_defined(
exports: &mut Vec<OutputExport<'_>>,
known: WasmLinkerSymbol,
indices: &LinkerDefinedIndices,
) -> bool {
let name = <&str>::from(known);
if let Some(index) = indices.function_index(known) {
push_function_export(exports, name, index);
return true;
}
if let Some(index) = indices.global_index(known) {
push_global_export(exports, name, index);
return true;
}
false
}

/// Export symbols requested via `--export` and `--export-if-defined`.
fn ensure_force_exports<'data>(
exports: &mut Vec<OutputExport<'data>>,
layout_inputs: &[WasmObjectLayoutInput<'data>],
object_index_maps: &[WasmObjectIndexMap],
symbol_db: &SymbolDb<'data, Wasm>,
entry: Option<&ResolvedEntry<'data>>,
entry_wrapper_func: Option<u32>,
indices: &LinkerDefinedIndices,
) -> Result<()> {
for name in symbol_db.args.force_export_symbol_names() {
let required = symbol_db.args.required_export_symbols.contains(name);
if let Some(known) = WasmLinkerSymbol::parse(name)
&& try_export_linker_defined(exports, known, indices)
{
continue;
}
let Some(symbol_id) =
symbol_db.get_unversioned(&UnversionedSymbolName::prehashed(name.as_bytes()))
else {
Expand Down Expand Up @@ -5550,7 +5672,7 @@ fn ensure_force_exports<'data>(
let mut index =
remap_wasm_index(&index_map.function_indices, def_sym.index, "function")?;
// If this is the entry and we wrap it, export the wrapper.
if let (Some(entry), Some(wrapper)) = (entry, entry_wrapper_func)
if let (Some(entry), Some(wrapper)) = (entry, indices.entry_wrapper_func)
&& export_name == entry.export_name
{
index = wrapper;
Expand Down Expand Up @@ -5820,6 +5942,15 @@ where
heap_end,
stack_first,
)?;
fill_exported_data_global_inits(
&mut layout,
&indices,
data_start,
data_end,
stack_size,
heap_end,
stack_first,
)?;
fill_stack_pointer_init(&mut layout, &indices, stack_size, stack_first)?;
ensure_entry_export(
&mut layout.exports,
Expand All @@ -5832,7 +5963,7 @@ where
&layout.object_index_maps,
symbol_db,
entry.as_ref(),
indices.entry_wrapper_func,
&indices,
)?;
}
{
Expand Down Expand Up @@ -6072,6 +6203,11 @@ impl WasmLinkerSymbol {
name.parse().ok()
}

fn materialize_on_export(self) -> bool {
// `--export` materializes every linker symbol except `__tls_base`.
!matches!(self, Self::TlsBase)
}

fn matches_import_kind(self, kind: WasmSymbolKind) -> bool {
match self {
Self::CallCtors => kind == WasmSymbolKind::Func,
Expand Down
39 changes: 39 additions & 0 deletions wild/tests/sources/wasm/export-linker-syms/export-linker-syms.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
// `--export` of linker-defined symbols.

//#Config:linker-globals
//#LinkArgs: --export=__wasm_call_ctors --export=__stack_pointer --export=__memory_base --export=__table_base --export=__heap_base --export=__data_end --export=__global_base --export=__dso_handle --export=__heap_end --export=__wasm_first_page_end
//#ExpectSym: __wasm_call_ctors
//#ExpectSym: __stack_pointer
//#ExpectSym: __memory_base
//#ExpectSym: __table_base
//#ExpectSym: __heap_base
//#ExpectSym: __data_end
//#ExpectSym: __global_base
//#ExpectSym: __dso_handle
//#ExpectSym: __heap_end
//#ExpectSym: __wasm_first_page_end
//#ExpectSym: _start

//#Config:export-if-defined
//#LinkArgs: --export-if-defined=__wasm_call_ctors --export-if-defined=__stack_pointer --export-if-defined=__heap_base
//#ExpectSym: __wasm_call_ctors
//#ExpectSym: __stack_pointer
//#ExpectSym: __heap_base
//#ExpectSym: _start

//#Config:no-entry
//#LinkArgs: --no-entry --export=__wasm_call_ctors
//#RunEnabled: false
//#ExpectSym: __wasm_call_ctors
//#NoSym: _start

//#Config:tls-base
//#LinkArgs: --export=__tls_base
//#ExpectError: symbol exported via --export not found: __tls_base

//#Config:tls-base-if-defined
//#LinkArgs: --export-if-defined=__tls_base
//#NoSym: __tls_base
//#ExpectSym: _start

void _start(void) {}
Loading