uce/spikes/wasm-phase0/realunit-report.md
2026-06-12 11:40:26 +00:00

7.0 KiB

UCE real generated unit -> WASM PIC side module report

Remote host: k-uce
Working directory/artifacts left in place: /tmp/uce/wasm-phase0/realunit/
Repository /Code/uce.openfu.com/uce was treated read-only; patched copies/shims only under /tmp/uce/wasm-phase0/realunit/.

Result

Both real generated units compiled and linked as WASM PIC side modules with dylink.0:

  • collections.uce.cpp -> /tmp/uce/wasm-phase0/realunit/collections.wasm
    • dylink.0: memsize=1208 memalign=2^2 tablesize=3 tablealign=2^0
    • imports: 52
    • exports: 23
    • allocator status: no exported/defined malloc, free, or operator new; operator new/sized delete are imports (env._Znwm, env._ZdlPvm).
  • hello.uce.cpp -> /tmp/uce/wasm-phase0/realunit/hello.wasm
    • dylink.0: memsize=5544 memalign=2^2 tablesize=0 tablealign=2^0
    • imports: 38
    • exports: 24
    • allocator status: no exported/defined malloc, free, or operator new; operator new/sized delete are imports (env._Znwm, env._ZdlPvm).

Inspector outputs are saved remotely as:

  • /tmp/uce/wasm-phase0/realunit/collections.inspect.txt
  • /tmp/uce/wasm-phase0/realunit/hello.inspect.txt

Final command lines

Run on k-uce from /tmp/uce/wasm-phase0/realunit.

collections:

/opt/wasi-sdk/bin/clang++ --target=wasm32-wasip1 -fPIC -fvisibility=default -O1 -fno-exceptions \
  -I/tmp/uce/wasm-phase0/realunit/shim -I/Code/uce.openfu.com/uce/src/lib \
  -c collections.uce.cpp -o collections.o

/opt/wasi-sdk/bin/wasm-ld -shared --experimental-pic --unresolved-symbols=import-dynamic \
  collections.o -o collections.wasm --export=__uce_render

python3 /Code/uce.openfu.com/uce/spikes/wasm-phase0/wasm_inspect.py collections.wasm

hello:

/opt/wasi-sdk/bin/clang++ --target=wasm32-wasip1 -fPIC -fvisibility=default -O1 -fno-exceptions \
  -I/tmp/uce/wasm-phase0/realunit/shim -I/Code/uce.openfu.com/uce/src/lib \
  -c hello.uce.cpp -o hello.o

/opt/wasi-sdk/bin/wasm-ld -shared --experimental-pic --unresolved-symbols=import-dynamic \
  hello.o -o hello.wasm --export=__uce_render

Note: the stub-spike command exported uce_unit_render, but these generated units define/export __uce_render from the repo RENDER macro, so the real-unit link exports __uce_render.

Ordered friction points and fixes

  1. WASI sysroot rejects <signal.h> by default.

    First compile failed via /Code/uce.openfu.com/uce/src/lib/sys.h:

    /share/wasi-sysroot/include/wasm32-wasip1/signal.h:2:2: error:
    "wasm lacks signal support; to enable minimal signal emulation, compile with -D_WASI_EMULATED_SIGNAL ..."
    

    Fix: added /tmp/uce/wasm-phase0/realunit/shim/signal.h before repo/system includes. Key lines:

    typedef int sig_atomic_t;
    typedef void (*sighandler_t)(int);
    #define SIGTERM 15
    int raise(int);
    sighandler_t signal(int, sighandler_t);
    

    This is only enough for declarations in sys.h; real signal behavior has no WASI equivalent.

  2. Generated collections.uce.cpp includes missing local demo_guard.h.

    First compile also failed:

    fatal error: 'demo_guard.h' file not found
    

    Fix: added empty include guard shim /tmp/uce/wasm-phase0/realunit/shim/demo_guard.h:

    #ifndef UCE_WASM_SHIM_DEMO_GUARD_H
    #define UCE_WASM_SHIM_DEMO_GUARD_H
    #endif
    
  3. Generated source uses an absolute repo include, preventing header interposition.

    Original generated line:

    #include "/Code/uce.openfu.com/uce/src/lib/uce_lib.h"
    

    To use patched shim headers without touching /Code, copied the generated units into the workdir and changed only that line to:

    #include "uce_lib.h"
    

    Copies are /tmp/uce/wasm-phase0/realunit/collections.uce.cpp and hello.uce.cpp.

  4. types.h defines global operator new/delete in every unit.

    A straight build linked, but exported allocator definitions (_Znwm, _ZdlPv), violating the side-module allocator rule. The repo code in types.h contains:

    void * operator new(decltype(sizeof(0)) n) noexcept(false) { ... malloc(n) ... }
    void operator delete(void * p) throw() { free(p); }
    

    Fix: copied uce_lib.h and types.h to the shim tree, then patched only the copied shim/types.h allocator block to declarations:

    // WASM side-module shim: allocator must be provided by the host/core module,
    // not defined in each generated unit. Keep declarations only.
    void * operator new(decltype(sizeof(0)) n) noexcept(false);
    void operator delete(void * p) throw();
    

    Verification from llvm-nm -C collections.o:

             U operator delete(void*, unsigned long)
             U operator new(unsigned long)
    

Notable imports / GOT entries

collections.wasm notable imports:

  • allocator/runtime: env._Znwm, env._ZdlPvm, libc++ string/iostream helpers, env.__stack_pointer, env.memory, env.__indirect_function_table
  • UCE/runtime functions: DValue::set, DValue::operator[], DValue::push, dv_filter, dv_map, dv_group_by, list_unique, list_sort, replace, to_upper, html_escape, json_encode, var_dump, time
  • MySQL wrappers are imported even for this unit because included header inline functions reference MySQL methods.
  • GOT.mem globals:
    • GOT.mem.context
    • GOT.mem._ZNSt3__25ctypeIcE2idE
    • GOT.mem._ZTVN10__cxxabiv117__class_type_infoE

hello.wasm notable imports:

  • allocator/runtime: env._Znwm, env._ZdlPvm, libc++ string/iostream helpers, env.memcmp
  • UCE/runtime functions: time, html_escape, html_escapey, var_dump
  • GOT.mem globals:
    • GOT.mem.context
    • GOT.mem._ZNSt3__219piecewise_constructE
    • GOT.mem._ZNSt3__25ctypeIcE2idE

Phase 2 land mines

  • src/lib/sys.h includes <signal.h> and declares signals/tasks/socket/process/file-lock APIs. WASI has no normal POSIX signals, fork/exec, traditional sockets, or process model. These need real repo-level #ifdef __wasm__ / runtime boundary design, not local shims.
  • types.h defines global allocator operators in a header. For side modules this must move to the core/runtime or be gated out under WASM side-module builds; otherwise each unit defines its own allocator entry points.
  • Generated units include /Code/.../uce_lib.h by absolute path. That makes cross-target header selection brittle. The generator should emit a logical include ("uce_lib.h") and the build should supply target-specific include order.
  • Header-defined helper functions cause many exports from each unit (to_string, MySQL wrapper functions, vector slow-path/lambda helpers, globals parent_pid, my_pid, context). For a clean side-module ABI, these should probably be hidden, moved out of headers, marked inline/static where appropriate, or provided by the core module.
  • MySQL inline wrappers are pulled into every unit by uce_lib.h even when the unit does not use MySQL directly. This is why MySQL::connect/disconnect/query/error imports appear in both outputs.
  • Kept -fno-exceptions; no try/catch blocker was hit for these two generated units.