crates/mimir-ingest/src/boundary/libc_map.rs
//! The curated C-library map: which system calls a C function makes, for
//! glibc and for musl (Linux; the names are x86_64's — on aarch64 a legacy name becomes its generic stand-in, `super::calls_on`). A Rust `extern "C"` item
//! (`ffi` node) is linked as a C symbol (its `link_name`); this table says
//! what that symbol does at the system boundary.
//!
//! How a symbol is answered ([`lookup`]):
//!
//! 1. an entry of [`TABLE`] — the libc's own implementation, read from its
//! sources (glibc 2.39 `sysdeps/unix/sysv/linux`, musl 1.2.5 `src/`);
//! `sure = false` marks a mapping that depends on the libc version, the
//! arguments, a vDSO (no system call at all on the fast path), buffering
//! or contention (a mutex only enters the kernel when contended) — such
//! an edge is written with `partial = true` (UNCERTAIN);
//! 2. a symbol in [`PURE`] makes no system call (string, memory, ctype,
//! attribute objects …);
//! 3. a symbol that IS a system call's name and has no entry is that
//! system call's thin wrapper in both libcs (`read`, `connect`, `bind` …);
//! 4. a glibc large-file name (`open64`, `lseek64`, `fstat64` …) is answered
//! as the name without `64`;
//! 5. anything else is UNMAPPED — not "pure": the lens names it as a gap.
use super::INDIRECT;
/// Which C library a target links.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub enum Libc {
Glibc,
Musl,
}
impl Libc {
pub fn as_str(self) -> &'static str {
match self {
Libc::Glibc => "gnu",
Libc::Musl => "musl",
}
}
/// The libc of a target triple (`x86_64-unknown-linux-gnu` → glibc).
pub fn of_target(triple: &str) -> Option<Libc> {
if !triple.contains("-linux-") {
return None;
}
if triple.ends_with("-musl") || triple.contains("-musl") {
Some(Libc::Musl)
} else if triple.contains("-gnu") {
Some(Libc::Glibc)
} else {
None
}
}
}
/// What one C symbol does at the system boundary.
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum Mapped {
/// The system calls it can make; `sure = false` = uncertain.
Calls(Vec<(&'static str, bool)>),
/// It makes no system call.
Pure,
}
/// `(symbol, glibc, musl, sure)`: the system calls as space-separated names;
/// musl `"="` = the same as glibc.
pub const TABLE: &[(&str, &str, &str, bool)] = &[
// ---- files
("open", "openat", "open", true),
("creat", "openat", "open", false),
("stat", "newfstatat", "stat", false),
("lstat", "newfstatat", "lstat", false),
("fstat", "fstat newfstatat", "fstat", false),
("fstatat", "newfstatat", "newfstatat stat lstat fstat", false),
("__xstat64", "stat newfstatat", "=", false),
("__lxstat64", "lstat newfstatat", "=", false),
("__fxstat64", "fstat newfstatat", "=", false),
("__fxstatat64", "newfstatat", "=", false),
("pread", "pread64", "=", true),
("pwrite", "pwrite64", "=", true),
("faccessat", "faccessat2 faccessat", "faccessat faccessat2", false),
("fcntl", "fcntl", "=", true),
("opendir", "openat fstat newfstatat", "open", false),
("fdopendir", "fcntl fstat newfstatat", "fcntl fstat", false),
("readdir", "getdents64", "=", false),
("readdir_r", "getdents64", "=", false),
("closedir", "close", "=", true),
("rewinddir", "lseek", "=", true),
("realpath", "readlink getcwd newfstatat", "readlink getcwd", false),
("posix_fadvise", "fadvise64", "=", true),
("posix_fallocate", "fallocate", "=", false),
("futimens", "utimensat", "=", true),
("futimes", "utimensat", "=", false),
("lutimes", "utimensat", "=", false),
("utimes", "utimensat utimes", "utimensat utimes", false),
("mkfifo", "mknodat mknod", "mknod", false),
("mkfifoat", "mknodat", "=", true),
("statvfs", "statfs", "=", true),
("fstatvfs", "fstatfs", "=", true),
("isatty", "ioctl", "=", true),
("tcgetattr", "ioctl", "=", true),
("tcsetattr", "ioctl", "=", true),
("ttyname_r", "readlink ioctl newfstatat", "readlink ioctl fstat", false),
("pathconf", "statfs", "=", false),
("fpathconf", "fstatfs", "=", false),
("eventfd", "eventfd2", "=", true),
("eventfd_read", "read", "=", true),
("eventfd_write", "write", "=", true),
("signalfd", "signalfd4", "=", true),
("fopen", "openat", "open", false),
("fdopen", "fcntl", "=", false),
("fclose", "close", "=", false),
("fread", "read", "=", false),
("fgets", "read", "=", false),
("getline", "read", "=", false),
("fwrite", "write", "=", false),
("fputs", "write", "=", false),
("puts", "write", "=", false),
("printf", "write", "=", false),
("fprintf", "write", "=", false),
("dprintf", "write", "=", false),
("fflush", "write", "=", false),
("perror", "write", "=", false),
("openpty", "openat ioctl", "open ioctl", false),
("shm_open", "openat", "open", true),
("shm_unlink", "unlink", "=", false),
// ---- network
("send", "sendto", "=", true),
("recv", "recvfrom", "=", true),
("getaddrinfo", "socket connect sendto recvfrom sendmmsg poll close openat read newfstatat bind getsockname recvmsg", "socket connect sendto recvfrom poll close open read bind", false),
("getnameinfo", "socket connect sendto recvfrom poll close openat read", "socket connect sendto recvfrom poll close open read", false),
("gethostbyname", "socket connect sendto recvfrom poll close openat read", "socket connect sendto recvfrom poll close open read", false),
("gethostbyname_r", "socket connect sendto recvfrom poll close openat read", "socket connect sendto recvfrom poll close open read", false),
("gethostbyaddr_r", "socket connect sendto recvfrom poll close openat read", "socket connect sendto recvfrom poll close open read", false),
("res_init", "openat read close", "open read close", false),
("__res_init", "openat read close", "open read close", false),
("getifaddrs", "socket bind sendto recvmsg getsockname close", "socket sendto recvfrom close", false),
("if_nametoindex", "socket ioctl close", "=", false),
("if_indextoname", "socket ioctl close", "=", false),
// ---- memory
("mmap", "mmap", "=", true),
("posix_madvise", "madvise", "=", true),
("sbrk", "brk", "=", true),
("malloc", "brk mmap munmap mremap madvise", "brk mmap munmap mremap madvise", false),
("calloc", "brk mmap munmap mremap madvise", "=", false),
("realloc", "brk mmap munmap mremap madvise", "=", false),
("reallocarray", "brk mmap munmap mremap madvise", "=", false),
("free", "munmap madvise brk", "munmap madvise brk", false),
("posix_memalign", "brk mmap munmap madvise", "=", false),
("aligned_alloc", "brk mmap munmap madvise", "=", false),
("memalign", "brk mmap munmap madvise", "=", false),
("valloc", "brk mmap munmap madvise", "=", false),
("strdup", "brk mmap", "=", false),
("strndup", "brk mmap", "=", false),
("setenv", "brk mmap", "=", false),
("unsetenv", "brk mmap", "=", false),
("putenv", "brk mmap", "=", false),
("sysconf", "sched_getaffinity sysinfo openat read close prlimit64", "sched_getaffinity sysinfo prlimit64", false),
("get_nprocs", "openat read close sched_getaffinity", "sched_getaffinity", false),
("get_nprocs_conf", "openat read close getdents64", "sched_getaffinity", false),
("dlopen", "openat read mmap mprotect munmap close newfstatat fstat", "open read mmap mprotect munmap close fstat", false),
("dlclose", "munmap", "=", false),
("setlocale", "openat read mmap close", "=", false),
("newlocale", "openat read mmap close", "=", false),
("localtime_r", "openat read close", "open read close", false),
("tzset", "openat read close", "open read close", false),
// ---- time (vDSO first: no system call on the fast path)
("clock_gettime", "clock_gettime", "=", false),
("gettimeofday", "gettimeofday", "=", false),
("time", "time", "=", false),
("clock_getres", "clock_getres", "=", false),
("getcpu", "getcpu", "=", false),
("sched_getcpu", "getcpu", "=", false),
("nanosleep", "clock_nanosleep", "nanosleep clock_nanosleep", false),
("sleep", "clock_nanosleep", "nanosleep clock_nanosleep", false),
("usleep", "clock_nanosleep", "nanosleep clock_nanosleep", false),
// ---- processes
("fork", "clone", "fork", true),
("_Fork", "clone", "fork", true),
("posix_spawn", "clone3 clone execve rt_sigprocmask rt_sigaction exit_group", "clone execve pipe2 read close rt_sigprocmask exit", false),
("posix_spawnp", "clone3 clone execve rt_sigprocmask rt_sigaction exit_group", "clone execve pipe2 read close rt_sigprocmask exit", false),
("system", "clone3 clone execve wait4 rt_sigaction rt_sigprocmask", "clone execve wait4 rt_sigaction rt_sigprocmask", false),
("popen", "pipe2 clone3 clone execve", "pipe2 clone execve", false),
("pclose", "wait4 close", "=", false),
("daemon", "clone setsid openat dup2", "fork setsid open dup2", false),
("execv", "execve", "=", true),
("execvp", "execve", "=", true),
("execvpe", "execve", "=", true),
("execl", "execve", "=", true),
("execlp", "execve", "=", true),
("execle", "execve", "=", true),
("fexecve", "execveat execve", "execveat execve", false),
("exit", "exit_group", "=", false),
("_exit", "exit_group", "=", true),
("_Exit", "exit_group", "=", true),
("abort", "rt_sigprocmask tgkill rt_sigaction exit_group getpid gettid", "rt_sigprocmask tkill rt_sigaction exit_group gettid", false),
("__stack_chk_fail", "rt_sigprocmask tgkill exit_group", "=", false),
("raise", "tgkill rt_sigprocmask getpid gettid", "tkill rt_sigprocmask gettid", false),
("wait", "wait4", "=", true),
("waitpid", "wait4", "=", true),
("getrlimit", "prlimit64", "prlimit64 getrlimit", false),
("setrlimit", "prlimit64", "prlimit64 setrlimit", false),
("prlimit", "prlimit64", "=", true),
("nice", "getpriority setpriority", "=", true),
("gethostname", "uname", "=", true),
("getpwuid_r", "openat read close newfstatat fstat socket connect", "open read close socket connect", false),
("getpwnam_r", "openat read close newfstatat fstat socket connect", "open read close socket connect", false),
("getgrgid_r", "openat read close newfstatat fstat socket connect", "open read close socket connect", false),
("getgrnam_r", "openat read close newfstatat fstat socket connect", "open read close socket connect", false),
("getpwuid", "openat read close newfstatat fstat socket connect", "open read close socket connect", false),
("getpwnam", "openat read close newfstatat fstat socket connect", "open read close socket connect", false),
("getgrouplist", "openat read close socket connect", "open read close socket connect", false),
("initgroups", "openat read close setgroups socket connect", "open read close setgroups", false),
("getlogin_r", "openat read close", "open read close", false),
("getcontext", "rt_sigprocmask", "=", true),
("setcontext", "rt_sigprocmask", "=", true),
("swapcontext", "rt_sigprocmask", "=", true),
("umount", "umount2", "=", true),
("klogctl", "syslog", "=", true),
// libc's syslog() is NOT the syslog system call: it writes /dev/log
("syslog", "socket connect sendto close", "socket connect send close", false),
("openlog", "socket connect", "=", false),
("closelog", "close", "=", false),
// ---- signals
("sigaction", "rt_sigaction", "=", true),
("signal", "rt_sigaction", "=", true),
("sigprocmask", "rt_sigprocmask", "=", true),
("pthread_sigmask", "rt_sigprocmask", "=", true),
("sigsuspend", "rt_sigsuspend", "=", true),
("sigwait", "rt_sigtimedwait", "=", true),
("sigwaitinfo", "rt_sigtimedwait", "=", true),
("sigtimedwait", "rt_sigtimedwait", "=", true),
("sigpending", "rt_sigpending", "=", true),
("sigqueue", "rt_sigqueueinfo", "=", true),
("pthread_kill", "tgkill", "tkill", true),
// ---- threads, locks (a lock enters the kernel only when contended)
("pthread_create", "clone3 clone mmap mprotect munmap rt_sigprocmask madvise", "clone mmap mprotect munmap rt_sigprocmask", false),
("pthread_join", "futex munmap", "=", false),
("pthread_detach", "munmap", "=", false),
("pthread_exit", "exit futex munmap madvise", "exit futex munmap", false),
("pthread_once", "futex", "=", false),
("pthread_mutex_lock", "futex", "=", false),
("pthread_mutex_trylock", "futex", "=", false),
("pthread_mutex_unlock", "futex", "=", false),
("pthread_mutex_timedlock", "futex", "=", false),
("pthread_cond_wait", "futex", "=", false),
("pthread_cond_timedwait", "futex", "=", false),
("pthread_cond_signal", "futex", "=", false),
("pthread_cond_broadcast", "futex", "=", false),
("pthread_rwlock_rdlock", "futex", "=", false),
("pthread_rwlock_wrlock", "futex", "=", false),
("pthread_rwlock_tryrdlock", "futex", "=", false),
("pthread_rwlock_trywrlock", "futex", "=", false),
("pthread_rwlock_unlock", "futex", "=", false),
("sem_wait", "futex", "=", false),
("sem_timedwait", "futex", "=", false),
("sem_post", "futex", "=", false),
("pthread_setname_np", "prctl openat write close", "prctl open write close", false),
("pthread_getname_np", "prctl openat read close", "prctl open read close", false),
("pthread_getattr_np", "openat read close sched_getaffinity prlimit64 mmap", "mremap mmap", false),
("pthread_setaffinity_np", "sched_setaffinity", "=", true),
("pthread_getaffinity_np", "sched_getaffinity", "=", true),
("pthread_setschedparam", "sched_setscheduler", "=", false),
("mq_send", "mq_timedsend", "=", true),
("mq_receive", "mq_timedreceive", "=", true),
// ---- random (glibc ≥ 2.41 may answer getrandom from the vDSO)
("getrandom", "getrandom", "getrandom", false),
("getentropy", "getrandom", "=", true),
("arc4random", "getrandom", "=", false),
("arc4random_buf", "getrandom", "=", false),
("arc4random_uniform", "getrandom", "=", false),
// ---- the raw door: the number is an argument
("syscall", INDIRECT, "=", false),
];
/// Symbols that make no system call.
pub const PURE: &[&str] = &[
"memcpy", "memmove", "memset", "memcmp", "memchr", "memrchr", "memmem", "bcmp", "bzero", "explicit_bzero", "strlen",
"strnlen", "strcmp", "strncmp", "strcpy", "strncpy", "stpcpy", "strcat", "strncat", "strchr", "strrchr", "strstr",
"strcasecmp", "strncasecmp", "strspn", "strcspn", "strpbrk", "strtok", "strtok_r", "strtol", "strtoul", "strtoll",
"strtoull", "strtod", "strtof", "atoi", "atol", "atof", "qsort", "bsearch", "abs", "labs", "toupper", "tolower",
"isalnum", "isalpha", "isdigit", "isspace", "isupper", "islower", "isprint", "isxdigit", "wcslen", "wmemchr", "strerror",
"strerror_r", "__xpg_strerror_r", "gai_strerror", "freeaddrinfo", "freeifaddrs", "__errno_location", "__errno",
"getauxval", "getpagesize", "gnu_get_libc_version", "confstr", "getenv", "secure_getenv", "malloc_usable_size",
"pthread_self", "pthread_equal", "pthread_attr_init", "pthread_attr_destroy", "pthread_attr_setstacksize",
"pthread_attr_getstacksize", "pthread_attr_getstack", "pthread_attr_getguardsize", "pthread_attr_setdetachstate",
"pthread_key_create", "pthread_key_delete", "pthread_getspecific", "pthread_setspecific", "pthread_mutex_init",
"pthread_mutex_destroy", "pthread_mutexattr_init", "pthread_mutexattr_settype", "pthread_mutexattr_destroy",
"pthread_cond_init", "pthread_cond_destroy", "pthread_condattr_init", "pthread_condattr_setclock",
"pthread_condattr_destroy", "pthread_rwlock_init", "pthread_rwlock_destroy", "pthread_rwlockattr_init",
"pthread_rwlockattr_destroy", "sem_init", "sem_destroy", "sigemptyset", "sigfillset", "sigaddset", "sigdelset",
"sigismember", "posix_spawnattr_init", "posix_spawnattr_destroy", "posix_spawnattr_setflags",
"posix_spawnattr_setsigmask", "posix_spawnattr_setsigdefault", "posix_spawnattr_setpgroup",
"posix_spawn_file_actions_init", "posix_spawn_file_actions_destroy", "posix_spawn_file_actions_adddup2",
"posix_spawn_file_actions_addclose", "posix_spawn_file_actions_addopen", "posix_spawn_file_actions_addchdir_np",
"posix_spawn_file_actions_addfchdir_np", "dlsym", "dladdr", "dlerror", "dl_iterate_phdr", "dirfd",
"__cxa_thread_atexit_impl", "__cxa_finalize", "cfmakeraw", "cfsetspeed", "cfgetispeed", "cfgetospeed", "inet_pton",
"inet_ntop", "localeconv", "nl_langinfo", "gmtime_r", "timegm", "__tls_get_addr", "_Unwind_Resume",
"_Unwind_RaiseException", "_Unwind_Backtrace", "_Unwind_GetIP", "_Unwind_GetIPInfo", "_Unwind_GetLanguageSpecificData",
"_Unwind_GetRegionStart", "_Unwind_GetTextRelBase", "_Unwind_GetDataRelBase", "_Unwind_SetGR", "_Unwind_SetIP",
"_Unwind_GetCFA", "_Unwind_DeleteException", "_Unwind_FindEnclosingFunction", "__gcc_personality_v0",
];
/// The system calls the C runtime makes BEFORE `main` (the start-up code of
/// a program linked with `libc` on `arch`: thread pointer, the tid address,
/// glibc's robust list / rseq / limits), read from musl 1.2.5
/// `src/env/__init_tls.c` + `src/thread/x86_64/__set_thread_area.s` and
/// glibc 2.39 `csu/libc-start.c` + `sysdeps/unix/sysv/linux/x86_64`. A
/// source graph never sees that code: the walk adds these as the start-up's
/// calls. `sure = false` = it depends on the build (static or dynamic, the
/// kernel answering rseq …).
pub fn startup(libc: Libc, arch: &str) -> &'static [(&'static str, bool)] {
match (libc, arch) {
(Libc::Musl, "x86_64") => &[("arch_prctl", true), ("set_tid_address", true)],
(Libc::Musl, _) => &[("set_tid_address", true)],
(Libc::Glibc, "x86_64") => &[
("arch_prctl", true),
("set_tid_address", true),
("set_robust_list", true),
("rseq", false),
("prlimit64", false),
("brk", false),
("mmap", false),
("mprotect", false),
("readlinkat", false),
("getrandom", false),
],
(Libc::Glibc, _) => &[("set_tid_address", true), ("set_robust_list", true), ("rseq", false), ("prlimit64", false), ("brk", false), ("mmap", false), ("mprotect", false), ("getrandom", false)],
}
}
fn names(s: &'static str) -> impl Iterator<Item = &'static str> {
s.split_ascii_whitespace()
}
/// What `symbol` does under `libc`, against the system call names of the
/// target's table (`is_syscall`). `None` = UNMAPPED.
pub fn lookup(symbol: &str, libc: Libc, is_syscall: &dyn Fn(&str) -> bool) -> Option<Mapped> {
if let Some(&(_, gnu, musl, sure)) = TABLE.iter().find(|e| e.0 == symbol) {
let list = match libc {
Libc::Glibc => gnu,
Libc::Musl if musl == "=" => gnu,
Libc::Musl => musl,
};
return Some(Mapped::Calls(names(list).map(|n| (n, sure)).collect()));
}
if PURE.contains(&symbol) {
return Some(Mapped::Pure);
}
if is_syscall(symbol)
&& let Some(n) = super::static_name(symbol)
{
return Some(Mapped::Calls(vec![(n, true)]));
}
// glibc's large-file names: the same function on a 64-bit target
if let Some(base) = symbol.strip_suffix("64").filter(|b| !b.is_empty()) {
return lookup(base, libc, is_syscall);
}
None
}