use std::ffi::OsString;
use std::path::{Path, PathBuf};
use crate::target::{Endianness, Target};
#[non_exhaustive]
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
pub enum Flavor {
#[default]
Gnu,
Darwin,
}
#[non_exhaustive]
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
pub enum OutputKind {
#[default]
Executable,
Pie,
Shared,
StaticExecutable,
StaticPie,
Relocatable,
}
#[non_exhaustive]
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, PartialOrd, Ord)]
pub enum StripMode {
#[default]
None,
Debug,
All,
}
#[non_exhaustive]
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
pub enum DiscardMode {
#[default]
Default,
Locals,
All,
None,
}
#[non_exhaustive]
#[derive(Clone, Debug, Default, PartialEq, Eq)]
pub enum BuildId {
#[default]
None,
Fast,
Sha1,
Md5,
Uuid,
Hex(Vec<u8>),
}
#[non_exhaustive]
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
pub enum HashStyle {
Sysv,
#[default]
Gnu,
Both,
}
#[non_exhaustive]
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
pub enum SymbolicMode {
#[default]
None,
All,
Functions,
NonWeak,
NonWeakFunctions,
}
#[non_exhaustive]
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum UnresolvedSymbols {
IgnoreAll,
ReportAll,
IgnoreInObjectFiles,
IgnoreInSharedLibs,
}
#[non_exhaustive]
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
pub enum ColorChoice {
#[default]
Auto,
Always,
Never,
}
#[non_exhaustive]
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
pub enum MagicMode {
#[default]
Normal,
Nmagic,
Omagic,
}
#[non_exhaustive]
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum SeparateCode {
None,
Code,
Loadable,
}
#[non_exhaustive]
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
pub enum ExecStack {
#[default]
FromInputs,
Executable,
NonExecutable,
}
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
pub enum CallGraphSort {
None,
Hfsort,
#[default]
Cdsort,
}
#[non_exhaustive]
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
pub struct DynamicFlags {
pub nodelete: bool,
pub nodlopen: bool,
pub nodump: bool,
pub initfirst: bool,
pub interpose: bool,
pub global: bool,
pub nodefaultlib: bool,
pub loadfltr: bool,
pub origin: bool,
pub singleton: bool,
pub group: bool,
}
#[non_exhaustive]
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
pub struct X86Features {
pub ibt: bool,
pub shstk: bool,
pub ibtplt: bool,
pub cet_report: ReportLevel,
pub isa_level: u8,
}
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
pub struct Aarch64Features {
pub force_bti: bool,
pub pac_plt: bool,
pub fix_cortex_a53_835769: bool,
}
#[non_exhaustive]
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
pub enum ReportLevel {
#[default]
None,
Warning,
Error,
}
#[non_exhaustive]
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
pub enum IcfMode {
#[default]
None,
Safe,
All,
}
#[non_exhaustive]
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
pub enum OrphanHandling {
#[default]
Place,
Warn,
Error,
Discard,
}
#[non_exhaustive]
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
pub enum SortSection {
#[default]
None,
Name,
Alignment,
}
#[non_exhaustive]
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
pub enum DebugCompression {
#[default]
None,
Zlib,
ZlibGnu,
ZlibGabi,
Zstd,
}
impl DebugCompression {
#[must_use]
pub fn name(self) -> &'static str {
match self {
Self::None => "none",
Self::Zlib => "zlib",
Self::ZlibGnu => "zlib-gnu",
Self::ZlibGabi => "zlib-gabi",
Self::Zstd => "zstd",
}
}
}
#[non_exhaustive]
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
pub enum Visibility {
#[default]
Default,
Internal,
Hidden,
Protected,
}
#[non_exhaustive]
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum OutputFormat {
Binary,
Ihex,
Srec,
Bfd(String),
}
impl OutputFormat {
#[must_use]
pub fn from_name(name: &str) -> Self {
match name {
"binary" => Self::Binary,
"ihex" => Self::Ihex,
"srec" => Self::Srec,
other => Self::Bfd(other.to_owned()),
}
}
#[must_use]
pub fn name(&self) -> &str {
match self {
Self::Binary => "binary",
Self::Ihex => "ihex",
Self::Srec => "srec",
Self::Bfd(name) => name,
}
}
#[must_use]
pub fn is_raw(&self) -> bool {
!matches!(self, Self::Bfd(_))
}
}
#[non_exhaustive]
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash)]
pub enum InputFormat {
#[default]
Auto,
Binary,
}
#[non_exhaustive]
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash)]
pub struct InputAttrs {
pub whole_archive: bool,
pub as_needed: bool,
pub static_only: bool,
pub in_group: bool,
pub copy_dt_needed: bool,
pub lazy: bool,
pub format: InputFormat,
pub load: crate::args::darwin::LoadMode,
}
#[non_exhaustive]
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub enum InputKind {
File(PathBuf),
Library(String),
Framework {
name: String,
suffix: Option<String>,
},
LibraryExact(String),
Script(PathBuf),
JustSymbols(PathBuf),
Bytes {
name: String,
data: std::sync::Arc<[u8]>,
},
}
impl InputKind {
#[must_use]
pub fn bytes(name: impl Into<String>, data: impl Into<std::sync::Arc<[u8]>>) -> Self {
Self::Bytes {
name: name.into(),
data: data.into(),
}
}
}
#[non_exhaustive]
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct InputSpec {
pub kind: InputKind,
pub attrs: InputAttrs,
pub position: usize,
}
impl InputSpec {
#[must_use]
pub fn new(kind: InputKind, attrs: InputAttrs) -> Self {
Self {
kind,
attrs,
position: 0,
}
}
}
#[non_exhaustive]
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct PeArgs {
pub subsystem: Option<u16>,
pub section_alignment: u32,
pub file_alignment: u32,
pub stack: (u64, u64),
pub heap: (u64, u64),
pub major_image_version: u16,
pub minor_image_version: u16,
pub major_os_version: u16,
pub minor_os_version: u16,
pub major_subsystem_version: u16,
pub minor_subsystem_version: u16,
pub dynamicbase: bool,
pub nxcompat: bool,
pub high_entropy_va: bool,
pub tsaware: bool,
pub no_seh: bool,
pub forceinteg: bool,
pub no_isolation: bool,
pub no_bind: bool,
pub wdmdriver: bool,
pub large_address_aware: bool,
pub reloc_section: bool,
pub insert_timestamp: bool,
pub out_implib: Option<PathBuf>,
pub output_def: Option<PathBuf>,
pub def_file: Option<PathBuf>,
pub export_all_symbols: bool,
pub exclude_all_symbols: bool,
pub exclude_symbols: Vec<String>,
pub exclude_modules_for_implib: Vec<String>,
pub kill_at: bool,
pub add_stdcall_alias: bool,
pub stdcall_fixup: Option<bool>,
pub long_section_names: Option<bool>,
pub auto_import: bool,
pub runtime_pseudo_reloc: bool,
pub exports: Vec<String>,
pub warn_duplicate_exports: bool,
pub explicit: PeExplicit,
}
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
pub struct PeExplicit {
pub large_address_aware: bool,
pub os_version: bool,
pub image_version: bool,
pub subsystem_version: bool,
}
impl Default for PeArgs {
fn default() -> Self {
use crate::coff::options as pe;
Self {
subsystem: None,
section_alignment: pe::DEFAULT_SECTION_ALIGNMENT,
file_alignment: pe::DEFAULT_FILE_ALIGNMENT,
stack: (pe::DEFAULT_STACK_RESERVE, pe::DEFAULT_STACK_COMMIT),
heap: (pe::DEFAULT_HEAP_RESERVE, pe::DEFAULT_HEAP_COMMIT),
major_image_version: 0,
minor_image_version: 0,
major_os_version: 4,
minor_os_version: 0,
major_subsystem_version: 5,
minor_subsystem_version: 2,
dynamicbase: true,
nxcompat: true,
high_entropy_va: true,
tsaware: false,
no_seh: false,
forceinteg: false,
no_isolation: false,
no_bind: false,
wdmdriver: false,
large_address_aware: true,
reloc_section: true,
insert_timestamp: false,
out_implib: None,
output_def: None,
def_file: None,
export_all_symbols: false,
exclude_all_symbols: false,
exclude_symbols: Vec::new(),
exclude_modules_for_implib: Vec::new(),
kill_at: false,
add_stdcall_alias: false,
stdcall_fixup: None,
long_section_names: None,
auto_import: true,
runtime_pseudo_reloc: true,
exports: Vec::new(),
warn_duplicate_exports: false,
explicit: PeExplicit::default(),
}
}
}
#[derive(Clone)]
pub struct OutputCompleteHook {
callback: std::sync::Arc<dyn Fn() + Send + Sync>,
called: std::sync::Arc<std::sync::atomic::AtomicBool>,
}
impl OutputCompleteHook {
pub fn new(callback: impl Fn() + Send + Sync + 'static) -> Self {
Self {
callback: std::sync::Arc::new(callback),
called: std::sync::Arc::default(),
}
}
pub fn call(&self) {
if !self.called.swap(true, std::sync::atomic::Ordering::AcqRel) {
(self.callback)();
}
}
}
impl std::fmt::Debug for OutputCompleteHook {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("OutputCompleteHook")
.field(
"called",
&self.called.load(std::sync::atomic::Ordering::Acquire),
)
.finish_non_exhaustive()
}
}
#[derive(Clone, Default)]
pub struct OutputBuffer {
image: std::sync::Arc<std::sync::Mutex<Option<Vec<u8>>>>,
}
impl OutputBuffer {
#[must_use]
pub fn new() -> Self {
Self::default()
}
pub fn store(&self, image: Vec<u8>) {
match self.image.lock() {
Ok(mut slot) => *slot = Some(image),
Err(poisoned) => *poisoned.into_inner() = Some(image),
}
}
#[must_use]
pub fn take(&self) -> Option<Vec<u8>> {
match self.image.lock() {
Ok(mut slot) => slot.take(),
Err(poisoned) => poisoned.into_inner().take(),
}
}
#[must_use]
pub fn is_filled(&self) -> bool {
match self.image.lock() {
Ok(slot) => slot.is_some(),
Err(poisoned) => poisoned.into_inner().is_some(),
}
}
}
impl PartialEq for OutputBuffer {
fn eq(&self, other: &Self) -> bool {
std::sync::Arc::ptr_eq(&self.image, &other.image)
}
}
impl Eq for OutputBuffer {}
impl std::fmt::Debug for OutputBuffer {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("OutputBuffer")
.field("filled", &self.is_filled())
.finish()
}
}
#[derive(Clone, Debug, Default)]
pub struct CancelToken {
cancelled: std::sync::Arc<std::sync::atomic::AtomicBool>,
}
impl PartialEq for CancelToken {
fn eq(&self, other: &Self) -> bool {
std::sync::Arc::ptr_eq(&self.cancelled, &other.cancelled)
}
}
impl Eq for CancelToken {}
impl CancelToken {
#[must_use]
pub fn new() -> Self {
Self::default()
}
pub fn cancel(&self) {
self.cancelled
.store(true, std::sync::atomic::Ordering::Relaxed);
}
#[must_use]
pub fn is_cancelled(&self) -> bool {
self.cancelled.load(std::sync::atomic::Ordering::Relaxed)
}
pub fn check(&self) -> crate::Result<()> {
if self.is_cancelled() {
Err(Self::error())
} else {
Ok(())
}
}
#[must_use]
pub fn error() -> crate::Error {
crate::Error::Cancelled
}
#[must_use]
pub fn is_cancellation(error: &crate::Error) -> bool {
matches!(error, crate::Error::Cancelled)
}
}
#[derive(Clone)]
pub struct TextOutput {
write: std::sync::Arc<dyn Fn(&str) + Send + Sync>,
what: &'static str,
}
impl TextOutput {
#[must_use]
pub fn new(write: impl Fn(&str) + Send + Sync + 'static) -> Self {
Self {
write: std::sync::Arc::new(write),
what: "callback",
}
}
#[must_use]
pub fn stdout() -> Self {
Self {
write: std::sync::Arc::new(|text: &str| {
use std::io::Write as _;
let mut out = std::io::stdout().lock();
let _ = out.write_all(text.as_bytes());
}),
what: "stdout",
}
}
#[must_use]
pub fn stderr() -> Self {
Self {
write: std::sync::Arc::new(|text: &str| {
use std::io::Write as _;
let mut out = std::io::stderr().lock();
let _ = out.write_all(text.as_bytes());
}),
what: "stderr",
}
}
pub fn write(&self, text: &str) {
(self.write)(text);
}
pub fn write_line(&self, text: &str) {
(self.write)(&format!("{text}\n"));
}
}
impl std::fmt::Debug for TextOutput {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_tuple("TextOutput").field(&self.what).finish()
}
}
#[non_exhaustive]
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum OutputBacking {
Mapped,
Written,
Buffered,
}
impl OutputBacking {
#[must_use]
pub fn from_name(name: &str) -> Option<Self> {
match name {
"mmap" | "mapped" => Some(Self::Mapped),
"write" | "written" | "pwrite" => Some(Self::Written),
"memory" | "buffer" | "buffered" => Some(Self::Buffered),
_ => None,
}
}
}
#[non_exhaustive]
#[derive(Clone, Debug)]
pub struct LinkOptions {
pub flavor: Flavor,
pub target: Option<Target>,
pub endian: Option<Endianness>,
pub output: Option<PathBuf>,
pub output_format: Option<OutputFormat>,
pub kind: OutputKind,
pub inputs: Vec<InputSpec>,
pub search_paths: Vec<PathBuf>,
pub nostdlib: bool,
pub sysroot: Option<PathBuf>,
pub default_script: Option<PathBuf>,
pub entry: Option<String>,
pub soname: Option<String>,
pub dynamic_linker: Option<PathBuf>,
pub no_dynamic_linker: bool,
pub rpaths: Vec<PathBuf>,
pub rpath_links: Vec<PathBuf>,
pub new_dtags: Option<bool>,
pub undefined: Vec<String>,
pub undefined_glob: Vec<String>,
pub require_defined: Vec<String>,
pub defsym: Vec<(String, String)>,
pub wrap: Vec<String>,
pub init: Option<String>,
pub fini: Option<String>,
pub auxiliary: Vec<String>,
pub filter: Vec<String>,
pub gc_sections: bool,
pub print_gc_sections: bool,
pub gc_keep_exported: bool,
pub why_live: Vec<String>,
pub icf: IcfMode,
pub print_icf_sections: bool,
pub keep_unique: Vec<String>,
pub ignore_data_address_equality: bool,
pub ignore_function_address_equality: bool,
pub strip: StripMode,
pub discard: DiscardMode,
pub retain_symbols_file: Option<PathBuf>,
pub build_id: BuildId,
pub hash_style: HashStyle,
pub eh_frame_hdr: bool,
pub export_dynamic: bool,
pub export_dynamic_symbols: Vec<String>,
pub export_dynamic_symbol_lists: Vec<PathBuf>,
pub dynamic_lists: Vec<PathBuf>,
pub dynamic_list_data: bool,
pub dynamic_list_cpp_new: bool,
pub dynamic_list_cpp_typeinfo: bool,
pub exclude_libs: Vec<String>,
pub version_scripts: Vec<PathBuf>,
pub undefined_version: Option<bool>,
pub default_symver: bool,
pub symbolic: SymbolicMode,
pub no_undefined: Option<bool>,
pub allow_shlib_undefined: Option<bool>,
pub unresolved_symbols: Option<UnresolvedSymbols>,
pub warn_unresolved_symbols: bool,
pub ignore_unresolved_symbols: Vec<String>,
pub allow_multiple_definition: bool,
pub warn_common: bool,
pub warn_backrefs: bool,
pub warn_backrefs_exclude: Vec<String>,
pub warn_textrel: bool,
pub error_textrel: bool,
pub bind_now: bool,
pub relro: bool,
pub separate_code: Option<SeparateCode>,
pub rosegment: Option<bool>,
pub exec_stack: ExecStack,
pub gnu_stack: bool,
pub stack_size: Option<u64>,
pub max_page_size: Option<u64>,
pub common_page_size: Option<u64>,
pub copy_relocs: bool,
pub combine_relocs: bool,
pub pack_relative_relocs: bool,
pub gnu_tls_tag: bool,
pub gnu2_tls_tag: bool,
pub apply_dynamic_relocs: bool,
pub dynamic_flags: DynamicFlags,
pub start_stop_gc: Option<bool>,
pub start_stop_visibility: Option<Visibility>,
pub keep_text_section_prefix: bool,
pub dynamic_undefined_weak: Option<bool>,
pub extern_protected_data: bool,
pub mark_plt: bool,
pub section_header: bool,
pub memory_seal: bool,
pub dead_reloc_in_nonalloc: Vec<(String, u64)>,
pub x86: X86Features,
pub fix_cortex_a53_843419: bool,
pub aarch64: Aarch64Features,
pub spare_dynamic_tags: Option<u64>,
pub emit_relocs: bool,
pub define_common: bool,
pub magic: MagicMode,
pub relax: bool,
pub merge_exidx_entries: bool,
pub relax_gp: bool,
pub image_base: Option<u64>,
pub section_starts: Vec<(String, u64)>,
pub text_segment: Option<u64>,
pub rodata_segment: Option<u64>,
pub ldata_segment: Option<u64>,
pub orphan_handling: OrphanHandling,
pub sort_section: SortSection,
pub compress_debug_sections: DebugCompression,
pub package_metadata: Option<String>,
pub symbol_ordering_file: Option<PathBuf>,
pub no_warn_symbol_ordering: bool,
pub call_graph_profile_sort: Option<CallGraphSort>,
pub call_graph_ordering_file: Option<PathBuf>,
pub print_symbol_order: Option<PathBuf>,
pub gdb_index: bool,
pub s390_pgste: bool,
pub debug_names: bool,
pub separate_debug_file: Option<Option<PathBuf>>,
pub dependency_file: Option<PathBuf>,
pub dependent_libraries: bool,
pub optimize: u8,
pub threads: Option<usize>,
pub map_file: Option<PathBuf>,
pub print_map: bool,
pub cref: bool,
pub trace: bool,
pub trace_symbols: Vec<String>,
pub verbose: bool,
pub demangle: bool,
pub fatal_warnings: bool,
pub no_warnings: bool,
pub error_limit: Option<u64>,
pub color: ColorChoice,
pub noinhibit_exec: bool,
pub pe: PeArgs,
pub darwin: crate::args::darwin::DarwinArgs,
pub plugins: Vec<(PathBuf, Vec<String>)>,
pub plugin_save_temps: bool,
pub exit_on_plugin_fatal: bool,
pub fork: bool,
pub on_output_complete: Option<OutputCompleteHook>,
pub input_provider: Option<std::sync::Arc<dyn crate::input::source::InputProvider>>,
pub output_buffer: Option<OutputBuffer>,
pub cancel: Option<CancelToken>,
pub map_output: Option<TextOutput>,
pub timing: Option<TextOutput>,
pub env_run_path: Vec<PathBuf>,
pub env_library_path: Vec<PathBuf>,
pub zero_ar_date: bool,
pub output_backing: Option<OutputBacking>,
pub ignored: Vec<OsString>,
pub warnings: Vec<String>,
}
impl Default for LinkOptions {
fn default() -> Self {
Self::new()
}
}
impl LinkOptions {
#[must_use]
pub fn new() -> Self {
Self {
demangle: true,
relro: true,
gnu_stack: true,
copy_relocs: true,
combine_relocs: true,
extern_protected_data: true,
section_header: true,
relax: true,
merge_exidx_entries: true,
gnu_tls_tag: true,
gnu2_tls_tag: true,
relax_gp: false,
dependent_libraries: true,
fork: true,
..Self::blank()
}
}
fn blank() -> Self {
Self {
flavor: Default::default(),
target: Default::default(),
endian: Default::default(),
output: Default::default(),
output_format: Default::default(),
kind: Default::default(),
inputs: Default::default(),
search_paths: Default::default(),
nostdlib: Default::default(),
sysroot: Default::default(),
default_script: Default::default(),
entry: Default::default(),
soname: Default::default(),
dynamic_linker: Default::default(),
no_dynamic_linker: Default::default(),
rpaths: Default::default(),
rpath_links: Default::default(),
new_dtags: Default::default(),
undefined: Default::default(),
undefined_glob: Default::default(),
require_defined: Default::default(),
defsym: Default::default(),
wrap: Default::default(),
init: Default::default(),
fini: Default::default(),
auxiliary: Default::default(),
filter: Default::default(),
gc_sections: Default::default(),
print_gc_sections: Default::default(),
gc_keep_exported: Default::default(),
why_live: Default::default(),
icf: Default::default(),
print_icf_sections: Default::default(),
keep_unique: Default::default(),
ignore_data_address_equality: Default::default(),
ignore_function_address_equality: Default::default(),
strip: Default::default(),
discard: Default::default(),
retain_symbols_file: Default::default(),
build_id: Default::default(),
hash_style: Default::default(),
eh_frame_hdr: Default::default(),
export_dynamic: Default::default(),
export_dynamic_symbols: Default::default(),
export_dynamic_symbol_lists: Default::default(),
dynamic_lists: Default::default(),
dynamic_list_data: Default::default(),
dynamic_list_cpp_new: Default::default(),
dynamic_list_cpp_typeinfo: Default::default(),
exclude_libs: Default::default(),
version_scripts: Default::default(),
undefined_version: Default::default(),
default_symver: Default::default(),
symbolic: Default::default(),
no_undefined: Default::default(),
allow_shlib_undefined: Default::default(),
unresolved_symbols: Default::default(),
warn_unresolved_symbols: Default::default(),
ignore_unresolved_symbols: Default::default(),
allow_multiple_definition: Default::default(),
warn_common: Default::default(),
warn_backrefs: Default::default(),
warn_backrefs_exclude: Default::default(),
warn_textrel: Default::default(),
error_textrel: Default::default(),
bind_now: Default::default(),
relro: Default::default(),
separate_code: Default::default(),
rosegment: Default::default(),
exec_stack: Default::default(),
gnu_stack: Default::default(),
stack_size: Default::default(),
max_page_size: Default::default(),
common_page_size: Default::default(),
copy_relocs: Default::default(),
combine_relocs: Default::default(),
pack_relative_relocs: Default::default(),
gnu_tls_tag: Default::default(),
gnu2_tls_tag: Default::default(),
apply_dynamic_relocs: Default::default(),
dynamic_flags: Default::default(),
start_stop_gc: Default::default(),
start_stop_visibility: Default::default(),
keep_text_section_prefix: Default::default(),
dynamic_undefined_weak: Default::default(),
extern_protected_data: Default::default(),
mark_plt: Default::default(),
section_header: Default::default(),
memory_seal: Default::default(),
dead_reloc_in_nonalloc: Default::default(),
x86: Default::default(),
fix_cortex_a53_843419: Default::default(),
aarch64: Default::default(),
spare_dynamic_tags: Default::default(),
emit_relocs: Default::default(),
define_common: Default::default(),
magic: Default::default(),
relax: Default::default(),
merge_exidx_entries: Default::default(),
relax_gp: Default::default(),
image_base: Default::default(),
section_starts: Default::default(),
text_segment: Default::default(),
rodata_segment: Default::default(),
ldata_segment: Default::default(),
orphan_handling: Default::default(),
sort_section: Default::default(),
compress_debug_sections: Default::default(),
package_metadata: Default::default(),
symbol_ordering_file: Default::default(),
no_warn_symbol_ordering: Default::default(),
call_graph_profile_sort: Default::default(),
call_graph_ordering_file: Default::default(),
print_symbol_order: Default::default(),
gdb_index: Default::default(),
s390_pgste: Default::default(),
debug_names: Default::default(),
separate_debug_file: Default::default(),
dependency_file: Default::default(),
dependent_libraries: Default::default(),
optimize: Default::default(),
threads: Default::default(),
map_file: Default::default(),
print_map: Default::default(),
cref: Default::default(),
trace: Default::default(),
trace_symbols: Default::default(),
verbose: Default::default(),
demangle: Default::default(),
fatal_warnings: Default::default(),
no_warnings: Default::default(),
error_limit: Default::default(),
color: Default::default(),
noinhibit_exec: Default::default(),
pe: Default::default(),
darwin: Default::default(),
plugins: Default::default(),
plugin_save_temps: Default::default(),
exit_on_plugin_fatal: Default::default(),
fork: Default::default(),
on_output_complete: Default::default(),
input_provider: Default::default(),
output_buffer: Default::default(),
cancel: Default::default(),
map_output: Default::default(),
timing: Default::default(),
env_run_path: Default::default(),
env_library_path: Default::default(),
zero_ar_date: Default::default(),
output_backing: Default::default(),
ignored: Default::default(),
warnings: Default::default(),
}
}
pub fn use_process_defaults(&mut self) {
self.map_output = Some(TextOutput::stdout());
if std::env::var_os("QLD_TIMING").is_some() {
self.timing = Some(TextOutput::stderr());
}
if let Some(run_path) = std::env::var_os("LD_RUN_PATH") {
self.env_run_path = std::env::split_paths(&run_path).collect();
}
if let Some(library_path) = std::env::var_os("LD_LIBRARY_PATH") {
self.env_library_path = std::env::split_paths(&library_path).collect();
}
self.zero_ar_date =
std::env::var_os("ZERO_AR_DATE").is_some_and(|v| !v.is_empty() && v != "0");
self.output_backing = std::env::var("QLD_OUTPUT_BACKING")
.ok()
.and_then(|value| OutputBacking::from_name(value.trim()));
}
pub(crate) fn print_text(&self, text: &str) {
if let Some(output) = &self.map_output {
output.write(text);
}
}
pub fn output_complete(&self) {
if let Some(hook) = &self.on_output_complete {
hook.call();
}
}
#[inline]
pub fn check_cancelled(&self) -> crate::Result<()> {
match &self.cancel {
Some(token) => token.check(),
None => Ok(()),
}
}
#[must_use]
pub fn output_path(&self) -> PathBuf {
self.output
.clone()
.unwrap_or_else(|| PathBuf::from("a.out"))
}
#[must_use]
pub fn is_pic(&self) -> bool {
matches!(
self.kind,
OutputKind::Pie | OutputKind::Shared | OutputKind::StaticPie
)
}
#[must_use]
pub fn is_dynamic(&self) -> bool {
matches!(
self.kind,
OutputKind::Executable | OutputKind::Pie | OutputKind::Shared
)
}
pub fn push_input(&mut self, kind: InputKind, attrs: InputAttrs) {
let position = self.inputs.len();
self.inputs.push(InputSpec {
kind,
attrs,
position,
});
}
#[must_use]
pub fn resolve_sysroot(&self, path: &Path) -> PathBuf {
match sysroot_relative(path) {
Some(rest) => match &self.sysroot {
Some(root) => {
let mut joined = root.clone().into_os_string();
joined.push(rest);
PathBuf::from(joined)
}
None => PathBuf::from(rest),
},
None => path.to_path_buf(),
}
}
}
#[must_use]
pub fn sysroot_relative(path: &Path) -> Option<&std::ffi::OsStr> {
let bytes = path.as_os_str().as_encoded_bytes();
let prefix_len = if bytes.starts_with(b"=") {
1
} else if bytes.starts_with(b"$SYSROOT") {
8
} else {
return None;
};
split_after_ascii_prefix(path.as_os_str(), prefix_len)
}
#[cfg(unix)]
fn split_after_ascii_prefix(s: &std::ffi::OsStr, len: usize) -> Option<&std::ffi::OsStr> {
use std::os::unix::ffi::OsStrExt;
s.as_bytes().get(len..).map(std::ffi::OsStr::from_bytes)
}
#[cfg(not(unix))]
fn split_after_ascii_prefix(s: &std::ffi::OsStr, len: usize) -> Option<&std::ffi::OsStr> {
s.to_str()
.and_then(|s| s.get(len..))
.map(std::ffi::OsStr::new)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn defaults_match_gnu_ld() {
let options = LinkOptions::new();
assert_eq!(options.output_path(), PathBuf::from("a.out"));
assert_eq!(options.kind, OutputKind::Executable);
assert!(!options.gc_sections);
assert!(options.demangle);
assert!(options.relro);
assert!(options.relax);
assert!(options.is_dynamic());
assert!(!options.is_pic());
}
#[test]
fn default_is_new() {
assert_eq!(
format!("{:?}", LinkOptions::default()),
format!("{:?}", LinkOptions::new())
);
let options = LinkOptions::default();
assert!(options.relro);
assert!(options.demangle);
assert!(options.relax);
assert!(options.gnu_stack);
assert!(options.copy_relocs);
assert!(options.combine_relocs);
assert!(options.extern_protected_data);
assert!(options.section_header);
assert!(options.dependent_libraries);
assert!(options.fork);
}
#[test]
fn inputs_keep_command_line_positions() {
let mut options = LinkOptions::new();
options.push_input(InputKind::File("a.o".into()), InputAttrs::default());
options.push_input(InputKind::Library("c".into()), InputAttrs::default());
assert_eq!(options.inputs[1].position, 1);
}
#[test]
fn sysroot_prefixes_resolve_without_io() {
let mut options = LinkOptions::new();
assert_eq!(
options.resolve_sysroot(Path::new("=/usr/lib")),
PathBuf::from("/usr/lib")
);
options.sysroot = Some("/sys".into());
assert_eq!(
options.resolve_sysroot(Path::new("=/usr/lib")),
PathBuf::from("/sys/usr/lib")
);
assert_eq!(
options.resolve_sysroot(Path::new("$SYSROOT/lib")),
PathBuf::from("/sys/lib")
);
assert_eq!(
options.resolve_sysroot(Path::new("/lib")),
PathBuf::from("/lib")
);
}
}