wasmerio/wasmer · error
Failed to convert u64 address to M::Offset
Error message
Failed to convert u64 address to M::Offset
What it means
The dlsym syscall resolves a symbol to a 64-bit address (function pointer or global data pointer) and writes it out to guest memory. The address must fit in the memory's Offset type (u32 for memory32); if a u64 address exceeds it, dlsym panics with "Failed to convert u64 address to M::Offset".
Source
Thrown at lib/wasix/src/syscalls/wasix/dlsym.rs:59
Some(ModuleHandle::from(handle))
};
let symbol = linker.resolve_export(&mut ctx, handle, &symbol);
let (env, mut store) = ctx.data_and_store_mut();
let memory = unsafe { env.memory_view(&store) };
let symbol = wasi_try_dl!(
symbol,
"failed to resolve symbol: {}",
memory,
err_buf,
err_buf_len
);
match symbol {
ResolvedExport::Function { func_ptr: addr } | ResolvedExport::Global { data_ptr: addr } => {
let Ok(addr) = addr.try_into() else {
panic!("Failed to convert u64 address to M::Offset");
};
wasi_try_mem_ok!(out_symbol.write(&memory, addr));
}
}
Ok(Errno::Success)
}
View on GitHub (pinned to 8c4b9ee9d3)
Solutions
- Ensure the loaded module and the calling instance both use matching memory index types (32-bit or 64-bit).
- Recompile the shared module with a 32-bit memory if the host instance uses memory32.
- If you maintain the runtime, write the full u64 into an i64 out-parameter or return Errno::Overflow instead of panicking.
- Verify the resolved address via the module's exports before calling dlsym on suspicious symbols.
Example fix
// before (module (memory i64 1)) ;; memory64 module loaded into 32-bit instance // after (module (memory 1)) ;; 32-bit memory matching the host instance
Defensive patterns
Strategy: validation
Validate before calling
// Ensure the dlopen'ed module uses the same memory index type as the caller
// Reject memory64 modules before dlopen:
fn uses_memory64(module: &wasmer::Module) -> bool {
module.imports().filter(|i| i.ty().memory().map(|m| m.index_type()) == Some(wasmer::IndexType::I64)).count() > 0
} Type guard
fn fits_in_memory32(addr: u64) -> bool { addr <= u32::MAX as u64 } Prevention
- Match memory index types (i32 vs i64) between the host instance and all dynamically loaded modules.
- Compile shared libraries with 32-bit memories when the main module uses memory32.
- Prefer a 64-bit memory instance if you expect addresses above 4GiB.
- Validate resolved addresses before writing them to 32-bit out-pointers.
When it happens
Trigger: Calling dlsym on a module whose resolved export function pointer or global data address exceeds 4GiB while running with a 32-bit memory (M = Memory32).
Common situations: dlopen'ing a module compiled for 64-bit memories (memory64) into a 32-bit-memory instance and then looking up its symbols; oversized/incorrect pointer values returned by a broken resolver.
Related errors
- Internal error: failed to resolve exported function {}: {e:?
- Internal error: Tried to import TLS symbol from module {} th
- Internal error: missing import resolution '{0}'.{1}
- Internal error: bad in-progress symbol resolution: {e:?}
- Internal error: bad in-progress symbol resolution
AI-assisted analysis of wasmerio/wasmer@8c4b9ee9d3 (2026-09-01).
Data as JSON: /api/errors/a77ec72c1b3d5ef1.
Report an issue: GitHub.