Skip to content

Commit

Permalink
Merge pull request #3 from EliseChouleur/keep-or-remove
Browse files Browse the repository at this point in the history
Add capability to choose between keep or remove
  • Loading branch information
tux3 authored Jan 12, 2023
2 parents 5364643 + 6860890 commit f99a854
Show file tree
Hide file tree
Showing 6 changed files with 114 additions and 52 deletions.
12 changes: 10 additions & 2 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,12 @@ pub struct ArMerger {
builder: Box<dyn ArBuilder>,
}

#[derive(PartialEq, Eq, Copy, Clone)]
pub enum ArmergeKeepOrRemove {
KeepSymbols,
RemoveSymbols,
}

impl ArMerger {
/// Open and extract the contents of the input static libraries
pub fn new<I: IntoParallelIterator<Item = InputLibrary<R>>, R: Read, O: AsRef<Path>>(
Expand Down Expand Up @@ -95,13 +101,15 @@ impl ArMerger {
/// `keep_symbols_regexes` contains the regex name pattern for public symbols to keep exported
pub fn merge_and_localize<Iter: IntoIterator<Item = Regex>>(
self,
keep_symbols_regexes: Iter,
keep_or_remove: ArmergeKeepOrRemove,
symbols_regexes: Iter,
) -> Result<(), MergeError> {
objects::merge(
self.builder,
self.extracted.contents_type,
self.extracted.object_dir,
keep_symbols_regexes.into_iter().collect(),
keep_or_remove,
symbols_regexes.into_iter().collect(),
)
}
}
42 changes: 30 additions & 12 deletions src/main.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use armerge::ArMerger;
use armerge::{ArmergeKeepOrRemove, ArMerger};
use regex::Regex;
use std::error::Error;
use std::path::PathBuf;
Expand All @@ -14,6 +14,10 @@ struct Opt {
#[structopt(short, long, number_of_values = 1)]
keep_symbols: Vec<String>,

/// Accepts regexes of the symbol names to hide, and keep the rest global
#[structopt(short, long, number_of_values = 1)]
remove_symbols: Vec<String>,

/// Output static library
#[structopt(short, long, parse(from_os_str))]
output: PathBuf,
Expand Down Expand Up @@ -59,17 +63,31 @@ fn err_main(opt: Opt) -> Result<(), Box<dyn Error>> {

let merger = ArMerger::new_from_paths(&opt.inputs, &opt.output)?;

if opt.keep_symbols.is_empty() {
// If we don't need to localize any symbols, this is the easy case where we just extract
// contents and re-pack them, no linker necessary.
merger.merge_simple()?;
} else {
let keep_symbols: Vec<Regex> = opt
.keep_symbols
.into_iter()
.map(|s| Regex::new(&s))
.collect::<Result<Vec<_>, _>>()?;
merger.merge_and_localize(keep_symbols)?;
match (opt.keep_symbols.is_empty(), opt.remove_symbols.is_empty()) {
(true, true) => {
// If we don't need to localize any symbols, this is the easy case where we just extract
// contents and re-pack them, no linker necessary.
merger.merge_simple()?;
},
(false, true) => {
let keep_symbols: Vec<Regex> = opt
.keep_symbols
.into_iter()
.map(|s| Regex::new(&s))
.collect::<Result<Vec<_>, _>>()?;
merger.merge_and_localize(ArmergeKeepOrRemove::KeepSymbols, keep_symbols)?;
},
(true, false) => {
let remove_symbols: Vec<Regex> = opt
.remove_symbols
.into_iter()
.map(|s| Regex::new(&s))
.collect::<Result<Vec<_>, _>>()?;
merger.merge_and_localize(ArmergeKeepOrRemove::RemoveSymbols, remove_symbols)?;
},
(false, false) => {
return Err("Can't have both keep-symbols and remove-symbols options at the same time".to_string().into());
}
}

Ok(())
Expand Down
46 changes: 30 additions & 16 deletions src/objects.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ mod system_filter;

use crate::arbuilder::ArBuilder;
use crate::objects::syms::ObjectSyms;
use crate::{ArchiveContents, MergeError};
use crate::{ArchiveContents, ArmergeKeepOrRemove, MergeError};
use regex::Regex;
use std::collections::HashMap;
use std::path::{Path, PathBuf};
Expand All @@ -24,18 +24,25 @@ pub fn merge_required_objects(
obj_dir: &Path,
merged_path: &Path,
objs: &HashMap<PathBuf, ObjectSyms>,
keeps: &[Regex],
keep_or_remove: ArmergeKeepOrRemove,
regexes: &[Regex],
) -> Result<(), MergeError> {
#[allow(clippy::if_same_then_else)] // Clippy can't see both [cfg] at once
if contents_type == ArchiveContents::Elf {
#[cfg(feature = "objpoke_symbols")]
builtin_filter::merge_required_objects(obj_dir, merged_path, objs, keeps)?;
builtin_filter::merge_required_objects(obj_dir, merged_path, objs, keep_or_remove, keeps)?;
#[cfg(not(feature = "objpoke_symbols"))]
system_filter::merge_required_objects(obj_dir, merged_path, objs, keeps)?;
system_filter::merge_required_objects(obj_dir, merged_path, objs, keep_or_remove, regexes)?;
} else if contents_type == ArchiveContents::MachO {
system_filter::merge_required_macho_objects(obj_dir, merged_path, objs, keeps)?;
system_filter::merge_required_macho_objects(
obj_dir,
merged_path,
objs,
keep_or_remove,
regexes,
)?;
} else {
system_filter::merge_required_objects(obj_dir, merged_path, objs, keeps)?;
system_filter::merge_required_objects(obj_dir, merged_path, objs, keep_or_remove, regexes)?;
}
Ok(())
}
Expand All @@ -44,33 +51,40 @@ pub fn merge(
mut output: Box<dyn ArBuilder>,
contents_type: ArchiveContents,
objects: ObjectTempDir,
mut keep_regexes: Vec<Regex>,
keep_or_remove: ArmergeKeepOrRemove,
mut regexes: Vec<Regex>,
) -> Result<(), MergeError> {
let merged_name = "merged.o";
let mut merged_path = objects.dir.path().to_owned();
merged_path.push(merged_name);

// When filtering symbols to keep just the public API visible,
// we must make an exception for the unwind symbols (if linked statically)
keep_regexes.push(Regex::new("^_?_Unwind_.*").expect("Failed to compile Regex"));
if keep_or_remove == ArmergeKeepOrRemove::KeepSymbols {
// When filtering symbols to keep just the public API visible,
// we must make an exception for the unwind symbols (if linked statically)
regexes.push(Regex::new("^_?_Unwind_.*").expect("Failed to compile Regex"));
}

let required_objects = filter_deps::filter_required_objects(&objects.objects, &keep_regexes)?;
let required_objects =
filter_deps::filter_required_objects(&objects.objects, keep_or_remove, &regexes)?;

if required_objects.is_empty() {
return Err(MergeError::NoObjectsLeft);
}

// When filtering symbols to keep just the public API visible,
// we must make an exception for the unwind symbols (if linked statically)
// However, some symbols are not indicative of the fact that we need to keep an object file
keep_regexes.push(Regex::new("_?__g.._personality_.*").expect("Failed to compile Regex"));
if keep_or_remove == ArmergeKeepOrRemove::KeepSymbols {
// When filtering symbols to keep just the public API visible,
// we must make an exception for the unwind symbols (if linked statically)
// However, some symbols are not indicative of the fact that we need to keep an object file
regexes.push(Regex::new("_?__g.._personality_.*").expect("Failed to compile Regex"));
}

merge_required_objects(
contents_type,
objects.dir.path(),
&merged_path,
&required_objects,
&keep_regexes,
keep_or_remove,
&regexes,
)?;

output.append_obj(&merged_path)?;
Expand Down
7 changes: 4 additions & 3 deletions src/objects/filter_deps.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use std::collections::{HashMap, HashSet};
use std::path::PathBuf;

use crate::MergeError;
use crate::{ArmergeKeepOrRemove, MergeError};
use rayon::iter::IntoParallelIterator;
use rayon::prelude::*;
use regex::Regex;
Expand All @@ -23,14 +23,15 @@ fn add_deps_recursive(

pub fn filter_required_objects(
objects: &[PathBuf],
keep_regexes: &[Regex],
keep_or_remove: ArmergeKeepOrRemove,
regexes: &[Regex],
) -> Result<HashMap<PathBuf, ObjectSyms>, MergeError> {
let mut object_syms = objects
.into_par_iter()
.map(|obj_path| {
Ok::<_, MergeError>((
obj_path.to_owned(),
ObjectSyms::new(obj_path, keep_regexes)?,
ObjectSyms::new(obj_path, keep_or_remove, regexes)?,
))
})
.collect::<Result<HashMap<PathBuf, ObjectSyms>, _>>()?;
Expand Down
17 changes: 13 additions & 4 deletions src/objects/syms.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use crate::MergeError;
use crate::{ArmergeKeepOrRemove, MergeError};
use object::{Object, ObjectSymbol, SymbolKind};
use rayon::prelude::*;
use regex::Regex;
Expand All @@ -14,7 +14,11 @@ pub struct ObjectSyms {
}

impl ObjectSyms {
pub fn new(object_path: &Path, keep_regexes: &[Regex]) -> Result<Self, MergeError> {
pub fn new(
object_path: &Path,
keep_or_remove: ArmergeKeepOrRemove,
regexes: &[Regex],
) -> Result<Self, MergeError> {
let mut globals = HashSet::new();
let mut undefineds = HashSet::new();
let mut kept_syms_list = String::new();
Expand Down Expand Up @@ -46,8 +50,13 @@ impl ObjectSyms {
}

if let Ok(name) = sym.name() {
for regex in keep_regexes {
if regex.is_match(name) {
for regex in regexes {
let keep_sym_condition = if keep_or_remove == ArmergeKeepOrRemove::KeepSymbols {
regex.is_match(name)
} else {
!regex.is_match(name)
};
if keep_sym_condition {
has_exported_symbols = true;
kept_syms_list += name;
kept_syms_list.push('\n');
Expand Down
42 changes: 27 additions & 15 deletions src/objects/system_filter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ use std::str::FromStr;

use crate::objects::merge::create_merged_object;
use crate::objects::syms::ObjectSyms;
use crate::MergeError;
use crate::{ArmergeKeepOrRemove, MergeError};
use object::{Object, ObjectSymbol, SymbolKind};
use regex::Regex;
use std::fs::File;
Expand Down Expand Up @@ -41,7 +41,8 @@ fn create_filtered_merged_macho_object(
pub fn create_symbol_filter_list(
object_dir: &Path,
objects: impl IntoIterator<Item = impl AsRef<Path>>,
keep_regexes: &[Regex],
keep_or_remove: ArmergeKeepOrRemove,
regexes: &[Regex],
) -> Result<PathBuf, MergeError> {
let filter_path = object_dir.join("localize.syms");
let mut filter_syms = HashSet::new();
Expand All @@ -55,23 +56,32 @@ pub fn create_symbol_filter_list(
inner: e,
})?;
'next_symbol: for sym in file.symbols() {
if !sym.is_global()
|| sym.is_undefined()
|| (sym.kind() != SymbolKind::Text
&& sym.kind() != SymbolKind::Data
&& sym.kind() != SymbolKind::Unknown/* ASM functions often end up unknown */)
if keep_or_remove == ArmergeKeepOrRemove::KeepSymbols
&& (!sym.is_global()
|| sym.is_undefined()
|| (sym.kind() != SymbolKind::Text
&& sym.kind() != SymbolKind::Data
&& sym.kind() != SymbolKind::Unknown/* ASM functions often end up unknown */))
{
continue;
}
if let Ok(name) = sym.name() {
for regex in keep_regexes {
for regex in regexes {
if regex.is_match(name) {
kept_count += 1;
if keep_or_remove == ArmergeKeepOrRemove::KeepSymbols {
kept_count += 1;
} else {
filter_syms.insert(name.to_owned());
}
continue 'next_symbol;
}
}

filter_syms.insert(name.to_owned());
if keep_or_remove == ArmergeKeepOrRemove::KeepSymbols {
filter_syms.insert(name.to_owned());
} else {
kept_count += 1;
}
}
}
}
Expand Down Expand Up @@ -135,25 +145,27 @@ pub fn merge_required_macho_objects(
obj_dir: &Path,
merged_path: &Path,
objects: &HashMap<PathBuf, ObjectSyms>,
keep_regexes: &[Regex],
keep_or_remove: ArmergeKeepOrRemove,
regexes: &[Regex],
) -> Result<(), MergeError> {
let filter_path = create_symbol_filter_list(obj_dir, objects.keys(), keep_regexes)?;
let filter_path = create_symbol_filter_list(obj_dir, objects.keys(), keep_or_remove, regexes)?;
create_filtered_merged_macho_object(merged_path, objects.keys(), &filter_path)
}

pub fn merge_required_objects(
obj_dir: &Path,
merged_path: &Path,
objects: &HashMap<PathBuf, ObjectSyms>,
keep_regexes: &[Regex],
keep_or_remove: ArmergeKeepOrRemove,
regexes: &[Regex],
) -> Result<(), MergeError> {
let filter_path = create_symbol_filter_list(obj_dir, objects.keys(), keep_regexes)?;
let filter_path = create_symbol_filter_list(obj_dir, objects.keys(), keep_or_remove, regexes)?;
create_filtered_merged_object(merged_path, objects.keys(), &filter_path)?;

// If a symbol we localize is in a COMDAT section group, we also want to turn it into a regular
// section group. Otherwise the local symbol is not really local, because the containing section
// could later get COMDAT-folded with other (potentially incompatible) object files.
demote_elf_comdats(merged_path, keep_regexes)
demote_elf_comdats(merged_path, regexes)
}

fn demote_elf_comdats(merged_path: &Path, keep_regexes: &[Regex]) -> Result<(), MergeError> {
Expand Down

0 comments on commit f99a854

Please sign in to comment.