Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 15 additions & 4 deletions libwild/src/args.rs
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,9 @@ pub struct Args {
pub(crate) undefined: Vec<String>,
pub(crate) relro: bool,
pub(crate) entry: Option<String>,
pub(crate) explicitly_export_dynamic: bool,
pub(crate) export_all_dynamic_symbols: bool,
pub(crate) export_list: Vec<String>,
pub(crate) export_list_path: Option<PathBuf>,

/// If set, GC stats will be written to the specified filename.
pub(crate) write_gc_stats: Option<PathBuf>,
Expand Down Expand Up @@ -299,7 +301,9 @@ impl Default for Args {
relro: true,
entry: None,
b_symbolic: BSymbolicKind::None,
explicitly_export_dynamic: false,
export_all_dynamic_symbols: false,
export_list: Vec::new(),
export_list_path: None,
got_plt_syms: false,
relax: true,
jobserver_client: None,
Expand Down Expand Up @@ -594,9 +598,16 @@ pub(crate) fn parse<F: Fn() -> I, S: AsRef<str>, I: Iterator<Item = S>>(input: F
} else if long_arg_eq("shared") || long_arg_eq("Bshareable") {
args.output_kind = Some(OutputKind::SharedObject);
} else if long_arg_eq("export-dynamic") || arg == "-E" {
args.explicitly_export_dynamic = true;
args.export_all_dynamic_symbols = true;
} else if long_arg_eq("no-export-dynamic") {
args.explicitly_export_dynamic = false;
args.export_all_dynamic_symbols = false;
} else if let Some(value) = get_option_value("export-dynamic-symbol") {
args.export_list.push(value);
} else if let Some(value) = get_option_value("export-dynamic-symbol-list") {
args.export_list_path = Some(PathBuf::from(&value));
} else if let Some(value) = get_option_value("dynamic-list") {
args.b_symbolic = BSymbolicKind::All;
args.export_list_path = Some(PathBuf::from(&value));
} else if let Some(value) = get_option_value("soname") {
args.soname = Some(value);
} else if arg == "-h" {
Expand Down
142 changes: 142 additions & 0 deletions libwild/src/export_list.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
use crate::error;
use crate::error::Result;
use crate::hash::PreHashed;
use crate::input_data::ScriptData;
use crate::linker_script::skip_comments_and_whitespace;
use crate::symbol::UnversionedSymbolName;
use crate::version_script::MatchRules;
use crate::version_script::SymbolLookupNameWrapper;
use crate::version_script::parse_matcher;
use winnow::BStr;
use winnow::Parser;

#[derive(Default)]
pub(crate) struct ExportList<'data>(MatchRules<'data>);

impl<'data> ExportList<'data> {
pub(crate) fn parse(data: ScriptData<'data>) -> Result<Self> {
parse_export_list
.parse(BStr::new(data.raw))
.map_err(|err| error!("Failed to parse symbol export list:\n{err}"))
}

// Based on Version Script counterpart
pub(crate) fn contains(&self, name: &PreHashed<UnversionedSymbolName>) -> bool {
let mut lookup_name = SymbolLookupNameWrapper::from_name(name);

if self.0.general.matches_exact(&mut lookup_name, false)
|| self.0.cxx.matches_exact(&mut lookup_name, true)
{
return true;
}

for &non_star in &[true, false] {
if self
.0
.general
.matches_glob(&mut lookup_name, non_star, false)
|| self.0.cxx.matches_glob(&mut lookup_name, non_star, true)
{
return true;
}
}

if self.0.general.matches_all() || self.0.cxx.matches_all() {
return true;
}

false
}

pub(crate) fn add_symbol(&mut self, symbol: &'data str, without_semicolon: bool) -> Result<()> {
let matcher = parse_matcher(&mut BStr::new(symbol), without_semicolon)?;
self.0.push(matcher);
Ok(())
}
}

fn parse_export_list<'input>(input: &mut &'input BStr) -> winnow::Result<ExportList<'input>> {
let mut out = ExportList::default();

skip_comments_and_whitespace(input)?;

'{'.parse_next(input)?;

loop {
skip_comments_and_whitespace(input)?;

if input.starts_with(b"};") {
"};".parse_next(input)?;
skip_comments_and_whitespace(input)?;
break;
}

let matcher = parse_matcher(input, false)?;
out.0.push(matcher);
}

Ok(out)
}

#[cfg(test)]
mod tests {
use super::*;
use crate::input_data::ScriptData;

#[test]
fn parse_inline() {
let data = ScriptData {
raw: b"{ f*; \"bar\"; extern \"C++\" { baz; qux; }; };",
};
let export_list = ExportList::parse(data).unwrap();
assert!(export_list.contains(&UnversionedSymbolName::prehashed(b"foo")));
assert!(export_list.contains(&UnversionedSymbolName::prehashed(b"bar")));
assert!(export_list.contains(&UnversionedSymbolName::prehashed(b"baz")));
assert!(export_list.contains(&UnversionedSymbolName::prehashed(b"qux")));
assert!(!export_list.contains(&UnversionedSymbolName::prehashed(b"not_exported")));
}

#[test]
fn parse_multiline_with_comments() {
let data = ScriptData {
raw: b"{
# Single line comment
foo;
\"bar\"; # With a quote

/*
* And a C-style comment
*/
baz*;

extern \"C++\" {
qux; # C++ symbol
};
};",
};
let export_list = ExportList::parse(data).unwrap();
assert!(export_list.contains(&UnversionedSymbolName::prehashed(b"foo")));
assert!(export_list.contains(&UnversionedSymbolName::prehashed(b"bar")));
assert!(export_list.contains(&UnversionedSymbolName::prehashed(b"baz-test")));
assert!(export_list.contains(&UnversionedSymbolName::prehashed(b"qux")));
assert!(!export_list.contains(&UnversionedSymbolName::prehashed(b"not_exported")));
}

#[test]
fn externs() {
let data = ScriptData {
raw: b"{
extern \"C\" {
foo;
};
extern \"C++\" {
bar;
};
};",
};
let export_list = ExportList::parse(data).unwrap();
assert!(export_list.contains(&UnversionedSymbolName::prehashed(b"foo")));
assert!(export_list.contains(&UnversionedSymbolName::prehashed(b"bar")));
assert!(!export_list.contains(&UnversionedSymbolName::prehashed(b"not_exported")));
}
}
19 changes: 13 additions & 6 deletions libwild/src/input_data.rs
Original file line number Diff line number Diff line change
Expand Up @@ -38,8 +38,9 @@ pub(crate) struct InputData<'data> {
/// This is like `files`, but archives have been split into their separate parts.
pub(crate) inputs: Vec<InputBytes<'data>>,

pub(crate) version_script_data: Option<VersionScriptData<'data>>,
pub(crate) version_script_data: Option<ScriptData<'data>>,
pub(crate) linker_scripts: Vec<InputLinkerScript<'data>>,
pub(crate) export_list_data: Option<ScriptData<'data>>,

/// Which files we loaded. The keys aren't relevant anymore, but we keep them to avoid
/// regenerating the map.
Expand All @@ -54,7 +55,7 @@ pub(crate) struct InputBytes<'data> {
}

#[derive(Clone, Copy)]
pub(crate) struct VersionScriptData<'data> {
pub(crate) struct ScriptData<'data> {
pub(crate) raw: &'data [u8],
}

Expand Down Expand Up @@ -189,7 +190,12 @@ impl<'data> InputData<'data> {
let version_script_data = args
.version_script_path
.as_ref()
.map(|path| read_version_script(path, inputs_arena))
.map(|path| read_script_data(path, inputs_arena))
.transpose()?;
let export_list_data = args
.export_list_path
.as_ref()
.map(|path| read_script_data(path, inputs_arena))
.transpose()?;

let (work_sender, work_recv) = crossbeam_channel::unbounded();
Expand Down Expand Up @@ -238,6 +244,7 @@ impl<'data> InputData<'data> {
inputs: Vec::new(),
linker_scripts: Vec::new(),
version_script_data,
export_list_data,
path_to_load_index: temporary_state.path_to_load_index,
};

Expand Down Expand Up @@ -595,10 +602,10 @@ impl<'data, 'ch> TemporaryState<'data, 'ch> {
}
}

fn read_version_script<'data>(
fn read_script_data<'data>(
path: &Path,
inputs_arena: &'data Arena<InputFile>,
) -> Result<VersionScriptData<'data>> {
) -> Result<ScriptData<'data>> {
let data = FileData::new(path, false)?;

let file = inputs_arena.alloc(InputFile {
Expand All @@ -609,7 +616,7 @@ fn read_version_script<'data>(
data: Some(data),
});

Ok(VersionScriptData { raw: file.data() })
Ok(ScriptData { raw: file.data() })
}

impl Input {
Expand Down
22 changes: 17 additions & 5 deletions libwild/src/layout.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4060,12 +4060,14 @@ impl<'data> ObjectLayoutState<'data> {
.context("Cannot parse .riscv.attributes section")?;
}

if resources.symbol_db.args.output_kind() == OutputKind::SharedObject
let export_all_dynamic = resources.symbol_db.args.output_kind() == OutputKind::SharedObject
&& (!resources.symbol_db.args.exclude_libs || !self.input.has_archive_semantics())
|| resources.symbol_db.args.needs_dynsym()
&& resources.symbol_db.args.explicitly_export_dynamic
&& resources.symbol_db.args.export_all_dynamic_symbols;
if export_all_dynamic
|| resources.symbol_db.args.needs_dynsym() && resources.symbol_db.export_list.is_some()
{
self.load_non_hidden_symbols::<A>(common, resources, queue)?;
self.load_non_hidden_symbols::<A>(common, resources, queue, export_all_dynamic)?;
}

Ok(())
Expand Down Expand Up @@ -4512,11 +4514,12 @@ impl<'data> ObjectLayoutState<'data> {
common: &mut CommonGroupState<'data>,
resources: &GraphResources<'data, 'scope>,
queue: &mut LocalWorkQueue,
export_all_dynamic: bool,
) -> Result {
for (sym_index, sym) in self.object.symbols.enumerate() {
let symbol_id = self.symbol_id_range().input_to_id(sym_index);

if !can_export_symbol(sym, symbol_id, resources.symbol_db) {
if !can_export_symbol(sym, symbol_id, resources.symbol_db, export_all_dynamic) {
continue;
}

Expand Down Expand Up @@ -4550,7 +4553,7 @@ impl<'data> ObjectLayoutState<'data> {
// regular object, then the shared object might send us a request to export the definition
// provided by the regular object. This isn't always possible, since the symbol might be
// hidden.
if !can_export_symbol(sym, symbol_id, resources.symbol_db) {
if !can_export_symbol(sym, symbol_id, resources.symbol_db, true) {
return Ok(());
}

Expand Down Expand Up @@ -4627,6 +4630,7 @@ fn can_export_symbol(
sym: &crate::elf::SymtabEntry,
symbol_id: SymbolId,
symbol_db: &SymbolDb,
export_all_dynamic: bool,
) -> bool {
if sym.is_undefined(LittleEndian) || sym.is_local() {
return false;
Expand All @@ -4648,6 +4652,14 @@ fn can_export_symbol(
return false;
}

if !export_all_dynamic
&& let Some(export_list) = &symbol_db.export_list
&& let Ok(symbol_name) = symbol_db.symbol_name(symbol_id)
&& !&export_list.contains(&UnversionedSymbolName::prehashed(symbol_name.bytes()))
{
return false;
}

true
}

Expand Down
2 changes: 2 additions & 0 deletions libwild/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ pub(crate) mod dwarf_address_info;
pub(crate) mod elf;
pub(crate) mod elf_writer;
pub mod error;
pub(crate) mod export_list;
pub(crate) mod file_kind;
pub(crate) mod file_writer;
pub(crate) mod fs;
Expand Down Expand Up @@ -203,6 +204,7 @@ impl Linker {
args,
&input_data.linker_scripts,
&self.herd,
input_data.export_list_data,
)?;

let resolved = resolution::resolve_symbols_and_sections(
Expand Down
Loading