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

  1. Ensure the loaded module and the calling instance both use matching memory index types (32-bit or 64-bit).
  2. Recompile the shared module with a 32-bit memory if the host instance uses memory32.
  3. If you maintain the runtime, write the full u64 into an i64 out-parameter or return Errno::Overflow instead of panicking.
  4. 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

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


AI-assisted analysis of wasmerio/wasmer@8c4b9ee9d3 (2026-09-01). Data as JSON: /api/errors/a77ec72c1b3d5ef1. Report an issue: GitHub.