The C Runtime (CRT) refers to the set of libraries, functions, and runtime components that enable the execution of C programs. It bridges the gap between the operating system and the compiled code, providing essential functionality such as memory management, input/output operations, and program initialization. While typically associated with the C language, CRT is also foundational for other languages that rely on C libraries or compilers.

Components of the C Runtime

The CRT consists of several key components that are essential for the execution of a C program. These include:

Initialization and Cleanup

Before the main function is executed, the CRT performs program initialization. Execution begins not in main but in a special entry point function called _start. This function is provided by the CRT and is responsible for:

  • Parsing arguments passed by the operating system according to the ABI
  • Invoke global constructors and setup global variables
  • Handling the return value of main
  • Execute global destructors
  • Pass back the result of main to the operating system using a system call like exit.

For example:

void _start() {
    int argc;
    char **argv;
    char **envp;
    // Call global constructors from .init_array section of the ELF
    call_global_constructors()
    // Setup argc, argv, envp from the stack
    int result = main(argc, argv);
    // Global destructors
    call_global_destructors()
    exit(result);
}

Standard Library Functions

The CRT includes implementations of standard library functions defined in the C Standard Library, such as malloc, free, printf, scanf, and strcpy. These functions provide abstractions over low-level system calls, making it easier for developers to interact with the underlying operating system.

System Call Wrappers

The CRT acts as an intermediary between the user code and the operating system. It provides wrappers for system calls, translating portable function calls (e.g., open, read, write) into system-specific instructions. This abstraction allows C programs to run on multiple platforms with minimal modification.

Exception Handling

For platforms supporting structured exception handling (e.g., Windows), the CRT provides mechanisms to handle runtime errors, such as division by zero or invalid memory access. This functionality often integrates with the underlying system’s exception handling.

Dynamic Libraries and Linking

For programs relying on dynamic libraries, the CRT plays a role in resolving symbols at runtime. The dynamic linker works in conjunction with the CRT to:

  • Load shared libraries into the program’s address space.
  • Resolve function addresses and variables in the Global Offset Table (GOT).
  • Manage relocation entries for position-independent code.

Platform Variations

The implementation of the CRT varies across platforms and compilers:

  • Glibc (GNU C Library) is the most common CRT implementation on Linux.
  • Microsoft’s Universal CRT (UCRT) is used on Windows.
  • musl is a lightweight CRT implementation designed for static linking and embedded systems.
  • libSystem is macOS’s implementation, which integrates components like libc and libdispatch.

Each implementation tailors the CRT to the platform’s specific system calls and conventions while adhering to the C standard.

Customization and Replacement

In some cases, developers may replace the default CRT to achieve lower-level control or meet specific requirements. For instance:

  • Embedded systems often use minimal CRT implementations to reduce overhead.
  • Game engines and performance-critical software may implement custom memory allocators and bypass certain CRT components.
  • Rust’s #![no_std] feature effectively eliminates the need for a full CRT, relying instead on direct system calls.