diff --git a/src/auditwheel/audit.rs b/src/auditwheel/audit.rs index 4330f8f9f..27dff9358 100644 --- a/src/auditwheel/audit.rs +++ b/src/auditwheel/audit.rs @@ -1,6 +1,7 @@ +use super::musllinux::{find_musl_libc, get_musl_version}; use super::policy::{Policy, MANYLINUX_POLICIES, MUSLLINUX_POLICIES}; use crate::auditwheel::PlatformTag; -use crate::target::{Arch, Target}; +use crate::target::Target; use anyhow::Result; use fs_err::File; use goblin::elf::{sym::STT_FUNC, Elf}; @@ -195,6 +196,22 @@ fn policy_is_satisfied( } } +fn get_default_platform_policies() -> Vec { + if let Ok(Some(musl_libc)) = find_musl_libc() { + if let Ok(Some((major, minor))) = get_musl_version(&musl_libc) { + return MUSLLINUX_POLICIES + .iter() + .filter(|policy| { + policy.name == "linux" + || policy.name == format!("musllinux_{}_{}", major, minor) + }) + .cloned() + .collect(); + } + } + MANYLINUX_POLICIES.clone() +} + /// An reimplementation of auditwheel, which checks elf files for /// manylinux/musllinux compliance. /// @@ -225,30 +242,26 @@ pub fn auditwheel_rs( // Find the highest possible policy, if any let platform_policies = match platform_tag { - Some(PlatformTag::Manylinux { .. }) | None => MANYLINUX_POLICIES.clone(), - Some(PlatformTag::Musllinux { .. }) => { - MUSLLINUX_POLICIES - .clone() - .into_iter() - .map(|mut policy| { - // Fixup musl libc lib_whitelist - if policy.lib_whitelist.remove("libc.so") { - let new_soname = match target.target_arch() { - Arch::Aarch64 => "libc.musl-aarch64.so.1", - Arch::Armv7L => "libc.musl-armv7.so.1", - Arch::Powerpc64Le => "libc.musl-ppc64le.so.1", - Arch::Powerpc64 => "", // musllinux doesn't support ppc64 - Arch::X86 => "libc.musl-x86.so.1", - Arch::X86_64 => "libc.musl-x86_64.so.1", - Arch::S390X => "libc.musl-s390x.so.1", - }; - if !new_soname.is_empty() { - policy.lib_whitelist.insert(new_soname.to_string()); - } - } - policy - }) - .collect() + Some(PlatformTag::Manylinux { .. }) => MANYLINUX_POLICIES.clone(), + Some(PlatformTag::Musllinux { x, y }) => MUSLLINUX_POLICIES + .clone() + .into_iter() + .filter(|policy| { + policy.name == "linux" || policy.name == format!("musllinux_{}_{}", x, y) + }) + .map(|mut policy| { + policy.fixup_musl_libc_so_name(target.target_arch()); + policy + }) + .collect(), + None => { + let mut policies = get_default_platform_policies(); + for policy in &mut policies { + if policy.name.starts_with("musllinux") { + policy.fixup_musl_libc_so_name(target.target_arch()); + } + } + policies } Some(PlatformTag::Linux) => unreachable!(), }; diff --git a/src/auditwheel/mod.rs b/src/auditwheel/mod.rs index c704287a0..df586752f 100644 --- a/src/auditwheel/mod.rs +++ b/src/auditwheel/mod.rs @@ -1,4 +1,5 @@ mod audit; +mod musllinux; mod platform_tag; mod policy; diff --git a/src/auditwheel/musllinux.rs b/src/auditwheel/musllinux.rs new file mode 100644 index 000000000..c149352bd --- /dev/null +++ b/src/auditwheel/musllinux.rs @@ -0,0 +1,47 @@ +use anyhow::{Context, Result}; +use goblin::elf::Elf; +use regex::Regex; +use std::fs; +use std::path::{Path, PathBuf}; +use std::process::{Command, Stdio}; + +/// Find musl libc path from executable's ELF header +pub fn find_musl_libc() -> Result> { + let buffer = fs::read("/bin/ls")?; + let elf = Elf::parse(&buffer)?; + Ok(elf.interpreter.map(PathBuf::from)) +} + +/// Read the musl version from libc library's output +/// +/// The libc library should output something like this to stderr:: +/// +/// musl libc (x86_64) +/// Version 1.2.2 +/// Dynamic Program Loader +pub fn get_musl_version(ld_path: impl AsRef) -> Result> { + let ld_path = ld_path.as_ref(); + let output = Command::new(&ld_path) + .stdout(Stdio::null()) + .stderr(Stdio::piped()) + .output()?; + let stderr = std::str::from_utf8(&output.stderr)?; + let expr = Regex::new(r"Version (\d+)\.(\d+)")?; + if let Some(capture) = expr.captures(stderr) { + let context = "Expected a digit"; + let major = capture + .get(1) + .unwrap() + .as_str() + .parse::() + .context(context)?; + let minor = capture + .get(2) + .unwrap() + .as_str() + .parse::() + .context(context)?; + return Ok(Some((major, minor))); + } + Ok(None) +} diff --git a/src/auditwheel/policy.rs b/src/auditwheel/policy.rs index 435d33b34..f9770e2c7 100644 --- a/src/auditwheel/policy.rs +++ b/src/auditwheel/policy.rs @@ -1,4 +1,5 @@ use crate::auditwheel::PlatformTag; +use crate::target::Arch; use once_cell::sync::Lazy; use serde::Deserialize; use std::cmp::{Ordering, PartialOrd}; @@ -100,6 +101,24 @@ impl Policy { .find(|p| p.name == name || p.aliases.iter().any(|alias| alias == name)) .cloned() } + + pub(crate) fn fixup_musl_libc_so_name(&mut self, target_arch: Arch) { + // Fixup musl libc lib_whitelist + if self.lib_whitelist.remove("libc.so") { + let new_soname = match target_arch { + Arch::Aarch64 => "libc.musl-aarch64.so.1", + Arch::Armv7L => "libc.musl-armv7.so.1", + Arch::Powerpc64Le => "libc.musl-ppc64le.so.1", + Arch::Powerpc64 => "", // musllinux doesn't support ppc64 + Arch::X86 => "libc.musl-x86.so.1", + Arch::X86_64 => "libc.musl-x86_64.so.1", + Arch::S390X => "libc.musl-s390x.so.1", + }; + if !new_soname.is_empty() { + self.lib_whitelist.insert(new_soname.to_string()); + } + } + } } #[cfg(test)]