nodejs can't work
Probably statx is missing
Hi @dex4er, can you expand what's missing for nodejs to function?
I was able to run node --version, node -e "console.log(1 + 1)" and even node inspect, so clearly some subset of the node runtime is functional. The problem seems to be some internal loader isn't able to load any scripts. For example, npm (which just a wrapper over node /path/to/npm.js) fails with:
internal/modules/cjs/loader.js:983
throw err;
^
Error: Cannot find module '/usr/local/bin/npm'
at Function.Module._resolveFilename (internal/modules/cjs/loader.js:980:15)
at Function.Module._load (internal/modules/cjs/loader.js:862:27)
at Function.executeUserEntryPoint [as runMain] (internal/modules/run_main.js:74:12)
at internal/main/run_main_module.js:18:47 {
code: 'MODULE_NOT_FOUND',
requireStack: []
}
For others that are reading this, I found out that nodejs uses libuv, which in turns usually uses glibc wrappers for syscalls. However, a recent change from last year made it start using statx, a relatively new syscall. Fakechroot works by shimming glibc calls using LD_PRELOAD so it can't shim the syscall libuv is calling directly.
So if you want to run nodejs in fakechroot you have three options:
- If nodejs is dynamically linked to libuv (via
--shared-libuvbuild flag), then you can patch libuv. - If nodejs is statically linked to libuv (the much more common one), then you have to patch libuv in the nodejs repository, and build node yourself. I built one for node
v12.6.2and I pushed the binary to DockerHubopenllb/node:fakechrootfor my usage. - The proper way is to contribute to libuv. Glibc 2.28 already had
statxsupport, so we just need to update libuv to that, and then bump libuv in nodejs.
Here's a gist to the git patch I applied, as well as stacktraces on how I determined the fix: https://gist.github.com/hinshun/ba1a8a7d32e8e974dcd60cbce64438d6
I also commented on the libuv issue but, while libuv could work around this limitation in fakechroot, it's IMO better to teach it about syscall() - syscall(__NR_statx) in particular.
Libuv also uses syscall() for other things it can't rely on being available from libc:
$ git grep -n 'syscall[(]' src/
src/unix/core.c:531: return syscall(SYS_close, fd);
src/unix/linux-syscalls.c:136: return syscall(__NR_sendmmsg, fd, mmsg, vlen, flags);
src/unix/linux-syscalls.c:149: return syscall(__NR_recvmmsg, fd, mmsg, vlen, flags, timeout);
src/unix/linux-syscalls.c:158: return syscall(__NR_preadv, fd, iov, iovcnt, (long)offset, (long)(offset >> 32));
src/unix/linux-syscalls.c:167: return syscall(__NR_pwritev, fd, iov, iovcnt, (long)offset, (long)(offset >> 32));
src/unix/linux-syscalls.c:176: return syscall(__NR_dup3, oldfd, newfd, flags);
src/unix/linux-syscalls.c:192: return syscall(__NR_statx, dirfd, path, flags, mask, statxbuf);
src/unix/linux-syscalls.c:201: return syscall(__NR_getrandom, buf, buflen, flags);
src/unix/random-sysctl-linux.c:71: if (syscall(SYS__sysctl, &args) == -1)
(SYS_close is used because glibc's close() is a cancellation point.)