Wild Ride from Raw Syscalls to Figuring Out NSS and libc

Published: (February 4, 2026 at 02:27 AM EST)
4 min read
Source: Dev.to

Source: Dev.to

I’ve always had a nerdy obsession with Linux. From day one I dove head‑first into the kernel, writing tiny tools using raw syscalls—skipping libc entirely and talking to the kernel directly. I made my own headers, wrote my own printf‑style routines, opened directories with getdents64, parsed /proc for processes… basically living dangerously in the kernel’s backyard.

At the time I thought I was the shit doing everything “the real way.” It worked, but I was skipping a whole layer of sanity that exists in libc. I had no idea about all the stuff it actually does: buffering, human‑readable formatting, and other niceties that make life less painful.

The WTF Moment: /etc/nsswitch.conf

Everything hit me when I stumbled upon /etc/nsswitch.conf while poking around network and user stuff. I opened it and literally went:

“What the actual hell is this? Who the fuck uses this? Isn’t the kernel supposed to know this?”

I was in full raw‑syscall mode, so the idea that some config file could tell libc how to handle getpwnam() or getaddrinfo() blew my mind. I had no clue why this “sign‑board” was even necessary.

Okay, Now NSS Makes Sense

Turns out, NSS = Name Service Switch. It’s basically a sign‑board inside libc telling it where the hell to look for information.

When a program calls something like getpwuid() or getpwnam(), it’s asking:

“Who is this user? What’s their username?”

The kernel doesn’t know usernames; it only knows numeric IDs (e.g., UID = 1000). That’s where libc + NSS swoop in like superheroes.

How It Actually Works

Say you call getpwnam("Deadpool"). libc does roughly the following:

  1. Read /etc/nsswitch.conf
    Finds the line for users:

    passwd: files systemd

    This means:

    • First, check local files (/etc/passwd)
    • Then, ask systemd’s dynamic user database
  2. Call each NSS module in order

    • libnss_files.so.2 → looks in /etc/passwd

      • Found? → return result
      • Not found? → continue
    • libnss_systemd.so.2 → asks systemd

      • Found? → return result
      • Not found? → continue (if there are more sources)
  3. Return a struct passwd to your program, containing username, UID, GID, home directory, shell, etc.

Important: NSS itself is not a running program; it’s just a config file plus a set of shared libraries. libc loads them dynamically, so no extra processes are involved.

The Secret Sauce: libc Beyond Raw Syscalls

libc is more than a thin wrapper around syscalls. Sure, it wraps things like write, read, open, getpid, but it also adds a ton of extra functionality:

CategoryExamples
Buffering & formattingprintf, fprintf, fopen, getline
Memory managementmalloc, free, realloc
Threading supportpthread_* functions
Policy & lookup logicNSS modules, locale handling, hostname resolution

Without NSS, this extra sauce works partially:

  • Syscalls still happen correctly (open, read, write, …)
  • Buffering, formatting, memory, threading still work
  • Any function that needs human‑readable names (users, groups, hosts, services) would fail or return NULL, because libc has no map telling it where to look.

NSS Modules Still Use Syscalls Under the Hood

Even NSS modules ultimately rely on syscalls:

  • libnss_files.so.2 reads /etc/passwd using open, read, close.
  • libnss_systemd.so.2 talks to the systemd service via IPC syscalls.

So everything still hits the kernel; NSS just adds a layer of logic so libc knows where and how to get the data.

Bottom line: libc + NSS = fancy middleware. Raw syscalls = truth. NSS gives meaning to that truth and ensures humans don’t see useless numbers.

How This Hit Me

Before the epiphany, my tools did things like:

  • Open directories with getdents64
  • Read /proc/<pid>/status → get numeric UIDs
  • Display numbers only

That’s fine if you just want raw truth, but it’s boring for humans. Humans want:

PID: 742   User: Deadpool   Process: bash

instead of:

PID: 742   UID: 1000      Process: bash

NSS basically translates truth into meaning, and libc does all the heavy lifting.

Layers of the System (Mental Picture)

Linux name‑service‑switch diagram

Key nuggets I learned

  • libc does way more than just wrap syscalls: buffering, formatting, threading, memory, and NSS.
  • NSS is optional if you only need numbers, but humans will hate your output.
  • Systemd has a dynamic user database for services and containers, which NSS can query.
  • Raw syscalls = truth; NSS = meaning.

My Reflection

Real talk: this was a big “oh‑shit” moment. My low‑level tools were cool, but they weren’t really “user‑friendly” or system‑aware. I’d been skipping an entire ecosystem of userspace logic.

Now I see the value in combining both:

  • Raw syscalls → understand the kernel and ABIs like a boss.
  • libc + NSS → make tools that are readable, robust, and actually useful.

One‑Liner to Summarize

“Syscalls give you the raw truth; NSS gives libc the damn map.”

to turn that truth into something humans can actually understand.

Back to Blog

Related posts

Read more »

Basics & History of Linux

UNIX Origins - 1964 – Bell Laboratories New Jersey began the UNIX project. - 1969 – The original project was withdrawn, but Dennis Ritchie and Ken Thompson con...