libwild/
lib.rs

1use crate::args::parse;
2use args::Args;
3use tracing_subscriber::EnvFilter;
4use tracing_subscriber::fmt;
5use tracing_subscriber::layer::SubscriberExt;
6use tracing_subscriber::util::SubscriberInitExt;
7
8pub(crate) mod aarch64;
9pub(crate) mod alignment;
10pub(crate) mod arch;
11pub(crate) mod archive;
12pub(crate) mod archive_splitter;
13pub mod args;
14pub(crate) mod debug_trace;
15pub(crate) mod diff;
16pub(crate) mod elf;
17pub(crate) mod elf_writer;
18pub mod error;
19pub(crate) mod file_kind;
20pub(crate) mod fs;
21pub(crate) mod gc_stats;
22pub(crate) mod grouping;
23pub(crate) mod hash;
24pub(crate) mod identity;
25pub(crate) mod input_data;
26pub(crate) mod layout;
27pub(crate) mod linker_script;
28pub(crate) mod output_section_id;
29pub(crate) mod output_section_map;
30pub(crate) mod output_section_part_map;
31pub(crate) mod output_trace;
32pub(crate) mod parsing;
33pub(crate) mod part_id;
34pub(crate) mod program_segments;
35pub(crate) mod resolution;
36pub(crate) mod save_dir;
37pub(crate) mod sharding;
38pub(crate) mod shutdown;
39pub(crate) mod slice;
40pub(crate) mod storage;
41pub(crate) mod string_merging;
42#[cfg(feature = "fork")]
43pub(crate) mod subprocess;
44#[cfg(not(feature = "fork"))]
45#[path = "subprocess_unsupported.rs"]
46pub(crate) mod subprocess;
47pub(crate) mod symbol;
48pub(crate) mod symbol_db;
49pub(crate) mod timing;
50pub(crate) mod validation;
51pub(crate) mod verification;
52pub(crate) mod x86_64;
53
54pub use subprocess::run_in_subprocess;
55
56pub struct Linker {
57    args: Args,
58}
59
60impl Linker {
61    pub fn from_args<S: AsRef<str>, I: Iterator<Item = S>>(args: I) -> error::Result<Self> {
62        Ok(Linker { args: parse(args)? })
63    }
64
65    pub fn run(&self) -> error::Result {
66        self.run_with_callback(None)
67    }
68
69    /// Runs the linker, calling `done_closure` when linking is complete, but before cleanup is
70    /// performed.
71    pub(crate) fn run_with_callback(
72        &self,
73        done_closure: Option<Box<dyn FnOnce()>>,
74    ) -> error::Result {
75        let args = &self.args;
76        if args.time_phases {
77            timing::init_tracing();
78        } else if args.write_trace {
79            output_trace::init(args);
80        } else if args.print_allocations.is_some() {
81            debug_trace::init();
82        } else {
83            tracing_subscriber::registry()
84                .with(fmt::layer())
85                .with(EnvFilter::from_default_env())
86                .init();
87        }
88        if args.should_print_version {
89            println!(
90                "Wild version {} (compatible with GNU linkers)",
91                env!("CARGO_PKG_VERSION")
92            );
93            if args.inputs.is_empty() {
94                return Ok(());
95            }
96        }
97        match args.arch {
98            arch::Architecture::X86_64 => {
99                link::<storage::InMemory, x86_64::X86_64>(args, done_closure)
100            }
101            arch::Architecture::AArch64 => {
102                link::<storage::InMemory, aarch64::AArch64>(args, done_closure)
103            }
104        }
105    }
106
107    pub fn should_fork(&self) -> bool {
108        self.args.should_fork()
109    }
110}
111
112#[tracing::instrument(skip_all, name = "Link")]
113fn link<S: storage::StorageModel, A: arch::Arch>(
114    args: &Args,
115    done_closure: Option<Box<dyn FnOnce()>>,
116) -> error::Result {
117    args.setup_thread_pool()?;
118    let mut output = elf_writer::Output::new(args);
119    let input_data = input_data::InputData::from_args(args)?;
120    let inputs = archive_splitter::split_archives(&input_data)?;
121    let files = parsing::parse_input_files(&inputs, args)?;
122    let groups = grouping::group_files(files, args);
123    let herd = bumpalo_herd::Herd::new();
124    let mut symbol_db =
125        symbol_db::SymbolDb::<S>::build(&groups, input_data.version_script_data.as_ref(), args)?;
126    let resolved = resolution::resolve_symbols_and_sections(&groups, &mut symbol_db, &herd)?;
127    let layout = layout::compute::<S, A>(&symbol_db, resolved, &mut output)?;
128    let output_file = output.write::<S, A>(&layout)?;
129    diff::maybe_diff()?;
130
131    let scope = tracing::info_span!("Shutdown");
132    let _scope = scope.enter();
133    shutdown::free_output(output_file);
134    // If there is a parent process waiting on this, inform it that linking is done and output ready
135    if let Some(done_callback) = done_closure {
136        done_callback();
137    }
138    shutdown::free_layout(layout);
139    shutdown::free_symbol_db(symbol_db);
140    shutdown::free_input_data(input_data);
141    Ok(())
142}