about summary refs log tree commit diff
path: root/library/compiler-builtins/crates/musl-math-sys/build.rs
diff options
context:
space:
mode:
Diffstat (limited to 'library/compiler-builtins/crates/musl-math-sys/build.rs')
-rw-r--r--library/compiler-builtins/crates/musl-math-sys/build.rs350
1 files changed, 350 insertions, 0 deletions
diff --git a/library/compiler-builtins/crates/musl-math-sys/build.rs b/library/compiler-builtins/crates/musl-math-sys/build.rs
new file mode 100644
index 00000000000..b00dbc73e28
--- /dev/null
+++ b/library/compiler-builtins/crates/musl-math-sys/build.rs
@@ -0,0 +1,350 @@
+use std::collections::BTreeMap;
+use std::path::{Path, PathBuf};
+use std::process::{Command, Stdio};
+use std::{env, fs, str};
+
+/// Static library that will be built
+const LIB_NAME: &str = "musl_math_prefixed";
+
+/// Files that have more than one symbol. Map of file names to the symbols defined in that file.
+const MULTIPLE_SYMBOLS: &[(&str, &[&str])] = &[
+    (
+        "__invtrigl",
+        &["__invtrigl", "__invtrigl_R", "__pio2_hi", "__pio2_lo"],
+    ),
+    ("__polevll", &["__polevll", "__p1evll"]),
+    ("erf", &["erf", "erfc"]),
+    ("erff", &["erff", "erfcf"]),
+    ("erfl", &["erfl", "erfcl"]),
+    ("exp10", &["exp10", "pow10"]),
+    ("exp10f", &["exp10f", "pow10f"]),
+    ("exp10l", &["exp10l", "pow10l"]),
+    ("exp2f_data", &["exp2f_data", "__exp2f_data"]),
+    ("exp_data", &["exp_data", "__exp_data"]),
+    ("j0", &["j0", "y0"]),
+    ("j0f", &["j0f", "y0f"]),
+    ("j1", &["j1", "y1"]),
+    ("j1f", &["j1f", "y1f"]),
+    ("jn", &["jn", "yn"]),
+    ("jnf", &["jnf", "ynf"]),
+    ("lgamma", &["lgamma", "__lgamma_r"]),
+    ("remainder", &["remainder", "drem"]),
+    ("remainderf", &["remainderf", "dremf"]),
+    ("lgammaf", &["lgammaf", "lgammaf_r", "__lgammaf_r"]),
+    ("lgammal", &["lgammal", "lgammal_r", "__lgammal_r"]),
+    ("log2_data", &["log2_data", "__log2_data"]),
+    ("log2f_data", &["log2f_data", "__log2f_data"]),
+    ("log_data", &["log_data", "__log_data"]),
+    ("logf_data", &["logf_data", "__logf_data"]),
+    ("pow_data", &["pow_data", "__pow_log_data"]),
+    ("powf_data", &["powf_data", "__powf_log2_data"]),
+    ("signgam", &["signgam", "__signgam"]),
+    ("sqrt_data", &["sqrt_data", "__rsqrt_tab"]),
+];
+
+fn main() {
+    let cfg = Config::from_env();
+
+    if cfg.target_env == "msvc"
+        || cfg.target_family == "wasm"
+        || cfg.target_features.iter().any(|f| f == "thumb-mode")
+    {
+        println!(
+            "cargo::warning=Musl doesn't compile with the current \
+            target {}; skipping build",
+            &cfg.target_string
+        );
+        return;
+    }
+
+    build_musl_math(&cfg);
+}
+
+#[allow(dead_code)]
+#[derive(Debug)]
+struct Config {
+    manifest_dir: PathBuf,
+    out_dir: PathBuf,
+    musl_dir: PathBuf,
+    musl_arch: String,
+    target_arch: String,
+    target_env: String,
+    target_family: String,
+    target_os: String,
+    target_string: String,
+    target_vendor: String,
+    target_features: Vec<String>,
+}
+
+impl Config {
+    fn from_env() -> Self {
+        let manifest_dir = PathBuf::from(env::var("CARGO_MANIFEST_DIR").unwrap());
+        let target_features = env::var("CARGO_CFG_TARGET_FEATURE")
+            .map(|feats| feats.split(',').map(ToOwned::to_owned).collect())
+            .unwrap_or_default();
+        let musl_dir = manifest_dir.join("musl");
+
+        let target_arch = env::var("CARGO_CFG_TARGET_ARCH").unwrap();
+        let musl_arch = if target_arch == "x86" {
+            "i386".to_owned()
+        } else {
+            target_arch.clone()
+        };
+
+        println!(
+            "cargo::rerun-if-changed={}/c_patches",
+            manifest_dir.display()
+        );
+        println!("cargo::rerun-if-changed={}", musl_dir.display());
+
+        Self {
+            manifest_dir,
+            out_dir: PathBuf::from(env::var("OUT_DIR").unwrap()),
+            musl_dir,
+            musl_arch,
+            target_arch,
+            target_env: env::var("CARGO_CFG_TARGET_ENV").unwrap(),
+            target_family: env::var("CARGO_CFG_TARGET_FAMILY").unwrap(),
+            target_os: env::var("CARGO_CFG_TARGET_OS").unwrap(),
+            target_string: env::var("TARGET").unwrap(),
+            target_vendor: env::var("CARGO_CFG_TARGET_VENDOR").unwrap(),
+            target_features,
+        }
+    }
+}
+
+/// Build musl math symbols to a static library
+fn build_musl_math(cfg: &Config) {
+    let musl_dir = &cfg.musl_dir;
+    let math = musl_dir.join("src/math");
+    let arch_dir = musl_dir.join("arch").join(&cfg.musl_arch);
+    assert!(
+        math.exists(),
+        "musl source not found. Is the submodule up to date?"
+    );
+
+    let source_map = find_math_source(&math, cfg);
+    let out_path = cfg.out_dir.join(format!("lib{LIB_NAME}.a"));
+
+    // Run configuration steps. Usually done as part of the musl `Makefile`.
+    let obj_include = cfg.out_dir.join("musl_obj/include");
+    fs::create_dir_all(&obj_include).unwrap();
+    fs::create_dir_all(obj_include.join("bits")).unwrap();
+    let sed_stat = Command::new("sed")
+        .arg("-f")
+        .arg(musl_dir.join("tools/mkalltypes.sed"))
+        .arg(arch_dir.join("bits/alltypes.h.in"))
+        .arg(musl_dir.join("include/alltypes.h.in"))
+        .stderr(Stdio::inherit())
+        .output()
+        .unwrap();
+    assert!(
+        sed_stat.status.success(),
+        "sed command failed: {:?}",
+        sed_stat.status
+    );
+
+    fs::write(obj_include.join("bits/alltypes.h"), sed_stat.stdout).unwrap();
+
+    let mut cbuild = cc::Build::new();
+    cbuild
+        .extra_warnings(false)
+        .warnings(false)
+        .flag_if_supported("-Wno-bitwise-op-parentheses")
+        .flag_if_supported("-Wno-literal-range")
+        .flag_if_supported("-Wno-parentheses")
+        .flag_if_supported("-Wno-shift-count-overflow")
+        .flag_if_supported("-Wno-shift-op-parentheses")
+        .flag_if_supported("-Wno-unused-but-set-variable")
+        .flag_if_supported("-std=c99")
+        .flag_if_supported("-ffreestanding")
+        .flag_if_supported("-nostdinc")
+        .define("_ALL_SOURCE", "1")
+        .define(
+            "ROOT_INCLUDE_FEATURES",
+            Some(musl_dir.join("include/features.h").to_str().unwrap()),
+        )
+        // Our overrides are in this directory
+        .include(cfg.manifest_dir.join("c_patches"))
+        .include(musl_dir.join("arch").join(&cfg.musl_arch))
+        .include(musl_dir.join("arch/generic"))
+        .include(musl_dir.join("src/include"))
+        .include(musl_dir.join("src/internal"))
+        .include(obj_include)
+        .include(musl_dir.join("include"))
+        .file(cfg.manifest_dir.join("c_patches/alias.c"));
+
+    for (sym_name, src_file) in source_map {
+        // Build the source file
+        cbuild.file(src_file);
+
+        // Trickery! Redefine the symbol names to have the prefix `musl_`, which allows us to
+        // differentiate these symbols from whatever we provide.
+        if let Some((_names, syms)) = MULTIPLE_SYMBOLS
+            .iter()
+            .find(|(name, _syms)| *name == sym_name)
+        {
+            // Handle the occasional file that defines multiple symbols
+            for sym in *syms {
+                cbuild.define(sym, Some(format!("musl_{sym}").as_str()));
+            }
+        } else {
+            // If the file doesn't define multiple symbols, the file name will be the symbol
+            cbuild.define(&sym_name, Some(format!("musl_{sym_name}").as_str()));
+        }
+    }
+
+    if cfg!(windows) {
+        // On Windows we don't have a good way to check symbols, so skip that step.
+        cbuild.compile(LIB_NAME);
+        return;
+    }
+
+    let objfiles = cbuild.compile_intermediates();
+
+    // We create the archive ourselves with relocations rather than letting `cc` do it so we can
+    // encourage it to resolve symbols now. This should help avoid accidentally linking the wrong
+    // thing.
+    let stat = cbuild
+        .get_compiler()
+        .to_command()
+        .arg("-r")
+        .arg("-o")
+        .arg(&out_path)
+        .args(objfiles)
+        .status()
+        .unwrap();
+    assert!(stat.success());
+
+    println!("cargo::rustc-link-lib={LIB_NAME}");
+    println!("cargo::rustc-link-search=native={}", cfg.out_dir.display());
+
+    validate_archive_symbols(&out_path);
+}
+
+/// Build a map of `name -> path`. `name` is typically the symbol name, but this doesn't account
+/// for files that provide multiple symbols.
+fn find_math_source(math_root: &Path, cfg: &Config) -> BTreeMap<String, PathBuf> {
+    let mut map = BTreeMap::new();
+    let mut arch_dir = None;
+
+    // Locate all files and directories
+    for item in fs::read_dir(math_root).unwrap() {
+        let path = item.unwrap().path();
+        let meta = fs::metadata(&path).unwrap();
+
+        if meta.is_dir() {
+            // Make note of the arch-specific directory if it exists
+            if path.file_name().unwrap() == cfg.target_arch.as_str() {
+                arch_dir = Some(path);
+            }
+            continue;
+        }
+
+        // Skip non-source files
+        if path.extension().is_some_and(|ext| ext == "h") {
+            continue;
+        }
+
+        let sym_name = path.file_stem().unwrap();
+        map.insert(sym_name.to_str().unwrap().to_owned(), path.to_owned());
+    }
+
+    // If arch-specific versions are available, build those instead.
+    if let Some(arch_dir) = arch_dir {
+        for item in fs::read_dir(arch_dir).unwrap() {
+            let path = item.unwrap().path();
+            let sym_name = path.file_stem().unwrap();
+
+            if path.extension().unwrap() == "s" {
+                // FIXME: we never build assembly versions since we have no good way to
+                // rename the symbol (our options are probably preprocessor or objcopy).
+                continue;
+            }
+            map.insert(sym_name.to_str().unwrap().to_owned(), path);
+        }
+    }
+
+    map
+}
+
+/// Make sure we don't have something like a loose unprefixed `_cos` called somewhere, which could
+/// wind up linking to system libraries rather than the built musl library.
+fn validate_archive_symbols(out_path: &Path) {
+    const ALLOWED_UNDEF_PFX: &[&str] = &[
+        // PIC and arch-specific
+        ".TOC",
+        "_GLOBAL_OFFSET_TABLE_",
+        "__x86.get_pc_thunk",
+        // gcc/compiler-rt/compiler-builtins symbols
+        "__add",
+        "__aeabi_",
+        "__div",
+        "__eq",
+        "__extend",
+        "__fix",
+        "__float",
+        "__gcc_",
+        "__ge",
+        "__gt",
+        "__le",
+        "__lshr",
+        "__lt",
+        "__mul",
+        "__ne",
+        "__stack_chk_fail",
+        "__stack_chk_guard",
+        "__sub",
+        "__trunc",
+        "__undef",
+        // string routines
+        "__bzero",
+        "bzero",
+        // FPENV interfaces
+        "feclearexcept",
+        "fegetround",
+        "feraiseexcept",
+        "fesetround",
+        "fetestexcept",
+    ];
+
+    // List global undefined symbols
+    let out = Command::new("nm")
+        .arg("-guj")
+        .arg(out_path)
+        .stderr(Stdio::inherit())
+        .output()
+        .unwrap();
+
+    let undef = str::from_utf8(&out.stdout).unwrap();
+    let mut undef = undef.lines().collect::<Vec<_>>();
+    undef.retain(|sym| {
+        // Account for file formats that add a leading `_`
+        !ALLOWED_UNDEF_PFX
+            .iter()
+            .any(|pfx| sym.starts_with(pfx) || sym[1..].starts_with(pfx))
+    });
+
+    assert!(
+        undef.is_empty(),
+        "found disallowed undefined symbols: {undef:#?}"
+    );
+
+    // Find any symbols that are missing the `_musl_` prefix`
+    let out = Command::new("nm")
+        .arg("-gUj")
+        .arg(out_path)
+        .stderr(Stdio::inherit())
+        .output()
+        .unwrap();
+
+    let defined = str::from_utf8(&out.stdout).unwrap();
+    let mut defined = defined.lines().collect::<Vec<_>>();
+    defined.retain(|sym| {
+        !(sym.starts_with("_musl_")
+            || sym.starts_with("musl_")
+            || sym.starts_with("__x86.get_pc_thunk"))
+    });
+
+    assert!(defined.is_empty(), "found unprefixed symbols: {defined:#?}");
+}