Preserve Unix path bytes through argument and file handling

Use OsString for arguments and PathBuf for filesystem names so valid Unix paths
survive parsing and reach the filesystem unchanged. Preserve their bytes in
loader metadata, LTO callbacks, reproduction archives and dependency rules as
well.

Add a regression using compiler-generated objects with non-UTF-8 bytes and
spaces in their names. Cover direct arguments, nested response files, library
search, linker scripts, thin archives and reproduction replay.
This commit is contained in:
Rui Ueyama committed 2026-09-09 14:16:13 +09:00
1 parent 05ee232da0
commit 1967c81832
46 files changed
+596 -448

No files matched your search

+2 -2
View File
@@ -16,7 +16,7 @@ static GLOBAL: mimalloc::MiMalloc = mimalloc::MiMalloc;
// Each target has its own monomorphized link function. Start with x86-64 and,
// if the inputs use another machine type, run the matching function instead.
fn link_for_target(target: &str, cmdline: &[String]) -> Result<i32, String> {
fn link_for_target(target: &str, cmdline: &[std::ffi::OsString]) -> Result<i32, String> {
match target {
#[cfg(feature = "x86_64")]
"x86_64" => mold_target_x86_64::link(cmdline),
@@ -66,7 +66,7 @@ fn link_for_target(target: &str, cmdline: &[String]) -> Result<i32, String> {
}
fn main() {
let args: Vec<String> = std::env::args().collect();
let args = std::env::args_os().collect();
let status = mold::driver::main(args, link_for_target);
std::process::exit(status);
}
+12 -10
View File
@@ -23,6 +23,8 @@
//! see the contents of libc.a by running `ar t
//! /usr/lib/x86_64-linux-gnu/libc.a`.
use std::path::{Path, PathBuf};
use crate::fatal;
use crate::mapped_file::MappedFile;
use crate::util;
@@ -55,14 +57,14 @@ impl<'a> ArHeader<'a> {
/// Returns the member's file name. A BSD-style long name is stored
/// right after the header, so `body` is advanced past it.
fn read_name(&self, strtab: &[u8], body: &mut &'a [u8]) -> String {
fn read_name(&self, strtab: &[u8], body: &mut &'a [u8]) -> PathBuf {
// BSD-style long filename
if let Some(rest) = self.name.strip_prefix(b"#1/") {
let len = parse_decimal(rest);
let (name, remaining) = body.split_at(len.min(body.len()));
*body = remaining;
let name = name.split(|&b| b == 0).next().unwrap_or(&[]);
return String::from_utf8_lossy(name).into_owned();
return PathBuf::from(util::os_str(name));
}
// SysV-style long filename
@@ -73,7 +75,7 @@ impl<'a> ArHeader<'a> {
.windows(2)
.position(|w| w == b"/\n")
.unwrap_or(start.len());
return String::from_utf8_lossy(&start[..end]).into_owned();
return PathBuf::from(util::os_str(&start[..end]));
}
// Short fileanme
@@ -82,7 +84,7 @@ impl<'a> ArHeader<'a> {
.iter()
.position(|&b| b == b'/')
.unwrap_or(self.name.len());
String::from_utf8_lossy(&self.name[..end]).into_owned()
PathBuf::from(util::os_str(&self.name[..end]))
}
}
@@ -100,7 +102,7 @@ fn parse_decimal(bytes: &[u8]) -> usize {
fn archive_members(
mf: &'static MappedFile,
thin: bool,
) -> impl Iterator<Item = (String, &'static [u8])> {
) -> impl Iterator<Item = (PathBuf, &'static [u8])> {
let data = mf.data();
let mut pos = 8;
let mut strtab: &'static [u8] = &[];
@@ -136,7 +138,7 @@ fn archive_members(
}
if thin && !hdr.name.starts_with(b"#1/") && !hdr.name.starts_with(b"/") {
fatal!("{}: filename is not stored as a long filename", mf.name);
fatal!("{}: filename is not stored as a long filename", mf.name.display());
}
// Read the name field
@@ -151,7 +153,7 @@ fn archive_members(
}
// Skip BSD archive symbol tables.
if name == "__.SYMDEF" || name == "__.SYMDEF SORTED" {
if name == Path::new("__.SYMDEF") || name == Path::new("__.SYMDEF SORTED") {
pos = body_end;
continue;
}
@@ -163,13 +165,13 @@ fn archive_members(
/// Returns the paths of the members of a thin archive, which are stored
/// outside of the archive file, without opening them.
pub fn get_thin_archive_member_paths(mf: &'static MappedFile) -> Vec<String> {
pub fn get_thin_archive_member_paths(mf: &'static MappedFile) -> Vec<PathBuf> {
archive_members(mf, true)
.map(|(name, _)| {
if name.starts_with('/') {
if name.is_absolute() {
name
} else {
format!("{}/{}", util::path_dirname(&mf.name), name)
mf.name.parent().unwrap_or(Path::new(".")).join(name)
}
})
.collect()
+2 -2
View File
@@ -80,10 +80,10 @@ fn create_contents<E: Arch>(ctx: &Context<E>) -> Vec<(u64, u64)> {
} else {
DT_RPATH
};
define(tag, dynstr.find_string(ctx.args.rpaths.as_bytes()));
define(tag, dynstr.find_string(ctx.args.rpaths.as_encoded_bytes()));
}
if !ctx.args.soname.is_empty() {
define(DT_SONAME, dynstr.find_string(ctx.args.soname.as_bytes()));
define(DT_SONAME, dynstr.find_string(ctx.args.soname.as_encoded_bytes()));
}
for s in &ctx.args.auxiliary {
define(DT_AUXILIARY, dynstr.find_string(s.as_bytes()));
+1 -1
View File
@@ -93,7 +93,7 @@ pub fn copy_buf<E: Arch>(ctx: &Context<E>, buf: &mut [u8]) {
s.hdr.shdr.sh_size.get() / ElfShdr::<E>::size() as u64
});
error!("{}: .dynsym: too many output sections: {nshdrs} requested, but ELF allows at most 65279",
ctx.args.output
ctx.args.output.display()
);
}
}
+11 -5
View File
@@ -6,7 +6,7 @@ use crate::context::Context;
use crate::elf::*;
use crate::util::align_to;
use crate::util::endian::Endian;
use crate::util::{path_filename, write_cstr};
use crate::util::write_cstr;
// .gnu_debuglink section contains a pathname and its CRC32 checksum for a
// separate debug info file. gdb can read the section to read debug info
@@ -14,7 +14,7 @@ use crate::util::{path_filename, write_cstr};
#[derive(Debug)]
pub struct GnuDebuglinkSection<E: Layout> {
pub hdr: ChunkHeader<E>,
pub filename: String,
pub filename: Vec<u8>,
pub crc32: u32,
}
@@ -24,7 +24,7 @@ impl<E: Layout> GnuDebuglinkSection<E> {
hdr.shdr.sh_addralign.set(4);
GnuDebuglinkSection {
hdr,
filename: String::new(),
filename: Vec::new(),
crc32: 0,
}
}
@@ -37,7 +37,13 @@ impl<E: Layout> Default for GnuDebuglinkSection<E> {
}
pub fn update_shdr<E: Arch>(ctx: &mut Context<E>) {
let filename = path_filename(&ctx.args.separate_debug_file);
let filename = ctx
.args
.separate_debug_file
.file_name()
.unwrap_or_default()
.as_encoded_bytes()
.to_vec();
let size = align_to(filename.len() as u64 + 1, 4) + 4;
let sec = ctx.gnu_debuglink.as_mut().unwrap();
sec.hdr.shdr.sh_size.set(size);
@@ -47,7 +53,7 @@ pub fn update_shdr<E: Arch>(ctx: &mut Context<E>) {
pub fn copy_buf<E: Arch>(ctx: &Context<E>, buf: &mut [u8]) {
let sec = ctx.gnu_debuglink.as_ref().unwrap();
buf.fill(0);
write_cstr(buf, sec.filename.as_bytes());
write_cstr(buf, &sec.filename);
let n = buf.len();
E::Endian::write_u32(&mut buf[n - 4..], sec.crc32);
}
+2 -2
View File
@@ -30,10 +30,10 @@ impl<E: Layout> Default for InterpSection<E> {
}
pub fn update_shdr<E: Arch>(ctx: &mut Context<E>) {
let size = ctx.args.dynamic_linker.len() as u64 + 1;
let size = ctx.args.dynamic_linker.as_os_str().len() as u64 + 1;
ctx.interp.as_mut().unwrap().hdr.shdr.sh_size.set(size);
}
pub fn copy_buf<E: Arch>(ctx: &Context<E>, buf: &mut [u8]) {
write_cstr(buf, ctx.args.dynamic_linker.as_bytes());
write_cstr(buf, ctx.args.dynamic_linker.as_os_str().as_encoded_bytes());
}
+3 -3
View File
@@ -86,7 +86,7 @@ pub struct ResolveOptions<'a> {
pub allocated_only: bool,
pub gc_sections: bool,
pub comment: Option<MergedSectionId>,
pub cmdline_args: &'a [String],
pub cmdline_args: &'a [std::ffi::OsString],
pub timers: &'a Timers,
}
@@ -534,7 +534,7 @@ pub fn resolve_sections<E: Arch>(
fn add_comment_strings<E: Layout>(
msec: &MergedSection<E>,
gc_sections: bool,
cmdline_args: &[String],
cmdline_args: &[std::ffi::OsString],
) {
let add = |s: String| {
let mut bytes = s.into_bytes();
@@ -549,7 +549,7 @@ fn add_comment_strings<E: Layout>(
if std::env::var("MOLD_DEBUG").is_ok_and(|v| !v.is_empty()) {
add(format!(
"mold command line: {}",
cmdline_args[1..].join(" ")
cmdline_args[1..].iter().map(|s| s.to_string_lossy()).collect::<Vec<_>>().join(" ")
));
}
}
+4 -5
View File
@@ -10,7 +10,6 @@ use crate::context::Context;
use crate::elf::*;
use crate::input_files::FileId;
use crate::util::endian::{U16, U32};
use crate::util::path_filename;
// .gnu.version contains a parallel table for .dynsym to specify symbol
// versions of defined symbols. This section appears only in .so files,
@@ -120,14 +119,14 @@ pub fn construct<E: Arch>(ctx: &mut Context<E>) {
};
let soname = if ctx.args.soname.is_empty() {
path_filename(&ctx.args.output)
ctx.args.output.file_name().unwrap_or_default().as_encoded_bytes().to_vec()
} else {
ctx.args.soname.clone()
ctx.args.soname.as_encoded_bytes().to_vec()
};
write(
&mut contents,
&mut ctx.dynstr,
soname.as_bytes(),
&soname,
1,
VER_FLG_BASE as u16,
);
@@ -137,7 +136,7 @@ pub fn construct<E: Arch>(ctx: &mut Context<E>) {
write(
&mut contents,
&mut ctx.dynstr,
verstr.as_bytes(),
verstr,
VER_NDX_LAST_RESERVED as u16 + 1 + i as u16,
0,
);
+184 -150
View File
@@ -1,14 +1,16 @@
//! Command-line argument parsing.
use std::collections::{HashMap, HashSet};
use std::ffi::{OsStr, OsString};
use std::io::IsTerminal;
use std::path::{Path, PathBuf};
use crate::arch;
use crate::elf::*;
use crate::mapped_file::MappedFile;
use crate::util::glob::Glob;
use crate::util::perf::Counter;
use crate::util::{self, align_down, path_clean, path_filename};
use crate::util::{self, align_down};
use crate::{fatal, out, warn};
const HELP: &str = "
@@ -326,7 +328,7 @@ pub enum DefsymValue {
/// A source of dynamic-list patterns, kept in command line order.
#[derive(Clone, Debug)]
pub enum DynamicListSource {
File(String),
File(PathBuf),
Pattern(String),
}
@@ -379,7 +381,7 @@ impl ReaderContext {
#[derive(Clone, Debug, Default)]
pub struct ReaderJob {
pub rctx: ReaderContext,
pub name: String,
pub name: PathBuf,
pub is_lib: bool,
pub mf: Option<&'static MappedFile>,
@@ -492,37 +494,37 @@ pub struct Args {
pub retain_symbols_file: Option<Vec<String>>,
pub physical_image_base: Option<u64>,
pub ttext_segment: Option<u64>,
pub map: String,
pub map: PathBuf,
pub audit: String,
pub chroot: String,
pub chroot: PathBuf,
pub depaudit: String,
pub dependency_file: String,
pub directory: String,
pub dynamic_linker: String,
pub output: String,
pub dependency_file: PathBuf,
pub directory: PathBuf,
pub dynamic_linker: PathBuf,
pub output: PathBuf,
pub package_metadata: String,
pub plugin: String,
pub print_gc_sections: String,
pub print_icf_sections: String,
pub rpaths: String,
pub separate_debug_file: String,
pub soname: String,
pub sysroot: String,
pub plugin: PathBuf,
pub print_gc_sections: PathBuf,
pub print_icf_sections: PathBuf,
pub rpaths: OsString,
pub separate_debug_file: PathBuf,
pub soname: OsString,
pub sysroot: PathBuf,
pub emulation: String,
pub section_align: HashMap<String, u64>,
pub section_start: HashMap<String, u64>,
pub discard_section: HashSet<String>,
pub exclude_libs: HashSet<String>,
pub ignore_ir_file: HashSet<String>,
pub ignore_ir_file: HashSet<OsString>,
pub wrap: HashSet<String>,
pub section_order: Vec<SectionOrder>,
pub require_defined: Vec<String>,
pub undefined: Vec<String>,
pub defsyms: Vec<(String, DefsymValue)>,
pub library_paths: Vec<String>,
pub library_paths: Vec<PathBuf>,
pub plugin_opt: Vec<String>,
pub version_definitions: Vec<String>,
pub version_scripts: Vec<String>,
pub version_definitions: Vec<Vec<u8>>,
pub version_scripts: Vec<PathBuf>,
pub dynamic_list: Vec<DynamicListSource>,
pub auxiliary: Vec<String>,
pub filter: Vec<String>,
@@ -638,22 +640,22 @@ impl Default for Args {
retain_symbols_file: None,
physical_image_base: None,
ttext_segment: None,
map: String::new(),
map: PathBuf::new(),
audit: String::new(),
chroot: String::new(),
chroot: PathBuf::new(),
depaudit: String::new(),
dependency_file: String::new(),
directory: String::new(),
dynamic_linker: String::new(),
output: "a.out".to_string(),
dependency_file: PathBuf::new(),
directory: PathBuf::new(),
dynamic_linker: PathBuf::new(),
output: PathBuf::from("a.out"),
package_metadata: String::new(),
plugin: String::new(),
print_gc_sections: String::new(),
print_icf_sections: String::new(),
rpaths: String::new(),
separate_debug_file: String::new(),
soname: String::new(),
sysroot: String::new(),
plugin: PathBuf::new(),
print_gc_sections: PathBuf::new(),
print_icf_sections: PathBuf::new(),
rpaths: OsString::new(),
separate_debug_file: PathBuf::new(),
soname: OsString::new(),
sysroot: PathBuf::new(),
emulation: String::new(),
section_align: HashMap::new(),
section_start: HashMap::new(),
@@ -714,9 +716,9 @@ fn is_space(c: u8) -> bool {
//
// This function opens a given file, tokenizes its contents, and returns a
// list of tokens.
fn read_response_file(path: &str, depth: usize) -> Vec<String> {
fn read_response_file(path: &Path, depth: usize) -> Vec<OsString> {
if depth > 10 {
fatal!("{path}: response file nesting too deep");
fatal!("{}: response file nesting too deep", path.display());
}
let mf = MappedFile::must_open(path);
@@ -742,7 +744,7 @@ fn read_response_file(path: &str, depth: usize) -> Vec<String> {
let c = data[i];
if c == b'\\' {
if i + 1 == data.len() {
fatal!("{path}: premature end of input");
fatal!("{}: premature end of input", path.display());
}
tok.push(data[i + 1]);
i += 2;
@@ -764,15 +766,15 @@ fn read_response_file(path: &str, depth: usize) -> Vec<String> {
}
}
if quote.is_some() {
fatal!("{path}: premature end of input");
fatal!("{}: premature end of input", path.display());
}
tokens.push(String::from_utf8_lossy(&tok).into_owned());
tokens.push(util::os_str(&tok).to_os_string());
}
let mut expanded = Vec::new();
for tok in tokens {
if let Some(nested) = tok.strip_prefix('@') {
expanded.extend(read_response_file(nested, depth + 1));
if let Some(nested) = tok.as_encoded_bytes().strip_prefix(b"@") {
expanded.extend(read_response_file(Path::new(util::os_str(nested)), depth + 1));
} else {
expanded.push(tok);
}
@@ -781,11 +783,11 @@ fn read_response_file(path: &str, depth: usize) -> Vec<String> {
}
// Replace "@path/to/some/text/file" with its file contents.
pub fn expand_response_files(argv: &[String]) -> Vec<String> {
pub fn expand_response_files(argv: &[OsString]) -> Vec<OsString> {
let mut args = Vec::new();
for arg in argv {
if let Some(path) = arg.strip_prefix('@') {
args.extend(read_response_file(path, 1));
if let Some(path) = arg.as_encoded_bytes().strip_prefix(b"@") {
args.extend(read_response_file(Path::new(util::os_str(path)), 1));
} else {
args.push(arg.clone());
}
@@ -805,18 +807,19 @@ pub fn expand_response_files(argv: &[String]) -> Vec<String> {
// as "-o magic". If you really want to specify the "omagic" option,
// you have to pass "--omagic". Single-letter option names take a
// single dash.
fn match_option<'a>(arg: &'a str, name: &str) -> Option<&'a str> {
let arg = arg.strip_prefix('-')?;
fn match_option<'a>(arg: &'a OsStr, name: &str) -> Option<&'a OsStr> {
let arg = arg.as_encoded_bytes().strip_prefix(b"-")?;
if name.len() == 1 {
return arg.strip_prefix(name);
return arg.strip_prefix(name.as_bytes()).map(util::os_str);
}
// Options beginning with "o" require double dashes
if name.starts_with('o') && !arg.starts_with('-') {
// Options beginning with "o" require double dashes.
if name.starts_with('o') && !arg.starts_with(b"-") {
return None;
}
let arg = arg.strip_prefix('-').unwrap_or(arg);
arg.strip_prefix(name)
arg.strip_prefix(b"-")
.unwrap_or(arg)
.strip_prefix(name.as_bytes())
.map(util::os_str)
}
fn parse_hex(opt: &str, value: &str) -> u64 {
@@ -899,7 +902,7 @@ fn parse_package_metadata(arg: &str) -> String {
String::from_utf8_lossy(&out).into_owned()
}
fn read_retain_symbols_file(path: &str) -> Vec<String> {
fn read_retain_symbols_file(path: &Path) -> Vec<String> {
let mf = MappedFile::must_open(path);
String::from_utf8_lossy(mf.data())
.lines()
@@ -1014,7 +1017,13 @@ pub struct ParsedArgs {
}
/// Parses all options. `cmdline` includes the program name.
pub fn parse_args(target: &TargetTraits, cmdline: &[String]) -> ParsedArgs {
pub fn parse_args(target: &TargetTraits, raw_cmdline: &[OsString]) -> ParsedArgs {
// Option names and numeric arguments are text; file arguments retain
// their OS representation through the raw argument readers below.
let cmdline: Vec<&str> = raw_cmdline
.iter()
.map(|s| s.to_str().unwrap_or(""))
.collect();
// Input file arguments are turned into ReaderJobs for
// read_input_files(). rctx tracks the reader state options, such as
// --as-needed, that apply to the files after them; each job gets a
@@ -1023,7 +1032,7 @@ pub fn parse_args(target: &TargetTraits, cmdline: &[String]) -> ParsedArgs {
let mut jobs: Vec<ReaderJob> = Vec::new();
let mut rctx = ReaderContext::default();
let mut rctx_stack: Vec<ReaderContext> = Vec::new();
let mut visited_libs: HashSet<String> = HashSet::new();
let mut visited_libs: HashSet<OsString> = HashSet::new();
a.color_diagnostics = std::io::stderr().is_terminal();
crate::error::set_color(a.color_diagnostics);
@@ -1041,9 +1050,9 @@ pub fn parse_args(target: &TargetTraits, cmdline: &[String]) -> ParsedArgs {
let mut report_undefined: Option<bool> = None;
let mut z_relro: Option<bool> = None;
let mut z_dynamic_undefined_weak: Option<bool> = None;
let mut separate_debug_file: Option<String> = None;
let mut separate_debug_file: Option<PathBuf> = None;
let mut shuffle_sections_seed: Option<u64> = None;
let mut rpaths: HashSet<String> = HashSet::new();
let mut rpaths: HashSet<OsString> = HashSet::new();
// We generally don't need to write addends to relocated places if the
// relocation type is RELA because RELA records contain addends.
@@ -1061,45 +1070,63 @@ pub fn parse_args(target: &TargetTraits, cmdline: &[String]) -> ParsedArgs {
let mut i = 1;
let mut arg = String::new();
let mut raw_arg = OsString::new();
// An option and its argument are either separate command line
// arguments or a single one, as in "-o foo" vs. "-ofoo" or
// "--output foo" vs. "--output=foo".
macro_rules! read_arg {
($name:expr) => {{
($name:expr) => {
read_arg!($name, false)
};
($name:expr, $raw:expr) => {{
let name: &str = $name;
match match_option(&cmdline[i], name) {
None => false,
let value = match match_option(&raw_cmdline[i], name) {
None => None,
Some(rest) if rest.is_empty() => {
if i + 1 == cmdline.len() {
fatal!("option -{name}: argument missing");
}
arg = cmdline[i + 1].clone();
i += 2;
true
Some(raw_cmdline[i - 1].as_os_str())
}
Some(rest) if name.len() == 1 => {
arg = rest.to_string();
i += 1;
true
Some(rest)
}
Some(rest) => match rest.strip_prefix('=') {
Some(value) => {
arg = value.to_string();
i += 1;
true
}
None => false,
},
Some(rest) => rest.as_encoded_bytes().strip_prefix(b"=").map(|value| {
i += 1;
util::os_str(value)
}),
};
if let Some(value) = value {
raw_arg = value.to_os_string();
if !$raw {
arg = value.to_str().unwrap_or_else(||
fatal!("option -{name}: expected a UTF-8 argument")
).to_string();
}
true
} else {
false
}
}};
}
macro_rules! read_eq {
($name:expr) => {{
match match_option(&cmdline[i], $name).and_then(|rest| rest.strip_prefix('=')) {
($name:expr) => {
read_eq!($name, false)
};
($name:expr, $raw:expr) => {{
match match_option(&raw_cmdline[i], $name)
.and_then(|rest| rest.as_encoded_bytes().strip_prefix(b"=")) {
Some(value) => {
arg = value.to_string();
raw_arg = util::os_str(value).to_os_string();
if !$raw {
arg = raw_arg.to_str().unwrap_or_else(||
fatal!("option -{}: expected a UTF-8 argument", $name)
).to_string();
}
i += 1;
true
}
@@ -1110,7 +1137,7 @@ pub fn parse_args(target: &TargetTraits, cmdline: &[String]) -> ParsedArgs {
macro_rules! read_flag {
($name:expr) => {{
if match_option(&cmdline[i], $name) == Some("") {
if match_option(&raw_cmdline[i], $name) == Some(OsStr::new("")) {
i += 1;
true
} else {
@@ -1156,10 +1183,10 @@ pub fn parse_args(target: &TargetTraits, cmdline: &[String]) -> ParsedArgs {
}
while i < cmdline.len() {
if !cmdline[i].starts_with('-') {
if !raw_cmdline[i].as_encoded_bytes().starts_with(b"-") {
let mut job = ReaderJob {
rctx: rctx.clone(),
name: cmdline[i].clone(),
name: PathBuf::from(&raw_cmdline[i]),
..Default::default()
};
job.rctx.pos = vec![jobs.len() as u32];
@@ -1169,14 +1196,14 @@ pub fn parse_args(target: &TargetTraits, cmdline: &[String]) -> ParsedArgs {
}
if read_flag!("help") {
out!("Usage: {} [options] file...\n{}", cmdline[0], HELP);
out!("Usage: {} [options] file...\n{}", raw_cmdline[0].to_string_lossy(), HELP);
std::process::exit(0);
}
if read_arg!("o") || read_arg!("output") {
a.output = arg.clone();
} else if read_arg!("dynamic-linker") || read_arg!("I") {
a.dynamic_linker = arg.clone();
if read_arg!("o", true) || read_arg!("output", true) {
a.output = PathBuf::from(&raw_arg);
} else if read_arg!("dynamic-linker", true) || read_arg!("I", true) {
a.dynamic_linker = PathBuf::from(&raw_arg);
} else if read_flag!("no-dynamic-linker") {
a.dynamic_linker.clear();
} else if read_flag!("v") {
@@ -1225,8 +1252,8 @@ pub fn parse_args(target: &TargetTraits, cmdline: &[String]) -> ParsedArgs {
a.discard_locals = false;
} else if read_arg!("e") || read_arg!("entry") {
a.entry = arg.clone();
} else if read_arg!("Map") {
a.map = arg.clone();
} else if read_arg!("Map", true) {
a.map = PathBuf::from(&raw_arg);
a.print_map = true;
} else if read_flag!("print-dependencies") {
a.print_dependencies = true;
@@ -1246,8 +1273,8 @@ pub fn parse_args(target: &TargetTraits, cmdline: &[String]) -> ParsedArgs {
rctx.in_lib = true;
} else if read_flag!("start-stop") {
a.start_stop = true;
} else if read_arg!("dependency-file") {
a.dependency_file = arg.clone();
} else if read_arg!("dependency-file", true) {
a.dependency_file = PathBuf::from(&raw_arg);
} else if read_arg!("defsym") {
let Some((name, value)) = arg.split_once('=').filter(|(_, v)| !v.is_empty()) else {
fatal!("-defsym: syntax error: {arg}");
@@ -1256,8 +1283,8 @@ pub fn parse_args(target: &TargetTraits, cmdline: &[String]) -> ParsedArgs {
.push((name.to_string(), parse_defsym_value(value)));
} else if read_flag!(":lto-pass2") {
a.lto_pass2 = true;
} else if read_arg!(":ignore-ir-file") {
a.ignore_ir_file.insert(arg.clone());
} else if read_arg!(":ignore-ir-file", true) {
a.ignore_ir_file.insert(raw_arg.clone());
} else if read_flag!("demangle") {
a.demangle = true;
crate::error::set_demangle(true);
@@ -1288,10 +1315,10 @@ pub fn parse_args(target: &TargetTraits, cmdline: &[String]) -> ParsedArgs {
a.trace_symbol.push(arg.clone());
} else if read_arg!("filler") {
a.filler = Some(parse_hex("filler", &arg) as u8);
} else if read_arg!("L") || read_arg!("library-path") {
a.library_paths.push(arg.clone());
} else if read_arg!("sysroot") {
a.sysroot = arg.clone();
} else if read_arg!("L", true) || read_arg!("library-path", true) {
a.library_paths.push(PathBuf::from(&raw_arg));
} else if read_arg!("sysroot", true) {
a.sysroot = PathBuf::from(&raw_arg);
} else if read_arg!("unique") {
if !a.unique.add(arg.as_bytes(), 1) {
fatal!("-unique: invalid glob pattern: {arg}");
@@ -1334,8 +1361,8 @@ pub fn parse_args(target: &TargetTraits, cmdline: &[String]) -> ParsedArgs {
}
_ => fatal!("invalid --hash-style argument: {arg}"),
}
} else if read_arg!("soname") || read_arg!("h") {
a.soname = arg.clone();
} else if read_arg!("soname", true) || read_arg!("h", true) {
a.soname = raw_arg.clone();
} else if read_arg!("audit") {
if !a.audit.is_empty() {
a.audit.push(':');
@@ -1401,10 +1428,10 @@ pub fn parse_args(target: &TargetTraits, cmdline: &[String]) -> ParsedArgs {
} else if read_flag!("stats") {
a.stats = true;
Counter::enable();
} else if read_arg!("C") || read_arg!("directory") {
a.directory = arg.clone();
} else if read_arg!("chroot") {
a.chroot = arg.clone();
} else if read_arg!("C", true) || read_arg!("directory", true) {
a.directory = PathBuf::from(&raw_arg);
} else if read_arg!("chroot", true) {
a.chroot = PathBuf::from(&raw_arg);
} else if read_flag!("color-diagnostics") || read_flag!("color-diagnostics=auto") {
a.color_diagnostics = std::io::stderr().is_terminal();
crate::error::set_color(a.color_diagnostics);
@@ -1475,8 +1502,8 @@ pub fn parse_args(target: &TargetTraits, cmdline: &[String]) -> ParsedArgs {
fatal!("-oformat: {arg} is not supported");
}
a.oformat_binary = true;
} else if read_arg!("retain-symbols-file") {
a.retain_symbols_file = Some(read_retain_symbols_file(&arg));
} else if read_arg!("retain-symbols-file", true) {
a.retain_symbols_file = Some(read_retain_symbols_file(Path::new(&raw_arg)));
} else if read_arg!("section-align") {
let Some((name, value)) = arg.split_once('=').filter(|(_, v)| !v.is_empty()) else {
fatal!("--section-align: syntax error: {arg}");
@@ -1571,10 +1598,10 @@ pub fn parse_args(target: &TargetTraits, cmdline: &[String]) -> ParsedArgs {
a.z_origin = true;
} else if read_z_flag!("nodefaultlib") {
a.z_nodefaultlib = true;
} else if read_eq!("separate-debug-file") {
separate_debug_file = Some(arg.clone());
} else if read_eq!("separate-debug-file", true) {
separate_debug_file = Some(PathBuf::from(&raw_arg));
} else if read_flag!("separate-debug-file") {
separate_debug_file = Some(String::new());
separate_debug_file = Some(PathBuf::new());
} else if read_flag!("no-separate-debug-file") {
separate_debug_file = None;
} else if read_z_flag!("separate-loadable-segments") {
@@ -1630,9 +1657,9 @@ pub fn parse_args(target: &TargetTraits, cmdline: &[String]) -> ParsedArgs {
} else if read_flag!("no-gc-sections") {
a.gc_sections = false;
} else if read_flag!("print-gc-sections") {
a.print_gc_sections = "-".to_string();
} else if read_eq!("print-gc-sections") {
a.print_gc_sections = arg.clone();
a.print_gc_sections = PathBuf::from("-");
} else if read_eq!("print-gc-sections", true) {
a.print_gc_sections = PathBuf::from(&raw_arg);
} else if read_flag!("no-print-gc-sections") {
a.print_gc_sections.clear();
} else if read_arg!("discard-section") {
@@ -1658,17 +1685,17 @@ pub fn parse_args(target: &TargetTraits, cmdline: &[String]) -> ParsedArgs {
} else if read_arg!("physical-image-base") {
a.physical_image_base = Some(parse_number("physical-image-base", &arg) as u64);
} else if read_flag!("print-icf-sections") {
a.print_icf_sections = "-".to_string();
} else if read_eq!("print-icf-sections") {
a.print_icf_sections = arg.clone();
a.print_icf_sections = PathBuf::from("-");
} else if read_eq!("print-icf-sections", true) {
a.print_icf_sections = PathBuf::from(&raw_arg);
} else if read_flag!("no-print-icf-sections") {
a.print_icf_sections.clear();
} else if read_flag!("quick-exit") {
a.quick_exit = true;
} else if read_flag!("no-quick-exit") {
a.quick_exit = false;
} else if read_arg!("plugin") {
a.plugin = arg.clone();
} else if read_arg!("plugin", true) {
a.plugin = PathBuf::from(&raw_arg);
} else if read_arg!("plugin-opt") {
a.plugin_opt.push(arg.clone());
} else if read_flag!("lto-cs-profile-generate") {
@@ -1753,13 +1780,13 @@ pub fn parse_args(target: &TargetTraits, cmdline: &[String]) -> ParsedArgs {
error_unresolved_symbols = false;
} else if read_flag!("error-unresolved-symbols") {
error_unresolved_symbols = true;
} else if read_arg!("rpath") {
add_rpath(&mut a, &mut rpaths, &arg);
} else if read_arg!("R") {
if crate::mapped_file::is_file(&arg) {
fatal!("-R{arg}: -R as an alias for --just-symbols is not supported");
} else if read_arg!("rpath", true) {
add_rpath(&mut a, &mut rpaths, &raw_arg);
} else if read_arg!("R", true) {
if crate::mapped_file::is_file(&raw_arg) {
fatal!("-R{}: -R as an alias for --just-symbols is not supported", raw_arg.to_string_lossy());
}
add_rpath(&mut a, &mut rpaths, &arg);
add_rpath(&mut a, &mut rpaths, &raw_arg);
} else if read_flag!("undefined-version") {
a.undefined_version = true;
} else if read_flag!("no-undefined-version") {
@@ -1854,17 +1881,17 @@ pub fn parse_args(target: &TargetTraits, cmdline: &[String]) -> ParsedArgs {
|| read_flag!("no-mmap-output-file")
{
// Ignored for compatibility.
} else if read_arg!("version-script") {
a.version_scripts.push(arg.clone());
} else if read_arg!("dynamic-list") {
} else if read_arg!("version-script", true) {
a.version_scripts.push(PathBuf::from(&raw_arg));
} else if read_arg!("dynamic-list", true) {
a.bsymbolic = BsymbolicKind::All;
a.dynamic_list.push(DynamicListSource::File(arg.clone()));
a.dynamic_list.push(DynamicListSource::File(PathBuf::from(&raw_arg)));
} else if read_arg!("dynamic-list-data") {
a.dynamic_list_data = true;
} else if read_arg!("export-dynamic-symbol") {
a.dynamic_list.push(DynamicListSource::Pattern(arg.clone()));
} else if read_arg!("export-dynamic-symbol-list") {
a.dynamic_list.push(DynamicListSource::File(arg.clone()));
} else if read_arg!("export-dynamic-symbol-list", true) {
a.dynamic_list.push(DynamicListSource::File(PathBuf::from(&raw_arg)));
} else if read_flag!("as-needed") {
rctx.as_needed = true;
} else if read_flag!("no-as-needed") {
@@ -1873,21 +1900,21 @@ pub fn parse_args(target: &TargetTraits, cmdline: &[String]) -> ParsedArgs {
rctx.whole_archive = true;
} else if read_flag!("no-whole-archive") {
rctx.whole_archive = false;
} else if read_arg!("l") || read_arg!("library") {
if visited_libs.insert(arg.clone()) {
} else if read_arg!("l", true) || read_arg!("library", true) {
if visited_libs.insert(raw_arg.clone()) {
let mut job = ReaderJob {
rctx: rctx.clone(),
name: arg.clone(),
name: PathBuf::from(&raw_arg),
is_lib: true,
..Default::default()
};
job.rctx.pos = vec![jobs.len() as u32];
jobs.push(job);
}
} else if read_arg!("script") || read_arg!("T") {
} else if read_arg!("script", true) || read_arg!("T", true) {
let mut job = ReaderJob {
rctx: rctx.clone(),
name: arg.clone(),
name: PathBuf::from(&raw_arg),
..Default::default()
};
job.rctx.pos = vec![jobs.len() as u32];
@@ -1908,31 +1935,36 @@ pub fn parse_args(target: &TargetTraits, cmdline: &[String]) -> ParsedArgs {
fatal!("unknown command line option: -dynamic; -dynamic is a macOS linker's option. mold does not support macOS."
);
} else {
fatal!("unknown command line option: {}", cmdline[i]);
fatal!("unknown command line option: {}", raw_cmdline[i].to_string_lossy());
}
}
if !a.chroot.is_empty() {
if !a.map.is_empty() {
a.map = format!("{}/{}", a.chroot, a.map);
if !a.chroot.as_os_str().is_empty() {
if !a.map.as_os_str().is_empty() {
a.map = a.chroot.join(a.map.strip_prefix("/").unwrap_or(&a.map));
}
if !a.dependency_file.is_empty() {
a.dependency_file = format!("{}/{}", a.chroot, a.dependency_file);
if !a.dependency_file.as_os_str().is_empty() {
a.dependency_file = a.chroot.join(
a.dependency_file
.strip_prefix("/")
.unwrap_or(&a.dependency_file),
);
}
}
if !a.directory.is_empty() {
if !a.directory.as_os_str().is_empty() {
if let Err(e) = std::env::set_current_dir(&a.directory) {
fatal!("chdir failed: {}: {e}", a.directory);
fatal!("chdir failed: {}: {e}", a.directory.display());
}
}
if !a.sysroot.is_empty() {
if !a.sysroot.as_os_str().is_empty() {
for path in &mut a.library_paths {
if let Some(rest) = path.strip_prefix('=') {
*path = format!("{}{rest}", a.sysroot);
} else if let Some(rest) = path.strip_prefix("$SYSROOT") {
*path = format!("{}{rest}", a.sysroot);
let bytes = path.as_os_str().as_encoded_bytes();
if let Some(rest) = bytes.strip_prefix(b"=").or_else(|| bytes.strip_prefix(b"$SYSROOT")) {
let mut full = a.sysroot.as_os_str().to_os_string();
full.push(util::os_str(rest));
*path = PathBuf::from(full);
}
}
}
@@ -1940,7 +1972,7 @@ pub fn parse_args(target: &TargetTraits, cmdline: &[String]) -> ParsedArgs {
// Clean library paths by removing redundant `/..` and `/.` so that
// they are easier to read in log messages.
for path in &mut a.library_paths {
*path = path_clean(path);
*path = util::clean_path(path);
}
if a.shared {
@@ -2042,16 +2074,18 @@ pub fn parse_args(target: &TargetTraits, cmdline: &[String]) -> ParsedArgs {
if a.default_symver {
let ver = if a.soname.is_empty() {
path_filename(&a.output)
a.output.file_name().unwrap_or_default().as_encoded_bytes().to_vec()
} else {
a.soname.clone()
a.soname.as_encoded_bytes().to_vec()
};
a.version_definitions.push(ver);
}
if let Some(file) = separate_debug_file {
a.separate_debug_file = if file.is_empty() {
format!("{}.dbg", a.output)
a.separate_debug_file = if file.as_os_str().is_empty() {
let mut name = a.output.as_os_str().to_os_string();
name.push(".dbg");
PathBuf::from(name)
} else {
file
};
@@ -2109,11 +2143,11 @@ pub fn parse_args(target: &TargetTraits, cmdline: &[String]) -> ParsedArgs {
}
}
fn add_rpath(a: &mut Args, seen: &mut HashSet<String>, path: &str) {
if seen.insert(path.to_string()) {
fn add_rpath(a: &mut Args, seen: &mut HashSet<OsString>, path: &OsStr) {
if seen.insert(path.to_os_string()) {
if !a.rpaths.is_empty() {
a.rpaths.push(':');
a.rpaths.push(":");
}
a.rpaths.push_str(path);
a.rpaths.push(path);
}
}
+2 -2
View File
@@ -104,7 +104,7 @@ pub struct Context<E: Arch> {
pub args: Args,
// Fully-expanded command line args
pub cmdline_args: Vec<String>,
pub cmdline_args: Vec<std::ffi::OsString>,
pub timers: Timers,
// Symbol table. Object file parsing records each global symbol with add(),
@@ -239,7 +239,7 @@ pub struct Context<E: Arch> {
}
impl<E: Arch> Context<E> {
pub fn new(args: Args, cmdline_args: Vec<String>) -> Context<E> {
pub fn new(args: Args, cmdline_args: Vec<std::ffi::OsString>) -> Context<E> {
let mut symbols = SymbolTable::new();
let syms = SyntheticSymbols {
entry: symbols.intern(crate::util::leak_bytes(args.entry.clone().into_bytes())),
+10 -10
View File
@@ -20,8 +20,8 @@ use crate::{error, fatal, passes};
/// inputs are actually for; the executable provides it, as the targets
/// are instantiated in crates of their own.
pub fn main(
argv: Vec<String>,
link_for_target: impl Fn(&str, &[String]) -> Result<i32, String>,
argv: Vec<std::ffi::OsString>,
link_for_target: impl Fn(&str, &[std::ffi::OsString]) -> Result<i32, String>,
) -> i32 {
// Process -run option first. process_run_subcommand() does not return.
if argv.get(1).is_some_and(|a| a == "-run" || a == "--run") {
@@ -88,7 +88,7 @@ fn wait_for_background<T>(receiver: mpsc::Receiver<T>, name: &str) -> T {
/// Links for the target `E`, or reports the target the inputs are actually
/// for.
pub fn link<E: Arch>(cmdline: &[String]) -> Result<i32, String> {
pub fn link<E: Arch>(cmdline: &[std::ffi::OsString]) -> Result<i32, String> {
let parsed = cmdline::parse_args(&target_traits::<E>(), cmdline);
let cmdline::ParsedArgs { args, jobs, .. } = parsed;
let mut ctx = Context::<E>::new(args, cmdline.to_vec());
@@ -141,13 +141,13 @@ pub fn link<E: Arch>(cmdline: &[String]) -> Result<i32, String> {
for path in ctx.args.version_scripts.clone() {
let chroot = ctx.args.chroot.clone();
let mf = crate::mapped_file::open_file(&chroot, &path).or_else(|| {
ctx.args
.library_paths
.iter()
.find_map(|dir| crate::mapped_file::open_file(&chroot, &format!("{dir}/{path}")))
ctx.args.library_paths.iter().find_map(|dir| {
let name = path.strip_prefix("/").unwrap_or(&path);
crate::mapped_file::open_file(&chroot, dir.join(name))
})
});
let Some(mf) = mf else {
fatal!("--version-script: file not found: {path}");
fatal!("--version-script: file not found: {}", path.display());
};
let mut rctx = cmdline::ReaderContext::default();
crate::linker_script::Script::new(&mut ctx, &mut rctx, mf).parse_version_script();
@@ -648,10 +648,10 @@ pub fn link<E: Arch>(cmdline: &[String]) -> Result<i32, String> {
drop(t_close);
// Handle --dependency-file
if !ctx.args.dependency_file.is_empty() {
if !ctx.args.dependency_file.as_os_str().is_empty() {
passes::write_dependency_file(&ctx);
}
if !ctx.args.plugin.is_empty() {
if !ctx.args.plugin.as_os_str().is_empty() {
crate::lto::cleanup();
}
drop(t_all);
+8 -3
View File
@@ -134,7 +134,7 @@ fn is_gcc_lto_obj<E: Layout>(data: &[u8], has_gcc_plugin: bool) -> bool {
}
/// Classifies a file by its contents.
pub fn get_file_type(plugin: &str, mf: &MappedFile) -> FileType {
pub fn get_file_type(plugin: &std::path::Path, mf: &MappedFile) -> FileType {
let data = mf.data();
if data.is_empty() {
return FileType::Empty;
@@ -143,7 +143,12 @@ pub fn get_file_type(plugin: &str, mf: &MappedFile) -> FileType {
// GCC FAT LTO objects can be linked as regular ELF objects. If the active
// plugin is LLVM's, treat them as regular objects so that we can fall back
// to native code instead of routing them through GCC LTO handling.
let has_gcc_plugin = !plugin.is_empty() && !plugin.contains("LLVMgold.");
let has_gcc_plugin = !plugin.as_os_str().is_empty()
&& !plugin
.as_os_str()
.as_encoded_bytes()
.windows(9)
.any(|s| s == b"LLVMgold.");
if data.starts_with(b"\x7fELF") && data.len() >= 20 {
let is_le = data[EI_DATA as usize] == ELFDATA2LSB as u8;
@@ -280,7 +285,7 @@ pub fn get_elf_target(data: &[u8]) -> Option<&'static str> {
// Read the beginning of a given file and returns its machine type
// (e.g. EM_X86_64 or EM_386).
pub fn get_machine_type(
plugin: &str,
plugin: &std::path::Path,
mf: &'static MappedFile,
script_target: impl FnOnce() -> Option<&'static str>,
) -> Option<&'static str> {
+3 -3
View File
@@ -285,7 +285,7 @@ fn sweep<E: Arch>(ctx: &Context<E>) {
.collect();
let path = &ctx.args.print_gc_sections;
if path.is_empty() {
if path.as_os_str().is_empty() {
return;
}
let mut out = String::new();
@@ -296,11 +296,11 @@ fn sweep<E: Arch>(ctx: &Context<E>) {
));
}
out.push_str("GC saved 0 bytes\n");
if path == "-" {
if path == std::path::Path::new("-") {
let _ = std::io::stdout().write_all(out.as_bytes());
} else {
std::fs::write(path, out)
.unwrap_or_else(|e| fatal!("--print-gc-sections: cannot open {path}: {e}"));
.unwrap_or_else(|e| fatal!("--print-gc-sections: cannot open {}: {e}", path.display()));
}
}
+3 -3
View File
@@ -655,11 +655,11 @@ fn print_icf_sections<E: Arch>(ctx: &Context<E>, sections: &[SectionRef]) {
out.push_str(&format!("ICF saved {saved} bytes\n"));
let path = &ctx.args.print_icf_sections;
if path == "-" {
if path == std::path::Path::new("-") {
let _ = std::io::stdout().write_all(out.as_bytes());
} else {
std::fs::write(path, out)
.unwrap_or_else(|e| fatal!("--print-icf-sections: cannot open {path}: {e}"));
.unwrap_or_else(|e| fatal!("--print-icf-sections: cannot open {}: {e}", path.display()));
}
}
@@ -733,7 +733,7 @@ pub fn icf_sections<E: Arch>(ctx: &mut Context<E>) {
.for_each(|(&r, &digest)| ctx.section(r).set_icf_leader(map.find(digest)));
}
if !ctx.args.print_icf_sections.is_empty() {
if !ctx.args.print_icf_sections.as_os_str().is_empty() {
print_icf_sections(ctx, &sections);
}
+14 -11
View File
@@ -32,9 +32,7 @@ use crate::symbol::{
};
use crate::util::endian::Endian;
use crate::util::perf::Counter;
use crate::util::{
self, align_to, bits, cstr_at, leak_bytes, path_clean, path_filename, read_uleb,
};
use crate::util::{self, align_to, bits, cstr_at, leak_bytes, path_clean, read_uleb};
use crate::{error, fatal, out, warn};
use bstr::BStr;
@@ -425,7 +423,7 @@ impl<E: Layout> InputFile<E> {
let Some(shdr_bytes) = data.get(shoff..shoff + num_sections * shdr_size) else {
fatal!(
"{}: e_shoff or e_shnum corrupted: {} {num_sections}",
mf.name,
mf.name.display(),
data.len()
);
};
@@ -436,7 +434,7 @@ impl<E: Layout> InputFile<E> {
is_little_endian: E::Endian::IS_LITTLE,
e_flags: ehdr.e_flags.get(),
shdrs,
..InputFile::empty(&mf.name)
..InputFile::empty(&mf.name.to_string_lossy())
};
// e_shstrndx is a 16-bit field. If .shstrtab's section index is
@@ -1091,7 +1089,7 @@ impl<E: Arch> ObjectFile<E> {
/// Opens an object file and reads its symbol table. Sections are read
/// later, once COMDAT group selection is done.
pub fn new(mf: &'static MappedFile, archive_name: String) -> ObjectFile<E> {
let display = FileName(&mf.name, &archive_name);
let display = FileName(&mf.name.to_string_lossy(), &archive_name);
let base = InputFile::<E>::parse(mf, &display);
let mut file = ObjectFile::with_base(base, archive_name);
file.parse_symbols();
@@ -1108,7 +1106,7 @@ impl<E: Arch> ObjectFile<E> {
strtab: &'static [u8],
comdat_keys: Vec<Option<&'static [u8]>>,
) -> ObjectFile<E> {
let mut base = InputFile::<E>::empty(&mf.name);
let mut base = InputFile::<E>::empty(&mf.name.to_string_lossy());
base.mf = Some(mf);
base.elf_syms = Cow::Owned(elf_syms);
base.symbol_strtab = strtab;
@@ -3050,7 +3048,7 @@ impl<E: Arch> SharedFile<E> {
}
pub fn new(mf: &'static MappedFile) -> SharedFile<E> {
let base = InputFile::<E>::parse(mf, &FileName(&mf.name, ""));
let base = InputFile::<E>::parse(mf, &FileName(&mf.name.to_string_lossy(), ""));
let mut file = SharedFile {
base,
soname: Vec::new(),
@@ -3084,10 +3082,15 @@ impl<E: Arch> SharedFile<E> {
if let Some(soname) = self.dynamic_strings(DT_SONAME as u64).first() {
return soname.to_vec();
}
if self.base.mf.is_none_or(|mf| mf.given_fullpath) {
return self.base.filename.as_bytes().to_vec();
if let Some(mf) = self.base.mf {
let name = if mf.given_fullpath {
mf.name.as_os_str()
} else {
mf.name.file_name().unwrap_or_default()
};
return name.as_encoded_bytes().to_vec();
}
path_filename(&self.base.filename).into_bytes()
self.base.filename.as_bytes().to_vec()
}
fn parse(&mut self) {
+41 -50
View File
@@ -6,13 +6,15 @@
//! The supported subset also includes `OUTPUT_FORMAT`, symbol assignments,
//! version scripts and dynamic lists.
use std::path::{Path, PathBuf};
use crate::arch::Arch;
use crate::cmdline::{DefsymValue, ReaderContext};
use crate::context::Context;
use crate::elf::*;
use crate::mapped_file::{must_open_file, open_file, MappedFile};
use crate::reader;
use crate::util::{self, path_clean};
use crate::util;
use crate::{fatal, warn};
/// A version script pattern.
@@ -70,7 +72,7 @@ fn syntax_error(mf: &MappedFile, tok: &[u8], msg: &str) -> ! {
let pos = offset_of(input, tok).min(input.len().saturating_sub(1));
let (line_start, line) = get_line(input, pos);
let lineno = input[..line_start].iter().filter(|&&b| b == b'\n').count() + 1;
let label = format!("{}:{}: ", mf.name, lineno);
let label = format!("{}:{}: ", mf.name.display(), lineno);
let indent = "mold: fatal: ".len() + label.len();
let column = pos - line_start;
fatal!(
@@ -115,7 +117,7 @@ fn tokenize(mf: &'static MappedFile) -> Vec<&'static [u8]> {
continue;
}
let is_word_char = |b: u8| b.is_ascii_alphanumeric() || b"_.$/\\~=+[]*?-!^:".contains(&b);
let is_word_char = |b: u8| b >= 0x80 || b.is_ascii_alphanumeric() || b"_.$/\\~=+[]*?-!^:".contains(&b);
let len = match input.iter().position(|&b| !is_word_char(b)) {
Some(0) => 1,
Some(pos) => pos,
@@ -127,21 +129,21 @@ fn tokenize(mf: &'static MappedFile) -> Vec<&'static [u8]> {
tokens
}
fn is_in_sysroot<E: Arch>(ctx: &Context<E>, path: &str) -> bool {
fn is_in_sysroot<E: Arch>(ctx: &Context<E>, path: &Path) -> bool {
let mut sysroot = ctx.args.sysroot.clone();
if sysroot.starts_with('/') && !ctx.args.chroot.is_empty() {
sysroot = format!("{}/{}", ctx.args.chroot, path_clean(&sysroot));
if sysroot.is_absolute() && !ctx.args.chroot.as_os_str().is_empty() {
sysroot = ctx
.args
.chroot
.join(util::clean_path(&sysroot).strip_prefix("/").unwrap());
}
let path = std::path::Path::new(path);
let sysroot = std::path::Path::new(&sysroot);
let (Ok(path), Ok(sysroot)) = (path.canonicalize(), sysroot.canonicalize()) else {
return false;
};
path != sysroot && path.starts_with(&sysroot)
}
/// Opens a file named by the script `mf`, resolving relative names
/// against the script's directory and the library search paths.
/// Opens a file named by a script, retaining the pathname's original bytes.
fn resolve_path<E: Arch>(
ctx: &Context<E>,
rctx: &ReaderContext,
@@ -149,63 +151,53 @@ fn resolve_path<E: Arch>(
tok: &'static [u8],
check_target: bool,
) -> &'static MappedFile {
let s = String::from_utf8_lossy(unquote(tok)).into_owned();
let s = unquote(tok);
let name = Path::new(util::os_str(s));
let chroot = &ctx.args.chroot;
let open = |path: &str| -> Option<&'static MappedFile> {
let open = |path: &Path| -> Option<&'static MappedFile> {
let mf = open_file(chroot, path)?;
if check_target {
if let Some(target) = reader::get_machine_type(ctx, rctx, mf) {
if target != E::NAME {
warn!(
"{path}: skipping incompatible file: {target} (e_machine {})",
E::E_MACHINE
);
warn!("{}: skipping incompatible file: {target} (e_machine {})",
path.display(), E::E_MACHINE);
return None;
}
}
}
Some(mf)
};
let in_sysroot = |suffix: &[u8]| {
let mut path = ctx.args.sysroot.as_os_str().to_os_string();
path.push(util::os_str(suffix));
PathBuf::from(path)
};
// GNU ld prepends the sysroot if a pathname starts with '/' and the
// script being processed is in the sysroot. We do the same.
if s.starts_with('/') && is_in_sysroot(ctx, &mf.name) {
let path = format!("{}{s}", ctx.args.sysroot);
return must_open_file(chroot, &path);
// Absolute names in a script within the sysroot are relative to that root.
if name.is_absolute() && is_in_sysroot(ctx, &mf.name) {
return must_open_file(chroot, in_sysroot(s));
}
if let Some(rest) = s.strip_prefix('=') {
let path = if ctx.args.sysroot.is_empty() {
rest.to_string()
} else {
format!("{}{rest}", ctx.args.sysroot)
};
return must_open_file(chroot, &path);
if let Some(rest) = s.strip_prefix(b"=") {
return must_open_file(chroot, in_sysroot(rest));
}
if let Some(lib) = s.strip_prefix("-l") {
return reader::find_library(ctx, rctx, lib);
if let Some(lib) = s.strip_prefix(b"-l") {
return reader::find_library(ctx, rctx, util::os_str(lib));
}
if !s.starts_with('/') {
let path = path_clean(&format!("{}/../{s}", mf.name));
if !name.is_absolute() {
let path = util::clean_path(&mf.name.parent().unwrap_or(Path::new(".")).join(name));
if let Some(mf) = open(&path) {
return mf;
}
}
if let Some(mf) = open(&s) {
if let Some(mf) = open(name) {
return mf;
}
for dir in &ctx.args.library_paths {
if let Some(mf) = open(&format!("{dir}/{s}")) {
if let Some(mf) = open(&dir.join(name.strip_prefix("/").unwrap_or(name))) {
return mf;
}
}
syntax_error(mf, tok, &format!("library not found: {s}"));
syntax_error(mf, tok, &format!("library not found: {}", util::display(s)));
}
/// The target a script produces output for: the one `OUTPUT_FORMAT`
@@ -258,7 +250,7 @@ impl<'a, E: Arch> Script<'a, E> {
fn skip<'t>(&self, tok: &'t [&'static [u8]], expected: &str) -> &'t [&'static [u8]] {
match tok.first() {
None => fatal!("{}: expected '{expected}', but got EOF", self.mf.name),
None => fatal!("{}: expected '{expected}', but got EOF", self.mf.name.display()),
Some(t) if *t == expected.as_bytes() => &tok[1..],
Some(t) => self.error(t, &format!("expected '{expected}'")),
}
@@ -289,7 +281,7 @@ impl<'a, E: Arch> Script<'a, E> {
let tok = self.skip(tok, "(");
match tok.iter().position(|t| *t == b")") {
Some(pos) => &tok[pos + 1..],
None => fatal!("{}: expected ')', but got EOF", self.mf.name),
None => fatal!("{}: expected ')', but got EOF", self.mf.name.display()),
}
}
@@ -315,7 +307,7 @@ impl<'a, E: Arch> Script<'a, E> {
}
if tok.is_empty() {
fatal!("{}: expected ')', but got EOF", self.mf.name);
fatal!("{}: expected ')', but got EOF", self.mf.name.display());
}
&tok[1..]
}
@@ -417,7 +409,7 @@ impl<'a, E: Arch> Script<'a, E> {
let pattern = self.unquote_pattern(t);
self.ctx.version_patterns.push(VersionPattern {
pattern,
source: self.mf.name.clone(),
source: self.mf.name.display().to_string(),
ver_str,
ver_idx: idx,
is_cpp,
@@ -447,8 +439,7 @@ impl<'a, E: Arch> Script<'a, E> {
} else {
let idx = next_ver;
next_ver += 1;
let name = String::from_utf8_lossy(t).into_owned();
self.ctx.args.version_definitions.push(name);
self.ctx.args.version_definitions.push(t.to_vec());
tok = &tok[1..];
(t, idx)
};
@@ -503,7 +494,7 @@ impl<'a, E: Arch> Script<'a, E> {
result.push(DynamicPattern {
pattern: self.unquote_pattern(t),
source: self.mf.name.clone(),
source: self.mf.name.display().to_string(),
is_cpp,
});
tok = self.skip(&tok[1..], ";");
@@ -539,7 +530,7 @@ fn read_label<'t>(tok: &'t [&'static [u8]], label: &[u8]) -> Option<&'t [&'stati
None
}
pub fn parse_dynamic_list<E: Arch>(ctx: &mut Context<E>, path: &str) -> Vec<DynamicPattern> {
pub fn parse_dynamic_list<E: Arch>(ctx: &mut Context<E>, path: &Path) -> Vec<DynamicPattern> {
let mf = must_open_file(&ctx.args.chroot.clone(), path);
let mut rctx = ReaderContext::default();
Script::new(ctx, &mut rctx, mf).parse_dynamic_list()
+28 -19
View File
@@ -415,8 +415,8 @@ unsafe extern "C" fn add_symbols(
/// Receives an object file the plugin compiled.
unsafe extern "C" fn add_input_file<E: Arch>(path: *const c_char) -> c_int {
let ctx = &mut *(CONTEXT.load(Ordering::Acquire) as *mut Context<E>);
let path = CStr::from_ptr(path).to_string_lossy().into_owned();
let mf = must_open_file("", &path);
let path = crate::util::os_str(CStr::from_ptr(path).to_bytes());
let mf = must_open_file(std::path::Path::new(""), path);
mf.set_dependency(false);
let mut file = ObjectFile::<E>::new(mf, String::new());
@@ -696,7 +696,7 @@ fn load_plugin<E: Arch>(ctx: &Context<E>) {
if LOADED.swap(true, Ordering::Relaxed) {
return;
}
let path = CString::new(ctx.args.plugin.as_str()).unwrap();
let path = CString::new(ctx.args.plugin.as_os_str().as_encoded_bytes()).unwrap();
// SAFETY: plain dlopen/dlsym calls.
let onload: OnloadFn = unsafe {
let handle = dynamic_open(path.as_ptr());
@@ -707,7 +707,7 @@ fn load_plugin<E: Arch>(ctx: &Context<E>) {
if onload.is_null() {
fatal!(
"failed to load plugin {}: {}",
ctx.args.plugin,
ctx.args.plugin.display(),
dlerror_string()
);
}
@@ -763,7 +763,12 @@ fn load_plugin<E: Arch>(ctx: &Context<E>) {
LDPT_ADD_INPUT_LIBRARY,
func(add_input_library as *const () as usize),
),
TagValue::ptr(LDPT_OUTPUT_NAME, cstr(&ctx.args.output)),
TagValue::ptr(
LDPT_OUTPUT_NAME,
CString::new(ctx.args.output.as_os_str().as_encoded_bytes())
.unwrap()
.into_raw() as *const c_void,
),
TagValue::ptr(
LDPT_SET_EXTRA_LIBRARY_PATH,
func(set_extra_library_path as *const () as usize),
@@ -843,7 +848,12 @@ fn load_plugin<E: Arch>(ctx: &Context<E>) {
/// Returns true if a given linker plugin looks like LLVM's one.
/// Returns false if it's GCC.
fn is_llvm<E: Arch>(ctx: &Context<E>) -> bool {
ctx.args.plugin.contains("LLVMgold.")
ctx.args
.plugin
.as_os_str()
.as_encoded_bytes()
.windows(9)
.any(|s| s == b"LLVMgold.")
}
/// Returns true if a given linker plugin supports the get_symbols_v3 API.
@@ -858,9 +868,9 @@ fn supports_v3_api<E: Arch>(ctx: &Context<E>) -> bool {
fn plugin_input_file(mf: &'static MappedFile) -> (PluginInputFile, File) {
let container = mf.parent.unwrap_or(mf);
let file = File::open(&container.name)
.unwrap_or_else(|e| fatal!("cannot open {}: {e}", container.name));
.unwrap_or_else(|e| fatal!("cannot open {}: {e}", container.name.display()));
let input = PluginInputFile {
name: CString::new(container.name.as_str()).unwrap().into_raw(),
name: CString::new(container.name.as_os_str().as_encoded_bytes()).unwrap().into_raw(),
#[cfg(not(windows))]
fd: file.as_raw_fd(),
#[cfg(windows)]
@@ -879,11 +889,11 @@ pub fn read_lto_object<E: Arch>(
mf: &'static MappedFile,
archive_name: String,
) -> Option<ObjectFile<E>> {
if ctx.args.plugin.is_empty() {
if ctx.args.plugin.as_os_str().is_empty() {
fatal!("{}: unable to handle this LTO object file because the -plugin option was not provided. \
Please make sure you added -flto not only when creating object files but also when linking \
the final executable.",
mf.name
mf.name.display()
);
}
load_plugin(ctx);
@@ -903,7 +913,7 @@ pub fn read_lto_object<E: Arch>(
if mf.parent.is_none() && mf.thin_parent.is_none() {
fatal!("{}: not claimed by the LTO plugin; please make sure you are using the same compiler of the \
same version for all object files",
mf.name
mf.name.display()
);
}
return None;
@@ -952,16 +962,15 @@ pub fn read_lto_object<E: Arch>(
///
/// This is an ugly hack and should be removed once GCC adopts the v3 API.
fn restart_process<E: Arch>(ctx: &Context<E>) -> ! {
let mut args: Vec<String> = ctx.cmdline_args.clone();
let mut args = ctx.cmdline_args.clone();
for file in &ctx.objs {
if file.is_lto_input && !file.base.is_reachable() {
args.push(format!(
"--:ignore-ir-file={}",
file.base.mf.unwrap().identifier()
));
let mut arg = std::ffi::OsString::from("--:ignore-ir-file=");
arg.push(file.base.mf.unwrap().identifier());
args.push(arg);
}
}
args.push("--:lto-pass2".to_string());
args.push("--:lto-pass2".into());
let _ = std::io::Write::flush(&mut std::io::stdout());
let _ = std::io::Write::flush(&mut std::io::stderr());
@@ -970,10 +979,10 @@ fn restart_process<E: Arch>(ctx: &Context<E>) -> ! {
let err = std::process::Command::new(path).args(&args[1..]).exec();
#[cfg(windows)]
let err = {
let path = CString::new(path.to_string_lossy().as_bytes()).unwrap();
let path = CString::new(path.as_os_str().as_encoded_bytes()).unwrap();
let args: Vec<CString> = args
.iter()
.map(|arg| CString::new(arg.as_bytes()).unwrap())
.map(|arg| CString::new(arg.as_encoded_bytes()).unwrap())
.collect();
let mut argv: Vec<*const c_char> = args.iter().map(|arg| arg.as_ptr()).collect();
argv.push(ptr::null());
+2 -2
View File
@@ -89,9 +89,9 @@ pub fn print_map<E: Arch>(ctx: &Context<E>) {
}
}
if !ctx.args.map.is_empty() && ctx.args.map != "-" {
if !ctx.args.map.as_os_str().is_empty() && ctx.args.map != std::path::Path::new("-") {
std::fs::write(&ctx.args.map, out)
.unwrap_or_else(|e| fatal!("--print-map: cannot open {}: {e}", ctx.args.map));
.unwrap_or_else(|e| fatal!("--print-map: cannot open {}: {e}", ctx.args.map.display()));
} else {
let _ = std::io::stdout().write_all(out.as_bytes());
}
+39 -32
View File
@@ -11,7 +11,7 @@ use std::borrow::Cow;
use std::fs::File;
use std::io::{self, Read};
use std::ops::Range;
use std::path::Path;
use std::path::{Path, PathBuf};
use std::ptr::NonNull;
use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::Mutex;
@@ -106,7 +106,7 @@ impl MappedBytes {
// memory. Either way, its contents are accessible through `data`.
#[derive(Debug)]
pub struct MappedFile {
pub name: String,
pub name: PathBuf,
pub(crate) data: MappedBytes,
/// False if the file was found by searching library paths (`-l`),
@@ -129,12 +129,13 @@ unsafe impl Send for MappedFile {}
unsafe impl Sync for MappedFile {}
impl MappedFile {
fn open_impl(path: &str) -> io::Result<&'static MappedFile> {
fn open_impl(path: &Path) -> io::Result<&'static MappedFile> {
let file = File::open(path)?;
let display = path.display();
let metadata = file
.metadata()
.unwrap_or_else(|e| fatal!("{path}: fstat failed: {e}"));
.unwrap_or_else(|e| fatal!("{display}: fstat failed: {e}"));
let size = metadata.len();
// True if `data` is a memory mapping of the file rather than a copy of
@@ -147,9 +148,9 @@ impl MappedFile {
(&file)
.take(size)
.read_to_end(&mut buf)
.unwrap_or_else(|e| fatal!("{path}: read failed: {e}"));
.unwrap_or_else(|e| fatal!("{display}: read failed: {e}"));
if buf.len() as u64 != size {
fatal!("{path}: file is shorter than its reported size");
fatal!("{display}: file is shorter than its reported size");
}
MappedBytes::from_mut(Vec::leak(buf))
} else {
@@ -161,15 +162,15 @@ impl MappedFile {
// process. Input files are not expected to change while the
// linker runs.
let map_len = usize::try_from(size)
.unwrap_or_else(|_| fatal!("{path}: file is too large to map"));
.unwrap_or_else(|_| fatal!("{display}: file is too large to map"));
let map = unsafe { memmap2::MmapOptions::new().len(map_len).map_copy(&file) }
.unwrap_or_else(|e| fatal!("{path}: mmap failed: {e}"));
.unwrap_or_else(|e| fatal!("{display}: mmap failed: {e}"));
is_mmapped = true;
MappedBytes::from_mut(Box::leak(Box::new(map)).as_mut())
};
let mf = util::leak(MappedFile {
name: path.to_string(),
name: path.to_path_buf(),
data,
given_fullpath: true,
parent: None,
@@ -184,21 +185,23 @@ impl MappedFile {
}
/// Opens a file, returning `None` if it doesn't exist.
pub fn open(path: &str) -> Option<&'static MappedFile> {
pub fn open(path: impl AsRef<Path>) -> Option<&'static MappedFile> {
let path = path.as_ref();
match Self::open_impl(path) {
Ok(mf) => Some(mf),
Err(e) if e.kind() == io::ErrorKind::NotFound => None,
Err(e) => fatal!("opening {path} failed: {e}"),
Err(e) => fatal!("opening {} failed: {e}", path.display()),
}
}
/// Opens a file that must exist.
pub fn must_open(path: &str) -> &'static MappedFile {
Self::open_impl(path).unwrap_or_else(|e| fatal!("cannot open {path}: {e}"))
pub fn must_open(path: impl AsRef<Path>) -> &'static MappedFile {
let path = path.as_ref();
Self::open_impl(path).unwrap_or_else(|e| fatal!("cannot open {}: {e}", path.display()))
}
/// Returns a view of a member of this archive.
pub fn slice(&'static self, name: String, start: usize, size: usize) -> &'static MappedFile {
pub fn slice(&'static self, name: PathBuf, start: usize, size: usize) -> &'static MappedFile {
let mf = util::leak(MappedFile {
name,
data: self.data.slice(start, size),
@@ -255,18 +258,21 @@ impl MappedFile {
// Returns a string that uniquely identify a file that is possibly
// in an archive.
pub fn identifier(&self) -> String {
pub fn identifier(&self) -> std::ffi::OsString {
if let Some(parent) = self.parent {
// We use the file offset within an archive as an identifier
// because archive members may have the same name.
return format!("{}:{}", parent.name, self.offset());
// Archive members may have the same name, so use the file offset.
let mut name = parent.name.as_os_str().to_os_string();
name.push(format!(":{}", self.offset()));
return name;
}
if let Some(thin_parent) = self.thin_parent {
// If this is a thin archive member, the filename part is
// guaranteed to be unique.
return format!("{}:{}", thin_parent.name, self.name);
if let Some(parent) = self.thin_parent {
// Thin archive members have unique filenames.
let mut name = parent.name.as_os_str().to_os_string();
name.push(":");
name.push(&self.name);
return name;
}
self.name.clone()
self.name.as_os_str().to_os_string()
}
pub fn is_dependency(&self) -> bool {
@@ -278,26 +284,27 @@ impl MappedFile {
}
}
fn apply_chroot<'a>(chroot: &str, path: &'a str) -> Cow<'a, str> {
if path.starts_with('/') && !chroot.is_empty() {
Cow::Owned(format!("{chroot}/{}", util::path_clean(path)))
fn apply_chroot<'a>(chroot: &Path, path: &'a Path) -> Cow<'a, Path> {
if path.is_absolute() && !chroot.as_os_str().is_empty() {
Cow::Owned(chroot.join(util::clean_path(path).strip_prefix("/").unwrap_or(path)))
} else {
Cow::Borrowed(path)
}
}
/// Opens an input file, applying `--chroot` to absolute paths.
pub fn open_file(chroot: &str, path: &str) -> Option<&'static MappedFile> {
MappedFile::open(&apply_chroot(chroot, path))
pub fn open_file(chroot: &Path, path: impl AsRef<Path>) -> Option<&'static MappedFile> {
MappedFile::open(apply_chroot(chroot, path.as_ref()))
}
/// Opens an input file that must exist, applying `--chroot` to absolute paths.
pub fn must_open_file(chroot: &str, path: &str) -> &'static MappedFile {
pub fn must_open_file(chroot: &Path, path: impl AsRef<Path>) -> &'static MappedFile {
let path = path.as_ref();
MappedFile::open_impl(&apply_chroot(chroot, path))
.unwrap_or_else(|e| fatal!("cannot open {path}: {e}"))
.unwrap_or_else(|e| fatal!("cannot open {}: {e}", path.display()))
}
/// Whether a path refers to something that is not a directory.
pub fn is_file(path: &str) -> bool {
Path::new(path).is_file()
pub fn is_file(path: impl AsRef<Path>) -> bool {
path.as_ref().is_file()
}
+21 -20
View File
@@ -89,7 +89,7 @@ enum Storage {
/// An output file, either memory-mapped or buffered in memory.
pub struct OutputFile {
path: String,
path: PathBuf,
tmp_path: Option<PathBuf>,
/// The file being written, if it is a real file.
file: Option<File>,
@@ -176,11 +176,12 @@ impl OutputFile {
/// isn't modified underneath the kernel. Anything else — a device, a
/// pipe, or standard output — is assembled in memory and written out
/// at the end.
pub fn open(path: &str, size: u64, perm: u32, overwrite_in_place: bool) -> OutputFile {
let is_special = path == "-" || std::fs::metadata(path).is_ok_and(|m| !m.is_file());
pub fn open(path: &Path, size: u64, perm: u32, overwrite_in_place: bool) -> OutputFile {
let is_special =
path == Path::new("-") || std::fs::metadata(path).is_ok_and(|m| !m.is_file());
if is_special {
return OutputFile {
path: path.to_string(),
path: path.to_path_buf(),
tmp_path: None,
file: None,
storage: Storage::Memory(vec![0; size as usize]),
@@ -192,10 +193,10 @@ impl OutputFile {
.parent()
.filter(|p| !p.as_os_str().is_empty())
.unwrap_or(Path::new("."));
let name = Path::new(path)
.file_name()
.map_or(String::new(), |n| n.to_string_lossy().into_owned());
let tmp = dir.join(format!(".{name}.{}", std::process::id()));
let mut name = std::ffi::OsString::from(".");
name.push(path.file_name().unwrap_or_default());
name.push(format!(".{}", std::process::id()));
let tmp = dir.join(name);
// Reuse an existing file if exists and writable because on Linux,
// writing to an existing file is much faster than creating a fresh
@@ -236,7 +237,7 @@ impl OutputFile {
let storage = map_file(&file, size);
let output = OutputFile {
path: path.to_string(),
path: path.to_path_buf(),
tmp_path: Some(tmp),
file: Some(file),
storage,
@@ -253,13 +254,13 @@ impl OutputFile {
/// stale one is not picked up by accident; [`Self::resize`] gives it its
/// size.
#[cfg(not(windows))]
pub fn open_locked(path: &str, perm: u32) -> OutputFile {
pub fn open_locked(path: &Path, perm: u32) -> OutputFile {
let mut file = open_options(perm)
.read(true)
.write(true)
.create(true)
.open(path)
.unwrap_or_else(|e| fatal!("cannot open {path}: {e}"));
.unwrap_or_else(|e| fatal!("cannot open {}: {e}", path.display()));
// SAFETY: flock on a valid descriptor.
unsafe {
libc::flock(file.as_raw_fd(), libc::LOCK_EX);
@@ -268,9 +269,9 @@ impl OutputFile {
// make the file unusable so that gdb won't use it by accident until
// it's ready.
file.write_all(&[0; 256])
.unwrap_or_else(|e| fatal!("{path}: write failed: {e}"));
.unwrap_or_else(|e| fatal!("{}: write failed: {e}", path.display()));
OutputFile {
path: path.to_string(),
path: path.to_path_buf(),
tmp_path: None,
file: Some(file),
storage: Storage::Memory(Vec::new()),
@@ -279,7 +280,7 @@ impl OutputFile {
}
#[cfg(windows)]
pub fn open_locked(_path: &str, _perm: u32) -> OutputFile {
pub fn open_locked(_path: &Path, _perm: u32) -> OutputFile {
fatal!("LockingOutputFile is not supported on Windows");
}
@@ -290,7 +291,7 @@ impl OutputFile {
.as_ref()
.expect("resizing an output file that isn't a file");
file.set_len(size)
.unwrap_or_else(|e| fatal!("{}: ftruncate failed: {e}", self.path));
.unwrap_or_else(|e| fatal!("{}: ftruncate failed: {e}", self.path.display()));
// Reserve twice as much address space as the file needs so that
// extend() can grow it into the mapping in place.
self.storage = map_file(file, size);
@@ -328,7 +329,7 @@ impl OutputFile {
(Storage::Memory(vec), _) => vec.resize(new_len, 0),
(Storage::Mmap { map, len }, Some(file)) => {
file.set_len(new_len as u64)
.unwrap_or_else(|e| fatal!("{}: ftruncate failed: {e}", self.path));
.unwrap_or_else(|e| fatal!("{}: ftruncate failed: {e}", self.path.display()));
preallocate(file, new_len as u64);
if new_len <= map.len() {
*len = new_len;
@@ -352,7 +353,7 @@ impl OutputFile {
match self.storage {
Storage::Mmap { map, .. } => drop(map),
Storage::Memory(vec) => {
if self.path == "-" {
if self.path == Path::new("-") {
let mut stdout = std::io::stdout().lock();
stdout
.write_all(&vec)
@@ -376,10 +377,10 @@ impl OutputFile {
.create(true)
.truncate(true)
.open(&self.path)
.unwrap_or_else(|e| fatal!("cannot open {}: {e}", self.path)),
.unwrap_or_else(|e| fatal!("cannot open {}: {e}", self.path.display())),
};
file.write_all(&vec)
.unwrap_or_else(|e| fatal!("{}: write failed: {e}", self.path));
.unwrap_or_else(|e| fatal!("{}: write failed: {e}", self.path.display()));
}
}
if let Some(tmp) = self.tmp_path {
@@ -392,7 +393,7 @@ impl OutputFile {
std::mem::forget(old);
}
std::fs::rename(&tmp, &self.path).unwrap_or_else(|e| {
fatal!("cannot rename {} to {}: {e}", tmp.display(), self.path)
fatal!("cannot rename {} to {}: {e}", tmp.display(), self.path.display())
});
*TMPFILE.lock().unwrap() = None;
}
+62 -43
View File
@@ -127,7 +127,7 @@ pub fn create_synthetic_sections<E: Arch>(ctx: &mut Context<E>) {
ctx.shstrtab = Some(ShstrtabSection::new());
chunks.push(ChunkId::Shstrtab);
}
if !ctx.args.dynamic_linker.is_empty() {
if !ctx.args.dynamic_linker.as_os_str().is_empty() {
ctx.interp = Some(InterpSection::new());
chunks.push(ChunkId::Interp);
}
@@ -163,7 +163,7 @@ pub fn create_synthetic_sections<E: Arch>(ctx: &mut Context<E>) {
ctx.eh_frame_reloc = Some(EhFrameRelocSection::<E>::new());
chunks.push(ChunkId::EhFrameReloc);
}
if !ctx.args.separate_debug_file.is_empty() {
if !ctx.args.separate_debug_file.as_os_str().is_empty() {
ctx.gnu_debuglink = Some(GnuDebuglinkSection::new());
chunks.push(ChunkId::GnuDebuglink);
}
@@ -1842,23 +1842,32 @@ pub fn print_dependencies<E: Arch>(ctx: &Context<E>) {
}
}
fn create_response_file<E: Arch>(ctx: &Context<E>) -> String {
let cwd = std::env::current_dir()
.map(|p| p.to_string_lossy().into_owned())
.unwrap_or_default();
let mut out = format!("-C {}\n", cwd.strip_prefix('/').unwrap_or(&cwd));
if cwd != "/" {
out.push_str("--chroot ..");
let depth = cwd.matches('/').count();
for _ in 1..depth {
out.push_str("/..");
fn create_response_file<E: Arch>(ctx: &Context<E>) -> Vec<u8> {
fn argument(out: &mut Vec<u8>, bytes: &[u8]) {
out.push(b'"');
for &byte in bytes {
if byte == b'"' || byte == b'\\' {
out.push(b'\\');
}
out.push(byte);
}
out.push('\n');
out.extend_from_slice(b"\"\n");
}
let cwd = std::env::current_dir().unwrap_or_default();
let bytes = cwd.as_os_str().as_encoded_bytes();
let mut out = b"-C\n".to_vec();
argument(&mut out, bytes.strip_prefix(b"/").unwrap_or(bytes));
if bytes != b"/" {
out.extend_from_slice(b"--chroot ..");
for _ in 1..bytes.iter().filter(|&&b| b == b'/').count() {
out.extend_from_slice(b"/..");
}
out.push(b'\n');
}
for arg in &ctx.cmdline_args[1..] {
if arg != "-repro" && arg != "--repro" {
out.push_str(arg);
out.push('\n');
argument(&mut out, arg.as_encoded_bytes());
}
}
out
@@ -1866,36 +1875,43 @@ fn create_response_file<E: Arch>(ctx: &Context<E>) -> String {
pub fn write_repro_file<E: Arch>(ctx: &Context<E>) {
let _t = ctx.timer("write_repro_file");
let path = format!("{}.repro.tar", ctx.args.output);
let basedir = format!("{}.repro", path_filename(&ctx.args.output));
let mut name = ctx.args.output.as_os_str().to_os_string();
name.push(".repro.tar");
let path = std::path::PathBuf::from(name);
let mut basedir = ctx
.args
.output
.file_name()
.unwrap_or_default()
.to_os_string();
basedir.push(".repro");
let mut tar = crate::util::tar::TarWriter::open(&path, &basedir)
.unwrap_or_else(|e| fatal!("cannot open {path}: {e}"));
.unwrap_or_else(|e| fatal!("cannot open {}: {e}", path.display()));
let write = |tar: &mut crate::util::tar::TarWriter, name: &str, data: &[u8]| {
let write = |tar: &mut crate::util::tar::TarWriter, name: &std::path::Path, data: &[u8]| {
tar.append(name, data)
.unwrap_or_else(|e| fatal!("{path}: write failed: {e}"));
.unwrap_or_else(|e| fatal!("{}: write failed: {e}", path.display()));
};
write(
&mut tar,
"response.txt",
create_response_file(ctx).as_bytes(),
std::path::Path::new("response.txt"),
&create_response_file(ctx),
);
write(
&mut tar,
"version.txt",
std::path::Path::new("version.txt"),
format!("{}\n", crate::cmdline::VERSION).as_bytes(),
);
let mut seen: HashSet<String> = HashSet::new();
let mut seen = HashSet::new();
for mf in crate::mapped_file::file_pool() {
if mf.parent.is_none() && seen.insert(mf.name.clone()) {
// We reopen a file because we may have modified the contents of mf
// in memory, which is mapped with PROT_WRITE and MAP_PRIVATE.
// Reopen the original contents because private mappings may have
// been modified. Preserve the symlink name used in response.txt.
let reopened = crate::mapped_file::must_open_file(&ctx.args.chroot, &mf.name);
// Keep the name used by response.txt, including symlink components.
let abs = std::path::absolute(&mf.name)
.unwrap_or_else(|e| fatal!("{}: cannot get absolute path: {e}", mf.name));
write(&mut tar, &abs.to_string_lossy(), reopened.data());
.unwrap_or_else(|e| fatal!("{}: cannot get absolute path: {e}", mf.name.display()));
write(&mut tar, &abs, reopened.data());
}
}
}
@@ -2479,8 +2495,8 @@ pub fn add_dynamic_strings<E: Arch>(ctx: &mut Context<E>) {
strings.extend(ctx.args.filter.iter().map(|s| s.clone().into_bytes()));
strings.push(ctx.args.audit.clone().into_bytes());
strings.push(ctx.args.depaudit.clone().into_bytes());
strings.push(ctx.args.rpaths.clone().into_bytes());
strings.push(ctx.args.soname.clone().into_bytes());
strings.push(ctx.args.rpaths.as_encoded_bytes().to_vec());
strings.push(ctx.args.soname.as_encoded_bytes().to_vec());
for s in strings {
if !s.is_empty() {
ctx.dynstr.add_string(&s);
@@ -3174,7 +3190,7 @@ pub fn parse_symbol_version<E: Arch>(ctx: &mut Context<E>) {
.enumerate()
.map(|(i, v)| {
(
v.clone().into_bytes(),
v.clone(),
i as u16 + VER_NDX_LAST_RESERVED as u16 + 1,
)
})
@@ -4647,33 +4663,36 @@ pub fn write_separate_debug_file<E: Arch>(ctx: &mut Context<E>) {
// Write Makefile-style dependency rules to a file specified by
// --dependency-file. This is analogous to the compiler's -M flag.
pub fn write_dependency_file<E: Arch>(ctx: &Context<E>) {
let mut deps: Vec<String> = Vec::new();
let mut seen: HashSet<String> = HashSet::new();
let mut deps = Vec::new();
let mut seen = HashSet::new();
for mf in crate::mapped_file::file_pool() {
if mf.is_dependency() && mf.parent.is_none() {
let path = crate::util::path_clean(&mf.name);
let path = crate::util::clean_path(&mf.name);
if seen.insert(path.clone()) {
deps.push(path);
}
}
}
let mut out = format!("{}:", ctx.args.output);
let mut out = ctx.args.output.as_os_str().as_encoded_bytes().to_vec();
out.push(b':');
for d in &deps {
out.push(' ');
out.push_str(d);
out.push(b' ');
out.extend_from_slice(d.as_os_str().as_encoded_bytes());
}
out.push('\n');
out.push(b'\n');
for d in &deps {
out.push_str(&format!("\n{d}:\n"));
out.push(b'\n');
out.extend_from_slice(d.as_os_str().as_encoded_bytes());
out.extend_from_slice(b":\n");
}
let path = &ctx.args.dependency_file;
if path == "-" {
let _ = std::io::stdout().write_all(out.as_bytes());
if path == std::path::Path::new("-") {
let _ = std::io::stdout().write_all(&out);
} else {
std::fs::write(path, out)
.unwrap_or_else(|e| fatal!("--dependency-file: cannot open {path}: {e}"));
.unwrap_or_else(|e| fatal!("--dependency-file: cannot open {}: {e}", path.display()));
}
}
+35 -30
View File
@@ -95,11 +95,11 @@ fn new_object_file<E: Arch>(
let target = filetype::get_machine_type(&ctx.args.plugin, mf, || None);
match target {
None => fatal!("{}: unknown machine type", mf.name),
None => fatal!("{}: unknown machine type", mf.name.display()),
Some(t) if t != ctx.args.emulation => {
fatal!(
"{}: incompatible file type: {} is expected but got {t}",
mf.name,
mf.name.display(),
ctx.args.emulation
)
}
@@ -117,15 +117,15 @@ fn new_shared_file<E: Arch>(
mf: &'static MappedFile,
) -> SharedFile<E> {
if rctx.is_static {
fatal!("{}: attempted static link of a dynamic object", mf.name);
fatal!("{}: attempted static link of a dynamic object", mf.name.display());
}
let target = filetype::get_machine_type(&ctx.args.plugin, mf, || None);
match target {
None => fatal!("{}: unknown machine type", mf.name),
None => fatal!("{}: unknown machine type", mf.name.display()),
Some(t) if t != ctx.args.emulation => {
fatal!(
"{}: incompatible file type: {} is expected but got {t}",
mf.name,
mf.name.display(),
ctx.args.emulation
)
}
@@ -195,7 +195,7 @@ fn read_archive_member<E: Arch>(
FileType::ElfDso => {
warn!(
"{archive_name}({}): shared object file in an archive is ignored",
mf.name
mf.name.display()
);
None
}
@@ -223,7 +223,7 @@ pub fn read_file<E: Arch>(ctx: &mut Context<E>, rctx: &mut ReaderContext, mf: &'
FileType::Ar | FileType::ThinAr => {
for child in archive_file::read_archive_members(mf) {
let child_rctx = rctx.next_child();
if let Some(loaded) = read_archive_member(ctx, &child_rctx, child, &mf.name) {
if let Some(loaded) = read_archive_member(ctx, &child_rctx, child, &mf.name.to_string_lossy()) {
push_loaded(ctx, loaded);
}
}
@@ -232,7 +232,7 @@ pub fn read_file<E: Arch>(ctx: &mut Context<E>, rctx: &mut ReaderContext, mf: &'
FileType::GccLtoObj | FileType::LlvmBitcode => {
defer_lto_object(ctx, rctx, mf, "");
}
_ => fatal!("{}: unknown file type", mf.name),
_ => fatal!("{}: unknown file type", mf.name.display()),
}
}
@@ -281,14 +281,14 @@ pub fn detect_machine_type<E: Arch>(ctx: &mut Context<E>, jobs: &[ReaderJob]) ->
fn open_library<E: Arch>(
ctx: &Context<E>,
rctx: &ReaderContext,
path: &str,
path: &std::path::Path,
) -> Option<&'static MappedFile> {
let mf = open_file(&ctx.args.chroot, path)?;
if let Some(target) = get_machine_type(ctx, rctx, mf) {
if target != E::NAME {
warn!(
"{path}: skipping incompatible file: {target} (e_machine {})",
E::E_MACHINE
"{}: skipping incompatible file: {target} (e_machine {})",
path.display(), E::E_MACHINE
);
return None;
}
@@ -300,29 +300,34 @@ fn open_library<E: Arch>(
pub fn find_library<E: Arch>(
ctx: &Context<E>,
rctx: &ReaderContext,
name: &str,
name: &std::ffi::OsStr,
) -> &'static MappedFile {
if let Some(name) = name.strip_prefix(':') {
if let Some(exact) = name.as_encoded_bytes().strip_prefix(b":") {
let exact = std::path::Path::new(crate::util::os_str(exact));
let exact = exact.strip_prefix("/").unwrap_or(exact);
for dir in &ctx.args.library_paths {
if let Some(mf) = open_library(ctx, rctx, &format!("{dir}/{name}")) {
if let Some(mf) = open_library(ctx, rctx, &dir.join(exact)) {
return mf;
}
}
fatal!("library not found: :{name}");
}
for dir in &ctx.args.library_paths {
let stem = format!("{dir}/lib{name}");
if !rctx.is_static {
if let Some(mf) = open_library(ctx, rctx, &format!("{stem}.so")) {
return mf;
} else {
let mut stem = std::ffi::OsString::from("lib");
stem.push(name);
for dir in &ctx.args.library_paths {
for suffix in if rctx.is_static {
&[".a"][..]
} else {
&[".so", ".a"][..]
} {
let mut filename = stem.clone();
filename.push(suffix);
if let Some(mf) = open_library(ctx, rctx, &dir.join(filename)) {
return mf;
}
}
}
if let Some(mf) = open_library(ctx, rctx, &format!("{stem}.a")) {
return mf;
}
}
fatal!("library not found: {name}");
fatal!("library not found: {}", name.to_string_lossy());
}
// Reads all input files.
@@ -375,7 +380,7 @@ pub fn read_input_files<E: Arch>(ctx: &mut Context<E>, jobs: Vec<ReaderJob>) {
// Everything else is a command line argument that we need to open.
let mf = if job.is_lib {
let mf = find_library(ctx_ref, &rctx, &job.name);
let mf = find_library(ctx_ref, &rctx, job.name.as_os_str());
crate::util::leak(MappedFile {
name: mf.name.clone(),
data: mf.data,
@@ -396,7 +401,7 @@ pub fn read_input_files<E: Arch>(ctx: &mut Context<E>, jobs: Vec<ReaderJob>) {
FileType::Ar => {
for child in archive_file::read_fat_archive_members(mf) {
let child_rctx = rctx.next_child();
let archive_name = mf.name.clone();
let archive_name = mf.name.to_string_lossy().into_owned();
let loaded = &loaded;
scope.spawn(move |_| {
if let Some(file) =
@@ -410,7 +415,7 @@ pub fn read_input_files<E: Arch>(ctx: &mut Context<E>, jobs: Vec<ReaderJob>) {
FileType::ThinAr => {
for path in archive_file::get_thin_archive_member_paths(mf) {
let child_rctx = rctx.next_child();
let archive_name = mf.name.clone();
let archive_name = mf.name.to_string_lossy().into_owned();
let loaded = &loaded;
scope.spawn(move |_| {
let child = must_open_file(&ctx_ref.args.chroot, &path);
@@ -446,7 +451,7 @@ pub fn read_input_files<E: Arch>(ctx: &mut Context<E>, jobs: Vec<ReaderJob>) {
FileType::GccLtoObj | FileType::LlvmBitcode => {
defer_lto_object(ctx_ref, &rctx, mf, "");
}
_ => fatal!("{}: unknown file type", mf.name),
_ => fatal!("{}: unknown file type", mf.name.display()),
}
});
});
+6 -4
View File
@@ -138,7 +138,7 @@ pub fn install_signal_handler() {}
/// `mold -run COMMAND ARGS...` runs a command with mold interposed as the
/// linker, which requires the `mold-wrapper.so` preload library.
#[cfg(not(windows))]
pub fn process_run_subcommand(argv: &[String]) -> ! {
pub fn process_run_subcommand(argv: &[std::ffi::OsString]) -> ! {
if argv.len() < 3 {
fatal!("-run: argument missing");
}
@@ -166,7 +166,9 @@ pub fn process_run_subcommand(argv: &[String]) -> ! {
std::env::set_var("MOLD_PATH", &self_path);
use std::os::unix::process::CommandExt;
let cmd = crate::util::path_filename(&argv[2]);
let cmd = std::path::Path::new(&argv[2])
.file_name()
.unwrap_or_default();
// If ld, ld.lld or ld.gold is specified, run mold instead
let err = if cmd == "ld" || cmd == "ld.lld" || cmd == "ld.gold" {
std::process::Command::new(&self_path)
@@ -176,10 +178,10 @@ pub fn process_run_subcommand(argv: &[String]) -> ! {
std::process::Command::new(&argv[2]).args(&argv[3..]).exec()
};
// Execute a given command
fatal!("mold -run failed: {}: {err}", argv[2]);
fatal!("mold -run failed: {}: {err}", argv[2].to_string_lossy());
}
#[cfg(windows)]
pub fn process_run_subcommand(_argv: &[String]) -> ! {
pub fn process_run_subcommand(_argv: &[std::ffi::OsString]) -> ! {
fatal!("-run is supported only on Unix");
}
+21 -6
View File
@@ -245,9 +245,25 @@ pub fn leak_bytes(bytes: Vec<u8>) -> &'static [u8] {
/// Normalizes a path lexically, resolving `.` and `..` components without
/// consulting the file system.
pub fn path_clean(path: &str) -> String {
use std::path::{Component, Path, PathBuf};
clean_path(std::path::Path::new(path))
.to_string_lossy()
.into_owned()
}
/// Converts bytes from a response file or linker script to an OS string.
/// Unix paths can contain arbitrary non-NUL bytes.
pub fn os_str(bytes: &[u8]) -> &std::ffi::OsStr {
use bstr::ByteSlice;
bytes
.to_os_str()
.unwrap_or_else(|_| crate::fatal!("invalid OS string: {}", display(bytes)))
}
/// Normalizes an OS path without resolving symlinks.
pub fn clean_path(path: &std::path::Path) -> std::path::PathBuf {
use std::path::{Component, PathBuf};
let mut out = PathBuf::new();
for component in Path::new(path).components() {
for component in path.components() {
match component {
Component::CurDir => {}
Component::ParentDir => match out.components().next_back() {
@@ -260,11 +276,10 @@ pub fn path_clean(path: &str) -> String {
other => out.push(other),
}
}
let s = out.to_string_lossy().into_owned();
if s.is_empty() {
".".to_string()
if out.as_os_str().is_empty() {
PathBuf::from(".")
} else {
s
out
}
}
+20 -10
View File
@@ -3,7 +3,10 @@
use std::fs::File;
use std::io::{self, Seek, SeekFrom, Write};
use super::{align_to, path_clean};
use std::ffi::{OsStr, OsString};
use std::path::Path;
use super::{align_to, clean_path};
const BLOCK_SIZE: u64 = 512;
@@ -23,7 +26,7 @@ const BLOCK_SIZE: u64 = 512;
/// For simplicity, we always emit a PAX header even for a short filename.
pub struct TarWriter {
out: File,
basedir: String,
basedir: OsString,
}
fn ustar_header(name: &[u8], mode: &[u8], size: u64, typeflag: u8) -> [u8; 512] {
@@ -48,29 +51,36 @@ fn ustar_header(name: &[u8], mode: &[u8], size: u64, typeflag: u8) -> [u8; 512]
/// Construct a string which contains something like
/// "16 path=foo/bar\n" where 16 is the size of the string
/// including the size string itself.
fn encode_path(basedir: &str, path: &str) -> String {
let path = path_clean(&format!("{basedir}/{path}"));
let len = " path=\n".len() + path.len();
fn encode_path(basedir: &OsStr, path: &Path) -> Vec<u8> {
let mut name = basedir.to_os_string();
name.push("/");
name.push(path);
let path = clean_path(Path::new(&name));
let bytes = path.as_os_str().as_encoded_bytes();
let len = b" path=\n".len() + bytes.len();
let total = len + len.to_string().len();
let total = len + total.to_string().len();
format!("{total} path={path}\n")
let mut out = format!("{total} path=").into_bytes();
out.extend_from_slice(bytes);
out.push(b'\n');
out
}
impl TarWriter {
pub fn open(output_path: &str, basedir: &str) -> io::Result<TarWriter> {
pub fn open(output_path: &Path, basedir: &OsStr) -> io::Result<TarWriter> {
Ok(TarWriter {
out: File::create(output_path)?,
basedir: basedir.to_string(),
basedir: basedir.to_os_string(),
})
}
pub fn append(&mut self, path: &str, data: &[u8]) -> io::Result<()> {
pub fn append(&mut self, path: &Path, data: &[u8]) -> io::Result<()> {
let attr = encode_path(&self.basedir, path);
// Write PAX header
self.out
.write_all(&ustar_header(b"/", b"", attr.len() as u64, b'x'))?;
// Write pathname
self.out.write_all(attr.as_bytes())?;
self.out.write_all(&attr)?;
self.pad()?;
// Write Ustar header
+1 -1
View File
@@ -3,6 +3,6 @@
/// Links for this target, or reports the target the inputs are actually
/// for.
pub fn link(cmdline: &[String]) -> Result<i32, String> {
pub fn link(cmdline: &[std::ffi::OsString]) -> Result<i32, String> {
mold::driver::link::<mold::arch::Arm32>(cmdline)
}
+1 -1
View File
@@ -3,6 +3,6 @@
/// Links for this target, or reports the target the inputs are actually
/// for.
pub fn link(cmdline: &[String]) -> Result<i32, String> {
pub fn link(cmdline: &[std::ffi::OsString]) -> Result<i32, String> {
mold::driver::link::<mold::arch::Arm32Be>(cmdline)
}
+1 -1
View File
@@ -3,6 +3,6 @@
/// Links for this target, or reports the target the inputs are actually
/// for.
pub fn link(cmdline: &[String]) -> Result<i32, String> {
pub fn link(cmdline: &[std::ffi::OsString]) -> Result<i32, String> {
mold::driver::link::<mold::arch::Arm64>(cmdline)
}
+1 -1
View File
@@ -3,6 +3,6 @@
/// Links for this target, or reports the target the inputs are actually
/// for.
pub fn link(cmdline: &[String]) -> Result<i32, String> {
pub fn link(cmdline: &[std::ffi::OsString]) -> Result<i32, String> {
mold::driver::link::<mold::arch::Arm64Be>(cmdline)
}
+1 -1
View File
@@ -3,6 +3,6 @@
/// Links for this target, or reports the target the inputs are actually
/// for.
pub fn link(cmdline: &[String]) -> Result<i32, String> {
pub fn link(cmdline: &[std::ffi::OsString]) -> Result<i32, String> {
mold::driver::link::<mold::arch::I386>(cmdline)
}
+1 -1
View File
@@ -3,6 +3,6 @@
/// Links for this target, or reports the target the inputs are actually
/// for.
pub fn link(cmdline: &[String]) -> Result<i32, String> {
pub fn link(cmdline: &[std::ffi::OsString]) -> Result<i32, String> {
mold::driver::link::<mold::arch::LoongArch32>(cmdline)
}
+1 -1
View File
@@ -3,6 +3,6 @@
/// Links for this target, or reports the target the inputs are actually
/// for.
pub fn link(cmdline: &[String]) -> Result<i32, String> {
pub fn link(cmdline: &[std::ffi::OsString]) -> Result<i32, String> {
mold::driver::link::<mold::arch::LoongArch64>(cmdline)
}
+1 -1
View File
@@ -3,6 +3,6 @@
/// Links for this target, or reports the target the inputs are actually
/// for.
pub fn link(cmdline: &[String]) -> Result<i32, String> {
pub fn link(cmdline: &[std::ffi::OsString]) -> Result<i32, String> {
mold::driver::link::<mold::arch::M68k>(cmdline)
}
+1 -1
View File
@@ -3,6 +3,6 @@
/// Links for this target, or reports the target the inputs are actually
/// for.
pub fn link(cmdline: &[String]) -> Result<i32, String> {
pub fn link(cmdline: &[std::ffi::OsString]) -> Result<i32, String> {
mold::driver::link::<mold::arch::Ppc32>(cmdline)
}
+1 -1
View File
@@ -3,6 +3,6 @@
/// Links for this target, or reports the target the inputs are actually
/// for.
pub fn link(cmdline: &[String]) -> Result<i32, String> {
pub fn link(cmdline: &[std::ffi::OsString]) -> Result<i32, String> {
mold::driver::link::<mold::arch::Ppc64V1>(cmdline)
}
+1 -1
View File
@@ -3,6 +3,6 @@
/// Links for this target, or reports the target the inputs are actually
/// for.
pub fn link(cmdline: &[String]) -> Result<i32, String> {
pub fn link(cmdline: &[std::ffi::OsString]) -> Result<i32, String> {
mold::driver::link::<mold::arch::Ppc64V2>(cmdline)
}
+1 -1
View File
@@ -3,6 +3,6 @@
/// Links for this target, or reports the target the inputs are actually
/// for.
pub fn link(cmdline: &[String]) -> Result<i32, String> {
pub fn link(cmdline: &[std::ffi::OsString]) -> Result<i32, String> {
mold::driver::link::<mold::arch::Riscv32>(cmdline)
}
+1 -1
View File
@@ -3,6 +3,6 @@
/// Links for this target, or reports the target the inputs are actually
/// for.
pub fn link(cmdline: &[String]) -> Result<i32, String> {
pub fn link(cmdline: &[std::ffi::OsString]) -> Result<i32, String> {
mold::driver::link::<mold::arch::Riscv32Be>(cmdline)
}
+1 -1
View File
@@ -3,6 +3,6 @@
/// Links for this target, or reports the target the inputs are actually
/// for.
pub fn link(cmdline: &[String]) -> Result<i32, String> {
pub fn link(cmdline: &[std::ffi::OsString]) -> Result<i32, String> {
mold::driver::link::<mold::arch::Riscv64>(cmdline)
}
+1 -1
View File
@@ -3,6 +3,6 @@
/// Links for this target, or reports the target the inputs are actually
/// for.
pub fn link(cmdline: &[String]) -> Result<i32, String> {
pub fn link(cmdline: &[std::ffi::OsString]) -> Result<i32, String> {
mold::driver::link::<mold::arch::Riscv64Be>(cmdline)
}
+1 -1
View File
@@ -3,6 +3,6 @@
/// Links for this target, or reports the target the inputs are actually
/// for.
pub fn link(cmdline: &[String]) -> Result<i32, String> {
pub fn link(cmdline: &[std::ffi::OsString]) -> Result<i32, String> {
mold::driver::link::<mold::arch::S390x>(cmdline)
}
+1 -1
View File
@@ -3,6 +3,6 @@
/// Links for this target, or reports the target the inputs are actually
/// for.
pub fn link(cmdline: &[String]) -> Result<i32, String> {
pub fn link(cmdline: &[std::ffi::OsString]) -> Result<i32, String> {
mold::driver::link::<mold::arch::Sh4>(cmdline)
}
+1 -1
View File
@@ -3,6 +3,6 @@
/// Links for this target, or reports the target the inputs are actually
/// for.
pub fn link(cmdline: &[String]) -> Result<i32, String> {
pub fn link(cmdline: &[std::ffi::OsString]) -> Result<i32, String> {
mold::driver::link::<mold::arch::Sh4Be>(cmdline)
}
+1 -1
View File
@@ -3,6 +3,6 @@
/// Links for this target, or reports the target the inputs are actually
/// for.
pub fn link(cmdline: &[String]) -> Result<i32, String> {
pub fn link(cmdline: &[std::ffi::OsString]) -> Result<i32, String> {
mold::driver::link::<mold::arch::Sparc64>(cmdline)
}
+1 -1
View File
@@ -3,6 +3,6 @@
/// Links for this target, or reports the target the inputs are actually
/// for.
pub fn link(cmdline: &[String]) -> Result<i32, String> {
pub fn link(cmdline: &[std::ffi::OsString]) -> Result<i32, String> {
mold::driver::link::<mold::arch::X86_64>(cmdline)
}
+40
View File
@@ -0,0 +1,40 @@
#!/usr/bin/env bash
. $(dirname $0)/common.inc
input=$'input\xff object.o'
dir=$'dir\xfe'
response=$'args\xff.rsp'
output=$'result\xfe.o'
echo 'int foo() { return 42; }' | $CC -c -o "$t/$input" -xc -
./mold -r -o $t/argv.o "$t/$input"
printf '"%s"\n' "$t/$input" > "$t/$response"
printf '"@%s"\n' "$t/$response" > $t/nested.rsp
./mold -r -o $t/response.o @$t/nested.rsp
mkdir -p "$t/$dir"
cp "$t/$input" "$t/$dir/libraw.a.o"
ar crs "$t/$dir/libraw.a" "$t/$dir/libraw.a.o"
echo '' | $CC -c -o $t/empty.o -xc -
./mold -r $t/empty.o -o $t/library.o -L"$t/$dir" --whole-archive -lraw
printf 'INPUT("%s")\n' "$input" > "$t/$dir.script"
./mold -r -o $t/script.o -T "$t/$dir.script"
ar crsT $t/thin.a "$t/$input"
./mold -r -o $t/thin.o --whole-archive $t/thin.a
printf '"--output=%s" "%s"\n' "$t/$output" "$t/$input" > $t/output.rsp
./mold -r --repro @$t/output.rsp
for file in argv.o response.o library.o script.o thin.o "$output"; do
nm "$t/$file" | grep -E ' T foo$'
done
# Replaying a reproduction must retain the raw names and spaces too.
ld=$PWD/mold
rm -rf "$t/$output.repro"
tar -C $t -xf "$t/$output.repro.tar"
(cd "$t/$output.repro"; "$ld" @response.txt)
nm "$t/$output.repro$PWD/$t/$output" | grep -E ' T foo$'