hardened-malloc-sys 13.0.0-beta.4

Rust bindings for GrapheneOS allocator
Documentation
//
// hardened-malloc-sys: Rust bindings for GrapheneOS allocator
// build.rs: Helper file for build-time information
//
// Copyright (c) 2025 Ali Polatel <alip@chesswob.org>
//
// SPDX-License-Identifier: MIT

use std::{env, fs, path::PathBuf};

fn read_config_file(path: &PathBuf) -> Vec<(String, String)> {
    let content =
        fs::read_to_string(path).expect(&format!("Failed to read config file {:?}", path));

    content
        .lines()
        .filter(|line| {
            let trimmed = line.trim();
            !trimmed.starts_with("#") && !trimmed.is_empty()
        })
        .filter_map(|line| {
            let parts: Vec<&str> = line.splitn(2, '=').collect();
            if parts.len() == 2 {
                Some((parts[0].trim().to_string(), parts[1].trim().to_string()))
            } else {
                panic!("Invalid config line: {line}!");
            }
        })
        .collect()
}

fn main() {
    // Path to the config directory (relative to the project root)
    let root = PathBuf::from(env!("CARGO_MANIFEST_DIR"));

    // Determine the config file based on the "light" feature
    let config_file = if env::var("CARGO_FEATURE_LIGHT").is_ok() {
        root.join("config-light.conf")
    } else {
        root.join("config-default.conf")
    };

    // Initialize the cc build system.
    let mut build = cc::Build::new();
    build.define("_GNU_SOURCE", Some("1"));
    build.flag_if_supported("-std=c17");
    build.flag_if_supported("-march=native");

    // Read configuration key-value pairs from the selected config file.
    // Add each config as a preprocessor define (-Dkey=value).
    let config = read_config_file(&config_file);
    for (key, value) in config {
        build.define(&key, Some(value.as_str()));
    }

    // Set source files directory (vendor/hardened-malloc).
    let vendor_dir = root.join("vendor").join("hardened-malloc");

    // Set source files.
    build.file(vendor_dir.join("chacha.c"));
    build.file(vendor_dir.join("h_malloc.c"));
    build.file(vendor_dir.join("memory.c"));
    build.file(vendor_dir.join("pages.c"));
    build.file(vendor_dir.join("random.c"));
    build.file(vendor_dir.join("util.c"));

    // Set include files.
    build.include(&vendor_dir);
    build.include(vendor_dir.join("include"));
    build.include(vendor_dir.join("third_party"));

    // Add LDFLAGS equivalent for linker flags.
    println!("cargo:rustc-link-arg=-Wl,-O1");
    println!("cargo:rustc-link-arg=-Wl,--as-needed");
    println!("cargo:rustc-link-arg=-Wl,-z,defs");
    println!("cargo:rustc-link-arg=-Wl,-z,relro");
    println!("cargo:rustc-link-arg=-Wl,-z,now");
    println!("cargo:rustc-link-arg=-Wl,-z,nodlopen");
    println!("cargo:rustc-link-arg=-Wl,-z,text");

    // Compile.
    build.compile("hardened_malloc");

    // Link statically.
    println!("cargo:rustc-link-lib=static=hardened_malloc");
    println!("cargo:rustc-link-search={}", env::var("OUT_DIR").unwrap());

    // Rerun the build script if config files change.
    println!("cargo:rerun-if-changed={}", config_file.display());
    println!("cargo:rerun-if-changed={}", vendor_dir.display());
}