Make diagnostics process-wide

Mold performs one link per process, so keep diagnostic settings, error state,
and output serialization in a process-wide singleton. This removes
diagnostic plumbing from contexts and helper APIs while preserving the mutex
that keeps messages from worker threads from interleaving.
This commit is contained in:
Rui Ueyama committed 2026-09-02 12:27:24 +09:00
1 parent c433f96074
commit 2e731001d9
63 files changed
+635 -1109

No files matched your search

+21 -23
View File
@@ -3,8 +3,6 @@
//! that the compiler can build them in parallel, and a feature per target
//! decides which of them are built in.
use mold::error::Diagnostics;
// Including mimalloc-new-delete.h overrides the new/delete operators.
// We need it only when using mimalloc as a dynamic library.
// This header should be included in only one source file, so we do
@@ -27,48 +25,48 @@ static GLOBAL: mimalloc::MiMalloc = mimalloc::MiMalloc;
// Since mold_main is a template, we can't run it without a type parameter.
// We speculatively run mold_main with X86_64, and if the speculation was
// wrong, re-run it with an actual machine type.
fn link_for_target(target: &str, cmdline: &[String], diag: &Diagnostics) -> Result<i32, String> {
fn link_for_target(target: &str, cmdline: &[String]) -> Result<i32, String> {
match target {
#[cfg(feature = "x86_64")]
"x86_64" => mold_target_x86_64::link(cmdline, diag),
"x86_64" => mold_target_x86_64::link(cmdline),
#[cfg(feature = "i386")]
"i386" => mold_target_i386::link(cmdline, diag),
"i386" => mold_target_i386::link(cmdline),
#[cfg(feature = "arm32")]
"arm32" => mold_target_arm32::link(cmdline, diag),
"arm32" => mold_target_arm32::link(cmdline),
#[cfg(feature = "arm32be")]
"arm32be" => mold_target_arm32be::link(cmdline, diag),
"arm32be" => mold_target_arm32be::link(cmdline),
#[cfg(feature = "arm64")]
"arm64" => mold_target_arm64::link(cmdline, diag),
"arm64" => mold_target_arm64::link(cmdline),
#[cfg(feature = "arm64be")]
"arm64be" => mold_target_arm64be::link(cmdline, diag),
"arm64be" => mold_target_arm64be::link(cmdline),
#[cfg(feature = "riscv64")]
"riscv64" => mold_target_riscv64::link(cmdline, diag),
"riscv64" => mold_target_riscv64::link(cmdline),
#[cfg(feature = "riscv64be")]
"riscv64be" => mold_target_riscv64be::link(cmdline, diag),
"riscv64be" => mold_target_riscv64be::link(cmdline),
#[cfg(feature = "riscv32")]
"riscv32" => mold_target_riscv32::link(cmdline, diag),
"riscv32" => mold_target_riscv32::link(cmdline),
#[cfg(feature = "riscv32be")]
"riscv32be" => mold_target_riscv32be::link(cmdline, diag),
"riscv32be" => mold_target_riscv32be::link(cmdline),
#[cfg(feature = "ppc64v2")]
"ppc64v2" => mold_target_ppc64v2::link(cmdline, diag),
"ppc64v2" => mold_target_ppc64v2::link(cmdline),
#[cfg(feature = "ppc32")]
"ppc32" => mold_target_ppc32::link(cmdline, diag),
"ppc32" => mold_target_ppc32::link(cmdline),
#[cfg(feature = "ppc64v1")]
"ppc64v1" => mold_target_ppc64v1::link(cmdline, diag),
"ppc64v1" => mold_target_ppc64v1::link(cmdline),
#[cfg(feature = "s390x")]
"s390x" => mold_target_s390x::link(cmdline, diag),
"s390x" => mold_target_s390x::link(cmdline),
#[cfg(feature = "sparc64")]
"sparc64" => mold_target_sparc64::link(cmdline, diag),
"sparc64" => mold_target_sparc64::link(cmdline),
#[cfg(feature = "m68k")]
"m68k" => mold_target_m68k::link(cmdline, diag),
"m68k" => mold_target_m68k::link(cmdline),
#[cfg(feature = "sh4")]
"sh4" => mold_target_sh4::link(cmdline, diag),
"sh4" => mold_target_sh4::link(cmdline),
#[cfg(feature = "sh4be")]
"sh4be" => mold_target_sh4be::link(cmdline, diag),
"sh4be" => mold_target_sh4be::link(cmdline),
#[cfg(feature = "loongarch64")]
"loongarch64" => mold_target_loongarch64::link(cmdline, diag),
"loongarch64" => mold_target_loongarch64::link(cmdline),
#[cfg(feature = "loongarch32")]
"loongarch32" => mold_target_loongarch32::link(cmdline, diag),
"loongarch32" => mold_target_loongarch32::link(cmdline),
_ => {
eprintln!("mold: unsupported target: {target}; rebuild mold with the appropriate target support");
std::process::exit(1);
+2 -7
View File
@@ -373,7 +373,7 @@ where
}
fn apply_eh_reloc(
ctx: &Context<Self>,
_ctx: &Context<Self>,
_isec: &InputSection,
rel: &Self::Rel,
loc: &mut [u8],
@@ -384,7 +384,7 @@ where
R_NONE => {}
R_ARM_ABS32 => End::write_u32(loc, val as u32),
R_ARM_REL32 => End::write_u32(loc, val.wrapping_sub(p) as u32),
_ => eh_frame::unsupported(ctx, rel),
_ => eh_frame::unsupported::<Self>(rel),
}
}
@@ -435,7 +435,6 @@ where
| R_ARM_V4BX
| R_ARM_TLS_GOTDESC => {}
_ => error!(
ctx,
"{}: unknown relocation: {}",
isec.display(file),
rel.type_name::<Self>()
@@ -482,7 +481,6 @@ where
osec.thunks.get(i).map_or_else(
|| {
fatal!(
ctx,
"{}: no TLSDESC trampoline after the call",
isec.display(file)
)
@@ -545,7 +543,6 @@ where
let is_blx = insn & 0xfe00_0000 == 0xfa00_0000;
if !is_bl && !is_blx {
fatal!(
ctx,
"{}: R_ARM_CALL refers to neither BL nor BLX",
isec.display(file)
);
@@ -725,7 +722,6 @@ where
}
}
_ => error!(
ctx,
"{}: unknown relocation: {}",
isec.display(file),
rel.type_name::<Self>()
@@ -756,7 +752,6 @@ where
tombstone.unwrap_or(s.wrapping_add(a).wrapping_sub(ctx.dtp_addr)) as u32,
),
_ => fatal!(
ctx,
"{}: invalid relocation for non-allocated sections: {}",
isec.display(file),
rel.type_name::<Self>()
+1 -3
View File
@@ -276,7 +276,7 @@ where
End::write_u32(loc, val.wrapping_sub(p) as u32);
}
R_AARCH64_PREL64 => End::write_u64(loc, val.wrapping_sub(p)),
_ => eh_frame::unsupported(ctx, rel),
_ => eh_frame::unsupported::<Self>(rel),
}
}
@@ -386,7 +386,6 @@ where
| R_AARCH64_TLSDESC_LD64_LO12
| R_AARCH64_TLSDESC_ADD_LO12 => {}
_ => error!(
ctx,
"{}: unknown relocation: {}",
isec.display(file),
rel.type_name::<Self>()
@@ -740,7 +739,6 @@ where
None => End::write_u64(loc, s.wrapping_add(a).wrapping_sub(ctx.dtp_addr)),
},
_ => fatal!(
ctx,
"{}: invalid relocation for non-allocated sections: {}",
isec.display(file),
rel.type_name::<Self>()
+2 -7
View File
@@ -163,7 +163,7 @@ impl Arch for I386 {
}
fn apply_eh_reloc(
ctx: &Context<Self>,
_ctx: &Context<Self>,
_isec: &InputSection,
rel: &Self::Rel,
loc: &mut [u8],
@@ -174,7 +174,7 @@ impl Arch for I386 {
R_NONE => {}
R_386_32 => write_u32(loc, val as u32),
R_386_PC32 => write_u32(loc, val.wrapping_sub(p) as u32),
_ => eh_frame::unsupported(ctx, rel),
_ => eh_frame::unsupported::<Self>(rel),
}
}
@@ -204,7 +204,6 @@ impl Arch for I386 {
Some(R_386_PLT32 | R_386_PC32 | R_386_GOT32 | R_386_GOT32X)
) {
fatal!(
ctx,
"{}: {} must be followed by PLT or GOT32",
isec.display(file),
rel.type_name::<Self>()
@@ -256,7 +255,6 @@ impl Arch for I386 {
R_386_32 | R_386_GOTOFF | R_386_TLS_LDO_32 | R_386_SIZE32 | R_386_TLS_DESC_CALL => {
}
_ => error!(
ctx,
"{}: unknown relocation: {}",
isec.display(file),
rel.type_name::<Self>()
@@ -425,7 +423,6 @@ impl Arch for I386 {
let insn = relax_tlsdesc_to_ie(&buf[..off]);
if insn == 0 {
fatal!(
ctx,
"{}: illegal instruction sequence for TLSDESC",
isec.display(file)
);
@@ -440,7 +437,6 @@ impl Arch for I386 {
let insn = relax_tlsdesc_to_le(&buf[..off]);
if insn == 0 {
fatal!(
ctx,
"{}: illegal instruction sequence for TLSDESC",
isec.display(file)
);
@@ -521,7 +517,6 @@ impl Arch for I386 {
(sym.esym(ctx).st_size().get() as u64).wrapping_add(a) as u32,
),
_ => fatal!(
ctx,
"{}: invalid relocation for non-allocated sections: {}",
isec.display(file),
rel.type_name::<Self>()
+1 -6
View File
@@ -408,7 +408,7 @@ where
LittleEndian::write_u32(loc, val.wrapping_sub(p) as u32);
}
R_LARCH_64_PCREL => LittleEndian::write_u64(loc, val.wrapping_sub(p)),
_ => eh_frame::unsupported(ctx, rel),
_ => eh_frame::unsupported::<Self>(rel),
}
}
@@ -489,7 +489,6 @@ where
| R_LARCH_TLS_DESC_LD
| R_LARCH_TLS_LE_ADD_R => {}
_ => error!(
ctx,
"{}: unknown relocation: {}",
isec.display(file),
rel.type_name::<Self>()
@@ -558,7 +557,6 @@ where
check(val, lo, hi);
if val & 0b11 != 0 {
error!(
ctx,
"{}: misaligned symbol {sym} for relocation {}",
isec.display(file),
rel.type_name::<Self>()
@@ -918,7 +916,6 @@ where
R_LARCH_ADD_ULEB128 => add_uleb(loc, sa, false),
R_LARCH_SUB_ULEB128 => add_uleb(loc, sa, true),
_ => fatal!(
ctx,
"{}: invalid relocation for non-allocated sections: {}",
isec.display(file),
rel.type_name::<Self>()
@@ -961,7 +958,6 @@ where
let alignment = if r.r_sym() != 0 {
if r.r_addend() >> 8 != 0 {
fatal!(
ctx,
"{}: ternary R_LARCH_ALIGN is not supported: {i}",
isec.display(file)
);
@@ -971,7 +967,6 @@ where
let alignment = r.r_addend() as u64 + 4;
if !alignment.is_power_of_two() {
fatal!(
ctx,
"{}: R_LARCH_ALIGN: invalid alignment requirement: {i}",
isec.display(file)
);
+3 -5
View File
@@ -112,7 +112,7 @@ impl Arch for M68k {
}
fn apply_eh_reloc(
ctx: &Context<Self>,
_ctx: &Context<Self>,
_isec: &InputSection,
rel: &Self::Rel,
loc: &mut [u8],
@@ -123,7 +123,7 @@ impl Arch for M68k {
R_NONE => {}
R_68K_32 => w32(loc, val as u32),
R_68K_PC32 => w32(loc, val.wrapping_sub(p) as u32),
_ => eh_frame::unsupported(ctx, rel),
_ => eh_frame::unsupported::<Self>(rel),
}
}
@@ -136,7 +136,7 @@ impl Arch for M68k {
}
let sym = &ctx.symbols[file.base.symbols[rel.r_sym() as usize]];
if sym.is_ifunc() {
error!(ctx, "{sym}: GNU ifunc symbol is not supported on m68k");
error!("{sym}: GNU ifunc symbol is not supported on m68k");
}
match rel.r_type() {
@@ -159,7 +159,6 @@ impl Arch for M68k {
}
R_68K_32 | R_68K_TLS_LDO32 | R_68K_TLS_LDO16 | R_68K_TLS_LDO8 => {}
_ => error!(
ctx,
"{}: unknown relocation: {}",
isec.display(file),
rel.type_name::<Self>()
@@ -284,7 +283,6 @@ impl Arch for M68k {
tombstone.unwrap_or(sa.wrapping_sub(ctx.dtp_addr)) as u32,
),
_ => fatal!(
ctx,
"{}: invalid relocation for non-allocated sections: {}",
isec.display(file),
rel.type_name::<Self>()
+2 -4
View File
@@ -198,7 +198,7 @@ impl Arch for Ppc32 {
}
fn apply_eh_reloc(
ctx: &Context<Self>,
_ctx: &Context<Self>,
_isec: &InputSection,
rel: &Self::Rel,
loc: &mut [u8],
@@ -209,7 +209,7 @@ impl Arch for Ppc32 {
R_NONE => {}
R_PPC_ADDR32 => w32(loc, val),
R_PPC_REL32 => w32(loc, val.wrapping_sub(p)),
_ => eh_frame::unsupported(ctx, rel),
_ => eh_frame::unsupported::<Self>(rel),
}
}
@@ -250,7 +250,6 @@ impl Arch for Ppc32 {
| R_PPC_TLSLD | R_PPC_DTPREL16_LO | R_PPC_DTPREL16_HI | R_PPC_DTPREL16_HA
| R_PPC_PLTSEQ | R_PPC_PLTCALL => {}
_ => error!(
ctx,
"{}: unknown relocation: {}",
isec.display(file),
rel.type_name::<Self>()
@@ -359,7 +358,6 @@ impl Arch for Ppc32 {
R_PPC_ADDR32 => w32(loc, tombstone.unwrap_or(sa)),
R_PPC_DTPREL32 => w32(loc, tombstone.unwrap_or(sa.wrapping_sub(ctx.dtp_addr))),
_ => fatal!(
ctx,
"{}: invalid relocation for non-allocated sections: {}",
isec.display(file),
rel.type_name::<Self>()
+6 -11
View File
@@ -177,7 +177,6 @@ pub fn rewrite_opd(ctx: &mut Context<Ppc64V1>) {
let _t = ctx.timer("rewrite_opd");
let editor = SymbolEditor::new(ctx.symbols.as_mut_slice());
let diag = &ctx.diag;
ctx.objs.par_iter_mut().for_each(|file| {
let Some(opd) = file
.input_sections()
@@ -213,17 +212,15 @@ pub fn rewrite_opd(ctx: &mut Context<Ppc64V1>) {
};
let rel = rels_at.get(&value).unwrap_or_else(|| {
editor.with_symbol(id, |sym| {
diag.fatal(format_args!(
fatal!(
"{file}: cannot find a relocation in .opd for {sym} at offset {value:#x}"
))
)
})
});
let target_id = local_symbols[rel.r_sym() as usize];
let origin = editor.with_symbol(target_id, |target| {
if target.ty() != STT_SECTION {
diag.fatal(format_args!(
"{file}: bad relocation in .opd referring to {target}"
));
fatal!("{file}: bad relocation in .opd referring to {target}");
}
target.origin_state()
});
@@ -263,11 +260,11 @@ pub fn rewrite_opd(ctx: &mut Context<Ppc64V1>) {
}
}
if let Some(rel) = unresolved {
diag.fatal(format_args!(
fatal!(
"{file}:({name}): cannot find a symbol in .opd for {} at offset {:#x}",
rel.type_name::<Ppc64V1>(),
rel.r_addend()
));
);
}
}
});
@@ -423,7 +420,7 @@ impl Arch for Ppc64V1 {
w32(loc, val.wrapping_sub(p));
}
R_PPC64_REL64 => w64(loc, val.wrapping_sub(p)),
_ => eh_frame::unsupported(ctx, rel),
_ => eh_frame::unsupported::<Self>(rel),
}
}
@@ -485,7 +482,6 @@ impl Arch for Ppc64V1 {
| R_PPC64_DTPREL16_LO
| R_PPC64_DTPREL16_LO_DS => {}
_ => error!(
ctx,
"{}: unknown relocation: {}",
isec.display(file),
rel.type_name::<Self>()
@@ -604,7 +600,6 @@ impl Arch for Ppc64V1 {
}
R_PPC64_DTPREL64 => w64(loc, sa.wrapping_sub(ctx.dtp_addr)),
_ => fatal!(
ctx,
"{}: invalid relocation for non-allocated sections: {}",
isec.display(file),
rel.type_name::<Self>()
+2 -4
View File
@@ -176,7 +176,7 @@ fn is_power10(ctx: &Context<Ppc64V2>) -> bool {
fn local_entry_offset(ctx: &Context<Ppc64V2>, sym: &Symbol) -> u64 {
match sym.esym(ctx).ppc64_local_entry() {
0 | 1 => 0,
7 => fatal!(ctx, "{sym}: local entry offset 7 is reserved"),
7 => fatal!("{sym}: local entry offset 7 is reserved"),
n => 1 << n,
}
}
@@ -491,7 +491,7 @@ impl Arch for Ppc64V2 {
w32(loc, val.wrapping_sub(p) as u32);
}
R_PPC64_REL64 => w64(loc, val.wrapping_sub(p)),
_ => eh_frame::unsupported(ctx, rel),
_ => eh_frame::unsupported::<Self>(rel),
}
}
@@ -566,7 +566,6 @@ impl Arch for Ppc64V2 {
| R_PPC64_DTPREL34
| R_PPC64_ENTRY => {}
_ => error!(
ctx,
"{}: unknown relocation: {}",
isec.display(file),
rel.type_name::<Self>()
@@ -725,7 +724,6 @@ impl Arch for Ppc64V2 {
}
R_PPC64_DTPREL64 => w64(loc, sa.wrapping_sub(ctx.dtp_addr)),
_ => fatal!(
ctx,
"{}: invalid relocation for non-allocated sections: {}",
isec.display(file),
rel.type_name::<Self>()
+5 -16
View File
@@ -253,11 +253,7 @@ fn find_paired_reloc<E: Arch>(
}
}
let file = &ctx.objs[isec.file.index()];
fatal!(
ctx,
"{}: paired relocation is missing: {i}",
isec.display(file)
);
fatal!("{}: paired relocation is missing: {i}", isec.display(file));
}
// Returns true if isec's i'th relocation refers to the following
@@ -346,13 +342,10 @@ where
ret |= EF_RISCV_RVC;
}
if flags & EF_RISCV_FLOAT_ABI != ret & EF_RISCV_FLOAT_ABI {
error!(ctx, "{file}: cannot link object files with different floating-point ABI from {first}");
error!("{file}: cannot link object files with different floating-point ABI from {first}");
}
if flags & EF_RISCV_RVE != ret & EF_RISCV_RVE {
error!(
ctx,
"{file}: cannot link object files with different EF_RISCV_RVE from {first}"
);
error!("{file}: cannot link object files with different EF_RISCV_RVE from {first}");
}
}
ret
@@ -432,7 +425,7 @@ where
check(val.wrapping_sub(p) as i64, -(1 << 31), 1 << 31);
End::write_u32(loc, val.wrapping_sub(p) as u32);
}
_ => eh_frame::unsupported(ctx, rel),
_ => eh_frame::unsupported::<Self>(rel),
}
}
@@ -501,7 +494,6 @@ where
| R_RISCV_SET_ULEB128
| R_RISCV_SUB_ULEB128 => {}
_ => error!(
ctx,
"{}: unknown relocation: {}",
isec.display(file),
rel.type_name::<Self>()
@@ -958,7 +950,6 @@ where
overwrite_uleb(loc, cur.wrapping_sub(sa));
}
_ => fatal!(
ctx,
"{}: invalid relocation for non-allocated sections: {}",
isec.display(file),
rel.type_name::<Self>()
@@ -1310,14 +1301,13 @@ pub fn attributes_contents<E: Arch>(ctx: &Context<E>) -> Vec<u8> {
let attrs = &file.riscv_attributes;
if let Some(val) = attrs.stack_align {
if stack.is_some_and(|s| s != val) {
error!(ctx, "{file}: stack alignment requirement mistmatch");
error!("{file}: stack alignment requirement mistmatch");
}
stack = Some(val);
}
if let Some(s) = attrs.arch {
let Some(arch2) = parse_arch_string(s) else {
error!(
ctx,
"{file}: corrupted .riscv.attributes ISA string: {}",
String::from_utf8_lossy(s)
);
@@ -1329,7 +1319,6 @@ pub fn attributes_contents<E: Arch>(ctx: &Context<E>) -> Vec<u8> {
match merge_extensions(&arch, &arch2) {
Some(merged) => arch = merged,
None => error!(
ctx,
"{file}: incompatible .riscv.attributes ISA string: {}",
String::from_utf8_lossy(s)
),
+1 -4
View File
@@ -216,7 +216,7 @@ impl Arch for S390x {
w32(loc, val.wrapping_sub(p) as u32);
}
R_390_64 => w64(loc, val),
_ => eh_frame::unsupported(ctx, rel),
_ => eh_frame::unsupported::<Self>(rel),
}
}
@@ -274,7 +274,6 @@ impl Arch for S390x {
R_390_64 | R_390_TLS_LDO32 | R_390_TLS_LDO64 | R_390_TLS_GDCALL
| R_390_TLS_LDCALL => {}
_ => error!(
ctx,
"{}: unknown relocation: {}",
isec.display(file),
rel.type_name::<Self>()
@@ -315,7 +314,6 @@ impl Arch for S390x {
check(val, lo, hi);
if val & 1 != 0 {
error!(
ctx,
"{}: misaligned symbol {sym} for relocation {}",
isec.display(file),
rel.type_name::<Self>()
@@ -519,7 +517,6 @@ impl Arch for S390x {
None => w64(&mut buf[off..], sa.wrapping_sub(ctx.dtp_addr)),
},
_ => fatal!(
ctx,
"{}: invalid relocation for non-allocated sections: {}",
isec.display(file),
rel.type_name::<Self>()
+3 -5
View File
@@ -266,7 +266,7 @@ where
}
fn apply_eh_reloc(
ctx: &Context<Self>,
_ctx: &Context<Self>,
_isec: &InputSection,
rel: &Self::Rel,
loc: &mut [u8],
@@ -277,7 +277,7 @@ where
R_NONE => {}
R_SH_DIR32 => End::write_u32(loc, val as u32),
R_SH_REL32 => End::write_u32(loc, val.wrapping_sub(p) as u32),
_ => eh_frame::unsupported(ctx, rel),
_ => eh_frame::unsupported::<Self>(rel),
}
}
@@ -290,7 +290,7 @@ where
}
let sym = &ctx.symbols[file.base.symbols[rel.r_sym() as usize]];
if sym.is_ifunc() {
error!(ctx, "{sym}: GNU ifunc symbol is not supported on sh4");
error!("{sym}: GNU ifunc symbol is not supported on sh4");
}
match rel.r_type() {
@@ -307,7 +307,6 @@ where
R_SH_TLS_LE_32 => check_tlsle(ctx, isec, sym, &rel),
R_SH_DIR32 | R_SH_GOTPC | R_SH_GOTOFF | R_SH_TLS_LDO_32 => {}
_ => fatal!(
ctx,
"{}: unknown relocation: {}",
isec.display(file),
rel.type_name::<Self>()
@@ -382,7 +381,6 @@ where
tombstone.unwrap_or(sa.wrapping_sub(ctx.dtp_addr)) as u32,
),
_ => fatal!(
ctx,
"{}: invalid relocation for non-allocated sections: {}",
isec.display(file),
rel.type_name::<Self>()
+1 -3
View File
@@ -274,7 +274,7 @@ impl Arch for Sparc64 {
);
w32(loc, val.wrapping_sub(p));
}
_ => eh_frame::unsupported(ctx, rel),
_ => eh_frame::unsupported::<Self>(rel),
}
}
@@ -362,7 +362,6 @@ impl Arch for Sparc64 {
| R_SPARC_TLS_IE_LO10
| R_SPARC_SIZE32 => {}
_ => error!(
ctx,
"{}: unknown relocation: {}",
isec.display(file),
rel.type_name::<Self>()
@@ -722,7 +721,6 @@ impl Arch for Sparc64 {
R_SPARC_TLS_DTPOFF32 => w32(loc, sa.wrapping_sub(ctx.dtp_addr)),
R_SPARC_TLS_DTPOFF64 => w64(loc, sa.wrapping_sub(ctx.dtp_addr)),
_ => fatal!(
ctx,
"{}: invalid relocation for non-allocated sections: {}",
isec.display(file),
rel.type_name::<Self>()
+1 -6
View File
@@ -171,7 +171,7 @@ impl Arch for X86_64 {
write_u32(loc, val.wrapping_sub(p) as u32);
}
R_X86_64_PC64 => write_u64(loc, val.wrapping_sub(p)),
_ => eh_frame::unsupported(ctx, rel),
_ => eh_frame::unsupported::<Self>(rel),
}
}
@@ -213,7 +213,6 @@ impl Arch for X86_64 {
);
if !ok {
fatal!(
ctx,
"{}: {} must be followed by PLT or GOTPCREL",
isec.display(file),
rel.type_name::<Self>()
@@ -284,7 +283,6 @@ impl Arch for X86_64 {
| R_X86_64_GOTPC32_TLSDESC
| R_X86_64_CODE_4_GOTPC32_TLSDESC => {}
_ => error!(
ctx,
"{}: unknown relocation: {}",
isec.display(file),
rel.type_name::<Self>()
@@ -489,7 +487,6 @@ impl Arch for X86_64 {
let insn = relax_tlsdesc_to_ie(&buf[..off], &rel);
if insn == 0 {
fatal!(
ctx,
"{}: illegal instruction sequence for {}",
isec.display(file),
rel.type_name::<Self>()
@@ -503,7 +500,6 @@ impl Arch for X86_64 {
let insn = relax_tlsdesc_to_le(&buf[..off], &rel);
if insn == 0 {
fatal!(
ctx,
"{}: illegal instruction sequence for {}",
isec.display(file),
rel.type_name::<Self>()
@@ -615,7 +611,6 @@ impl Arch for X86_64 {
sym.esym(ctx).st_size().get().wrapping_add(a),
),
_ => fatal!(
ctx,
"{}: invalid relocation for non-allocated sections: {}",
isec.display(file),
rel.type_name::<Self>()
+12 -31
View File
@@ -23,7 +23,6 @@
//! see the contents of libc.a by running `ar t
//! /usr/lib/x86_64-linux-gnu/libc.a`.
use crate::error::Diagnostics;
use crate::fatal;
use crate::mapped_file::MappedFile;
use crate::util;
@@ -98,12 +97,7 @@ fn parse_decimal(bytes: &[u8]) -> usize {
/// Iterates over the members of an archive as (header, name, body)
/// triples, skipping the symbol table and string table.
fn for_each_member(
diag: &Diagnostics,
mf: &'static MappedFile,
thin: bool,
mut f: impl FnMut(String, &'static [u8]),
) {
fn for_each_member(mf: &'static MappedFile, thin: bool, mut f: impl FnMut(String, &'static [u8])) {
let data = mf.data();
let mut pos = 8;
let mut strtab: &[u8] = &[];
@@ -136,11 +130,7 @@ fn for_each_member(
}
if thin && !hdr.name.starts_with(b"#1/") && !hdr.name.starts_with(b"/") {
fatal!(
diag,
"{}: filename is not stored as a long filename",
mf.name
);
fatal!("{}: filename is not stored as a long filename", mf.name);
}
// Read the name field
@@ -169,9 +159,9 @@ fn for_each_member(
/// 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(diag: &Diagnostics, mf: &'static MappedFile) -> Vec<String> {
pub fn get_thin_archive_member_paths(mf: &'static MappedFile) -> Vec<String> {
let mut paths = Vec::new();
for_each_member(diag, mf, true, |name, _| {
for_each_member(mf, true, |name, _| {
if name.starts_with('/') {
paths.push(name);
} else {
@@ -181,14 +171,11 @@ pub fn get_thin_archive_member_paths(diag: &Diagnostics, mf: &'static MappedFile
paths
}
pub fn read_thin_archive_members(
diag: &Diagnostics,
mf: &'static MappedFile,
) -> Vec<&'static MappedFile> {
get_thin_archive_member_paths(diag, mf)
pub fn read_thin_archive_members(mf: &'static MappedFile) -> Vec<&'static MappedFile> {
get_thin_archive_member_paths(mf)
.into_iter()
.map(|path| {
let member = MappedFile::must_open(diag, &path);
let member = MappedFile::must_open(&path);
util::leak(MappedFile {
name: member.name.clone(),
data: member.data,
@@ -201,27 +188,21 @@ pub fn read_thin_archive_members(
.collect::<Vec<_>>()
}
pub fn read_fat_archive_members(
diag: &Diagnostics,
mf: &'static MappedFile,
) -> Vec<&'static MappedFile> {
pub fn read_fat_archive_members(mf: &'static MappedFile) -> Vec<&'static MappedFile> {
let mut members = Vec::new();
let base = mf.data().as_ptr() as usize;
for_each_member(diag, mf, false, |name, body| {
for_each_member(mf, false, |name, body| {
let start = body.as_ptr() as usize - base;
members.push(mf.slice(name, start, body.len()));
});
members
}
pub fn read_archive_members(
diag: &Diagnostics,
mf: &'static MappedFile,
) -> Vec<&'static MappedFile> {
pub fn read_archive_members(mf: &'static MappedFile) -> Vec<&'static MappedFile> {
if mf.data().starts_with(b"!<arch>\n") {
read_fat_archive_members(diag, mf)
read_fat_archive_members(mf)
} else {
debug_assert!(mf.data().starts_with(b"!<thin>\n"));
read_thin_archive_members(diag, mf)
read_thin_archive_members(mf)
}
}
+98 -119
View File
@@ -5,7 +5,6 @@ use std::io::IsTerminal;
use crate::arch;
use crate::elf::*;
use crate::error::Diagnostics;
use crate::mapped_file::MappedFile;
use crate::util::glob::Glob;
use crate::util::perf::Counter;
@@ -715,12 +714,12 @@ 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(diag: &Diagnostics, path: &str, depth: usize) -> Vec<String> {
fn read_response_file(path: &str, depth: usize) -> Vec<String> {
if depth > 10 {
fatal!(diag, "{path}: response file nesting too deep");
fatal!("{path}: response file nesting too deep");
}
let mf = MappedFile::must_open(diag, path);
let mf = MappedFile::must_open(path);
mf.set_dependency(false);
let data = mf.data();
let mut tokens = Vec::new();
@@ -747,7 +746,7 @@ fn read_response_file(diag: &Diagnostics, path: &str, depth: usize) -> Vec<Strin
let c = data[i];
if c == b'\\' {
if i + 1 == data.len() {
fatal!(diag, "{path}: premature end of input");
fatal!("{path}: premature end of input");
}
tok.push(data[i + 1]);
i += 2;
@@ -769,7 +768,7 @@ fn read_response_file(diag: &Diagnostics, path: &str, depth: usize) -> Vec<Strin
}
}
if quote.is_some() {
fatal!(diag, "{path}: premature end of input");
fatal!("{path}: premature end of input");
}
tokens.push(String::from_utf8_lossy(&tok).into_owned());
}
@@ -777,7 +776,7 @@ fn read_response_file(diag: &Diagnostics, path: &str, depth: usize) -> Vec<Strin
let mut expanded = Vec::new();
for tok in tokens {
if let Some(nested) = tok.strip_prefix('@') {
expanded.extend(read_response_file(diag, nested, depth + 1));
expanded.extend(read_response_file(nested, depth + 1));
} else {
expanded.push(tok);
}
@@ -786,11 +785,11 @@ fn read_response_file(diag: &Diagnostics, path: &str, depth: usize) -> Vec<Strin
}
// Replace "@path/to/some/text/file" with its file contents.
pub fn expand_response_files(diag: &Diagnostics, argv: &[String]) -> Vec<String> {
pub fn expand_response_files(argv: &[String]) -> Vec<String> {
let mut args = Vec::new();
for arg in argv {
if let Some(path) = arg.strip_prefix('@') {
args.extend(read_response_file(diag, path, 1));
args.extend(read_response_file(path, 1));
} else {
args.push(arg.clone());
}
@@ -824,16 +823,16 @@ fn match_option<'a>(arg: &'a str, name: &str) -> Option<&'a str> {
arg.strip_prefix(name)
}
fn parse_hex(diag: &Diagnostics, opt: &str, value: &str) -> u64 {
fn parse_hex(opt: &str, value: &str) -> u64 {
let digits = value
.strip_prefix("0x")
.or_else(|| value.strip_prefix("0X"))
.unwrap_or(value);
if digits.is_empty() || !digits.bytes().all(|b| b.is_ascii_hexdigit()) {
fatal!(diag, "option -{opt}: not a hexadecimal number");
fatal!("option -{opt}: not a hexadecimal number");
}
u64::from_str_radix(digits, 16)
.unwrap_or_else(|_| fatal!(diag, "option -{opt}: not a hexadecimal number"))
.unwrap_or_else(|_| fatal!("option -{opt}: not a hexadecimal number"))
}
/// Parses an integer in C syntax (decimal, `0x` hex or leading-zero octal).
@@ -847,13 +846,13 @@ fn parse_c_number(s: &str) -> Option<u64> {
}
}
fn parse_number(diag: &Diagnostics, opt: &str, value: &str) -> i64 {
fn parse_number(opt: &str, value: &str) -> i64 {
let (negative, digits) = match value.strip_prefix('-') {
Some(rest) => (true, rest),
None => (false, value),
};
let n = parse_c_number(digits)
.unwrap_or_else(|| fatal!(diag, "option -{opt}: not a number: {value}")) as i64;
let n = parse_c_number(digits).unwrap_or_else(|| fatal!("option -{opt}: not a number: {value}"))
as i64;
if negative {
-n
} else {
@@ -869,12 +868,12 @@ fn from_hex(c: u8) -> u8 {
}
}
fn parse_hex_build_id(diag: &Diagnostics, arg: &str) -> Vec<u8> {
fn parse_hex_build_id(arg: &str) -> Vec<u8> {
let digits = arg
.strip_prefix("0x")
.or_else(|| arg.strip_prefix("0X"))
.filter(|d| !d.is_empty() && d.len() % 2 == 0 && d.bytes().all(|b| b.is_ascii_hexdigit()))
.unwrap_or_else(|| fatal!(diag, "invalid build-id: {arg}"));
.unwrap_or_else(|| fatal!("invalid build-id: {arg}"));
digits
.as_bytes()
.chunks(2)
@@ -882,7 +881,7 @@ fn parse_hex_build_id(diag: &Diagnostics, arg: &str) -> Vec<u8> {
.collect()
}
fn parse_package_metadata(diag: &Diagnostics, arg: &str) -> String {
fn parse_package_metadata(arg: &str) -> String {
let bytes = arg.as_bytes();
let mut out = Vec::new();
let mut i = 0;
@@ -892,7 +891,7 @@ fn parse_package_metadata(diag: &Diagnostics, arg: &str) -> String {
|| !bytes[i + 1].is_ascii_hexdigit()
|| !bytes[i + 2].is_ascii_hexdigit()
{
fatal!(diag, "--package-metadata: invalid string: {arg}");
fatal!("--package-metadata: invalid string: {arg}");
}
out.push((from_hex(bytes[i + 1]) << 4) | from_hex(bytes[i + 2]));
i += 3;
@@ -904,8 +903,8 @@ fn parse_package_metadata(diag: &Diagnostics, arg: &str) -> String {
String::from_utf8_lossy(&out).into_owned()
}
fn read_retain_symbols_file(diag: &Diagnostics, path: &str) -> Vec<String> {
let mf = MappedFile::must_open(diag, path);
fn read_retain_symbols_file(path: &str) -> Vec<String> {
let mf = MappedFile::must_open(path);
String::from_utf8_lossy(mf.data())
.lines()
.map(|line| line.trim_matches(|c| c == ' ' || c == '\t'))
@@ -914,7 +913,7 @@ fn read_retain_symbols_file(diag: &Diagnostics, path: &str) -> Vec<String> {
.collect()
}
fn parse_section_order(diag: &Diagnostics, arg: &str) -> Vec<SectionOrder> {
fn parse_section_order(arg: &str) -> Vec<SectionOrder> {
let parse_value = |s: &str| -> Option<u64> {
if let Some(hex) = s.strip_prefix("0x").or_else(|| s.strip_prefix("0X")) {
u64::from_str_radix(hex, 16).ok()
@@ -957,7 +956,7 @@ fn parse_section_order(diag: &Diagnostics, arg: &str) -> Vec<SectionOrder> {
order.kind = SectionOrderKind::Section;
order.name = tok.to_string();
} else {
fatal!(diag, "--section-order: parse error: {arg}");
fatal!("--section-order: parse error: {arg}");
}
orders.push(order);
}
@@ -968,20 +967,17 @@ fn parse_section_order(diag: &Diagnostics, arg: &str) -> Vec<SectionOrder> {
if is_first {
is_first = false;
} else if order.name == "EHDR" {
fatal!(
diag,
"--section-order: EHDR must be the first section specifier: {arg}"
);
fatal!("--section-order: EHDR must be the first section specifier: {arg}");
}
}
}
orders
}
fn parse_defsym_value(diag: &Diagnostics, s: &str) -> DefsymValue {
fn parse_defsym_value(s: &str) -> DefsymValue {
if let Some(hex) = s.strip_prefix("0x").or_else(|| s.strip_prefix("0X")) {
let Ok(v) = u64::from_str_radix(hex, 16) else {
fatal!(diag, "-defsym: not a number: {s}");
fatal!("-defsym: not a number: {s}");
};
return DefsymValue::Addr(v);
}
@@ -1022,7 +1018,7 @@ pub struct ParsedArgs {
}
/// Parses all options. `cmdline` includes the program name.
pub fn parse_args(diag: &Diagnostics, target: &TargetTraits, cmdline: &[String]) -> ParsedArgs {
pub fn parse_args(target: &TargetTraits, cmdline: &[String]) -> ParsedArgs {
// 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
@@ -1034,10 +1030,10 @@ pub fn parse_args(diag: &Diagnostics, target: &TargetTraits, cmdline: &[String])
let mut visited_libs: HashSet<String> = HashSet::new();
a.color_diagnostics = stderr_is_tty();
diag.set_color(a.color_diagnostics);
diag.set_fatal_warnings(a.fatal_warnings);
diag.set_suppress_warnings(a.suppress_warnings);
diag.set_noinhibit_exec(a.noinhibit_exec);
crate::error::set_color(a.color_diagnostics);
crate::error::set_fatal_warnings(a.fatal_warnings);
crate::error::set_suppress_warnings(a.suppress_warnings);
crate::error::set_noinhibit_exec(a.noinhibit_exec);
crate::error::set_demangle(a.demangle);
a.page_size = target.page_size;
@@ -1080,7 +1076,7 @@ pub fn parse_args(diag: &Diagnostics, target: &TargetTraits, cmdline: &[String])
None => false,
Some(rest) if rest.is_empty() => {
if i + 1 == cmdline.len() {
fatal!(diag, "option -{name}: argument missing");
fatal!("option -{name}: argument missing");
}
arg = cmdline[i + 1].clone();
i += 2;
@@ -1177,7 +1173,7 @@ pub fn parse_args(diag: &Diagnostics, target: &TargetTraits, cmdline: &[String])
}
if read_flag!("help") {
out!(diag, "Usage: {} [options] file...\n{}", cmdline[0], HELP);
out!("Usage: {} [options] file...\n{}", cmdline[0], HELP);
std::process::exit(0);
}
@@ -1188,15 +1184,13 @@ pub fn parse_args(diag: &Diagnostics, target: &TargetTraits, cmdline: &[String])
} else if read_flag!("no-dynamic-linker") {
a.dynamic_linker.clear();
} else if read_flag!("v") {
out!(diag, "{VERSION}");
out!("{VERSION}");
version_shown = true;
} else if read_flag!("version") {
out!(diag, "{VERSION}");
out!("{VERSION}");
std::process::exit(0);
} else if read_flag!("V") {
out!(
diag,
"{VERSION}\n Supported emulations:\n elf_x86_64\n elf_i386\n aarch64elf\n \
out!("{VERSION}\n Supported emulations:\n elf_x86_64\n elf_i386\n aarch64elf\n \
aarch64linux\n aarch64elfb\n aarch64linuxb\n armelf_linux_eabi\n elf64lriscv\n \
elf64briscv\n elf32lriscv\n elf32briscv\n elf32ppc\n elf64ppc\n elf64lppc\n \
elf64_s390\n elf64_sparc\n m68kelf\n shlelf_linux\n shelf_linux\n \
@@ -1208,7 +1202,7 @@ pub fn parse_args(diag: &Diagnostics, target: &TargetTraits, cmdline: &[String])
} else if read_arg!("m") {
match arch::emulation_to_target(&arg) {
Some(name) => a.emulation = name.to_string(),
None => fatal!(diag, "unknown -m argument: {arg}"),
None => fatal!("unknown -m argument: {arg}"),
}
} else if read_flag!("end-lib") {
rctx.in_lib = false;
@@ -1249,9 +1243,9 @@ pub fn parse_args(diag: &Diagnostics, target: &TargetTraits, cmdline: &[String])
} else if read_flag!("shared") || read_flag!("Bshareable") {
a.shared = true;
} else if read_arg!("spare-dynamic-tags") {
a.spare_dynamic_tags = parse_number(diag, "spare-dynamic-tags", &arg);
a.spare_dynamic_tags = parse_number("spare-dynamic-tags", &arg);
} else if read_arg!("spare-program-headers") {
a.spare_program_headers = parse_number(diag, "spare-program-headers", &arg);
a.spare_program_headers = parse_number("spare-program-headers", &arg);
} else if read_flag!("start-lib") {
rctx.in_lib = true;
} else if read_flag!("start-stop") {
@@ -1260,10 +1254,10 @@ pub fn parse_args(diag: &Diagnostics, target: &TargetTraits, cmdline: &[String])
a.dependency_file = arg.clone();
} else if read_arg!("defsym") {
let Some((name, value)) = arg.split_once('=').filter(|(_, v)| !v.is_empty()) else {
fatal!(diag, "-defsym: syntax error: {arg}");
fatal!("-defsym: syntax error: {arg}");
};
a.defsyms
.push((name.to_string(), parse_defsym_value(diag, value)));
.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") {
@@ -1282,12 +1276,12 @@ pub fn parse_args(diag: &Diagnostics, target: &TargetTraits, cmdline: &[String])
a.default_symver = true;
} else if read_flag!("noinhibit-exec") {
a.noinhibit_exec = true;
diag.set_noinhibit_exec(true);
crate::error::set_noinhibit_exec(true);
} else if read_flag!("shuffle-sections") {
a.shuffle_sections = ShuffleSectionsKind::Shuffle;
} else if read_eq!("shuffle-sections") {
a.shuffle_sections = ShuffleSectionsKind::Shuffle;
shuffle_sections_seed = Some(parse_number(diag, "shuffle-sections", &arg) as u64);
shuffle_sections_seed = Some(parse_number("shuffle-sections", &arg) as u64);
} else if read_flag!("reverse-sections") {
a.shuffle_sections = ShuffleSectionsKind::Reverse;
} else if read_flag!("rosegment") {
@@ -1297,26 +1291,26 @@ pub fn parse_args(diag: &Diagnostics, target: &TargetTraits, cmdline: &[String])
} else if read_arg!("y") || read_arg!("trace-symbol") {
a.trace_symbol.push(arg.clone());
} else if read_arg!("filler") {
a.filler = Some(parse_hex(diag, "filler", &arg) as u8);
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!("unique") {
if !a.unique.add(arg.as_bytes(), 1) {
fatal!(diag, "-unique: invalid glob pattern: {arg}");
fatal!("-unique: invalid glob pattern: {arg}");
}
} else if read_arg!("unresolved-symbols") {
match arg.as_str() {
"report-all" | "ignore-in-shared-libs" => report_undefined = Some(true),
"ignore-all" | "ignore-in-object-files" => report_undefined = Some(false),
_ => fatal!(diag, "unknown --unresolved-symbols argument: {arg}"),
_ => fatal!("unknown --unresolved-symbols argument: {arg}"),
}
} else if read_arg!("undefined") || read_arg!("u") {
a.undefined.push(arg.clone());
} else if read_arg!("undefined-glob") {
if !a.undefined_glob.add(arg.as_bytes(), 0) {
fatal!(diag, "--undefined-glob: invalid pattern: {arg}");
fatal!("--undefined-glob: invalid pattern: {arg}");
}
} else if read_arg!("require-defined") {
a.require_defined.push(arg.clone());
@@ -1342,7 +1336,7 @@ pub fn parse_args(diag: &Diagnostics, target: &TargetTraits, cmdline: &[String])
a.hash_style_sysv = false;
a.hash_style_gnu = false;
}
_ => fatal!(diag, "invalid --hash-style argument: {arg}"),
_ => fatal!("invalid --hash-style argument: {arg}"),
}
} else if read_arg!("soname") || read_arg!("h") {
a.soname = arg.clone();
@@ -1407,7 +1401,7 @@ pub fn parse_args(diag: &Diagnostics, target: &TargetTraits, cmdline: &[String])
} else if read_flag!("no-use-android-relr-tags") {
a.use_android_relr_tags = false;
} else if read_arg!("package-metadata") {
a.package_metadata = parse_package_metadata(diag, &arg);
a.package_metadata = parse_package_metadata(&arg);
} else if read_flag!("stats") {
a.stats = true;
Counter::enable();
@@ -1417,13 +1411,13 @@ pub fn parse_args(diag: &Diagnostics, target: &TargetTraits, cmdline: &[String])
a.chroot = arg.clone();
} else if read_flag!("color-diagnostics") || read_flag!("color-diagnostics=auto") {
a.color_diagnostics = stderr_is_tty();
diag.set_color(a.color_diagnostics);
crate::error::set_color(a.color_diagnostics);
} else if read_flag!("color-diagnostics=always") {
a.color_diagnostics = true;
diag.set_color(true);
crate::error::set_color(true);
} else if read_flag!("color-diagnostics=never") {
a.color_diagnostics = false;
diag.set_color(false);
crate::error::set_color(false);
} else if read_flag!("warn-common") {
a.warn_common = true;
} else if read_flag!("no-warn-common") {
@@ -1455,27 +1449,23 @@ pub fn parse_args(diag: &Diagnostics, target: &TargetTraits, cmdline: &[String])
"none" => a.compress_debug_sections = ELFCOMPRESS_NONE,
s if s.starts_with("zlib:") => {
a.compress_debug_sections = ELFCOMPRESS_ZLIB;
let level = parse_number(diag, "compress-debug-sections", &s[5..]);
let level = parse_number("compress-debug-sections", &s[5..]);
if !(0..=9).contains(&level) {
fatal!(
diag,
"invalid --compress-debug-sections argument: {arg} (zlib level must be between 0 and 9)"
fatal!("invalid --compress-debug-sections argument: {arg} (zlib level must be between 0 and 9)"
);
}
a.compress_debug_sections_level = level;
}
s if s.starts_with("zstd:") => {
a.compress_debug_sections = ELFCOMPRESS_ZSTD;
let level = parse_number(diag, "compress-debug-sections", &s[5..]);
let level = parse_number("compress-debug-sections", &s[5..]);
if !(1..=22).contains(&level) {
fatal!(
diag,
"invalid --compress-debug-sections argument: {arg} (zstd level must be between 1 and 22)"
fatal!("invalid --compress-debug-sections argument: {arg} (zstd level must be between 1 and 22)"
);
}
a.compress_debug_sections_level = level;
}
_ => fatal!(diag, "invalid --compress-debug-sections argument: {arg}"),
_ => fatal!("invalid --compress-debug-sections argument: {arg}"),
}
} else if read_arg!("wrap") {
a.wrap.insert(arg.clone());
@@ -1486,39 +1476,39 @@ pub fn parse_args(diag: &Diagnostics, target: &TargetTraits, cmdline: &[String])
a.omagic = false;
} else if read_arg!("oformat") {
if arg != "binary" {
fatal!(diag, "-oformat: {arg} is not supported");
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(diag, &arg));
a.retain_symbols_file = Some(read_retain_symbols_file(&arg));
} else if read_arg!("section-align") {
let Some((name, value)) = arg.split_once('=').filter(|(_, v)| !v.is_empty()) else {
fatal!(diag, "--section-align: syntax error: {arg}");
fatal!("--section-align: syntax error: {arg}");
};
let value = parse_number(diag, "section-align", value);
let value = parse_number("section-align", value);
if value <= 0 || !(value as u64).is_power_of_two() {
fatal!(diag, "--section-align={arg}: value must be a power of 2");
fatal!("--section-align={arg}: value must be a power of 2");
}
a.section_align.insert(name.to_string(), value as u64);
} else if read_arg!("section-start") {
let Some((name, value)) = arg.split_once('=').filter(|(_, v)| !v.is_empty()) else {
fatal!(diag, "--section-start: syntax error: {arg}");
fatal!("--section-start: syntax error: {arg}");
};
a.section_start
.insert(name.to_string(), parse_hex(diag, "section-start", value));
.insert(name.to_string(), parse_hex("section-start", value));
} else if read_arg!("section-order") {
a.section_order = parse_section_order(diag, &arg);
a.section_order = parse_section_order(&arg);
} else if read_arg!("Tbss") {
a.section_start
.insert(".bss".to_string(), parse_hex(diag, "Tbss", &arg));
.insert(".bss".to_string(), parse_hex("Tbss", &arg));
} else if read_arg!("Tdata") {
a.section_start
.insert(".data".to_string(), parse_hex(diag, "Tdata", &arg));
.insert(".data".to_string(), parse_hex("Tdata", &arg));
} else if read_arg!("Ttext") {
a.section_start
.insert(".text".to_string(), parse_hex(diag, "Ttext", &arg));
.insert(".text".to_string(), parse_hex("Ttext", &arg));
} else if read_arg!("Ttext-segment") {
a.ttext_segment = Some(parse_number(diag, "Ttext-segment", &arg) as u64);
a.ttext_segment = Some(parse_number("Ttext-segment", &arg) as u64);
} else if read_flag!("repro") {
a.repro = true;
} else if read_z_flag!("now") {
@@ -1536,9 +1526,9 @@ pub fn parse_args(diag: &Diagnostics, target: &TargetTraits, cmdline: &[String])
} else if read_z_flag!("execstack-if-needed") {
a.z_execstack_if_needed = true;
} else if read_z_arg!("max-page-size") {
a.page_size = parse_number(diag, "-z max-page-size", &arg) as u64;
a.page_size = parse_number("-z max-page-size", &arg) as u64;
if !a.page_size.is_power_of_two() {
fatal!(diag, "-z max-page-size {arg}: value must be a power of 2");
fatal!("-z max-page-size {arg}: value must be a power of 2");
}
} else if read_z_flag!("start-stop-visibility=protected") {
a.z_start_stop_visibility_protected = true;
@@ -1598,7 +1588,7 @@ pub fn parse_args(diag: &Diagnostics, target: &TargetTraits, cmdline: &[String])
} else if read_z_flag!("noseparate-code") {
z_separate_code = Some(SeparateCodeKind::NoSeparateCode);
} else if read_z_arg!("stack-size") {
a.z_stack_size = parse_number(diag, "-z stack-size", &arg) as u64;
a.z_stack_size = parse_number("-z stack-size", &arg) as u64;
} else if read_z_flag!("dynamic-undefined-weak") {
z_dynamic_undefined_weak = Some(true);
} else if read_z_flag!("nodynamic-undefined-weak") {
@@ -1617,10 +1607,7 @@ pub fn parse_args(diag: &Diagnostics, target: &TargetTraits, cmdline: &[String])
a.z_x86_64_isa_level |= GNU_PROPERTY_X86_ISA_1_V4;
} else if read_z_flag!("rewrite-endbr") {
if !target.is_x86_64 && !target.is_arm64 {
fatal!(
diag,
"-z rewrite-endbr is supported only on x86-64 and arm64"
);
fatal!("-z rewrite-endbr is supported only on x86-64 and arm64");
}
a.z_rewrite_endbr = true;
} else if read_z_flag!("norewrite-endbr") {
@@ -1631,13 +1618,13 @@ pub fn parse_args(diag: &Diagnostics, target: &TargetTraits, cmdline: &[String])
a.nmagic = false;
} else if read_flag!("fatal-warnings") {
a.fatal_warnings = true;
diag.set_fatal_warnings(true);
crate::error::set_fatal_warnings(true);
} else if read_flag!("no-fatal-warnings") {
a.fatal_warnings = false;
diag.set_fatal_warnings(false);
crate::error::set_fatal_warnings(false);
} else if read_flag!("w") || read_flag!("no-warnings") {
a.suppress_warnings = true;
diag.set_suppress_warnings(true);
crate::error::set_suppress_warnings(true);
} else if read_flag!("fork") {
a.fork = true;
} else if read_flag!("no-fork") {
@@ -1664,16 +1651,16 @@ pub fn parse_args(diag: &Diagnostics, target: &TargetTraits, cmdline: &[String])
}
"safe" => a.icf = true,
"none" => a.icf = false,
_ => fatal!(diag, "unknown --icf argument: {arg}"),
_ => fatal!("unknown --icf argument: {arg}"),
}
} else if read_flag!("no-icf") {
a.icf = false;
} else if read_flag!("ignore-data-address-equality") {
a.ignore_data_address_equality = true;
} else if read_arg!("image-base") {
a.image_base = parse_number(diag, "image-base", &arg) as u64;
a.image_base = parse_number("image-base", &arg) as u64;
} else if read_arg!("physical-image-base") {
a.physical_image_base = Some(parse_number(diag, "physical-image-base", &arg) as u64);
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") {
@@ -1748,13 +1735,13 @@ pub fn parse_args(diag: &Diagnostics, target: &TargetTraits, cmdline: &[String])
} else if read_arg!("thinlto-jobs") {
a.plugin_opt.push(format!("jobs={arg}"));
} else if read_arg!("thread-count") {
a.thread_count = Some(parse_number(diag, "thread-count", &arg).max(1) as usize);
a.thread_count = Some(parse_number("thread-count", &arg).max(1) as usize);
} else if read_flag!("threads") {
a.thread_count = None;
} else if read_flag!("no-threads") {
a.thread_count = Some(1);
} else if read_eq!("threads") {
a.thread_count = Some(parse_number(diag, "threads", &arg).max(1) as usize);
a.thread_count = Some(parse_number("threads", &arg).max(1) as usize);
} else if read_flag!("discard-all") || read_flag!("x") {
a.discard_all = true;
} else if read_flag!("discard-locals") || read_flag!("X") {
@@ -1774,10 +1761,7 @@ pub fn parse_args(diag: &Diagnostics, target: &TargetTraits, cmdline: &[String])
add_rpath(&mut a, &mut rpaths, &arg);
} else if read_arg!("R") {
if crate::mapped_file::is_file(&arg) {
fatal!(
diag,
"-R{arg}: -R as an alias for --just-symbols is not supported"
);
fatal!("-R{arg}: -R as an alias for --just-symbols is not supported");
}
add_rpath(&mut a, &mut rpaths, &arg);
} else if read_flag!("undefined-version") {
@@ -1805,9 +1789,9 @@ pub fn parse_args(diag: &Diagnostics, target: &TargetTraits, cmdline: &[String])
}
s if s.starts_with("0x") || s.starts_with("0X") => {
a.build_id.kind = BuildIdKind::Hex;
a.build_id.value = parse_hex_build_id(diag, s);
a.build_id.value = parse_hex_build_id(s);
}
_ => fatal!(diag, "invalid --build-id argument: {arg}"),
_ => fatal!("invalid --build-id argument: {arg}"),
}
} else if read_flag!("no-build-id") {
a.build_id.kind = BuildIdKind::None;
@@ -1817,13 +1801,11 @@ pub fn parse_args(diag: &Diagnostics, target: &TargetTraits, cmdline: &[String])
a.be8 = false;
} else if read_arg!("format") || read_arg!("b") {
if arg == "binary" {
fatal!(
diag,
"mold does not support `-b binary`. If you want to convert a binary file into an \
fatal!("mold does not support `-b binary`. If you want to convert a binary file into an \
object file, use `objcopy -I binary -O default <input-file> <output-file.o>` instead."
);
}
fatal!(diag, "unknown command line option: -b {arg}");
fatal!("unknown command line option: -b {arg}");
} else if read_arg!("fuse-ld") {
} else if read_arg!("auxiliary") || read_arg!("f") {
a.auxiliary.push(arg.clone());
@@ -1919,20 +1901,18 @@ pub fn parse_args(diag: &Diagnostics, target: &TargetTraits, cmdline: &[String])
} else if read_flag!("pop-state") {
rctx = rctx_stack
.pop()
.unwrap_or_else(|| fatal!(diag, "no state pushed before popping"));
.unwrap_or_else(|| fatal!("no state pushed before popping"));
} else if cmdline[i].starts_with("-z") && cmdline[i].len() > 2 {
warn!(diag, "unknown command line option: {}", cmdline[i]);
warn!("unknown command line option: {}", cmdline[i]);
i += 1;
} else if cmdline[i] == "-z" && i + 1 < cmdline.len() {
warn!(diag, "unknown command line option: -z {}", cmdline[i + 1]);
warn!("unknown command line option: -z {}", cmdline[i + 1]);
i += 2;
} else if cmdline[i] == "-dynamic" {
fatal!(
diag,
"unknown command line option: -dynamic; -dynamic is a macOS linker's option. mold does not support macOS."
fatal!("unknown command line option: -dynamic; -dynamic is a macOS linker's option. mold does not support macOS."
);
} else {
fatal!(diag, "unknown command line option: {}", cmdline[i]);
fatal!("unknown command line option: {}", cmdline[i]);
}
}
@@ -1947,7 +1927,7 @@ pub fn parse_args(diag: &Diagnostics, target: &TargetTraits, cmdline: &[String])
if !a.directory.is_empty() {
if let Err(e) = std::env::set_current_dir(&a.directory) {
fatal!(diag, "chdir failed: {}: {e}", a.directory);
fatal!("chdir failed: {}: {e}", a.directory);
}
}
@@ -1973,7 +1953,7 @@ pub fn parse_args(diag: &Diagnostics, target: &TargetTraits, cmdline: &[String])
if let Some(val) = a.ttext_segment {
if val % a.page_size != 0 {
warn!(diag, "-Ttext-segment is not a multiple of page size: {val}");
warn!("-Ttext-segment is not a multiple of page size: {val}");
}
a.image_base = align_down(val, a.page_size);
} else if a.pic {
@@ -2030,10 +2010,10 @@ pub fn parse_args(diag: &Diagnostics, target: &TargetTraits, cmdline: &[String])
if !a.shared {
if !a.filter.is_empty() {
fatal!(diag, "-filter may not be used without -shared");
fatal!("-filter may not be used without -shared");
}
if !a.auxiliary.is_empty() {
fatal!(diag, "-auxiliary may not be used without -shared");
fatal!("-auxiliary may not be used without -shared");
}
}
@@ -2042,23 +2022,22 @@ pub fn parse_args(diag: &Diagnostics, target: &TargetTraits, cmdline: &[String])
// to handle it as an exception.
if (!target.is_rela || target.is_sh4) && !a.apply_dynamic_relocs {
fatal!(
diag,
"--no-apply-dynamic-relocs may not be used on {}",
target.name
);
}
if target.is_sparc && a.apply_dynamic_relocs {
fatal!(diag, "--apply-dynamic-relocs may not be used on SPARC64");
fatal!("--apply-dynamic-relocs may not be used on SPARC64");
}
if !a.section_start.is_empty() && !a.section_order.is_empty() {
fatal!(diag, "--section-start may not be used with --section-order");
fatal!("--section-start may not be used with --section-order");
}
if a.image_base % a.page_size != 0 {
fatal!(diag, "-image-base must be a multiple of -max-page-size");
fatal!("-image-base must be a multiple of -max-page-size");
}
if a.emulation == "arm32be" && !a.be8 {
fatal!(diag, "--be32 is not supported");
fatal!("--be32 is not supported");
}
if std::env::var("MOLD_REPRO").is_ok_and(|v| !v.is_empty()) {
+2 -15
View File
@@ -5,12 +5,11 @@ use std::collections::{HashMap, HashSet};
use std::fmt;
use std::marker::PhantomData;
use std::sync::atomic::{AtomicBool, AtomicU32};
use std::sync::{Arc, Mutex, MutexGuard, OnceLock};
use std::sync::{Mutex, MutexGuard, OnceLock};
use crate::arch::Arch;
use crate::cmdline::Args;
use crate::elf::{ElfSym, ElfWord};
use crate::error::{Diagnostics, HasDiagnostics};
use crate::input_files::{DsoId, FileId, FileList, InputFile, ObjId, ObjectFile, SharedFile};
use crate::input_sections::{
FragmentRef, InputSection, InputSectionId, SectionArena, SectionFragment, SectionRef,
@@ -86,7 +85,6 @@ pub struct SyntheticSymbols {
pub struct Context<E: Arch> {
// Command-line arguments
pub args: Args,
pub diag: Arc<Diagnostics>,
// Fully-expanded command line args
pub cmdline_args: Vec<String>,
@@ -219,14 +217,8 @@ pub struct Context<E: Arch> {
_arch: PhantomData<E>,
}
impl<E: Arch> HasDiagnostics for Context<E> {
fn diagnostics(&self) -> &Diagnostics {
&self.diag
}
}
impl<E: Arch> Context<E> {
pub fn new(args: Args, diag: Diagnostics, cmdline_args: Vec<String>) -> Context<E> {
pub fn new(args: Args, cmdline_args: Vec<String>) -> Context<E> {
let mut symbols = SymbolTable::new();
let syms = SyntheticSymbols {
entry: symbols.intern(crate::util::leak_bytes(args.entry.clone().into_bytes())),
@@ -255,7 +247,6 @@ impl<E: Arch> Context<E> {
verneed: VerneedSection::new(),
note_package: NotePackageSection::new(),
args,
diag: Arc::new(diag),
cmdline_args,
timers: Timers::new(),
symbols,
@@ -320,10 +311,6 @@ impl<E: Arch> Context<E> {
}
}
pub fn checkpoint(&self) {
self.diag.checkpoint();
}
/// Returns this worker's symbol bin. Looking it up once per file keeps the
/// synchronization cost outside the per-symbol loop.
pub(crate) fn symbol_bin(&self) -> MutexGuard<'_, Bins<SymbolSlot>> {
+21 -39
View File
@@ -1,7 +1,7 @@
//! The linker driver: runs the passes in order.
use std::fmt;
use std::sync::{mpsc, Arc};
use std::sync::mpsc;
use rayon::prelude::*;
@@ -9,7 +9,6 @@ use crate::arch::{self, Arch};
use crate::cmdline::{self, Args, TargetTraits};
use crate::context::Context;
use crate::elf::*;
use crate::error::Diagnostics;
use crate::input_files::FileId;
use crate::output_chunks::{self, ChunkId};
use crate::output_file::{split_ranges, OutputFile, Range};
@@ -22,13 +21,11 @@ use crate::{error, fatal, out, passes};
/// are instantiated in crates of their own.
pub fn main(
argv: Vec<String>,
link_for_target: impl Fn(&str, &[String], &Diagnostics) -> Result<i32, String>,
link_for_target: impl Fn(&str, &[String]) -> Result<i32, String>,
) -> i32 {
let diag = Diagnostics::new(false);
// Process -run option first. process_run_subcommand() does not return.
if argv.get(1).is_some_and(|a| a == "-run" || a == "--run") {
crate::subprocess::process_run_subcommand(&diag, &argv);
crate::subprocess::process_run_subcommand(&argv);
}
// parse_nonpositional_args() may chdir(2) for -C. If we end up
@@ -37,7 +34,7 @@ pub fn main(
let orig_cwd = std::env::current_dir().ok();
// Parse non-positional command line options
let cmdline = cmdline::expand_response_files(&diag, &argv);
let cmdline = cmdline::expand_response_files(&argv);
// Parse with x86-64 defaults; if the target turns out to be different,
// start over with the right one.
@@ -46,7 +43,7 @@ pub fn main(
if let Some(cwd) = &orig_cwd {
let _ = std::env::set_current_dir(cwd);
}
match link_for_target(&target, &cmdline, &diag) {
match link_for_target(&target, &cmdline) {
Ok(status) => return status,
Err(actual) => target = actual,
}
@@ -66,14 +63,6 @@ fn target_traits<E: Arch>() -> TargetTraits {
}
}
fn configure_diagnostics(diag: &Diagnostics, args: &Args) {
diag.set_color(args.color_diagnostics);
diag.set_fatal_warnings(args.fatal_warnings);
diag.set_suppress_warnings(args.suppress_warnings);
diag.set_noinhibit_exec(args.noinhibit_exec);
error::set_demangle(args.demangle);
}
fn thread_count(args: &Args) -> usize {
// mold doesn't scale well with too many threads, so limit it to 32.
args.thread_count.unwrap_or_else(|| {
@@ -99,13 +88,10 @@ 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], diag: &Diagnostics) -> Result<i32, String> {
let parsed = cmdline::parse_args(diag, &target_traits::<E>(), cmdline);
pub fn link<E: Arch>(cmdline: &[String]) -> Result<i32, String> {
let parsed = cmdline::parse_args(&target_traits::<E>(), cmdline);
let cmdline::ParsedArgs { args, jobs, .. } = parsed;
configure_diagnostics(diag, &args);
let mut ctx = Context::<E>::new(args, Diagnostics::new(false), cmdline.to_vec());
configure_diagnostics(&ctx.diag, &ctx.args);
let mut ctx = Context::<E>::new(args, cmdline.to_vec());
// If no -m option is given, deduce it from input files.
if ctx.args.emulation.is_empty() {
@@ -154,13 +140,14 @@ pub fn link<E: Arch>(cmdline: &[String], diag: &Diagnostics) -> Result<i32, Stri
// Version scripts and dynamic lists given on the command line.
for path in ctx.args.version_scripts.clone() {
let chroot = ctx.args.chroot.clone();
let mf = crate::mapped_file::open_file(&ctx.diag, &chroot, &path).or_else(|| {
ctx.args.library_paths.iter().find_map(|dir| {
crate::mapped_file::open_file(&ctx.diag, &chroot, &format!("{dir}/{path}"))
})
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}")))
});
let Some(mf) = mf else {
fatal!(ctx, "--version-script: file not found: {path}");
fatal!("--version-script: file not found: {path}");
};
let mut rctx = cmdline::ReaderContext::default();
crate::linker_script::Script::new(&mut ctx, &mut rctx, mf).parse_version_script();
@@ -254,11 +241,10 @@ pub fn link<E: Arch>(cmdline: &[String], diag: &Diagnostics) -> Result<i32, Stri
let gdb_input_job = if ctx.args.gdb_index && !ctx.args.relocatable {
let timer = t_before_copy.handle();
let inputs = crate::gdb_index::prepare_inputs(&mut ctx);
let diag = Arc::clone(&ctx.diag);
let (sender, receiver) = std::sync::mpsc::sync_channel(1);
let job = move || {
let timer = timer.child("read_gdb_index_inputs");
let data = crate::gdb_index::read_inputs::<E>(timer, &diag, inputs);
let data = crate::gdb_index::read_inputs::<E>(timer, inputs);
let _ = sender.send(data);
};
// Rayon has no task priorities, so queue this as an ordinary
@@ -388,10 +374,7 @@ pub fn link<E: Arch>(cmdline: &[String], diag: &Diagnostics) -> Result<i32, Stri
for name in ctx.args.require_defined.clone() {
let id = ctx.get_symbol(name.as_bytes());
if ctx.symbols[id].file().is_none() {
error!(
ctx,
"--require-defined: undefined symbol: {}", ctx.symbols[id]
);
error!("--require-defined: undefined symbol: {}", ctx.symbols[id]);
}
}
@@ -588,7 +571,6 @@ pub fn link<E: Arch>(cmdline: &[String], diag: &Diagnostics) -> Result<i32, Stri
// Output buffer
let t_open = ctx.timer("open_file");
let mut output = OutputFile::open(
&ctx.diag,
&ctx.args.output,
filesize,
0o777,
@@ -638,11 +620,11 @@ pub fn link<E: Arch>(cmdline: &[String], diag: &Diagnostics) -> Result<i32, Stri
}
t_copy.stop();
}
ctx.checkpoint();
error::checkpoint();
// Close the output file. This is the end of the linker's main job.
let t_close = ctx.timer("close_file");
output.close(&ctx.diag);
output.close();
drop(t_close);
// Handle --dependency-file
@@ -681,7 +663,7 @@ pub fn link<E: Arch>(cmdline: &[String], diag: &Diagnostics) -> Result<i32, Stri
if ctx.args.quick_exit {
error::exit_after_cleanup(0);
}
ctx.checkpoint();
error::checkpoint();
Ok(0)
}
@@ -870,6 +852,6 @@ impl<E: Arch> fmt::Debug for Context<E> {
}
/// Prints the version banner, for `-v`.
pub fn print_version(diag: &Diagnostics) {
out!(diag, "{}", cmdline::VERSION);
pub fn print_version() {
out!("{}", cmdline::VERSION);
}
+73 -123
View File
@@ -7,7 +7,7 @@
use std::fmt;
use std::io::{self, Write};
use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::{Arc, Mutex};
use std::sync::Mutex;
/// Whether to demangle symbol names in diagnostics. This is process-wide
/// state so that `Display` implementations, which have no access to the
@@ -22,116 +22,84 @@ pub fn demangle_enabled() -> bool {
DEMANGLE.load(Ordering::Relaxed)
}
/// Diagnostic settings and state, shared by all threads.
#[derive(Debug, Default)]
pub struct Diagnostics {
color: AtomicBool,
fatal_warnings: AtomicBool,
suppress_warnings: AtomicBool,
noinhibit_exec: AtomicBool,
has_error: AtomicBool,
lock: Mutex<()>,
// Diagnostic settings, error state and output serialization are process-wide.
static COLOR: AtomicBool = AtomicBool::new(false);
static FATAL_WARNINGS: AtomicBool = AtomicBool::new(false);
static SUPPRESS_WARNINGS: AtomicBool = AtomicBool::new(false);
static NOINHIBIT_EXEC: AtomicBool = AtomicBool::new(false);
static HAS_ERROR: AtomicBool = AtomicBool::new(false);
static OUTPUT_LOCK: Mutex<()> = Mutex::new(());
pub fn set_color(on: bool) {
COLOR.store(on, Ordering::Relaxed);
}
impl Diagnostics {
pub fn new(color: bool) -> Self {
let diag = Diagnostics::default();
diag.color.store(color, Ordering::Relaxed);
diag
}
pub fn set_fatal_warnings(on: bool) {
FATAL_WARNINGS.store(on, Ordering::Relaxed);
}
pub fn set_color(&self, on: bool) {
self.color.store(on, Ordering::Relaxed);
}
pub fn set_suppress_warnings(on: bool) {
SUPPRESS_WARNINGS.store(on, Ordering::Relaxed);
}
pub fn set_fatal_warnings(&self, on: bool) {
self.fatal_warnings.store(on, Ordering::Relaxed);
}
pub fn set_noinhibit_exec(on: bool) {
NOINHIBIT_EXEC.store(on, Ordering::Relaxed);
}
pub fn set_suppress_warnings(&self, on: bool) {
self.suppress_warnings.store(on, Ordering::Relaxed);
}
// Some C++ stdlibs don't support std::osyncstream even though
// it's is in the C++20 standard. So we implement it ourselves.
fn emit(prefix_mono: &str, prefix_color: &str, msg: fmt::Arguments) {
let prefix = if COLOR.load(Ordering::Relaxed) {
prefix_color
} else {
prefix_mono
};
let text = format!("{prefix}{msg}\n");
let _guard = OUTPUT_LOCK.lock().unwrap_or_else(|e| e.into_inner());
let _ = io::stderr().write_all(text.as_bytes());
}
pub fn set_noinhibit_exec(&self, on: bool) {
self.noinhibit_exec.store(on, Ordering::Relaxed);
}
/// Reports an unrecoverable error and exits.
pub fn fatal(msg: fmt::Arguments) -> ! {
emit("mold: fatal: ", "mold: \x1b[0;1;31mfatal:\x1b[0m ", msg);
exit_after_cleanup(1);
}
pub fn has_error(&self) -> bool {
self.has_error.load(Ordering::Relaxed)
/// Reports an error. With `--noinhibit-exec` it is downgraded to a warning.
pub fn error(msg: fmt::Arguments) {
if NOINHIBIT_EXEC.load(Ordering::Relaxed) {
emit("mold: warning: ", "mold: \x1b[0;1;35mwarning:\x1b[0m ", msg);
} else {
emit("mold: error: ", "mold: \x1b[0;1;31merror:\x1b[0m ", msg);
HAS_ERROR.store(true, Ordering::Relaxed);
}
}
fn color(&self) -> bool {
self.color.load(Ordering::Relaxed)
/// Reports a warning. With `--fatal-warnings` it is promoted to an error.
pub fn warn(msg: fmt::Arguments) {
if SUPPRESS_WARNINGS.load(Ordering::Relaxed) {
return;
}
// Some C++ stdlibs don't support std::osyncstream even though
// it's is in the C++20 standard. So we implement it ourselves.
fn emit(&self, prefix_mono: &str, prefix_color: &str, msg: fmt::Arguments) {
let prefix = if self.color() {
prefix_color
} else {
prefix_mono
};
let text = format!("{prefix}{msg}\n");
let _guard = self.lock.lock().unwrap_or_else(|e| e.into_inner());
let _ = io::stderr().write_all(text.as_bytes());
if FATAL_WARNINGS.load(Ordering::Relaxed) {
emit("mold: error: ", "mold: \x1b[0;1;31merror:\x1b[0m ", msg);
HAS_ERROR.store(true, Ordering::Relaxed);
} else {
emit("mold: warning: ", "mold: \x1b[0;1;35mwarning:\x1b[0m ", msg);
}
}
/// Reports an unrecoverable error and exits.
pub fn fatal(&self, msg: fmt::Arguments) -> ! {
self.emit("mold: fatal: ", "mold: \x1b[0;1;31mfatal:\x1b[0m ", msg);
/// Prints an informational message to stdout.
pub fn out(msg: fmt::Arguments) {
let text = format!("{msg}\n");
let _guard = OUTPUT_LOCK.lock().unwrap_or_else(|e| e.into_inner());
let _ = io::stdout().write_all(text.as_bytes());
}
/// Exits with a failure status if any error has been reported.
pub fn checkpoint() {
if HAS_ERROR.load(Ordering::Relaxed) {
exit_after_cleanup(1);
}
/// Reports an error. With `--noinhibit-exec` it is downgraded to a warning.
pub fn error(&self, msg: fmt::Arguments) {
if self.noinhibit_exec.load(Ordering::Relaxed) {
self.emit("mold: warning: ", "mold: \x1b[0;1;35mwarning:\x1b[0m ", msg);
} else {
self.emit("mold: error: ", "mold: \x1b[0;1;31merror:\x1b[0m ", msg);
self.has_error.store(true, Ordering::Relaxed);
}
}
/// Reports a warning. With `--fatal-warnings` it is promoted to an error.
pub fn warn(&self, msg: fmt::Arguments) {
if self.suppress_warnings.load(Ordering::Relaxed) {
return;
}
if self.fatal_warnings.load(Ordering::Relaxed) {
self.emit("mold: error: ", "mold: \x1b[0;1;31merror:\x1b[0m ", msg);
self.has_error.store(true, Ordering::Relaxed);
} else {
self.emit("mold: warning: ", "mold: \x1b[0;1;35mwarning:\x1b[0m ", msg);
}
}
/// Prints an informational message to stdout.
pub fn out(&self, msg: fmt::Arguments) {
let text = format!("{msg}\n");
let _guard = self.lock.lock().unwrap_or_else(|e| e.into_inner());
let _ = io::stdout().write_all(text.as_bytes());
}
/// Exits with a failure status if any error has been reported.
pub fn checkpoint(&self) {
if self.has_error() {
exit_after_cleanup(1);
}
}
}
/// Anything that can report diagnostics; implemented by the linker
/// context and by [`Diagnostics`] itself so that the macros below accept
/// either.
pub trait HasDiagnostics {
fn diagnostics(&self) -> &Diagnostics;
}
impl HasDiagnostics for Diagnostics {
fn diagnostics(&self) -> &Diagnostics {
self
}
}
/// Removes a partially-written output file, then terminates the process
@@ -152,29 +120,29 @@ pub fn exit_after_cleanup(status: i32) -> ! {
#[macro_export]
macro_rules! fatal {
($ctx:expr, $($arg:tt)*) => {
$crate::error::HasDiagnostics::diagnostics(&$ctx).fatal(format_args!($($arg)*))
($($arg:tt)*) => {
$crate::error::fatal(format_args!($($arg)*))
};
}
#[macro_export]
macro_rules! error {
($ctx:expr, $($arg:tt)*) => {
$crate::error::HasDiagnostics::diagnostics(&$ctx).error(format_args!($($arg)*))
($($arg:tt)*) => {
$crate::error::error(format_args!($($arg)*))
};
}
#[macro_export]
macro_rules! warn {
($ctx:expr, $($arg:tt)*) => {
$crate::error::HasDiagnostics::diagnostics(&$ctx).warn(format_args!($($arg)*))
($($arg:tt)*) => {
$crate::error::warn(format_args!($($arg)*))
};
}
#[macro_export]
macro_rules! out {
($ctx:expr, $($arg:tt)*) => {
$crate::error::HasDiagnostics::diagnostics(&$ctx).out(format_args!($($arg)*))
($($arg:tt)*) => {
$crate::error::out(format_args!($($arg)*))
};
}
@@ -184,21 +152,3 @@ macro_rules! out {
pub fn errno_string() -> String {
io::Error::last_os_error().to_string()
}
impl<T: HasDiagnostics + ?Sized> HasDiagnostics for &T {
fn diagnostics(&self) -> &Diagnostics {
(**self).diagnostics()
}
}
impl<T: HasDiagnostics + ?Sized> HasDiagnostics for &mut T {
fn diagnostics(&self) -> &Diagnostics {
(**self).diagnostics()
}
}
impl<T: HasDiagnostics + ?Sized> HasDiagnostics for Arc<T> {
fn diagnostics(&self) -> &Diagnostics {
(**self).diagnostics()
}
}
+2 -4
View File
@@ -3,7 +3,6 @@
use crate::arch::{self, TargetInfo};
use crate::archive_file;
use crate::elf::*;
use crate::error::Diagnostics;
use crate::mapped_file::MappedFile;
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
@@ -281,14 +280,13 @@ 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(
diag: &Diagnostics,
plugin: &str,
mf: &'static MappedFile,
script_target: impl FnOnce() -> Option<&'static str>,
) -> Option<&'static str> {
match get_file_type(plugin, mf) {
FileType::ElfObj | FileType::ElfDso | FileType::GccLtoObj => get_elf_target(mf.data()),
FileType::Ar => archive_file::read_fat_archive_members(diag, mf)
FileType::Ar => archive_file::read_fat_archive_members(mf)
.into_iter()
.find(|child| {
matches!(
@@ -297,7 +295,7 @@ pub fn get_machine_type(
)
})
.and_then(|child| get_elf_target(child.data())),
FileType::ThinAr => archive_file::read_thin_archive_members(diag, mf)
FileType::ThinAr => archive_file::read_thin_archive_members(mf)
.into_iter()
.find(|child| {
matches!(
+1 -1
View File
@@ -294,7 +294,7 @@ fn sweep<E: Arch>(ctx: &Context<E>) {
let _ = std::io::stdout().write_all(out.as_bytes());
} else {
std::fs::write(path, out)
.unwrap_or_else(|e| fatal!(ctx, "--print-gc-sections: cannot open {path}: {e}"));
.unwrap_or_else(|e| fatal!("--print-gc-sections: cannot open {path}: {e}"));
}
}
+49 -86
View File
@@ -85,7 +85,6 @@ use rayon::prelude::*;
use crate::arch::Arch;
use crate::context::Context;
use crate::elf::*;
use crate::error::Diagnostics;
use crate::fatal;
use crate::output_chunks::ChunkId;
use crate::output_file::{split_at_offsets, OutputFile};
@@ -280,17 +279,15 @@ struct UnitHeader {
}
/// A cursor over DWARF data.
struct Reader<'d, 'a, E: Arch> {
diag: &'d Diagnostics,
struct Reader<'a, E: Arch> {
data: &'a [u8],
pos: usize,
marker: std::marker::PhantomData<E>,
}
impl<'d, 'a, E: Arch> Reader<'d, 'a, E> {
fn new(diag: &'d Diagnostics, data: &'a [u8], pos: usize) -> Reader<'d, 'a, E> {
impl<'a, E: Arch> Reader<'a, E> {
fn new(data: &'a [u8], pos: usize) -> Reader<'a, E> {
Reader {
diag,
data,
pos,
marker: std::marker::PhantomData,
@@ -299,7 +296,7 @@ impl<'d, 'a, E: Arch> Reader<'d, 'a, E> {
fn take(&mut self, n: usize) -> &'a [u8] {
let Some(bytes) = self.data.get(self.pos..self.pos + n) else {
fatal!(self.diag, "--gdb-index: truncated debug info");
fatal!("--gdb-index: truncated debug info");
};
self.pos += n;
bytes
@@ -357,18 +354,18 @@ impl<'d, 'a, E: Arch> Reader<'d, 'a, E> {
// SAFETY: strnlen reads at most rest.len() bytes from the slice.
let len = unsafe { libc::strnlen(rest.as_ptr().cast(), rest.len()) };
if len == rest.len() {
fatal!(self.diag, "--gdb-index: unterminated string in debug info");
fatal!("--gdb-index: unterminated string in debug info");
}
self.pos += len + 1;
&rest[..len]
}
}
fn parse_unit_header<E: Arch>(diag: &Diagnostics, data: &[u8], pos: usize) -> UnitHeader {
fn parse_unit_header<E: Arch>(data: &[u8], pos: usize) -> UnitHeader {
// The first word is either a DWARF32 unit length or DWARF64's reserved
// marker. unit_length excludes its own encoding: four bytes in DWARF32, or
// the four-byte marker plus eight-byte length in DWARF64.
let mut r = Reader::<E>::new(diag, data, pos);
let mut r = Reader::<E>::new(data, pos);
let mut unit_length = r.u32() as u64;
let mut initial_length_size = 4;
let mut offset_size = 4;
@@ -380,10 +377,7 @@ fn parse_unit_header<E: Arch>(diag: &Diagnostics, data: &[u8], pos: usize) -> Un
let version = r.u16();
if version > 5 {
fatal!(
diag,
"--gdb-index: DWARF version {version} is not supported"
);
fatal!("--gdb-index: DWARF version {version} is not supported");
}
let mut hdr = UnitHeader {
@@ -431,32 +425,27 @@ struct RangeSections<'a> {
/// The first DIE refers to an abbreviation by its ULEB128 code. Walk the
/// unit's abbreviation table to find the attribute forms for that DIE.
fn find_cu_abbrev<'d, 'a, E: Arch>(
diag: &'d Diagnostics,
die: &mut Reader<'d, 'a, E>,
fn find_cu_abbrev<'a, E: Arch>(
die: &mut Reader<'a, E>,
abbrev_section: &'a [u8],
hdr: &UnitHeader,
) -> Reader<'d, 'a, E> {
) -> Reader<'a, E> {
if hdr.address_size as usize != E::WORD_SIZE {
fatal!(
diag,
"--gdb-index: unsupported address size {}",
hdr.address_size
);
fatal!("--gdb-index: unsupported address size {}", hdr.address_size);
}
let abbrev_code = die.uleb();
let mut abbrev = Reader::<E>::new(diag, abbrev_section, hdr.abbrev_offset as usize);
let mut abbrev = Reader::<E>::new(abbrev_section, hdr.abbrev_offset as usize);
loop {
let code = abbrev.uleb();
if code == 0 {
fatal!(diag, "--gdb-index: .debug_abbrev does not contain a record for the first .debug_info record");
fatal!("--gdb-index: .debug_abbrev does not contain a record for the first .debug_info record");
}
let tag = abbrev.uleb(); // tag
abbrev.u8(); // skip has_children byte
if code == abbrev_code {
// Found a record
if tag != DW_TAG_compile_unit as u64 && tag != DW_TAG_skeleton_unit as u64 {
fatal!(diag, "--gdb-index: the first entry's tag is not DW_TAG_compile_unit/DW_TAG_skeleton_unit but {tag:#x}");
fatal!("--gdb-index: the first entry's tag is not DW_TAG_compile_unit/DW_TAG_skeleton_unit but {tag:#x}");
}
return abbrev;
}
@@ -477,7 +466,7 @@ fn find_cu_abbrev<'d, 'a, E: Arch>(
/// .debug_info contains variable-length fields. `offset_size` is four or eight
/// bytes according to the DWARF32/DWARF64 format; Word<E> is instead the
/// target's address width. This function advances over one scalar value.
fn read_scalar<E: Arch>(diag: &Diagnostics, r: &mut Reader<E>, form: u64, offset_size: u8) -> u64 {
fn read_scalar<E: Arch>(r: &mut Reader<E>, form: u64, offset_size: u8) -> u64 {
match form as u32 {
DW_FORM_flag_present => 0,
DW_FORM_data1 | DW_FORM_flag | DW_FORM_strx1 | DW_FORM_addrx1 | DW_FORM_ref1 => {
@@ -495,7 +484,7 @@ fn read_scalar<E: Arch>(diag: &Diagnostics, r: &mut Reader<E>, form: u64, offset
r.cstr();
0
}
_ => fatal!(diag, "--gdb-index: unhandled debug info form: {form:#x}"),
_ => fatal!("--gdb-index: unhandled debug info form: {form:#x}"),
}
}
@@ -517,14 +506,9 @@ fn read_debug_ranges<E: Arch>(r: &mut Reader<E>, mut base: u64) -> Vec<(u64, u64
}
/// Read a range list from .debug_rnglists starting at the given offset.
fn read_rnglist<E: Arch>(
diag: &Diagnostics,
r: &mut Reader<E>,
addrx: &[u8],
mut base: u64,
) -> Vec<(u64, u64)> {
fn read_rnglist<E: Arch>(r: &mut Reader<E>, addrx: &[u8], mut base: u64) -> Vec<(u64, u64)> {
let addr_at = |i: u64| -> u64 {
let mut a = Reader::<E>::new(diag, addrx, i as usize * E::WORD_SIZE);
let mut a = Reader::<E>::new(addrx, i as usize * E::WORD_SIZE);
a.uint(E::WORD_SIZE)
};
let mut vec = Vec::new();
@@ -559,10 +543,7 @@ fn read_rnglist<E: Arch>(
let len = r.uleb();
vec.push((a, a + len));
}
kind => fatal!(
diag,
"--gdb-index: unknown .debug_rnglists entry kind: {kind:#x}"
),
kind => fatal!("--gdb-index: unknown .debug_rnglists entry kind: {kind:#x}"),
}
}
}
@@ -575,15 +556,11 @@ fn read_rnglist<E: Arch>(
/// ranges are read from .debug_ranges (or .debug_rnglists for DWARF5).
/// Otherwise, a range is read directly from .debug_info (or possibly
/// from .debug_addr for DWARF5).
fn read_address_ranges<E: Arch>(
diag: &Diagnostics,
secs: &RangeSections,
cu: &Compunit,
) -> Vec<(u64, u64)> {
fn read_address_ranges<E: Arch>(secs: &RangeSections, cu: &Compunit) -> Vec<(u64, u64)> {
// Read .debug_info to find the record at a given offset.
let hdr = parse_unit_header::<E>(diag, secs.info, cu.offset as usize);
let mut die = Reader::<E>::new(diag, secs.info, (cu.offset + hdr.header_size) as usize);
let mut abbrev = find_cu_abbrev::<E>(diag, &mut die, secs.abbrev, &hdr);
let hdr = parse_unit_header::<E>(secs.info, cu.offset as usize);
let mut die = Reader::<E>::new(secs.info, (cu.offset + hdr.header_size) as usize);
let mut abbrev = find_cu_abbrev::<E>(&mut die, secs.abbrev, &hdr);
// Now, read debug info records.
let mut low_pc: Option<(u64, u64)> = None;
@@ -599,7 +576,7 @@ fn read_address_ranges<E: Arch>(
if name == 0 && form == 0 {
break;
}
let val = read_scalar::<E>(diag, &mut die, form, hdr.offset_size);
let val = read_scalar::<E>(&mut die, form, hdr.offset_size);
match name as u32 {
DW_AT_low_pc => low_pc = Some((form, val)),
DW_AT_high_pc => high_pc = Some((form, val)),
@@ -610,9 +587,8 @@ fn read_address_ranges<E: Arch>(
}
}
let addr_at = |i: u64| -> u64 {
Reader::<E>::new(diag, addrx, i as usize * E::WORD_SIZE).uint(E::WORD_SIZE)
};
let addr_at =
|i: u64| -> u64 { Reader::<E>::new(addrx, i as usize * E::WORD_SIZE).uint(E::WORD_SIZE) };
let base = low_pc.map_or(0, |(_, val)| val);
// Before DWARF 5, DW_AT_ranges is a byte offset into .debug_ranges. In
@@ -620,24 +596,23 @@ fn read_address_ranges<E: Arch>(
// index into the offset table rooted at DW_AT_rnglists_base (rnglistx).
if let Some((form, val)) = ranges {
if hdr.version <= 4 {
let mut r = Reader::<E>::new(diag, secs.ranges, val as usize);
let mut r = Reader::<E>::new(secs.ranges, val as usize);
return read_debug_ranges::<E>(&mut r, base);
}
if form == DW_FORM_sec_offset as u64 {
let mut r = Reader::<E>::new(diag, secs.rnglists, val as usize);
return read_rnglist::<E>(diag, &mut r, addrx, base);
let mut r = Reader::<E>::new(secs.rnglists, val as usize);
return read_rnglist::<E>(&mut r, addrx, base);
}
let Some(list_base) = rnglists_base else {
fatal!(diag, "--gdb-index: missing DW_AT_rnglists_base");
fatal!("--gdb-index: missing DW_AT_rnglists_base");
};
let mut entry = Reader::<E>::new(
diag,
secs.rnglists,
(list_base + val * hdr.offset_size as u64) as usize,
);
let offset = entry.offset(hdr.offset_size);
let mut r = Reader::<E>::new(diag, secs.rnglists, (list_base + offset) as usize);
return read_rnglist::<E>(diag, &mut r, addrx, base);
let mut r = Reader::<E>::new(secs.rnglists, (list_base + offset) as usize);
return read_rnglist::<E>(&mut r, addrx, base);
}
// For one contiguous range, high_pc is either an address or an unsigned
@@ -649,10 +624,7 @@ fn read_address_ranges<E: Arch>(
let lo = match lo_form as u32 {
DW_FORM_addr => lo_val,
DW_FORM_addrx | DW_FORM_addrx1 | DW_FORM_addrx2 | DW_FORM_addrx4 => addr_at(lo_val),
_ => fatal!(
diag,
"--gdb-index: unhandled form for DW_AT_low_pc: {lo_form:#x}"
),
_ => fatal!("--gdb-index: unhandled form for DW_AT_low_pc: {lo_form:#x}"),
};
let hi = match hi_form as u32 {
DW_FORM_addr => hi_val,
@@ -660,10 +632,7 @@ fn read_address_ranges<E: Arch>(
DW_FORM_udata | DW_FORM_data1 | DW_FORM_data2 | DW_FORM_data4 | DW_FORM_data8 => {
lo + hi_val
}
_ => fatal!(
diag,
"--gdb-index: unhandled form for DW_AT_high_pc: {hi_form:#x}"
),
_ => fatal!("--gdb-index: unhandled form for DW_AT_high_pc: {hi_form:#x}"),
};
vec![(lo, hi)]
}
@@ -708,7 +677,6 @@ pub struct GdbInputFile {
/// `.gdb_index` work diverge. Relocations are reduced to the unit associations
/// the reader needs, so the background task never aliases an ObjectFile.
pub fn prepare_inputs<E: Arch>(ctx: &mut Context<E>) -> Vec<GdbInputFile> {
let diag = &*ctx.diag;
ctx.objs
.par_iter_mut()
.map(|file| {
@@ -729,7 +697,7 @@ pub fn prepare_inputs<E: Arch>(ctx: &mut Context<E>) -> Vec<GdbInputFile> {
if !isec.is_alive() {
continue;
}
isec.uncompress::<E>(diag, &name, section_name, input_size);
isec.uncompress::<E>(&name, section_name, input_size);
debug_info.push(DebugInfoInput {
shndx,
contents: isec.contents(),
@@ -749,11 +717,11 @@ pub fn prepare_inputs<E: Arch>(ctx: &mut Context<E>) -> Vec<GdbInputFile> {
let Some(isec) = file.section_mut(shndx as usize) else {
continue;
};
isec.uncompress::<E>(diag, &name, section_name, input_size);
isec.uncompress::<E>(&name, section_name, input_size);
let isec = file.section_at(shndx);
let mut relocations = Vec::new();
for rel in file.relocation_iter(diag, isec.relsec_idx()) {
for rel in file.relocation_iter(isec.relsec_idx()) {
let esym = &file.base.elf_syms[rel.r_sym() as usize];
if let Some(target) = file.symbol_section(rel.r_sym() as usize) {
relocations.push(PubnamesRelocation {
@@ -783,7 +751,7 @@ pub fn prepare_inputs<E: Arch>(ctx: &mut Context<E>) -> Vec<GdbInputFile> {
}
/// Reads the units of every live `.debug_info` section of a file.
fn read_debug_units<E: Arch>(diag: &Diagnostics, file: &GdbInputFile, file_idx: u32) -> FileUnits {
fn read_debug_units<E: Arch>(file: &GdbInputFile, file_idx: u32) -> FileUnits {
let mut units = FileUnits::default();
for input in &file.debug_info {
// Read every unit in one input .debug_info contribution. Keeping this separate
@@ -791,7 +759,7 @@ fn read_debug_units<E: Arch>(diag: &Diagnostics, file: &GdbInputFile, file_idx:
let contents = input.contents;
let mut pos = 0;
while pos < contents.len() {
let unit = parse_unit_header::<E>(diag, contents, pos);
let unit = parse_unit_header::<E>(contents, pos);
match unit.unit_type as u32 {
DW_UT_compile | DW_UT_partial | DW_UT_skeleton | DW_UT_split_compile => {
units.cus.push(Compunit {
@@ -811,7 +779,7 @@ fn read_debug_units<E: Arch>(diag: &Diagnostics, file: &GdbInputFile, file_idx:
shndx: input.shndx,
names: Vec::new(),
}),
kind => fatal!(diag, "--gdb-index: unknown unit type: {kind:#x}"),
kind => fatal!("--gdb-index: unknown unit type: {kind:#x}"),
}
pos += unit.size as usize;
}
@@ -859,12 +827,12 @@ fn pubnames_unit<'a>(
/// DWARF32 or DWARF64 header identifying one debug unit, followed by
/// (DIE offset, 1-byte kind, NUL-terminated name) tuples. The GNU kind byte lets
/// GDB distinguish functions, variables and types without reading their DIEs.
fn read_pubnames<E: Arch>(diag: &Diagnostics, file: &GdbInputFile, units: &mut FileUnits) {
fn read_pubnames<E: Arch>(file: &GdbInputFile, units: &mut FileUnits) {
for input in &file.pubnames {
let contents = input.contents;
let mut pos = 0;
while pos < contents.len() {
let mut r = Reader::<E>::new(diag, contents, pos);
let mut r = Reader::<E>::new(contents, pos);
let (set_size, offset_size, field_offset) = if r.u32() == u32::MAX {
// Header of one GNU pubnames or pubtypes set in DWARF64 format.
let size = r.u64();
@@ -880,7 +848,7 @@ fn read_pubnames<E: Arch>(diag: &Diagnostics, file: &GdbInputFile, units: &mut F
r.offset(offset_size); // debug_info_size
let Some(names) = pubnames_unit(input, field_offset, units) else {
fatal!(diag, "{}: corrupted debug_info_offset", file.name);
fatal!("{}: corrupted debug_info_offset", file.name);
};
let end = pos + set_size as usize;
while r.pos < end {
@@ -1022,17 +990,13 @@ fn estimate_names<T: Sync>(units: &[T], names: impl Fn(&T) -> &[NameRecord] + Sy
/// Read compilation units and their public names, deduplicate and intern the
/// names, and determine the constant-pool layout. This stage needs only input
/// sections, so it can run before output-section offsets are assigned.
pub fn read_inputs<E: Arch>(
timer: Timer,
diag: &Diagnostics,
files: Vec<GdbInputFile>,
) -> GdbIndexData {
pub fn read_inputs<E: Arch>(timer: Timer, files: Vec<GdbInputFile>) -> GdbIndexData {
let _timer = timer;
let per_file: Vec<FileUnits> = files
.par_iter()
.map(|file| {
let mut units = read_debug_units::<E>(diag, file, file.file);
read_pubnames::<E>(diag, file, &mut units);
let mut units = read_debug_units::<E>(file, file.file);
read_pubnames::<E>(file, &mut units);
for cu in &mut units.cus {
dedup_names(&mut cu.names);
}
@@ -1403,9 +1367,8 @@ pub fn write<E: Arch>(ctx: &mut Context<E>, output: &mut OutputFile) {
addr: section_contents(ctx, buf, ".debug_addr"),
rnglists: section_contents(ctx, buf, ".debug_rnglists"),
};
let diag = &ctx.diag;
data.cus.par_iter_mut().for_each(|cu| {
cu.ranges = read_address_ranges::<E>(diag, &secs, cu);
cu.ranges = read_address_ranges::<E>(&secs, cu);
cu.ranges.retain(|&(start, end)| start != 0 && start != end);
});
}
@@ -1437,7 +1400,7 @@ pub fn write<E: Arch>(ctx: &mut Context<E>, output: &mut OutputFile) {
let size = const_pool_offset + (data.type_pool_size + data.name_pool_size) as usize;
let file_size = output.len();
output.extend(&ctx.diag, size);
output.extend(size);
let buf = &mut output.buf()[file_size..];
// Write a section header. A zero language marks the version 9 shortcut
+1 -1
View File
@@ -663,7 +663,7 @@ fn print_icf_sections<E: Arch>(ctx: &Context<E>, sections: &[SectionRef]) {
let _ = std::io::stdout().write_all(out.as_bytes());
} else {
std::fs::write(path, out)
.unwrap_or_else(|e| fatal!(ctx, "--print-icf-sections: cannot open {path}: {e}"));
.unwrap_or_else(|e| fatal!("--print-icf-sections: cannot open {path}: {e}"));
}
}
+99 -178
View File
@@ -20,7 +20,6 @@ use crate::arch::{Arch, Family};
use crate::cmdline::Args;
use crate::context::Context;
use crate::elf::*;
use crate::error::Diagnostics;
use crate::input_sections::{
CieRecord, FdeRecord, FragmentRef, InputSection, MergeableSection, RelocationSpan, SFrameFde,
SectionList,
@@ -372,17 +371,13 @@ impl<E: Layout> InputFile<E> {
}
/// Reads the ELF and section headers.
fn parse(
diag: &Diagnostics,
mf: &'static MappedFile,
display: &dyn fmt::Display,
) -> InputFile<E> {
fn parse(mf: &'static MappedFile, display: &dyn fmt::Display) -> InputFile<E> {
let data = mf.data();
if data.len() < std::mem::size_of::<ElfEhdr<E>>() {
fatal!(diag, "{display}: file too small");
fatal!("{display}: file too small");
}
if !data.starts_with(b"\x7fELF") {
fatal!(diag, "{display}: not an ELF file");
fatal!("{display}: not an ELF file");
}
let ehdr = record_from_bytes::<ElfEhdr<E>>(data);
@@ -403,7 +398,6 @@ impl<E: Layout> InputFile<E> {
let Some(shdr_bytes) = data.get(shoff..shoff + num_sections * shdr_size) else {
fatal!(
diag,
"{}: e_shoff or e_shnum corrupted: {} {num_sections}",
mf.name,
data.len()
@@ -426,7 +420,7 @@ impl<E: Layout> InputFile<E> {
} else {
ehdr.e_shstrndx.get() as usize
};
file.shstrtab = file.section_contents_checked(diag, shstrtab_idx, display);
file.shstrtab = file.section_contents_checked(shstrtab_idx, display);
file
}
@@ -459,33 +453,23 @@ impl<E: Layout> InputFile<E> {
/// The contents of a section, checked against the file size.
#[inline]
pub fn section_contents(&self, diag: &Diagnostics, idx: usize) -> &'static [u8] {
self.section_contents_checked(diag, idx, &self.filename)
pub fn section_contents(&self, idx: usize) -> &'static [u8] {
self.section_contents_checked(idx, &self.filename)
}
fn section_contents_checked(
&self,
diag: &Diagnostics,
idx: usize,
display: &dyn fmt::Display,
) -> &'static [u8] {
fn section_contents_checked(&self, idx: usize, display: &dyn fmt::Display) -> &'static [u8] {
if idx >= self.shdrs.len() {
fatal!(diag, "{display}: invalid section index: {idx}");
fatal!("{display}: invalid section index: {idx}");
}
let shdr = &self.shdrs[idx];
let (sh_offset, sh_size) = (shdr.sh_offset.get(), shdr.sh_size.get());
self.section_contents_range_checked(diag, sh_offset, sh_size, display)
self.section_contents_range_checked(sh_offset, sh_size, display)
}
/// The contents described by a section header.
#[inline]
pub(crate) fn section_contents_from_shdr(
&self,
diag: &Diagnostics,
shdr: &ElfShdr<E>,
) -> &'static [u8] {
pub(crate) fn section_contents_from_shdr(&self, shdr: &ElfShdr<E>) -> &'static [u8] {
self.section_contents_range_checked(
diag,
shdr.sh_offset.get(),
shdr.sh_size.get(),
&self.filename,
@@ -494,7 +478,6 @@ impl<E: Layout> InputFile<E> {
fn section_contents_range_checked(
&self,
diag: &Diagnostics,
sh_offset: u64,
sh_size: u64,
display: &dyn fmt::Display,
@@ -503,10 +486,7 @@ impl<E: Layout> InputFile<E> {
let start = sh_offset as usize;
let end = start.saturating_add(sh_size as usize);
if end > data.len() {
fatal!(
diag,
"{display}: section header is out of range: {sh_offset}"
);
fatal!("{display}: section header is out of range: {sh_offset}");
}
&data[start..end]
}
@@ -902,15 +882,11 @@ struct CrelReader<'a, E: Layout> {
}
impl<'a, E: Arch> CrelReader<'a, E> {
fn new(diag: &Diagnostics, file: &dyn fmt::Display, mut data: &'a [u8]) -> Self {
fn new(file: &dyn fmt::Display, mut data: &'a [u8]) -> Self {
let hdr = read_uleb(&mut data);
let is_rela = hdr & 0b100 != 0;
if is_rela && !E::IS_RELA {
fatal!(
diag,
"{file}: CREL with addends is not supported for {}",
E::NAME
);
fatal!("{file}: CREL with addends is not supported for {}", E::NAME);
}
CrelReader {
data,
@@ -1033,12 +1009,8 @@ impl<E: Layout> ExactSizeIterator for RelocationIter<'_, E> {}
// at the moment.
//
// This function converts a CREL relocation table to a regular one.
fn decode_crel<E: Arch>(
diag: &Diagnostics,
file: &dyn fmt::Display,
data: &[u8],
) -> Box<[ElfRel<E>]> {
let reader = CrelReader::<E>::new(diag, file, data);
fn decode_crel<E: Arch>(file: &dyn fmt::Display, data: &[u8]) -> Box<[ElfRel<E>]> {
let reader = CrelReader::<E>::new(file, data);
// Own a fixed-size array without value-initializing trivial elements
// that the caller is about to overwrite.
let mut rels = Box::<[ElfRel<E>]>::new_uninit_slice(reader.len());
@@ -1111,11 +1083,11 @@ 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(diag: &Diagnostics, mf: &'static MappedFile, archive_name: String) -> ObjectFile<E> {
pub fn new(mf: &'static MappedFile, archive_name: String) -> ObjectFile<E> {
let display = FileName(&mf.name, &archive_name);
let base = InputFile::<E>::parse(diag, mf, &display);
let base = InputFile::<E>::parse(mf, &display);
let mut file = ObjectFile::with_base(base, archive_name);
file.parse_symbols(diag);
file.parse_symbols();
file
}
@@ -1196,11 +1168,7 @@ impl<E: Arch> ObjectFile<E> {
/// Iterates over relocations without materializing a deferred CREL table.
#[inline(always)]
pub(crate) fn relocation_iter<'a>(
&'a self,
diag: &Diagnostics,
relsec_idx: Option<u32>,
) -> RelocationIter<'a, E> {
pub(crate) fn relocation_iter<'a>(&'a self, relsec_idx: Option<u32>) -> RelocationIter<'a, E> {
let Some(relsec_idx) = relsec_idx else {
return RelocationIter::ordinary(&[]);
};
@@ -1209,7 +1177,7 @@ impl<E: Arch> ObjectFile<E> {
&& !self.decoded_crel.get(index).is_some_and(Option::is_some)
{
let data = self.input_relocation_data(relsec_idx);
return RelocationIter::crel(CrelReader::<E>::new(diag, self, data));
return RelocationIter::crel(CrelReader::<E>::new(self, data));
}
RelocationIter::ordinary(self.relocations(Some(relsec_idx)))
@@ -1392,27 +1360,25 @@ impl<E: Arch> ObjectFile<E> {
// Read global symbols before archive extraction so they can participate in
// symbol resolution without constructing input sections.
fn parse_symbols(&mut self, diag: &Diagnostics) {
fn parse_symbols(&mut self) {
if let Some(idx) = self.base.find_section(SHT_SYMTAB) {
let shdr = &self.base.shdrs[idx];
// In ELF, all local symbols precede global symbols in the symbol table.
// sh_info has an index of the first global symbol.
self.base.first_global = shdr.sh_info.get() as usize;
let contents = self.base.section_contents(diag, idx);
let contents = self.base.section_contents(idx);
if !contents
.len()
.is_multiple_of(std::mem::size_of::<ElfSym<E>>())
{
fatal!(diag, "{self}: corrupted section");
fatal!("{self}: corrupted section");
}
self.base.elf_syms = Cow::Borrowed(records_from_bytes::<ElfSym<E>>(contents));
self.base.symbol_strtab = self
.base
.section_contents(diag, shdr.sh_link.get() as usize);
self.base.symbol_strtab = self.base.section_contents(shdr.sh_link.get() as usize);
self.base.populate_symbol_name_lengths();
if let Some(idx) = self.base.find_section(SHT_SYMTAB_SHNDX) {
let bytes = self.base.section_contents(diag, idx);
let bytes = self.base.section_contents(idx);
self.symtab_shndx = bytes.chunks_exact(4).map(E::Endian::read_u32).collect();
}
}
@@ -1481,7 +1447,7 @@ impl<E: Arch> ObjectFile<E> {
// Read COMDAT groups and detect GCC offload objects. Both affect which input
// sections can be discarded before LTO.
pub fn read_section_metadata(&mut self, diag: &Diagnostics) {
pub fn read_section_metadata(&mut self) {
debug_assert!(!self.sections_parsed);
for i in 0..self.num_elf_sections {
@@ -1502,7 +1468,7 @@ impl<E: Arch> ObjectFile<E> {
continue;
}
if shdr.sh_info.get() as usize >= self.base.elf_syms.len() {
fatal!(diag, "{self}: invalid symbol index");
fatal!("{self}: invalid symbol index");
}
let esym = &self.base.elf_syms[shdr.sh_info.get() as usize];
@@ -1520,16 +1486,16 @@ impl<E: Arch> ObjectFile<E> {
continue;
}
let contents = self.base.section_contents_from_shdr(diag, shdr);
let contents = self.base.section_contents_from_shdr(shdr);
if contents.len() < 4 {
fatal!(diag, "{self}: empty SHT_GROUP");
fatal!("{self}: empty SHT_GROUP");
}
let kind = E::Endian::read_u32(contents);
if kind == 0 {
continue;
}
if kind != GRP_COMDAT {
fatal!(diag, "{self}: unsupported SHT_GROUP format");
fatal!("{self}: unsupported SHT_GROUP format");
}
// Ordinary global signatures already have a Symbol. Local, section
@@ -1626,9 +1592,9 @@ impl<E: Arch> ObjectFile<E> {
// <format-version>
// [ <section-length> "vendor-name" <file-tag> <size> <attribute>*]+ ]*
fn read_riscv_attributes(&mut self, diag: &Diagnostics, data: &'static [u8]) {
fn read_riscv_attributes(&mut self, data: &'static [u8]) {
if data.is_empty() {
fatal!(diag, "{self}: corrupted .riscv.attributes section");
fatal!("{self}: corrupted .riscv.attributes section");
}
if data[0] != b'A' {
return;
@@ -1638,7 +1604,7 @@ impl<E: Arch> ObjectFile<E> {
while !data.is_empty() {
let sz = E::Endian::read_u32(data) as usize;
if data.len() < sz || sz < 4 {
fatal!(diag, "{self}: corrupted .riscv.attributes section");
fatal!("{self}: corrupted .riscv.attributes section");
}
let mut p = &data[4..sz];
data = &data[sz..];
@@ -1648,7 +1614,7 @@ impl<E: Arch> ObjectFile<E> {
};
p = rest;
if p.first() != Some(&(ELF_TAG_FILE as u8)) {
fatal!(diag, "{self}: corrupted .riscv.attributes section");
fatal!("{self}: corrupted .riscv.attributes section");
}
p = &p[5..]; // skip the tag and the sub-sub-section size
@@ -1674,7 +1640,6 @@ impl<E: Arch> ObjectFile<E> {
fn initialize_sections(
&mut self,
diag: &Diagnostics,
args: &Args,
id: ObjId,
section_arena: &crate::input_sections::SectionArena,
@@ -1707,8 +1672,8 @@ impl<E: Arch> ObjectFile<E> {
continue;
}
if E::IS_RISCV && sh_type == SHT_RISCV_ATTRIBUTES {
let contents = self.base.section_contents_from_shdr(diag, shdr);
self.read_riscv_attributes(diag, contents);
let contents = self.base.section_contents_from_shdr(shdr);
self.read_riscv_attributes(contents);
continue;
}
@@ -1725,12 +1690,12 @@ impl<E: Arch> ObjectFile<E> {
continue;
};
if target_flags & SHF_ALLOC as u64 != 0 {
let contents = self.base.section_contents_from_shdr(diag, shdr);
let contents = self.base.section_contents_from_shdr(shdr);
if !contents
.len()
.is_multiple_of(std::mem::size_of::<ElfRel<E>>())
{
fatal!(diag, "{self}: corrupted section");
fatal!("{self}: corrupted section");
}
self.num_frag_syms += self.count_frag_syms(rels_from_bytes::<E>(contents));
}
@@ -1745,8 +1710,8 @@ impl<E: Arch> ObjectFile<E> {
};
let target_is_alloc = target_flags & SHF_ALLOC as u64 != 0;
if target_is_alloc || args.relocatable || args.emit_relocs {
let contents = self.base.section_contents_from_shdr(diag, shdr);
let decoded = decode_crel::<E>(diag, self, contents);
let contents = self.base.section_contents_from_shdr(shdr);
let decoded = decode_crel::<E>(self, contents);
// Count the relocations just decoded while they are in cache.
if target_is_alloc {
@@ -1760,7 +1725,6 @@ impl<E: Arch> ObjectFile<E> {
let name = cstr_at(self.base.shstrtab, shdr.sh_name.get() as usize);
if !is_known_section_type::<E>(shdr) {
fatal!(
diag,
"{self}: {}: unsupported section type: 0x{:x}",
util::display(name),
shdr.sh_type.get()
@@ -1774,9 +1738,7 @@ impl<E: Arch> ObjectFile<E> {
if name == b".note.GNU-stack" && !args.relocatable {
if flags & SHF_EXECINSTR as u64 != 0 {
if !args.z_execstack && !args.z_execstack_if_needed {
warn!(
diag,
"{self}: this file may cause a segmentation fault because it requires an executable stack. See https://github.com/rui314/mold/tree/main/docs/execstack.md for more info."
warn!("{self}: this file may cause a segmentation fault because it requires an executable stack. See https://github.com/rui314/mold/tree/main/docs/execstack.md for more info."
);
}
self.needs_executable_stack = true;
@@ -1785,7 +1747,7 @@ impl<E: Arch> ObjectFile<E> {
}
if name == b".note.gnu.property" {
let contents = self.base.section_contents_from_shdr(diag, shdr);
let contents = self.base.section_contents_from_shdr(shdr);
self.parse_note_gnu_property(contents);
continue;
}
@@ -1826,7 +1788,7 @@ impl<E: Arch> ObjectFile<E> {
if name == b".comment"
&& self
.base
.section_contents_from_shdr(diag, shdr)
.section_contents_from_shdr(shdr)
.starts_with(b"rustc ")
{
self.is_rust_obj = true;
@@ -1840,8 +1802,7 @@ impl<E: Arch> ObjectFile<E> {
continue;
}
let isec =
InputSection::new::<E>(diag, self, id, i as u32, shdr, BStr::new(name));
let isec = InputSection::new::<E>(self, id, i as u32, shdr, BStr::new(name));
// Save .llvm_addrsig for --icf=safe.
if shdr.sh_type.get() == SHT_LLVM_ADDRSIG && !args.relocatable {
@@ -1898,9 +1859,7 @@ impl<E: Arch> ObjectFile<E> {
// It exists only in DWARF 4, has been removed in DWARF 5 and neither
// GCC nor Clang generate it by default (-fdebug-types-section is
// needed). As such there is probably little need to support it.
fatal!(
diag,
"{self}: mold's --gdb-index is not compatible with .debug_types; to fix this error, remove -fdebug-types-section and recompile"
fatal!("{self}: mold's --gdb-index is not compatible with .debug_types; to fix this error, remove -fdebug-types-section and recompile"
);
}
}
@@ -1929,8 +1888,8 @@ impl<E: Arch> ObjectFile<E> {
if let Some(Some(decoded)) = self.decoded_crel.get(i) {
!decoded.is_empty()
} else {
let contents = self.base.section_contents_from_shdr(diag, shdr);
CrelReader::<E>::new(diag, self, contents).len() != 0
let contents = self.base.section_contents_from_shdr(shdr);
CrelReader::<E>::new(self, contents).len() != 0
}
} else {
shdr.sh_size.get() != 0
@@ -1988,7 +1947,6 @@ impl<E: Arch> ObjectFile<E> {
// them too so a different copy can become live.
pub(crate) fn parse_sections(
&mut self,
diag: &Diagnostics,
args: &Args,
id: ObjId,
section_arena: &crate::input_sections::SectionArena,
@@ -2017,8 +1975,8 @@ impl<E: Arch> ObjectFile<E> {
// symbol-table length.
unsafe {
allocator.allocate(count, |base_id, slots| {
self.initialize_sections(diag, args, id, section_arena);
self.initialize_local_symbols(diag, id, base_id, slots);
self.initialize_sections(args, id, section_arena);
self.initialize_local_symbols(id, base_id, slots);
self.sort_relocations();
self.sections_parsed = true;
});
@@ -2042,14 +2000,13 @@ impl<E: Arch> ObjectFile<E> {
/// initialized, those left over with blank symbols.
pub fn initialize_local_symbols(
&mut self,
diag: &Diagnostics,
id: ObjId,
base_id: SymbolId,
slots: &mut [MaybeUninit<Symbol>],
) {
let mut next = 0;
if !slots.is_empty() && !self.base.elf_syms.is_empty() {
next = self.initialize_local_symbols_into(diag, id, base_id, slots);
next = self.initialize_local_symbols_into(id, base_id, slots);
}
for slot in &mut slots[next..] {
slot.write(Symbol::new(BStr::new(b"")));
@@ -2059,7 +2016,6 @@ impl<E: Arch> ObjectFile<E> {
/// Returns the number of slots written.
fn initialize_local_symbols_into(
&mut self,
diag: &Diagnostics,
id: ObjId,
base_id: SymbolId,
slots: &mut [MaybeUninit<Symbol>],
@@ -2076,7 +2032,7 @@ impl<E: Arch> ObjectFile<E> {
for i in 1..self.base.first_global {
let esym = &self.base.elf_syms[i];
if esym.is_common() {
fatal!(diag, "{self}: common local symbol?");
fatal!("{self}: common local symbol?");
}
if self.is_discarded_comdat_sym(i, esym) {
self.base.symbols[i] = SymbolId::DISCARDED_COMDAT;
@@ -2143,7 +2099,7 @@ impl<E: Arch> ObjectFile<E> {
// In order to create .eh_frame_hdr, linker has to read .eh_frame.
//
// This function parses an input .eh_frame section.
pub fn parse_ehframe(&mut self, diag: &Diagnostics) {
pub fn parse_ehframe(&mut self) {
let eh_frame_sections = std::mem::take(&mut self.eh_frame_sections);
for &shndx in &eh_frame_sections {
let isec = self.section_at(shndx);
@@ -2185,12 +2141,8 @@ impl<E: Arch> ObjectFile<E> {
fde_ptr_size: 0,
is_leader: false,
};
cie.fde_ptr_size = parse_fde_encoding::<E>(
diag,
self,
isec,
&contents[begin_offset..end_offset],
);
cie.fde_ptr_size =
parse_fde_encoding::<E>(self, isec, &contents[begin_offset..end_offset]);
new_cies.push(cie);
} else {
// This is FDE.
@@ -2202,7 +2154,6 @@ impl<E: Arch> ObjectFile<E> {
}
if rels[rel_begin].r_offset() as usize - begin_offset != 8 {
fatal!(
diag,
"{}: FDE's first relocation should have offset 8",
isec.display(self)
);
@@ -2227,7 +2178,7 @@ impl<E: Arch> ObjectFile<E> {
.iter()
.position(|c| c.input_offset as i64 == target)
else {
fatal!(diag, "{}: bad FDE pointer", isec.display(self));
fatal!("{}: bad FDE pointer", isec.display(self));
};
fde.cie_idx = (cies_begin + ci) as u16;
}
@@ -2293,7 +2244,7 @@ impl<E: Arch> ObjectFile<E> {
// FDE's func_start, which points to the function the FDE describes; we
// use it both to find the function (for garbage collection and to obtain
// its output address) and to re-emit a PC-relative offset.
pub fn parse_sframe(&mut self, diag: &Diagnostics) {
pub fn parse_sframe(&mut self) {
let Some(abi) = E::SFRAME_ABI else {
return;
};
@@ -2306,15 +2257,14 @@ impl<E: Arch> ObjectFile<E> {
let data = isec.contents();
if data.len() < SFrameHeader::<E>::size() {
fatal!(diag, "{}: corrupted .sframe section", isec.display(self));
fatal!("{}: corrupted .sframe section", isec.display(self));
}
let hdr = SFrameHeader::<E>::parse(data);
if hdr.magic.get() != SFRAME_MAGIC {
fatal!(diag, "{}: corrupted .sframe section", isec.display(self));
fatal!("{}: corrupted .sframe section", isec.display(self));
}
if hdr.abi_arch != abi {
fatal!(
diag,
"{}: .sframe is incompatible with {}",
isec.display(self),
E::NAME
@@ -2373,7 +2323,6 @@ impl<E: Arch> ObjectFile<E> {
&mut self,
ctx_args: &Args,
merged: &RwLock<Vec<MergedSection<E>>>,
diag: &Diagnostics,
) {
let mut sections = std::mem::take(&mut self.sections);
for i in 0..sections.len() {
@@ -2392,7 +2341,7 @@ impl<E: Arch> ObjectFile<E> {
sections
.regular_section_mut(i)
.expect("a regular section")
.uncompress::<E>(diag, self, name, self.base.shdrs[i].sh_size.get() as usize);
.uncompress::<E>(self, name, self.base.shdrs[i].sh_size.get() as usize);
sections.set_mergeable(i, parent);
}
self.sections = sections;
@@ -2445,7 +2394,6 @@ impl<E: Arch> ObjectFile<E> {
// are not handled here for performance reasons.
pub(crate) fn reattach_section_symbols(
&self,
diag: &Diagnostics,
id: ObjId,
symbols: &SymbolEditor<'_>,
merged: &[MergedSection<E>],
@@ -2465,7 +2413,7 @@ impl<E: Arch> ObjectFile<E> {
continue;
}
let Some((frag, offset)) = m.fragment(esym.st_value().get()) else {
fatal!(diag, "{self}: bad symbol value: {}", esym.st_value().get());
fatal!("{self}: bad symbol value: {}", esym.st_value().get());
};
let frag = FragmentRef {
section: m.parent,
@@ -2494,7 +2442,6 @@ impl<E: Arch> ObjectFile<E> {
// to the newly created symbol.
pub(crate) fn reattach_fragment_relocations(
&mut self,
diag: &Diagnostics,
id: ObjId,
merged: &[MergedSection<E>],
base_id: SymbolId,
@@ -2568,7 +2515,7 @@ impl<E: Arch> ObjectFile<E> {
let Some((frag, in_frag_offset)) =
m.fragment(esym.st_value().get().wrapping_add(addend as u64))
else {
fatal!(diag, "{self}: bad relocation at {}", record.r_sym());
fatal!("{self}: bad relocation at {}", record.r_sym());
};
(
FragmentRef {
@@ -2628,17 +2575,13 @@ impl<E: Arch> ObjectFile<E> {
for rel in cie.rels::<E>(self) {
let sym = &ctx.symbols[self.base.symbols[rel.r_sym() as usize]];
if ctx.args.pic && rel.r_type() == E::R_ABS {
error!(
ctx,
"{self}: relocation {} in .eh_frame can not be used when making a position-independent output; recompile with -fPIE or -fPIC",
error!("{self}: relocation {} in .eh_frame can not be used when making a position-independent output; recompile with -fPIE or -fPIC",
rel.type_name::<E>()
);
}
if sym.is_imported() {
if sym.ty() != STT_FUNC {
fatal!(
ctx,
"{self}: {sym}: .eh_frame CIE record with an external data reference is not supported"
fatal!("{self}: {sym}: .eh_frame CIE record with an external data reference is not supported"
);
}
sym.add_flags(NEEDS_PLT);
@@ -2667,7 +2610,6 @@ impl<E: Arch> ObjectFile<E> {
// symbols in previous passes.
pub fn convert_common_symbols(
&mut self,
diag: &Diagnostics,
args: &Args,
id: ObjId,
symbols: &mut SymbolTable,
@@ -2686,7 +2628,7 @@ impl<E: Arch> ObjectFile<E> {
let sym = &symbols[sym_id];
if sym.file() != Some(FileId::Obj(id)) {
if args.warn_common {
warn!(diag, "{self}: multiple common symbols: {sym}");
warn!("{self}: multiple common symbols: {sym}");
}
continue;
}
@@ -2708,7 +2650,7 @@ impl<E: Arch> ObjectFile<E> {
self.elf_sections2.push(shdr);
let shndx = self.num_elf_sections + self.elf_sections2.len() - 1;
let isec = InputSection::new::<E>(diag, self, id, shndx as u32, &shdr, BStr::new(name));
let isec = InputSection::new::<E>(self, id, shndx as u32, &shdr, BStr::new(name));
self.sections.push(isec, section_arena);
let sym = &mut symbols[sym_id];
@@ -2783,7 +2725,7 @@ impl<E: Arch> ObjectFile<E> {
// Returns true if a given section contains a DWARF32 debug record.
// `isec` must be a .debug_info section.
pub fn is_dwarf32(&mut self, diag: &Diagnostics) -> bool {
pub fn is_dwarf32(&mut self) -> bool {
let name = self.to_string();
for shndx in self.debug_info_sections.clone() {
let isec = self.section_at(shndx);
@@ -2796,7 +2738,7 @@ impl<E: Arch> ObjectFile<E> {
}
let mut buf = [0u8; 12];
let input_size = self.shdr(shndx as usize).sh_size.get() as usize;
isec.copy_contents_to::<E>(diag, &name, section_name, input_size, &mut buf);
isec.copy_contents_to::<E>(&name, section_name, input_size, &mut buf);
// A .debug_info section contains compilation units (CUs). A 32-bit CU
// starts with a 32-bit size field, while a 64-bit CU starts with a
// magic number 0xffff'ffff followed by a 64-bit size field.
@@ -2821,7 +2763,7 @@ impl<E: Arch> ObjectFile<E> {
// An input .debug_info section may be compressed using zlib or zstd, so
// we need to uncompress it before accessing `isec->contents`.
let isec = self.section_mut(shndx as usize).unwrap();
isec.uncompress::<E>(diag, &name, section_name, input_size);
isec.uncompress::<E>(&name, section_name, input_size);
let contents = isec.contents();
let mut p = first_size;
while contents.len() - p >= 12 {
@@ -3007,12 +2949,7 @@ fn should_write_to_local_symtab<E: Arch>(ctx: &Context<E>, sym: &Symbol) -> bool
// Initialize cie's fde_ptr_size member by parsing the augmentation
// string. We need this member to remove FDE records referring to an
// empty segment from the output .eh_frame_hdr.
fn parse_fde_encoding<E: Arch>(
diag: &Diagnostics,
file: &ObjectFile<E>,
isec: &InputSection,
data: &[u8],
) -> u8 {
fn parse_fde_encoding<E: Arch>(file: &ObjectFile<E>, isec: &InputSection, data: &[u8]) -> u8 {
// Returns the size in bytes of a value in the DWARF exception header
// encoding `enc`.
let ptr_size = |enc: u8| -> u8 {
@@ -3021,7 +2958,6 @@ fn parse_fde_encoding<E: Arch>(
DW_EH_PE_udata4 | DW_EH_PE_sdata4 => 4,
DW_EH_PE_udata8 | DW_EH_PE_sdata8 => 8,
_ => fatal!(
diag,
"{}: unsupported FDE pointer encoding: {enc}",
isec.display(file)
),
@@ -3031,11 +2967,7 @@ fn parse_fde_encoding<E: Arch>(
// Skip the length, CIE ID and version fields.
let version = data[8];
if version != 1 && version != 3 {
fatal!(
diag,
"{}: unsupported CIE version: {version}",
isec.display(file)
);
fatal!("{}: unsupported CIE version: {version}", isec.display(file));
}
let mut rest = &data[9..];
// Read the augmentation string
@@ -3052,7 +2984,6 @@ fn parse_fde_encoding<E: Arch>(
}
if aug[0] != b'z' {
fatal!(
diag,
"{}: unsupported CIE augmentation string: {}",
isec.display(file),
util::display(aug)
@@ -3089,7 +3020,6 @@ fn parse_fde_encoding<E: Arch>(
// 'G' (AArch64 memory tagging) are not followed by data
}
_ => fatal!(
diag,
"{}: unsupported CIE augmentation string: {}",
isec.display(file),
util::display(aug)
@@ -3103,7 +3033,6 @@ fn parse_fde_encoding<E: Arch>(
// We support only raw absolute and PC-relative pointers.
if enc & 0xf0 != 0 && enc as u32 & 0xf0 != DW_EH_PE_pcrel {
fatal!(
diag,
"{}: unsupported FDE pointer encoding: {enc}",
isec.display(file)
);
@@ -3167,8 +3096,8 @@ impl<E: Arch> SharedFile<E> {
DsoId(self.base.file_index)
}
pub fn new(diag: &Diagnostics, mf: &'static MappedFile) -> SharedFile<E> {
let base = InputFile::<E>::parse(diag, mf, &FileName(&mf.name, ""));
pub fn new(mf: &'static MappedFile) -> SharedFile<E> {
let base = InputFile::<E>::parse(mf, &FileName(&mf.name, ""));
let mut file = SharedFile {
base,
soname: String::new(),
@@ -3179,29 +3108,27 @@ impl<E: Arch> SharedFile<E> {
symbol2_keys: Vec::new(),
sorted_syms: OnceLock::new(),
};
file.parse(diag);
file.parse();
file
}
/// The strings of the dynamic entries with the given tag, such as the
/// DT_NEEDED libraries.
fn dynamic_strings(&self, diag: &Diagnostics, tag: u64) -> Vec<&'static [u8]> {
fn dynamic_strings(&self, tag: u64) -> Vec<&'static [u8]> {
let Some(idx) = self.base.find_section(SHT_DYNAMIC) else {
return Vec::new();
};
let shdr = &self.base.shdrs[idx];
let strtab = self
.base
.section_contents(diag, shdr.sh_link.get() as usize);
ElfDyn::<E>::parse_all(self.base.section_contents(diag, idx))
let strtab = self.base.section_contents(shdr.sh_link.get() as usize);
ElfDyn::<E>::parse_all(self.base.section_contents(idx))
.into_iter()
.filter(|entry| entry.d_tag.get() == tag)
.map(|entry| cstr_at(strtab, entry.d_val.get() as usize))
.collect()
}
fn get_soname(&self, diag: &Diagnostics) -> String {
if let Some(soname) = self.dynamic_strings(diag, DT_SONAME as u64).first() {
fn get_soname(&self) -> String {
if let Some(soname) = self.dynamic_strings(DT_SONAME as u64).first() {
return String::from_utf8_lossy(soname).into_owned();
}
if self.base.mf.is_none_or(|mf| mf.given_fullpath) {
@@ -3210,22 +3137,22 @@ impl<E: Arch> SharedFile<E> {
path_filename(&self.base.filename)
}
fn parse(&mut self, diag: &Diagnostics) {
fn parse(&mut self) {
let Some(symtab_idx) = self.base.find_section(SHT_DYNSYM) else {
return;
};
let symtab_shdr = &self.base.shdrs[symtab_idx];
self.base.symbol_strtab = self
.base
.section_contents(diag, symtab_shdr.sh_link.get() as usize);
self.soname = self.get_soname(diag);
self.version_strings = self.read_version_strings(diag);
.section_contents(symtab_shdr.sh_link.get() as usize);
self.soname = self.get_soname();
self.version_strings = self.read_version_strings();
// Read a symbol table.
let esyms = records_from_bytes::<ElfSym<E>>(self.base.section_contents(diag, symtab_idx));
let esyms = records_from_bytes::<ElfSym<E>>(self.base.section_contents(symtab_idx));
let first = symtab_shdr.sh_info.get() as usize;
if esyms.len() < first {
fatal!(diag, "{self}: invalid symbol table");
fatal!("{self}: invalid symbol table");
}
// Only the symbols this file exports are kept, so the table is
// rebuilt rather than read in place.
@@ -3234,7 +3161,7 @@ impl<E: Arch> SharedFile<E> {
let vers: Vec<u16> = match self.base.find_section(SHT_GNU_VERSYM) {
Some(idx) => self
.base
.section_contents(diag, idx)
.section_contents(idx)
.chunks_exact(2)
.map(E::Endian::read_u16)
.collect(),
@@ -3260,7 +3187,6 @@ impl<E: Arch> SharedFile<E> {
if ver as u32 == VER_NDX_LOCAL {
if !esym.is_undef() {
fatal!(
diag,
"{self}: invalid version index 0 for defined symbol {}",
util::display(cstr_at(
self.base.symbol_strtab,
@@ -3362,12 +3288,12 @@ impl<E: Arch> SharedFile<E> {
}
}
pub fn dt_needed(&self, diag: &Diagnostics) -> Vec<&'static [u8]> {
self.dynamic_strings(diag, DT_NEEDED as u64)
pub fn dt_needed(&self) -> Vec<&'static [u8]> {
self.dynamic_strings(DT_NEEDED as u64)
}
pub fn dt_audit(&self, diag: &Diagnostics) -> &'static [u8] {
self.dynamic_strings(diag, DT_AUDIT as u64)
pub fn dt_audit(&self) -> &'static [u8] {
self.dynamic_strings(DT_AUDIT as u64)
.first()
.copied()
.unwrap_or(b"")
@@ -3408,7 +3334,7 @@ impl<E: Arch> SharedFile<E> {
// version names indexed by versym value. The versym index space is
// shared between the two sections (vd_ndx for defined symbols and
// vna_other for undefined ones), so a single vector covers both.
fn read_version_strings(&self, diag: &Diagnostics) -> Vec<&'static [u8]> {
fn read_version_strings(&self) -> Vec<&'static [u8]> {
let mut vec: Vec<&'static [u8]> = Vec::new();
let mut set = |idx: usize, name: &'static [u8]| {
if vec.len() <= idx {
@@ -3418,15 +3344,15 @@ impl<E: Arch> SharedFile<E> {
};
if let Some(idx) = self.base.find_section(SHT_GNU_VERDEF) {
let verdef = self.base.section_contents(diag, idx);
let verdef = self.base.section_contents(idx);
let strtab = self
.base
.section_contents(diag, self.base.shdrs[idx].sh_link.get() as usize);
.section_contents(self.base.shdrs[idx].sh_link.get() as usize);
let mut pos = 0;
loop {
let ver = ElfVerdef::<E>::parse(&verdef[pos..]);
if u32::from(ver.vd_ndx.get()) == VER_NDX_UNSPECIFIED {
fatal!(diag, "{self}: symbol version too large");
fatal!("{self}: symbol version too large");
}
let aux = ElfVerdaux::<E>::parse(&verdef[pos + ver.vd_aux.get() as usize..]);
set(
@@ -3441,10 +3367,10 @@ impl<E: Arch> SharedFile<E> {
}
if let Some(idx) = self.base.find_section(SHT_GNU_VERNEED) {
let verneed = self.base.section_contents(diag, idx);
let verneed = self.base.section_contents(idx);
let strtab = self
.base
.section_contents(diag, self.base.shdrs[idx].sh_link.get() as usize);
.section_contents(self.base.shdrs[idx].sh_link.get() as usize);
let mut pos = 0;
loop {
let vn = ElfVerneed::<E>::parse(&verneed[pos..]);
@@ -3821,17 +3747,12 @@ impl<E: Arch> SharedFile<E> {
}
/// Prints a `--trace-symbol` line for a reference or definition.
pub fn print_trace_symbol<R: SymbolRecord>(
diag: &Diagnostics,
file: &dyn fmt::Display,
esym: &R,
sym: &Symbol,
) {
pub fn print_trace_symbol<R: SymbolRecord>(file: &dyn fmt::Display, esym: &R, sym: &Symbol) {
if !esym.is_undef() {
out!(diag, "trace-symbol: {file}: definition of {sym}");
out!("trace-symbol: {file}: definition of {sym}");
} else if esym.is_weak() {
out!(diag, "trace-symbol: {file}: weak reference to {sym}");
out!("trace-symbol: {file}: weak reference to {sym}");
} else {
out!(diag, "trace-symbol: {file}: reference to {sym}");
out!("trace-symbol: {file}: reference to {sym}");
}
}
+12 -33
View File
@@ -11,7 +11,6 @@ use crate::arch::{Arch, Family};
use crate::cmdline::UnresolvedKind;
use crate::context::Context;
use crate::elf::*;
use crate::error::Diagnostics;
use crate::input_files::{ObjId, ObjectFile, RelocationIter};
use crate::output_chunks::merged::{MergedSection, MergedSectionId};
use crate::output_chunks::OutputSectionId;
@@ -176,7 +175,6 @@ fn to_p2align(alignment: u64) -> u8 {
impl InputSection {
#[inline]
pub fn new<E: Arch>(
diag: &Diagnostics,
file: &ObjectFile<E>,
file_id: ObjId,
shndx: u32,
@@ -187,7 +185,7 @@ impl InputSection {
if shdr.sh_type.get() == SHT_NOBITS || (shndx as usize) >= file.num_elf_sections {
&[]
} else {
file.base.section_contents_from_shdr(diag, shdr)
file.base.section_contents_from_shdr(shdr)
};
let (sh_size, p2align) = if shdr.sh_flags.get() & SHF_COMPRESSED as u64 != 0 {
@@ -227,7 +225,7 @@ impl InputSection {
// SH-4 stores addends to sections despite being RELA, which is a
// special (and buggy) case.
if !E::IS_RELA || E::FAMILY == Family::Sh4 {
isec.uncompress::<E>(diag, file, name, shdr.sh_size.get() as usize);
isec.uncompress::<E>(file, name, shdr.sh_size.get() as usize);
}
isec
}
@@ -439,18 +437,12 @@ impl InputSection {
/// Replaces compressed contents with a decompressed copy. `file` is
/// the owning file's name, for diagnostics.
pub fn uncompress<E: Arch>(
&mut self,
diag: &Diagnostics,
file: &dyn fmt::Display,
name: &BStr,
input_size: usize,
) {
pub fn uncompress<E: Arch>(&mut self, file: &dyn fmt::Display, name: &BStr, input_size: usize) {
if !self.is_compressed() {
return;
}
let mut buf = vec![0u8; self.sh_size as usize];
self.copy_contents_to::<E>(diag, file, name, input_size, &mut buf);
self.copy_contents_to::<E>(file, name, input_size, &mut buf);
self.contents = leak_bytes(buf).as_ptr() as usize;
self.flags.fetch_or(IS_UNCOMPRESSED, Ordering::Relaxed);
}
@@ -459,7 +451,6 @@ impl InputSection {
/// `sh_size` bytes long.
pub fn copy_contents_to<E: Arch>(
&self,
diag: &Diagnostics,
file: &dyn fmt::Display,
name: &BStr,
input_size: usize,
@@ -477,7 +468,7 @@ impl InputSection {
let hdr_size = std::mem::size_of::<ElfChdr<E>>();
if input_size < hdr_size {
fatal!(diag, "{file}:({name}): corrupted compressed section");
fatal!("{file}:({name}): corrupted compressed section");
}
// SAFETY: input_size comes from this section's validated ELF header.
let contents =
@@ -488,13 +479,10 @@ impl InputSection {
let result = match chdr.ch_type().get() {
ELFCOMPRESS_ZLIB => zlib_decompress(data, buf),
ELFCOMPRESS_ZSTD => zstd_decompress(data, buf),
ty => fatal!(
diag,
"{file}:({name}): unsupported compression type: 0x{ty:x}"
),
ty => fatal!("{file}:({name}): unsupported compression type: 0x{ty:x}"),
};
if let Err(msg) = result {
fatal!(diag, "{file}:({name}): uncompress failed: {msg}");
fatal!("{file}:({name}): uncompress failed: {msg}");
}
}
@@ -612,7 +600,7 @@ impl InputSection {
#[inline(always)]
pub(crate) fn relocations<'a, E: Arch>(&self, ctx: &'a Context<E>) -> RelocationIter<'a, E> {
let file = &ctx.objs[self.file.index()];
file.relocation_iter(&ctx.diag, self.relsec_idx())
file.relocation_iter(self.relsec_idx())
}
#[inline]
@@ -711,7 +699,6 @@ impl InputSection {
let rel = self.rels::<E>(file)[rel_idx];
let sym = &ctx.symbols[file.base.symbols[rel.r_sym() as usize]];
error!(
ctx,
"{}: relocation {} against {} out of range: {val} is not in [{lo}, {hi})",
self.display(file),
rel.type_name::<E>(),
@@ -919,7 +906,7 @@ impl InputSection {
ctx.objs[owner.index()]
);
}
error!(ctx, "{msg}");
error!("{msg}");
}
#[cold]
@@ -963,7 +950,7 @@ impl InputSection {
// section and shrink the overall size of it.
if self.r_deltas().is_empty() {
// If a section is not relaxed, we can copy it as a one big chunk.
self.copy_contents_to::<E>(&ctx.diag, file, self.name(file), input_size, buf);
self.copy_contents_to::<E>(file, self.name(file), input_size, buf);
} else {
// A relaxed section is copied piece-wise.
let contents = self.original_contents(file);
@@ -1103,9 +1090,7 @@ fn do_action<E: Arch>(
) {
match action {
Action::None => {}
Action::Error => error!(
ctx,
"{}: {} relocation at offset 0x{:x} against symbol `{}' can not be used; recompile with -fPIC",
Action::Error => error!("{}: {} relocation at offset 0x{:x} against symbol `{}' can not be used; recompile with -fPIC",
isec.display(&ctx.objs[isec.file.index()]),
rel.type_name::<E>(),
rel.r_offset(),
@@ -1194,9 +1179,7 @@ pub fn scan_tlsdesc<E: Arch>(ctx: &Context<E>, sym: &Symbol) {
pub fn check_tlsle<E: Arch>(ctx: &Context<E>, isec: &InputSection, sym: &Symbol, rel: &ElfRel<E>) {
if ctx.args.shared {
error!(
ctx,
"{}: relocation {} against `{}` can not be used when making a shared object; recompile with -fPIC",
error!("{}: relocation {} against `{}` can not be used when making a shared object; recompile with -fPIC",
isec.display(&ctx.objs[isec.file.index()]),
rel.type_name::<E>(),
sym
@@ -1499,7 +1482,6 @@ impl MergeableSection {
/// We do not support mergeable sections that have relocations.
pub fn split_contents<E: Arch>(
&mut self,
diag: &Diagnostics,
file: &dyn fmt::Display,
section: &InputSection,
name: &BStr,
@@ -1509,7 +1491,6 @@ impl MergeableSection {
let data = section.contents();
if data.len() > u32::MAX as usize {
fatal!(
diag,
"{}: mergeable section too large",
format_args!("{file}:({name})")
);
@@ -1523,7 +1504,6 @@ impl MergeableSection {
self.frag_offsets.push(pos as u32);
let Some(end) = find_null(data, pos, entsize) else {
fatal!(
diag,
"{}: string is not null terminated",
format_args!("{file}:({name})")
);
@@ -1533,7 +1513,6 @@ impl MergeableSection {
} else {
if !data.len().is_multiple_of(entsize) {
fatal!(
diag,
"{}: section size is not multiple of sh_entsize",
format_args!("{file}:({name})")
);
+16 -25
View File
@@ -65,7 +65,7 @@ fn unquote(s: &[u8]) -> &[u8] {
}
/// Reports a syntax error, pointing at the offending token.
fn syntax_error<E: Arch>(ctx: &Context<E>, mf: &MappedFile, tok: &[u8], msg: &str) -> ! {
fn syntax_error(mf: &MappedFile, tok: &[u8], msg: &str) -> ! {
let input = mf.data();
let pos = offset_of(input, tok).min(input.len().saturating_sub(1));
let (line_start, line) = get_line(input, pos);
@@ -74,14 +74,13 @@ fn syntax_error<E: Arch>(ctx: &Context<E>, mf: &MappedFile, tok: &[u8], msg: &st
let indent = "mold: fatal: ".len() + label.len();
let column = pos - line_start;
fatal!(
ctx,
"{label}{}\n{}^ {msg}",
util::display(line),
" ".repeat(indent + column)
);
}
fn tokenize<E: Arch>(ctx: &Context<E>, mf: &'static MappedFile) -> Vec<&'static [u8]> {
fn tokenize(mf: &'static MappedFile) -> Vec<&'static [u8]> {
let mut tokens = Vec::new();
let mut input = mf.data();
@@ -93,7 +92,7 @@ fn tokenize<E: Arch>(ctx: &Context<E>, mf: &'static MappedFile) -> Vec<&'static
if input.starts_with(b"/*") {
let Some(pos) = input[2..].windows(2).position(|w| w == b"*/") else {
syntax_error(ctx, mf, input, "unclosed comment");
syntax_error(mf, input, "unclosed comment");
};
input = &input[pos + 4..];
continue;
@@ -109,7 +108,7 @@ fn tokenize<E: Arch>(ctx: &Context<E>, mf: &'static MappedFile) -> Vec<&'static
if c == b'"' {
let Some(pos) = input[1..].iter().position(|&b| b == b'"') else {
syntax_error(ctx, mf, input, "unclosed string literal");
syntax_error(mf, input, "unclosed string literal");
};
tokens.push(&input[..pos + 2]);
input = &input[pos + 2..];
@@ -151,18 +150,14 @@ fn resolve_path<E: Arch>(
check_target: bool,
) -> &'static MappedFile {
let s = String::from_utf8_lossy(unquote(tok)).into_owned();
let diag = &ctx.diag;
let chroot = &ctx.args.chroot;
let open = |path: &str| -> Option<&'static MappedFile> {
let mf = open_file(diag, chroot, path)?;
let mf = open_file(chroot, path)?;
if check_target {
if let Some(target) =
crate::filetype::get_machine_type(diag, &ctx.args.plugin, mf, || None)
{
if let Some(target) = crate::filetype::get_machine_type(&ctx.args.plugin, mf, || None) {
if target != E::NAME {
warn!(
ctx,
"{path}: skipping incompatible file: {target} (e_machine {})",
E::E_MACHINE
);
@@ -177,7 +172,7 @@ fn resolve_path<E: Arch>(
// 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(diag, chroot, &path);
return must_open_file(chroot, &path);
}
if let Some(rest) = s.strip_prefix('=') {
@@ -186,7 +181,7 @@ fn resolve_path<E: Arch>(
} else {
format!("{}{rest}", ctx.args.sysroot)
};
return must_open_file(diag, chroot, &path);
return must_open_file(chroot, &path);
}
if let Some(lib) = s.strip_prefix("-l") {
@@ -210,7 +205,7 @@ fn resolve_path<E: Arch>(
}
}
syntax_error(ctx, mf, tok, &format!("library not found: {s}"));
syntax_error(mf, tok, &format!("library not found: {s}"));
}
/// The target a script produces output for: the one `OUTPUT_FORMAT`
@@ -220,7 +215,7 @@ pub fn output_target<E: Arch>(
rctx: &ReaderContext,
mf: &'static MappedFile,
) -> Option<&'static str> {
let tokens = tokenize(ctx, mf);
let tokens = tokenize(mf);
let mut tok: &[&'static [u8]] = &tokens;
if tok.len() >= 3 && tok[0] == b"OUTPUT_FORMAT" && tok[1] == b"(" {
@@ -248,7 +243,7 @@ impl<'a, E: Arch> Script<'a, E> {
rctx: &'a mut ReaderContext,
mf: &'static MappedFile,
) -> Self {
let tokens = tokenize(ctx, mf);
let tokens = tokenize(mf);
Script {
ctx,
rctx,
@@ -258,16 +253,12 @@ impl<'a, E: Arch> Script<'a, E> {
}
fn error(&self, tok: &[u8], msg: &str) -> ! {
syntax_error(self.ctx, self.mf, tok, msg)
syntax_error(self.mf, tok, msg)
}
fn skip<'t>(&self, tok: &'t [&'static [u8]], expected: &str) -> &'t [&'static [u8]] {
match tok.first() {
None => fatal!(
self.ctx,
"{}: expected '{expected}', but got EOF",
self.mf.name
),
None => fatal!("{}: expected '{expected}', but got EOF", self.mf.name),
Some(t) if *t == expected.as_bytes() => &tok[1..],
Some(t) => self.error(t, &format!("expected '{expected}'")),
}
@@ -298,7 +289,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!(self.ctx, "{}: expected ')', but got EOF", self.mf.name),
None => fatal!("{}: expected ')', but got EOF", self.mf.name),
}
}
@@ -324,7 +315,7 @@ impl<'a, E: Arch> Script<'a, E> {
}
if tok.is_empty() {
fatal!(self.ctx, "{}: expected ')', but got EOF", self.mf.name);
fatal!("{}: expected ')', but got EOF", self.mf.name);
}
&tok[1..]
}
@@ -556,7 +547,7 @@ fn read_label<'t>(tok: &'t [&'static [u8]], label: &[u8]) -> Option<&'t [&'stati
}
pub fn parse_dynamic_list<E: Arch>(ctx: &mut Context<E>, path: &str) -> Vec<DynamicPattern> {
let mf = must_open_file(&ctx.diag, &ctx.args.chroot.clone(), path);
let mf = must_open_file(&ctx.args.chroot.clone(), path);
let mut rctx = ReaderContext::default();
Script::new(ctx, &mut rctx, mf).parse_dynamic_list()
}
+18 -40
View File
@@ -118,7 +118,6 @@ use crate::arch::Arch;
use crate::cmdline::VERSION;
use crate::context::Context;
use crate::elf::*;
use crate::error::Diagnostics;
use crate::input_files::{FileId, ObjId, ObjectFile};
use crate::mapped_file::{must_open_file, MappedFile};
use crate::symbol::SymbolId;
@@ -369,18 +368,10 @@ static HOOKS: Mutex<Hooks> = Mutex::new(Hooks {
static CLAIM_LOCK: Mutex<()> = Mutex::new(());
static CLAIMED_SYMBOLS: Mutex<Vec<ClaimedSymbol>> = Mutex::new(Vec::new());
/// The linker's state, for callbacks. `DIAG` is set once the plugin is
/// loaded; `CONTEXT` only while the plugin is compiling, when nothing
/// else touches the context.
static DIAG: AtomicPtr<Diagnostics> = AtomicPtr::new(ptr::null_mut());
/// The linker's state, for callbacks. `CONTEXT` is set only while the plugin
/// is compiling, when nothing else touches the context.
static CONTEXT: AtomicPtr<c_void> = AtomicPtr::new(ptr::null_mut());
fn diag() -> &'static Diagnostics {
// SAFETY: set in `load_plugin` to the diagnostics of the context,
// which outlives the plugin.
unsafe { &*DIAG.load(Ordering::Acquire) }
}
// Event handlers
/// Reports a message the plugin formatted.
///
@@ -391,9 +382,9 @@ fn diag() -> &'static Diagnostics {
pub unsafe extern "C" fn mold_lto_report(level: c_int, msg: *const c_char) {
let msg = unsafe { CStr::from_ptr(msg) }.to_string_lossy();
match level {
LDPL_INFO => out!(diag(), "{msg}"),
LDPL_WARNING => warn!(diag(), "{msg}"),
_ => fatal!(diag(), "{msg}"),
LDPL_INFO => out!("{msg}"),
LDPL_WARNING => warn!("{msg}"),
_ => fatal!("{msg}"),
}
}
@@ -426,10 +417,10 @@ unsafe extern "C" fn add_symbols(
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(&ctx.diag, "", &path);
let mf = must_open_file("", &path);
mf.set_dependency(false);
let mut file = ObjectFile::<E>::new(&ctx.diag, mf, String::new());
let mut file = ObjectFile::<E>::new(mf, String::new());
file.is_lto_output = true;
file.base.set_reachable(true);
file.base.priority = ctx.lto_file_priority;
@@ -635,7 +626,7 @@ unsafe extern "C" fn get_api_version(
linker_version: *mut *const c_char,
) -> c_int {
if LAPI_V1 < minimal_api_supported {
fatal!(diag(), "LTO plugin does not support V0 or V1 API");
fatal!("LTO plugin does not support V0 or V1 API");
}
// The plugin reads the string after this function has returned
static LINKER_VERSION: OnceLock<CString> = OnceLock::new();
@@ -703,22 +694,16 @@ fn dlerror_string() -> String {
/// dlopen the linker plugin file
fn load_plugin<E: Arch>(ctx: &Context<E>) {
LOADED.get_or_init(|| {
DIAG.store(
std::sync::Arc::as_ptr(&ctx.diag) as *mut Diagnostics,
Ordering::Release,
);
let path = CString::new(ctx.args.plugin.as_str()).unwrap();
// SAFETY: plain dlopen/dlsym calls.
let onload: OnloadFn = unsafe {
let handle = dynamic_open(path.as_ptr());
if handle.is_null() {
fatal!(ctx, "could not open plugin file: {}", dlerror_string());
fatal!("could not open plugin file: {}", dlerror_string());
}
let onload = dynamic_symbol(handle, c"onload".as_ptr());
if onload.is_null() {
fatal!(
ctx,
"failed to load plugin {}: {}",
ctx.args.plugin,
dlerror_string()
@@ -849,7 +834,7 @@ fn load_plugin<E: Arch>(ctx: &Context<E>) {
// SAFETY: the transfer vector is terminated by LDPT_NULL.
let status = unsafe { onload(tv.as_ptr()) };
if status != LDPS_OK {
fatal!(ctx, "LTO plugin's onload failed: {status}");
fatal!("LTO plugin's onload failed: {status}");
}
});
}
@@ -869,13 +854,10 @@ fn supports_v3_api<E: Arch>(ctx: &Context<E>) -> bool {
/// Describes a file to the plugin, which reads it through a descriptor,
/// at an offset for an archive member. The descriptor stays open as long
/// as the returned file does.
fn plugin_input_file<E: Arch>(
ctx: &Context<E>,
mf: &'static MappedFile,
) -> (PluginInputFile, File) {
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!(ctx, "cannot open {}: {e}", container.name));
.unwrap_or_else(|e| fatal!("cannot open {}: {e}", container.name));
let input = PluginInputFile {
name: CString::new(container.name.as_str()).unwrap().into_raw(),
#[cfg(not(windows))]
@@ -897,9 +879,7 @@ pub fn read_lto_object<E: Arch>(
archive_name: String,
) -> Option<ObjectFile<E>> {
if ctx.args.plugin.is_empty() {
fatal!(
ctx,
"{}: unable to handle this LTO object file because the -plugin option was not provided. \
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
@@ -907,7 +887,7 @@ pub fn read_lto_object<E: Arch>(
}
load_plugin(ctx);
let Some(claim_file) = HOOKS.lock().unwrap().claim_file else {
fatal!(ctx, "LTO plugin did not register a claim_file hook");
fatal!("LTO plugin did not register a claim_file hook");
};
// We read input files in parallel, but the plugin interface is not
@@ -918,7 +898,7 @@ pub fn read_lto_object<E: Arch>(
// claim sequence.
let _claim = CLAIM_LOCK.lock().unwrap();
// Create plugin's object instance
let (input, file) = plugin_input_file(ctx, mf);
let (input, file) = plugin_input_file(mf);
let mut claimed: c_int = 0;
// claim_file_hook() calls add_symbols() which initializes `plugin_symbols`
// SAFETY: `input` describes an open file.
@@ -927,9 +907,7 @@ pub fn read_lto_object<E: Arch>(
if claimed == 0 {
if mf.parent.is_none() && mf.thin_parent.is_none() {
fatal!(
ctx,
"{}: not claimed by the LTO plugin; please make sure you are using the same compiler of the \
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
);
@@ -1069,7 +1047,7 @@ pub fn run_plugin<E: Arch>(ctx: &mut Context<E>) {
.expect("the plugin registered a claim_file hook");
for file in &ctx.objs {
if file.base.is_reachable() && !file.is_lto_input && file.is_gcc_offload_obj {
let (input, _file) = plugin_input_file(ctx, file.base.mf.unwrap());
let (input, _file) = plugin_input_file(file.base.mf.unwrap());
let mut claimed: c_int = 0;
// SAFETY: `input` describes an open file.
unsafe { claim_file(&input, &mut claimed) };
@@ -1088,7 +1066,7 @@ pub fn run_plugin<E: Arch>(ctx: &mut Context<E>) {
let status = unsafe { all_symbols_read() };
CONTEXT.store(ptr::null_mut(), Ordering::Release);
if status != LDPS_OK {
fatal!(ctx, "LTO: all_symbols_read_hook returns {status}");
fatal!("LTO: all_symbols_read_hook returns {status}");
}
}
+1 -1
View File
@@ -11,7 +11,7 @@ pub fn read_lto_object<E: Arch>(
_mf: &'static MappedFile,
_archive_name: String,
) -> Option<ObjectFile<E>> {
fatal!(ctx, "LTO is not supported on Windows");
fatal!("LTO is not supported on Windows");
}
pub fn run_plugin<E: Arch>(_ctx: &mut Context<E>) {}
+1 -1
View File
@@ -92,7 +92,7 @@ pub fn print_map<E: Arch>(ctx: &Context<E>) {
if !ctx.args.map.is_empty() && ctx.args.map != "-" {
std::fs::write(&ctx.args.map, out)
.unwrap_or_else(|e| fatal!(ctx, "--print-map: cannot open {}: {e}", ctx.args.map));
.unwrap_or_else(|e| fatal!("--print-map: cannot open {}: {e}", ctx.args.map));
} else {
let _ = std::io::stdout().write_all(out.as_bytes());
}
+15 -17
View File
@@ -18,7 +18,7 @@ use std::sync::Mutex;
#[cfg(not(windows))]
use rayon::prelude::*;
use crate::error::{errno_string, Diagnostics};
use crate::error::errno_string;
use crate::fatal;
use crate::util;
@@ -130,16 +130,16 @@ unsafe impl Sync for MappedFile {}
impl MappedFile {
/// Opens a file, returning `None` if it doesn't exist.
pub fn open(diag: &Diagnostics, path: &str) -> Option<&'static MappedFile> {
pub fn open(path: &str) -> Option<&'static MappedFile> {
let file = match File::open(path) {
Ok(file) => file,
Err(e) if e.kind() == std::io::ErrorKind::NotFound => return None,
Err(e) => fatal!(diag, "opening {path} failed: {e}"),
Err(e) => fatal!("opening {path} failed: {e}"),
};
let metadata = file
.metadata()
.unwrap_or_else(|e| fatal!(diag, "{path}: fstat failed: {e}"));
.unwrap_or_else(|e| fatal!("{path}: fstat failed: {e}"));
let size = metadata.len();
// True if `data` is a memory mapping of the file rather than a copy of
@@ -152,9 +152,9 @@ impl MappedFile {
(&file)
.take(size)
.read_to_end(&mut buf)
.unwrap_or_else(|e| fatal!(diag, "{path}: read failed: {e}"));
.unwrap_or_else(|e| fatal!("{path}: read failed: {e}"));
if buf.len() as u64 != size {
fatal!(diag, "{path}: file is shorter than its reported size");
fatal!("{path}: file is shorter than its reported size");
}
MappedBytes::from_mut(Vec::leak(buf))
} else {
@@ -166,9 +166,9 @@ 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!(diag, "{path}: file is too large to map"));
.unwrap_or_else(|_| fatal!("{path}: file is too large to map"));
let map = unsafe { memmap2::MmapOptions::new().len(map_len).map_copy(&file) }
.unwrap_or_else(|e| fatal!(diag, "{path}: mmap failed: {e}"));
.unwrap_or_else(|e| fatal!("{path}: mmap failed: {e}"));
is_mmapped = true;
MappedBytes::from_mut(Box::leak(Box::new(map)).as_mut())
};
@@ -189,9 +189,8 @@ impl MappedFile {
}
/// Opens a file that must exist.
pub fn must_open(diag: &Diagnostics, path: &str) -> &'static MappedFile {
MappedFile::open(diag, path)
.unwrap_or_else(|| fatal!(diag, "cannot open {path}: {}", errno_string()))
pub fn must_open(path: &str) -> &'static MappedFile {
MappedFile::open(path).unwrap_or_else(|| fatal!("cannot open {path}: {}", errno_string()))
}
/// Returns a view of a member of this archive.
@@ -276,18 +275,17 @@ impl MappedFile {
}
/// Opens an input file, applying `--chroot` to absolute paths.
pub fn open_file(diag: &Diagnostics, chroot: &str, path: &str) -> Option<&'static MappedFile> {
pub fn open_file(chroot: &str, path: &str) -> Option<&'static MappedFile> {
if path.starts_with('/') && !chroot.is_empty() {
let path = format!("{chroot}/{}", util::path_clean(path));
return MappedFile::open(diag, &path);
return MappedFile::open(&path);
}
MappedFile::open(diag, path)
MappedFile::open(path)
}
/// Opens an input file that must exist, applying `--chroot` to absolute paths.
pub fn must_open_file(diag: &Diagnostics, chroot: &str, path: &str) -> &'static MappedFile {
open_file(diag, chroot, path)
.unwrap_or_else(|| fatal!(diag, "cannot open {path}: {}", errno_string()))
pub fn must_open_file(chroot: &str, path: &str) -> &'static MappedFile {
open_file(chroot, path).unwrap_or_else(|| fatal!("cannot open {path}: {}", errno_string()))
}
/// Whether a path refers to something that is not a directory.
+2 -4
View File
@@ -362,7 +362,7 @@ pub fn copy_buf<E: Arch>(ctx: &Context<E>, buf: &mut [u8], hdr_buf: Option<&mut
// The table entries are 32-bit offsets from .eh_frame_hdr.
if !is_int(func_addr.wrapping_sub(origin) as i64, 32) {
let sym = &ctx.symbols[file.base.symbols[rels[0].r_sym() as usize]];
error!(ctx, "{file}: {sym}: address out of range of .eh_frame_hdr");
error!("{file}: {sym}: address out of range of .eh_frame_hdr");
}
E::Endian::write_i32(entry, func_addr.wrapping_sub(origin) as i32);
E::Endian::write_i32(
@@ -397,7 +397,6 @@ pub fn check_range<E: Arch>(
let file = &ctx.objs[isec.file.index()];
let sym: &Symbol = &ctx.symbols[file.base.symbols[rel.r_sym() as usize]];
error!(
ctx,
"{}: relocation {} against {sym} out of range: {val} is not in [{lo}, {hi})",
isec.display(file),
rel.type_name::<E>()
@@ -593,9 +592,8 @@ pub mod eh_frame_reloc {
}
/// Fatal error for `.eh_frame` contents that can't be handled.
pub fn unsupported<E: Arch>(ctx: &Context<E>, rel: &ElfRel<E>) -> ! {
pub fn unsupported<E: Arch>(rel: &ElfRel<E>) -> ! {
fatal!(
ctx,
"unsupported relocation in .eh_frame: {}",
rel.type_name::<E>()
)
+5 -9
View File
@@ -17,8 +17,8 @@ use crate::arch::Arch;
use crate::cmdline::Args;
use crate::context::Context;
use crate::elf::*;
use crate::error::Diagnostics;
use crate::input_sections::{InputSection, MergeableSection, SectionFragment, SectionRef};
use crate::out;
use crate::output_chunks::ChunkHeader;
use crate::output_file::split_at_offsets;
use crate::util::align_to;
@@ -86,7 +86,6 @@ pub struct ResolveMember<'a> {
pub struct ResolveOptions<'a> {
pub allocated_only: bool,
pub gc_sections: bool,
pub diag: &'a Diagnostics,
pub comment: Option<MergedSectionId>,
pub cmdline_args: &'a [String],
pub timers: &'a Timers,
@@ -248,7 +247,6 @@ pub fn resolve<E: Arch>(ctx: &mut Context<E>, id: MergedSectionId) {
objs,
merged_sections,
args,
diag,
..
} = ctx;
let msec = &merged_sections[id.index()];
@@ -276,7 +274,7 @@ pub fn resolve<E: Arch>(ctx: &mut Context<E>, id: MergedSectionId) {
.mergeable_with_section_mut(i as usize)
.expect("a mergeable section");
let name = isec.name(file);
m.split_contents::<E>(diag, file, isec, name, msec, &mut sketch);
m.split_contents::<E>(file, isec, name, msec, &mut sketch);
}
file.sections = slots;
sketch
@@ -341,7 +339,6 @@ pub fn resolve_sections<E: Arch>(
let ResolveOptions {
allocated_only,
gc_sections,
diag,
comment,
cmdline_args,
timers,
@@ -356,7 +353,6 @@ pub fn resolve_sections<E: Arch>(
.par_iter_mut()
.fold(HyperLogLog::default, |mut sketch, member| {
member.mergeable.split_contents::<E>(
diag,
&FileName {
filename: member.filename,
archive_name: member.archive_name,
@@ -572,11 +568,11 @@ pub fn write_to<E: Arch>(ctx: &Context<E>, id: MergedSectionId, buf: &mut [u8])
});
}
pub fn print_stats<E: Layout>(msec: &MergedSection<E>, diag: &Diagnostics) {
diag.out(format_args!(
pub fn print_stats<E: Layout>(msec: &MergedSection<E>) {
out!(
"{} estimation={} actual={}",
msec.hdr.name,
msec.estimation,
msec.fragments.len()
));
);
}
+2 -7
View File
@@ -90,23 +90,18 @@ pub mod copyrel {
debug_assert!(sym.is_undef_weak());
let file = ctx.file_display(sym.file().unwrap());
error!(
ctx,
"{file}: cannot create a copy relocation for {sym}; recompile with -fPIE or -fPIC"
);
return;
};
let dso = &ctx.dsos[dso_id.index()];
if sym.esym(ctx).st_visibility() == STV_PROTECTED {
error!(
ctx,
"{dso}: cannot create a copy relocation for protected symbol '{sym}'; recompile with -fPIC"
error!("{dso}: cannot create a copy relocation for protected symbol '{sym}'; recompile with -fPIC"
);
return;
}
if !ctx.args.z_copyreloc {
error!(
ctx,
"-z nocopyreloc: {dso}: cannot create a copy relocation for symbol '{sym}'; recompile with -fPIC"
error!("-z nocopyreloc: {dso}: cannot create a copy relocation for symbol '{sym}'; recompile with -fPIC"
);
return;
}
+3 -3
View File
@@ -278,7 +278,7 @@ fn entry_addr<E: Arch>(ctx: &Context<E>) -> u64 {
return sym.addr(ctx);
}
if !ctx.args.shared {
warn!(ctx, "entry symbol is not defined: {sym}");
warn!("entry symbol is not defined: {sym}");
}
0
}
@@ -383,8 +383,8 @@ pub fn to_phdr_flags<E: Arch>(ctx: &Context<E>, id: ChunkId) -> u32 {
if exec && ctx.args.execute_only {
if write {
error!(
ctx,
"--execute-only is not compatible with writable section: {}", hdr.name
"--execute-only is not compatible with writable section: {}",
hdr.name
);
}
return PF_X;
+2 -6
View File
@@ -522,17 +522,13 @@ pub fn scan_abs_relocations<E: Arch>(
let isec = ctx.input_section(r.isec);
if r.kind != AbsRelKind::None && isec.sh_flags & SHF_WRITE as u64 == 0 {
if ctx.args.z_text {
error!(
ctx,
"{}: relocation at offset 0x{:x} against symbol `{}' can not be used; recompile with -fPIC",
error!("{}: relocation at offset 0x{:x} against symbol `{}' can not be used; recompile with -fPIC",
ctx.input_section_display(r.isec),
r.offset,
sym
);
} else if ctx.args.warn_textrel {
warn!(
ctx,
"{}: relocation against symbol `{}' in read-only section",
warn!("{}: relocation against symbol `{}' in read-only section",
ctx.input_section_display(r.isec),
sym
);
+2 -4
View File
@@ -685,9 +685,7 @@ pub mod dynsym {
let nshdrs = ctx.shdr.as_ref().map_or(0, |s| {
s.hdr.shdr.sh_size.get() / ElfShdr::<E>::size() as u64
});
error!(
ctx,
"{}: .dynsym: too many output sections: {nshdrs} requested, but ELF allows at most 65279",
error!("{}: .dynsym: too many output sections: {nshdrs} requested, but ELF allows at most 65279",
ctx.args.output
);
return;
@@ -933,7 +931,7 @@ pub mod gnu_hash {
/// Chunk ids for which dynamic symbol information must exist.
pub fn require_dynsym<E: Arch>(ctx: &Context<E>) {
if ctx.dynsym.symbols.is_empty() {
fatal!(ctx, "internal error: dynamic symbol table required");
fatal!("internal error: dynamic symbol table required");
}
}
+21 -33
View File
@@ -18,7 +18,6 @@ use memmap2::MmapMut;
#[cfg(not(windows))]
use memmap2::MmapOptions;
use crate::error::Diagnostics;
use crate::fatal;
/// The temporary file being written, removed on a fatal error.
@@ -187,13 +186,7 @@ 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(
diag: &Diagnostics,
path: &str,
size: u64,
perm: u32,
overwrite_in_place: bool,
) -> OutputFile {
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());
if is_special {
return OutputFile {
@@ -241,14 +234,14 @@ impl OutputFile {
.create(true)
.truncate(true)
.open(&tmp)
.unwrap_or_else(|e| fatal!(diag, "cannot open {}: {e}", tmp.display()))
.unwrap_or_else(|e| fatal!("cannot open {}: {e}", tmp.display()))
});
*TMPFILE.lock().unwrap() = Some(tmp.clone());
set_permissions(&file, perm)
.unwrap_or_else(|e| fatal!(diag, "{}: fchmod failed: {e}", tmp.display()));
.unwrap_or_else(|e| fatal!("{}: fchmod failed: {e}", tmp.display()));
file.set_len(size)
.unwrap_or_else(|e| fatal!(diag, "{}: ftruncate failed: {e}", tmp.display()));
.unwrap_or_else(|e| fatal!("{}: ftruncate failed: {e}", tmp.display()));
preallocate(&file, size);
let storage = map_file(&file, size);
@@ -271,13 +264,13 @@ impl OutputFile {
/// made unusable right away so that a stale one isn't picked up by
/// accident; [`Self::resize`] gives it its size.
#[cfg(not(windows))]
pub fn open_locked(diag: &Diagnostics, path: &str, perm: u32) -> OutputFile {
pub fn open_locked(path: &str, perm: u32) -> OutputFile {
let mut file = open_options(perm)
.read(true)
.write(true)
.create(true)
.open(path)
.unwrap_or_else(|e| fatal!(diag, "cannot open {path}: {e}"));
.unwrap_or_else(|e| fatal!("cannot open {path}: {e}"));
// SAFETY: flock on a valid descriptor.
unsafe {
libc::flock(file.as_raw_fd(), libc::LOCK_EX);
@@ -286,7 +279,7 @@ 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!(diag, "{path}: write failed: {e}"));
.unwrap_or_else(|e| fatal!("{path}: write failed: {e}"));
OutputFile {
path: path.to_string(),
tmp_path: None,
@@ -297,18 +290,18 @@ impl OutputFile {
}
#[cfg(windows)]
pub fn open_locked(diag: &Diagnostics, _path: &str, _perm: u32) -> OutputFile {
fatal!(diag, "LockingOutputFile is not supported on Windows");
pub fn open_locked(_path: &str, _perm: u32) -> OutputFile {
fatal!("LockingOutputFile is not supported on Windows");
}
/// Sets the size of a file opened with [`Self::open_locked`].
pub fn resize(&mut self, diag: &Diagnostics, size: u64) {
pub fn resize(&mut self, size: u64) {
let file = self
.file
.as_ref()
.expect("resizing an output file that isn't a file");
file.set_len(size)
.unwrap_or_else(|e| fatal!(diag, "{}: ftruncate failed: {e}", self.path));
.unwrap_or_else(|e| fatal!("{}: ftruncate failed: {e}", self.path));
// As in MemoryMappedOutputFile, we map the file with twice as much
// address space as its size so that extend() can grow the file into
// the mapping in place.
@@ -352,13 +345,13 @@ impl OutputFile {
// it for .gdb_index, whose size is not known until all other
// sections have been written. The new space is zero-initialized.
// `buf` and `ctx.buf` may move as a result of this call.
pub fn extend(&mut self, diag: &Diagnostics, size: usize) {
pub fn extend(&mut self, size: usize) {
let new_len = self.len() + size;
match (&mut self.storage, &self.file) {
(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!(diag, "{}: ftruncate failed: {e}", self.path));
.unwrap_or_else(|e| fatal!("{}: ftruncate failed: {e}", self.path));
preallocate(file, new_len as u64);
if new_len <= map.len() {
*len = new_len;
@@ -380,7 +373,7 @@ impl OutputFile {
/// Finishes writing and moves the file into place. The mapping is
/// released without waiting for the data to reach the disk.
pub fn close(self, diag: &Diagnostics) {
pub fn close(self) {
#[cfg(not(windows))]
set_output_buffer_range(0, 0);
match self.storage {
@@ -391,7 +384,7 @@ impl OutputFile {
stdout
.write_all(&vec)
.and_then(|()| stdout.flush())
.unwrap_or_else(|e| fatal!(diag, "write failed: {e}"));
.unwrap_or_else(|e| fatal!("write failed: {e}"));
// Close the descriptor too: the parent process may
// already have exited, and a program that then runs
// the output would fail with ETXTBSY while this
@@ -410,10 +403,10 @@ impl OutputFile {
.create(true)
.truncate(true)
.open(&self.path)
.unwrap_or_else(|e| fatal!(diag, "cannot open {}: {e}", self.path)),
.unwrap_or_else(|e| fatal!("cannot open {}: {e}", self.path)),
};
file.write_all(&vec)
.unwrap_or_else(|e| fatal!(diag, "{}: write failed: {e}", self.path));
.unwrap_or_else(|e| fatal!("{}: write failed: {e}", self.path));
}
}
if let Some(tmp) = self.tmp_path {
@@ -426,12 +419,7 @@ impl OutputFile {
std::mem::forget(old);
}
std::fs::rename(&tmp, &self.path).unwrap_or_else(|e| {
fatal!(
diag,
"cannot rename {} to {}: {e}",
tmp.display(),
self.path
)
fatal!("cannot rename {} to {}: {e}", tmp.display(), self.path)
});
*TMPFILE.lock().unwrap() = None;
}
@@ -497,10 +485,10 @@ pub fn split_ranges<'a>(buf: &'a mut [u8], ranges: &[Range]) -> Vec<&'a mut [u8]
}
/// Creates a file with the given contents.
pub fn write_file(diag: &Diagnostics, path: &str, contents: &[u8]) {
let mut file = File::create(path).unwrap_or_else(|e| fatal!(diag, "cannot open {path}: {e}"));
pub fn write_file(path: &str, contents: &[u8]) {
let mut file = File::create(path).unwrap_or_else(|e| fatal!("cannot open {path}: {e}"));
file.write_all(contents)
.unwrap_or_else(|e| fatal!(diag, "{path}: write failed: {e}"));
.unwrap_or_else(|e| fatal!("{path}: write failed: {e}"));
}
#[cfg(all(test, not(windows)))]
+45 -93
View File
@@ -247,7 +247,7 @@ fn mark_live_file<E: Arch>(ctx: &Context<E>, id: FileId) -> Vec<FileId> {
sym.merge_visibility(esym.st_visibility());
}
if sym.is_traced() {
crate::input_files::print_trace_symbol(&ctx.diag, file, esym, sym);
crate::input_files::print_trace_symbol(file, esym, sym);
}
if let Some(target) = sym.file() {
@@ -257,7 +257,6 @@ fn mark_live_file<E: Arch>(ctx: &Context<E>, id: FileId) -> Vec<FileId> {
found.push(target);
if sym.is_traced() {
out!(
ctx,
"trace-symbol: {file} keeps {} for {sym}",
ctx.file_display(target)
);
@@ -272,7 +271,7 @@ fn mark_live_file<E: Arch>(ctx: &Context<E>, id: FileId) -> Vec<FileId> {
let esym = &file.base.elf_syms[i];
let sym = &ctx.symbols[file.base.symbols[i]];
if sym.is_traced() {
crate::input_files::print_trace_symbol(&ctx.diag, file, esym, sym);
crate::input_files::print_trace_symbol(file, esym, sym);
}
// We follow undefined symbols in a DSO only to handle
// --no-allow-shlib-undefined.
@@ -284,7 +283,6 @@ fn mark_live_file<E: Arch>(ctx: &Context<E>, id: FileId) -> Vec<FileId> {
found.push(target);
if sym.is_traced() {
out!(
ctx,
"trace-symbol: {file} keeps {} for {sym}",
ctx.file_display(target)
);
@@ -597,18 +595,13 @@ fn parse_input_sections<E: Arch>(ctx: &mut Context<E>) {
// the other signatures for interning while each file's metadata is hot.
let t = ctx.timer("read_section_metadata");
let (bins, pending): (Vec<Bins<ComdatSymbolSlot>>, Vec<Vec<PendingComdatOwner>>) = {
let Context {
objs,
symbols,
diag,
..
} = ctx;
let Context { objs, symbols, .. } = ctx;
let work = ComdatWorkBins::new();
objs.par_iter_mut().for_each(|file| {
work.with_local(|bins, pending| {
if file.base.is_reachable() {
if file.base.mf.is_some() && !file.is_lto_input && !file.sections_parsed {
file.read_section_metadata(diag);
file.read_section_metadata();
}
let priority = file.base.priority;
let is_lto_output = file.is_lto_output;
@@ -771,7 +764,6 @@ fn parse_input_sections<E: Arch>(ctx: &mut Context<E>) {
objs,
symbols,
section_arena,
diag,
args,
..
} = ctx;
@@ -783,7 +775,6 @@ fn parse_input_sections<E: Arch>(ctx: &mut Context<E>) {
&& !file.sections_parsed
{
file.parse_sections(
diag,
args,
file.id(),
section_arena,
@@ -951,9 +942,8 @@ pub fn do_lto<E: Arch>(ctx: &mut Context<E>) {
pub fn parse_eh_frame_sections<E: Arch>(ctx: &mut Context<E>) {
let _t = ctx.timer("parse_eh_frame_sections");
let Context { objs, diag, .. } = ctx;
objs.par_iter_mut()
.for_each(|file| file.parse_ehframe(diag));
let Context { objs, .. } = ctx;
objs.par_iter_mut().for_each(|file| file.parse_ehframe());
}
pub fn parse_sframe_sections<E: Arch>(ctx: &mut Context<E>) {
@@ -961,8 +951,8 @@ pub fn parse_sframe_sections<E: Arch>(ctx: &mut Context<E>) {
return;
}
let _t = ctx.timer("parse_sframe_sections");
let Context { objs, diag, .. } = ctx;
objs.par_iter_mut().for_each(|file| file.parse_sframe(diag));
let Context { objs, .. } = ctx;
objs.par_iter_mut().for_each(|file| file.parse_sframe());
}
/// Registers direct, stable member borrows with their merged sections for a
@@ -1002,12 +992,11 @@ pub fn create_merged_sections<E: Arch>(ctx: &mut Context<E>) {
objs,
merged_sections,
args,
diag,
..
} = ctx;
let merged = RwLock::new(std::mem::take(merged_sections));
objs.par_iter_mut()
.for_each(|file| file.convert_mergeable_sections(args, &merged, diag));
.for_each(|file| file.convert_mergeable_sections(args, &merged));
*merged_sections = merged.into_inner().unwrap();
}
drop(t);
@@ -1036,7 +1025,6 @@ pub fn create_merged_sections<E: Arch>(ctx: &mut Context<E>) {
crate::output_chunks::merged::ResolveOptions {
allocated_only: true,
gc_sections: ctx.args.gc_sections,
diag: &ctx.diag,
comment: ctx.comment,
cmdline_args: &ctx.cmdline_args,
timers: &ctx.timers,
@@ -1049,7 +1037,6 @@ pub fn create_merged_sections<E: Arch>(ctx: &mut Context<E>) {
objs,
symbols,
merged_sections,
diag,
..
} = ctx;
@@ -1081,8 +1068,8 @@ pub fn create_merged_sections<E: Arch>(ctx: &mut Context<E>) {
.zip(slices)
.for_each(|(file, (base_id, slots))| {
let id = file.id();
file.reattach_section_symbols(diag, id, &editor, merged_sections);
file.reattach_fragment_relocations(diag, id, merged_sections, base_id, slots);
file.reattach_section_symbols(id, &editor, merged_sections);
file.reattach_fragment_relocations(id, merged_sections, base_id, slots);
});
});
}
@@ -1094,20 +1081,12 @@ pub fn convert_common_symbols<E: Arch>(ctx: &mut Context<E>) {
let Context {
objs,
symbols,
diag,
args,
section_arena,
..
} = ctx;
for file in objs {
file.convert_common_symbols(
diag,
args,
file.id(),
symbols,
default_version,
section_arena,
);
file.convert_common_symbols(args, file.id(), symbols, default_version, section_arena);
}
}
@@ -1736,7 +1715,7 @@ pub fn add_synthetic_symbols<E: Arch>(ctx: &mut Context<E>) {
DefsymValue::Symbol(target) => {
let sym2 = ctx.get_symbol(target.as_bytes());
if ctx.symbols[sym2].file().is_none() {
error!(ctx, "--defsym: undefined symbol: {}", ctx.symbols[sym2]);
error!("--defsym: undefined symbol: {}", ctx.symbols[sym2]);
continue;
}
let sym2_esym = ctx.symbols[sym2].esym(ctx);
@@ -1791,15 +1770,9 @@ pub fn check_cet_errors<E: Arch>(ctx: &Context<E>) {
] {
if !has_feature(file, feature) {
if warning {
warn!(
ctx,
"{file}: -cet-report=warning: missing GNU_PROPERTY_X86_FEATURE_1_{name}"
);
warn!("{file}: -cet-report=warning: missing GNU_PROPERTY_X86_FEATURE_1_{name}");
} else {
error!(
ctx,
"{file}: -cet-report=error: missing GNU_PROPERTY_X86_FEATURE_1_{name}"
);
error!("{file}: -cet-report=error: missing GNU_PROPERTY_X86_FEATURE_1_{name}");
}
}
}
@@ -1808,7 +1781,6 @@ pub fn check_cet_errors<E: Arch>(ctx: &Context<E>) {
pub fn print_dependencies<E: Arch>(ctx: &Context<E>) {
out!(
ctx,
"# This is an output of the mold linker's --print-dependencies option.\n\
#\n\
# Each line consists of 4 fields, <section1>, <section2>, <symbol-type> and\n\
@@ -1823,9 +1795,8 @@ pub fn print_dependencies<E: Arch>(ctx: &Context<E>) {
let println = |src: &dyn std::fmt::Display, sym: &Symbol, is_weak: bool| {
let kind = if is_weak { 'w' } else { 'u' };
match sym.input_section() {
Some(sec) => out!(ctx, "{src}\t{}\t{kind}\t{sym}", ctx.section_display(sec)),
Some(sec) => out!("{src}\t{}\t{kind}\t{sym}", ctx.section_display(sec)),
None => out!(
ctx,
"{src}\t{}\t{kind}\t{sym}",
ctx.file_display(sym.file().unwrap())
),
@@ -1891,11 +1862,11 @@ pub fn write_repro_file<E: Arch>(ctx: &Context<E>) {
let path = format!("{}.repro.tar", ctx.args.output);
let basedir = format!("{}.repro", path_filename(&ctx.args.output));
let mut tar = crate::util::tar::TarWriter::open(&path, &basedir)
.unwrap_or_else(|e| fatal!(ctx, "cannot open {path}: {e}"));
.unwrap_or_else(|e| fatal!("cannot open {path}: {e}"));
let write = |tar: &mut crate::util::tar::TarWriter, name: &str, data: &[u8]| {
tar.append(name, data)
.unwrap_or_else(|e| fatal!(ctx, "{path}: write failed: {e}"));
.unwrap_or_else(|e| fatal!("{path}: write failed: {e}"));
};
write(
&mut tar,
@@ -1913,8 +1884,7 @@ pub fn write_repro_file<E: Arch>(ctx: &Context<E>) {
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.
let reopened =
crate::mapped_file::must_open_file(&ctx.diag, &ctx.args.chroot, &mf.name);
let reopened = crate::mapped_file::must_open_file(&ctx.args.chroot, &mf.name);
let abs = std::fs::canonicalize(&mf.name)
.map(|p| p.to_string_lossy().into_owned())
.unwrap_or(mf.name.clone());
@@ -1966,13 +1936,12 @@ pub fn check_duplicate_symbols<E: Arch>(ctx: &Context<E>) {
}
}
error!(
ctx,
"duplicate symbol: {file}: {}: {sym}",
ctx.file_display(owner)
);
}
});
ctx.checkpoint();
crate::error::checkpoint();
}
// A default-versioned symbol `foo@@VER` can also be referred to as
@@ -2007,7 +1976,6 @@ pub fn check_symbol_version_conflicts<E: Arch>(ctx: &Context<E>) {
if !sym2.is_weak() && sym2.ver_idx as u32 == (sym.ver_idx as u32 & !VERSYM_HIDDEN) {
let file = &ctx.objs[obj.index()];
error!(
ctx,
"duplicate symbol: {file}: {}: {}",
ctx.file_display(sym2.file().unwrap()),
crate::util::display(file.base.symbol_name_in(sym.sym_idx as usize))
@@ -2015,7 +1983,7 @@ pub fn check_symbol_version_conflicts<E: Arch>(ctx: &Context<E>) {
}
}
}
ctx.checkpoint();
crate::error::checkpoint();
}
// GCC and Clang set the SHT_NOBITS flag for an output section only if the
@@ -2080,7 +2048,7 @@ pub fn check_shlib_undefined<E: Arch>(ctx: &mut Context<E>) {
// for the program, because if there's a missing .so, an undefined
// symbol might be defined by that library.
let complete = ctx.dsos.iter().all(|dso| {
dso.dt_needed(&ctx.diag)
dso.dt_needed()
.iter()
.all(|needed| ctx.dso_sonames.contains(&*String::from_utf8_lossy(needed)))
});
@@ -2102,10 +2070,7 @@ pub fn check_shlib_undefined<E: Arch>(ctx: &mut Context<E>) {
&& !defined
&& !has_dso_definition(ctx, id)
{
error!(
ctx,
"{file}: --no-allow-shlib-undefined: undefined symbol: {sym}"
);
error!("{file}: --no-allow-shlib-undefined: undefined symbol: {sym}");
}
}
});
@@ -2154,9 +2119,7 @@ pub fn check_symbol_types<E: Arch>(ctx: &Context<E>) {
&& st_type2 != STT_NOTYPE
&& canonicalize(esym1.st_type()) != canonicalize(st_type2)
{
warn!(
ctx,
"symbol type mismatch: {sym}\n>>> defined in {} as {}\n>>> defined in {file} as {}",
warn!("symbol type mismatch: {sym}\n>>> defined in {} as {}\n>>> defined in {file} as {}",
ctx.file_display(owner),
stt_to_string(esym1.st_type()),
stt_to_string(st_type2)
@@ -2326,9 +2289,9 @@ pub fn sort_debug_info_sections<E: Arch>(ctx: &mut Context<E>) {
// Record whether each input file contains DWARF32 debug info.
{
let Context { objs, diag, .. } = ctx;
let Context { objs, .. } = ctx;
objs.par_iter_mut().for_each(|file| {
file.is_dwarf32 = !file.debug_info_sections.is_empty() && file.is_dwarf32(diag);
file.is_dwarf32 = !file.debug_info_sections.is_empty() && file.is_dwarf32();
});
}
@@ -2407,11 +2370,10 @@ pub fn fixup_ctors_in_init_array<E: Arch>(ctx: &mut Context<E>) {
{
continue;
}
let diag = &ctx.diag;
let file = &ctx.objs[section_ref.file.index()];
let isec = ctx.input_section(m);
if !isec.sh_size.is_multiple_of(word as u64) {
fatal!(diag, "{}: section corrupted", isec.display(file));
fatal!("{}: section corrupted", isec.display(file));
}
let mut contents = isec.contents().to_vec();
let n = contents.len() / word;
@@ -2497,7 +2459,7 @@ pub fn add_dynamic_strings<E: Arch>(ctx: &mut Context<E>) {
.map(crate::input_files::SharedFile::id)
.collect();
for id in dso_ids {
let audit = ctx.dsos[id.index()].dt_audit(&ctx.diag);
let audit = ctx.dsos[id.index()].dt_audit();
if !audit.is_empty() {
if !ctx.args.depaudit.is_empty() {
ctx.args.depaudit.push(':');
@@ -2558,7 +2520,6 @@ pub fn compute_section_sizes<E: Arch>(ctx: &mut Context<E>) {
crate::output_chunks::merged::ResolveOptions {
allocated_only: false,
gc_sections: ctx.args.gc_sections,
diag: &ctx.diag,
comment: ctx.comment,
cmdline_args: &ctx.cmdline_args,
timers: &ctx.timers,
@@ -2651,7 +2612,6 @@ pub fn claim_unresolved_symbols<E: Arch>(ctx: &mut Context<E>) {
let sym = &ctx.symbols[id];
if sym.is_traced() {
out!(
ctx,
"trace-symbol: {}: unresolved{} symbol {sym}",
ctx.objs[obj_id.index()],
if esym.is_weak() { " weak" } else { "" }
@@ -2734,7 +2694,7 @@ pub fn scan_relocations<E: Arch>(ctx: &mut Context<E>) {
.for_each(|file| file.scan_relocations(ctx_ref));
}
// Exit if there was a relocation that refers an undefined symbol.
ctx.checkpoint();
crate::error::checkpoint();
// Word-size absolute relocations (e.g. R_X86_64_64) are handled
// separately because they can be promoted to dynamic relocations.
@@ -2761,7 +2721,7 @@ pub fn scan_relocations<E: Arch>(ctx: &mut Context<E>) {
osec.dynrel_offsets = offsets;
}
// Exit if the absolute-relocation pass reported an error.
ctx.checkpoint();
crate::error::checkpoint();
// Aggregate dynamic symbols to a single vector.
let syms: Vec<SymbolId> = {
@@ -2875,7 +2835,7 @@ pub fn scan_relocations<E: Arch>(ctx: &mut Context<E>) {
}
if ctx.has_textrel.load(Ordering::Relaxed) && ctx.args.warn_textrel {
warn!(ctx, "creating a DT_TEXTREL in an output file");
warn!("creating a DT_TEXTREL in an output file");
}
}
@@ -2931,12 +2891,12 @@ pub fn report_undef_errors<E: Arch>(ctx: &Context<E>) {
// Remove the trailing '\n' because Error/Warn adds it automatically
msg.pop();
if ctx.args.unresolved_symbols == UnresolvedKind::Error {
error!(ctx, "{msg}");
error!("{msg}");
} else {
warn!(ctx, "{msg}");
warn!("{msg}");
}
}
ctx.checkpoint();
crate::error::checkpoint();
}
pub fn create_reloc_sections<E: Arch>(ctx: &mut Context<E>) {
@@ -3095,14 +3055,12 @@ pub fn apply_version_script<E: Arch>(ctx: &mut Context<E>) {
if v.is_cpp {
if !cpp_matcher.add(v.pattern, i as i64) {
fatal!(
ctx,
"invalid version pattern: {}",
crate::util::display(v.pattern)
);
}
} else if has_wildcard(v.pattern) && !matcher.add(v.pattern, i as i64) {
fatal!(
ctx,
"invalid version pattern: {}",
crate::util::display(v.pattern)
);
@@ -3140,7 +3098,6 @@ pub fn apply_version_script<E: Arch>(ctx: &mut Context<E>) {
let sym = &ctx.symbols[id];
if sym.file().is_none() && !ctx.args.undefined_version {
warn!(
ctx,
"{}: cannot assign version `{}` to symbol `{sym}`: symbol not found",
v.source,
crate::util::display(v.ver_str)
@@ -3208,7 +3165,6 @@ pub fn parse_symbol_version<E: Arch>(ctx: &mut Context<E>) {
}
let Some(&ver_idx) = verdefs.get(ver) else {
error!(
ctx,
"{}: symbol {} has undefined version {}",
ctx.objs[obj_id.index()],
ctx.symbols[id],
@@ -3382,7 +3338,6 @@ pub fn compute_import_export<E: Arch>(ctx: &mut Context<E>) {
if p.is_cpp {
if !cpp_matcher.add(p.pattern, 1) {
fatal!(
ctx,
"{}: invalid dynamic list entry: {}",
p.source,
crate::util::display(p.pattern)
@@ -3393,7 +3348,6 @@ pub fn compute_import_export<E: Arch>(ctx: &mut Context<E>) {
if p.pattern.iter().any(|&c| matches!(c, b'*' | b'?' | b'[')) {
if !matcher.add(p.pattern, 1) {
fatal!(
ctx,
"{}: invalid dynamic list entry: {}",
p.source,
crate::util::display(p.pattern)
@@ -3682,8 +3636,8 @@ fn sort_output_sections_by_order<E: Arch>(ctx: &mut Context<E>) {
return i as i64;
}
error!(
ctx,
"--section-order: missing section specification for {}", hdr.name
"--section-order: missing section specification for {}",
hdr.name
);
0
};
@@ -3911,9 +3865,7 @@ fn set_virtual_addresses_by_order<E: Arch>(ctx: &mut Context<E>) {
}
SectionOrderKind::Addr => {
if addr != ctx.args.image_base && ord.value < addr {
error!(
ctx,
"--section-order: address goes backward: requested {:#x} < current {addr:#x} (at token '{}')",
error!("--section-order: address goes backward: requested {:#x} < current {addr:#x} (at token '{}')",
ord.value,
ord.token
);
@@ -4107,7 +4059,7 @@ pub fn set_osec_offsets<E: Arch>(ctx: &mut Context<E>) -> u64 {
continue;
}
}
ctx.checkpoint();
crate::error::checkpoint();
// Assigning new offsets may change the contents and the length
// of the program header, so repeat it until converge.
@@ -4556,7 +4508,7 @@ pub fn write_separate_debug_file<E: Arch>(ctx: &mut Context<E>) {
let path = ctx.args.separate_debug_file.clone();
// Open an output file early
let mut output = OutputFile::open_locked(&ctx.diag, &path, 0o666);
let mut output = OutputFile::open_locked(&path, 0o666);
// We want to write to the debug info file in background so that the
// user doesn't have to wait for it to complete.
@@ -4636,7 +4588,7 @@ pub fn write_separate_debug_file<E: Arch>(ctx: &mut Context<E>) {
}
// Write to a separate debug file
output.resize(&ctx.diag, fileoff);
output.resize(fileoff);
crate::driver::copy_chunks(ctx, output.buf());
if ctx.gdb_index.is_some() {
@@ -4652,9 +4604,9 @@ pub fn write_separate_debug_file<E: Arch>(ctx: &mut Context<E>) {
ctx.gnu_debuglink.as_ref().unwrap().crc32,
);
let len = output.len();
output.extend(&ctx.diag, trailer.len());
output.extend(trailer.len());
output.buf()[len..].copy_from_slice(&trailer);
output.close(&ctx.diag);
output.close();
}
// Write Makefile-style dependency rules to a file specified by
@@ -4686,7 +4638,7 @@ pub fn write_dependency_file<E: Arch>(ctx: &Context<E>) {
let _ = std::io::stdout().write_all(out.as_bytes());
} else {
std::fs::write(path, out)
.unwrap_or_else(|e| fatal!(ctx, "--dependency-file: cannot open {path}: {e}"));
.unwrap_or_else(|e| fatal!("--dependency-file: cannot open {path}: {e}"));
}
}
@@ -4799,10 +4751,10 @@ pub fn show_stats<E: Arch>(ctx: &Context<E>) {
NUM_RELS.add(count);
}
Counter::print(&ctx.diag);
Counter::print();
for section in &ctx.merged_sections {
crate::output_chunks::merged::print_stats(section, &ctx.diag);
crate::output_chunks::merged::print_stats(section);
}
}
+27 -36
View File
@@ -80,7 +80,7 @@ pub fn get_machine_type<E: Arch>(
) -> Option<&'static str> {
match get_file_type(ctx, mf) {
FileType::Text => crate::linker_script::output_target(ctx, rctx, mf),
_ => filetype::get_machine_type(&ctx.diag, &ctx.args.plugin, mf, || None),
_ => filetype::get_machine_type(&ctx.args.plugin, mf, || None),
}
}
@@ -93,12 +93,11 @@ fn new_object_file<E: Arch>(
static COUNT: Counter = Counter::new("parsed_objs");
COUNT.increment();
let target = filetype::get_machine_type(&ctx.diag, &ctx.args.plugin, mf, || None);
let target = filetype::get_machine_type(&ctx.args.plugin, mf, || None);
match target {
None => fatal!(ctx, "{}: unknown machine type", mf.name),
None => fatal!("{}: unknown machine type", mf.name),
Some(t) if t != ctx.args.emulation => {
fatal!(
ctx,
"{}: incompatible file type: {} is expected but got {t}",
mf.name,
ctx.args.emulation
@@ -106,7 +105,7 @@ fn new_object_file<E: Arch>(
}
_ => {}
}
let mut file = ObjectFile::<E>::new(&ctx.diag, mf, archive_name.to_string());
let mut file = ObjectFile::<E>::new(mf, archive_name.to_string());
file.base.as_needed = rctx.in_lib || (!archive_name.is_empty() && !rctx.whole_archive);
file.register_global_symbols(&ctx.args, &mut ctx.symbol_bin());
file
@@ -118,18 +117,13 @@ fn new_shared_file<E: Arch>(
mf: &'static MappedFile,
) -> SharedFile<E> {
if rctx.is_static {
fatal!(
ctx,
"{}: attempted static link of a dynamic object",
mf.name
);
fatal!("{}: attempted static link of a dynamic object", mf.name);
}
let target = filetype::get_machine_type(&ctx.diag, &ctx.args.plugin, mf, || None);
let target = filetype::get_machine_type(&ctx.args.plugin, mf, || None);
match target {
None => fatal!(ctx, "{}: unknown machine type", mf.name),
None => fatal!("{}: unknown machine type", mf.name),
Some(t) if t != ctx.args.emulation => {
fatal!(
ctx,
"{}: incompatible file type: {} is expected but got {t}",
mf.name,
ctx.args.emulation
@@ -137,7 +131,7 @@ fn new_shared_file<E: Arch>(
}
_ => {}
}
let mut file = SharedFile::<E>::new(&ctx.diag, mf);
let mut file = SharedFile::<E>::new(mf);
file.base.as_needed = rctx.as_needed;
file
}
@@ -181,8 +175,8 @@ fn read_archive_member<E: Arch>(
}
FileType::ElfDso => {
warn!(
ctx,
"{archive_name}({}): shared object file in an archive is ignored", mf.name
"{archive_name}({}): shared object file in an archive is ignored",
mf.name
);
None
}
@@ -207,7 +201,7 @@ pub fn read_file<E: Arch>(ctx: &mut Context<E>, rctx: &mut ReaderContext, mf: &'
push_loaded(ctx, Loaded::Dso(rctx.pos.clone(), Box::new(file)));
}
FileType::Ar | FileType::ThinAr => {
for child in archive_file::read_archive_members(&ctx.diag, mf) {
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) {
push_loaded(ctx, loaded);
@@ -220,7 +214,7 @@ pub fn read_file<E: Arch>(ctx: &mut Context<E>, rctx: &mut ReaderContext, mf: &'
push_loaded(ctx, Loaded::Obj(rctx.pos.clone(), Box::new(file)));
}
}
_ => fatal!(ctx, "{}: unknown file type", mf.name),
_ => fatal!("{}: unknown file type", mf.name),
}
}
@@ -243,11 +237,9 @@ pub fn detect_machine_type<E: Arch>(ctx: &mut Context<E>, jobs: &[ReaderJob]) ->
if job.is_lib {
continue;
}
if let Some(mf) = open_file(&ctx.diag, &ctx.args.chroot, &job.name) {
if let Some(mf) = open_file(&ctx.args.chroot, &job.name) {
if get_file_type(ctx, mf) != FileType::Text {
if let Some(target) =
filetype::get_machine_type(&ctx.diag, &ctx.args.plugin, mf, || None)
{
if let Some(target) = filetype::get_machine_type(&ctx.args.plugin, mf, || None) {
return target.to_string();
}
}
@@ -257,7 +249,7 @@ pub fn detect_machine_type<E: Arch>(ctx: &mut Context<E>, jobs: &[ReaderJob]) ->
if job.is_lib {
continue;
}
if let Some(mf) = open_file(&ctx.diag, &ctx.args.chroot, &job.name) {
if let Some(mf) = open_file(&ctx.args.chroot, &job.name) {
if get_file_type(ctx, mf) == FileType::Text {
if let Some(target) = crate::linker_script::output_target(ctx, &job.rctx, mf) {
return target.to_string();
@@ -265,7 +257,7 @@ pub fn detect_machine_type<E: Arch>(ctx: &mut Context<E>, jobs: &[ReaderJob]) ->
}
}
}
fatal!(ctx, "-m option is missing");
fatal!("-m option is missing");
}
fn open_library<E: Arch>(
@@ -273,11 +265,10 @@ fn open_library<E: Arch>(
rctx: &ReaderContext,
path: &str,
) -> Option<&'static MappedFile> {
let mf = open_file(&ctx.diag, &ctx.args.chroot, path)?;
let mf = open_file(&ctx.args.chroot, path)?;
if let Some(target) = get_machine_type(ctx, rctx, mf) {
if target != E::NAME {
warn!(
ctx,
"{path}: skipping incompatible file: {target} (e_machine {})",
E::E_MACHINE
);
@@ -299,7 +290,7 @@ pub fn find_library<E: Arch>(
return mf;
}
}
fatal!(ctx, "library not found: :{name}");
fatal!("library not found: :{name}");
}
for dir in &ctx.args.library_paths {
@@ -313,7 +304,7 @@ pub fn find_library<E: Arch>(
return mf;
}
}
fatal!(ctx, "library not found: {name}");
fatal!("library not found: {name}");
}
// Reads all input files.
@@ -372,7 +363,7 @@ pub fn read_input_files<E: Arch>(ctx: &mut Context<E>, jobs: Vec<ReaderJob>) {
is_dependency: std::sync::atomic::AtomicBool::new(true),
})
} else {
must_open_file(&ctx_ref.diag, &ctx_ref.args.chroot, &job.name)
must_open_file(&ctx_ref.args.chroot, &job.name)
};
// An archive member is enqueued by the job that read its archive
@@ -381,7 +372,7 @@ pub fn read_input_files<E: Arch>(ctx: &mut Context<E>, jobs: Vec<ReaderJob>) {
// parallel.
match get_file_type(ctx_ref, mf) {
FileType::Ar => {
for child in archive_file::read_fat_archive_members(&ctx_ref.diag, mf) {
for child in archive_file::read_fat_archive_members(mf) {
let child_rctx = rctx.next_child();
let archive_name = mf.name.clone();
let loaded = &loaded;
@@ -395,12 +386,12 @@ 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(&ctx_ref.diag, mf) {
for path in archive_file::get_thin_archive_member_paths(mf) {
let child_rctx = rctx.next_child();
let archive_name = mf.name.clone();
let loaded = &loaded;
scope.spawn(move |_| {
let child = must_open_file(&ctx_ref.diag, &ctx_ref.args.chroot, &path);
let child = must_open_file(&ctx_ref.args.chroot, &path);
let child = crate::util::leak(MappedFile {
name: child.name.clone(),
data: child.data,
@@ -435,7 +426,7 @@ pub fn read_input_files<E: Arch>(ctx: &mut Context<E>, jobs: Vec<ReaderJob>) {
loaded.push(Loaded::Obj(rctx.pos, Box::new(file)));
}
}
_ => fatal!(ctx_ref, "{}: unknown file type", mf.name),
_ => fatal!("{}: unknown file type", mf.name),
}
});
});
@@ -471,7 +462,7 @@ pub fn read_input_files<E: Arch>(ctx: &mut Context<E>, jobs: Vec<ReaderJob>) {
let mut file = objs[idx.index()].take().unwrap();
file.base.priority = priority;
if ctx.args.trace {
out!(ctx, "trace: {file}");
out!("trace: {file}");
}
let id = ObjId(ctx.objs.push(file));
ctx.file_by_priority.push(Some(FileId::Obj(id)));
@@ -480,7 +471,7 @@ pub fn read_input_files<E: Arch>(ctx: &mut Context<E>, jobs: Vec<ReaderJob>) {
let mut file = dsos[idx.index()].take().unwrap();
file.base.priority = priority;
if ctx.args.trace {
out!(ctx, "trace: {file}");
out!("trace: {file}");
}
let id = DsoId(ctx.dsos.push(file));
ctx.file_by_priority.push(Some(FileId::Dso(id)));
@@ -489,6 +480,6 @@ pub fn read_input_files<E: Arch>(ctx: &mut Context<E>, jobs: Vec<ReaderJob>) {
}
if ctx.objs.is_empty() && ctx.dsos.is_empty() {
fatal!(ctx, "no input files");
fatal!("no input files");
}
}
+3 -3
View File
@@ -180,10 +180,10 @@ pub fn combine_objects<E: Arch>(ctx: &mut Context<E>) {
passes::compute_section_headers(ctx);
let filesize = set_osec_offsets(ctx);
let mut output = OutputFile::open(&ctx.diag, &ctx.args.output, filesize, 0o666, false);
let mut output = OutputFile::open(&ctx.args.output, filesize, 0o666, false);
crate::driver::copy_chunks(ctx, output.buf());
output.close(&ctx.diag);
ctx.checkpoint();
output.close();
crate::error::checkpoint();
if ctx.args.print_map {
crate::mapfile::print_map(ctx);
+6 -7
View File
@@ -4,7 +4,6 @@
#[cfg(not(windows))]
use std::sync::atomic::{AtomicI32, Ordering};
use crate::error::Diagnostics;
use crate::fatal;
#[cfg(not(windows))]
@@ -139,9 +138,9 @@ 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(diag: &Diagnostics, argv: &[String]) -> ! {
pub fn process_run_subcommand(argv: &[String]) -> ! {
if argv.len() < 3 {
fatal!(diag, "-run: argument missing");
fatal!("-run: argument missing");
}
let self_path = crate::util::self_path();
let candidates = [
@@ -159,7 +158,7 @@ pub fn process_run_subcommand(diag: &Diagnostics, argv: &[String]) -> ! {
];
// Get the mold-wrapper.so path
let Some(dso) = candidates.into_iter().flatten().find(|p| p.is_file()) else {
fatal!(diag, "mold-wrapper.so is missing");
fatal!("mold-wrapper.so is missing");
};
// Set environment variables
@@ -177,10 +176,10 @@ pub fn process_run_subcommand(diag: &Diagnostics, argv: &[String]) -> ! {
std::process::Command::new(&argv[2]).args(&argv[3..]).exec()
};
// Execute a given command
fatal!(diag, "mold -run failed: {}: {err}", argv[2]);
fatal!("mold -run failed: {}: {err}", argv[2]);
}
#[cfg(windows)]
pub fn process_run_subcommand(diag: &Diagnostics, _argv: &[String]) -> ! {
fatal!(diag, "-run is supported only on Unix");
pub fn process_run_subcommand(_argv: &[String]) -> ! {
fatal!("-run is supported only on Unix");
}
+1 -2
View File
@@ -1075,7 +1075,6 @@ impl Symbol {
return eh_frame.sh_addr.get();
}
crate::fatal!(
ctx,
"symbol referring to .eh_frame is not supported: {} {}",
self,
ctx.file_display(self.file().unwrap())
@@ -1171,7 +1170,7 @@ impl Symbol {
return addr;
}
}
crate::fatal!(ctx, "range extension thunk out of range: {}", self);
crate::fatal!("range extension thunk out of range: {}", self);
}
/// The symbol's index in the output symbol table.
+4 -4
View File
@@ -6,7 +6,7 @@ use std::sync::atomic::{AtomicBool, AtomicI64, Ordering};
use std::sync::{Arc, Mutex};
use std::time::Instant;
use crate::error::Diagnostics;
use crate::out;
static COUNTERS_ENABLED: AtomicBool = AtomicBool::new(false);
static COUNTERS: Mutex<Vec<&'static Counter>> = Mutex::new(Vec::new());
@@ -46,15 +46,15 @@ impl Counter {
self.value.fetch_add(delta, Ordering::Relaxed);
}
pub fn print(diag: &Diagnostics) {
pub fn print() {
let mut counters = COUNTERS.lock().unwrap().clone();
counters.sort_by_key(|counter| counter.value.load(Ordering::Relaxed));
for counter in counters {
diag.out(format_args!(
out!(
"{:>20}={}",
counter.name,
counter.value.load(Ordering::Relaxed)
));
);
}
}
}
+2 -4
View File
@@ -1,10 +1,8 @@
//! The linker instantiated for arm32. Every target has a crate like this
//! one, so that the compiler can build the targets in parallel.
use mold::error::Diagnostics;
/// Links for this target, or reports the target the inputs are actually
/// for.
pub fn link(cmdline: &[String], diag: &Diagnostics) -> Result<i32, String> {
mold::driver::link::<mold::arch::Arm32>(cmdline, diag)
pub fn link(cmdline: &[String]) -> Result<i32, String> {
mold::driver::link::<mold::arch::Arm32>(cmdline)
}
+2 -4
View File
@@ -1,10 +1,8 @@
//! The linker instantiated for arm32be. Every target has a crate like this
//! one, so that the compiler can build the targets in parallel.
use mold::error::Diagnostics;
/// Links for this target, or reports the target the inputs are actually
/// for.
pub fn link(cmdline: &[String], diag: &Diagnostics) -> Result<i32, String> {
mold::driver::link::<mold::arch::Arm32Be>(cmdline, diag)
pub fn link(cmdline: &[String]) -> Result<i32, String> {
mold::driver::link::<mold::arch::Arm32Be>(cmdline)
}
+2 -4
View File
@@ -1,10 +1,8 @@
//! The linker instantiated for arm64. Every target has a crate like this
//! one, so that the compiler can build the targets in parallel.
use mold::error::Diagnostics;
/// Links for this target, or reports the target the inputs are actually
/// for.
pub fn link(cmdline: &[String], diag: &Diagnostics) -> Result<i32, String> {
mold::driver::link::<mold::arch::Arm64>(cmdline, diag)
pub fn link(cmdline: &[String]) -> Result<i32, String> {
mold::driver::link::<mold::arch::Arm64>(cmdline)
}
+2 -4
View File
@@ -1,10 +1,8 @@
//! The linker instantiated for arm64be. Every target has a crate like this
//! one, so that the compiler can build the targets in parallel.
use mold::error::Diagnostics;
/// Links for this target, or reports the target the inputs are actually
/// for.
pub fn link(cmdline: &[String], diag: &Diagnostics) -> Result<i32, String> {
mold::driver::link::<mold::arch::Arm64Be>(cmdline, diag)
pub fn link(cmdline: &[String]) -> Result<i32, String> {
mold::driver::link::<mold::arch::Arm64Be>(cmdline)
}
+2 -4
View File
@@ -1,10 +1,8 @@
//! The linker instantiated for i386. Every target has a crate like this
//! one, so that the compiler can build the targets in parallel.
use mold::error::Diagnostics;
/// Links for this target, or reports the target the inputs are actually
/// for.
pub fn link(cmdline: &[String], diag: &Diagnostics) -> Result<i32, String> {
mold::driver::link::<mold::arch::I386>(cmdline, diag)
pub fn link(cmdline: &[String]) -> Result<i32, String> {
mold::driver::link::<mold::arch::I386>(cmdline)
}
+2 -4
View File
@@ -1,10 +1,8 @@
//! The linker instantiated for loongarch32. Every target has a crate like this
//! one, so that the compiler can build the targets in parallel.
use mold::error::Diagnostics;
/// Links for this target, or reports the target the inputs are actually
/// for.
pub fn link(cmdline: &[String], diag: &Diagnostics) -> Result<i32, String> {
mold::driver::link::<mold::arch::LoongArch32>(cmdline, diag)
pub fn link(cmdline: &[String]) -> Result<i32, String> {
mold::driver::link::<mold::arch::LoongArch32>(cmdline)
}
+2 -4
View File
@@ -1,10 +1,8 @@
//! The linker instantiated for loongarch64. Every target has a crate like this
//! one, so that the compiler can build the targets in parallel.
use mold::error::Diagnostics;
/// Links for this target, or reports the target the inputs are actually
/// for.
pub fn link(cmdline: &[String], diag: &Diagnostics) -> Result<i32, String> {
mold::driver::link::<mold::arch::LoongArch64>(cmdline, diag)
pub fn link(cmdline: &[String]) -> Result<i32, String> {
mold::driver::link::<mold::arch::LoongArch64>(cmdline)
}
+2 -4
View File
@@ -1,10 +1,8 @@
//! The linker instantiated for m68k. Every target has a crate like this
//! one, so that the compiler can build the targets in parallel.
use mold::error::Diagnostics;
/// Links for this target, or reports the target the inputs are actually
/// for.
pub fn link(cmdline: &[String], diag: &Diagnostics) -> Result<i32, String> {
mold::driver::link::<mold::arch::M68k>(cmdline, diag)
pub fn link(cmdline: &[String]) -> Result<i32, String> {
mold::driver::link::<mold::arch::M68k>(cmdline)
}
+2 -4
View File
@@ -1,10 +1,8 @@
//! The linker instantiated for ppc32. Every target has a crate like this
//! one, so that the compiler can build the targets in parallel.
use mold::error::Diagnostics;
/// Links for this target, or reports the target the inputs are actually
/// for.
pub fn link(cmdline: &[String], diag: &Diagnostics) -> Result<i32, String> {
mold::driver::link::<mold::arch::Ppc32>(cmdline, diag)
pub fn link(cmdline: &[String]) -> Result<i32, String> {
mold::driver::link::<mold::arch::Ppc32>(cmdline)
}
+2 -4
View File
@@ -1,10 +1,8 @@
//! The linker instantiated for ppc64v1. Every target has a crate like this
//! one, so that the compiler can build the targets in parallel.
use mold::error::Diagnostics;
/// Links for this target, or reports the target the inputs are actually
/// for.
pub fn link(cmdline: &[String], diag: &Diagnostics) -> Result<i32, String> {
mold::driver::link::<mold::arch::Ppc64V1>(cmdline, diag)
pub fn link(cmdline: &[String]) -> Result<i32, String> {
mold::driver::link::<mold::arch::Ppc64V1>(cmdline)
}
+2 -4
View File
@@ -1,10 +1,8 @@
//! The linker instantiated for ppc64v2. Every target has a crate like this
//! one, so that the compiler can build the targets in parallel.
use mold::error::Diagnostics;
/// Links for this target, or reports the target the inputs are actually
/// for.
pub fn link(cmdline: &[String], diag: &Diagnostics) -> Result<i32, String> {
mold::driver::link::<mold::arch::Ppc64V2>(cmdline, diag)
pub fn link(cmdline: &[String]) -> Result<i32, String> {
mold::driver::link::<mold::arch::Ppc64V2>(cmdline)
}
+2 -4
View File
@@ -1,10 +1,8 @@
//! The linker instantiated for riscv32. Every target has a crate like this
//! one, so that the compiler can build the targets in parallel.
use mold::error::Diagnostics;
/// Links for this target, or reports the target the inputs are actually
/// for.
pub fn link(cmdline: &[String], diag: &Diagnostics) -> Result<i32, String> {
mold::driver::link::<mold::arch::Riscv32>(cmdline, diag)
pub fn link(cmdline: &[String]) -> Result<i32, String> {
mold::driver::link::<mold::arch::Riscv32>(cmdline)
}
+2 -4
View File
@@ -1,10 +1,8 @@
//! The linker instantiated for riscv32be. Every target has a crate like this
//! one, so that the compiler can build the targets in parallel.
use mold::error::Diagnostics;
/// Links for this target, or reports the target the inputs are actually
/// for.
pub fn link(cmdline: &[String], diag: &Diagnostics) -> Result<i32, String> {
mold::driver::link::<mold::arch::Riscv32Be>(cmdline, diag)
pub fn link(cmdline: &[String]) -> Result<i32, String> {
mold::driver::link::<mold::arch::Riscv32Be>(cmdline)
}
+2 -4
View File
@@ -1,10 +1,8 @@
//! The linker instantiated for riscv64. Every target has a crate like this
//! one, so that the compiler can build the targets in parallel.
use mold::error::Diagnostics;
/// Links for this target, or reports the target the inputs are actually
/// for.
pub fn link(cmdline: &[String], diag: &Diagnostics) -> Result<i32, String> {
mold::driver::link::<mold::arch::Riscv64>(cmdline, diag)
pub fn link(cmdline: &[String]) -> Result<i32, String> {
mold::driver::link::<mold::arch::Riscv64>(cmdline)
}
+2 -4
View File
@@ -1,10 +1,8 @@
//! The linker instantiated for riscv64be. Every target has a crate like this
//! one, so that the compiler can build the targets in parallel.
use mold::error::Diagnostics;
/// Links for this target, or reports the target the inputs are actually
/// for.
pub fn link(cmdline: &[String], diag: &Diagnostics) -> Result<i32, String> {
mold::driver::link::<mold::arch::Riscv64Be>(cmdline, diag)
pub fn link(cmdline: &[String]) -> Result<i32, String> {
mold::driver::link::<mold::arch::Riscv64Be>(cmdline)
}
+2 -4
View File
@@ -1,10 +1,8 @@
//! The linker instantiated for s390x. Every target has a crate like this
//! one, so that the compiler can build the targets in parallel.
use mold::error::Diagnostics;
/// Links for this target, or reports the target the inputs are actually
/// for.
pub fn link(cmdline: &[String], diag: &Diagnostics) -> Result<i32, String> {
mold::driver::link::<mold::arch::S390x>(cmdline, diag)
pub fn link(cmdline: &[String]) -> Result<i32, String> {
mold::driver::link::<mold::arch::S390x>(cmdline)
}
+2 -4
View File
@@ -1,10 +1,8 @@
//! The linker instantiated for sh4. Every target has a crate like this
//! one, so that the compiler can build the targets in parallel.
use mold::error::Diagnostics;
/// Links for this target, or reports the target the inputs are actually
/// for.
pub fn link(cmdline: &[String], diag: &Diagnostics) -> Result<i32, String> {
mold::driver::link::<mold::arch::Sh4>(cmdline, diag)
pub fn link(cmdline: &[String]) -> Result<i32, String> {
mold::driver::link::<mold::arch::Sh4>(cmdline)
}
+2 -4
View File
@@ -1,10 +1,8 @@
//! The linker instantiated for sh4be. Every target has a crate like this
//! one, so that the compiler can build the targets in parallel.
use mold::error::Diagnostics;
/// Links for this target, or reports the target the inputs are actually
/// for.
pub fn link(cmdline: &[String], diag: &Diagnostics) -> Result<i32, String> {
mold::driver::link::<mold::arch::Sh4Be>(cmdline, diag)
pub fn link(cmdline: &[String]) -> Result<i32, String> {
mold::driver::link::<mold::arch::Sh4Be>(cmdline)
}
+2 -4
View File
@@ -1,10 +1,8 @@
//! The linker instantiated for sparc64. Every target has a crate like this
//! one, so that the compiler can build the targets in parallel.
use mold::error::Diagnostics;
/// Links for this target, or reports the target the inputs are actually
/// for.
pub fn link(cmdline: &[String], diag: &Diagnostics) -> Result<i32, String> {
mold::driver::link::<mold::arch::Sparc64>(cmdline, diag)
pub fn link(cmdline: &[String]) -> Result<i32, String> {
mold::driver::link::<mold::arch::Sparc64>(cmdline)
}
+2 -4
View File
@@ -1,10 +1,8 @@
//! The linker instantiated for x86_64. Every target has a crate like this
//! one, so that the compiler can build the targets in parallel.
use mold::error::Diagnostics;
/// Links for this target, or reports the target the inputs are actually
/// for.
pub fn link(cmdline: &[String], diag: &Diagnostics) -> Result<i32, String> {
mold::driver::link::<mold::arch::X86_64>(cmdline, diag)
pub fn link(cmdline: &[String]) -> Result<i32, String> {
mold::driver::link::<mold::arch::X86_64>(cmdline)
}