From 4a69b58479ada9e7f59d6a01c800ce5357df1d34 Mon Sep 17 00:00:00 2001 From: jianfengjiang Date: Wed, 22 Sep 2021 19:32:56 +0800 Subject: [PATCH] Infer default dynamic loader --- tools/copy_bom/src/bom.rs | 50 ++++++++++++++++++++----------- tools/copy_bom/src/util.rs | 61 +++++++++++++++++++++++++++++++++----- 2 files changed, 86 insertions(+), 25 deletions(-) diff --git a/tools/copy_bom/src/bom.rs b/tools/copy_bom/src/bom.rs index 687b900a..2e8d3143 100644 --- a/tools/copy_bom/src/bom.rs +++ b/tools/copy_bom/src/bom.rs @@ -8,7 +8,8 @@ use crate::error::{FILE_NOT_EXISTS_ERROR, INVALID_BOM_FILE_ERROR}; use crate::util::{ check_file_hash, copy_dir, copy_file, copy_shared_object, create_link, dest_in_root, - find_dependent_shared_objects, find_included_bom_file, mkdir, resolve_envs, + find_dependent_shared_objects, find_included_bom_file, infer_default_loader, mkdir, + resolve_envs, }; use serde::{Deserialize, Serialize}; use serde_yaml; @@ -136,12 +137,14 @@ impl Bom { fn get_bom_management(self, root_dir: &str) -> BomManagement { let mut bom_management = BomManagement::default(); bom_management.dirs_to_make.push(root_dir.to_string()); // init root dir + let mut target_managements = Vec::new(); if let Some(ref targets) = self.targets { for target in targets { let target_management = target.get_target_management(root_dir); - bom_management.add_target_management(target_management, root_dir); + target_managements.push(target_management); } } + bom_management.add_target_managements(target_managements, root_dir); bom_management } @@ -353,26 +356,37 @@ impl NormalFile { } impl BomManagement { - fn add_target_management(&mut self, mut target_management: TargetManagement, root_dir: &str) { - // First, we need to resolve environmental variables - target_management.resolve_environmental_variables(); - let TargetManagement { - dirs_to_make, - links_to_create, - dirs_to_copy, - files_to_copy, - files_autodep, - } = target_management; - self.dirs_to_make.extend(dirs_to_make.into_iter()); - self.links_to_create.extend(links_to_create.into_iter()); - self.dirs_to_copy.extend(dirs_to_copy.into_iter()); - self.files_to_copy.extend(files_to_copy.into_iter()); - self.autodep(files_autodep, root_dir); + fn add_target_managements( + &mut self, + target_managements: Vec, + root_dir: &str, + ) { + let mut files_autodep_in_bom = Vec::new(); + for mut target_management in target_managements.into_iter() { + // First, we need to resolve environmental variables + target_management.resolve_environmental_variables(); + let TargetManagement { + dirs_to_make, + links_to_create, + dirs_to_copy, + files_to_copy, + files_autodep, + } = target_management; + self.dirs_to_make.extend(dirs_to_make.into_iter()); + self.links_to_create.extend(links_to_create.into_iter()); + self.dirs_to_copy.extend(dirs_to_copy.into_iter()); + self.files_to_copy.extend(files_to_copy.into_iter()); + files_autodep_in_bom.extend(files_autodep.into_iter()); + } + + self.autodep(files_autodep_in_bom, root_dir); } fn autodep(&mut self, files_autodep: Vec, root_dir: &str) { + let default_loader = infer_default_loader(&files_autodep); + debug!("default loader in autodep: {:?}", default_loader); for file_autodep in files_autodep.iter() { - let mut shared_objects = find_dependent_shared_objects(file_autodep); + let mut shared_objects = find_dependent_shared_objects(file_autodep, &default_loader); for (src, dest) in shared_objects.drain() { let dest_path = dest_in_root(root_dir, &dest); // First, we create dir to store the dependency diff --git a/tools/copy_bom/src/util.rs b/tools/copy_bom/src/util.rs index 20555340..e9d1267f 100644 --- a/tools/copy_bom/src/util.rs +++ b/tools/copy_bom/src/util.rs @@ -196,11 +196,14 @@ pub fn calculate_file_hash(filename: &str) -> String { /// and analyze the stdout. We use regex to match the pattern of the loader output. /// The loader will automatically find all dependencies recursively, i.e., it will also find dependencies /// for each shared object, so we only need to analyze the top elf file. -pub fn find_dependent_shared_objects(file_path: &str) -> HashSet<(String, String)> { +pub fn find_dependent_shared_objects( + file_path: &str, + default_loader: &Option<(String, String)>, +) -> HashSet<(String, String)> { let mut shared_objects = HashSet::new(); // find dependencies for the input file // first, we find the dynamic loader for the elf file, if we can't find the loader, return empty shared objects - let dynamic_loader = auto_dynamic_loader(file_path); + let dynamic_loader = auto_dynamic_loader(file_path, default_loader); if dynamic_loader.is_none() { return shared_objects; } @@ -260,11 +263,37 @@ fn command_output_of_executing_dynamic_loader( } } -/// This function will try to find a dynamic loader for a elf file automatically -/// If we find the loader, we will return Some((loader_src, loader_dest)). -/// This is because the loader_src and loader_dest may not be the same directory. -/// If we can't find the loader, this function will return None -fn auto_dynamic_loader(filename: &str) -> Option<(String, String)> { +/// This function will try to find a dynamic loader for a elf file automatically. +/// If will first try to read the interp section of elf file. If the file does not have interp section, +/// and the default loader is *NOT* None, it will return default loader. +/// It there is no interp section and default loader is None, it will return None. +/// If we find the loader, we will return Some((occlum_elf_loader, inlined_elf_loader)). +/// This is because the occlum_elf_loader and inlined_elf_loader may not be the same directory. +fn auto_dynamic_loader( + filename: &str, + default_loader: &Option<(String, String)>, +) -> Option<(String, String)> { + let elf_file = match elf::File::open_path(filename) { + Err(_) => return None, + Ok(elf_file) => elf_file, + }; + match elf_file.get_section(".interp") { + None => { + // When the elf file does not has interp section + // 1. if we have default loader, we will return the default loader + // 2. Otherwise we will return None and give warning. + if let Some(default_loader) = default_loader { + return Some(default_loader.clone()); + } else { + warn!("cannot autodep for file {}. ", filename); + return None; + } + } + Some(_) => read_loader_from_interp_section(filename), + } +} + +fn read_loader_from_interp_section(filename: &str) -> Option<(String, String)> { let elf_file = match elf::File::open_path(filename) { Err(_) => return None, Ok(elf_file) => elf_file, @@ -293,6 +322,24 @@ fn auto_dynamic_loader(filename: &str) -> Option<(String, String)> { )) } +// try to infer default loader for all files to autodep +// If all files with .interp section points to the same loader, +// this loader will be viewed as the default loader +// Otherwise, no default loader can be found. +pub fn infer_default_loader(files_autodep: &Vec) -> Option<(String, String)> { + let mut loaders = HashSet::new(); + for filename in files_autodep.iter() { + if let Some(loader) = read_loader_from_interp_section(filename) { + loaders.insert(loader); + } + } + if loaders.len() == 1 { + return loaders.into_iter().next(); + } else { + return None; + } +} + /// resolve the results of dynamic loader to extract dependencies pub fn extract_dependencies_from_output( file_path: &str,