libc Rust Documentation

repository·main·Indexed 25 days ago

https://github.com/rust-lang/libc

Raw FFI bindings to platform libraries like libc, version 1.0.0-alpha.4. Includes guidelines for safe struct initialization and platform-independent coding, details on Cargo features like 'std' and 'extra_traits', and stability guarantees across Tier 1, 2, and 3 targets. Also contains documentation for ctest, a tool for automated FFI binding testing, and the libc-test framework for maintaining API backward compatibility.

Tokens
2.6K
Snippets
8
Records
15
Agent score
77%

What's inside libc

  1. How libc API stability tests work

    main
    The libc-test directory contains text files that define the supported API surface for libc. These files are consumed by build.rs during the build process to automatically generate tests. The purpose of these tests is to ensure that existing APIs are not accidentally removed between different releases of the libc crate, maintaining backward compatibility.
  2. How ctest validates FFI bindings

    main

    The ctest library automates the validation of *-sys crates by:

    1. Parsing the Rust crate: It learns about all definitions (functions, constants, structs, etc.) within the target Rust module.
    2. Generating a dual-language test suite:
      • A Rust file containing a main function (included via include! in your project).
      • A C file compiled as part of the build process. This C file includes the actual C headers and provides the ground truth regarding C-side types, sizes, and alignments.
    3. Comparison: The generated tests ensure that Rust function signatures, constant values, struct layouts/alignment, and type sizes/alignment match the C equivalents exactly.
  3. The file inclusion order for API tests

    main

    API test files are processed in a specific hierarchical order to build the complete set of supported symbols. When defining or adding new API tests, understand that they are included in this sequence:

    1. Family: General groupings (e.g., unix.txt). Note that windows is handled separately as an OS name.
    2. Vendor: Specific vendors (e.g., apple.txt). This allows sharing system calls across related OSs (e.g., ios.txt and macos.txt sharing a kernel).
    3. OS: Operating system specific files (e.g., linux.txt, macos.txt, windows.txt).
    4. Architecture: Architecture-specific system calls (e.g., linux-x86_64.txt or linux-aarch64.txt).
    5. Target Environment: Environment-specific files (e.g., windows-mscv.txt or windows-gnu.txt).
  4. Set up ctest for automated FFI binding testing

    main

    To use ctest to validate that your *-sys crate's Rust APIs match their C definitions, follow these steps:

    1. Create a test project: Create a new Cargo binary project (e.g., systest) within your repository.
    2. Configure dependencies: Add ctest to your [build-dependencies] and include your *-sys crate and libc in your [dependencies].
    3. Implement a build script: Use ctest::TestGenerator in build.rs to specify C headers, include directories, and the target Rust module to generate tests for.
    4. Include generated tests: In your test project's src/main.rs, use the include! macro to pull in the generated Rust file from OUT_DIR.
    5. Run tests: Execute cargo run within the test project directory.
    $ cargo new --bin systest
  5. Understand libc stability and breaking changes

    main

    While libc aims for semver compatibility, it must occasionally ship changes within a semver-compatible release to follow platform API changes. This is because the underlying C APIs change frequently.

    Common 'breaking' changes that may occur within a semver release:

    • Adding fields to an exhaustive struct.
    • Changing the type or removing fields named padding, reserved, or similar.
    • Changing the length of an array type.
    • Changing a struct field from one type to another (e.g., int to long).
    • Changing the values of constants or type aliases.

    Stability Guarantees:

    • libc aims to follow platform changes, including breaking ones.
    • Public API is generally not expected to change on Tier 1 targets.
    • Tier 2 targets have relaxed stability requirements.
    • Tier 3 targets have no stability enforcement.
  6. Usage guidelines for libc FFI bindings

    main

    Because libc provides raw FFI bindings to platform system libraries, users should follow these guidelines to avoid soundness and stability issues:

    1. Initialize structs safely: Never use MaybeUninit::uninit() followed by assume_init() for libc structs, as they often contain padding or may change in size. Instead, use MaybeUninit::zeroed() or Default implementations. Alternatively, access fields via raw pointers.
    2. Avoid hardcoding platform details: Do not rely on exact constant values, array lengths, or specific type alias sizes (e.g., assuming time_t is always i64). Use the provided type aliases or constants directly (e.g., use [c_char; IFNAMSIZ] instead of [c_char; 16]).
    3. Do not use anonymous type names: Avoid naming types like __c_anonymous_* in your code. These are internal representations of C anonymous fields. Access the fields through the parent struct instead.
    4. Avoid reserved fields: Do not rely on fields named __reserved, _pad, or _spare, as their types and presence change frequently to accommodate platform updates.
    5. Monitor deprecations: Pay attention to deprecation warnings, as they signal necessary API migrations.
  7. Include generated tests in your Rust source

    main

    Once ctest has generated the test files via the build script, you must include the resulting Rust file in your src/main.rs using the include! macro and the OUT_DIR environment variable. This allows the generated main function to run the validation logic.

    #![allow(bad_style)]
    
    use libc::*;
    use mylib_sys::*;
    
    include!(concat!(env!("OUT_DIR"), "/all.rs"));
  8. Configure TestGenerator in build.rs

    main

    The ctest::TestGenerator is used within a build.rs script to define how the test suite is constructed. You must provide the C header files, the include paths for those headers, and the path to the Rust source file you wish to validate.

    Key methods:

    • .header(path): Specifies a C header file where APIs are defined.
    • .include(path): Specifies a directory containing header files.
    • ctest::generate_test(&mut cfg, rust_src_path, output_filename): Generates the test suite, taking the configuration, the path to the *-sys library source, and the name of the Rust file to be generated.
    fn main() {
        let mut cfg = ctest::TestGenerator::new();
    
        // Include the header files where the C APIs are defined
        cfg.header("foo.h")
           .header("bar.h");
    
        // Include the directory where the header files are defined
        cfg.include("path/to/include");
    
        // Generate the tests, passing the path to the `*-sys` library as well as
        // the module to generate.
        ctest::generate_test(&mut cfg, "../mylib-sys/lib.rs", "all.rs");
    }
  9. Configure libc Cargo features

    main

    The libc crate provides several features to control its behavior and capabilities:

    • std: (Default) Assumes the standard library provides necessary link directives. If disabled, libc will emit its own link directives. Note: This feature is slated for removal in libc 1.0. For no-std environments, start adding your own #[link] attributes or rustc-link-lib directives now.
    • extra_traits: Adds Eq, Hash, and PartialEq implementations to all libc types (which already implement Clone, Copy, and Debug). Note: This feature is expected to be removed in libc 1.0.
    • Deprecated features: const-extern-fn, align, and use_std are deprecated and have no effect.
  10. Update pre-generated test files

    main

    If you modify the test templates for either Rust or C, you must update the pre-generated test files used for verification by running the tests with the LIBC_BLESS=1 environment variable.

    $ LIBC_BLESS=1 cargo test
  11. Linux futex operation constants

    main

    The linux::futex module provides the fundamental operation constants used with the Linux futex (fast userspace mutex) system call. These constants define the primary actions such as waiting, waking, and requeueing.

    // Primary operations
    FUTEX_WAIT
    FUTEX_WAKE
    FUTEX_FD
    FUTEX_REQUEUE
    FUTEX_CMP_REQUEUE
    FUTEX_WAKE_OP
    FUTEX_LOCK_PI
    FUTEX_UNLOCK_PI
    FUTEX_TRYLOCK_PI
    FUTEX_WAIT_BITSET
    FUTEX_WAKE_BITSET
    FUTEX_WAIT_REQUEUE_PI
    FUTEX_CMP_REQUEUE_PI
    FUTEX_LOCK_PI2
    
    // Flags
    FUTEX_PRIVATE_FLAG
    FUTEX_CLOCK_REALTIME
    
    // Command mask
    FUTEX_CMD_MASK