From eae4ec307b5ae712a0d896a59eccd47eceefc2d7 Mon Sep 17 00:00:00 2001 From: Ningyuan Li Date: Wed, 14 Jun 2023 00:12:53 +0900 Subject: [PATCH] supports skip status for library --- common/bin/src/lib.rs | 2 +- common/verify/src/bin/binary.rs | 42 ++++---- common/verify/src/bin/mod.rs | 4 +- common/verify/src/ipk/component.rs | 99 +++++++++++++++---- common/verify/src/ipk/mod.rs | 13 ++- packages/fw-extract/src/main.rs | 2 +- packages/ipk-verify/src/main.rs | 148 +++++++++++++++++++++-------- packages/ipk-verify/src/output.rs | 64 +++++++++---- 8 files changed, 268 insertions(+), 106 deletions(-) diff --git a/common/bin/src/lib.rs b/common/bin/src/lib.rs index fd2bded..c02620e 100644 --- a/common/bin/src/lib.rs +++ b/common/bin/src/lib.rs @@ -11,7 +11,7 @@ pub struct BinaryInfo { pub undefined: Vec, } -#[derive(Debug, Serialize, Deserialize)] +#[derive(Debug, Clone, Serialize, Deserialize)] pub struct LibraryInfo { pub name: String, pub needed: Vec, diff --git a/common/verify/src/bin/binary.rs b/common/verify/src/bin/binary.rs index 604e123..b8cd0f1 100644 --- a/common/verify/src/bin/binary.rs +++ b/common/verify/src/bin/binary.rs @@ -18,29 +18,9 @@ impl VerifyWithFirmware for BinaryInfo { .or_else(|| self.find_library(name)); }; - fn resolve_symbols( - lib: &LibraryInfo, - undefined: &mut Vec, - visited: &mut HashSet, - lib_resolver: &F, - ) where - F: Fn(&str) -> Option, - { - undefined.retain(|symbol| !lib.has_symbol(symbol)); - for needed in &lib.needed { - if visited.contains(needed) { - continue; - } - visited.insert(needed.clone()); - if let Some(needed) = lib_resolver(needed) { - resolve_symbols(&needed, undefined, visited, lib_resolver); - } - } - } - for needed in &self.needed { if let Some(lib) = find_library(needed) { - resolve_symbols( + recursive_resolve_symbols( &lib, &mut result.undefined_sym, &mut visited_libs, @@ -54,6 +34,26 @@ impl VerifyWithFirmware for BinaryInfo { } } +pub(crate) fn recursive_resolve_symbols( + lib: &LibraryInfo, + undefined: &mut Vec, + visited: &mut HashSet, + lib_resolver: &F, +) where + F: Fn(&str) -> Option, +{ + undefined.retain(|symbol| !lib.has_symbol(symbol)); + for needed in &lib.needed { + if visited.contains(needed) { + continue; + } + visited.insert(needed.clone()); + if let Some(needed) = lib_resolver(needed) { + recursive_resolve_symbols(&needed, undefined, visited, lib_resolver); + } + } +} + impl BinVerifyResult { pub fn new(name: String) -> Self { return Self { diff --git a/common/verify/src/bin/mod.rs b/common/verify/src/bin/mod.rs index 9d2242b..7b7813c 100644 --- a/common/verify/src/bin/mod.rs +++ b/common/verify/src/bin/mod.rs @@ -1,6 +1,6 @@ -mod binary; +pub(crate) mod binary; -#[derive(Debug)] +#[derive(Debug, Eq, PartialEq)] pub struct BinVerifyResult { pub name: String, pub missing_lib: Vec, diff --git a/common/verify/src/ipk/component.rs b/common/verify/src/ipk/component.rs index 459dd44..fbe551c 100644 --- a/common/verify/src/ipk/component.rs +++ b/common/verify/src/ipk/component.rs @@ -1,20 +1,52 @@ -use bin_lib::BinaryInfo; +use std::collections::HashSet; + +use bin_lib::{BinaryInfo, LibraryInfo}; use fw_lib::Firmware; use ipk_lib::Component; -use crate::ipk::ComponentVerifyResult; -use crate::{bin::BinVerifyResult, VerifyWithFirmware}; +use crate::bin::binary::recursive_resolve_symbols; +use crate::ipk::{ComponentBinVerifyResult, ComponentVerifyResult}; +use crate::{bin::BinVerifyResult, VerifyResult, VerifyWithFirmware}; trait ComponentImpl { fn verify_bin(&self, bin: &BinaryInfo, firmware: &Firmware) -> BinVerifyResult; } +impl VerifyResult for ComponentVerifyResult { + fn is_good(&self) -> bool { + if let ComponentBinVerifyResult::Failed { .. } = &self.exe { + return false; + } + for (required, result) in &self.libs { + if !required { + continue; + } + if let ComponentBinVerifyResult::Failed { .. } = result { + return false; + } + } + return true; + } +} + impl ComponentImpl for Component { fn verify_bin(&self, bin: &BinaryInfo, firmware: &Firmware) -> BinVerifyResult { let mut result = bin.verify(firmware); + let mut visited_libs: HashSet = Default::default(); result.missing_lib.retain_mut(|lib| { if let Some(lib) = self.find_lib(lib) { - result.undefined_sym.retain(|sym| !lib.has_symbol(sym)); + let find_library = |name: &str| -> Option { + return firmware + .find_library(name) + .or_else(|| self.find_lib(name).cloned()); + }; + + recursive_resolve_symbols( + &lib, + &mut result.undefined_sym, + &mut visited_libs, + &find_library, + ); return false; } return true; @@ -30,25 +62,44 @@ impl VerifyWithFirmware for Component { } else { return ComponentVerifyResult { id: self.id.clone(), - exe: None, + exe: ComponentBinVerifyResult::Skipped { + name: String::new(), + }, libs: Default::default(), }; }; - let mut libs: Vec<(bool, BinVerifyResult)> = self + let mut libs: Vec<(bool, ComponentBinVerifyResult)> = self .libs .iter() .map(|lib| { - ( - self.is_required(lib), - self.verify_bin( - &BinaryInfo { + let required = self.is_required(lib); + // System library has higher precedence + if let Some(_) = firmware.find_library(&lib.name) { + return ( + required, + ComponentBinVerifyResult::Skipped { name: lib.name.clone(), - rpath: Default::default(), - needed: lib.needed.clone(), - undefined: lib.undefined.clone(), }, - firmware, - ), + ); + } + let verify_result = self.verify_bin( + &BinaryInfo { + name: lib.name.clone(), + rpath: Default::default(), + needed: lib.needed.clone(), + undefined: lib.undefined.clone(), + }, + firmware, + ); + ( + required, + if verify_result.is_good() { + ComponentBinVerifyResult::Ok { + name: verify_result.name, + } + } else { + ComponentBinVerifyResult::Failed(verify_result) + }, ) }) .collect(); @@ -57,12 +108,26 @@ impl VerifyWithFirmware for Component { if !required_cmp.is_eq() { return required_cmp.reverse(); } - return lib_a.name.cmp(&lib_b.name); + return lib_a.name().cmp(&lib_b.name()); }); return ComponentVerifyResult { id: self.id.clone(), - exe: Some(exe), + exe: if exe.is_good() { + ComponentBinVerifyResult::Ok { name: exe.name } + } else { + ComponentBinVerifyResult::Failed(exe) + }, libs, }; } } + +impl ComponentBinVerifyResult { + pub fn name(&self) -> &str { + return match self { + ComponentBinVerifyResult::Skipped { name } => name, + ComponentBinVerifyResult::Ok { name } => name, + ComponentBinVerifyResult::Failed(result) => &result.name, + }; + } +} diff --git a/common/verify/src/ipk/mod.rs b/common/verify/src/ipk/mod.rs index f4ee99d..40e4375 100644 --- a/common/verify/src/ipk/mod.rs +++ b/common/verify/src/ipk/mod.rs @@ -3,7 +3,7 @@ use ipk_lib::Package; use crate::{bin::BinVerifyResult, VerifyWithFirmware}; -mod component; +pub mod component; #[derive(Debug)] pub struct PackageVerifyResult { @@ -14,8 +14,15 @@ pub struct PackageVerifyResult { #[derive(Debug)] pub struct ComponentVerifyResult { pub id: String, - pub exe: Option, - pub libs: Vec<(bool, BinVerifyResult)>, + pub exe: ComponentBinVerifyResult, + pub libs: Vec<(bool, ComponentBinVerifyResult)>, +} + +#[derive(Debug, Eq, PartialEq)] +pub enum ComponentBinVerifyResult { + Skipped { name: String }, + Ok { name: String }, + Failed(BinVerifyResult), } impl VerifyWithFirmware for Package { diff --git a/packages/fw-extract/src/main.rs b/packages/fw-extract/src/main.rs index 0ab8965..17b7478 100644 --- a/packages/fw-extract/src/main.rs +++ b/packages/fw-extract/src/main.rs @@ -6,8 +6,8 @@ use std::path::PathBuf; use std::process::exit; use clap::Parser; -use regex::Regex; use fw_lib::FirmwareInfo; +use regex::Regex; mod extractor; diff --git a/packages/ipk-verify/src/main.rs b/packages/ipk-verify/src/main.rs index 8850374..b8435ec 100644 --- a/packages/ipk-verify/src/main.rs +++ b/packages/ipk-verify/src/main.rs @@ -10,8 +10,9 @@ use semver::VersionReq; use fw_lib::Firmware; use ipk_lib::Package; -use verify_lib::ipk::{ComponentVerifyResult, PackageVerifyResult}; -use verify_lib::VerifyWithFirmware; +use verify_lib::bin::BinVerifyResult; +use verify_lib::ipk::{ComponentBinVerifyResult, ComponentVerifyResult, PackageVerifyResult}; +use verify_lib::{VerifyResult, VerifyWithFirmware}; use crate::output::ReportOutput; @@ -86,9 +87,7 @@ fn main() { if to_file { eprintln!("Verifying package {}...", package.id); } - output - .write_fmt(format_args!("## Package {}\n\n", package.id)) - .unwrap(); + output.h2(&format!("Package {}", package.id)).unwrap(); let results: Vec<(&Firmware, PackageVerifyResult)> = firmwares .iter() .map(|fw| { @@ -100,26 +99,26 @@ fn main() { if to_file { eprintln!(" - App {}", result.app.id); } - output - .write_fmt(format_args!("### App {}\n\n", result.app.id)) - .unwrap(); - print_component_results( + output.h3(&format!("App {}", result.app.id)).unwrap(); + print_component_summary( results.iter().map(|(fw, res)| (*fw, &res.app)).collect(), &mut output, &format, ) .unwrap(); + print_component_details( + results.iter().map(|(fw, res)| (*fw, &res.app)).collect(), + &mut output, + ) + .unwrap(); for idx in 0..result.services.len() { if to_file { eprintln!(" - Service {}", result.services.get(idx).unwrap().id); } output - .write_fmt(format_args!( - "### Service {}", - result.services.get(idx).unwrap().id - )) + .h3(&format!("Service {}", result.services.get(idx).unwrap().id)) .unwrap(); - print_component_results( + print_component_summary( results .iter() .map(|(fw, res)| (*fw, res.services.get(idx).unwrap())) @@ -128,52 +127,117 @@ fn main() { &format, ) .unwrap(); + print_component_details( + results + .iter() + .map(|(fw, res)| (*fw, res.services.get(idx).unwrap())) + .collect(), + &mut output, + ) + .unwrap(); } } } -fn print_component_results( +fn print_component_summary( results: Vec<(&Firmware, &ComponentVerifyResult)>, out: &mut Box, out_fmt: &OutputFormat, ) -> Result<(), Error> { let (_, result) = *results.first().unwrap(); - if let Some(exe) = &result.exe { - let mut table = Table::new(); - table.set_format(out.table_format(out_fmt)); - table.set_titles(Row::from_iter( - iter::once(String::new()).chain( + if let ComponentBinVerifyResult::Skipped { .. } = &result.exe { + out.write_fmt(format_args!("Skip because this component is not native\n"))?; + return Ok(()); + } + let mut table = Table::new(); + table.set_format(out.table_format(out_fmt)); + table.set_titles(Row::from_iter( + iter::once(String::new()).chain( + results + .iter() + .map(|(firmware, _result)| firmware.info.release.to_string()), + ), + )); + table.add_row(Row::new( + iter::once(Cell::new(result.exe.name())) + .chain( results .iter() - .map(|(firmware, _result)| firmware.info.release.to_string()), - ), - )); + .map(|(_, result)| out.result_cell(&result.exe, out_fmt)), + ) + .collect(), + )); + for (idx, (required, lib)) in result.libs.iter().enumerate() { + let name = if *required { + Cell::new(&format!("required lib {}", lib.name())) + } else { + Cell::new(&format!("lib {}", lib.name())) + }; table.add_row(Row::new( - iter::once(Cell::new(&exe.name)) + iter::once(name) .chain( - results - .iter() - .map(|(_, result)| out.result_cell(result.exe.as_ref().unwrap(), out_fmt)), + results.iter().map(|(_, result)| { + out.result_cell(&result.libs.get(idx).unwrap().1, out_fmt) + }), ) .collect(), )); - for (idx, (required, lib)) in result.libs.iter().enumerate() { - let name = if *required { - Cell::new(&format!("required lib {}", lib.name)) + } + out.print_table(&table)?; + return Ok(()); +} + +fn print_component_details( + results: Vec<(&Firmware, &ComponentVerifyResult)>, + out: &mut Box, +) -> Result<(), Error> { + let (_, result) = *results.first().unwrap(); + if results.iter().all(|r| r.1.is_good()) { + out.write_fmt(format_args!("All OK\n"))?; + return Ok(()); + } + out.h4(result.exe.name())?; + for (fw, result) in &results { + if let ComponentBinVerifyResult::Failed(result) = &result.exe { + out.h5(&format!("On {}", fw.info))?; + print_bin_verify_details(result, out)?; + out.write_fmt(format_args!("\n"))?; + } + } + for (index, (required, lib)) in result.libs.iter().enumerate() { + if !required { + continue; + } + if results.iter().all(|(fw, result)| { + if let ComponentBinVerifyResult::Failed(_) = result.libs.get(index).unwrap().1 { + false } else { - Cell::new(&format!("lib {}", lib.name)) - }; - table.add_row(Row::new( - iter::once(name) - .chain(results.iter().map(|(_, result)| { - out.result_cell(&result.libs.get(idx).unwrap().1, out_fmt) - })) - .collect(), - )); + true + } + }) { + continue; } - out.print_table(&table)?; - } else { - out.write_fmt(format_args!("Skip because this component is not native\n"))?; + out.h4(lib.name())?; + for (fw, result) in &results { + if let ComponentBinVerifyResult::Failed(result) = &result.libs.get(index).unwrap().1 { + out.h5(&format!("On {}", fw.info))?; + print_bin_verify_details(result, out)?; + out.write_fmt(format_args!("\n"))?; + } + } + } + return Ok(()); +} + +fn print_bin_verify_details( + result: &BinVerifyResult, + out: &mut Box, +) -> Result<(), Error> { + for lib in &result.missing_lib { + out.write_fmt(format_args!("* Library {lib} is missing\n"))?; + } + for sym in &result.undefined_sym { + out.write_fmt(format_args!("* Symbol {sym} is undefined\n"))?; } return Ok(()); } diff --git a/packages/ipk-verify/src/output.rs b/packages/ipk-verify/src/output.rs index 5976ee4..6b6741e 100644 --- a/packages/ipk-verify/src/output.rs +++ b/packages/ipk-verify/src/output.rs @@ -5,28 +5,38 @@ use prettytable::format::{FormatBuilder, LinePosition, LineSeparator, TableForma use prettytable::{Cell, Table}; use term::{color, Attr}; -use verify_lib::{bin::BinVerifyResult, VerifyResult}; +use verify_lib::ipk::ComponentBinVerifyResult; use crate::OutputFormat; pub trait PrintTable { - fn result_cell(&self, result: &BinVerifyResult, out_fmt: &OutputFormat) -> Cell { - return if result.is_good() { - let mut cell = Cell::new(if *out_fmt == OutputFormat::Markdown { - ":ok:" - } else { - "OK" - }); - cell.style(Attr::ForegroundColor(color::BRIGHT_GREEN)); - cell - } else { - let mut cell = Cell::new(if *out_fmt == OutputFormat::Markdown { - ":x:" - } else { - "FAIL" - }); - cell.style(Attr::ForegroundColor(color::BRIGHT_RED)); - cell + fn result_cell(&self, result: &ComponentBinVerifyResult, out_fmt: &OutputFormat) -> Cell { + return match result { + ComponentBinVerifyResult::Ok { .. } => { + let mut cell = Cell::new(if *out_fmt == OutputFormat::Markdown { + ":ok:" + } else { + "OK" + }); + cell.style(Attr::ForegroundColor(color::BRIGHT_GREEN)); + cell + } + ComponentBinVerifyResult::Skipped { .. } => { + Cell::new(if *out_fmt == OutputFormat::Markdown { + ":zero:" + } else { + "SKIP" + }) + } + ComponentBinVerifyResult::Failed(_) => { + let mut cell = Cell::new(if *out_fmt == OutputFormat::Markdown { + ":x:" + } else { + "FAIL" + }); + cell.style(Attr::ForegroundColor(color::BRIGHT_RED)); + cell + } }; } @@ -46,7 +56,23 @@ pub trait PrintTable { fn print_table(&mut self, table: &Table) -> Result<(), Error>; } -pub trait ReportOutput: PrintTable + Write {} +pub trait ReportOutput: PrintTable + Write { + fn h2(&mut self, heading: &str) -> Result<(), Error> { + return self.write_fmt(format_args!("## {heading}\n\n")); + } + + fn h3(&mut self, heading: &str) -> Result<(), Error> { + return self.write_fmt(format_args!("### {heading}\n\n")); + } + + fn h4(&mut self, heading: &str) -> Result<(), Error> { + return self.write_fmt(format_args!("#### {heading}\n\n")); + } + + fn h5(&mut self, heading: &str) -> Result<(), Error> { + return self.write_fmt(format_args!("##### {heading}\n\n")); + } +} impl PrintTable for Stdout { fn print_table(&mut self, table: &Table) -> Result<(), Error> {