Symptom
On the deployed wasm32-hwjs glibc image, hexdump <file> (util-linux, argv form) fails:
$ printf ABC > /tmp/f && hexdump -C /tmp/f
hexdump: /tmp/f: Function not implemented
while reading the same file on stdin works:
$ printf ABC | hexdump -C
00000000 41 42 43 |ABC|
and coreutils od /tmp/f reads the file fine. So it is not a file/VFS problem — it is specific to how hexdump opens a named file.
Root cause
util-linux hexdump opens a named file by reopening it onto stdin: freopen(name, "r", stdin) (text-utils/hexdump-display.c:next()). On glibc 2.43 freopen implements the fd move with dup3(newfd, 0, 0). In arch/wasm32/kernel/syscall_table.c the dup3 slot (x86_64 #292) is default-NULL — dup (#32), dup2 (#33) and fcntl (#72) are wired, but dup3 is not — so do_syscall returns -ENOSYS, freopen returns NULL, and hexdump reports the errno. od uses a plain open()/fopen() with no fd move, which is why it is unaffected.
This is not hexdump-specific: any freopen() user, and any direct dup3(2) caller, hits -ENOSYS on this kernel.
Reproduction
Deployed machine: sasha/core-image-brintos-emcraft-glibc (util-linux 2.39.3, glibc 2.43). In the browser terminal:
printf ABC > /tmp/f && hexdump -C /tmp/f # -> hexdump: /tmp/f: Function not implemented
printf ABC | hexdump -C # -> 00000000 41 42 43 |ABC| (works)
Evidence in-tree: grep -nE '\[(32|33|292)\]\s*=' arch/wasm32/kernel/syscall_table.c shows [32]/[33] wired and no [292].
Proposed fix
Wire dup3 in the wasm32 table, in the same shape as the existing dup2 entry (3-arg: dup3(oldfd, newfd, flags)):
--- a/arch/wasm32/kernel/syscall_table.c
+++ b/arch/wasm32/kernel/syscall_table.c
@@
extern long sys_dup2(unsigned int oldfd, unsigned int newfd);
+extern long sys_dup3(unsigned int oldfd, unsigned int newfd, int flags);
@@
static long w_sys_dup2(long a0, long a1, long a2, long a3, long a4, long a5)
{ IGNORE_TAIL_4; return sys_dup2((unsigned int)a0, (unsigned int)a1); }
+
+/* dup3(oldfd, newfd, flags) — 3 args (glibc freopen()/dup2 -> dup3) */
+static long w_sys_dup3(long a0, long a1, long a2, long a3, long a4, long a5)
+{ IGNORE_TAIL_3; return sys_dup3((unsigned int)a0, (unsigned int)a1, (int)a2); }
@@
[33] = w_sys_dup2, /* dup2 */
+ [292] = w_sys_dup3, /* dup3 (glibc freopen; util-linux hexdump <file>) */
Candidate, not yet built — flagged because of the alias-materialization caveat from #10: if the plain sys_dup3 reference leaves a dangling 0xFFFFFFFF relocation (the SYSCALL_DEFINE alias non-materialization that broke the kernel link for sys_capget/sys_capset), reference __se_sys_dup3 instead, exactly as #10 recommends for capget/capset. dup2 links today with the plain sys_dup2, so the plain form is the first thing to try.
Workaround in the meantime: dump a file on stdin (hexdump -C < file), the identical formatting path.
Proposed fix targets brintos/linux only — no distro submodule pins moved. All distros share one lineage, so pin bumps are coordinated on merge, not per-engineer.