From 003f4ecd3b4ee16f8502f603643c566de499fdba Mon Sep 17 00:00:00 2001 From: Delete228 <31237303+Delete228@users.noreply.github.com> Date: Tue, 20 Oct 2026 16:38:14 +0300 Subject: [PATCH 1/8] fix: restore Factorio ARM64 controller input through readv Steam's ARM64 overlay can fall back from read() to readv(). Route registered controller reads through the existing private ring reader so the game receives input events instead of backing-file bytes. Preserve ordinary descriptor passthrough and evdev per-vector reads. --- app/src/main/cpp/fakeinput_steam.cpp | 41 +++++++++++++++++++++++++++- 1 file changed, 40 insertions(+), 1 deletion(-) diff --git a/app/src/main/cpp/fakeinput_steam.cpp b/app/src/main/cpp/fakeinput_steam.cpp index 486b72076..2efba76ce 100644 --- a/app/src/main/cpp/fakeinput_steam.cpp +++ b/app/src/main/cpp/fakeinput_steam.cpp @@ -3577,7 +3577,7 @@ EXPORT int close(int fd) { static constexpr int kMaxRingWaitMs = 2; static constexpr int kLowLatencyRingWaitUs = 250; -EXPORT ssize_t read(int fd, void *buf, size_t count) { +static ssize_t read_input(int fd, void *buf, size_t count) { if (!watched(fd)) return syscall(SYS_read, fd, buf, count); std::unique_lock guard(controller_mutex()); auto controller = controller_map().find(fd); @@ -3673,6 +3673,45 @@ EXPORT ssize_t read(int fd, void *buf, size_t count) { } } +EXPORT ssize_t read(int fd, void *buf, size_t count) { + return read_input(fd, buf, count); +} + +EXPORT ssize_t readv(int fd, const struct iovec *iov, int iovcnt) { + if (!is_fake_input_fd(fd)) return syscall(SYS_readv, fd, iov, iovcnt); + if (iovcnt < 0 || iovcnt > IOV_MAX) { + errno = EINVAL; + return -1; + } + if (iovcnt && !iov) { + errno = EFAULT; + return -1; + } + size_t size = 0; + for (int i = 0; i < iovcnt; ++i) { + if (iov[i].iov_len > static_cast(SSIZE_MAX) - size) { + errno = EINVAL; + return -1; + } + size += iov[i].iov_len; + if (iov[i].iov_len && !iov[i].iov_base) { + errno = EFAULT; + return -1; + } + } + + // evdev's readv calls its read handler per vector; Steam's ARM64 overlay uses this path too. + ssize_t total = 0; + for (int i = 0; i < iovcnt; ++i) { + if (!iov[i].iov_len) continue; + ssize_t got = read_input(fd, iov[i].iov_base, iov[i].iov_len); + if (got < 0) return total ? total : -1; + total += got; + if (static_cast(got) != iov[i].iov_len) break; + } + return total; +} + EXPORT ssize_t write(int fd, const void *buf, size_t count) { static auto my_write = reinterpret_cast(dlsym(RTLD_NEXT, "write")); From f817af960714ae296abbbac90d1a5a70bccd6ad4 Mon Sep 17 00:00:00 2001 From: Delete228 <31237303+Delete228@users.noreply.github.com> Date: Tue, 20 Oct 2026 16:42:24 +0300 Subject: [PATCH 2/8] test: cover vectored controller reads Cover readv on original and virtual controller rings, an overlay-style read-to-readv fallback, event-aligned vectors, empty vectors, nonblocking errors, and ordinary descriptor passthrough. --- tools/tests/test_fakeinput_ring.py | 140 ++++++++++++++++++++++++++--- 1 file changed, 127 insertions(+), 13 deletions(-) diff --git a/tools/tests/test_fakeinput_ring.py b/tools/tests/test_fakeinput_ring.py index 3c87bd11e..42d6f85b6 100644 --- a/tools/tests/test_fakeinput_ring.py +++ b/tools/tests/test_fakeinput_ring.py @@ -18,24 +18,44 @@ READER = r""" #include #include #include +#include #include -int main(void) +static int vector_count; + +static ssize_t controller_read(int fd, void *buffer, size_t size) { + if (!vector_count) + return read(fd, buffer, size); + struct iovec vectors[3] = { + {buffer, sizeof(struct input_event)}, + {(char *)buffer + sizeof(struct input_event), 0}, + {(char *)buffer + sizeof(struct input_event), size - sizeof(struct input_event)}, + }; + if (vector_count == 1) { + vectors[0].iov_len = size; + return readv(fd, vectors, 1); + } + return readv(fd, vectors, 3); +} + +int main(int argc, char **argv) +{ + vector_count = argc > 1 ? (argv[1][0] == '1' ? 1 : 3) : 0; struct input_event events[64]; int fd = open("/dev/input/event16", O_RDONLY | O_NONBLOCK); if (fd < 0) { perror("open"); return 1; } - while (read(fd, events, sizeof(events)) > 0) + while (controller_read(fd, events, sizeof(events)) > 0) ; puts("ready"); fflush(stdout); char go; if (read(0, &go, 1) != 1) return 1; - ssize_t got = read(fd, events, sizeof(events)); + ssize_t got = controller_read(fd, events, sizeof(events)); for (ssize_t i = 0; i < got / (ssize_t)sizeof(events[0]); i++) printf("%ld.%06ld %u %u %d\n", (long)events[i].time.tv_sec, (long)events[i].time.tv_usec, events[i].type, events[i].code, events[i].value); @@ -65,6 +85,61 @@ int main(void) return 0; } """ +READV_CHECKS = r""" +#include +#include +#include +#include +#include +#include +#include + +int main(void) +{ + struct input_event events[64]; + int fd = open("/dev/input/event16", O_RDONLY | O_NONBLOCK); + if (fd < 0) + return 1; + while (read(fd, events, sizeof(events)) > 0) + ; + struct iovec vector = {events, sizeof(events)}; + if (readv(fd, &vector, 1) != -1 || errno != EAGAIN) + return 2; + if (readv(fd, NULL, 0) != 0) + return 3; + vector.iov_len = 0; + if (readv(fd, &vector, 1) != 0) + return 4; + vector.iov_len = sizeof(struct input_event) - 1; + if (readv(fd, &vector, 1) != -1 || errno != EINVAL) + return 5; + ssize_t (*invoke)(int, const struct iovec *, int) = readv; + if (invoke(fd, NULL, -1) != -1 || errno != EINVAL) + return 6; + if (invoke(fd, NULL, 1025) != -1 || errno != EINVAL) + return 7; + if (invoke(fd, NULL, 1) != -1 || errno != EFAULT) + return 8; + int pipefd[2]; + char text[5] = {0}; + struct iovec parts[2] = {{text, 1}, {text + 1, 3}}; + if (pipe(pipefd) || write(pipefd[1], "test", 4) != 4 || + readv(pipefd[0], parts, 2) != 4 || strcmp(text, "test")) + return 9; + puts("ok"); + return 0; +} +""" +OVERLAY_READ = r""" +#include +#include + +ssize_t read(int fd, void *buffer, size_t size) +{ + struct iovec vector = {buffer, size}; + return readv(fd, &vector, 1); +} +""" def can_build(bits): @@ -82,15 +157,16 @@ class FakeinputRingTest(unittest.TestCase): self.tmp = Path(tempfile.mkdtemp()) self.addCleanup(shutil.rmtree, self.tmp, True) - def ring(self): + def ring(self, virtual=True): hooks = self.tmp / "hooks" hooks.mkdir() - (hooks / "event16").touch() - ring = bytearray(HEADER + CAPACITY * EVENT + DESCRIPTION) + (hooks / ("event16" if virtual else "event0")).touch() + ring = bytearray(HEADER + CAPACITY * EVENT + (DESCRIPTION if virtual else 0)) struct.pack_into(" Date: Tue, 20 Oct 2026 16:43:35 +0300 Subject: [PATCH 3/8] build: require the controller readv export Check that packaged libfakeinput libraries export readv alongside the existing read hook. --- tools/linuxfs/check-preloads.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/linuxfs/check-preloads.sh b/tools/linuxfs/check-preloads.sh index 78021c440..30f94a96a 100644 --- a/tools/linuxfs/check-preloads.sh +++ b/tools/linuxfs/check-preloads.sh @@ -9,7 +9,7 @@ for bad in libstdc++.so.6 libgcc_s.so.1; do done syms() { "${READELF:-readelf}" -Ws "$1" | awk '$4 == "FUNC" && $5 == "GLOBAL" {sub(/@.*/, "", $8); print $8}'; } FAKE=$(syms "$D/libfakeinput.so") -for sym in open openat ioctl read close poll ppoll select stat fstat access scandir; do +for sym in open openat ioctl read readv close poll ppoll select stat fstat access scandir; do grep -qx "$sym" <<<"$FAKE" || { echo "::error::libfakeinput.so does not export $sym"; exit 1; } done if grep -qxE '__gxx_personality_v0|__cxa_throw|_Unwind_Resume' <<<"$FAKE"; then From c4298e73b48473d320dcb8c413eb2203c7d44eb1 Mon Sep 17 00:00:00 2001 From: Delete228 <31237303+Delete228@users.noreply.github.com> Date: Tue, 20 Oct 2026 17:29:42 +0300 Subject: [PATCH 4/8] fix: safely copy readv descriptors before validation Copy the caller's iovec array through process_vm_readv before inspecting it, so unmapped, protected, or partially accessible arrays return EFAULT instead of crashing the process. Verified valid and invalid copies in the Android guest. --- app/src/main/cpp/fakeinput_steam.cpp | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/app/src/main/cpp/fakeinput_steam.cpp b/app/src/main/cpp/fakeinput_steam.cpp index 2efba76ce..3b16d9ac8 100644 --- a/app/src/main/cpp/fakeinput_steam.cpp +++ b/app/src/main/cpp/fakeinput_steam.cpp @@ -3687,6 +3687,18 @@ EXPORT ssize_t readv(int fd, const struct iovec *iov, int iovcnt) { errno = EFAULT; return -1; } + std::vector copied_iov(static_cast(iovcnt)); + if (iovcnt) { + size_t bytes = copied_iov.size() * sizeof(struct iovec); + struct iovec local = {copied_iov.data(), bytes}; + struct iovec remote = {const_cast(iov), bytes}; + ssize_t copied = syscall(SYS_process_vm_readv, getpid(), &local, 1, &remote, 1, 0); + if (copied != static_cast(bytes)) { + errno = EFAULT; + return -1; + } + } + iov = copied_iov.data(); size_t size = 0; for (int i = 0; i < iovcnt; ++i) { if (iov[i].iov_len > static_cast(SSIZE_MAX) - size) { From a17a40db02b5ebb4513de57e240ccc202f270ad8 Mon Sep 17 00:00:00 2001 From: Delete228 <31237303+Delete228@users.noreply.github.com> Date: Tue, 20 Oct 2026 17:34:14 +0300 Subject: [PATCH 5/8] test: reject inaccessible readv descriptor arrays Cover unmapped, PROT_NONE, and partially accessible iovec arrays. The probe crashes with SIGSEGV before the descriptor-copy fix and returns EFAULT afterward. The Linux fakeinput suite and native ARM64 probe pass; Factorio was manually retested on the Thor with the updated library. --- tools/tests/test_fakeinput_ring.py | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/tools/tests/test_fakeinput_ring.py b/tools/tests/test_fakeinput_ring.py index 42d6f85b6..af749d44a 100644 --- a/tools/tests/test_fakeinput_ring.py +++ b/tools/tests/test_fakeinput_ring.py @@ -91,6 +91,7 @@ READV_CHECKS = r""" #include #include #include +#include #include #include @@ -120,6 +121,22 @@ int main(void) return 7; if (invoke(fd, NULL, 1) != -1 || errno != EFAULT) return 8; + long page = sysconf(_SC_PAGESIZE); + void *area = mmap(NULL, 2 * page, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); + if (area == MAP_FAILED) + return 10; + struct iovec *boundary = (struct iovec *)((char *)area + page - sizeof(struct iovec)); + *boundary = vector; + if (mprotect((char *)area + page, page, PROT_NONE)) + return 11; + if (invoke(fd, boundary, 2) != -1 || errno != EFAULT) + return 12; + if (invoke(fd, boundary + 1, 1) != -1 || errno != EFAULT) + return 13; + if (munmap(area, 2 * page)) + return 14; + if (invoke(fd, boundary, 1) != -1 || errno != EFAULT) + return 15; int pipefd[2]; char text[5] = {0}; struct iovec parts[2] = {{text, 1}, {text + 1, 3}}; From c0359d1c8cfc25eb516dd51953bf2a9efe761144 Mon Sep 17 00:00:00 2001 From: Delete228 <31237303+Delete228@users.noreply.github.com> Date: Tue, 20 Oct 2026 17:53:40 +0300 Subject: [PATCH 6/8] fix: safely copy controller events to caller buffers Stage ring and keyframe events before copying them with process_vm_writev. Return EFAULT for inaccessible destinations and advance the reader only for complete events delivered, preserving partial readv progress. Retested Factorio on the Thor with the exact ARM64 library. --- app/src/main/cpp/fakeinput_steam.cpp | 39 ++++++++++++++++++---------- 1 file changed, 26 insertions(+), 13 deletions(-) diff --git a/app/src/main/cpp/fakeinput_steam.cpp b/app/src/main/cpp/fakeinput_steam.cpp index 3b16d9ac8..09428e442 100644 --- a/app/src/main/cpp/fakeinput_steam.cpp +++ b/app/src/main/cpp/fakeinput_steam.cpp @@ -3577,6 +3577,21 @@ EXPORT int close(int fd) { static constexpr int kMaxRingWaitMs = 2; static constexpr int kLowLatencyRingWaitUs = 250; +static ssize_t copy_input_events(void *buf, const struct input_event *events, size_t count) { + size_t bytes = count * sizeof(struct input_event); + struct iovec local = {const_cast(events), bytes}; + struct iovec remote = {buf, bytes}; + ssize_t copied = syscall(SYS_process_vm_writev, getpid(), &local, 1, &remote, 1, 0); + if (copied < 0) return -1; + if (copied != static_cast(bytes)) { + errno = EFAULT; + // evdev reads only report complete input_event records. + copied -= copied % sizeof(struct input_event); + if (!copied) return -1; + } + return copied; +} + static ssize_t read_input(int fd, void *buf, size_t count) { if (!watched(fd)) return syscall(SYS_read, fd, buf, count); std::unique_lock guard(controller_mutex()); @@ -3622,42 +3637,44 @@ static ssize_t read_input(int fd, void *buf, size_t count) { if (events == 0) break; const uint8_t *ring_events = reinterpret_cast(fake.ring) + FAKE_INPUT_RING_HEADER_SIZE; + std::vector input_events(events); for (size_t i = 0; i < events; ++i) { size_t index = (fake.read_seq + i) % FAKE_INPUT_RING_CAPACITY; RingEvent stored; memcpy(&stored, ring_events + index * FAKE_INPUT_EVENT_SIZE, FAKE_INPUT_EVENT_SIZE); - struct input_event ev = {}; + struct input_event &ev = input_events[i]; ev.time.tv_sec = stored.sec; ev.time.tv_usec = stored.usec; ev.type = stored.type; ev.code = stored.code; ev.value = stored.value; if (ev.type == EV_ABS) ev.code = presented_abs_code(fake, ev.code); - memcpy(static_cast(buf) + i * sizeof(ev), &ev, sizeof(ev)); } __atomic_thread_fence(__ATOMIC_ACQUIRE); if (snap.sequence != __atomic_load_n(&fake.ring->snapshot_seq, __ATOMIC_RELAXED)) continue; - fake.read_seq += events; - return static_cast(events * sizeof(struct input_event)); + ssize_t copied = copy_input_events(buf, input_events.data(), events); + if (copied > 0) fake.read_seq += copied / sizeof(struct input_event); + return copied; } } size_t events = std::min(requested, fake.keyframe_remaining); + struct input_event input_events[kNeutralEventCount] = {}; struct timeval now = {}; gettimeofday(&now, nullptr); for (size_t i = 0; i < events; ++i) { - size_t index = kNeutralEventCount - fake.keyframe_remaining; - struct input_event ev = {}; + size_t index = kNeutralEventCount - fake.keyframe_remaining + i; + struct input_event &ev = input_events[i]; ev.time = now; ev.type = kNeutralEvents[index].type; ev.code = kNeutralEvents[index].code; ev.value = keyframe_value(fake, ev.type, ev.code); if (ev.type == EV_ABS) ev.code = presented_abs_code(fake, ev.code); - memcpy(static_cast(buf) + i * sizeof(ev), &ev, sizeof(ev)); - --fake.keyframe_remaining; } - return static_cast(events * sizeof(struct input_event)); + ssize_t copied = copy_input_events(buf, input_events, events); + if (copied > 0) fake.keyframe_remaining -= copied / sizeof(struct input_event); + return copied; } if (nonblock) { errno = EAGAIN; @@ -3706,10 +3723,6 @@ EXPORT ssize_t readv(int fd, const struct iovec *iov, int iovcnt) { return -1; } size += iov[i].iov_len; - if (iov[i].iov_len && !iov[i].iov_base) { - errno = EFAULT; - return -1; - } } // evdev's readv calls its read handler per vector; Steam's ARM64 overlay uses this path too. From 197d31c2ed2ce3ea6acbbc2f4b8aeca521c72131 Mon Sep 17 00:00:00 2001 From: Delete228 <31237303+Delete228@users.noreply.github.com> Date: Tue, 20 Oct 2026 17:55:25 +0300 Subject: [PATCH 7/8] test: cover controller destination faults and partial reads Exercise keyframe and ring reads with unmapped, PROT_NONE, read-only and page-boundary destinations. Verify complete-event progress within one vector and across vectors, including a NULL destination. The Linux fakeinput suite passes 11 tests with 3 i386 skips; both native ARM64 probes pass on the Thor. --- tools/tests/test_fakeinput_ring.py | 82 +++++++++++++++++++++++++++++- 1 file changed, 80 insertions(+), 2 deletions(-) diff --git a/tools/tests/test_fakeinput_ring.py b/tools/tests/test_fakeinput_ring.py index af749d44a..86afc9939 100644 --- a/tools/tests/test_fakeinput_ring.py +++ b/tools/tests/test_fakeinput_ring.py @@ -147,6 +147,74 @@ int main(void) return 0; } """ +READV_DESTINATION_CHECKS = r""" +#include +#include +#include +#include +#include +#include +#include + +int main(int argc, char **argv) +{ + struct input_event events[64]; + int fd = open("/dev/input/event16", O_RDONLY | O_NONBLOCK); + if (fd < 0) + return 1; + if (argc > 1) + while (read(fd, events, sizeof(events)) > 0) + ; + puts("ready"); + fflush(stdout); + char go; + if (read(0, &go, 1) != 1) + return 2; + long page = sysconf(_SC_PAGESIZE); + char *area = mmap(NULL, 2 * page, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); + if (area == MAP_FAILED || mprotect(area + page, page, PROT_NONE)) + return 3; + struct iovec vector = {area + page, sizeof(struct input_event)}; + if (readv(fd, &vector, 1) != -1 || errno != EFAULT) + return 4; + if (read(fd, vector.iov_base, vector.iov_len) != -1 || errno != EFAULT) + return 5; + if (mprotect(area, page, PROT_READ)) + return 6; + vector.iov_base = area; + if (readv(fd, &vector, 1) != -1 || errno != EFAULT) + return 7; + if (mprotect(area, page, PROT_READ | PROT_WRITE) || munmap(area + page, page)) + return 8; + vector.iov_base = area + page; + if (readv(fd, &vector, 1) != -1 || errno != EFAULT) + return 9; + vector.iov_base = area + page - 1; + if (readv(fd, &vector, 1) != -1 || errno != EFAULT) + return 15; + vector.iov_base = area + page - sizeof(struct input_event); + vector.iov_len = 2 * sizeof(struct input_event); + if (readv(fd, &vector, 1) != sizeof(struct input_event)) + return 10; + struct input_event *first = vector.iov_base; + if (first->type != EV_KEY || first->code != BTN_A) + return 11; + struct iovec parts[2] = {{events, sizeof(struct input_event)}, {NULL, sizeof(struct input_event)}}; + if (readv(fd, parts, 2) != sizeof(struct input_event)) + return 12; + if (events[0].type != (argc > 1 ? EV_ABS : EV_KEY) || events[0].code != (argc > 1 ? ABS_Y : BTN_B)) + return 13; + vector.iov_base = events; + vector.iov_len = sizeof(events); + ssize_t got = readv(fd, &vector, 1); + if (got != (argc > 1 ? 1 : 18) * sizeof(struct input_event) || + events[0].type != (argc > 1 ? EV_SYN : EV_KEY) || events[0].code != (argc > 1 ? SYN_REPORT : BTN_X)) + return 14; + munmap(area, page); + puts("ok"); + return 0; +} +""" OVERLAY_READ = r""" #include #include @@ -199,8 +267,9 @@ class FakeinputRingTest(unittest.TestCase): subprocess.run(["gcc", bits, "-o", str(binary), str(self.tmp / (name + ".c"))], check=True) return library, binary - def read_through(self, bits, vectors=None, overlay=False, virtual=True): - program = READER if virtual else READER.replace("event16", "event0") + def read_through(self, bits, vectors=None, overlay=False, virtual=True, program=READER): + if not virtual: + program = program.replace("event16", "event0") library, reader = self.build(bits, program, "reader") hooks, path = self.ring(virtual=virtual) env = dict(os.environ, LD_PRELOAD=str(library), FAKE_EVDEV_DIR=str(hooks)) @@ -262,6 +331,15 @@ class FakeinputRingTest(unittest.TestCase): self.assertEqual(run.returncode, 0, run.stderr) self.assertEqual(run.stdout, "ok\n") + def test_readv_keyframes_handle_destination_faults_and_partial_progress(self): + self.assertEqual(self.read_through("-m64", program=READV_DESTINATION_CHECKS), "ok\n") + + def test_readv_ring_events_handle_destination_faults_and_partial_progress(self): + self.assertEqual(self.read_through("-m64", vectors=1, program=READV_DESTINATION_CHECKS), "ok\n") + + def test_a_32_bit_guest_handles_readv_destination_faults(self): + self.assertEqual(self.read_through("-m32", program=READV_DESTINATION_CHECKS), "ok\n") + def test_an_event_node_rejects_joydev_requests_so_sdl_reads_its_id(self): library, probe = self.build("-m64", PROBE, "probe") hooks, _ = self.ring() From cbc113ea3eb6084dfe11292e2f26f1f52d863f5b Mon Sep 17 00:00:00 2001 From: maxjivi05 Date: Tue, 20 Oct 2026 17:53:19 -0400 Subject: [PATCH 8/8] fix: Mono P/Invoke readv calls reach the controller hook too --- app/src/main/cpp/fakeinput_steam.cpp | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/app/src/main/cpp/fakeinput_steam.cpp b/app/src/main/cpp/fakeinput_steam.cpp index 5c4fc4e3a..ba4b14ec5 100644 --- a/app/src/main/cpp/fakeinput_steam.cpp +++ b/app/src/main/cpp/fakeinput_steam.cpp @@ -3588,7 +3588,6 @@ static ssize_t copy_input_events(void *buf, const struct input_event *events, si if (copied < 0) return -1; if (copied != static_cast(bytes)) { errno = EFAULT; - // evdev reads only report complete input_event records. copied -= copied % sizeof(struct input_event); if (!copied) return -1; } @@ -3728,7 +3727,6 @@ EXPORT ssize_t readv(int fd, const struct iovec *iov, int iovcnt) { size += iov[i].iov_len; } - // evdev's readv calls its read handler per vector; Steam's ARM64 overlay uses this path too. ssize_t total = 0; for (int i = 0; i < iovcnt; ++i) { if (!iov[i].iov_len) continue; @@ -4175,10 +4173,10 @@ EXPORT int __fxstat64(int version, int fd, struct stat64 *buf) { using dlsym_fn = void *(*)(void *, const char *); static const char *const interposed_names[] = { - "open", "open64", "openat", "openat64", "close", "read", "write", "writev", "ioctl", "poll", - "ppoll", "select", "stat", "stat64", "fstat", "fstat64", "__xstat", "__xstat64", "__fxstat", - "__fxstat64", "statx", "statfs", "statfs64", "fstatfs", "fstatfs64", "access", "faccessat", - "opendir", "scandir", "inotify_add_watch", + "open", "open64", "openat", "openat64", "close", "read", "readv", "write", "writev", "ioctl", + "poll", "ppoll", "select", "stat", "stat64", "fstat", "fstat64", "__xstat", "__xstat64", + "__fxstat", "__fxstat64", "statx", "statfs", "statfs64", "fstatfs", "fstatfs64", "access", + "faccessat", "opendir", "scandir", "inotify_add_watch", }; static dlsym_fn next_dlsym() {