C Standard Library (libc)

  • Implementation of the C Standard.

  • Each one implements the C Standard optimized for the target platform.

  • BSD libc

    • Various implementations distributed with BSD-derived operating systems.

  • GNU C Library (glibc)

    • Used in GNU Hurd, GNU/kFreeBSD, and most Linux distributions.

  • Microsoft C run-time library (MSVCRT / UCRT).

    • Part of Microsoft Visual C++. There are two versions of the library: the formerly-redistributable (until Visual Studio 2013) MSVCRT which is not compliant to the C99 standard, and the newer UCRT (Universal C Run Time) shipped as part of Windows 10 and 11 which is C99-compliant.

  • dietlibc

    • An alternative small implementation of the C standard library (MMU-less)

  • ÎĽClibc

    • AC standard library for embedded ÎĽClinux systems (MMU-less)

    • uclibc-ng

      • An embedded C library, fork of ÎĽClibc, still maintained, with memory management unit (MMU) support

  • Newlib

    • A C standard library for embedded systems (MMU-less)6 and used in the Cygwin GNU distribution for Windows

  • klibc

    • Primarily for booting Linux systems

  • musl

    • Another lightweight C standard library implementation for Linux systems

  • Bionic

    • Originally developed by Google for the Android embedded system operating system, derived from BSD libc

  • picolibc

    • Developed by Keith Packard, targeting small embedded systems with limited RAM, based on code from Newlib and AVR Libc.

What am I using?

  • If you just wrote that code and compiled it normally with a mainstream compiler, here’s what you can expect:

    • Linux (gcc or clang) :

      • uses glibc’s by default.

      gcc main.c -o main   # links with glibc automatically
      
    • macOS (clang)

      • Uses Apple’s libc (based on FreeBSD’s) implementation.

    • Windows (MSVC)

      • Uses Microsoft’s Universal CRT (UCRT) implementation.

  • It’s always the platform’s standard C library implementation, automatically linked when you compile with gcc , clang , or MSVC .

Finding out which implementation was used

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int main()
{
    int *nums = malloc(sizeof(int) * 5);

    memset(nums, 0, sizeof(int) * 5);
    return 0;
}
  • Just looking at this code above is not possible to know which implementation was used.

  • To find out, you can check:

    1. Which C library your program links to

      • ldd ./a.out   shows dynamic libraries. If you see libc.so.6  you’re using glibc (or its symlink). If you see musl  or libc.so  from another vendor that tells you the vendor.

    2. Which memset  symbol is present in that libc

      • objdump -T /lib/x86_64-linux-gnu/libc.so.6 | grep memset  or nm -D /lib/x86_64-linux-gnu/libc.so.6 | grep memset

      • This shows whether memset  is provided by that library.

    3. Does your binary actually call the libc function or was it optimized/ inlined?

      • Inspect the binary symbols: readelf -Ws ./a.out | grep memset

        • If you see an UND  entry for memset  it’s an external call.

      • Disassemble the binary: objdump -d ./a.out | less  and search for call.*memset .

        • If there is no call, compile-time optimization may have inlined a loop or emitted an intrinsic.

      • To see compiler codegen: compile to assembly:

        • gcc -O2 test.c -S -o test.s  (optimized) and gcc -O0 test.c -S -o test_O0.s  (unoptimized). Inspect for a call  to memset  or a loop/ rep stosb  sequence.