Yeah, I was just thinking about it as a way to reduce the scope of the "preload" step of symbolic interpretation, for the case where you want to work with semi-structured IR (GIMPLE) rather than machine code.
My assumption was that by the time you're down to machine code, you'll still be able to recover the key column of the table — the syscall numbers themselves — but the rest of the data you want to show in the table won't exist any more, having existed only as things like identifier names. So you'd want to back up at least one or two steps.
> This is not the case unless you meticulously configure it accordingly, which is not so simple and requires constant manual (sigh) updates to the build configuration each kernel release.
I was less assuming the possibility of one kernel that has all syscalls, and more assuming that you could build O(N) "probe kernels", one per uarch.
I think the concept of there being "optional syscalls" that only appear if you configure in added capabilities beyond the uarch, didn't even occur to me.
How does that even work, libc-wise? I had assumed that the userland-kernel-ABI expectation was such that the set of syscalls possible to call for a given uarch is static, but with some just be stubbed to always return an error if the given capability isn't in the kernel. But I guess, if the "return an error like a stub" logic is the same as the "this syscall isn't implemented logic", then there needn't be any concrete code in the kernel that calls out those syscall numbers as existing...
If so, maybe consider that a bug? Submit a patch to have an arch's stubbed optional syscalls return a different error than for syscalls that don't exist for that arch, thus forcing such syscalls to be somehow documented in the kernel even when stubbed?
> There isn't a way to e.g. pretend that "all kernel services were already initialized" as you say in point #2.
To be clear, I wasn't talking about compile-time code inclusion; I was talking about runtime, when using the strategy I outlined to compile a subset of the Linux kernel as a "library kernel" / exokernel. The kernel does a lot of stuff on boot — brings up hardware, starts daemons, etc — and you'd want to skip including any of that, if you wanted to throw the code into a fuzzer, because that'd all distract the fuzzer from your goal of fuzzing the syscall handler. So you'd want the executable you built to just call the syscall handler as if it was running in the context of a bootstrapped-and-running kernel — statically declaring all the same static globals, but just never calling the code to initialize any of it. So you'd likely get a program that always crashes with a null dereference — but that doesn't matter, since your goal is to discover through fuzzing the conjunction of value constraints that overdetermines the control-flow to reach one null dereference vs another.