make-initrd-ng: use goblin instead of shelling out to patchelf and friends

This commit is contained in:
K900 2022-07-30 12:31:15 +03:00
parent 1356441cb1
commit daee67dae6
3 changed files with 141 additions and 50 deletions

View File

@ -1,5 +1,97 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 3
[[package]]
name = "cfg-if"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
[[package]]
name = "goblin"
version = "0.5.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "91766b1121940d622933a13e20665857648681816089c9bc2075c4b75a6e4f6b"
dependencies = [
"log",
"plain",
"scroll",
]
[[package]]
name = "log"
version = "0.4.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e"
dependencies = [
"cfg-if",
]
[[package]]
name = "make-initrd-ng"
version = "0.1.0"
dependencies = [
"goblin",
]
[[package]]
name = "plain"
version = "0.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b4596b6d070b27117e987119b4dac604f3c58cfb0b191112e24771b2faeac1a6"
[[package]]
name = "proc-macro2"
version = "1.0.42"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c278e965f1d8cf32d6e0e96de3d3e79712178ae67986d9cf9151f51e95aac89b"
dependencies = [
"unicode-ident",
]
[[package]]
name = "quote"
version = "1.0.20"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3bcdf212e9776fbcb2d23ab029360416bb1706b1aea2d1a5ba002727cbcab804"
dependencies = [
"proc-macro2",
]
[[package]]
name = "scroll"
version = "0.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "04c565b551bafbef4157586fa379538366e4385d42082f255bfd96e4fe8519da"
dependencies = [
"scroll_derive",
]
[[package]]
name = "scroll_derive"
version = "0.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bdbda6ac5cd1321e724fa9cee216f3a61885889b896f073b8f82322789c5250e"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "syn"
version = "1.0.98"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c50aef8a904de4c23c788f104b7dddc7d6f79c647c7c8ce4cc8f73eb0ca773dd"
dependencies = [
"proc-macro2",
"quote",
"unicode-ident",
]
[[package]]
name = "unicode-ident"
version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "15c61ba63f9235225a22310255a29b806b907c9b8c964bcbd0a2c70f3f2deea7"

View File

@ -7,3 +7,4 @@ edition = "2018"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
goblin = "0.5.0"

View File

@ -3,11 +3,13 @@ use std::env;
use std::ffi::OsStr;
use std::fs;
use std::hash::Hash;
use std::io::{BufRead, BufReader, Error, ErrorKind};
use std::io::{BufRead, BufReader, Error};
use std::os::unix;
use std::path::{Component, Path, PathBuf};
use std::process::Command;
use goblin::{elf::Elf, Object};
struct NonRepeatingQueue<T> {
queue: VecDeque<T>,
seen: HashSet<T>,
@ -38,50 +40,32 @@ impl<T: Clone + Eq + Hash> NonRepeatingQueue<T> {
}
}
fn patch_elf<S: AsRef<OsStr>, P: AsRef<OsStr>>(mode: S, path: P) -> Result<String, Error> {
let output = Command::new("patchelf").arg(&mode).arg(&path).output()?;
if output.status.success() {
Ok(String::from_utf8(output.stdout).expect("Failed to parse output"))
} else {
Err(Error::new(
ErrorKind::Other,
format!(
"failed: patchelf {:?} {:?}",
OsStr::new(&mode),
OsStr::new(&path)
),
))
}
}
fn copy_file<P: AsRef<Path> + AsRef<OsStr>, S: AsRef<Path> + AsRef<OsStr>>(
fn add_dependencies<P: AsRef<Path> + AsRef<OsStr>>(
source: P,
target: S,
elf: Elf,
queue: &mut NonRepeatingQueue<Box<Path>>,
) -> Result<(), Error> {
fs::copy(&source, &target)?;
if !Command::new("ldd").arg(&source).output()?.status.success() {
// Not dynamically linked - no need to recurse
return Ok(());
) {
if let Some(interp) = elf.interpreter {
queue.push_back(Box::from(Path::new(interp)));
}
let rpath_string = patch_elf("--print-rpath", &source)?;
let needed_string = patch_elf("--print-needed", &source)?;
// Shared libraries don't have an interpreter
if let Ok(interpreter_string) = patch_elf("--print-interpreter", &source) {
queue.push_back(Box::from(Path::new(&interpreter_string.trim())));
}
let rpaths = if elf.runpaths.len() > 0 {
elf.runpaths
} else if elf.rpaths.len() > 0 {
elf.rpaths
} else {
vec![]
};
let rpath = rpath_string
.trim()
.split(":")
let rpaths_as_path = rpaths
.into_iter()
.flat_map(|p| p.split(":"))
.map(|p| Box::<Path>::from(Path::new(p)))
.collect::<Vec<_>>();
for line in needed_string.lines() {
for line in elf.libraries {
let mut found = false;
for path in &rpath {
for path in &rpaths_as_path {
let lib = path.join(line);
if lib.exists() {
// No need to recurse. The queue will bring it back round.
@ -100,22 +84,36 @@ fn copy_file<P: AsRef<Path> + AsRef<OsStr>, S: AsRef<Path> + AsRef<OsStr>>(
);
}
}
}
// Make file writable to strip it
let mut permissions = fs::metadata(&target)?.permissions();
permissions.set_readonly(false);
fs::set_permissions(&target, permissions)?;
fn copy_file<P: AsRef<Path> + AsRef<OsStr>, S: AsRef<Path> + AsRef<OsStr>>(
source: P,
target: S,
queue: &mut NonRepeatingQueue<Box<Path>>,
) -> Result<(), Error> {
fs::copy(&source, &target)?;
// Strip further than normal
if !Command::new("strip")
.arg("--strip-all")
.arg(OsStr::new(&target))
.output()?
.status
.success()
{
println!("{:?} was not successfully stripped.", OsStr::new(&target));
}
let contents = fs::read(&source)?;
if let Ok(Object::Elf(e)) = Object::parse(&contents) {
add_dependencies(source, e, queue);
// Make file writable to strip it
let mut permissions = fs::metadata(&target)?.permissions();
permissions.set_readonly(false);
fs::set_permissions(&target, permissions)?;
// Strip further than normal
if !Command::new("strip")
.arg("--strip-all")
.arg(OsStr::new(&target))
.output()?
.status
.success()
{
println!("{:?} was not successfully stripped.", OsStr::new(&target));
}
};
Ok(())
}