Skip to content

Commit

Permalink
improved rpath support
Browse files Browse the repository at this point in the history
  • Loading branch information
mariotaku committed Aug 22, 2023
1 parent 97fa33f commit d8f49a1
Show file tree
Hide file tree
Showing 13 changed files with 203 additions and 173 deletions.
1 change: 0 additions & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

43 changes: 11 additions & 32 deletions common/bin/src/binary.rs
Original file line number Diff line number Diff line change
@@ -1,36 +1,15 @@
use std::fs::File;
use std::io::{Error, ErrorKind};
use std::path::Path;

use elf::dynamic::Dyn;
use elf::endian::AnyEndian;
use elf::symbol::Symbol;
use elf::{abi, ElfStream};

use crate::{BinaryInfo, LibraryInfo};
use crate::BinaryInfo;

impl BinaryInfo {
pub fn find_library(&self, name: &str) -> Option<LibraryInfo> {
for rpath in &self.rpath {
let path =
Path::new(&rpath.replace("$ORIGIN", &self.dir.as_ref().unwrap().to_string_lossy()))
.join(name);
if let Ok(f) = File::open(&path) {
return LibraryInfo::parse(f, false, path.file_name().unwrap().to_string_lossy())
.map_err(|e| {
Error::new(ErrorKind::InvalidData, format!("Bad library info: {e:?}"))
})
.ok();
}
}
return None;
}

pub fn parse<S, N, D>(source: S, name: N, dir: Option<D>) -> Result<Self, elf::ParseError>
pub fn parse<S, N>(source: S, name: N) -> Result<Self, elf::ParseError>
where
S: std::io::Read + std::io::Seek,
N: AsRef<str>,
D: AsRef<Path>,
{
let mut rpath = Vec::<String>::new();
let mut needed = Vec::<String>::new();
Expand All @@ -50,13 +29,12 @@ impl BinaryInfo {
dynstr_table.get(entry.d_val() as usize).unwrap(),
));
}
abi::DT_RPATH | abi::DT_RUNPATH => rpath.push(
abi::DT_RPATH | abi::DT_RUNPATH => rpath.extend(
dynstr_table
.get(entry.d_val() as usize)
.unwrap()
.split(":")
.map(|s| String::from(s))
.collect(),
.map(|s| String::from(s)),
),
_ => {}
}
Expand All @@ -81,18 +59,19 @@ impl BinaryInfo {
if !sym.is_undefined() || sym.st_name == 0 || sym.st_bind() == abi::STB_WEAK {
return vec![];
}
if let Some(ver_table) = &ver_table {
if let Some(ver) = ver_table.get_requirement(index).ok().flatten() {
return vec![format!("{name}@{}", ver.name)];
}
if let Some(ver) = ver_table
.as_ref()
.map(|t| t.get_requirement(index).ok().flatten())
.flatten()
{
return vec![format!("{name}@{}", ver.name)];
}
return vec![name.clone()];
})
.collect();

return Ok(Self {
name: String::from(name.as_ref()),
dir: dir.map(|d| d.as_ref().to_path_buf()),
rpath,
needed,
undefined,
Expand All @@ -110,7 +89,7 @@ mod tests {
fn test_parse() {
let mut content = Cursor::new(include_bytes!("fixtures/sample.bin"));
let info =
BinaryInfo::parse(&mut content, "sample.bin", None).expect("should not have any error");
BinaryInfo::parse(&mut content, "sample.bin").expect("should not have any error");
assert_eq!(info.needed[0], "libc.so.6");
}
}
17 changes: 15 additions & 2 deletions common/bin/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
use std::path::PathBuf;
use serde::{Deserialize, Serialize};

pub mod binary;
Expand All @@ -7,7 +6,6 @@ pub mod library;
#[derive(Debug, Serialize, Deserialize)]
pub struct BinaryInfo {
pub name: String,
pub dir: Option<PathBuf>,
pub rpath: Vec<String>,
pub needed: Vec<String>,
pub undefined: Vec<String>,
Expand All @@ -22,4 +20,19 @@ pub struct LibraryInfo {
pub names: Vec<String>,
#[serde(skip_serializing_if = "Vec::is_empty", default)]
pub undefined: Vec<String>,
#[serde(skip_serializing, default = "LibraryPriority::default")]
pub priority: LibraryPriority,
}

#[derive(Debug, Clone, Deserialize, PartialEq)]
pub enum LibraryPriority {
Rpath,
System,
Package,
}

impl Default for LibraryPriority {
fn default() -> Self {
return LibraryPriority::System;
}
}
1 change: 1 addition & 0 deletions common/bin/src/library.rs
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,7 @@ impl LibraryInfo {
symbols,
undefined,
names: Default::default(),
priority: Default::default(),
});
}
}
2 changes: 1 addition & 1 deletion common/fw/src/firmware.rs
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,7 @@ impl Firmware {
return if cfg!(feature = "linux-install") {
PathBuf::from("/usr/share/webosbrew/compat-checker/data")
} else {
PathBuf::from("common/data")
PathBuf::from("common").join("data")
};
}
}
148 changes: 92 additions & 56 deletions common/ipk/src/component.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,11 @@ use std::collections::HashMap;
use std::fs;
use std::fs::File;
use std::io::{Error, ErrorKind};
use std::path::Path;
use std::path::{Path, PathBuf};

use path_slash::CowExt;

use bin_lib::{BinaryInfo, LibraryInfo};
use bin_lib::{BinaryInfo, LibraryInfo, LibraryPriority};

use crate::{AppInfo, Component, ServiceInfo, Symlinks};

Expand All @@ -19,10 +19,10 @@ impl AppInfo {

impl ServiceInfo {
fn is_native(&self) -> bool {
if let (Some(engine), Some(_)) = (&self.engine, &self.executable) {
return engine == "native";
}
return false;
let (Some(engine), Some(_)) = (&self.engine, &self.executable) else {
return false;
};
return engine == "native";
}
}

Expand All @@ -47,29 +47,31 @@ impl Component<AppInfo> {
libs: Default::default(),
});
}
let libs = Self::list_libs(dir, links)?;
let exe_path = dir.join(Cow::from_slash(&info.main));
let bin_info = BinaryInfo::parse(
File::open(&exe_path).map_err(|e| {
Error::new(
e.kind(),
format!("Failed to open main executable {}: {e}", info.main),
)
})?,
exe_path.file_name().unwrap().to_string_lossy(),
)
.map_err(|e| {
Error::new(
ErrorKind::InvalidData,
format!("Bad app executable {}: {e}", info.main),
)
})?;
let libs = Self::list_libs(
dir,
&Component::<AppInfo>::rpath(&bin_info.rpath, &exe_path),
links,
)?;
return Ok(Self {
id: info.id.clone(),
info: info.clone(),
exe: Some(
BinaryInfo::parse(
File::open(&exe_path).map_err(|e| {
Error::new(
e.kind(),
format!("Failed to open main executable {}: {e}", info.main),
)
})?,
exe_path.file_name().unwrap().to_string_lossy(),
exe_path.parent()
)
.map_err(|e| {
Error::new(
ErrorKind::InvalidData,
format!("Bad app executable {}: {e}", info.main),
)
})?,
),
exe: Some(bin_info),
libs,
});
}
Expand All @@ -88,24 +90,26 @@ impl Component<ServiceInfo> {
});
}
let executable = info.executable.as_ref().unwrap();
let libs = Self::list_libs(dir, links)?;
let exe_path = dir.join(Cow::from_slash(executable));
let bin_info = BinaryInfo::parse(
File::open(dir.join(&exe_path))?,
exe_path.file_name().unwrap().to_string_lossy(),
)
.map_err(|e| {
Error::new(
ErrorKind::InvalidData,
format!("Bad app executable {}: {e:?}", executable),
)
})?;
let libs = Self::list_libs(
dir,
&Component::<ServiceInfo>::rpath(&bin_info.rpath, &exe_path),
links,
)?;
return Ok(Self {
id: info.id.clone(),
info: info.clone(),
exe: Some(
BinaryInfo::parse(
File::open(dir.join(&exe_path))?,
exe_path.file_name().unwrap().to_string_lossy(),
exe_path.parent()
)
.map_err(|e| {
Error::new(
ErrorKind::InvalidData,
format!("Bad app executable {}: {e:?}", executable),
)
})?,
),
exe: Some(bin_info),
libs,
});
}
Expand All @@ -117,35 +121,67 @@ impl<T> Component<T> {
}

pub fn is_required(&self, lib: &LibraryInfo) -> bool {
if let Some(exe) = &self.exe {
if exe
.needed
.iter()
.find(|needed| lib.has_name(needed))
.is_some()
{
return true;
}
}
return false;
let Some(exe) = &self.exe else {
return false;
};
return exe
.needed
.iter()
.find(|needed| lib.has_name(needed))
.is_some();
}

fn list_libs(dir: &Path, links: &Symlinks) -> Result<Vec<LibraryInfo>, Error> {
fn rpath<P>(rpath: &Vec<String>, bin_path: P) -> Vec<PathBuf>
where
P: AsRef<Path>,
{
return rpath
.iter()
.filter_map(|p| {
PathBuf::from(p.replace(
"$ORIGIN",
bin_path.as_ref().parent().unwrap().to_str().unwrap(),
))
.canonicalize()
.ok()
})
.collect();
}

fn list_libs(
dir: &Path,
rpath: &Vec<PathBuf>,
links: &Symlinks,
) -> Result<Vec<LibraryInfo>, Error> {
let mut libs = HashMap::new();
if let Ok(entries) = fs::read_dir(dir.join("lib")) {
let lib_dir = dir.join("lib").canonicalize();
let mut lib_dirs: Vec<(&Path, bool)> = rpath.iter().map(|p| (p.as_path(), true)).collect();
if let Ok(lib_dir) = lib_dir.as_ref() {
if !rpath.contains(&lib_dir) {
lib_dirs.push((lib_dir.as_path(), false));
}
}
for (lib_dir, is_rpath) in lib_dirs {
let Ok(entries) = fs::read_dir(lib_dir) else{continue;};
for entry in entries {
let entry = entry?;
if !entry.file_type()?.is_file() {
continue;
}
let path = entry.path();
if let Ok(lib) = LibraryInfo::parse(
let Ok(mut lib) = LibraryInfo::parse(
File::open(&path)?,
true,
path.file_name().unwrap().to_string_lossy(),
) {
libs.insert(path, lib);
}
) else {
continue;
};
lib.priority = if is_rpath {
LibraryPriority::Rpath
} else {
LibraryPriority::Package
};
libs.insert(path, lib);
}
}
for (path, lib) in &mut libs {
Expand Down
1 change: 0 additions & 1 deletion common/verify/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[dependencies]
fw-lib = { path = "../../common/fw" }
bin-lib = { path = "../../common/bin", optional = true }
ipk-lib = { path = "../../common/ipk", optional = true }

Expand Down
Loading

0 comments on commit d8f49a1

Please sign in to comment.