Adopt rustfmt's Max width heuristics

This commit is contained in:
Rui Ueyama committed 2026-09-18 14:19:38 +09:00
1 parent 8ceae6875f
commit 9b9d9d7ef9
82 files changed
+1023 -2949

No files matched your search

+7 -24
View File
@@ -37,21 +37,15 @@ fn git_hash(source_dir: &Path) -> Option<String> {
return None;
}
let path = PathBuf::from(String::from_utf8(output.stdout).ok()?.trim());
Some(if path.is_absolute() {
path
} else {
source_dir.join(path)
})
Some(if path.is_absolute() { path } else { source_dir.join(path) })
};
let reftable = git_path("reftable/tables.list").filter(|p| p.exists());
if let Some(head) = git_path("HEAD").filter(|p| p.exists()) {
println!("cargo:rerun-if-changed={}", head.display());
if let Ok(contents) = std::fs::read_to_string(&head) {
if let Some(reference) = contents
.strip_prefix("ref: ")
.map(str::trim)
.filter(|_| reftable.is_none())
if let Some(reference) =
contents.strip_prefix("ref: ").map(str::trim).filter(|_| reftable.is_none())
{
if let Some(path) = git_path(reference) {
// A packed reference may acquire a loose file later.
@@ -63,24 +57,13 @@ fn git_hash(source_dir: &Path) -> Option<String> {
}
}
}
for path in git_path("packed-refs")
.into_iter()
.chain(reftable)
.filter(|p| p.exists())
{
for path in git_path("packed-refs").into_iter().chain(reftable).filter(|p| p.exists()) {
println!("cargo:rerun-if-changed={}", path.display());
}
let output = Command::new("git")
.arg("-C")
.arg(source_dir)
.args(["rev-parse", "HEAD"])
.output()
.ok()?;
output
.status
.success()
.then(|| String::from_utf8_lossy(&output.stdout).trim().to_string())
let output =
Command::new("git").arg("-C").arg(source_dir).args(["rev-parse", "HEAD"]).output().ok()?;
output.status.success().then(|| String::from_utf8_lossy(&output.stdout).trim().to_string())
}
fn main() {
+1 -3
View File
@@ -2,9 +2,7 @@ use std::path::Path;
use std::process::ExitCode;
fn main() -> ExitCode {
let root = Path::new(env!("CARGO_MANIFEST_DIR"))
.parent()
.unwrap();
let root = Path::new(env!("CARGO_MANIFEST_DIR")).parent().unwrap();
let cases = [root.join("tests")];
mold_tests::run(&cases, Path::new(env!("CARGO_BIN_EXE_mold")))
}
+2
View File
@@ -0,0 +1,2 @@
use_small_heuristics = "Max"
use_field_init_shorthand = true
+34 -78
View File
@@ -103,10 +103,7 @@ fn thm2<End: Endian>(loc: &[u8]) -> u16 {
fn write_arm_mov<End: Endian>(loc: &mut [u8], val: u32) {
let imm12 = b(val as u64, 11, 0);
let imm4 = b(val as u64, 15, 12);
End::write_u32(
loc,
(End::read_u32(loc) & 0xfff0_f000) | (imm4 << 16) | imm12,
);
End::write_u32(loc, (End::read_u32(loc) & 0xfff0_f000) | (imm4 << 16) | imm12);
}
fn write_thm_b21<End: Endian>(loc: &mut [u8], val: u32) {
@@ -158,11 +155,7 @@ fn write_thm_mov<End: Endian>(loc: &mut [u8], val: u32) {
/// Sets the second halfword's bit that turns a Thumb BLX into a BL, or
/// clears it for the reverse.
fn set_thm_bl<End: Endian>(loc: &mut [u8], is_bl: bool) {
let second = if is_bl {
thm2::<End>(loc) | 0x1000
} else {
thm2::<End>(loc) & !0x1000
};
let second = if is_bl { thm2::<End>(loc) | 0x1000 } else { thm2::<End>(loc) & !0x1000 };
End::write_u16(&mut loc[2..], second);
}
@@ -302,10 +295,7 @@ where
const PLT_HDR_SIZE: u64 = 32;
const PLT_SIZE: u64 = 16;
const PLTGOT_SIZE: u64 = 16;
const THUNK: Option<ThunkLayout> = Some(ThunkLayout {
header_size: 16,
entry_size: 16,
});
const THUNK: Option<ThunkLayout> = Some(ThunkLayout { header_size: 16, entry_size: 16 });
const TRAP: &'static [u8] = &[0xff, 0xde]; // udf
const R_COPY: u32 = R_ARM_COPY;
@@ -318,13 +308,8 @@ where
const R_TPOFF: u32 = R_ARM_TLS_TPOFF32;
const R_DTPMOD: u32 = R_ARM_TLS_DTPMOD32;
const R_TLSDESC: Option<u32> = Some(R_ARM_TLS_DESC);
const R_FUNCALL: &'static [u32] = &[
R_ARM_JUMP24,
R_ARM_THM_JUMP24,
R_ARM_CALL,
R_ARM_THM_CALL,
R_ARM_PLT32,
];
const R_FUNCALL: &'static [u32] =
&[R_ARM_JUMP24, R_ARM_THM_JUMP24, R_ARM_CALL, R_ARM_THM_CALL, R_ARM_PLT32];
fn rel_to_string(r_type: u32) -> std::borrow::Cow<'static, str> {
arm32_rel_to_string(r_type)
@@ -360,9 +345,7 @@ where
write_code(buf, &PLT_ENTRY);
End::write_u32(
&mut buf[12..],
sym.gotplt_addr(ctx)
.wrapping_sub(sym.plt_addr(ctx))
.wrapping_sub(12) as u32,
sym.gotplt_addr(ctx).wrapping_sub(sym.plt_addr(ctx)).wrapping_sub(12) as u32,
);
}
@@ -370,9 +353,7 @@ where
write_code(buf, &PLT_ENTRY);
End::write_u32(
&mut buf[12..],
sym.got_pltgot_addr(ctx)
.wrapping_sub(sym.plt_addr(ctx))
.wrapping_sub(12) as u32,
sym.got_pltgot_addr(ctx).wrapping_sub(sym.plt_addr(ctx)).wrapping_sub(12) as u32,
);
}
@@ -417,9 +398,9 @@ where
scan_pcrel(ctx, isec, sym, &rel)
}
R_ARM_TLS_GD32 => sym.add_flags(NEEDS_TLSGD),
R_ARM_TLS_LDM32 => ctx
.needs_tlsld
.store(true, std::sync::atomic::Ordering::Relaxed),
R_ARM_TLS_LDM32 => {
ctx.needs_tlsld.store(true, std::sync::atomic::Ordering::Relaxed)
}
R_ARM_TLS_IE32 => sym.add_flags(NEEDS_GOTTP),
R_ARM_TLS_CALL | R_ARM_THM_TLS_CALL => scan_tlsdesc(ctx, sym),
R_ARM_TLS_LE32 => check_tlsle(ctx, isec, sym, &rel),
@@ -483,12 +464,7 @@ where
let tlsdesc_trampoline = || {
let i = osec.thunks.partition_point(|thunk| thunk.addr(osec) <= p);
osec.thunks.get(i).map_or_else(
|| {
fatal!(
"{}: no TLSDESC trampoline after the call",
isec.display(file)
)
},
|| fatal!("{}: no TLSDESC trampoline after the call", isec.display(file)),
|thunk| thunk.addr(osec),
)
};
@@ -530,10 +506,9 @@ where
}
R_ARM_BASE_PREL => write32(loc, got.wrapping_add(a).wrapping_sub(p) as u32),
R_ARM_GOTOFF32 => write32(loc, (sa | t).wrapping_sub(got) as u32),
R_ARM_GOT_PREL | R_ARM_TARGET2 => write32(
loc,
got.wrapping_add(g()).wrapping_add(a).wrapping_sub(p) as u32,
),
R_ARM_GOT_PREL | R_ARM_TARGET2 => {
write32(loc, got.wrapping_add(g()).wrapping_add(a).wrapping_sub(p) as u32)
}
R_ARM_GOT_BREL => write32(loc, g().wrapping_add(a) as u32),
R_ARM_CALL => {
if sym.is_remaining_undef_weak() {
@@ -546,10 +521,7 @@ where
let is_bl = insn & 0xff00_0000 == 0xeb00_0000;
let is_blx = insn & 0xfe00_0000 == 0xfa00_0000;
if !is_bl && !is_blx {
fatal!(
"{}: R_ARM_CALL refers to neither BL nor BLX",
isec.display(file)
);
fatal!("{}: R_ARM_CALL refers to neither BL nor BLX", isec.display(file));
}
if is_int(pcrel as i64, 26) {
if t != 0 {
@@ -586,9 +558,8 @@ where
if sym.is_remaining_undef_weak() {
write32(loc, ARM_NOP); // NOP
} else {
let val = if t != 0 { arm_thunk() } else { s }
.wrapping_add(a)
.wrapping_sub(p);
let val =
if t != 0 { arm_thunk() } else { s }.wrapping_add(a).wrapping_sub(p);
write32(loc, (End::read_u32(loc) & 0xff00_0000) | b(val, 25, 2));
}
}
@@ -624,29 +595,23 @@ where
}
R_ARM_PREL31 => {
check(pcrel as i64, -(1 << 30), 1 << 30);
write32(
loc,
(End::read_u32(loc) & 0x8000_0000) | (pcrel as u32 & 0x7fff_ffff),
);
write32(loc, (End::read_u32(loc) & 0x8000_0000) | (pcrel as u32 & 0x7fff_ffff));
}
R_ARM_THM_MOVW_ABS_NC => write_thm_mov::<End>(loc, (sa | t) as u32),
R_ARM_MOVT_PREL => write_arm_mov::<End>(loc, (pcrel >> 16) as u32),
R_ARM_THM_MOVT_PREL => write_thm_mov::<End>(loc, (pcrel >> 16) as u32),
R_ARM_MOVT_ABS => write_arm_mov::<End>(loc, (sa >> 16) as u32),
R_ARM_THM_MOVT_ABS => write_thm_mov::<End>(loc, (sa >> 16) as u32),
R_ARM_TLS_GD32 => write32(
loc,
sym.tlsgd_addr(ctx).wrapping_add(a).wrapping_sub(p) as u32,
),
R_ARM_TLS_LDM32 => write32(
loc,
ctx.got.tlsld_addr().wrapping_add(a).wrapping_sub(p) as u32,
),
R_ARM_TLS_GD32 => {
write32(loc, sym.tlsgd_addr(ctx).wrapping_add(a).wrapping_sub(p) as u32)
}
R_ARM_TLS_LDM32 => {
write32(loc, ctx.got.tlsld_addr().wrapping_add(a).wrapping_sub(p) as u32)
}
R_ARM_TLS_LDO32 => write32(loc, sa.wrapping_sub(ctx.dtp_addr) as u32),
R_ARM_TLS_IE32 => write32(
loc,
sym.gottp_addr(ctx).wrapping_add(a).wrapping_sub(p) as u32,
),
R_ARM_TLS_IE32 => {
write32(loc, sym.gottp_addr(ctx).wrapping_add(a).wrapping_sub(p) as u32)
}
R_ARM_TLS_LE32 => write32(loc, sa.wrapping_sub(ctx.tp_addr) as u32),
// ARM32 TLSDESC uses the following code sequence to materialize
// a TP-relative address in r0.
@@ -687,10 +652,8 @@ where
let adjust = if a & 1 != 0 { 5 } else { 8 };
write32(
loc,
sym.gottp_addr(ctx)
.wrapping_sub(p)
.wrapping_add(a)
.wrapping_sub(adjust) as u32,
sym.gottp_addr(ctx).wrapping_sub(p).wrapping_add(a).wrapping_sub(adjust)
as u32,
);
} else {
write32(loc, s.wrapping_sub(ctx.tp_addr) as u32);
@@ -832,10 +795,9 @@ where
R_ARM_MOVW_PREL_NC | R_ARM_MOVW_ABS_NC | R_ARM_MOVT_PREL | R_ARM_MOVT_ABS => {
write_arm_mov::<End>(loc, val as u32)
}
R_ARM_PREL31 => End::write_u32(
loc,
(End::read_u32(loc) & 0x8000_0000) | (val as u32 & 0x7fff_ffff),
),
R_ARM_PREL31 => {
End::write_u32(loc, (End::read_u32(loc) & 0x8000_0000) | (val as u32 & 0x7fff_ffff))
}
R_ARM_THM_MOVW_PREL_NC
| R_ARM_THM_MOVW_ABS_NC
| R_ARM_THM_MOVT_PREL
@@ -861,10 +823,7 @@ where
let j1 = bit(thm(1), 13);
let imm6 = bits(thm(0), 5, 0);
let imm11 = bits(thm(1), 10, 0);
sign_extend(
(s << 20) | (j2 << 19) | (j1 << 18) | (imm6 << 12) | (imm11 << 1),
21,
)
sign_extend((s << 20) | (j2 << 19) | (j1 << 18) | (imm6 << 12) | (imm11 << 1), 21)
}
R_ARM_THM_CALL | R_ARM_THM_JUMP24 | R_ARM_THM_TLS_CALL => {
// https://developer.arm.com/documentation/ddi0597/2024-12/Base-Instructions/BL--BLX--immediate---Branch-with-Link-and-optional-Exchange--immediate--
@@ -875,10 +834,7 @@ where
let i2 = (j2 ^ s) ^ 1;
let imm10 = bits(thm(0), 9, 0);
let imm11 = bits(thm(1), 10, 0);
sign_extend(
(s << 24) | (i1 << 23) | (i2 << 22) | (imm10 << 12) | (imm11 << 1),
25,
)
sign_extend((s << 24) | (i1 << 23) | (i2 << 22) | (imm10 << 12) | (imm11 << 1), 25)
}
R_ARM_CALL | R_ARM_JUMP24 | R_ARM_PLT32 | R_ARM_TLS_CALL => sign_extend(arm(), 24) << 2,
R_ARM_MOVW_PREL_NC | R_ARM_MOVW_ABS_NC | R_ARM_MOVT_PREL | R_ARM_MOVT_ABS => {
+16 -36
View File
@@ -73,17 +73,11 @@ fn or_insn(loc: &mut [u8], v: u32) {
}
fn write_adrp(loc: &mut [u8], val: u64) {
or_insn(
loc,
((bits(val, 13, 12) << 29) | (bits(val, 32, 14) << 5)) as u32,
);
or_insn(loc, ((bits(val, 13, 12) << 29) | (bits(val, 32, 14) << 5)) as u32);
}
fn write_adr(loc: &mut [u8], val: u64) {
or_insn(
loc,
((bits(val, 1, 0) << 29) | (bits(val, 20, 2) << 5)) as u32,
);
or_insn(loc, ((bits(val, 1, 0) << 29) | (bits(val, 20, 2) << 5)) as u32);
}
/// Rewrites a MOV to MOVZ or MOVN, whichever represents `val`.
@@ -134,10 +128,8 @@ where
i: usize,
) -> bool {
let rel = &rels[i];
if !matches!(
rel.r_type(),
R_AARCH64_ADR_PREL_PG_HI21 | R_AARCH64_ADR_PREL_PG_HI21_NC
) || !ctx.args.relax
if !matches!(rel.r_type(), R_AARCH64_ADR_PREL_PG_HI21 | R_AARCH64_ADR_PREL_PG_HI21_NC)
|| !ctx.args.relax
{
return false;
}
@@ -151,10 +143,7 @@ where
}
let s = sym.addr(ctx);
let p = isec.addr(ctx) + rel.r_offset();
let val = s
.wrapping_add(rel.r_addend() as u64)
.wrapping_sub(p)
.wrapping_sub(4) as i64;
let val = s.wrapping_add(rel.r_addend() as u64).wrapping_sub(p).wrapping_sub(4) as i64;
let off = rel.r_offset() as usize;
let loc = &isec.contents()[off..];
is_int(val, 21)
@@ -181,10 +170,7 @@ where
const PLT_HDR_SIZE: u64 = 32;
const PLT_SIZE: u64 = 16;
const PLTGOT_SIZE: u64 = 16;
const THUNK: Option<ThunkLayout> = Some(ThunkLayout {
header_size: 0,
entry_size: 24,
});
const THUNK: Option<ThunkLayout> = Some(ThunkLayout { header_size: 0, entry_size: 24 });
const SFRAME_ABI: Option<u8> = Some(if End::IS_LITTLE {
SFRAME_ABI_AARCH64_ENDIAN_LITTLE
} else {
@@ -557,15 +543,11 @@ where
End::write_u32(loc, pcrel as u32);
}
R_AARCH64_PREL64 => End::write_u64(loc, pcrel),
R_AARCH64_LD64_GOT_LO12_NC => or_insn(
loc,
(bits(g().wrapping_add(got).wrapping_add(a), 11, 3) << 10) as u32,
),
R_AARCH64_LD64_GOT_LO12_NC => {
or_insn(loc, (bits(g().wrapping_add(got).wrapping_add(a), 11, 3) << 10) as u32)
}
R_AARCH64_LD64_GOTPAGE_LO15 => {
let val = g()
.wrapping_add(got)
.wrapping_add(a)
.wrapping_sub(page(got));
let val = g().wrapping_add(got).wrapping_add(a).wrapping_sub(page(got));
check(val as i64, 0, 1 << 15);
or_insn(loc, (bits(val, 14, 3) << 10) as u32);
}
@@ -574,10 +556,9 @@ where
check(val as i64, -(1 << 32), 1 << 32);
write_adrp(loc, val);
}
R_AARCH64_TLSIE_LD64_GOTTPREL_LO12_NC => or_insn(
loc,
(bits(sym.gottp_addr(ctx).wrapping_add(a), 11, 3) << 10) as u32,
),
R_AARCH64_TLSIE_LD64_GOTTPREL_LO12_NC => {
or_insn(loc, (bits(sym.gottp_addr(ctx).wrapping_add(a), 11, 3) << 10) as u32)
}
R_AARCH64_TLSLE_MOVW_TPREL_G0 => {
check(tprel as i64, -(1 << 15), 1 << 15);
write_movn_movz(loc, tprel as i64);
@@ -608,10 +589,9 @@ where
check(val as i64, -(1 << 32), 1 << 32);
write_adrp(loc, val);
}
R_AARCH64_TLSGD_ADD_LO12_NC => or_insn(
loc,
(bits(sym.tlsgd_addr(ctx).wrapping_add(a), 11, 0) << 10) as u32,
),
R_AARCH64_TLSGD_ADD_LO12_NC => {
or_insn(loc, (bits(sym.tlsgd_addr(ctx).wrapping_add(a), 11, 0) << 10) as u32)
}
// ARM64 TLSDESC uses the following code sequence to materialize
// a TP-relative address in x0.
//
+13 -21
View File
@@ -99,9 +99,8 @@ impl Arch for I386 {
buf[..16].copy_from_slice(&INSN);
write_u32(
&mut buf[3..],
gotplt
.wrapping_sub(u64::from(ctx.got.hdr.shdr.sh_addr.get()))
.wrapping_add(4) as u32,
gotplt.wrapping_sub(u64::from(ctx.got.hdr.shdr.sh_addr.get())).wrapping_add(4)
as u32,
);
} else {
const INSN: [u8; 16] = [
@@ -129,8 +128,7 @@ impl Arch for I386 {
write_u32(&mut buf[1..], reloc_offset as u32);
write_u32(
&mut buf[7..],
sym.gotplt_addr(ctx)
.wrapping_sub(u64::from(ctx.got.hdr.shdr.sh_addr.get())) as u32,
sym.gotplt_addr(ctx).wrapping_sub(u64::from(ctx.got.hdr.shdr.sh_addr.get())) as u32,
);
} else {
const INSN: [u8; 16] = [
@@ -153,8 +151,8 @@ impl Arch for I386 {
buf[..8].copy_from_slice(&INSN);
write_u32(
&mut buf[2..],
sym.got_pltgot_addr(ctx)
.wrapping_sub(u64::from(ctx.got.hdr.shdr.sh_addr.get())) as u32,
sym.got_pltgot_addr(ctx).wrapping_sub(u64::from(ctx.got.hdr.shdr.sh_addr.get()))
as u32,
);
} else {
const INSN: [u8; 8] = [
@@ -203,10 +201,7 @@ impl Arch for I386 {
if rel.r_type() == R_386_TLS_GD || rel.r_type() == R_386_TLS_LDM {
let next = rels.get(i).map(|r| r.r_type());
if !matches!(
next,
Some(R_386_PLT32 | R_386_PC32 | R_386_GOT32 | R_386_GOT32X)
) {
if !matches!(next, Some(R_386_PLT32 | R_386_PC32 | R_386_GOT32 | R_386_GOT32X)) {
fatal!(
"{}: {} must be followed by PLT or GOT32",
isec.display(file),
@@ -250,8 +245,7 @@ impl Arch for I386 {
if ctx.args.is_static || (ctx.args.relax && !ctx.args.shared) {
i += 1;
} else {
ctx.needs_tlsld
.store(true, std::sync::atomic::Ordering::Relaxed);
ctx.needs_tlsld.store(true, std::sync::atomic::Ordering::Relaxed);
}
}
R_386_TLS_GOTDESC => scan_tlsdesc(ctx, sym),
@@ -346,10 +340,9 @@ impl Arch for I386 {
&mut buf[off..],
sym.gottp_addr(ctx).wrapping_add(a).wrapping_sub(got) as u32,
),
R_386_TLS_LE => write_u32(
&mut buf[off..],
s.wrapping_add(a).wrapping_sub(ctx.tp_addr) as u32,
),
R_386_TLS_LE => {
write_u32(&mut buf[off..], s.wrapping_add(a).wrapping_sub(ctx.tp_addr) as u32)
}
R_386_TLS_IE => {
write_u32(&mut buf[off..], sym.gottp_addr(ctx).wrapping_add(a) as u32)
}
@@ -377,10 +370,9 @@ impl Arch for I386 {
relax_ld_to_le(buf, off, next, ctx.tp_addr.wrapping_sub(ctx.tls_begin));
}
}
R_386_TLS_LDO_32 => write_u32(
&mut buf[off..],
s.wrapping_add(a).wrapping_sub(ctx.dtp_addr) as u32,
),
R_386_TLS_LDO_32 => {
write_u32(&mut buf[off..], s.wrapping_add(a).wrapping_sub(ctx.dtp_addr) as u32)
}
R_386_SIZE32 => write_u32(
&mut buf[off..],
(sym.esym(ctx).st_size().get() as u64).wrapping_add(a) as u32,
+23 -63
View File
@@ -103,11 +103,7 @@ fn hi20(val: u64, pc: u64) -> u64 {
// Compensating all the sign-extensions is a bit complicated. The
// psABI gives the following formula.
fn higher(val: u64, pc: u64) -> u64 {
let compensation = if val & 0x800 != 0 {
0x1000u64.wrapping_sub(0x1_0000_0000)
} else {
0
};
let compensation = if val & 0x800 != 0 { 0x1000u64.wrapping_sub(0x1_0000_0000) } else { 0 };
let val = val.wrapping_add(0x8000_0000).wrapping_add(compensation);
page(val).wrapping_sub(page(pc.wrapping_sub(8)))
}
@@ -179,10 +175,7 @@ fn rj(insn: u32) -> u32 {
fn set_rj(loc: &mut [u8], rj: u32) {
debug_assert!(rj < 32);
set_insn(
loc,
(insn(loc) & 0b111111_1111111111111111_00000_11111) | (rj << 5),
);
set_insn(loc, (insn(loc) & 0b111111_1111111111111111_00000_11111) | (rj << 5));
}
/// Rewrites the instruction at `loc` into `pcaddi $rd, imm`, keeping
@@ -214,11 +207,7 @@ fn add_bits(loc: &mut [u8], size: u32, val: u64, subtract: bool) {
fn add_uleb(loc: &mut [u8], val: u64, subtract: bool) {
let cur = read_uleb(&mut &*loc);
let val = if subtract {
cur.wrapping_sub(val)
} else {
cur.wrapping_add(val)
};
let val = if subtract { cur.wrapping_sub(val) } else { cur.wrapping_add(val) };
overwrite_uleb(loc, val);
}
@@ -255,10 +244,7 @@ fn is_relaxable_got_load<E: Arch>(
/// Relocations that only guide relaxation and never relocate anything.
fn is_marker(r_type: u32) -> bool {
matches!(
r_type,
R_NONE | R_LARCH_RELAX | R_LARCH_MARK_LA | R_LARCH_MARK_PCREL | R_LARCH_ALIGN
)
matches!(r_type, R_NONE | R_LARCH_RELAX | R_LARCH_MARK_LA | R_LARCH_MARK_PCREL | R_LARCH_ALIGN)
}
const PLT_ENTRY_64: [u32; 4] = [
@@ -315,26 +301,11 @@ where
const R_ABS: u32 = if IS_64 { R_LARCH_64 } else { R_LARCH_32 };
const R_RELATIVE: u32 = R_LARCH_RELATIVE;
const R_IRELATIVE: Option<u32> = Some(R_LARCH_IRELATIVE);
const R_DTPOFF: u32 = if IS_64 {
R_LARCH_TLS_DTPREL64
} else {
R_LARCH_TLS_DTPREL32
};
const R_TPOFF: u32 = if IS_64 {
R_LARCH_TLS_TPREL64
} else {
R_LARCH_TLS_TPREL32
};
const R_DTPMOD: u32 = if IS_64 {
R_LARCH_TLS_DTPMOD64
} else {
R_LARCH_TLS_DTPMOD32
};
const R_TLSDESC: Option<u32> = Some(if IS_64 {
R_LARCH_TLS_DESC64
} else {
R_LARCH_TLS_DESC32
});
const R_DTPOFF: u32 = if IS_64 { R_LARCH_TLS_DTPREL64 } else { R_LARCH_TLS_DTPREL32 };
const R_TPOFF: u32 = if IS_64 { R_LARCH_TLS_TPREL64 } else { R_LARCH_TLS_TPREL32 };
const R_DTPMOD: u32 = if IS_64 { R_LARCH_TLS_DTPMOD64 } else { R_LARCH_TLS_DTPMOD32 };
const R_TLSDESC: Option<u32> =
Some(if IS_64 { R_LARCH_TLS_DESC64 } else { R_LARCH_TLS_DESC32 });
const R_FUNCALL: &'static [u32] = &[R_LARCH_B26, R_LARCH_CALL36];
fn rel_to_string(r_type: u32) -> std::borrow::Cow<'static, str> {
@@ -712,11 +683,7 @@ where
// Rewrite PCADDU18I + JIRL to B or BL
debug_assert_eq!(removed, 4);
let jirl = insn(&contents[rel.r_offset() as usize + 4..]);
let opcode = if rd(jirl) == 0 {
0x5000_0000
} else {
0x5400_0000
};
let opcode = if rd(jirl) == 0 { 0x5000_0000 } else { 0x5400_0000 };
set_insn(loc, opcode);
write_d10k16(loc, pcrel >> 2);
if ctx.args.emit_relocs {
@@ -900,10 +867,9 @@ where
R_LARCH_SUB16 => add_bits(loc, 16, sa, true),
R_LARCH_SUB32 => add_bits(loc, 32, sa, true),
R_LARCH_SUB64 => add_bits(loc, 64, sa, true),
R_LARCH_TLS_DTPREL32 => write_ul32(
loc,
tombstone().unwrap_or(sa.wrapping_sub(ctx.dtp_addr)) as u32,
),
R_LARCH_TLS_DTPREL32 => {
write_ul32(loc, tombstone().unwrap_or(sa.wrapping_sub(ctx.dtp_addr)) as u32)
}
R_LARCH_TLS_DTPREL64 => {
write_ul64(loc, tombstone().unwrap_or(sa.wrapping_sub(ctx.dtp_addr)))
}
@@ -928,10 +894,7 @@ where
// Records that `d` bytes go away at relocation `r`.
fn record<R: RelRecord>(deltas: &mut Vec<RelocDelta>, delta: &mut i64, r: &R, d: i64) {
*delta += d;
deltas.push(RelocDelta {
offset: r.r_offset(),
delta: *delta,
});
deltas.push(RelocDelta { offset: r.r_offset(), delta: *delta });
}
for i in 0..rels.len() {
@@ -1002,10 +965,9 @@ where
//
// addi.d $t0, $tp, <tp-offset>
R_LARCH_TLS_LE_HI20_R | R_LARCH_TLS_LE_ADD_R => {
let val = sym
.addr(ctx)
.wrapping_add(r.r_addend() as u64)
.wrapping_sub(ctx.tp_addr) as i64;
let val =
sym.addr(ctx).wrapping_add(r.r_addend() as u64).wrapping_sub(ctx.tp_addr)
as i64;
if is_int(val, 12) {
remove(4);
}
@@ -1076,10 +1038,9 @@ where
R_LARCH_TLS_DESC_PC_HI20 => {
if sym.has_tlsdesc(&ctx.symbols) {
let p = isec.addr(ctx) + r.r_offset();
let dist = sym
.tlsdesc_addr(ctx)
.wrapping_add(r.r_addend() as u64)
.wrapping_sub(p) as i64;
let dist =
sym.tlsdesc_addr(ctx).wrapping_add(r.r_addend() as u64).wrapping_sub(p)
as i64;
if is_int(dist, 22) {
remove(4);
}
@@ -1095,10 +1056,9 @@ where
R_LARCH_TLS_DESC_LD
if !sym.has_tlsdesc(&ctx.symbols) && !sym.has_gottp(&ctx.symbols) =>
{
let val = sym
.addr(ctx)
.wrapping_add(r.r_addend() as u64)
.wrapping_sub(ctx.tp_addr) as i64;
let val =
sym.addr(ctx).wrapping_add(r.r_addend() as u64).wrapping_sub(ctx.tp_addr)
as i64;
if (0..0x1000).contains(&val) {
remove(4);
}
+5 -10
View File
@@ -90,9 +90,7 @@ impl Arch for M68k {
);
write_ub32(
&mut buf[10..],
sym.gotplt_addr(ctx)
.wrapping_sub(sym.plt_addr(ctx))
.wrapping_sub(8) as u32,
sym.gotplt_addr(ctx).wrapping_sub(sym.plt_addr(ctx)).wrapping_sub(8) as u32,
);
}
@@ -101,9 +99,7 @@ impl Arch for M68k {
buf[..8].copy_from_slice(&INSN);
write_ub32(
&mut buf[4..],
sym.got_pltgot_addr(ctx)
.wrapping_sub(sym.plt_addr(ctx))
.wrapping_sub(2) as u32,
sym.got_pltgot_addr(ctx).wrapping_sub(sym.plt_addr(ctx)).wrapping_sub(2) as u32,
);
}
@@ -271,10 +267,9 @@ impl Arch for M68k {
match rel.r_type() {
R_68K_32 => write_ub32(loc, tombstone.unwrap_or(sa) as u32),
R_68K_TLS_LDO32 => write_ub32(
loc,
tombstone.unwrap_or(sa.wrapping_sub(ctx.dtp_addr)) as u32,
),
R_68K_TLS_LDO32 => {
write_ub32(loc, tombstone.unwrap_or(sa.wrapping_sub(ctx.dtp_addr)) as u32)
}
_ => fatal!(
"{}: invalid relocation for non-allocated sections: {}",
isec.display(file),
+5 -11
View File
@@ -128,10 +128,7 @@ impl Arch for Ppc32 {
const PLT_HDR_SIZE: u64 = 64;
const PLT_SIZE: u64 = 36;
const PLTGOT_SIZE: u64 = 36;
const THUNK: Option<ThunkLayout> = Some(ThunkLayout {
header_size: 0,
entry_size: 36,
});
const THUNK: Option<ThunkLayout> = Some(ThunkLayout { header_size: 0, entry_size: 36 });
const TRAP: &'static [u8] = &[0x7f, 0xe0, 0x00, 0x08]; // trap
const R_COPY: u32 = R_PPC_COPY;
@@ -257,9 +254,7 @@ impl Arch for Ppc32 {
) {
let file = &ctx.objs[isec.file.index()];
let got = u64::from(ctx.got.hdr.shdr.sh_addr.get());
let got2 = file
.got2
.map_or(0, |shndx| file.section_at(shndx).addr(ctx));
let got2 = file.got2.map_or(0, |shndx| file.section_at(shndx).addr(ctx));
for rel in rels {
if rel.r_type() == R_NONE {
@@ -344,10 +339,9 @@ impl Arch for Ppc32 {
match rel.r_type() {
R_PPC_ADDR32 => write_ub32(loc, tombstone.unwrap_or(sa) as u32),
R_PPC_DTPREL32 => write_ub32(
loc,
tombstone.unwrap_or(sa.wrapping_sub(ctx.dtp_addr)) as u32,
),
R_PPC_DTPREL32 => {
write_ub32(loc, tombstone.unwrap_or(sa.wrapping_sub(ctx.dtp_addr)) as u32)
}
_ => fatal!(
"{}: invalid relocation for non-allocated sections: {}",
isec.display(file),
+6 -22
View File
@@ -169,10 +169,7 @@ pub fn rewrite_opd(ctx: &mut Context<Ppc64V1>) {
let Some(opd) = file
.input_sections()
.find(|s| s.name(file) == ".opd")
.map(|s| SectionRef {
file: s.file,
shndx: s.shndx,
})
.map(|s| SectionRef { file: s.file, shndx: s.shndx })
else {
return;
};
@@ -180,12 +177,8 @@ pub fn rewrite_opd(ctx: &mut Context<Ppc64V1>) {
file.kill_section(opd.shndx as usize);
let local_symbols = &file.base.symbols;
let rels_at: HashMap<u64, ElfRel<Ppc64V1>> = file
.section_at(opd.shndx)
.rels(file)
.iter()
.map(|r| (r.r_offset(), *r))
.collect();
let rels_at: HashMap<u64, ElfRel<Ppc64V1>> =
file.section_at(opd.shndx).rels(file).iter().map(|r| (r.r_offset(), *r)).collect();
// Move symbols from .opd to .text.
let mut descriptors: Vec<(u64, u32)> = Vec::new(); // (offset in .opd, local symbol index)
@@ -294,10 +287,7 @@ impl Arch for Ppc64V1 {
const PLT_HDR_SIZE: u64 = 44;
const PLT_SIZE: u64 = 8;
const PLTGOT_SIZE: u64 = 0;
const THUNK: Option<ThunkLayout> = Some(ThunkLayout {
header_size: 0,
entry_size: 28,
});
const THUNK: Option<ThunkLayout> = Some(ThunkLayout { header_size: 0, entry_size: 28 });
const TRAP: &'static [u8] = &[0x7f, 0xe0, 0x00, 0x08]; // trap
const R_COPY: u32 = R_PPC64_COPY;
@@ -363,19 +353,13 @@ impl Arch for Ppc64V1 {
if idx < 0x8000 {
write_insns(buf, &[0x3800_0000, 0x4b00_0000]); // li r0, PLT_INDEX; b plt0
or32(buf, idx);
or32(
&mut buf[4..],
plt0.wrapping_sub(sym.plt_addr(ctx)).wrapping_sub(4) & 0x00ff_ffff,
);
or32(&mut buf[4..], plt0.wrapping_sub(sym.plt_addr(ctx)).wrapping_sub(4) & 0x00ff_ffff);
} else {
// lis r0, PLT_INDEX@high; ori r0, r0, PLT_INDEX@lo; b plt0
write_insns(buf, &[0x3c00_0000, 0x6000_0000, 0x4b00_0000]);
or32(buf, high(idx));
or32(&mut buf[4..], lo(idx));
or32(
&mut buf[8..],
plt0.wrapping_sub(sym.plt_addr(ctx)).wrapping_sub(8) & 0x00ff_ffff,
);
or32(&mut buf[8..], plt0.wrapping_sub(sym.plt_addr(ctx)).wrapping_sub(8) & 0x00ff_ffff);
}
}
+2 -8
View File
@@ -358,10 +358,7 @@ pub const SAVE_RESTORE_INSNS: &[(&str, u32)] = &[
/// The contents of the `.save_restore_regs` section.
pub fn save_restore_contents() -> Vec<u8> {
SAVE_RESTORE_INSNS
.iter()
.flat_map(|&(_, insn)| insn.to_le_bytes())
.collect()
SAVE_RESTORE_INSNS.iter().flat_map(|&(_, insn)| insn.to_le_bytes()).collect()
}
impl Arch for Ppc64V2 {
@@ -374,10 +371,7 @@ impl Arch for Ppc64V2 {
const PLT_HDR_SIZE: u64 = 52;
const PLT_SIZE: u64 = 4;
const PLTGOT_SIZE: u64 = 0;
const THUNK: Option<ThunkLayout> = Some(ThunkLayout {
header_size: 0,
entry_size: 24,
});
const THUNK: Option<ThunkLayout> = Some(ThunkLayout { header_size: 0, entry_size: 24 });
const TRAP: &'static [u8] = &[0x08, 0x00, 0xe0, 0x7f]; // trap
const R_COPY: u32 = R_PPC64_COPY;
+14 -51
View File
@@ -105,10 +105,7 @@ fn b(val: u64, hi: u32, lo: u32) -> u32 {
}
fn write_itype(loc: &mut [u8], val: u64) {
write32(
loc,
(insn32(loc) & 0b000000_00000_11111_111_11111_1111111) | (b(val, 11, 0) << 20),
);
write32(loc, (insn32(loc) & 0b000000_00000_11111_111_11111_1111111) | (b(val, 11, 0) << 20));
}
fn write_stype(loc: &mut [u8], val: u64) {
@@ -125,10 +122,7 @@ fn write_btype(loc: &mut [u8], val: u64) {
| b(val, 10, 5) << 25
| b(val, 4, 1) << 8
| (bit(val, 11) as u32) << 7;
write32(
loc,
(insn32(loc) & 0b000000_11111_11111_111_00000_1111111) | imm,
);
write32(loc, (insn32(loc) & 0b000000_11111_11111_111_00000_1111111) | imm);
}
fn write_utype(loc: &mut [u8], val: u64) {
@@ -149,10 +143,7 @@ fn write_jtype(loc: &mut [u8], val: u64) {
| b(val, 10, 1) << 21
| (bit(val, 11) as u32) << 20
| b(val, 19, 12) << 12;
write32(
loc,
(insn32(loc) & 0b000000_00000_00000_000_11111_1111111) | imm,
);
write32(loc, (insn32(loc) & 0b000000_00000_00000_000_11111_1111111) | imm);
}
fn write_citype(loc: &mut [u8], val: u64) {
@@ -191,10 +182,7 @@ fn write_cjtype(loc: &mut [u8], val: u64) {
fn set_rs1(loc: &mut [u8], rs1: u32) {
debug_assert!(rs1 < 32);
write32(
loc,
(insn32(loc) & 0b111111_11111_00000_111_11111_1111111) | (rs1 << 15),
);
write32(loc, (insn32(loc) & 0b111111_11111_00000_111_11111_1111111) | (rs1 << 15));
}
fn rd(loc: &[u8]) -> u32 {
@@ -313,21 +301,9 @@ where
const R_ABS: u32 = if IS_64 { R_RISCV_64 } else { R_RISCV_32 };
const R_RELATIVE: u32 = R_RISCV_RELATIVE;
const R_IRELATIVE: Option<u32> = Some(R_RISCV_IRELATIVE);
const R_DTPOFF: u32 = if IS_64 {
R_RISCV_TLS_DTPREL64
} else {
R_RISCV_TLS_DTPREL32
};
const R_TPOFF: u32 = if IS_64 {
R_RISCV_TLS_TPREL64
} else {
R_RISCV_TLS_TPREL32
};
const R_DTPMOD: u32 = if IS_64 {
R_RISCV_TLS_DTPMOD64
} else {
R_RISCV_TLS_DTPMOD32
};
const R_DTPOFF: u32 = if IS_64 { R_RISCV_TLS_DTPREL64 } else { R_RISCV_TLS_DTPREL32 };
const R_TPOFF: u32 = if IS_64 { R_RISCV_TLS_TPREL64 } else { R_RISCV_TLS_TPREL32 };
const R_DTPMOD: u32 = if IS_64 { R_RISCV_TLS_DTPMOD64 } else { R_RISCV_TLS_DTPMOD32 };
const R_TLSDESC: Option<u32> = Some(R_RISCV_TLSDESC);
const R_FUNCALL: &'static [u32] = &[R_RISCV_CALL, R_RISCV_CALL_PLT];
@@ -393,10 +369,7 @@ where
}
fn write_pltgot_entry(ctx: &Context<Self>, buf: &mut [u8], sym: &Symbol) {
write_plt_stub::<IS_64>(
buf,
sym.got_pltgot_addr(ctx).wrapping_sub(sym.plt_addr(ctx)),
);
write_plt_stub::<IS_64>(buf, sym.got_pltgot_addr(ctx).wrapping_sub(sym.plt_addr(ctx)));
}
fn apply_eh_reloc(
@@ -967,10 +940,7 @@ where
// Records that `d` bytes go away at relocation `r`.
fn record<R: RelRecord>(deltas: &mut Vec<RelocDelta>, delta: &mut i64, r: &R, d: i64) {
*delta += d;
deltas.push(RelocDelta {
offset: r.r_offset(),
delta: *delta,
});
deltas.push(RelocDelta { offset: r.r_offset(), delta: *delta });
}
for i in 0..rels.len() {
@@ -1089,10 +1059,9 @@ where
// sw t0, %tprel_lo(foo)(tp)
//
// Here, we remove `lui` and `add` if the offset is within ±2 KiB.
let val = sym
.addr(ctx)
.wrapping_add(r.r_addend() as u64)
.wrapping_sub(ctx.tp_addr) as i64;
let val =
sym.addr(ctx).wrapping_add(r.r_addend() as u64).wrapping_sub(ctx.tp_addr)
as i64;
if is_int(val, 12) {
remove(4);
}
@@ -1230,9 +1199,7 @@ fn parse_arch_string(s: &[u8]) -> Option<Vec<Extension<'_>>> {
let name = &element[..major_start];
let ok_name = name.bytes().next().is_some_and(|c| c.is_ascii_lowercase())
&& name.bytes().last().is_some_and(|c| c.is_ascii_lowercase())
&& name
.bytes()
.all(|c| c.is_ascii_lowercase() || c.is_ascii_digit());
&& name.bytes().all(|c| c.is_ascii_lowercase() || c.is_ascii_digit());
if !ok_name {
return None;
}
@@ -1255,11 +1222,7 @@ fn merge_extensions<'a>(x: &[Extension<'a>], y: &[Extension<'a>]) -> Option<Vec<
// Merge ISA extension strings
while let (Some(a), Some(b)) = (x.first(), y.first()) {
if a.name == b.name {
result.push(if (a.major, a.minor) < (b.major, b.minor) {
*b
} else {
*a
});
result.push(if (a.major, a.minor) < (b.major, b.minor) { *b } else { *a });
x = &x[1..];
y = &y[1..];
} else if extension_precedes(a.name, b.name) {
+2 -6
View File
@@ -68,10 +68,7 @@ fn or12(loc: &mut [u8], val: u64) {
/// Writes a 20-bit displacement, which is split into a 12-bit low part
/// and an 8-bit high part.
fn write_mid20(loc: &mut [u8], val: u64) {
write_ub32(
loc,
read_ub32(loc) | ((bits(val, 11, 0) << 16) | (bits(val, 19, 12) << 8)) as u32,
);
write_ub32(loc, read_ub32(loc) | ((bits(val, 11, 0) << 16) | (bits(val, 19, 12) << 8)) as u32);
}
/// Whether the GOT-loading LGRL at `loc` (opcode 0xc4?8, preceded by
@@ -245,8 +242,7 @@ impl Arch for S390x {
if ctx.args.is_static || (ctx.args.relax && !ctx.args.shared) {
// Do nothing
} else {
ctx.needs_tlsld
.store(true, std::sync::atomic::Ordering::Relaxed);
ctx.needs_tlsld.store(true, std::sync::atomic::Ordering::Relaxed);
}
}
R_390_TLS_LE32 | R_390_TLS_LE64 => check_tlsle(ctx, isec, sym, rel),
+6 -13
View File
@@ -142,11 +142,8 @@ where
const PLT_HDR_SIZE: u64 = 16;
const PLT_SIZE: u64 = 20;
const PLTGOT_SIZE: u64 = 12;
const TRAP: &'static [u8] = if End::IS_LITTLE {
&[0xfd, 0xff]
} else {
&[0xff, 0xfd]
}; // illegal
// illegal instruction
const TRAP: &'static [u8] = if End::IS_LITTLE { &[0xfd, 0xff] } else { &[0xff, 0xfd] };
const R_COPY: u32 = R_SH_COPY;
const R_GLOB_DAT: u32 = R_SH_GLOB_DAT;
@@ -248,10 +245,7 @@ where
0x0009, // nop
]; // 1: .long GOT_ENTRY
Self::write_insns(buf, &INSN);
End::write_u32(
&mut buf[8..],
got.wrapping_sub(ctx.got.hdr.shdr.sh_addr.get()) as u32,
);
End::write_u32(&mut buf[8..], got.wrapping_sub(ctx.got.hdr.shdr.sh_addr.get()) as u32);
} else {
const INSN: [u16; 4] = [
0xd001, // mov.l 1f, r0
@@ -372,10 +366,9 @@ where
match rel.r_type() {
R_SH_DIR32 => End::write_u32(loc, tombstone.unwrap_or(sa) as u32),
R_SH_TLS_LDO_32 => End::write_u32(
loc,
tombstone.unwrap_or(sa.wrapping_sub(ctx.dtp_addr)) as u32,
),
R_SH_TLS_LDO_32 => {
End::write_u32(loc, tombstone.unwrap_or(sa.wrapping_sub(ctx.dtp_addr)) as u32)
}
_ => fatal!(
"{}: invalid relocation for non-allocated sections: {}",
isec.display(file),
+19 -59
View File
@@ -191,10 +191,7 @@ impl Arch for Sparc64 {
}
let plt1 = plt + Self::PLT_SIZE;
or32(buf, bits(entry - plt, 21, 0));
or32(
&mut buf[4..],
bits(plt1.wrapping_sub(entry).wrapping_sub(4), 20, 2),
);
or32(&mut buf[4..], bits(plt1.wrapping_sub(entry).wrapping_sub(4), 20, 2));
} else {
const INSN: [u32; 6] = [
0x8a10_000f, // mov %o7, %g5
@@ -209,10 +206,7 @@ impl Arch for Sparc64 {
}
let call = entry + 4;
let ptroff = plt_ptr_offset(ctx.plt.symbols.len(), idx);
or32(
&mut buf[12..],
bits((plt + ptroff).wrapping_sub(call), 12, 0),
);
or32(&mut buf[12..], bits((plt + ptroff).wrapping_sub(call), 12, 0));
// The data pointer initially holds (.PLT0 - call) so that the first call
// jumps to .PLT0, where the loader's lazy resolver lives. The resolver
@@ -236,9 +230,7 @@ impl Arch for Sparc64 {
}
write_ub64(
&mut buf[24..],
sym.got_pltgot_addr(ctx)
.wrapping_sub(sym.plt_addr(ctx))
.wrapping_sub(4),
sym.got_pltgot_addr(ctx).wrapping_sub(sym.plt_addr(ctx)).wrapping_sub(4),
);
}
@@ -526,11 +518,7 @@ impl Arch for Sparc64 {
R_SPARC_PC22 | R_SPARC_PCPLT22 | R_SPARC_PC_LM22 => or32(loc, bits(pcrel, 31, 10)),
R_SPARC_OLO10 => or32(
loc,
bits(
bits(sa, 9, 0).wrapping_add(u64::from(rel.r_type_data.get())),
12,
0,
),
bits(bits(sa, 9, 0).wrapping_add(u64::from(rel.r_type_data.get())), 12, 0),
),
R_SPARC_HH22 => or32(loc, bits(sa, 63, 42)),
R_SPARC_HM10 => or32(loc, bits(sa, 41, 32)),
@@ -545,20 +533,12 @@ impl Arch for Sparc64 {
if sym.has_tlsgd(&ctx.symbols) {
or32(
loc,
bits(
sym.tlsgd_addr(ctx).wrapping_add(a).wrapping_sub(got),
31,
10,
),
bits(sym.tlsgd_addr(ctx).wrapping_add(a).wrapping_sub(got), 31, 10),
);
} else if sym.has_gottp(&ctx.symbols) {
or32(
loc,
bits(
sym.gottp_addr(ctx).wrapping_add(a).wrapping_sub(got),
31,
10,
),
bits(sym.gottp_addr(ctx).wrapping_add(a).wrapping_sub(got), 31, 10),
);
} else {
or32(loc, bits(!sa.wrapping_sub(ctx.tp_addr), 31, 10));
@@ -580,10 +560,7 @@ impl Arch for Sparc64 {
} else {
// add %rs1, %rs2, %rd → xor %rs1, $imm, %rd
write_ub32(loc, 0x8018_2000 | rs1 | rd);
or32(
loc,
bits(sa.wrapping_sub(ctx.tp_addr), 9, 0) | 0b1_1100_0000_0000,
);
or32(loc, bits(sa.wrapping_sub(ctx.tp_addr), 9, 0) | 0b1_1100_0000_0000);
}
}
R_SPARC_TLS_GD_ADD => {
@@ -599,10 +576,7 @@ impl Arch for Sparc64 {
}
R_SPARC_TLS_GD_CALL => {
if sym.has_tlsgd(&ctx.symbols) {
or32(
loc,
bits(tls_get_addr().wrapping_add(a).wrapping_sub(p), 31, 2),
);
or32(loc, bits(tls_get_addr().wrapping_add(a).wrapping_sub(p), 31, 2));
} else if sym.has_gottp(&ctx.symbols) {
// When we rewrite a branch instruction with a non-branch one,
// we need to swap the instruction and the following one so that
@@ -622,11 +596,7 @@ impl Arch for Sparc64 {
if ctx.got.has_tlsld() {
or32(
loc,
bits(
ctx.got.tlsld_addr().wrapping_add(a).wrapping_sub(got),
31,
10,
),
bits(ctx.got.tlsld_addr().wrapping_add(a).wrapping_sub(got), 31, 10),
);
} else {
or32(loc, bits(ctx.tp_addr.wrapping_sub(ctx.tls_begin), 31, 10));
@@ -649,33 +619,23 @@ impl Arch for Sparc64 {
}
R_SPARC_TLS_LDM_CALL => {
if ctx.got.has_tlsld() {
or32(
loc,
bits(tls_get_addr().wrapping_add(a).wrapping_sub(p), 31, 2),
);
or32(loc, bits(tls_get_addr().wrapping_add(a).wrapping_sub(p), 31, 2));
} else {
write_ub32(loc, 0x0100_0000); // nop
}
}
R_SPARC_TLS_LDO_HIX22 => or32(loc, bits(sa.wrapping_sub(ctx.dtp_addr), 31, 10)),
R_SPARC_TLS_LDO_LOX10 => or32(loc, bits(sa.wrapping_sub(ctx.dtp_addr), 9, 0)),
R_SPARC_TLS_IE_HI22 => or32(
loc,
bits(
sym.gottp_addr(ctx).wrapping_add(a).wrapping_sub(got),
31,
10,
),
),
R_SPARC_TLS_IE_LO10 => or32(
loc,
bits(sym.gottp_addr(ctx).wrapping_add(a).wrapping_sub(got), 9, 0),
),
R_SPARC_TLS_IE_HI22 => {
or32(loc, bits(sym.gottp_addr(ctx).wrapping_add(a).wrapping_sub(got), 31, 10))
}
R_SPARC_TLS_IE_LO10 => {
or32(loc, bits(sym.gottp_addr(ctx).wrapping_add(a).wrapping_sub(got), 9, 0))
}
R_SPARC_TLS_LE_HIX22 => or32(loc, bits(!sa.wrapping_sub(ctx.tp_addr), 31, 10)),
R_SPARC_TLS_LE_LOX10 => or32(
loc,
bits(sa.wrapping_sub(ctx.tp_addr), 9, 0) | 0b1_1100_0000_0000,
),
R_SPARC_TLS_LE_LOX10 => {
or32(loc, bits(sa.wrapping_sub(ctx.tp_addr), 9, 0) | 0b1_1100_0000_0000)
}
R_SPARC_SIZE32 => {
write_ub32(loc, sym.esym(ctx).st_size().get().wrapping_add(a) as u32)
}
+22 -44
View File
@@ -104,14 +104,8 @@ impl Arch for X86_64 {
buf[..32].copy_from_slice(&INSN);
let gotplt = ctx.gotplt.shdr.sh_addr.get();
let plt = ctx.plt.hdr.shdr.sh_addr.get();
write_u32(
&mut buf[8..],
gotplt.wrapping_sub(plt).wrapping_sub(4) as u32,
);
write_u32(
&mut buf[14..],
gotplt.wrapping_sub(plt).wrapping_sub(2) as u32,
);
write_u32(&mut buf[8..], gotplt.wrapping_sub(plt).wrapping_sub(4) as u32);
write_u32(&mut buf[14..], gotplt.wrapping_sub(plt).wrapping_sub(2) as u32);
}
fn write_plt_entry(ctx: &Context<Self>, buf: &mut [u8], sym: &Symbol) {
@@ -147,10 +141,7 @@ impl Arch for X86_64 {
0xcc, 0xcc, // (padding)
];
buf[..8].copy_from_slice(&INSN);
let disp = sym
.got_pltgot_addr(ctx)
.wrapping_sub(sym.plt_addr(ctx))
.wrapping_sub(6);
let disp = sym.got_pltgot_addr(ctx).wrapping_sub(sym.plt_addr(ctx)).wrapping_sub(6);
write_u32(&mut buf[2..], disp as u32);
}
@@ -263,8 +254,7 @@ impl Arch for X86_64 {
if ctx.args.is_static || (ctx.args.relax && !ctx.args.shared) {
i += 1;
} else {
ctx.needs_tlsld
.store(true, std::sync::atomic::Ordering::Relaxed);
ctx.needs_tlsld.store(true, std::sync::atomic::Ordering::Relaxed);
}
}
R_X86_64_GOTTPOFF | R_X86_64_CODE_4_GOTTPOFF => {
@@ -381,10 +371,9 @@ impl Arch for X86_64 {
R_X86_64_GOTPC64 => {
write_u64(&mut buf[off..], got_base.wrapping_add(a).wrapping_sub(p))
}
R_X86_64_GOTPCREL => write32s(
buf,
g.wrapping_add(got_base).wrapping_add(a).wrapping_sub(p),
),
R_X86_64_GOTPCREL => {
write32s(buf, g.wrapping_add(got_base).wrapping_add(a).wrapping_sub(p))
}
R_X86_64_GOTPCREL64 => write_u64(
&mut buf[off..],
g.wrapping_add(got_base).wrapping_add(a).wrapping_sub(p),
@@ -406,10 +395,7 @@ impl Arch for X86_64 {
continue;
}
}
write32s(
buf,
g.wrapping_add(got_base).wrapping_add(a).wrapping_sub(p),
);
write32s(buf, g.wrapping_add(got_base).wrapping_add(a).wrapping_sub(p));
}
R_X86_64_TLSGD => {
if sym.has_tlsgd(&ctx.symbols) {
@@ -434,10 +420,9 @@ impl Arch for X86_64 {
}
}
R_X86_64_DTPOFF32 => write32s(buf, s.wrapping_add(a).wrapping_sub(ctx.dtp_addr)),
R_X86_64_DTPOFF64 => write_u64(
&mut buf[off..],
s.wrapping_add(a).wrapping_sub(ctx.dtp_addr),
),
R_X86_64_DTPOFF64 => {
write_u64(&mut buf[off..], s.wrapping_add(a).wrapping_sub(ctx.dtp_addr))
}
R_X86_64_TPOFF32 => write32s(buf, s.wrapping_add(a).wrapping_sub(ctx.tp_addr)),
R_X86_64_TPOFF64 => {
write_u64(&mut buf[off..], s.wrapping_add(a).wrapping_sub(ctx.tp_addr))
@@ -523,10 +508,9 @@ impl Arch for X86_64 {
}
}
R_X86_64_SIZE32 => write32(buf, sym.esym(ctx).st_size().get().wrapping_add(a)),
R_X86_64_SIZE64 => write_u64(
&mut buf[off..],
sym.esym(ctx).st_size().get().wrapping_add(a),
),
R_X86_64_SIZE64 => {
write_u64(&mut buf[off..], sym.esym(ctx).st_size().get().wrapping_add(a))
}
_ => unreachable!("unexpected relocation {}", rel.type_name::<Self>()),
}
}
@@ -586,30 +570,24 @@ impl Arch for X86_64 {
},
R_X86_64_DTPOFF64 => match isec.tombstone_with_file(ctx, file, sym, frag) {
Some(v) => write_u64(&mut buf[off..], v),
None => write_u64(
&mut buf[off..],
s.wrapping_add(a).wrapping_sub(ctx.dtp_addr),
),
None => {
write_u64(&mut buf[off..], s.wrapping_add(a).wrapping_sub(ctx.dtp_addr))
}
},
R_X86_64_GOTOFF64 => write_u64(
&mut buf[off..],
s.wrapping_add(a)
.wrapping_sub(ctx.gotplt.shdr.sh_addr.get()),
s.wrapping_add(a).wrapping_sub(ctx.gotplt.shdr.sh_addr.get()),
),
R_X86_64_GOTPC64 => {
// PC-relative relocation doesn't make sense for non-memory-allocated
// section, but GCC 6.3.0 seems to create this reloc for
// _GLOBAL_OFFSET_TABLE_.
write_u64(
&mut buf[off..],
ctx.gotplt.shdr.sh_addr.get().wrapping_add(a),
)
write_u64(&mut buf[off..], ctx.gotplt.shdr.sh_addr.get().wrapping_add(a))
}
R_X86_64_SIZE32 => write32(buf, sym.esym(ctx).st_size().get().wrapping_add(a)),
R_X86_64_SIZE64 => write_u64(
&mut buf[off..],
sym.esym(ctx).st_size().get().wrapping_add(a),
),
R_X86_64_SIZE64 => {
write_u64(&mut buf[off..], sym.esym(ctx).st_size().get().wrapping_add(a))
}
_ => fatal!(
"{}: invalid relocation for non-allocated sections: {}",
isec.display(file),
+3 -13
View File
@@ -41,10 +41,7 @@ impl<'a> ArHeader<'a> {
fn parse(bytes: &'a [u8]) -> Option<Self> {
let bytes = bytes.get(..HEADER_SIZE)?;
let size = parse_decimal(&bytes[48..58]);
Some(ArHeader {
name: &bytes[..16],
size,
})
Some(ArHeader { name: &bytes[..16], size })
}
fn is_strtab(&self) -> bool {
@@ -76,11 +73,7 @@ impl<'a> ArHeader<'a> {
}
// Short fileanme
let end = self
.name
.iter()
.position(|&b| b == b'/')
.unwrap_or(self.name.len());
let end = self.name.iter().position(|&b| b == b'/').unwrap_or(self.name.len());
PathBuf::from(util::os_str(&self.name[..end]))
}
}
@@ -135,10 +128,7 @@ fn archive_members(
}
if thin && !hdr.name.starts_with(b"#1/") && !hdr.name.starts_with(b"/") {
fatal!(
"{}: filename is not stored as a long filename",
mf.name.display()
);
fatal!("{}: filename is not stored as a long filename", mf.name.display());
}
// Read the name field
+16 -27
View File
@@ -41,16 +41,11 @@ pub fn create<E: Arch>(ctx: &mut Context<E>) {
}) else {
return;
};
let ChunkId::Output(osec) = ctx.chunks[i] else {
unreachable!()
};
let ChunkId::Output(osec) = ctx.chunks[i] else { unreachable!() };
let mut hdr = ChunkHeader::<E>::new(".ARM.exidx", SHT_ARM_EXIDX, SHF_ALLOC as u64);
hdr.shdr.sh_addralign.set(4);
ctx.arm_exidx = Some(ArmExidxSection {
hdr,
output_section: osec,
});
ctx.arm_exidx = Some(ArmExidxSection { hdr, output_section: osec });
ctx.chunks[i] = ChunkId::ArmExidx;
// The input sections are consumed here rather than copied.
@@ -153,16 +148,13 @@ fn contents<E: Arch>(ctx: &Context<E>) -> Vec<u8> {
// records by address, we first translate them so that the addresses
// are relative to the beginning of the section.
let is_relative = |val: u32| val != CANTUNWIND && val & 0x8000_0000 == 0;
entries
.par_iter_mut()
.enumerate()
.for_each(|(i, (addr, val))| {
let offset = (i * ENTRY_SIZE) as u32;
*addr = (sign_extend(*addr as u64, 31) as u32).wrapping_add(offset);
if is_relative(*val) {
*val = 0x7fff_ffff & val.wrapping_add(offset);
}
});
entries.par_iter_mut().enumerate().for_each(|(i, (addr, val))| {
let offset = (i * ENTRY_SIZE) as u32;
*addr = (sign_extend(*addr as u64, 31) as u32).wrapping_add(offset);
if is_relative(*val) {
*val = 0x7fff_ffff & val.wrapping_add(offset);
}
});
entries.sort_by_key(|&(addr, _)| addr);
// Remove duplicate adjacent entries. That is, if two adjacent functions
@@ -171,16 +163,13 @@ fn contents<E: Arch>(ctx: &Context<E>) -> Vec<u8> {
entries.dedup_by_key(|&mut (_, val)| val);
// Make addresses relative to themselves.
entries
.par_iter_mut()
.enumerate()
.for_each(|(i, (addr, val))| {
let offset = (i * ENTRY_SIZE) as u32;
*addr = 0x7fff_ffff & addr.wrapping_sub(offset);
if is_relative(*val) {
*val = 0x7fff_ffff & val.wrapping_sub(offset);
}
});
entries.par_iter_mut().enumerate().for_each(|(i, (addr, val))| {
let offset = (i * ENTRY_SIZE) as u32;
*addr = 0x7fff_ffff & addr.wrapping_sub(offset);
if is_relative(*val) {
*val = 0x7fff_ffff & val.wrapping_sub(offset);
}
});
buf.truncate(entries.len() * ENTRY_SIZE);
for (i, (addr, val)) in entries.iter().enumerate() {
+1 -4
View File
@@ -20,10 +20,7 @@ impl<E: Layout> BuildIdSection<E> {
let mut hdr = ChunkHeader::<E>::new(".note.gnu.build-id", SHT_NOTE, SHF_ALLOC as u64);
hdr.shdr.sh_addralign.set(4);
hdr.shdr.sh_size.set(1);
BuildIdSection {
hdr,
contents: Vec::new(),
}
BuildIdSection { hdr, contents: Vec::new() }
}
}
+1 -2
View File
@@ -62,6 +62,5 @@ pub fn new<E: Arch>(ctx: &Context<E>, original: ChunkId) -> CompressedSection<E>
pub fn copy_buf<E: Arch>(ctx: &Context<E>, i: u32, buf: &mut [u8]) {
let sec = &ctx.compressed_sections[i as usize];
sec.chdr.write(buf);
sec.compressor
.write_to(&mut buf[std::mem::size_of::<ElfChdr<E>>()..]);
sec.compressor.write_to(&mut buf[std::mem::size_of::<ElfChdr<E>>()..]);
}
+3 -14
View File
@@ -19,17 +19,10 @@ pub struct CopyrelSection<E: Layout> {
impl<E: Layout> CopyrelSection<E> {
pub fn new(is_relro: bool) -> CopyrelSection<E> {
let name = if is_relro {
".copyrel.rel.ro"
} else {
".copyrel"
};
let name = if is_relro { ".copyrel.rel.ro" } else { ".copyrel" };
let mut hdr = ChunkHeader::<E>::new(name, SHT_NOBITS, (SHF_ALLOC | SHF_WRITE) as u64);
hdr.is_relro = is_relro;
CopyrelSection {
hdr,
symbols: Vec::new(),
}
CopyrelSection { hdr, symbols: Vec::new() }
}
}
@@ -67,11 +60,7 @@ pub fn add_symbol<E: Arch>(ctx: &mut Context<E>, relro: bool, id: SymbolId) {
// symbols have to refer to the copied place as well.
let aliases = dso.symbols_at(ctx, sym, dso_id);
let sec = if relro {
&mut ctx.copyrel_relro
} else {
&mut ctx.copyrel
};
let sec = if relro { &mut ctx.copyrel_relro } else { &mut ctx.copyrel };
sec.symbols.push(id);
let offset = align_to(sec.hdr.shdr.sh_size.get(), alignment);
sec.hdr.shdr.sh_size.set(offset + size);
+9 -36
View File
@@ -39,10 +39,7 @@ pub fn new_header<E: Arch>(args: &crate::cmdline::Args) -> ChunkHeader<E> {
//
// This function returns true if DT_AARCH64_VARIANT_PCS needs to be set.
fn contains_variant_pcs<E: Arch>(ctx: &Context<E>) -> bool {
ctx.plt
.symbols
.iter()
.any(|&id| ctx.symbols[id].esym(ctx).arm64_variant_pcs())
ctx.plt.symbols.iter().any(|&id| ctx.symbols[id].esym(ctx).arm64_variant_pcs())
}
fn sym_addr_if_defined<E: Arch>(ctx: &Context<E>, id: SymbolId) -> Option<u64> {
@@ -56,28 +53,18 @@ fn sym_addr_if_defined<E: Arch>(ctx: &Context<E>, id: SymbolId) -> Option<u64> {
fn for_each_entry<E: Arch>(ctx: &Context<E>, mut define: impl FnMut(u32, u64)) {
let dynstr = &ctx.dynstr;
let plt = &ctx.plt;
let (rel, relsz, relent) = if E::IS_RELA {
(DT_RELA, DT_RELASZ, DT_RELAENT)
} else {
(DT_REL, DT_RELSZ, DT_RELENT)
};
let (rel, relsz, relent) =
if E::IS_RELA { (DT_RELA, DT_RELASZ, DT_RELAENT) } else { (DT_REL, DT_RELSZ, DT_RELENT) };
for dso in &ctx.dsos {
define(DT_NEEDED, dynstr.find_string(dso.soname));
}
if !ctx.args.rpaths.is_empty() {
let tag = if ctx.args.enable_new_dtags {
DT_RUNPATH
} else {
DT_RPATH
};
let tag = if ctx.args.enable_new_dtags { DT_RUNPATH } else { DT_RPATH };
define(tag, dynstr.find_string(ctx.args.rpaths.as_encoded_bytes()));
}
if !ctx.args.soname.is_empty() {
define(
DT_SONAME,
dynstr.find_string(ctx.args.soname.as_encoded_bytes()),
);
define(DT_SONAME, dynstr.find_string(ctx.args.soname.as_encoded_bytes()));
}
for s in &ctx.args.auxiliary {
define(DT_AUXILIARY, dynstr.find_string(s));
@@ -158,10 +145,7 @@ fn for_each_entry<E: Arch>(ctx: &Context<E>, mut define: impl FnMut(u32, u64)) {
if ctx.find_chunk_by_type(SHT_PREINIT_ARRAY).is_some() {
let start = value(ctx.syms.preinit_array_start);
define(DT_PREINIT_ARRAY, start);
define(
DT_PREINIT_ARRAYSZ,
value(ctx.syms.preinit_array_end) - start,
);
define(DT_PREINIT_ARRAYSZ, value(ctx.syms.preinit_array_end) - start);
}
if ctx.find_chunk_by_type(SHT_FINI_ARRAY).is_some() {
let start = value(ctx.syms.fini_array_start);
@@ -247,12 +231,7 @@ fn for_each_entry<E: Arch>(ctx: &Context<E>, mut define: impl FnMut(u32, u64)) {
define(DT_AARCH64_VARIANT_PCS, 0);
}
// RISC-V has the same feature but with a different name.
if E::IS_RISCV
&& plt
.symbols
.iter()
.any(|&id| ctx.symbols[id].esym(ctx).riscv_variant_cc())
{
if E::IS_RISCV && plt.symbols.iter().any(|&id| ctx.symbols[id].esym(ctx).riscv_variant_cc()) {
define(DT_RISCV_VARIANT_CC, 0);
}
if E::FAMILY == Family::Ppc32 {
@@ -293,16 +272,10 @@ pub fn update_shdr<E: Arch>(ctx: &mut Context<E>) {
}
pub fn copy_buf<E: Arch>(ctx: &Context<E>, buf: &mut [u8]) {
debug_assert_eq!(
ctx.dynamic.as_ref().unwrap().shdr.sh_size.get() as usize,
buf.len()
);
debug_assert_eq!(ctx.dynamic.as_ref().unwrap().shdr.sh_size.get() as usize, buf.len());
let mut slots = buf.chunks_exact_mut(ElfDyn::<E>::size());
for_each_entry(ctx, |d_tag, d_val| {
let entry = ElfDyn::<E> {
d_tag: E::Word::new(d_tag as u64),
d_val: E::Word::new(d_val),
};
let entry = ElfDyn::<E> { d_tag: E::Word::new(d_tag as u64), d_val: E::Word::new(d_val) };
entry.write(slots.next().unwrap());
});
debug_assert!(slots.next().is_none());
+1 -4
View File
@@ -75,10 +75,7 @@ pub fn copy_buf<E: Arch>(ctx: &Context<E>, buf: &mut [u8]) {
.collect();
parts.into_par_iter().for_each(|(chunk, out)| {
for entry in chunk {
write_cstr(
&mut out[(entry.offset - chunk[0].offset) as usize..],
entry.name,
);
write_cstr(&mut out[(entry.offset - chunk[0].offset) as usize..], entry.name);
}
});
}
+3 -9
View File
@@ -34,11 +34,7 @@ impl<E: Arch> DynsymSection<E> {
let entsize = std::mem::size_of::<ElfSym<E>>() as u64;
hdr.shdr.sh_entsize.set(entsize);
hdr.shdr.sh_addralign.set(E::WORD_SIZE as u64);
DynsymSection {
hdr,
symbols: Vec::new(),
dynstr_entries: Vec::new(),
}
DynsymSection { hdr, symbols: Vec::new(), dynstr_entries: Vec::new() }
}
#[inline]
@@ -86,10 +82,8 @@ pub fn copy_buf<E: Arch>(ctx: &Context<E>, buf: &mut [u8]) {
}
});
if overflow.load(Ordering::Relaxed) {
let nshdrs = ctx
.shdr
.as_ref()
.map_or(0, |s| s.shdr.sh_size.get() / ElfShdr::<E>::size() as u64);
let nshdrs =
ctx.shdr.as_ref().map_or(0, |s| s.shdr.sh_size.get() / ElfShdr::<E>::size() as u64);
error!("{}: .dynsym: too many output sections: {nshdrs} requested, but ELF allows at most 65279",
ctx.args.output.display()
);
+9 -36
View File
@@ -152,18 +152,10 @@ pub fn copy_buf<E: Arch>(ctx: &Context<E>, buf: &mut [u8], hdr_buf: Option<&mut
for rel in cie.rels(file) {
let sym = &ctx.symbols[file.base.symbols[rel.r_sym() as usize]];
let loc = (rel.r_offset() - cie.input_offset as u64) as usize;
let val = sym
.addr(ctx)
.wrapping_add(file.section_at(cie.section).rel_addend(rel) as u64);
let val =
sym.addr(ctx).wrapping_add(file.section_at(cie.section).rel_addend(rel) as u64);
let p = sh_addr + cie.output_offset as u64 + loc as u64;
E::apply_eh_reloc(
ctx,
file.section_at(cie.section),
rel,
&mut dst[loc..],
p,
val,
);
E::apply_eh_reloc(ctx, file.section_at(cie.section), rel, &mut dst[loc..], p, val);
}
};
@@ -177,12 +169,7 @@ pub fn copy_buf<E: Arch>(ctx: &Context<E>, buf: &mut [u8], hdr_buf: Option<&mut
let mut items: Vec<Item<E>> = ctx
.objs
.iter()
.map(|file| Item {
file,
cies: Vec::new(),
fdes: None,
hdr_entries: None,
})
.map(|file| Item { file, cies: Vec::new(), fdes: None, hdr_entries: None })
.collect();
for (fi, file) in ctx.objs.iter().enumerate() {
@@ -234,10 +221,7 @@ pub fn copy_buf<E: Arch>(ctx: &Context<E>, buf: &mut [u8], hdr_buf: Option<&mut
dst[..contents.len()].copy_from_slice(contents);
let cie = &file.cies[fde.cie_idx as usize];
E::Endian::write_u32(
&mut dst[4..],
(offset + 4 - cie.output_offset as u64) as u32,
);
E::Endian::write_u32(&mut dst[4..], (offset + 4 - cie.output_offset as u64) as u32);
if ctx.args.relocatable {
continue;
}
@@ -248,18 +232,10 @@ pub fn copy_buf<E: Arch>(ctx: &Context<E>, buf: &mut [u8], hdr_buf: Option<&mut
for (j, rel) in rels.iter().enumerate() {
let sym = &ctx.symbols[file.base.symbols[rel.r_sym() as usize]];
let loc = (rel.r_offset() - fde.input_offset as u64) as usize;
let val = sym
.addr(ctx)
.wrapping_add(file.section_at(cie.section).rel_addend(rel) as u64);
let val =
sym.addr(ctx).wrapping_add(file.section_at(cie.section).rel_addend(rel) as u64);
let p = sh_addr + offset + loc as u64;
E::apply_eh_reloc(
ctx,
file.section_at(cie.section),
rel,
&mut dst[loc..],
p,
val,
);
E::apply_eh_reloc(ctx, file.section_at(cie.section), rel, &mut dst[loc..], p, val);
if j == 0 {
func_addr = val;
}
@@ -334,8 +310,5 @@ pub fn check_range<E: Arch>(
/// Fatal error for `.eh_frame` contents that can't be handled.
pub fn unsupported<E: Arch>(rel: &ElfRel<E>) -> ! {
fatal!(
"unsupported relocation in .eh_frame: {}",
rel.type_name::<E>()
)
fatal!("unsupported relocation in .eh_frame: {}", rel.type_name::<E>())
}
+4 -11
View File
@@ -11,11 +11,8 @@ use crate::input_files::ObjectFile;
// EhFrameRelocSection contains relocation records for .eh_frame. It is used
// only for relocatable outputs (an .o file rather than an executable or .so).
pub fn new_header<E: Arch>() -> ChunkHeader<E> {
let (name, ty) = if E::IS_RELA {
(".rela.eh_frame", SHT_RELA)
} else {
(".rel.eh_frame", SHT_REL)
};
let (name, ty) =
if E::IS_RELA { (".rela.eh_frame", SHT_RELA) } else { (".rel.eh_frame", SHT_REL) };
let mut hdr = ChunkHeader::<E>::new(name, ty, SHF_INFO_LINK as u64);
hdr.shdr.sh_addralign.set(E::WORD_SIZE as u64);
let entsize = std::mem::size_of::<ElfRel<E>>() as u64;
@@ -28,12 +25,8 @@ pub fn update_shdr<E: Arch>(ctx: &mut Context<E>) {
.objs
.par_iter()
.map(|file| {
let cies: usize = file
.cies
.iter()
.filter(|c| c.is_leader)
.map(|c| c.rels(file).len())
.sum();
let cies: usize =
file.cies.iter().filter(|c| c.is_leader).map(|c| c.rels(file).len()).sum();
let fdes: usize = file.fdes.iter().map(|f| f.rels(file).len()).sum();
cies + fdes
})
+1 -6
View File
@@ -32,12 +32,7 @@ impl<E: Layout> Default for GnuDebuglinkSection<E> {
}
pub fn update_shdr<E: Arch>(ctx: &mut Context<E>) {
let filename = ctx
.args
.separate_debug_file
.file_name()
.unwrap_or_default()
.as_encoded_bytes();
let filename = ctx.args.separate_debug_file.file_name().unwrap_or_default().as_encoded_bytes();
let size = align_to(filename.len() as u64 + 1, 4) + 4;
let sec = ctx.gnu_debuglink.as_mut().unwrap();
sec.hdr.shdr.sh_size.set(size);
+3 -13
View File
@@ -41,12 +41,7 @@ impl<E: Arch> GnuHashSection<E> {
pub fn new() -> GnuHashSection<E> {
let mut hdr = ChunkHeader::<E>::new(".gnu.hash", SHT_GNU_HASH, SHF_ALLOC as u64);
hdr.shdr.sh_addralign.set(E::WORD_SIZE as u64);
GnuHashSection {
hdr,
num_buckets: 0,
num_bloom: 1,
num_exported: 0,
}
GnuHashSection { hdr, num_buckets: 0, num_bloom: 1, num_exported: 0 }
}
}
@@ -63,9 +58,7 @@ pub fn update_shdr<E: Arch>(ctx: &mut Context<E>) {
let word = E::WORD_SIZE as u64;
let gh = ctx.gnu_hash.as_mut().unwrap();
// We allocate 12 bits for each symbol in the bloom filter.
gh.num_bloom = ((gh.num_exported as u64 * 12) / (word * 8))
.max(1)
.next_power_of_two() as u32;
gh.num_bloom = ((gh.num_exported as u64 * 12) / (word * 8)).max(1).next_power_of_two() as u32;
gh.hdr.shdr.sh_size.set(
GnuHashSection::<E>::HEADER_SIZE
+ gh.num_bloom as u64 * word // Bloom filter
@@ -125,10 +118,7 @@ pub fn copy_buf<E: Arch>(ctx: &Context<E>, buf: &mut [u8]) {
// least-significant bit 1.
let h = ctx.symbols[id.unwrap()].aux(&ctx.symbols).unwrap().djb_hash;
let last = i + 1 == syms.len() || indices[i] != indices[i + 1];
E::Endian::write_u32(
&mut buf[table_off + i * 4..],
if last { h | 1 } else { h & !1 },
);
E::Endian::write_u32(&mut buf[table_off + i * 4..], if last { h | 1 } else { h & !1 });
}
}
+10 -56
View File
@@ -68,11 +68,7 @@ pub fn add_got_symbol<E: Arch>(ctx: &mut Context<E>, sym: SymbolId) {
ctx.symbols.aux_mut(sym).got_idx = idx;
// An IFUNC symbol uses two GOT slots in a position-dependent
// executable.
let increment = if is_pde_ifunc {
2 * word::<E>()
} else {
word::<E>()
};
let increment = if is_pde_ifunc { 2 * word::<E>() } else { word::<E>() };
ctx.got.hdr.shdr.sh_size.set(size.wrapping_add(increment));
ctx.got.got_syms.push(sym);
}
@@ -143,12 +139,7 @@ struct GotEntry {
// Their addresses are not unique; each thread has its own copy of TLVs.
fn for_each_entry<E: Arch>(ctx: &Context<E>, mut emit: impl FnMut(GotEntry)) {
let mut add = |idx: u32, val: u64, r_type: u32, sym: Option<SymbolId>| {
emit(GotEntry {
idx,
val,
r_type,
sym,
});
emit(GotEntry { idx, val, r_type, sym });
};
let got = &ctx.got;
@@ -162,19 +153,9 @@ fn for_each_entry<E: Arch>(ctx: &Context<E>, mut emit: impl FnMut(GotEntry)) {
if sym.is_ifunc() {
if sym.is_pde_ifunc(ctx) {
add(idx, sym.plt_addr(ctx), R_NONE, None);
add(
idx + 1,
sym.addr_with(ctx, AddrFlags::NO_PLT),
r_irelative,
None,
);
add(idx + 1, sym.addr_with(ctx, AddrFlags::NO_PLT), r_irelative, None);
} else {
add(
idx,
sym.addr_with(ctx, AddrFlags::NO_PLT),
r_irelative,
None,
);
add(idx, sym.addr_with(ctx, AddrFlags::NO_PLT), r_irelative, None);
}
continue;
}
@@ -185,12 +166,7 @@ fn for_each_entry<E: Arch>(ctx: &Context<E>, mut emit: impl FnMut(GotEntry)) {
add(idx, 0, E::R_GLOB_DAT, Some(id));
} else if ctx.args.pic && sym.is_relative() {
// We know the symbol's address, but it needs a base relocation.
add(
idx,
sym.addr_with(ctx, AddrFlags::NO_PLT),
E::R_RELATIVE,
None,
);
add(idx, sym.addr_with(ctx, AddrFlags::NO_PLT), E::R_RELATIVE, None);
} else {
// We know the symbol's exact run-time address at link-time.
add(idx, sym.addr_with(ctx, AddrFlags::NO_PLT), R_NONE, None);
@@ -209,22 +185,12 @@ fn for_each_entry<E: Arch>(ctx: &Context<E>, mut emit: impl FnMut(GotEntry)) {
// If we are creating a shared library, we know the TLV's offset
// within the current TLS block. We don't know the module ID though.
add(idx, 0, E::R_DTPMOD, None);
add(
idx + 1,
sym.addr(ctx).wrapping_sub(ctx.dtp_addr),
R_NONE,
None,
);
add(idx + 1, sym.addr(ctx).wrapping_sub(ctx.dtp_addr), R_NONE, None);
} else {
// If we are creating an executable, we know both the module ID and
// the offset. Module ID 1 indicates the main executable.
add(idx, 1, R_NONE, None);
add(
idx + 1,
sym.addr(ctx).wrapping_sub(ctx.dtp_addr),
R_NONE,
None,
);
add(idx + 1, sym.addr(ctx).wrapping_sub(ctx.dtp_addr), R_NONE, None);
}
}
@@ -241,12 +207,7 @@ fn for_each_entry<E: Arch>(ctx: &Context<E>, mut emit: impl FnMut(GotEntry)) {
if sym.is_imported() {
add(idx, 0, r_tlsdesc, Some(id));
} else {
add(
idx,
sym.addr(ctx).wrapping_sub(ctx.tls_begin),
r_tlsdesc,
None,
);
add(idx, sym.addr(ctx).wrapping_sub(ctx.tls_begin), r_tlsdesc, None);
}
}
}
@@ -261,12 +222,7 @@ fn for_each_entry<E: Arch>(ctx: &Context<E>, mut emit: impl FnMut(GotEntry)) {
} else if ctx.args.shared {
// If we know the offset within the current thread vector,
// let the dynamic linker to adjust it.
add(
idx,
sym.addr(ctx).wrapping_sub(ctx.tls_begin),
E::R_TPOFF,
None,
);
add(idx, sym.addr(ctx).wrapping_sub(ctx.tls_begin), E::R_TPOFF, None);
} else {
// Otherwise, we know the offset from the thread pointer (TP) at
// link-time, so we can fill the GOT entry directly.
@@ -348,9 +304,7 @@ pub fn write_dynrels<E: Arch>(ctx: &Context<E>, out: &mut [E::Rel]) {
let rel = ElfRel::<E>::new(
ctx.got.hdr.shdr.sh_addr.get() + ent.idx as u64 * word::<E>(),
ent.r_type,
ent.sym
.and_then(|s| ctx.symbols[s].dynsym_idx(&ctx.symbols))
.unwrap_or(0),
ent.sym.and_then(|s| ctx.symbols[s].dynsym_idx(&ctx.symbols)).unwrap_or(0),
ent.val as i64,
);
let is_relr = rel.r_type() == E::R_RELATIVE && rel.r_offset().is_multiple_of(word::<E>());
+2 -10
View File
@@ -9,11 +9,7 @@ use crate::util::endian::Endian;
// .got.plt is similar to .got in the sense that it is a table containing
// pointers. The contents in .got.plt are function pointers used by .plt.
pub fn new_header<E: Arch>(args: &crate::cmdline::Args) -> ChunkHeader<E> {
let sh_type = if E::IS_PPC64 {
SHT_NOBITS
} else {
SHT_PROGBITS
};
let sh_type = if E::IS_PPC64 { SHT_NOBITS } else { SHT_PROGBITS };
let mut hdr = ChunkHeader::<E>::new(".got.plt", sh_type, (SHF_ALLOC | SHF_WRITE) as u64);
hdr.is_relro = args.z_now;
hdr.shdr.sh_addralign.set(E::WORD_SIZE as u64);
@@ -53,11 +49,7 @@ pub fn copy_buf<E: Arch>(ctx: &Context<E>, buf: &mut [u8]) {
};
// The first slot of .got.plt points to _DYNAMIC, as requested by
// the psABI. The second and the third slots are reserved by the psABI.
write(
buf,
0,
ctx.dynamic.as_ref().map_or(0, |d| d.shdr.sh_addr.get()),
);
write(buf, 0, ctx.dynamic.as_ref().map_or(0, |d| d.shdr.sh_addr.get()));
write(buf, 1, 0);
write(buf, 2, 0);
for i in 0..ctx.plt.symbols.len() {
+24 -81
View File
@@ -153,10 +153,7 @@ impl<E: Arch> BackgroundMerge<E> {
}
let input = file.section_at(info.shndx);
members[info.parent.index()].push(BackgroundMember {
reference: SectionRef {
file: file.id(),
shndx: info.shndx,
},
reference: SectionRef { file: file.id(), shndx: info.shndx },
info: info.clone(),
data: input.contents(),
filename: file.base.filename.clone(),
@@ -278,11 +275,7 @@ impl<E: Layout> MergedSection<E> {
let flags = sh_flags & !(SHF_GROUP as u64) & !(SHF_COMPRESSED as u64);
let mut entsize = shdr.sh_entsize.get();
if entsize == 0 {
entsize = if sh_flags & SHF_STRINGS as u64 != 0 {
1
} else {
shdr.sh_addralign.get()
};
entsize = if sh_flags & SHF_STRINGS as u64 != 0 { 1 } else { shdr.sh_addralign.get() };
}
if entsize == 0 {
return None;
@@ -290,24 +283,14 @@ impl<E: Layout> MergedSection<E> {
let name = merged_output_name(args, name, flags, entsize, addralign);
let sh_type = shdr.sh_type.get();
let key = MergedSectionKey {
name: BStr::new(&name),
flags,
sh_type,
entsize,
};
let key = MergedSectionKey { name: BStr::new(&name), flags, sh_type, entsize };
if let Some((_, id)) = cache.entries.iter().find(|(k, _)| *k == key) {
return Some(*id);
}
let mut remember = |id, name: &'static BStr| {
// Bound lookup cost even when --unique creates many sections.
if cache.entries.len() < 32 {
let key = MergedSectionKey {
name,
flags,
sh_type,
entsize,
};
let key = MergedSectionKey { name, flags, sh_type, entsize };
cache.entries.push((key, id));
}
Some(id)
@@ -355,9 +338,7 @@ impl<E: Layout> MergedSection<E> {
// Non-memory-allocated strings are typically identifiers used by debug info.
// To remove such strings, use the `strip` command.
let is_alive = !gc_sections || !self.is_alloc();
let (id, frag, _) = self
.map
.insert_with(data, hash, || SectionFragment::new(is_alive));
let (id, frag, _) = self.map.insert_with(data, hash, || SectionFragment::new(is_alive));
// Most insertions find the fragment there already, so the alignment
// is only written when it grows: an atomic update on every insertion
// would bounce the cache line of a popular fragment between cores.
@@ -375,12 +356,7 @@ impl<E: Layout> MergedSection<E> {
/// Splits the members into fragments and deduplicates them.
pub fn resolve<E: Arch>(ctx: &mut Context<E>, id: MergedSectionId) {
let timers = ctx.timers.clone();
let Context {
objs,
merged_sections,
args,
..
} = ctx;
let Context { objs, merged_sections, args, .. } = ctx;
let msec = &merged_sections[id.index()];
let gc_sections = args.gc_sections;
@@ -402,9 +378,8 @@ pub fn resolve<E: Arch>(ctx: &mut Context<E>, id: MergedSectionId) {
.fold(HyperLogLog::default, |mut sketch, (file, shndx)| {
let mut slots = std::mem::take(&mut file.sections);
for &i in shndx {
let (m, isec) = slots
.merge_info_with_section_mut(i as usize)
.expect("a mergeable section");
let (m, isec) =
slots.merge_info_with_section_mut(i as usize).expect("a mergeable section");
let name = isec.name(file);
m.split_contents::<E>(file, isec.contents(), name, msec, &mut sketch);
}
@@ -471,13 +446,7 @@ pub fn resolve_sections<E: Arch>(
members: &mut [Vec<ResolveMember<'_>>],
options: ResolveOptions<'_>,
) {
let ResolveOptions {
allocated_only,
gc_sections,
comment,
cmdline_args,
timers,
} = options;
let ResolveOptions { allocated_only, gc_sections, comment, cmdline_args, timers } = options;
let t = timers.start("split_contents");
let estimates: Vec<Option<HyperLogLog>> = sections
.par_iter()
@@ -504,15 +473,12 @@ pub fn resolve_sections<E: Arch>(
// We aim 2/3 occupation ratio
let t = timers.start("resize");
sections
.par_iter_mut()
.zip(&estimates)
.for_each(|(section, estimate)| {
if let Some(estimate) = estimate {
section.estimation = estimate.cardinality();
section.map = ConcurrentMap::with_capacity(section.estimation as usize * 3 / 2);
}
});
sections.par_iter_mut().zip(&estimates).for_each(|(section, estimate)| {
if let Some(estimate) = estimate {
section.estimation = estimate.cardinality();
section.map = ConcurrentMap::with_capacity(section.estimation as usize * 3 / 2);
}
});
drop(t);
let t = timers.start("resolve_contents");
@@ -522,9 +488,7 @@ pub fn resolve_sections<E: Arch>(
.filter(|(section, _)| !section.resolved && (!allocated_only || section.is_alloc()))
.for_each(|(section, members)| {
members.par_iter_mut().for_each(|member| {
member
.merge_info
.resolve_contents(member.data, section, gc_sections);
member.merge_info.resolve_contents(member.data, section, gc_sections);
});
});
drop(t);
@@ -540,11 +504,7 @@ pub fn resolve_sections<E: Arch>(
}
// Compute section alignment
let p2align = members
.iter()
.map(|member| member.merge_info.p2align)
.max()
.unwrap_or(0);
let p2align = members.iter().map(|member| member.merge_info.p2align).max().unwrap_or(0);
section.hdr.shdr.sh_addralign.set(1 << p2align);
section.fragments = std::mem::take(&mut section.map).freeze();
section.resolved = true;
@@ -621,37 +581,25 @@ pub fn layout<E: Layout>(msec: &mut MergedSection<E>) {
far_size += frags.key(entry).len() as u64;
}
}
ShardLayout {
near_size,
far_size,
fragments,
}
ShardLayout { near_size, far_size, fragments }
})
.collect();
let addralign = msec.hdr.shdr.sh_addralign.get();
let mut shard_offsets = Vec::with_capacity(NUM_SHARDS * 2 + 1);
shard_offsets.push(0);
for size in shards
.iter()
.map(|shard| shard.near_size)
.chain(shards.iter().map(|shard| shard.far_size))
for size in
shards.iter().map(|shard| shard.near_size).chain(shards.iter().map(|shard| shard.far_size))
{
shard_offsets.push(align_to(
shard_offsets.last().copied().unwrap() + size,
addralign,
));
shard_offsets.push(align_to(shard_offsets.last().copied().unwrap() + size, addralign));
}
shards.par_iter().enumerate().for_each(|(i, shard)| {
for &entry in &shard.fragments {
let frag = frags.get(entry);
if frag.is_alive() {
let base = if frag.is_32bit() {
shard_offsets[i]
} else {
shard_offsets[i + NUM_SHARDS]
};
let base =
if frag.is_32bit() { shard_offsets[i] } else { shard_offsets[i + NUM_SHARDS] };
frag.set_offset(frag.offset() + base);
}
}
@@ -707,10 +655,5 @@ pub fn write_to<E: Arch>(ctx: &Context<E>, id: MergedSectionId, buf: &mut [u8])
}
pub fn print_stats<E: Layout>(msec: &MergedSection<E>) {
out!(
"{} estimation={} actual={}",
msec.hdr.name,
msec.estimation,
msec.fragments.len()
);
out!("{} estimation={} actual={}", msec.hdr.name, msec.estimation, msec.fragments.len());
}
+11 -39
View File
@@ -197,10 +197,7 @@ impl<E: Layout> ChunkHeader<E> {
}
pub fn with_name(name: &'static BStr, sh_type: u32, sh_flags: u64) -> ChunkHeader<E> {
ChunkHeader {
name,
..ChunkHeader::<E>::new("", sh_type, sh_flags)
}
ChunkHeader { name, ..ChunkHeader::<E>::new("", sh_type, sh_flags) }
}
pub fn is_alloc(&self) -> bool {
@@ -242,10 +239,7 @@ impl<E: Arch> OutputPhdr<E> {
pub fn new(sh_flags: u64) -> OutputPhdr<E> {
let mut hdr = ChunkHeader::<E>::new("PHDR", 0, sh_flags);
hdr.shdr.sh_addralign.set(E::WORD_SIZE as u64);
OutputPhdr {
hdr,
phdrs: Vec::new(),
}
OutputPhdr { hdr, phdrs: Vec::new() }
}
}
@@ -274,11 +268,8 @@ fn write_ehdr<E: Arch>(ctx: &Context<E>, buf: &mut [u8]) {
let mut ehdr = ElfEhdr::<E>::default();
ehdr.e_ident[..4].copy_from_slice(b"\x7fELF");
ehdr.e_ident[EI_CLASS as usize] = if E::IS_64 { ELFCLASS64 } else { ELFCLASS32 } as u8;
ehdr.e_ident[EI_DATA as usize] = if E::IS_LITTLE_ENDIAN {
ELFDATA2LSB
} else {
ELFDATA2MSB
} as u8;
ehdr.e_ident[EI_DATA as usize] =
if E::IS_LITTLE_ENDIAN { ELFDATA2LSB } else { ELFDATA2MSB } as u8;
ehdr.e_ident[EI_VERSION as usize] = EV_CURRENT as u8;
ehdr.e_machine.set(E::E_MACHINE as u16);
ehdr.e_version.set(EV_CURRENT);
@@ -308,8 +299,7 @@ fn write_ehdr<E: Arch>(ctx: &Context<E>, buf: &mut [u8]) {
if let Some(phdr) = &ctx.phdr {
ehdr.e_phoff.set(phdr.hdr.shdr.sh_offset.get());
ehdr.e_phentsize
.set(std::mem::size_of::<ElfPhdr<E>>() as u16);
ehdr.e_phentsize.set(std::mem::size_of::<ElfPhdr<E>>() as u16);
ehdr.e_phnum
.set((phdr.hdr.shdr.sh_size.get() / std::mem::size_of::<ElfPhdr<E>>() as u64) as u16);
}
@@ -321,11 +311,7 @@ fn write_ehdr<E: Arch>(ctx: &Context<E>, buf: &mut [u8]) {
// large value there. If it is >65535, the real value is stored to
// the zero'th section's sh_size field.
let shnum = shdr.shdr.sh_size.get() / ElfShdr::<E>::size() as u64;
ehdr.e_shnum.set(if shnum <= u16::MAX as u64 {
shnum as u16
} else {
0
});
ehdr.e_shnum.set(if shnum <= u16::MAX as u64 { shnum as u16 } else { 0 });
}
ehdr.write(buf);
@@ -369,10 +355,7 @@ pub fn to_phdr_flags<E: Arch>(ctx: &Context<E>, id: ChunkId) -> u32 {
// .text is not readable if --execute-only
if exec && ctx.args.execute_only {
if write {
error!(
"--execute-only is not compatible with writable section: {}",
hdr.name
);
error!("--execute-only is not compatible with writable section: {}", hdr.name);
}
return PF_X;
}
@@ -398,8 +381,7 @@ fn create_phdr<E: Arch>(ctx: &Context<E>) -> Vec<ElfPhdr<E>> {
// significant for segments with zero on-file size. We still want to
// keep it congruent with the virtual address modulo page size
// because some loaders (at least FreeBSD's) are picky about it.
phdr.p_offset_mut()
.set(shdr.sh_addr.get() % ctx.args.page_size);
phdr.p_offset_mut().set(shdr.sh_addr.get() % ctx.args.page_size);
} else {
phdr.p_offset_mut().set(shdr.sh_offset.get());
phdr.p_filesz_mut().set(shdr.sh_size.get());
@@ -563,11 +545,7 @@ fn create_phdr<E: Arch>(ctx: &Context<E>) -> Vec<ElfPhdr<E>> {
// contain any segments. It controls executable bit of stack area.
let mut stack = ElfPhdr::<E>::default();
stack.p_type_mut().set(PT_GNU_STACK);
stack.p_flags_mut().set(if ctx.args.z_execstack {
PF_R | PF_W | PF_X
} else {
PF_R | PF_W
});
stack.p_flags_mut().set(if ctx.args.z_execstack { PF_R | PF_W | PF_X } else { PF_R | PF_W });
stack.p_memsz_mut().set(ctx.args.z_stack_size);
stack.p_align_mut().set(1);
vec.push(stack);
@@ -625,10 +603,7 @@ fn create_phdr<E: Arch>(ctx: &Context<E>) -> Vec<ElfPhdr<E>> {
vec[first].p_paddr_mut().set(addr);
addr += vec[first].p_memsz().get();
for p in vec[first + 1..]
.iter_mut()
.take_while(|p| p.p_type().get() == PT_LOAD)
{
for p in vec[first + 1..].iter_mut().take_while(|p| p.p_type().get() == PT_LOAD) {
if in_sync
&& addr <= p.p_vaddr().get()
&& p.p_vaddr().get() < addr + ctx.args.page_size * 2
@@ -645,10 +620,7 @@ fn create_phdr<E: Arch>(ctx: &Context<E>) -> Vec<ElfPhdr<E>> {
}
}
vec.resize(
vec.len() + ctx.args.spare_program_headers.max(0) as usize,
ElfPhdr::<E>::default(),
);
vec.resize(vec.len() + ctx.args.spare_program_headers.max(0) as usize, ElfPhdr::<E>::default());
vec
}
+2 -4
View File
@@ -27,12 +27,10 @@ pub fn update_shdr<E: Arch>(ctx: &mut Context<E>) {
}
pub fn copy_buf<E: Arch>(ctx: &Context<E>, buf: &mut [u8]) {
let content_size = ctx.note_package.shdr.sh_size.get() as u32 - 16;
buf.fill(0);
E::Endian::write_u32(buf, 4); // Name size
E::Endian::write_u32(
&mut buf[4..],
ctx.note_package.shdr.sh_size.get() as u32 - 16,
); // Content size
E::Endian::write_u32(&mut buf[4..], content_size);
E::Endian::write_u32(&mut buf[8..], NT_FDO_PACKAGING_METADATA);
buf[12..16].copy_from_slice(b"FDO\0");
write_cstr(&mut buf[16..], ctx.args.package_metadata.as_bytes()); // Content
+5 -17
View File
@@ -20,10 +20,7 @@ impl<E: Arch> NotePropertySection<E> {
pub fn new() -> NotePropertySection<E> {
let mut hdr = ChunkHeader::<E>::new(".note.gnu.property", SHT_NOTE, SHF_ALLOC as u64);
hdr.shdr.sh_addralign.set(E::WORD_SIZE as u64);
NotePropertySection {
hdr,
contents: Vec::new(),
}
NotePropertySection { hdr, contents: Vec::new() }
}
}
@@ -44,15 +41,9 @@ fn entry_size<E: Arch>() -> usize {
// Merges input files' .note.gnu.property values.
pub fn update_shdr<E: Arch>(ctx: &mut Context<E>) {
// Obtain the list of keys
let files: Vec<&crate::input_files::ObjectFile<E>> = ctx
.objs
.iter()
.filter(|file| !ctx.is_internal(file.id()))
.collect();
let keys: BTreeSet<u32> = files
.iter()
.flat_map(|f| f.gnu_properties.keys().copied())
.collect();
let files: Vec<&crate::input_files::ObjectFile<E>> =
ctx.objs.iter().filter(|file| !ctx.is_internal(file.id())).collect();
let keys: BTreeSet<u32> = files.iter().flat_map(|f| f.gnu_properties.keys().copied()).collect();
let value = |f: &crate::input_files::ObjectFile<E>, key: u32| {
f.gnu_properties.get(&key).copied().unwrap_or(0)
};
@@ -63,10 +54,7 @@ pub fn update_shdr<E: Arch>(ctx: &mut Context<E>) {
if (GNU_PROPERTY_X86_UINT32_AND_LO..=GNU_PROPERTY_X86_UINT32_AND_HI).contains(&key) {
// An AND feature is set if all input objects have the property and
// the feature.
map.insert(
key,
files.iter().fold(u32::MAX, |acc, f| acc & value(f, key)),
);
map.insert(key, files.iter().fold(u32::MAX, |acc, f| acc & value(f, key)));
} else if (GNU_PROPERTY_X86_UINT32_OR_LO..=GNU_PROPERTY_X86_UINT32_OR_HI).contains(&key) {
// An OR feature is set if some input object has the feature.
map.insert(key, files.iter().fold(0, |acc, f| acc | value(f, key)));
+4 -15
View File
@@ -27,10 +27,7 @@ impl<E: Layout> Ppc64OpdSection<E> {
pub fn new() -> Ppc64OpdSection<E> {
let mut hdr = ChunkHeader::<E>::new(".opd", SHT_PROGBITS, (SHF_ALLOC | SHF_WRITE) as u64);
hdr.shdr.sh_addralign.set(8);
Ppc64OpdSection {
hdr,
symbols: Vec::new(),
}
Ppc64OpdSection { hdr, symbols: Vec::new() }
}
}
@@ -42,15 +39,10 @@ impl<E: Layout> Default for Ppc64OpdSection<E> {
/// A descriptor holds the function's own address, not that of its PLT
/// entry or of the descriptor itself.
const ENTRY_POINT: AddrFlags = AddrFlags {
no_plt: true,
no_opd: true,
};
const ENTRY_POINT: AddrFlags = AddrFlags { no_plt: true, no_opd: true };
fn section<E: Arch>(ctx: &Context<E>) -> &Ppc64OpdSection<E> {
ctx.ppc64_opd
.as_ref()
.expect("PPC64 ELFv1 has an .opd section")
ctx.ppc64_opd.as_ref().expect("PPC64 ELFv1 has an .opd section")
}
fn toc<E: Arch>(ctx: &Context<E>) -> u64 {
@@ -58,10 +50,7 @@ fn toc<E: Arch>(ctx: &Context<E>) -> u64 {
}
pub fn add_symbol<E: Arch>(ctx: &mut Context<E>, sym: SymbolId) {
let opd = ctx
.ppc64_opd
.as_mut()
.expect("PPC64 ELFv1 has an .opd section");
let opd = ctx.ppc64_opd.as_mut().expect("PPC64 ELFv1 has an .opd section");
let idx = opd.symbols.len() as u32;
assert_ne!(idx, u32::MAX);
ctx.symbols.aux_mut(sym).opd_idx = idx;
+32 -68
View File
@@ -73,11 +73,7 @@ unsafe impl Sync for OutputBuffer<'_> {}
impl<'a> OutputBuffer<'a> {
#[inline]
pub(crate) fn new(buf: &'a mut [u8]) -> OutputBuffer<'a> {
OutputBuffer {
ptr: buf.as_mut_ptr(),
len: buf.len(),
marker: PhantomData,
}
OutputBuffer { ptr: buf.as_mut_ptr(), len: buf.len(), marker: PhantomData }
}
/// Runs `f` on one range owned by the current parallel-loop iteration.
@@ -178,11 +174,7 @@ pub fn layout<E: Arch>(ctx: &Context<E>, id: OutputSectionId) -> u64 {
off = align_to(off, 1 << isec.p2align()) + isec.sh_size;
align = align.max(1 << isec.p2align());
}
Group {
size: off,
offset: 0,
align,
}
Group { size: off, offset: 0, align }
})
.collect();
@@ -194,18 +186,15 @@ pub fn layout<E: Arch>(ctx: &Context<E>, id: OutputSectionId) -> u64 {
}
// Assign offsets to input sections.
osec.members
.par_chunks(GROUP_SIZE)
.zip(&groups)
.for_each(|(members, g)| {
let mut off = g.offset;
for &m in members {
let isec = ctx.input_section(m);
off = align_to(off, 1 << isec.p2align());
isec.set_offset(off);
off += isec.sh_size;
}
});
osec.members.par_chunks(GROUP_SIZE).zip(&groups).for_each(|(members, g)| {
let mut off = g.offset;
for &m in members {
let isec = ctx.input_section(m);
off = align_to(off, 1 << isec.p2align());
isec.set_offset(off);
off += isec.sh_size;
}
});
off
}
@@ -221,11 +210,9 @@ pub fn write_to<E: Arch>(ctx: &Context<E>, id: OutputSectionId, buf: &mut [u8])
members.par_iter().enumerate().for_each(|(i, &member)| {
let isec = ctx.input_section(member);
let start = isec.offset() as usize;
let next_start = members
.get(i + 1)
.map_or(osec.hdr.shdr.sh_size.get() as usize, |&next| {
ctx.input_section(next).offset() as usize
});
let next_start = members.get(i + 1).map_or(osec.hdr.shdr.sh_size.get() as usize, |&next| {
ctx.input_section(next).offset() as usize
});
// SAFETY: output-section member offsets are ordered and each
// iteration owns the bytes up to the next member.
unsafe {
@@ -325,22 +312,14 @@ pub fn copy_buf<E: Arch>(ctx: &Context<E>, id: OutputSectionId, buf: &mut [u8])
}
pub fn num_dynrels<E: Arch>(ctx: &Context<E>, id: OutputSectionId) -> u64 {
ctx.output_sections[id.index()]
.dynrel_offsets
.last()
.copied()
.unwrap_or(0)
ctx.output_sections[id.index()].dynrel_offsets.last().copied().unwrap_or(0)
}
/// Marks the base relocations that RELR can encode and returns their
/// offsets within the section.
pub fn relr_offsets<E: Arch>(ctx: &mut Context<E>, id: OutputSectionId) -> Vec<u64> {
let word = E::WORD_SIZE as u64;
let Context {
objs,
output_sections,
..
} = ctx;
let Context { objs, output_sections, .. } = ctx;
let osec = &mut output_sections[id.index()];
let nshards = osec.dynrel_offsets.len().saturating_sub(1);
let mut relr_offsets = vec![0u64; nshards + 1];
@@ -393,10 +372,8 @@ pub fn write_dynrels<E: Arch>(ctx: &Context<E>, id: OutputSectionId, out: &mut [
debug_assert_eq!(out.len(), count);
let slices = crate::output_file::split_at_offsets(out, &offsets[..nshards]);
osec.abs_rels
.par_chunks(DYNREL_SHARD_SIZE)
.zip(slices.into_par_iter())
.for_each(|(rels, slots)| {
osec.abs_rels.par_chunks(DYNREL_SHARD_SIZE).zip(slices.into_par_iter()).for_each(
|(rels, slots)| {
let mut i = 0;
for r in rels {
let sym = &ctx.symbols[r.sym];
@@ -409,12 +386,9 @@ pub fn write_dynrels<E: Arch>(ctx: &Context<E>, id: OutputSectionId, out: &mut [
}
let rel = match r.kind {
AbsRelKind::None | AbsRelKind::Relr => None,
AbsRelKind::BaseRel => Some(ElfRel::<E>::new(
p,
E::R_RELATIVE,
0,
s.wrapping_add(a as u64) as i64,
)),
AbsRelKind::BaseRel => {
Some(ElfRel::<E>::new(p, E::R_RELATIVE, 0, s.wrapping_add(a as u64) as i64))
}
AbsRelKind::IFunc => E::R_IRELATIVE.map(|r_type| {
let val = sym.addr_with(ctx, AddrFlags::NO_PLT).wrapping_add(a as u64);
ElfRel::<E>::new(p, r_type, 0, val as i64)
@@ -432,27 +406,20 @@ pub fn write_dynrels<E: Arch>(ctx: &Context<E>, id: OutputSectionId, out: &mut [
}
}
debug_assert_eq!(i, slots.len());
});
},
);
}
fn abs_rel_kind<E: Arch>(ctx: &Context<E>, sym: &crate::symbol::Symbol) -> AbsRelKind {
if sym.is_ifunc() {
return if sym.is_pde_ifunc(ctx) {
AbsRelKind::None
} else {
AbsRelKind::IFunc
};
return if sym.is_pde_ifunc(ctx) { AbsRelKind::None } else { AbsRelKind::IFunc };
}
if sym.is_absolute() {
return AbsRelKind::None;
}
// True if the symbol's address is in the output file.
if !sym.is_imported() || sym.flags() & NEEDS_CANONICAL != 0 {
return if ctx.args.pic {
AbsRelKind::BaseRel
} else {
AbsRelKind::None
};
return if ctx.args.pic { AbsRelKind::BaseRel } else { AbsRelKind::None };
}
AbsRelKind::DynRel
}
@@ -487,16 +454,13 @@ pub fn scan_abs_relocations<E: Arch>(
.flat_map_iter(|&m| {
let isec = ctx.input_section(m);
let file = &ctx.objs[isec.file.index()];
isec.rels(file)
.iter()
.filter(|r| is_absrel::<E>(r))
.map(move |r| AbsRel {
isec: m,
offset: r.r_offset(),
sym: file.base.symbols[r.r_sym() as usize],
addend: isec.rel_addend(r),
kind: AbsRelKind::None,
})
isec.rels(file).iter().filter(|r| is_absrel::<E>(r)).map(move |r| AbsRel {
isec: m,
offset: r.r_offset(),
sym: file.base.symbols[r.r_sym() as usize],
addend: isec.rel_addend(r),
kind: AbsRelKind::None,
})
})
.collect();
+2 -9
View File
@@ -29,10 +29,7 @@ impl<E: Arch> PltSection<E> {
} else {
hdr.shdr.sh_addralign.set(16);
}
PltSection {
hdr,
symbols: Vec::new(),
}
PltSection { hdr, symbols: Vec::new() }
}
}
@@ -115,11 +112,7 @@ pub fn compute_symtab_size<E: Arch>(ctx: &mut Context<E>) {
.par_iter()
.map(|&id| ctx.symbols[id].name().len() as u64 + "$plt".len() as u64 + 1)
.sum();
plt.hdr.num_local_symtab = if E::FAMILY == Family::Arm32 {
n * 3 + 2
} else {
n
};
plt.hdr.num_local_symtab = if E::FAMILY == Family::Arm32 { n * 3 + 2 } else { n };
plt.hdr.strtab_size = strtab_size;
}
+1 -4
View File
@@ -22,10 +22,7 @@ impl<E: Layout> PltGotSection<E> {
let mut hdr =
ChunkHeader::<E>::new(".plt.got", SHT_PROGBITS, (SHF_ALLOC | SHF_EXECINSTR) as u64);
hdr.shdr.sh_addralign.set(16);
PltGotSection {
hdr,
symbols: Vec::new(),
}
PltGotSection { hdr, symbols: Vec::new() }
}
}
+3 -10
View File
@@ -33,11 +33,7 @@ impl<E: Arch> RelDynSection<E> {
let mut hdr = ChunkHeader::<E>::new(name, ty, SHF_ALLOC as u64);
hdr.shdr.sh_entsize.set(entsize);
hdr.shdr.sh_addralign.set(align);
RelDynSection {
hdr,
android_encoded: Vec::new(),
keep_android_size: false,
}
RelDynSection { hdr, android_encoded: Vec::new(), keep_android_size: false }
}
}
@@ -100,11 +96,8 @@ pub fn construct_relr<E: Arch>(ctx: &mut Context<E>) {
}
}
let size: u64 = ctx
.chunks
.iter()
.map(|&id| ctx.chunk_header(id).relr.len() as u64 * word)
.sum();
let size: u64 =
ctx.chunks.iter().map(|&id| ctx.chunk_header(id).relr.len() as u64 * word).sum();
if let Some(relrdyn) = &mut ctx.relrdyn {
relrdyn.shdr.sh_size.set(size);
}
+7 -26
View File
@@ -21,14 +21,9 @@ pub struct RelocSection<E: Layout> {
pub fn new<E: Arch>(ctx: &Context<E>, osec_id: OutputSectionId) -> RelocSection<E> {
let osec = &ctx.output_sections[osec_id.index()];
let (prefix, ty) = if E::IS_RELA {
(b".rela".as_slice(), SHT_RELA)
} else {
(b".rel".as_slice(), SHT_REL)
};
let name = BStr::new(crate::util::leak_bytes(
[prefix, osec.hdr.name.as_ref()].concat(),
));
let (prefix, ty) =
if E::IS_RELA { (b".rela".as_slice(), SHT_RELA) } else { (b".rel".as_slice(), SHT_REL) };
let name = BStr::new(crate::util::leak_bytes([prefix, osec.hdr.name.as_ref()].concat()));
let mut hdr = ChunkHeader::<E>::with_name(name, ty, SHF_INFO_LINK as u64);
hdr.shdr.sh_addralign.set(E::WORD_SIZE as u64);
let entsize = std::mem::size_of::<ElfRel<E>>() as u64;
@@ -44,11 +39,7 @@ pub fn new<E: Arch>(ctx: &Context<E>, osec_id: OutputSectionId) -> RelocSection<
sum += isec.rels(file).len() as u64;
}
hdr.shdr.sh_size.set(sum * entsize);
RelocSection {
hdr,
output_section: osec_id,
offsets,
}
RelocSection { hdr, output_section: osec_id, offsets }
}
pub fn update_shdr<E: Arch>(ctx: &mut Context<E>, i: u32) {
@@ -95,10 +86,7 @@ fn symidx_addend<'a, E: Arch>(
if !isec.is_alloc() {
if let Some((frag, addend)) = isec.fragment(ctx, rel, cache) {
let msec = &ctx.merged_sections[frag.section.index()];
return (
msec.hdr.shndx,
msec.fragments.get(frag.entry).offset() as i64 + addend,
);
return (msec.hdr.shndx, msec.fragments.get(frag.entry).offset() as i64 + addend);
}
}
@@ -133,10 +121,7 @@ mod tests {
esym.set_type(STT_SECTION);
sym.set_esym(&esym);
assert_eq!(
output_symidx_addend(&ctx, &sym, || panic!("read discarded relocation")),
None
);
assert_eq!(output_symidx_addend(&ctx, &sym, || panic!("read discarded relocation")), None);
}
}
@@ -166,11 +151,7 @@ pub fn copy_buf<E: Arch>(ctx: &Context<E>, i: u32, buf: &mut [u8], osec_buf: Opt
// than in r_addend, and the relocation records we emit here are
// meant to be consumed as if they were in an object file, so we
// follow that convention.
let out_addend = if E::FAMILY == crate::arch::Family::Sh4 {
0
} else {
addend
};
let out_addend = if E::FAMILY == crate::arch::Family::Sh4 { 0 } else { addend };
out[base + j] = ElfRel::<E>::new(r_offset, rel.r_type(), symidx, out_addend);
if ctx.args.relocatable {
+1 -5
View File
@@ -7,11 +7,7 @@ use crate::elf::*;
// .rel.plt contains relocation information for .plt.
pub fn new_header<E: Arch>() -> ChunkHeader<E> {
let (name, ty) = if E::IS_RELA {
(".rela.plt", SHT_RELA)
} else {
(".rel.plt", SHT_REL)
};
let (name, ty) = if E::IS_RELA { (".rela.plt", SHT_RELA) } else { (".rel.plt", SHT_REL) };
let mut hdr = ChunkHeader::<E>::new(name, ty, SHF_ALLOC as u64);
let entsize = std::mem::size_of::<ElfRel<E>>() as u64;
hdr.shdr.sh_entsize.set(entsize);
+2 -10
View File
@@ -40,11 +40,7 @@ use crate::util::endian::Endian;
//
// .relr.dyn is designed to store base relocations in a space-efficient way.
pub fn new_header<E: Arch>(args: &crate::cmdline::Args) -> ChunkHeader<E> {
let ty = if args.use_android_relr_tags {
SHT_ANDROID_RELR
} else {
SHT_RELR
};
let ty = if args.use_android_relr_tags { SHT_ANDROID_RELR } else { SHT_RELR };
let mut hdr = ChunkHeader::<E>::new(".relr.dyn", ty, SHF_ALLOC as u64);
hdr.shdr.sh_entsize.set(E::WORD_SIZE as u64);
hdr.shdr.sh_addralign.set(E::WORD_SIZE as u64);
@@ -57,11 +53,7 @@ pub fn copy_buf<E: Arch>(ctx: &Context<E>, buf: &mut [u8]) {
for &id in &ctx.chunks {
let hdr = ctx.chunk_header(id);
for &val in &hdr.relr {
let v = if val & 1 != 0 {
val
} else {
hdr.shdr.sh_addr.get() + val
};
let v = if val & 1 != 0 { val } else { hdr.shdr.sh_addr.get() + val };
if E::IS_64 {
E::Endian::write_u64(&mut buf[i * w..], v);
} else {
+4 -16
View File
@@ -27,11 +27,7 @@ impl<E: Layout> SFrameSection<E> {
pub fn new() -> SFrameSection<E> {
let mut hdr = ChunkHeader::<E>::new(".sframe", SHT_GNU_SFRAME, SHF_ALLOC as u64);
hdr.shdr.sh_addralign.set(8);
SFrameSection {
hdr,
header: SFrameHeader::<E>::default(),
fdes: Vec::new(),
}
SFrameSection { hdr, header: SFrameHeader::<E>::default(), fdes: Vec::new() }
}
}
@@ -79,11 +75,7 @@ pub fn construct<E: Arch>(ctx: &mut Context<E>) {
magic: U16::new(SFRAME_MAGIC),
version: 3,
flags: SFRAME_F_FDE_FUNC_START_PCREL
| if ctx.args.relocatable {
0
} else {
SFRAME_F_FDE_SORTED
},
| if ctx.args.relocatable { 0 } else { SFRAME_F_FDE_SORTED },
abi_arch: abi,
cfa_fixed_ra_offset: if E::FAMILY == Family::X86_64 { -8 } else { 0 },
num_fdes: U32::new(fdes.len() as u32),
@@ -113,9 +105,7 @@ pub fn sort<E: Arch>(ctx: &mut Context<E>) {
// each entry in that same order.
let addr = |ctx: &Context<E>, (fi, i): (ObjId, u32)| {
let fde = &ctx.objs[fi.index()].sframe_fdes[i as usize];
ctx.symbols[fde.sym]
.addr(ctx)
.wrapping_add(fde.addend as u64)
ctx.symbols[fde.sym].addr(ctx).wrapping_add(fde.addend as u64)
};
let mut fdes = std::mem::take(&mut ctx.sframe.fdes);
fdes.sort_by_key(|&f| addr(ctx, f));
@@ -146,9 +136,7 @@ pub fn copy_buf<E: Arch>(ctx: &Context<E>, buf: &mut [u8]) {
let func_start_offset = if ctx.args.relocatable {
0
} else {
let func_addr = ctx.symbols[fde.sym]
.addr(ctx)
.wrapping_add(fde.addend as u64);
let func_addr = ctx.symbols[fde.sym].addr(ctx).wrapping_add(fde.addend as u64);
let field_addr =
sframe.hdr.shdr.sh_addr.get() + hdr_size as u64 + (i * idx_size) as u64;
func_addr.wrapping_sub(field_addr) as i64
+15 -36
View File
@@ -26,11 +26,7 @@ pub fn update_shdr<E: Arch>(ctx: &mut Context<E>) {
let mut nsyms = 1u32;
// Section symbols
nsyms += ctx
.chunks
.iter()
.filter(|&&id| ctx.chunk_header(id).shndx != 0)
.count() as u32;
nsyms += ctx.chunks.iter().filter(|&&id| ctx.chunk_header(id).shndx != 0).count() as u32;
// Linker-synthesized symbols
for i in 0..ctx.chunks.len() {
@@ -57,11 +53,7 @@ pub fn update_shdr<E: Arch>(ctx: &mut Context<E>) {
}
let first_global = ctx.objs.first().map_or(nsyms, |f| f.base.global_symtab_idx);
let size = if nsyms == 1 {
0
} else {
nsyms as u64 * std::mem::size_of::<ElfSym<E>>() as u64
};
let size = if nsyms == 1 { 0 } else { nsyms as u64 * std::mem::size_of::<ElfSym<E>>() as u64 };
ctx.symtab.shdr.sh_info.set(first_global);
ctx.symtab.shdr.sh_link.set(ctx.strtab.shndx);
ctx.symtab.shdr.sh_size.set(size);
@@ -159,15 +151,9 @@ pub fn copy_buf<E: Arch>(
let (mut rest, mut pos) = (symtab, 0);
let (mut xrest, mut xpos) = (xindex, 0);
let mut entries = |(start, count): (u32, u32)| {
let syms = carve(
&mut rest,
&mut pos,
start as usize * size,
count as usize * size,
);
let xindex = xrest
.as_mut()
.map(|x| carve(x, &mut xpos, start as usize * 4, count as usize * 4));
let syms = carve(&mut rest, &mut pos, start as usize * size, count as usize * size);
let xindex =
xrest.as_mut().map(|x| carve(x, &mut xpos, start as usize * 4, count as usize * 4));
(syms, xindex)
};
let locals: Vec<_> = parts.iter().map(|p| entries(p.locals)).collect();
@@ -177,12 +163,8 @@ pub fn copy_buf<E: Arch>(
.zip(locals)
.map(|(part, (lsyms, lx))| {
let (gsyms, gx) = entries(part.globals);
let strtab = carve(
&mut srest,
&mut spos,
part.strtab.0 as usize,
part.strtab.1 as usize,
);
let strtab =
carve(&mut srest, &mut spos, part.strtab.0 as usize, part.strtab.1 as usize);
let block = SymtabBlock::new(
SymtabEntries::new(lsyms, lx),
SymtabEntries::new(gsyms, gx),
@@ -193,12 +175,11 @@ pub fn copy_buf<E: Arch>(
})
.collect();
work.into_par_iter()
.for_each(|(writer, mut block)| match writer {
Writer::Chunk(id) => chunks::populate_symtab(ctx, id, &mut block),
Writer::Obj(id) => ctx.objs[id.index()].populate_symtab(ctx, id, &mut block),
Writer::Dso(id) => ctx.dsos[id.index()].populate_symtab(ctx, id, &mut block),
});
work.into_par_iter().for_each(|(writer, mut block)| match writer {
Writer::Chunk(id) => chunks::populate_symtab(ctx, id, &mut block),
Writer::Obj(id) => ctx.objs[id.index()].populate_symtab(ctx, id, &mut block),
Writer::Dso(id) => ctx.dsos[id.index()].populate_symtab(ctx, id, &mut block),
});
}
/// Splits `len` bytes starting at `start` off `rest`, which begins
@@ -335,9 +316,8 @@ pub fn to_output_esym<E: Arch>(ctx: &Context<E>, sym: &Symbol, st_name: u32) ->
let m = file
.merge_info(file.shndx_at_in(sym.sym_idx() as usize))
.expect("mergeable section");
let (frag, addend) = m
.fragment(sym.esym(ctx).st_value().get())
.expect("fragment");
let (frag, addend) =
m.fragment(sym.esym(ctx).st_value().get()).expect("fragment");
let msec = &ctx.merged_sections[m.parent.index()];
shndx = Some(msec.hdr.shndx);
esym.set_visibility(sym.visibility());
@@ -349,8 +329,7 @@ pub fn to_output_esym<E: Arch>(ctx: &Context<E>, sym: &Symbol, st_name: u32) ->
// Symbol in a regular section
shndx = Some(st_shndx_of(sym, isec));
esym.set_visibility(sym.visibility());
esym.st_value_mut()
.set(sym.addr_with(ctx, AddrFlags::NO_PLT));
esym.st_value_mut().set(sym.addr_with(ctx, AddrFlags::NO_PLT));
}
}
}
+16 -33
View File
@@ -24,10 +24,7 @@ impl<E: Layout> VerdefSection<E> {
pub fn new() -> VerdefSection<E> {
let mut hdr = ChunkHeader::<E>::new(".gnu.version_d", SHT_GNU_VERDEF, SHF_ALLOC as u64);
hdr.shdr.sh_addralign.set(4);
VerdefSection {
hdr,
contents: Vec::new(),
}
VerdefSection { hdr, contents: Vec::new() }
}
}
@@ -61,23 +58,19 @@ pub fn construct<E: Arch>(ctx: &mut Context<E>) {
ctx.versym.contents.resize(n, VER_NDX_GLOBAL as u16);
ctx.versym.contents[0] = VER_NDX_LOCAL as u16;
ctx.versym
.contents
.par_iter_mut()
.zip(&ctx.dynsym.symbols)
.for_each(|(ver, &id)| {
let Some(id) = id else { return };
let sym = &ctx.symbols[id];
if !matches!(sym.file(), Some(FileId::Obj(_))) {
return;
}
// An unversioned undefined symbol takes version index 0.
if sym.ver_idx as u32 != VER_NDX_UNSPECIFIED {
*ver = sym.ver_idx;
} else if sym.is_undef() {
*ver = VER_NDX_LOCAL as u16;
}
});
ctx.versym.contents.par_iter_mut().zip(&ctx.dynsym.symbols).for_each(|(ver, &id)| {
let Some(id) = id else { return };
let sym = &ctx.symbols[id];
if !matches!(sym.file(), Some(FileId::Obj(_))) {
return;
}
// An unversioned undefined symbol takes version index 0.
if sym.ver_idx as u32 != VER_NDX_UNSPECIFIED {
*ver = sym.ver_idx;
} else if sym.is_undef() {
*ver = VER_NDX_LOCAL as u16;
}
});
// Allocate a buffer for .gnu.version_d and write to it
let verdef_size = ElfVerdef::<E>::size();
@@ -119,21 +112,11 @@ pub fn construct<E: Arch>(ctx: &mut Context<E>) {
};
let soname = if ctx.args.soname.is_empty() {
ctx.args
.output
.file_name()
.unwrap_or_default()
.as_encoded_bytes()
ctx.args.output.file_name().unwrap_or_default().as_encoded_bytes()
} else {
ctx.args.soname.as_encoded_bytes()
};
write(
&mut contents,
&mut ctx.dynstr,
soname,
1,
VER_FLG_BASE as u16,
);
write(&mut contents, &mut ctx.dynstr, soname, 1, VER_FLG_BASE as u16);
for (i, verstr) in ctx.args.version_definitions.iter().enumerate() {
write(
+2 -8
View File
@@ -22,10 +22,7 @@ impl<E: Layout> VerneedSection<E> {
pub fn new() -> VerneedSection<E> {
let mut hdr = ChunkHeader::<E>::new(".gnu.version_r", SHT_GNU_VERNEED, SHF_ALLOC as u64);
hdr.shdr.sh_addralign.set(4);
VerneedSection {
hdr,
contents: Vec::new(),
}
VerneedSection { hdr, contents: Vec::new() }
}
}
@@ -51,10 +48,7 @@ impl<E: Layout> Default for VerneedSection<E> {
// about this dummy version name and simply ignores it.
fn is_glibc2<E: Layout>(dso: &crate::input_files::SharedFile<E>) -> bool {
dso.soname.starts_with(b"libc.so.")
&& dso
.version_strings
.iter()
.any(|v| v.starts_with(b"GLIBC_2."))
&& dso.version_strings.iter().any(|v| v.starts_with(b"GLIBC_2."))
}
pub fn construct<E: Arch>(ctx: &mut Context<E>) {
+1 -4
View File
@@ -25,10 +25,7 @@ impl<E: Layout> VersymSection<E> {
let mut hdr = ChunkHeader::<E>::new(".gnu.version", SHT_GNU_VERSYM, SHF_ALLOC as u64);
hdr.shdr.sh_entsize.set(2);
hdr.shdr.sh_addralign.set(2);
VersymSection {
hdr,
contents: Vec::new(),
}
VersymSection { hdr, contents: Vec::new() }
}
}
+61 -183
View File
@@ -768,10 +768,7 @@ fn read_response_file(path: &Path, depth: usize) -> Vec<Cow<'static, OsStr>> {
fatal!("{}: premature end of input", path.display());
}
if let Some(nested) = tok.strip_prefix(b"@") {
expanded.extend(read_response_file(
Path::new(util::os_str(nested)),
depth + 1,
));
expanded.extend(read_response_file(Path::new(util::os_str(nested)), depth + 1));
} else {
expanded.push(match tok {
Cow::Borrowed(bytes) => Cow::Borrowed(util::os_str(bytes)),
@@ -818,17 +815,11 @@ fn match_option<'a>(arg: &'a OsStr, name: &str) -> Option<&'a OsStr> {
if name.starts_with('o') && !arg.starts_with(b"-") {
return None;
}
arg.strip_prefix(b"-")
.unwrap_or(arg)
.strip_prefix(name.as_bytes())
.map(util::os_str)
arg.strip_prefix(b"-").unwrap_or(arg).strip_prefix(name.as_bytes()).map(util::os_str)
}
fn parse_hex(opt: &str, value: &str) -> u64 {
let digits = value
.strip_prefix("0x")
.or_else(|| value.strip_prefix("0X"))
.unwrap_or(value);
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!("option -{opt}: not a hexadecimal number");
}
@@ -844,16 +835,14 @@ fn parse_c_number(s: &str) -> Option<u64> {
} else {
(false, s.strip_prefix('+').unwrap_or(s))
};
let (digits, radix) = if let Some(hex) = digits
.strip_prefix("0x")
.or_else(|| digits.strip_prefix("0X"))
{
(hex, 16)
} else if digits.starts_with('0') {
(digits, 8)
} else {
(digits, 10)
};
let (digits, radix) =
if let Some(hex) = digits.strip_prefix("0x").or_else(|| digits.strip_prefix("0X")) {
(hex, 16)
} else if digits.starts_with('0') {
(digits, 8)
} else {
(digits, 10)
};
if digits.is_empty() || !digits.chars().all(|c| c.is_digit(radix)) {
return None;
}
@@ -889,11 +878,7 @@ fn parse_hex_build_id(arg: &str) -> Vec<u8> {
.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!("invalid build-id: {arg}"));
digits
.as_bytes()
.chunks(2)
.map(|pair| (from_hex(pair[0]) << 4) | from_hex(pair[1]))
.collect()
digits.as_bytes().chunks(2).map(|pair| (from_hex(pair[0]) << 4) | from_hex(pair[1])).collect()
}
fn parse_package_metadata(arg: &str) -> String {
@@ -937,27 +922,19 @@ fn parse_section_order(arg: &[u8]) -> Vec<SectionOrder> {
}
};
let is_section_name = |s: &[u8]| {
s.first()
.copied()
.is_some_and(|c| c.is_ascii_alphanumeric() || c == b'_' || c == b'.')
s.first().copied().is_some_and(|c| c.is_ascii_alphanumeric() || c == b'_' || c == b'.')
&& !s.iter().any(|c| c.is_ascii_whitespace())
};
let mut orders = Vec::new();
for tok in arg
.split(|&c| c == b' ' || c == b'\t')
.filter(|t| !t.is_empty())
{
for tok in arg.split(|&c| c == b' ' || c == b'\t').filter(|t| !t.is_empty()) {
let order = if matches!(
tok.to_ascii_uppercase().as_slice(),
b"TEXT" | b"DATA" | b"RODATA" | b"BSS"
) {
SectionOrder::Group(std::str::from_utf8(tok).unwrap().to_string())
} else if let Some(value) = tok.strip_prefix(b"=").and_then(parse_value) {
SectionOrder::Addr {
value,
token: std::str::from_utf8(tok).unwrap().to_string(),
}
SectionOrder::Addr { value, token: std::str::from_utf8(tok).unwrap().to_string() }
} else if let Some(v) = tok.strip_prefix(b"%").and_then(parse_value) {
SectionOrder::Align(v)
} else if let Some(name) = tok.strip_prefix(b"!").filter(|n| !n.is_empty()) {
@@ -988,9 +965,8 @@ fn parse_section_order(arg: &[u8]) -> Vec<SectionOrder> {
fn parse_defsym_value(s: &[u8]) -> DefsymValue {
if let Some(hex) = s.strip_prefix(b"0x").or_else(|| s.strip_prefix(b"0X")) {
let Some(v) = std::str::from_utf8(hex)
.ok()
.and_then(|hex| u64::from_str_radix(hex, 16).ok())
let Some(v) =
std::str::from_utf8(hex).ok().and_then(|hex| u64::from_str_radix(hex, 16).ok())
else {
fatal!("-defsym: not a number: {}", util::display(s));
};
@@ -998,9 +974,7 @@ fn parse_defsym_value(s: &[u8]) -> DefsymValue {
}
if !s.is_empty() && s.iter().all(u8::is_ascii_digit) {
let s = std::str::from_utf8(s).unwrap();
let value = s
.parse()
.unwrap_or_else(|_| fatal!("-defsym: not a number: {s}"));
let value = s.parse().unwrap_or_else(|_| fatal!("-defsym: not a number: {s}"));
return DefsymValue::Addr(value);
}
DefsymValue::Symbol(s.to_vec())
@@ -1106,21 +1080,12 @@ impl<'a> ArgCursor<'a> {
("lto-obj-path", "obj-path="),
("opt-remarks-filename", "opt-remarks-filename="),
("opt-remarks-format", "opt-remarks-format="),
(
"opt-remarks-hotness-threshold",
"opt-remarks-hotness-threshold=",
),
("opt-remarks-hotness-threshold", "opt-remarks-hotness-threshold="),
("opt-remarks-passes", "opt-remarks-passes="),
(
"lto-pseudo-probe-for-profiling",
"pseudo-probe-for-profiling=",
),
("lto-pseudo-probe-for-profiling", "pseudo-probe-for-profiling="),
("lto-sample-profile", "sample-profile="),
("thinlto-index-only", "thinlto-index-only="),
(
"thinlto-object-suffix-replace",
"thinlto-object-suffix-replace=",
),
("thinlto-object-suffix-replace", "thinlto-object-suffix-replace="),
("thinlto-prefix-replace", "thinlto-prefix-replace="),
("thinlto-cache-dir", "cache-dir="),
("thinlto-cache-policy", "cache-policy="),
@@ -1257,10 +1222,7 @@ pub fn parse_args(target: &TargetTraits, raw_cmdline: &[Cow<'_, OsStr>]) -> Pars
// we write addends to relocated places.
a.apply_dynamic_relocs = !matches!(target.family, arch::Family::Sparc64 | arch::Family::RiscV);
let mut cursor = ArgCursor {
args: raw_cmdline,
index: 1,
};
let mut cursor = ArgCursor { args: raw_cmdline, index: 1 };
let mut arg = "";
let mut raw_arg = OsStr::new("");
@@ -1313,11 +1275,7 @@ pub fn parse_args(target: &TargetTraits, raw_cmdline: &[Cow<'_, OsStr>]) -> Pars
}
if cursor.read_flag("help") {
out!(
"Usage: {} [options] file...\n{}",
raw_cmdline[0].to_string_lossy(),
HELP
);
out!("Usage: {} [options] file...\n{}", raw_cmdline[0].to_string_lossy(), HELP);
std::process::exit(0);
}
@@ -1365,10 +1323,7 @@ pub fn parse_args(target: &TargetTraits, raw_cmdline: &[Cow<'_, OsStr>]) -> Pars
} else if cursor.read_flag("Bno-symbolic") {
a.bsymbolic = BsymbolicKind::None;
} else if read_arg!("exclude-libs", true) {
for lib in raw_arg
.as_encoded_bytes()
.split(|b| matches!(b, b',' | b':'))
{
for lib in raw_arg.as_encoded_bytes().split(|b| matches!(b, b',' | b':')) {
a.exclude_libs.insert(lib.to_vec());
}
} else if cursor.read_flag("q") || cursor.read_flag("emit-relocs") {
@@ -1402,10 +1357,8 @@ pub fn parse_args(target: &TargetTraits, raw_cmdline: &[Cow<'_, OsStr>]) -> Pars
} else if read_arg!("dependency-file", true) {
a.dependency_file = PathBuf::from(raw_arg);
} else if read_arg!("defsym", true) {
let Some((name, value)) = raw_arg
.as_encoded_bytes()
.split_once_str(b"=")
.filter(|(_, v)| !v.is_empty())
let Some((name, value)) =
raw_arg.as_encoded_bytes().split_once_str(b"=").filter(|(_, v)| !v.is_empty())
else {
fatal!("-defsym: syntax error: {}", raw_arg.to_string_lossy());
};
@@ -1445,10 +1398,7 @@ pub fn parse_args(target: &TargetTraits, raw_cmdline: &[Cow<'_, OsStr>]) -> Pars
a.sysroot = PathBuf::from(raw_arg);
} else if read_arg!("unique", true) {
if !unique.add(raw_arg.as_encoded_bytes(), 1) {
fatal!(
"-unique: invalid glob pattern: {}",
raw_arg.to_string_lossy()
);
fatal!("-unique: invalid glob pattern: {}", raw_arg.to_string_lossy());
}
} else if read_arg!("unresolved-symbols") {
match arg {
@@ -1460,10 +1410,7 @@ pub fn parse_args(target: &TargetTraits, raw_cmdline: &[Cow<'_, OsStr>]) -> Pars
a.undefined.push(raw_arg.as_encoded_bytes().to_vec());
} else if read_arg!("undefined-glob", true) {
if !undefined_glob.add(raw_arg.as_encoded_bytes(), 0) {
fatal!(
"--undefined-glob: invalid pattern: {}",
raw_arg.to_string_lossy()
);
fatal!("--undefined-glob: invalid pattern: {}", raw_arg.to_string_lossy());
}
} else if read_arg!("require-defined", true) {
a.require_defined.push(raw_arg.as_encoded_bytes().to_vec());
@@ -1632,10 +1579,7 @@ pub fn parse_args(target: &TargetTraits, raw_cmdline: &[Cow<'_, OsStr>]) -> Pars
.unwrap_or_else(|_| fatal!("--section-align: invalid number: {}", value.as_bstr()));
let value = parse_number("section-align", value);
if value <= 0 || !(value as u64).is_power_of_two() {
fatal!(
"--section-align={}: value must be a power of 2",
arg.as_bstr()
);
fatal!("--section-align={}: value must be a power of 2", arg.as_bstr());
}
a.section_align.insert(name.to_vec(), value as u64);
} else if read_arg!("section-start", true) {
@@ -1646,19 +1590,15 @@ pub fn parse_args(target: &TargetTraits, raw_cmdline: &[Cow<'_, OsStr>]) -> Pars
};
let value = std::str::from_utf8(value)
.unwrap_or_else(|_| fatal!("--section-start: invalid number: {}", value.as_bstr()));
a.section_start
.insert(name.to_vec(), parse_hex("section-start", value));
a.section_start.insert(name.to_vec(), parse_hex("section-start", value));
} else if read_arg!("section-order", true) {
a.section_order = parse_section_order(raw_arg.as_encoded_bytes());
} else if read_arg!("Tbss") {
a.section_start
.insert(b".bss".to_vec(), parse_hex("Tbss", arg));
a.section_start.insert(b".bss".to_vec(), parse_hex("Tbss", arg));
} else if read_arg!("Tdata") {
a.section_start
.insert(b".data".to_vec(), parse_hex("Tdata", arg));
a.section_start.insert(b".data".to_vec(), parse_hex("Tdata", arg));
} else if read_arg!("Ttext") {
a.section_start
.insert(b".text".to_vec(), parse_hex("Ttext", arg));
a.section_start.insert(b".text".to_vec(), parse_hex("Ttext", arg));
} else if read_arg!("Ttext-segment") {
a.ttext_segment = Some(parse_number("Ttext-segment", arg) as u64);
} else if cursor.read_flag("repro") {
@@ -1680,10 +1620,9 @@ pub fn parse_args(target: &TargetTraits, raw_cmdline: &[Cow<'_, OsStr>]) -> Pars
if !a.page_size.is_power_of_two() {
fatal!("-z max-page-size {arg}: value must be a power of 2");
}
} else if let Some(value) = cursor.read_z_switch(
"start-stop-visibility=protected",
"start-stop-visibility=hidden",
) {
} else if let Some(value) =
cursor.read_z_switch("start-stop-visibility=protected", "start-stop-visibility=hidden")
{
a.z_start_stop_visibility_protected = value;
} else if cursor.read_z_flag("noexecstack") {
a.z_execstack = false;
@@ -1780,8 +1719,7 @@ pub fn parse_args(target: &TargetTraits, raw_cmdline: &[Cow<'_, OsStr>]) -> Pars
} else if cursor.read_flag("no-print-gc-sections") {
a.print_gc_sections = None;
} else if read_arg!("discard-section", true) {
a.discard_section
.insert(raw_arg.as_encoded_bytes().to_vec());
a.discard_section.insert(raw_arg.as_encoded_bytes().to_vec());
} else if read_arg!("no-discard-section", true) {
a.discard_section.remove(raw_arg.as_encoded_bytes());
} else if read_arg!("icf") {
@@ -1933,28 +1871,21 @@ pub fn parse_args(target: &TargetTraits, raw_cmdline: &[Cow<'_, OsStr>]) -> Pars
a.version_scripts.push(PathBuf::from(raw_arg));
} else if read_arg!("dynamic-list", true) {
a.bsymbolic = BsymbolicKind::All;
a.dynamic_list
.push(DynamicListSource::File(PathBuf::from(raw_arg)));
a.dynamic_list.push(DynamicListSource::File(PathBuf::from(raw_arg)));
} else if read_arg!("dynamic-list-data") {
a.dynamic_list_data = true;
} else if read_arg!("export-dynamic-symbol", true) {
a.dynamic_list.push(DynamicListSource::Pattern(
raw_arg.as_encoded_bytes().to_vec(),
));
a.dynamic_list.push(DynamicListSource::Pattern(raw_arg.as_encoded_bytes().to_vec()));
} else if read_arg!("export-dynamic-symbol-list", true) {
a.dynamic_list
.push(DynamicListSource::File(PathBuf::from(raw_arg)));
a.dynamic_list.push(DynamicListSource::File(PathBuf::from(raw_arg)));
} else if let Some(value) = cursor.read_switch("as-needed", "no-as-needed") {
rctx.as_needed = value;
} else if let Some(value) = cursor.read_switch("whole-archive", "no-whole-archive") {
rctx.whole_archive = value;
} else if read_arg!("l", true) || read_arg!("library", true) {
if visited_libs.insert(raw_arg) {
let mut job = ReaderJob {
rctx: rctx.clone(),
name: PathBuf::from(raw_arg),
is_lib: true,
};
let mut job =
ReaderJob { rctx: rctx.clone(), name: PathBuf::from(raw_arg), is_lib: true };
job.rctx.pos = vec![jobs.len() as u32];
jobs.push(job);
}
@@ -1969,9 +1900,7 @@ pub fn parse_args(target: &TargetTraits, raw_cmdline: &[Cow<'_, OsStr>]) -> Pars
} else if cursor.read_flag("push-state") {
rctx_stack.push(rctx.clone());
} else if cursor.read_flag("pop-state") {
rctx = rctx_stack
.pop()
.unwrap_or_else(|| fatal!("no state pushed before popping"));
rctx = rctx_stack.pop().unwrap_or_else(|| fatal!("no state pushed before popping"));
} else if cursor.text().starts_with("-z") && cursor.text().len() > 2 {
warn!("unknown command line option: {}", cursor.text());
cursor.index += 1;
@@ -1985,19 +1914,13 @@ pub fn parse_args(target: &TargetTraits, raw_cmdline: &[Cow<'_, OsStr>]) -> Pars
fatal!("unknown command line option: -dynamic; -dynamic is a macOS linker's option. mold does not support macOS."
);
} else {
fatal!(
"unknown command line option: {}",
cursor.current().to_string_lossy()
);
fatal!("unknown command line option: {}", cursor.current().to_string_lossy());
}
}
if !a.chroot.as_os_str().is_empty() && !a.dependency_file.as_os_str().is_empty() {
a.dependency_file = a.chroot.join(
a.dependency_file
.strip_prefix("/")
.unwrap_or(&a.dependency_file),
);
a.dependency_file =
a.chroot.join(a.dependency_file.strip_prefix("/").unwrap_or(&a.dependency_file));
}
a.map = map_path.map(|mut path| {
@@ -2018,9 +1941,7 @@ pub fn parse_args(target: &TargetTraits, raw_cmdline: &[Cow<'_, OsStr>]) -> Pars
if !a.sysroot.as_os_str().is_empty() {
for path in &mut a.library_paths {
let bytes = path.as_os_str().as_encoded_bytes();
if let Some(rest) = bytes
.strip_prefix(b"=")
.or_else(|| bytes.strip_prefix(b"$SYSROOT"))
if let Some(rest) = bytes.strip_prefix(b"=").or_else(|| bytes.strip_prefix(b"$SYSROOT"))
{
let mut full = a.sysroot.as_os_str().to_os_string();
full.push(util::os_str(rest));
@@ -2109,10 +2030,7 @@ pub fn parse_args(target: &TargetTraits, raw_cmdline: &[Cow<'_, OsStr>]) -> Pars
// zero, and actual addends are written to relocated places. So we need
// to handle it as an exception.
if (!target.is_rela || target.family == arch::Family::Sh4) && !a.apply_dynamic_relocs {
fatal!(
"--no-apply-dynamic-relocs may not be used on {}",
target.name
);
fatal!("--no-apply-dynamic-relocs may not be used on {}", target.name);
}
if target.family == arch::Family::Sparc64 && a.apply_dynamic_relocs {
fatal!("--apply-dynamic-relocs may not be used on SPARC64");
@@ -2134,11 +2052,7 @@ pub fn parse_args(target: &TargetTraits, raw_cmdline: &[Cow<'_, OsStr>]) -> Pars
if a.default_symver {
let ver = if a.soname.is_empty() {
a.output
.file_name()
.unwrap_or_default()
.as_encoded_bytes()
.to_vec()
a.output.file_name().unwrap_or_default().as_encoded_bytes().to_vec()
} else {
a.soname.as_encoded_bytes().to_vec()
};
@@ -2236,10 +2150,7 @@ mod tests {
.into_iter()
.map(|s| Cow::Borrowed(OsStr::new(s)))
.collect();
let mut cursor = ArgCursor {
args: &args,
index: 1,
};
let mut cursor = ArgCursor { args: &args, index: 1 };
assert_eq!(cursor.read_arg("output"), None);
assert_eq!(cursor.index, 1);
assert_eq!(cursor.read_arg("o"), Some(OsStr::new("utput")));
@@ -2247,18 +2158,9 @@ mod tests {
assert!(!cursor.read_z_flag("lazy"));
assert!(cursor.read_z_flag("now"));
assert_eq!(cursor.read_z_arg("max-page-size"), Some("4096"));
assert_eq!(
cursor.read_switch("gc-sections", "no-gc-sections"),
Some(true)
);
assert_eq!(
cursor.read_switch("gc-sections", "no-gc-sections"),
Some(false)
);
assert_eq!(
cursor.read_lto_option(),
Some(b"cs-profile-path=data".to_vec())
);
assert_eq!(cursor.read_switch("gc-sections", "no-gc-sections"), Some(true));
assert_eq!(cursor.read_switch("gc-sections", "no-gc-sections"), Some(false));
assert_eq!(cursor.read_lto_option(), Some(b"cs-profile-path=data".to_vec()));
assert_eq!(cursor.read_lto_option(), Some(b"save-temps".to_vec()));
assert_eq!(cursor.read_lto_option(), Some(b"O2".to_vec()));
assert_eq!(cursor.index, args.len());
@@ -2267,27 +2169,13 @@ mod tests {
#[cfg(unix)]
#[test]
fn cursor_borrows_non_utf8_separate_and_attached_values() {
let args: Vec<_> = [
b"mold".as_slice(),
b"-o",
b"out-\xff",
b"--plugin-opt=arg-\xfe",
]
.into_iter()
.map(|s| Cow::Borrowed(util::os_str(s)))
.collect();
let mut cursor = ArgCursor {
args: &args,
index: 1,
};
assert_eq!(
cursor.read_arg("o").unwrap().as_encoded_bytes(),
b"out-\xff"
);
assert_eq!(
cursor.read_arg("plugin-opt").unwrap().as_encoded_bytes(),
b"arg-\xfe"
);
let args: Vec<_> = [b"mold".as_slice(), b"-o", b"out-\xff", b"--plugin-opt=arg-\xfe"]
.into_iter()
.map(|s| Cow::Borrowed(util::os_str(s)))
.collect();
let mut cursor = ArgCursor { args: &args, index: 1 };
assert_eq!(cursor.read_arg("o").unwrap().as_encoded_bytes(), b"out-\xff");
assert_eq!(cursor.read_arg("plugin-opt").unwrap().as_encoded_bytes(), b"arg-\xfe");
assert_eq!(cursor.index, args.len());
}
@@ -2309,17 +2197,7 @@ mod tests {
] {
assert_eq!(parse_number("test", text), expected, "{text:?}");
}
for text in [
"",
" ",
"+",
"08",
"0x",
"0x+1",
"1 ",
"1x",
"18446744073709551616",
] {
for text in ["", " ", "+", "08", "0x", "0x+1", "1 ", "1x", "18446744073709551616"] {
assert_eq!(parse_c_number(text), None, "{text:?}");
}
}
+6 -24
View File
@@ -277,11 +277,7 @@ impl<E: Arch> Context<E> {
fini: symbols.intern(crate::util::leak_bytes(std::mem::take(&mut args.fini))),
..SyntheticSymbols::default()
};
let timers = if args.perf {
Timers::new()
} else {
Timers::disabled()
};
let timers = if args.perf { Timers::new() } else { Timers::disabled() };
Context {
reldyn: RelDynSection::<E>::new(&args),
@@ -364,18 +360,13 @@ impl<E: Arch> Context<E> {
/// 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>> {
self.symbol_bins
.get_or_init(|| WorkerLocal::new(Bins::new))
.get()
self.symbol_bins.get_or_init(|| WorkerLocal::new(Bins::new)).get()
}
/// Takes all keys recorded since the previous gather, leaving empty bins
/// in place for an LTO output file to use before the second gather.
pub(crate) fn take_symbol_bins(&mut self) -> Vec<Bins<SymbolSlot>> {
self.symbol_bins
.get_mut()
.map(|bins| bins.take().collect())
.unwrap_or_default()
self.symbol_bins.get_mut().map(|bins| bins.take().collect()).unwrap_or_default()
}
pub fn get_symbol(&mut self, name: &[u8]) -> SymbolId {
@@ -438,10 +429,7 @@ impl<E: Arch> Context<E> {
/// The address of a PPC64 ELFv1 function descriptor.
pub fn opd_addr(&self, idx: u32) -> u64 {
let opd = self
.ppc64_opd
.as_ref()
.expect("PPC64 ELFv1 has an .opd section");
let opd = self.ppc64_opd.as_ref().expect("PPC64 ELFv1 has an .opd section");
opd.hdr.shdr.sh_addr.get() + idx as u64 * crate::chunks::opd::ENTRY_SIZE
}
@@ -476,18 +464,12 @@ impl<E: Arch> Context<E> {
/// Finds the first chunk of a section type.
pub fn find_chunk_by_type(&self, sh_type: u32) -> Option<ChunkId> {
self.chunks
.iter()
.copied()
.find(|&c| self.chunk_header(c).shdr.sh_type.get() == sh_type)
self.chunks.iter().copied().find(|&c| self.chunk_header(c).shdr.sh_type.get() == sh_type)
}
/// Finds the first chunk with a name.
pub fn find_chunk_by_name(&self, name: &[u8]) -> Option<ChunkId> {
self.chunks
.iter()
.copied()
.find(|&c| self.chunk_header(c).name == name)
self.chunks.iter().copied().find(|&c| self.chunk_header(c).name == name)
}
/// Starts a `--perf` timer for a pass.
+34 -71
View File
@@ -63,21 +63,13 @@ pub fn main(
}
fn target_traits<E: Arch>() -> TargetTraits {
TargetTraits {
name: E::NAME,
is_rela: E::IS_RELA,
family: E::FAMILY,
page_size: E::PAGE_SIZE,
}
TargetTraits { name: E::NAME, is_rela: E::IS_RELA, family: E::FAMILY, page_size: E::PAGE_SIZE }
}
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(|| {
std::thread::available_parallelism()
.map_or(1, |n| n.get())
.min(32)
})
args.thread_count
.unwrap_or_else(|| std::thread::available_parallelism().map_or(1, |n| n.get()).min(32))
}
/// Links for the target `E`, or reports the target the inputs are actually
@@ -153,12 +145,11 @@ pub fn link<E: Arch>(cmdline: Arc<[Cow<'static, OsStr>]>) -> Result<i32, &'stati
ctx.dynamic_list_patterns.extend(patterns);
}
cmdline::DynamicListSource::Pattern(pattern) => {
ctx.dynamic_list_patterns
.push(crate::linker_script::DynamicPattern {
pattern: crate::util::leak_bytes(pattern),
source: std::path::Path::new("<command line>"),
is_cpp: false,
});
ctx.dynamic_list_patterns.push(crate::linker_script::DynamicPattern {
pattern: crate::util::leak_bytes(pattern),
source: std::path::Path::new("<command line>"),
is_cpp: false,
});
}
}
}
@@ -428,10 +419,7 @@ pub fn link<E: Arch>(cmdline: Arc<[Cow<'static, OsStr>]>) -> Result<i32, &'stati
let gdb_table_workers = (threads * 3 / 8).clamp(1, 12);
let mut gdb_table_job = if ctx.gdb_index.is_some() && ctx.gnu_debuglink.is_none() {
let timer = t_all.handle();
let mut data = ctx
.gdb_index_data
.take()
.expect("missing .gdb_index input data");
let mut data = ctx.gdb_index_data.take().expect("missing .gdb_index input data");
crate::gdb_index::prepare_tables(&ctx, &mut data);
Some(Background::spawn(".gdb_index table", move || {
let timer = timer.child("build_gdb_index_tables");
@@ -636,11 +624,7 @@ pub fn link<E: Arch>(cmdline: Arc<[Cow<'static, OsStr>]>) -> Result<i32, &'stati
fn file_range<E: Arch>(ctx: &Context<E>, id: ChunkId) -> Range<u64> {
let hdr = ctx.chunk_header(id);
let size = if hdr.shdr.sh_type.get() == SHT_NOBITS {
0
} else {
hdr.shdr.sh_size.get()
};
let size = if hdr.shdr.sh_type.get() == SHT_NOBITS { 0 } else { hdr.shdr.sh_size.get() };
let offset = hdr.shdr.sh_offset.get();
offset..offset + size
}
@@ -665,10 +649,8 @@ pub fn copy_chunks<E: Arch>(ctx: &Context<E>, buf: &mut [u8]) {
let mut last: Vec<Task> = Vec::new();
let is_reloc_sec = |id: ChunkId| {
let ty = ctx.chunk_header(id).shdr.sh_type.get();
matches!(
id,
ChunkId::Reloc(_) | ChunkId::EhFrameReloc | ChunkId::SFrameReloc
) || ty == SHT_REL
matches!(id, ChunkId::Reloc(_) | ChunkId::EhFrameReloc | ChunkId::SFrameReloc)
|| ty == SHT_REL
|| (E::FAMILY == arch::Family::Sh4 && ty == SHT_RELA)
};
@@ -688,23 +670,13 @@ pub fn copy_chunks<E: Arch>(ctx: &Context<E>, buf: &mut [u8]) {
}
ChunkId::Reloc(i) => {
let osec = ctx.reloc_sections[i as usize].output_section;
last.push(Task {
chunk: id,
extra: [Some(ChunkId::Output(osec)), None],
});
last.push(Task { chunk: id, extra: [Some(ChunkId::Output(osec)), None] });
}
ChunkId::EhFrameReloc => last.push(Task {
chunk: id,
extra: [Some(ChunkId::EhFrame), None],
}),
_ if is_reloc_sec(id) => last.push(Task {
chunk: id,
extra: [None; 2],
}),
_ => first.push(Task {
chunk: id,
extra: [None; 2],
}),
ChunkId::EhFrameReloc => {
last.push(Task { chunk: id, extra: [Some(ChunkId::EhFrame), None] })
}
_ if is_reloc_sec(id) => last.push(Task { chunk: id, extra: [None; 2] }),
_ => first.push(Task { chunk: id, extra: [None; 2] }),
}
}
@@ -728,12 +700,8 @@ pub fn copy_chunks<E: Arch>(ctx: &Context<E>, buf: &mut [u8]) {
passes::report_undef_errors(ctx);
// Zero-clear paddings between chunks
let mut ranges: Vec<Range<u64>> = ctx
.chunks
.iter()
.map(|&id| file_range(ctx, id))
.filter(|r| !r.is_empty())
.collect();
let mut ranges: Vec<Range<u64>> =
ctx.chunks.iter().map(|&id| file_range(ctx, id)).filter(|r| !r.is_empty()).collect();
ranges.sort_by_key(|r| r.start);
let mut pos = 0usize;
for r in ranges {
@@ -770,26 +738,21 @@ fn run_tasks<E: Arch>(
})
.collect();
work.into_par_iter()
.for_each(|(task, own, [extra1, extra2])| {
let name = ctx.chunk_header(task.chunk).name;
let name = if name.is_empty() {
bstr::BStr::new("(header)")
} else {
name
};
let _t = timer.child(name);
match task.chunk {
ChunkId::EhFrame => chunks::eh_frame::copy_buf(ctx, own, extra1),
ChunkId::Symtab => {
let strtab = extra1.unwrap();
chunks::symtab::copy_buf(ctx, own, strtab, extra2);
}
ChunkId::Reloc(i) => chunks::reloc::copy_buf(ctx, i, own, extra1),
ChunkId::EhFrameReloc => chunks::eh_frame_reloc::copy_buf(ctx, own, extra1),
id => chunks::copy_buf(ctx, id, own),
work.into_par_iter().for_each(|(task, own, [extra1, extra2])| {
let name = ctx.chunk_header(task.chunk).name;
let name = if name.is_empty() { bstr::BStr::new("(header)") } else { name };
let _t = timer.child(name);
match task.chunk {
ChunkId::EhFrame => chunks::eh_frame::copy_buf(ctx, own, extra1),
ChunkId::Symtab => {
let strtab = extra1.unwrap();
chunks::symtab::copy_buf(ctx, own, strtab, extra2);
}
});
ChunkId::Reloc(i) => chunks::reloc::copy_buf(ctx, i, own, extra1),
ChunkId::EhFrameReloc => chunks::eh_frame_reloc::copy_buf(ctx, own, extra1),
id => chunks::copy_buf(ctx, id, own),
}
});
// .eh_frame_hdr's header, whose table .eh_frame wrote.
if tasks.iter().any(|t| t.chunk == ChunkId::EhFrame) && ctx.eh_frame_hdr.is_some() {
+1 -5
View File
@@ -49,11 +49,7 @@ pub fn set_noinhibit_exec(on: bool) {
// Format each message before taking the lock so diagnostics from different
// threads cannot interleave.
fn emit(prefix_mono: &str, prefix_color: &str, msg: fmt::Arguments) {
let prefix = if COLOR.load(Ordering::Relaxed) {
prefix_color
} else {
prefix_mono
};
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());
+5 -15
View File
@@ -25,9 +25,8 @@ fn is_text_file(data: &[u8]) -> bool {
/// Whether an ELF relocatable object is really a GCC LTO object.
fn is_gcc_lto_obj<E: Layout>(data: &[u8], has_gcc_plugin: bool) -> bool {
let Some(ehdr) = data
.get(..std::mem::size_of::<ElfEhdr<E>>())
.map(record_from_bytes::<ElfEhdr<E>>)
let Some(ehdr) =
data.get(..std::mem::size_of::<ElfEhdr<E>>()).map(record_from_bytes::<ElfEhdr<E>>)
else {
return false;
};
@@ -76,9 +75,7 @@ fn is_gcc_lto_obj<E: Layout>(data: &[u8], has_gcc_plugin: bool) -> bool {
ehdr.e_shstrndx.get() as usize
};
let shstrtab_offset = if has_gcc_plugin {
shdrs
.get(shstrtab_idx)
.map(|shdr| shdr.sh_offset.get() as usize)
shdrs.get(shstrtab_idx).map(|shdr| shdr.sh_offset.get() as usize)
} else {
None
};
@@ -162,11 +159,7 @@ pub fn get_file_type(plugin: &std::path::Path, mf: &MappedFile) -> FileType {
(false, true) => is_gcc_lto_obj::<arch::M68k>(data, has_gcc_plugin),
(false, false) => is_gcc_lto_obj::<arch::Sparc64>(data, has_gcc_plugin),
};
return if is_lto {
FileType::GccLtoObj
} else {
FileType::ElfObj
};
return if is_lto { FileType::GccLtoObj } else { FileType::ElfObj };
}
if e_type == ET_DYN {
return FileType::ElfDso;
@@ -289,10 +282,7 @@ pub fn get_machine_type(
FileType::ElfObj | FileType::ElfDso | FileType::GccLtoObj => get_elf_target(mf.data()),
FileType::Ar | FileType::ThinAr => archive_file::read_archive_members(chroot, mf)
.find(|child| {
matches!(
get_file_type(plugin, child),
FileType::ElfObj | FileType::GccLtoObj
)
matches!(get_file_type(plugin, child), FileType::ElfObj | FileType::GccLtoObj)
})
.and_then(|child| get_elf_target(child.data())),
_ => None,
+4 -13
View File
@@ -21,10 +21,7 @@ fn should_keep<E: Arch>(file: &ObjectFile<E>, isec: &InputSection<E>) -> bool {
return true;
}
flags & SHF_GNU_RETAIN != 0
|| matches!(
ty,
SHT_NOTE | SHT_INIT_ARRAY | SHT_FINI_ARRAY | SHT_PREINIT_ARRAY
)
|| matches!(ty, SHT_NOTE | SHT_INIT_ARRAY | SHT_FINI_ARRAY | SHT_PREINIT_ARRAY)
|| name.starts_with(b".ctors")
|| name.starts_with(b".dtors")
|| name.starts_with(b".init")
@@ -130,8 +127,7 @@ fn collect_root_set<'a, E: Arch>(ctx: &'a Context<E>) -> Vec<&'a InputSection<E>
#[inline]
fn start_stop_name(name: &[u8]) -> Option<&[u8]> {
name.strip_prefix(b"__start_")
.or_else(|| name.strip_prefix(b"__stop_"))
name.strip_prefix(b"__start_").or_else(|| name.strip_prefix(b"__stop_"))
}
fn visit_section<'scope, E: Arch>(
@@ -254,9 +250,7 @@ fn mark<'a, E: Arch>(
let _t = ctx.timer("mark");
rayon::scope(|scope| {
roots
.par_chunks(GC_BATCH)
.for_each(|batch| visit_batch(ctx, batch, map, scope));
roots.par_chunks(GC_BATCH).for_each(|batch| visit_batch(ctx, batch, map, scope));
});
}
@@ -273,10 +267,7 @@ fn sweep<E: Arch>(ctx: &Context<E>) {
if isec.is_alive() && !isec.is_visited() {
file.kill_section(isec.shndx as usize);
if report {
removed.push(SectionRef {
file: isec.file,
shndx: isec.shndx,
});
removed.push(SectionRef { file: isec.file, shndx: isec.shndx });
}
}
}
+66 -144
View File
@@ -94,10 +94,7 @@ struct NameType {
impl NameType {
fn new(hash: u64, kind: u8, name: &'static [u8]) -> NameType {
NameType {
hash_and_type: (hash << 8) | kind as u64,
name: name.as_ptr() as usize,
}
NameType { hash_and_type: (hash << 8) | kind as u64, name: name.as_ptr() as usize }
}
fn hash(self) -> u64 {
@@ -141,9 +138,7 @@ union NameRecord {
impl NameRecord {
fn new(hash: u64, kind: u8, name: &'static [u8]) -> NameRecord {
NameRecord {
nametype: NameType::new(hash, kind, name),
}
NameRecord { nametype: NameType::new(hash, kind, name) }
}
fn nametype(self) -> NameType {
@@ -251,11 +246,7 @@ struct Reader<'a, E: Arch> {
impl<'a, E: Arch> Reader<'a, E> {
fn new(data: &'a [u8], pos: usize) -> Reader<'a, E> {
Reader {
data,
pos,
marker: std::marker::PhantomData,
}
Reader { data, pos, marker: std::marker::PhantomData }
}
fn take(&mut self, n: usize) -> &'a [u8] {
@@ -567,10 +558,8 @@ fn read_address_ranges<E: Arch>(secs: &RangeSections, cu: &Compunit) -> Vec<(u64
let Some(list_base) = rnglists_base else {
fatal!("--gdb-index: missing DW_AT_rnglists_base");
};
let mut entry = Reader::<E>::new(
secs.rnglists,
(list_base + val * hdr.offset_size as u64) as usize,
);
let mut entry =
Reader::<E>::new(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(secs.rnglists, (list_base + offset) as usize);
return read_rnglist::<E>(&mut r, addrx, base);
@@ -661,22 +650,12 @@ pub fn prepare_inputs<E: Arch>(ctx: &mut Context<E>) -> Vec<GdbInputFile> {
if !isec.is_alive() {
continue;
}
isec.uncompress(
&display_file(&filename, archive_name),
section_name,
input_size,
);
debug_info.push(DebugInfoInput {
shndx,
contents: isec.contents(),
});
isec.uncompress(&display_file(&filename, archive_name), section_name, input_size);
debug_info.push(DebugInfoInput { shndx, contents: isec.contents() });
}
let mut pubnames = Vec::new();
for shndx in [file.debug_pubnames, file.debug_pubtypes]
.into_iter()
.flatten()
{
for shndx in [file.debug_pubnames, file.debug_pubtypes].into_iter().flatten() {
let Some(section_name) = file.section(shndx as usize).map(|isec| isec.name(file))
else {
continue;
@@ -685,11 +664,7 @@ 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(
&display_file(&filename, archive_name),
section_name,
input_size,
);
isec.uncompress(&display_file(&filename, archive_name), section_name, input_size);
let isec = file.section_at(shndx);
let mut relocations = Vec::new();
@@ -706,23 +681,14 @@ pub fn prepare_inputs<E: Arch>(ctx: &mut Context<E>) -> Vec<GdbInputFile> {
});
}
}
pubnames.push(PubnamesInput {
contents: isec.contents(),
relocations,
});
pubnames.push(PubnamesInput { contents: isec.contents(), relocations });
}
if debug_info.is_empty() && pubnames.is_empty() {
return None;
}
Some(GdbInputFile {
file: file_id,
filename,
archive_name,
debug_info,
pubnames,
})
Some(GdbInputFile { file: file_id, filename, archive_name, debug_info, pubnames })
})
.collect()
}
@@ -780,16 +746,10 @@ fn pubnames_unit<'a>(
// Units are appended in input section and contribution offset order, so both
// the CU and TU vectors are sorted by this key.
if let Ok(i) = units
.cus
.binary_search_by_key(&key, |cu| (cu.shndx, cu.offset))
{
if let Ok(i) = units.cus.binary_search_by_key(&key, |cu| (cu.shndx, cu.offset)) {
return Some(&mut units.cus[i].names);
}
if let Ok(i) = units
.tus
.binary_search_by_key(&key, |tu| (tu.shndx, tu.offset))
{
if let Ok(i) = units.tus.binary_search_by_key(&key, |tu| (tu.shndx, tu.offset)) {
return Some(&mut units.tus[i].names);
}
None
@@ -832,11 +792,7 @@ fn read_pubnames<E: Arch>(file: &GdbInputFile, units: &mut FileUnits) {
}
let kind = r.u8();
let name = r.cstr();
names.push(NameRecord::new(
xxhash_rust::xxh3::xxh3_64(name),
kind,
name,
));
names.push(NameRecord::new(xxhash_rust::xxh3::xxh3_64(name), kind, name));
}
pos = end;
}
@@ -894,10 +850,7 @@ fn initialize_gdb_name(name: *const u8) -> (u32, NameEntry) {
if c.is_ascii_uppercase() {
c = c.to_ascii_lowercase();
}
hash = hash
.wrapping_mul(67)
.wrapping_add(c as u32)
.wrapping_sub(113);
hash = hash.wrapping_mul(67).wrapping_add(c as u32).wrapping_sub(113);
size += 1;
}
(
@@ -1047,20 +1000,17 @@ pub fn read_inputs<E: Arch>(timer: Timer, files: Vec<GdbInputFile>) -> GdbIndexD
offset
})
.collect();
entries
.par_chunks(chunk_size)
.zip(chunk_offsets)
.for_each(|(chunk, mut size)| {
for &entry_ref in chunk {
// SAFETY: entries contains each map entry exactly once, so
// parallel chunks update disjoint values.
let entry = unsafe { &mut *entry_ref.value_mut_ptr(&names) };
entry.type_vector_offset = size.type_bytes;
entry.name_offset = pool_size.type_bytes + size.name_bytes;
size.type_bytes += entry.count.load(Ordering::Relaxed) * 4 + 4;
size.name_bytes += entry_ref.key_len(&names) as u32 + 1;
}
});
entries.par_chunks(chunk_size).zip(chunk_offsets).for_each(|(chunk, mut size)| {
for &entry_ref in chunk {
// SAFETY: entries contains each map entry exactly once, so
// parallel chunks update disjoint values.
let entry = unsafe { &mut *entry_ref.value_mut_ptr(&names) };
entry.type_vector_offset = size.type_bytes;
entry.name_offset = pool_size.type_bytes + size.name_bytes;
size.type_bytes += entry.count.load(Ordering::Relaxed) * 4 + 4;
size.name_bytes += entry_ref.key_len(&names) as u32 + 1;
}
});
let ht_size = (entries.len() as u32 * 5 / 4 + 1).next_power_of_two();
GdbIndexData {
cus,
@@ -1111,15 +1061,12 @@ fn limited_parallel_for_mut_init<T: Send, S: Send>(
let workers = workers.max(1).min(values.len());
let chunk_size = values.len().div_ceil(workers);
values
.par_chunks_mut(chunk_size)
.enumerate()
.for_each(|(chunk_idx, chunk)| {
let mut state = init();
for (i, value) in chunk.iter_mut().enumerate() {
op(&mut state, chunk_idx * chunk_size + i, value);
}
});
values.par_chunks_mut(chunk_size).enumerate().for_each(|(chunk_idx, chunk)| {
let mut state = init();
for (i, value) in chunk.iter_mut().enumerate() {
op(&mut state, chunk_idx * chunk_size + i, value);
}
});
}
/// Build the name lookup table and the constant pool for .gdb_index. They
@@ -1197,39 +1144,34 @@ pub fn build_tables(timer: Timer, mut data: GdbIndexData, workers: usize) -> Gdb
// Prefix each type vector with its length and sort it for deterministic
// output. Store the NUL-terminated name at its assigned string-pool offset.
limited_parallel_for_mut_init(
&mut data.entries,
workers,
Vec::new,
|scratch, _, entry_ref| {
let entry_ref = *entry_ref;
let entry = entry_ref.value(names);
let count = entry.count.load(Ordering::Relaxed);
let words = (pool_addr as *mut u32).wrapping_add(entry.type_vector_offset as usize / 4);
// SAFETY: every occurrence filled its distinct value slot above;
// writing the prefix completes this vector before a slice is made.
unsafe { words.write(count) };
let values = unsafe { std::slice::from_raw_parts_mut(words.add(1), count as usize) };
if values.len() < 256 {
values.sort_unstable();
} else {
radix_sort(values, scratch);
}
// SAFETY: the prefix and all count values are now initialized.
let words = unsafe { std::slice::from_raw_parts_mut(words, count as usize + 1) };
for word in &mut *words {
*word = word.to_le();
}
let key = entry_ref.key(names);
let name = (pool_addr as *mut u8).wrapping_add(entry.name_offset as usize);
// SAFETY: prefix-scan offsets assign this entry a distinct
// key.len()+1 byte range in the name pool.
unsafe {
name.copy_from_nonoverlapping(key.as_ptr(), key.len());
name.add(key.len()).write(0);
}
},
);
limited_parallel_for_mut_init(&mut data.entries, workers, Vec::new, |scratch, _, entry_ref| {
let entry_ref = *entry_ref;
let entry = entry_ref.value(names);
let count = entry.count.load(Ordering::Relaxed);
let words = (pool_addr as *mut u32).wrapping_add(entry.type_vector_offset as usize / 4);
// SAFETY: every occurrence filled its distinct value slot above;
// writing the prefix completes this vector before a slice is made.
unsafe { words.write(count) };
let values = unsafe { std::slice::from_raw_parts_mut(words.add(1), count as usize) };
if values.len() < 256 {
values.sort_unstable();
} else {
radix_sort(values, scratch);
}
// SAFETY: the prefix and all count values are now initialized.
let words = unsafe { std::slice::from_raw_parts_mut(words, count as usize + 1) };
for word in &mut *words {
*word = word.to_le();
}
let key = entry_ref.key(names);
let name = (pool_addr as *mut u8).wrapping_add(entry.name_offset as usize);
// SAFETY: prefix-scan offsets assign this entry a distinct
// key.len()+1 byte range in the name pool.
unsafe {
name.copy_from_nonoverlapping(key.as_ptr(), key.len());
name.add(key.len()).write(0);
}
});
// Box<[u32]> rounds the byte allocation up to a whole word; initialize only
// those padding bytes, which are not part of the serialized tables.
@@ -1269,13 +1211,8 @@ pub fn build_tables_now<E: Arch>(ctx: &mut Context<E>) {
ctx.gdb_index_data = Some(build_tables(timer, data, rayon::current_num_threads()));
}
const RANGE_SECTION_NAMES: [&[u8]; 5] = [
b".debug_info",
b".debug_abbrev",
b".debug_ranges",
b".debug_addr",
b".debug_rnglists",
];
const RANGE_SECTION_NAMES: [&[u8]; 5] =
[b".debug_info", b".debug_abbrev", b".debug_ranges", b".debug_addr", b".debug_rnglists"];
/// Whether GDB index construction needs a section's relocated contents.
pub(crate) fn needs_section_contents(name: &[u8]) -> bool {
@@ -1291,10 +1228,7 @@ fn section_contents<'a, E: Arch>(ctx: &'a Context<E>, buf: &'a [u8], name: &[u8]
continue;
}
if let ChunkId::Compressed(i) = id {
return ctx.compressed_sections[i as usize]
.uncompressed_data
.as_deref()
.unwrap_or(&[]);
return ctx.compressed_sections[i as usize].uncompressed_data.as_deref().unwrap_or(&[]);
}
return &buf[hdr.shdr.sh_offset.get() as usize
..(hdr.shdr.sh_offset.get() + hdr.shdr.sh_size.get()) as usize];
@@ -1326,13 +1260,7 @@ pub fn write<E: Arch>(ctx: &mut Context<E>, output: &mut OutputFile) {
let buf = output.buf();
let [info, abbrev, ranges, addr, rnglists] =
RANGE_SECTION_NAMES.map(|name| section_contents(ctx, buf, name));
let secs = RangeSections {
info,
abbrev,
ranges,
addr,
rnglists,
};
let secs = RangeSections { info, abbrev, ranges, addr, rnglists };
data.cus.par_iter_mut().for_each(|cu| {
cu.ranges = read_address_ranges::<E>(&secs, cu);
cu.ranges.retain(|&(start, end)| start != 0 && start != end);
@@ -1406,10 +1334,7 @@ pub fn write<E: Arch>(ctx: &mut Context<E>, output: &mut OutputFile) {
data.cus
.par_iter()
.enumerate()
.zip(split_at_offsets(
&mut buf[ranges_offset..symtab_offset],
&range_offsets,
))
.zip(split_at_offsets(&mut buf[ranges_offset..symtab_offset], &range_offsets))
.for_each(|((i, cu), buf)| {
for (range, entry) in cu.ranges.iter().zip(buf.chunks_exact_mut(20)) {
entry[..8].copy_from_slice(&range.0.to_le_bytes());
@@ -1420,10 +1345,7 @@ pub fn write<E: Arch>(ctx: &mut Context<E>, output: &mut OutputFile) {
let symtab_size = data.ht_size as usize * 8;
let tables = words_as_bytes(&data.tables);
parallel_copy(
&mut buf[symtab_offset..symtab_offset + symtab_size],
&tables[..symtab_size],
);
parallel_copy(&mut buf[symtab_offset..symtab_offset + symtab_size], &tables[..symtab_size]);
parallel_copy(
&mut buf[const_pool_offset..size],
&tables[symtab_size..symtab_size + size - const_pool_offset],
+19 -47
View File
@@ -180,9 +180,7 @@ impl DigestMap {
// The round number wrapped around, making stale slot stamps
// ambiguous, so reset the table. In practice, ICF converges long
// before this point.
self.slots
.par_iter()
.for_each(|slot| slot.hi.store(0, Ordering::Relaxed));
self.slots.par_iter().for_each(|slot| slot.hi.store(0, Ordering::Relaxed));
self.round = 1;
}
}
@@ -244,9 +242,7 @@ impl DigestMap {
loop {
let current = SectionRef::decode(cur);
if candidate_priority
>= ctx
.section(current)
.priority(&ctx.objs[current.file.index()])
>= ctx.section(current).priority(&ctx.objs[current.file.index()])
{
break;
}
@@ -428,17 +424,8 @@ fn gather_sections<E: Arch>(ctx: &Context<E>) -> Vec<SectionRef> {
file_indices.push(file_indices.last().unwrap() + count);
}
let total = *file_indices.last().unwrap();
assert!(
u32::try_from(total).is_ok(),
"too many ICF-eligible sections"
);
let mut sections = vec![
SectionRef {
file: ObjId(0),
shndx: 0,
};
total
];
assert!(u32::try_from(total).is_ok(), "too many ICF-eligible sections");
let mut sections = vec![SectionRef { file: ObjId(0), shndx: 0 }; total];
let mut rest = sections.as_mut_slice();
let mut section_chunks = Vec::with_capacity(ctx.objs.len());
@@ -449,25 +436,20 @@ fn gather_sections<E: Arch>(ctx: &Context<E>) -> Vec<SectionRef> {
}
// Fill `sections` contents.
ctx.objs
.par_iter()
.zip(section_chunks.into_par_iter())
.enumerate()
.for_each(|(fi, (file, out))| {
ctx.objs.par_iter().zip(section_chunks.into_par_iter()).enumerate().for_each(
|(fi, (file, out))| {
let base = file_indices[fi];
let mut local = 0;
for isec in file.input_sections() {
if isec.icf_index().is_some() {
isec.set_icf_index((base + local) as u32);
out[local] = SectionRef {
file: file.id(),
shndx: isec.shndx,
};
out[local] = SectionRef { file: file.id(), shndx: isec.shndx };
local += 1;
}
}
debug_assert_eq!(local, out.len());
});
},
);
sections
}
@@ -528,17 +510,14 @@ fn gather_edges<E: Arch>(ctx: &Context<E>, sections: &[SectionRef]) -> Edges {
let mut values = vec![0; *indices.last().unwrap() as usize];
// Split at vertex boundaries so each task owns a disjoint slice of
// the edge array.
rayon::iter::split(
(0..sections.len(), values.as_mut_slice()),
|(range, out)| {
if range.len() <= 1 {
return ((range, out), None);
}
let mid = range.start + range.len() / 2;
let (left, right) = out.split_at_mut((indices[mid] - indices[range.start]) as usize);
((range.start..mid, left), Some((mid..range.end, right)))
},
)
rayon::iter::split((0..sections.len(), values.as_mut_slice()), |(range, out)| {
if range.len() <= 1 {
return ((range, out), None);
}
let mid = range.start + range.len() / 2;
let (left, right) = out.split_at_mut((indices[mid] - indices[range.start]) as usize);
((range.start..mid, left), Some((mid..range.end, right)))
})
.for_each(|(range, out)| {
let base = indices[range.start] as usize;
let mut i = 0;
@@ -609,11 +588,7 @@ fn print_icf_sections<E: Arch>(ctx: &Context<E>, sections: &[SectionRef], output
for (leader, members) in &leaders {
writeln!(out, "selected section {}", ctx.section_display(*leader))?;
for m in members {
writeln!(
out,
" removing identical section {}",
ctx.section_display(*m)
)?;
writeln!(out, " removing identical section {}", ctx.section_display(*m))?;
saved += ctx.section(*leader).contents().len();
}
}
@@ -636,10 +611,7 @@ pub fn icf_sections<E: Arch>(ctx: &mut Context<E>) {
// `digests` holds the current digest of each vertex.
let mut digests: Vec<Digest> = {
let _t = ctx.timer("compute_digests");
sections
.par_iter()
.map(|&r| compute_digest(ctx, &key, r))
.collect()
sections.par_iter().map(|&r| compute_digest(ctx, &key, r)).collect()
};
let edges = gather_edges(ctx, &sections);
+66 -207
View File
@@ -70,11 +70,7 @@ impl NameLen {
fn get(self, strtab: &'static [u8], offset: usize) -> &'static [u8] {
let rest = strtab.get(offset..).unwrap_or(&[]);
let lower = self.lower_bound().min(rest.len());
let len = if self.is_long() {
lower + cstr_at(rest, lower).len()
} else {
lower
};
let len = if self.is_long() { lower + cstr_at(rest, lower).len() } else { lower };
&rest[..len]
}
}
@@ -141,10 +137,7 @@ pub trait FileInPool {
impl<T: FileInPool> Default for FileList<T> {
fn default() -> FileList<T> {
FileList {
pool: Vec::new(),
live: Vec::new(),
}
FileList { pool: Vec::new(), live: Vec::new() }
}
}
@@ -186,9 +179,7 @@ impl<T: FileInPool> FileList<T> {
}
pub fn first(&self) -> Option<&T> {
self.live
.first()
.map(|&index| self.pool[index as usize].as_ref())
self.live.first().map(|&index| self.pool[index as usize].as_ref())
}
/// Returns the nth live file; stable pool indices may contain holes.
@@ -197,18 +188,11 @@ impl<T: FileInPool> FileList<T> {
}
pub fn iter(&self) -> FileIter<'_, T> {
FileIter {
pool: &self.pool,
live: self.live.iter(),
}
FileIter { pool: &self.pool, live: self.live.iter() }
}
pub fn iter_mut(&mut self) -> FileIterMut<'_, T> {
FileIterMut {
pool: &mut self.pool,
live: &self.live,
offset: 0,
}
FileIterMut { pool: &mut self.pool, live: &self.live, offset: 0 }
}
/// Iterates over the pool, including files erased from the live vector.
@@ -220,9 +204,7 @@ impl<T: FileInPool> FileList<T> {
where
T: Sync,
{
self.live
.par_iter()
.map(|&index| self.pool[index as usize].as_ref())
self.live.par_iter().map(|&index| self.pool[index as usize].as_ref())
}
pub fn par_iter_mut(&mut self) -> impl IndexedParallelIterator<Item = &mut T>
@@ -292,9 +274,7 @@ impl<'a, T> Iterator for FileIter<'a, T> {
type Item = &'a T;
fn next(&mut self) -> Option<&'a T> {
self.live
.next()
.map(|&index| self.pool[index as usize].as_ref())
self.live.next().map(|&index| self.pool[index as usize].as_ref())
}
fn size_hint(&self) -> (usize, Option<usize>) {
@@ -304,9 +284,7 @@ impl<'a, T> Iterator for FileIter<'a, T> {
impl<'a, T> DoubleEndedIterator for FileIter<'a, T> {
fn next_back(&mut self) -> Option<&'a T> {
self.live
.next_back()
.map(|&index| self.pool[index as usize].as_ref())
self.live.next_back().map(|&index| self.pool[index as usize].as_ref())
}
}
@@ -471,9 +449,7 @@ impl<E: Layout> InputFile<E> {
// Since it is a 16-bit integer field, it's not large enough to
// represent >65535 sections. If an object file contains more than 65535
// sections, the actual number is stored to sh_size field.
let first = data
.get(shoff..shoff + shdr_size)
.map(record_from_bytes::<ElfShdr<E>>);
let first = data.get(shoff..shoff + shdr_size).map(record_from_bytes::<ElfShdr<E>>);
let num_sections = match (ehdr.e_shnum.get(), first) {
(0, Some(first)) => first.sh_size.get() as usize,
(n, _) => n as usize,
@@ -575,9 +551,7 @@ impl<E: Layout> InputFile<E> {
}
pub fn find_section(&self, sh_type: u32) -> Option<usize> {
self.shdrs
.iter()
.position(|shdr| shdr.sh_type.get() == sh_type)
self.shdrs.iter().position(|shdr| shdr.sh_type.get() == sh_type)
}
#[inline]
@@ -606,14 +580,12 @@ impl<E: Layout> InputFile<E> {
// Find the source filename. It should be listed in symtab as STT_FILE.
pub fn source_name(&self, symbols: &SymbolTable) -> Option<&'static [u8]> {
self.local_symbols()
.iter()
.map(|&id| &symbols[id])
.find(|sym| sym.ty() == STT_FILE)
.map(|sym| {
self.local_symbols().iter().map(|&id| &symbols[id]).find(|sym| sym.ty() == STT_FILE).map(
|sym| {
let name: &'static BStr = sym.name();
&**name
})
},
)
}
}
@@ -634,10 +606,7 @@ impl ComdatGroupRef {
fn new(sect_idx: u32, signature: SymbolId) -> ComdatGroupRef {
assert!(signature.0 < Self::IS_OWNER);
ComdatGroupRef {
sect_idx,
signature_and_owner: signature.0,
}
ComdatGroupRef { sect_idx, signature_and_owner: signature.0 }
}
#[inline]
@@ -702,9 +671,7 @@ struct DecodedRelocations<R> {
impl<R> DecodedRelocations<R> {
fn new(records: Box<[R]>) -> DecodedRelocations<R> {
DecodedRelocations {
records: UnsafeCell::new(records),
}
DecodedRelocations { records: UnsafeCell::new(records) }
}
fn as_slice(&self) -> &[R] {
@@ -735,9 +702,7 @@ impl<R> DecodedRelocations<R> {
impl<R: fmt::Debug> fmt::Debug for DecodedRelocations<R> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_tuple("DecodedRelocations")
.field(&self.as_slice())
.finish()
f.debug_tuple("DecodedRelocations").field(&self.as_slice()).finish()
}
}
@@ -895,12 +860,7 @@ pub(crate) fn display_file<'a>(
if archive_name.as_os_str().is_empty() {
write!(f, "{}", path_clean(filename))
} else {
write!(
f,
"{}({})",
crate::util::clean_path(archive_name).display(),
filename
)
write!(f, "{}({})", crate::util::clean_path(archive_name).display(), filename)
}
})
}
@@ -1015,12 +975,7 @@ impl<E: Layout> Iterator for CrelReader<'_, E> {
self.addend = self.addend.wrapping_add(util::read_sleb(&mut self.data));
}
Some(ElfRel::<E>::new(
self.offset,
self.r_type as u32,
self.r_sym as u32,
self.addend,
))
Some(ElfRel::<E>::new(self.offset, self.r_type as u32, self.r_sym as u32, self.addend))
}
#[inline]
@@ -1200,9 +1155,7 @@ impl<E: Arch> ObjectFile<E> {
#[inline]
pub(crate) fn shndx_from(&self, idx: usize, st_shndx: u16) -> usize {
if st_shndx as u32 == SHN_XINDEX {
self.symtab_shndx
.get(idx)
.map_or(0, |bytes| E::Endian::read_u32(bytes)) as usize
self.symtab_shndx.get(idx).map_or(0, |bytes| E::Endian::read_u32(bytes)) as usize
} else if st_shndx as u32 >= SHN_LORESERVE {
0
} else {
@@ -1222,9 +1175,7 @@ impl<E: Arch> ObjectFile<E> {
/// Returns the logical ID of the section at `shndx`.
#[inline]
pub fn section_id(&self, shndx: usize) -> Option<InputSectionId> {
self.sections
.input_index(shndx)
.map(|index| InputSectionId::new(self.id(), index))
self.sections.input_index(shndx).map(|index| InputSectionId::new(self.id(), index))
}
/// Returns a section together with its logical ID without resolving the
@@ -1342,8 +1293,7 @@ impl<E: Arch> ObjectFile<E> {
fn set_decoded_crel(&mut self, index: usize, rels: Box<[E::Rel]>) {
if self.decoded_crel.len() <= index {
self.decoded_crel
.resize_with(self.num_elf_sections, || None);
self.decoded_crel.resize_with(self.num_elf_sections, || None);
}
debug_assert!(self.decoded_crel[index].is_none());
self.decoded_crel[index] = Some(DecodedRelocations::new(rels));
@@ -1432,10 +1382,7 @@ impl<E: Arch> ObjectFile<E> {
// 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(idx);
if !contents
.len()
.is_multiple_of(std::mem::size_of::<ElfSym<E>>())
{
if !contents.len().is_multiple_of(std::mem::size_of::<ElfSym<E>>()) {
fatal!("{self}: corrupted section");
}
self.base.elf_syms = Cow::Borrowed(records_from_bytes::<ElfSym<E>>(contents));
@@ -1474,18 +1421,11 @@ impl<E: Arch> ObjectFile<E> {
}
// Find the name length and version separator in one scan.
let strtab = self
.base
.symbol_strtab
.get(esym.st_name().get() as usize..)
.unwrap_or_default();
let strtab =
self.base.symbol_strtab.get(esym.st_name().get() as usize..).unwrap_or_default();
let pos = memchr::memchr2(0, b'@', strtab).unwrap_or(strtab.len());
let len = pos
+ if strtab.get(pos) == Some(&b'@') {
cstr_at(strtab, pos).len()
} else {
0
};
let len =
pos + if strtab.get(pos) == Some(&b'@') { cstr_at(strtab, pos).len() } else { 0 };
self.base.symbol_name_lengths.push(NameLen::new(len));
let mut key = &strtab[..len];
let name = &key[..pos];
@@ -1537,10 +1477,7 @@ impl<E: Arch> ObjectFile<E> {
let (sh_type, sh_flags) = (shdr.sh_type.get(), shdr.sh_flags.get());
if sh_flags & SHF_EXCLUDE as u64 != 0
&& self
.base
.section_name(i)
.starts_with(b".gnu.offload_lto_.symtab.")
&& self.base.section_name(i).starts_with(b".gnu.offload_lto_.symtab.")
{
self.is_gcc_offload_obj = true;
}
@@ -1593,8 +1530,7 @@ impl<E: Arch> ObjectFile<E> {
SymbolId::DISCARDED_COMDAT
};
let group_idx = self.comdat_groups.len() as u32;
self.comdat_groups
.push(ComdatGroupRef::new(i as u32, signature));
self.comdat_groups.push(ComdatGroupRef::new(i as u32, signature));
if !is_own_global {
self.pending_comdat_signatures.push(PendingComdatSignature {
key: name,
@@ -1809,10 +1745,7 @@ impl<E: Arch> ObjectFile<E> {
}
if name == b".comment"
&& self
.base
.section_contents_from_shdr(shdr)
.starts_with(b"rustc ")
&& self.base.section_contents_from_shdr(shdr).starts_with(b"rustc ")
{
self.is_rust_obj = true;
}
@@ -2009,9 +1942,7 @@ impl<E: Arch> ObjectFile<E> {
if self.base.elf_syms.is_empty() {
return 0;
}
1 + (1..self.base.first_global)
.filter(|&i| !self.is_discarded_comdat(i))
.count()
1 + (1..self.base.first_global).filter(|&i| !self.is_discarded_comdat(i)).count()
}
/// Fills in the local symbols. `slots` has `num_local_symbols()`
@@ -2178,10 +2109,7 @@ impl<E: Arch> ObjectFile<E> {
);
}
// The function may belong to a discarded COMDAT group.
if self
.symbol_section(rels[rel_begin].r_sym() as usize)
.is_none()
{
if self.symbol_section(rels[rel_begin].r_sym() as usize).is_none() {
continue;
}
new_fdes.push(FdeRecord::new(begin_offset as u32, rel_begin as u32));
@@ -2278,11 +2206,7 @@ impl<E: Arch> ObjectFile<E> {
fatal!("{}: corrupted .sframe section", isec.display(self));
}
if hdr.abi_arch != abi {
fatal!(
"{}: .sframe is incompatible with {}",
isec.display(self),
E::NAME
);
fatal!("{}: .sframe is incompatible with {}", isec.display(self), E::NAME);
}
// We support only SFrame Version 3. A section written in an older
// version (e.g. by an old assembler) isn't an error; we just ignore
@@ -2354,10 +2278,11 @@ impl<E: Arch> ObjectFile<E> {
else {
continue;
};
sections
.regular_section_mut(i)
.expect("a regular section")
.uncompress(self, name, self.base.shdrs[i].sh_size.get() as usize);
sections.regular_section_mut(i).expect("a regular section").uncompress(
self,
name,
self.base.shdrs[i].sh_size.get() as usize,
);
sections.set_merge_info(i, parent);
}
self.sections = sections;
@@ -2431,10 +2356,7 @@ impl<E: Arch> ObjectFile<E> {
let Some((frag, offset)) = m.fragment(esym.st_value().get()) else {
fatal!("{self}: bad symbol value: {}", esym.st_value().get());
};
let frag = FragmentRef {
section: m.parent,
entry: frag,
};
let frag = FragmentRef { section: m.parent, entry: frag };
symbols.with_symbol(sym_id, |sym| {
// If the symbol resolved to a definition in another file, leave it
// alone. Overwriting it would discard the chosen definition, and since
@@ -2534,10 +2456,7 @@ impl<E: Arch> ObjectFile<E> {
fatal!("{self}: bad relocation at {}", record.r_sym());
};
(
FragmentRef {
section: m.parent,
entry: frag,
},
FragmentRef { section: m.parent, entry: frag },
(in_frag_offset - addend) as u64,
)
})
@@ -2548,11 +2467,7 @@ impl<E: Arch> ObjectFile<E> {
let dummy_idx = (self.base.elf_syms.len() + fragments.len()) as u32;
fragments.push(FragmentSymbol {
fragment: frag,
value,
sym_idx: record.r_sym(),
});
fragments.push(FragmentSymbol { fragment: frag, value, sym_idx: record.r_sym() });
rel.set_r_sym(dummy_idx);
}
@@ -2645,11 +2560,7 @@ impl<E: Arch> ObjectFile<E> {
} else {
(SHF_ALLOC | SHF_WRITE) as u64
});
let name: &'static [u8] = if sym.ty() == STT_TLS {
b".tls_common"
} else {
b".common"
};
let name: &'static [u8] = if sym.ty() == STT_TLS { b".tls_common" } else { b".common" };
self.elf_sections2.push(shdr);
let shndx = self.num_elf_sections + self.elf_sections2.len() - 1;
@@ -2842,11 +2753,7 @@ pub struct SymtabEntries<'a> {
impl<'a> SymtabEntries<'a> {
pub fn new(syms: &'a mut [u8], xindex: Option<&'a mut [u8]>) -> SymtabEntries<'a> {
SymtabEntries {
syms,
xindex,
len: 0,
}
SymtabEntries { syms, xindex, len: 0 }
}
fn push<E: Arch>(&mut self, esym: ElfSym<E>, xindex: u32) {
@@ -2866,13 +2773,7 @@ impl<'a> SymtabBlock<'a> {
strtab: &'a mut [u8],
strtab_base: u64,
) -> SymtabBlock<'a> {
SymtabBlock {
locals,
globals,
strtab,
strtab_base,
strtab_len: 0,
}
SymtabBlock { locals, globals, strtab, strtab_base, strtab_len: 0 }
}
/// Zeroes reserved space that was not used by emitted symbols.
@@ -2967,10 +2868,7 @@ fn parse_fde_encoding<E: Arch>(file: &ObjectFile<E>, isec: &InputSection<E>, dat
DW_EH_PE_absptr => E::WORD_SIZE as u8,
DW_EH_PE_udata4 | DW_EH_PE_sdata4 => 4,
DW_EH_PE_udata8 | DW_EH_PE_sdata8 => 8,
_ => fatal!(
"{}: unsupported FDE pointer encoding: {enc}",
isec.display(file)
),
_ => fatal!("{}: unsupported FDE pointer encoding: {enc}", isec.display(file)),
}
};
@@ -3042,10 +2940,7 @@ fn parse_fde_encoding<E: Arch>(file: &ObjectFile<E>, isec: &InputSection<E>, dat
// We support only raw absolute and PC-relative pointers.
if enc & 0xf0 != 0 && enc as u32 & 0xf0 != DW_EH_PE_pcrel {
fatal!(
"{}: unsupported FDE pointer encoding: {enc}",
isec.display(file)
);
fatal!("{}: unsupported FDE pointer encoding: {enc}", isec.display(file));
}
ptr_size(enc)
}
@@ -3120,29 +3015,23 @@ impl<E: Arch> SharedFile<E> {
/// The strings of the dynamic entries with the given tag, such as the
/// DT_NEEDED libraries.
fn dynamic_strings(&self, tag: u64) -> impl Iterator<Item = &'static [u8]> + '_ {
self.base
.find_section(SHT_DYNAMIC)
.into_iter()
.flat_map(move |idx| {
let shdr = &self.base.shdrs[idx];
let strtab = self.base.section_contents(shdr.sh_link.get() as usize);
self.base
.section_contents(idx)
.chunks_exact(ElfDyn::<E>::size())
.map(ElfDyn::<E>::parse)
.filter(move |entry| entry.d_tag.get() == tag)
.map(move |entry| cstr_at(strtab, entry.d_val.get() as usize))
})
self.base.find_section(SHT_DYNAMIC).into_iter().flat_map(move |idx| {
let shdr = &self.base.shdrs[idx];
let strtab = self.base.section_contents(shdr.sh_link.get() as usize);
self.base
.section_contents(idx)
.chunks_exact(ElfDyn::<E>::size())
.map(ElfDyn::<E>::parse)
.filter(move |entry| entry.d_tag.get() == tag)
.map(move |entry| cstr_at(strtab, entry.d_val.get() as usize))
})
}
fn get_soname(&self) -> &'static [u8] {
if let Some(soname) = self.dynamic_strings(DT_SONAME as u64).next() {
return soname;
}
let mf = self
.base
.mf
.expect("a shared object is backed by an input mapping");
let mf = self.base.mf.expect("a shared object is backed by an input mapping");
let name = if mf.given_fullpath {
mf.name.as_os_str()
} else {
@@ -3156,9 +3045,7 @@ impl<E: Arch> SharedFile<E> {
return;
};
let symtab_shdr = &self.base.shdrs[symtab_idx];
self.base.symbol_strtab = self
.base
.section_contents(symtab_shdr.sh_link.get() as usize);
self.base.symbol_strtab = self.base.section_contents(symtab_shdr.sh_link.get() as usize);
self.soname = self.get_soname();
self.version_strings = self.read_version_strings();
@@ -3231,11 +3118,7 @@ impl<E: Arch> SharedFile<E> {
// resolve_symbols only consults versyms[] for defined symbols
// (see SharedFile::resolve_symbols), so VER_NDX_GLOBAL is fine
// for undefined entries.
self.versyms.push(if esym.is_undef() {
VER_NDX_GLOBAL as u16
} else {
ver
});
self.versyms.push(if esym.is_undef() { VER_NDX_GLOBAL as u16 } else { ver });
let name = cstr_at(self.base.symbol_strtab, esym.st_name().get() as usize);
let has_version = ver as u32 != VER_NDX_GLOBAL
@@ -3275,17 +3158,9 @@ impl<E: Arch> SharedFile<E> {
};
self.base.symbols.push(SymbolId::DISCARDED_COMDAT);
self.symbols2.push(SymbolId::NONE);
bins.record(
key,
name.len(),
SymbolSlot::new(self.base.symbols.last_mut().unwrap()),
);
bins.record(key, name.len(), SymbolSlot::new(self.base.symbols.last_mut().unwrap()));
if let Some(key) = alias {
bins.record(
key,
name.len(),
SymbolSlot::new(self.symbols2.last_mut().unwrap()),
);
bins.record(key, name.len(), SymbolSlot::new(self.symbols2.last_mut().unwrap()));
}
}
@@ -3349,9 +3224,7 @@ impl<E: Arch> SharedFile<E> {
if let Some(idx) = self.base.find_section(SHT_GNU_VERDEF) {
let verdef = self.base.section_contents(idx);
let strtab = self
.base
.section_contents(self.base.shdrs[idx].sh_link.get() as usize);
let strtab = self.base.section_contents(self.base.shdrs[idx].sh_link.get() as usize);
let mut pos = 0;
loop {
let ver = ElfVerdef::<E>::parse(&verdef[pos..]);
@@ -3359,10 +3232,7 @@ impl<E: Arch> SharedFile<E> {
fatal!("{self}: symbol version too large");
}
let aux = ElfVerdaux::<E>::parse(&verdef[pos + ver.vd_aux.get() as usize..]);
set(
ver.vd_ndx.get() as usize,
cstr_at(strtab, aux.vda_name.get() as usize),
);
set(ver.vd_ndx.get() as usize, cstr_at(strtab, aux.vda_name.get() as usize));
if ver.vd_next.get() == 0 {
break;
}
@@ -3372,9 +3242,7 @@ impl<E: Arch> SharedFile<E> {
if let Some(idx) = self.base.find_section(SHT_GNU_VERNEED) {
let verneed = self.base.section_contents(idx);
let strtab = self
.base
.section_contents(self.base.shdrs[idx].sh_link.get() as usize);
let strtab = self.base.section_contents(self.base.shdrs[idx].sh_link.get() as usize);
let mut pos = 0;
loop {
let vn = ElfVerneed::<E>::parse(&verneed[pos..]);
@@ -3560,11 +3428,7 @@ unsafe impl Sync for SymbolEditor<'_> {}
impl<'a> SymbolEditor<'a> {
pub(crate) fn new(symbols: &'a mut [Symbol]) -> SymbolEditor<'a> {
SymbolEditor {
symbols: symbols.as_mut_ptr(),
len: symbols.len(),
_symbols: PhantomData,
}
SymbolEditor { symbols: symbols.as_mut_ptr(), len: symbols.len(), _symbols: PhantomData }
}
#[inline]
@@ -3602,12 +3466,7 @@ impl<'a, E: Arch> SymbolResolver<'a, E> {
dsos: &'a FileList<SharedFile<E>>,
default_version: u16,
) -> SymbolResolver<'a, E> {
SymbolResolver {
editor: SymbolEditor::new(symbols),
objs,
dsos,
default_version,
}
SymbolResolver { editor: SymbolEditor::new(symbols), objs, dsos, default_version }
}
#[inline]
+38 -134
View File
@@ -36,10 +36,7 @@ impl SectionRef {
#[inline]
pub(crate) fn decode(value: u64) -> SectionRef {
SectionRef {
file: ObjId((value >> 32) as u32),
shndx: value as u32,
}
SectionRef { file: ObjId((value >> 32) as u32), shndx: value as u32 }
}
}
@@ -55,10 +52,7 @@ const _: () = assert!(std::mem::size_of::<InputSectionId>() == 8);
impl InputSectionId {
/// A placeholder used only while a member array is being filled.
pub(crate) const NONE: InputSectionId = InputSectionId {
file: ObjId(0),
index: u32::MAX,
};
pub(crate) const NONE: InputSectionId = InputSectionId { file: ObjId(0), index: u32::MAX };
#[inline]
pub(crate) fn new(file: ObjId, index: u32) -> InputSectionId {
@@ -71,10 +65,7 @@ impl InputSectionId {
#[inline]
pub(crate) const fn from_raw(value: u64) -> InputSectionId {
InputSectionId {
file: ObjId((value >> 32) as u32),
index: (value as u32).wrapping_sub(1),
}
InputSectionId { file: ObjId((value >> 32) as u32), index: (value as u32).wrapping_sub(1) }
}
#[inline]
@@ -338,10 +329,7 @@ impl<E: Arch> InputSection<E> {
#[inline]
pub fn section_ref(&self) -> SectionRef {
SectionRef {
file: self.file,
shndx: self.shndx,
}
SectionRef { file: self.file, shndx: self.shndx }
}
#[inline]
@@ -479,11 +467,8 @@ impl<E: Arch> InputSection<E> {
num_elf_sections: usize,
) -> &'static BStr {
if self.shndx as usize >= num_elf_sections {
let name: &[u8] = if self.sh_flags & SHF_TLS as u64 != 0 {
b".tls_common"
} else {
b".common"
};
let name: &[u8] =
if self.sh_flags & SHF_TLS as u64 != 0 { b".tls_common" } else { b".common" };
return BStr::new(name);
}
@@ -606,10 +591,7 @@ impl<E: Arch> InputSection<E> {
}
let begin = self.fde_begin as usize;
let end = begin
+ file.fdes[begin..]
.iter()
.position(|fde| fde.is_last)
.expect("unterminated FDE run")
+ file.fdes[begin..].iter().position(|fde| fde.is_last).expect("unterminated FDE run")
+ 1;
&file.fdes[begin..end]
}
@@ -633,12 +615,7 @@ impl<E: Arch> InputSection<E> {
Some((frag, addend)) => (ctx.fragment_addr(frag), addend as u64),
None => (sym.addr(ctx), self.rel_addend(rel) as u64),
};
Some(NonAllocReloc {
sym,
s,
a,
frag: frag.map(|(f, _)| f),
})
Some(NonAllocReloc { sym, s, a, frag: frag.map(|(f, _)| f) })
}
/// The addend of a relocation against this section.
@@ -811,22 +788,14 @@ impl<E: Arch> InputSection<E> {
}
let m = cache.section?;
let addend = self.rel_addend(rel);
let value = if cache.is_section {
cache.value.wrapping_add(addend as u64)
} else {
cache.value
};
let value =
if cache.is_section { cache.value.wrapping_add(addend as u64) } else { cache.value };
let (frag, offset) = m.fragment_with_hint(value, &mut cache.next)?;
if let Some(&entry) = m.fragments.get(cache.next + 7) {
ctx.merged_sections[m.parent.index()]
.fragments
.prefetch(entry);
ctx.merged_sections[m.parent.index()].fragments.prefetch(entry);
}
Some((
FragmentRef {
section: m.parent,
entry: frag,
},
FragmentRef { section: m.parent, entry: frag },
offset + if cache.is_section { 0 } else { addend },
))
}
@@ -893,11 +862,7 @@ impl<E: Arch> InputSection<E> {
// 0 is an invalid value in most debug info sections, so we use it
// as a tombstone value. .debug_loc and .debug_ranges reserve 0 as
// the terminator marker, so we use 1 if that's the case.
Some(if name == b".debug_loc" || name == b".debug_ranges" {
1
} else {
0
})
Some(if name == b".debug_loc" || name == b".debug_ranges" { 1 } else { 0 })
}
/// Test if the symbol a given relocation refers to has already been resolved.
@@ -972,12 +937,7 @@ impl<E: Arch> InputSection<E> {
sym
);
if let Some(owner) = find_comdat_owner(ctx, file, rel.r_sym() as usize) {
write!(
msg,
"\n>>> prevailing definition is in {}",
ctx.objs[owner.index()]
)
.unwrap();
write!(msg, "\n>>> prevailing definition is in {}", ctx.objs[owner.index()]).unwrap();
}
error!("{msg}");
}
@@ -1000,12 +960,7 @@ impl<E: Arch> InputSection<E> {
write!(msg, ":({func})").unwrap();
}
msg.push('\n');
ctx.undef_errors
.lock()
.unwrap()
.entry(sym_id)
.or_default()
.push(msg);
ctx.undef_errors.lock().unwrap().entry(sym_id).or_default().push(msg);
}
/// Copies the section to `buf` and applies relocations.
@@ -1032,9 +987,7 @@ impl<E: Arch> InputSection<E> {
.copy_from_slice(&contents[..deltas[0].offset as usize]);
for (i, d) in deltas.iter().enumerate() {
let end = deltas
.get(i + 1)
.map_or(contents.len(), |n| n.offset as usize);
let end = deltas.get(i + 1).map_or(contents.len(), |n| n.offset as usize);
let removed = removed_bytes(deltas, i) as usize;
let src_start = d.offset as usize + removed;
let dst_start = (d.offset as i64 + removed as i64 - d.delta) as usize;
@@ -1354,10 +1307,7 @@ fn record_size<E: Layout>(contents: &[u8], offset: u32) -> usize {
fn rels_in<E: Layout>(rels: &[E::Rel], begin: u32, end: usize) -> &[E::Rel] {
let begin = begin as usize;
let rest = &rels[begin..];
let count = rest
.iter()
.take_while(|r| (r.r_offset() as usize) < end)
.count();
let count = rest.iter().take_while(|r| (r.r_offset() as usize) < end).count();
&rels[begin..begin + count]
}
@@ -1572,10 +1522,7 @@ impl MergeInfo {
sketch: &mut HyperLogLog,
) {
if data.len() > u32::MAX as usize {
fatal!(
"{}: mergeable section too large",
format_args!("{file}:({name})")
);
fatal!("{}: mergeable section too large", format_args!("{file}:({name})"));
}
let entsize = parent.hdr.shdr.sh_entsize.get() as usize;
@@ -1594,10 +1541,7 @@ impl MergeInfo {
let mut pos = 0;
while pos < data.len() {
let Some(end) = find_null(data, pos, entsize) else {
fatal!(
"{}: string is not null terminated",
format_args!("{file}:({name})")
);
fatal!("{}: string is not null terminated", format_args!("{file}:({name})"));
};
add_fragment(pos, end + entsize - pos);
pos = end + entsize;
@@ -1637,12 +1581,8 @@ impl MergeInfo {
if let Some(&hash) = self.hashes.get(i + LOOKAHEAD) {
parent.map.prefetch(hash);
}
let id = parent.insert(
self.contents(data, i),
self.hashes[i],
self.p2align,
gc_sections,
);
let id =
parent.insert(self.contents(data, i), self.hashes[i], self.p2align, gc_sections);
self.fragments.push(id);
}
@@ -1653,14 +1593,8 @@ impl MergeInfo {
/// Finds the fragment containing `offset` and the offset within it.
#[inline]
pub fn fragment(&self, offset: u64) -> Option<(EntryId, i64)> {
let idx = self
.frag_offsets
.partition_point(|&o| o as u64 <= offset)
.checked_sub(1)?;
Some((
self.fragments[idx],
offset as i64 - self.frag_offsets[idx] as i64,
))
let idx = self.frag_offsets.partition_point(|&o| o as u64 <= offset).checked_sub(1)?;
Some((self.fragments[idx], offset as i64 - self.frag_offsets[idx] as i64))
}
#[inline]
@@ -1668,21 +1602,12 @@ impl MergeInfo {
let mut idx = *next;
if idx >= self.frag_offsets.len()
|| offset < self.frag_offsets[idx] as u64
|| self
.frag_offsets
.get(idx + 1)
.is_some_and(|&o| o as u64 <= offset)
|| self.frag_offsets.get(idx + 1).is_some_and(|&o| o as u64 <= offset)
{
idx = self
.frag_offsets
.partition_point(|&o| o as u64 <= offset)
.checked_sub(1)?;
idx = self.frag_offsets.partition_point(|&o| o as u64 <= offset).checked_sub(1)?;
}
*next = idx + 1;
Some((
self.fragments[idx],
offset as i64 - self.frag_offsets[idx] as i64,
))
Some((self.fragments[idx], offset as i64 - self.frag_offsets[idx] as i64))
}
#[inline]
@@ -1726,11 +1651,7 @@ const SECTION_INDEX_MASK: u32 = !HAS_MERGE_INFO;
impl<E: Arch> Default for SectionList<E> {
fn default() -> Self {
SectionList {
indices: Vec::new(),
inputs: Vec::new(),
merge_info: Vec::new(),
}
SectionList { indices: Vec::new(), inputs: Vec::new(), merge_info: Vec::new() }
}
}
@@ -1839,8 +1760,7 @@ impl<E: Arch> SectionList<E> {
debug_assert!(self.merge_info.len() < SECTION_INDEX_MASK as usize);
let input_index = value - 1;
let input = &self.inputs[input_index as usize];
self.merge_info
.push(MergeInfo::new(parent, input_index, input));
self.merge_info.push(MergeInfo::new(parent, input_index, input));
self.indices[shndx] = HAS_MERGE_INFO | self.merge_info.len() as u32;
}
@@ -1855,10 +1775,7 @@ impl<E: Arch> SectionList<E> {
}
let merge_info_idx = ((value & SECTION_INDEX_MASK) - 1) as usize;
let input_index = self.merge_info[merge_info_idx].input_index;
Some((
&mut self.merge_info[merge_info_idx],
&self.inputs[input_index as usize],
))
Some((&mut self.merge_info[merge_info_idx], &self.inputs[input_index as usize]))
}
/// The regular sections in section order.
@@ -1876,16 +1793,13 @@ impl<E: Arch> SectionList<E> {
file: ObjId,
) -> impl Iterator<Item = (InputSectionId, &mut InputSection<E>)> {
let indices = &self.indices;
self.inputs
.iter_mut()
.enumerate()
.filter_map(move |(input_index, section)| {
let value = indices[section.shndx as usize];
(value != 0 && value & HAS_MERGE_INFO == 0).then(|| {
debug_assert_eq!(value as usize, input_index + 1);
(InputSectionId::new(file, input_index as u32), section)
})
self.inputs.iter_mut().enumerate().filter_map(move |(input_index, section)| {
let value = indices[section.shndx as usize];
(value != 0 && value & HAS_MERGE_INFO == 0).then(|| {
debug_assert_eq!(value as usize, input_index + 1);
(InputSectionId::new(file, input_index as u32), section)
})
})
}
pub fn merge_infos(&self) -> impl Iterator<Item = &MergeInfo> {
@@ -1912,10 +1826,7 @@ mod tests {
fn fragment_hint_handles_sequential_repeated_and_backward_offsets() {
let map = crate::util::concurrent_map::ConcurrentMap::with_capacity(12);
let fragments = (0..12)
.map(|i| {
map.insert_with(&b"abcdefghijkl"[i..i + 1], i as u64, || ())
.0
})
.map(|i| map.insert_with(&b"abcdefghijkl"[i..i + 1], i as u64, || ()).0)
.collect();
let info = MergeInfo {
parent: MergedSectionId(0),
@@ -1928,16 +1839,9 @@ mod tests {
};
let mut next = 0;
for offset in [0, 5, 8, 11, 11, 6, 35, 38, 35, 5, 4, 8] {
assert_eq!(
info.fragment_with_hint(offset, &mut next),
info.fragment(offset)
);
assert_eq!(info.fragment_with_hint(offset, &mut next), info.fragment(offset));
}
let empty = MergeInfo {
fragments: Vec::new(),
frag_offsets: Vec::new(),
..info
};
let empty = MergeInfo { fragments: Vec::new(), frag_offsets: Vec::new(), ..info };
assert_eq!(empty.fragment_with_hint(0, &mut next), None);
}
}
+2 -6
View File
@@ -44,12 +44,8 @@ pub fn acquire_global_lock() {
let name = String::from_utf8_lossy(&name);
std::path::PathBuf::from(format!("/tmp/mold-lock-{name}"))
};
let Ok(file) = OpenOptions::new()
.write(true)
.create(true)
.truncate(false)
.mode(0o600)
.open(path)
let Ok(file) =
OpenOptions::new().write(true).create(true).truncate(false).mode(0o600).open(path)
else {
return;
};
+11 -21
View File
@@ -42,14 +42,8 @@ pub struct Script<'a, E: Arch> {
}
fn get_line(input: &[u8], pos: usize) -> (usize, &[u8]) {
let start = input[..pos]
.iter()
.rposition(|&b| b == b'\n')
.map_or(0, |i| i + 1);
let end = input[pos..]
.iter()
.position(|&b| b == b'\n')
.map_or(input.len(), |i| pos + i);
let start = input[..pos].iter().rposition(|&b| b == b'\n').map_or(0, |i| i + 1);
let end = input[pos..].iter().position(|&b| b == b'\n').map_or(input.len(), |i| pos + i);
(start, &input[start..end])
}
@@ -74,11 +68,7 @@ fn syntax_error(mf: &MappedFile, tok: &[u8], msg: &str) -> ! {
let label = format!("{}:{}: ", mf.name.display(), lineno);
let indent = "mold: fatal: ".len() + label.len();
let column = pos - line_start;
fatal!(
"{label}{}\n{}^ {msg}",
util::display(line),
" ".repeat(indent + column)
);
fatal!("{label}{}\n{}^ {msg}", util::display(line), " ".repeat(indent + column));
}
fn tokenize(mf: &'static MappedFile) -> Vec<&'static [u8]> {
@@ -117,7 +107,8 @@ fn tokenize(mf: &'static MappedFile) -> Vec<&'static [u8]> {
continue;
}
let is_word_char = |b: u8| b >= 0x80 || b.is_ascii_alphanumeric() || b"_.$/\\~=+[]*?-!^:".contains(&b);
let is_word_char =
|b: u8| b >= 0x80 || b.is_ascii_alphanumeric() || b"_.$/\\~=+[]*?-!^:".contains(&b);
let len = match input.iter().position(|&b| !is_word_char(b)) {
Some(0) => 1,
Some(pos) => pos,
@@ -153,8 +144,11 @@ fn resolve_path<E: Arch>(
if check_target {
if let Some(target) = reader::get_machine_type(ctx, rctx, mf) {
if target != E::NAME {
warn!("{}: skipping incompatible file: {target} (e_machine {})",
path.display(), E::E_MACHINE);
warn!(
"{}: skipping incompatible file: {target} (e_machine {})",
path.display(),
E::E_MACHINE
);
return None;
}
}
@@ -386,11 +380,7 @@ impl<'a, E: Arch> Script<'a, E> {
continue;
}
let idx = if is_global {
ver_idx
} else {
VER_NDX_LOCAL as u16
};
let idx = if is_global { ver_idx } else { VER_NDX_LOCAL as u16 };
if t == b"*" {
self.ctx.default_version = idx;
} else {
+27 -94
View File
@@ -138,17 +138,11 @@ struct TagValue {
impl TagValue {
fn int(tag: c_int, val: c_int) -> TagValue {
TagValue {
tag,
data: TagData { val },
}
TagValue { tag, data: TagData { val } }
}
fn ptr(tag: c_int, ptr: *const c_void) -> TagValue {
TagValue {
tag,
data: TagData { ptr },
}
TagValue { tag, data: TagData { ptr } }
}
}
@@ -483,10 +477,7 @@ unsafe fn get_symbols<E: Arch>(
let ctx = &*(CONTEXT.load(Ordering::Acquire) as *const Context<E>);
let psyms = std::slice::from_raw_parts_mut(psyms, nsyms as usize);
let handle = handle as *const MappedFile;
let Some(file) = ctx
.objs
.iter()
.find(|f| f.base.mf.is_some_and(|mf| ptr::eq(mf, handle)))
let Some(file) = ctx.objs.iter().find(|f| f.base.mf.is_some_and(|mf| ptr::eq(mf, handle)))
else {
return LDPS_BAD_HANDLE;
};
@@ -550,9 +541,7 @@ unsafe extern "C" fn get_api_version(
// The plugin reads the string after this function has returned
static LINKER_VERSION: OnceLock<CString> = OnceLock::new();
*linker_identifier = c"mold".as_ptr();
*linker_version = LINKER_VERSION
.get_or_init(|| CString::new(VERSION).unwrap())
.as_ptr();
*linker_version = LINKER_VERSION.get_or_init(|| CString::new(VERSION).unwrap()).as_ptr();
if LAPI_V1 <= maximal_api_supported {
HOOKS.lock().unwrap().gcc_api_v1 = true;
return LAPI_V1;
@@ -625,11 +614,7 @@ fn load_plugin<E: Arch>(ctx: &Context<E>) {
}
let onload = dynamic_symbol(handle, c"onload".as_ptr());
if onload.is_null() {
fatal!(
"failed to load plugin {}: {}",
ctx.args.plugin.display(),
dlerror_string()
);
fatal!("failed to load plugin {}: {}", ctx.args.plugin.display(), dlerror_string());
}
std::mem::transmute::<*mut c_void, OnloadFn>(onload)
};
@@ -652,18 +637,12 @@ fn load_plugin<E: Arch>(ctx: &Context<E>) {
tv.push(TagValue::ptr(LDPT_OPTION, cstr(opt)));
}
tv.extend([
TagValue::ptr(
LDPT_REGISTER_CLAIM_FILE_HOOK,
register_claim_file_hook as *const c_void,
),
TagValue::ptr(LDPT_REGISTER_CLAIM_FILE_HOOK, register_claim_file_hook as *const c_void),
TagValue::ptr(
LDPT_REGISTER_ALL_SYMBOLS_READ_HOOK,
register_all_symbols_read_hook as *const c_void,
),
TagValue::ptr(
LDPT_REGISTER_CLEANUP_HOOK,
register_cleanup_hook as *const c_void,
),
TagValue::ptr(LDPT_REGISTER_CLEANUP_HOOK, register_cleanup_hook as *const c_void),
TagValue::ptr(LDPT_ADD_SYMBOLS, add_symbols as *const c_void),
TagValue::ptr(LDPT_GET_SYMBOLS, get_symbols_v1 as *const c_void),
TagValue::ptr(LDPT_ADD_INPUT_FILE, add_input_file::<E> as *const c_void),
@@ -672,39 +651,17 @@ fn load_plugin<E: Arch>(ctx: &Context<E>) {
TagValue::ptr(LDPT_ADD_INPUT_LIBRARY, add_input_library as *const c_void),
TagValue::ptr(
LDPT_OUTPUT_NAME,
CString::new(ctx.args.output.as_os_str().as_encoded_bytes())
.unwrap()
.into_raw() as *const c_void,
),
TagValue::ptr(
LDPT_SET_EXTRA_LIBRARY_PATH,
set_extra_library_path as *const c_void,
CString::new(ctx.args.output.as_os_str().as_encoded_bytes()).unwrap().into_raw()
as *const c_void,
),
TagValue::ptr(LDPT_SET_EXTRA_LIBRARY_PATH, set_extra_library_path as *const c_void),
TagValue::ptr(LDPT_GET_VIEW, get_view as *const c_void),
TagValue::ptr(
LDPT_GET_INPUT_SECTION_COUNT,
get_input_section_count as *const c_void,
),
TagValue::ptr(
LDPT_GET_INPUT_SECTION_TYPE,
get_input_section_type as *const c_void,
),
TagValue::ptr(
LDPT_GET_INPUT_SECTION_NAME,
get_input_section_name as *const c_void,
),
TagValue::ptr(
LDPT_GET_INPUT_SECTION_CONTENTS,
get_input_section_contents as *const c_void,
),
TagValue::ptr(
LDPT_UPDATE_SECTION_ORDER,
update_section_order as *const c_void,
),
TagValue::ptr(
LDPT_ALLOW_SECTION_ORDERING,
allow_section_ordering as *const c_void,
),
TagValue::ptr(LDPT_GET_INPUT_SECTION_COUNT, get_input_section_count as *const c_void),
TagValue::ptr(LDPT_GET_INPUT_SECTION_TYPE, get_input_section_type as *const c_void),
TagValue::ptr(LDPT_GET_INPUT_SECTION_NAME, get_input_section_name as *const c_void),
TagValue::ptr(LDPT_GET_INPUT_SECTION_CONTENTS, get_input_section_contents as *const c_void),
TagValue::ptr(LDPT_UPDATE_SECTION_ORDER, update_section_order as *const c_void),
TagValue::ptr(LDPT_ALLOW_SECTION_ORDERING, allow_section_ordering as *const c_void),
TagValue::ptr(LDPT_ADD_SYMBOLS_V2, add_symbols as *const c_void),
TagValue::ptr(LDPT_GET_SYMBOLS_V2, get_symbols_v2::<E> as *const c_void),
TagValue::ptr(
@@ -720,14 +677,8 @@ fn load_plugin<E: Arch>(ctx: &Context<E>) {
LDPT_GET_INPUT_SECTION_ALIGNMENT,
get_input_section_alignment as *const c_void,
),
TagValue::ptr(
LDPT_GET_INPUT_SECTION_SIZE,
get_input_section_size as *const c_void,
),
TagValue::ptr(
LDPT_REGISTER_NEW_INPUT_HOOK,
register_new_input_hook as *const c_void,
),
TagValue::ptr(LDPT_GET_INPUT_SECTION_SIZE, get_input_section_size as *const c_void),
TagValue::ptr(LDPT_REGISTER_NEW_INPUT_HOOK, register_new_input_hook as *const c_void),
TagValue::ptr(LDPT_GET_WRAP_SYMBOLS, get_wrap_symbols as *const c_void),
TagValue::ptr(LDPT_GET_API_VERSION, get_api_version as *const c_void),
TagValue::int(LDPT_NULL, 0),
@@ -760,9 +711,7 @@ fn plugin_input_file(mf: &'static MappedFile) -> (PluginInputFile, File) {
let file = File::open(&container.name)
.unwrap_or_else(|e| fatal!("cannot open {}: {e}", container.name.display()));
let input = PluginInputFile {
name: CString::new(container.name.as_os_str().as_encoded_bytes())
.unwrap()
.into_raw(),
name: CString::new(container.name.as_os_str().as_encoded_bytes()).unwrap().into_raw(),
#[cfg(not(windows))]
fd: file.as_raw_fd(),
#[cfg(windows)]
@@ -830,13 +779,7 @@ pub fn read_lto_object<E: Arch>(
comdat_keys.push(sym.comdat_key.map(leak_bytes));
}
// Create mold's object instance
Some(ObjectFile::<E>::lto_input(
mf,
archive_name,
elf_syms,
leak_bytes(strtab),
comdat_keys,
))
Some(ObjectFile::<E>::lto_input(mf, archive_name, elf_syms, leak_bytes(strtab), comdat_keys))
}
/// This function restarts mold itself with `--:lto-pass2` and
@@ -854,11 +797,8 @@ pub fn read_lto_object<E: Arch>(
///
/// This is an ugly hack and should be removed once GCC adopts the v3 API.
fn restart_process<E: Arch>(ctx: &Context<E>) -> ! {
let mut args: Vec<Cow<'_, OsStr>> = ctx
.cmdline_args
.iter()
.map(|arg| Cow::Borrowed(arg.as_ref()))
.collect();
let mut args: Vec<Cow<'_, OsStr>> =
ctx.cmdline_args.iter().map(|arg| Cow::Borrowed(arg.as_ref())).collect();
for file in &ctx.objs {
if file.is_lto_input() && !file.base.is_reachable() {
let mut arg = OsString::from("--:ignore-ir-file=");
@@ -876,10 +816,8 @@ fn restart_process<E: Arch>(ctx: &Context<E>) -> ! {
#[cfg(windows)]
let err = {
let path = CString::new(path.as_os_str().as_encoded_bytes()).unwrap();
let args: Vec<CString> = args
.iter()
.map(|arg| CString::new(arg.as_encoded_bytes()).unwrap())
.collect();
let args: Vec<CString> =
args.iter().map(|arg| CString::new(arg.as_encoded_bytes()).unwrap()).collect();
let mut argv: Vec<*const c_char> = args.iter().map(|arg| arg.as_ptr()).collect();
argv.push(ptr::null());
// SAFETY: path and every argument are NUL-terminated and argv ends in null.
@@ -924,9 +862,7 @@ pub fn run_plugin<E: Arch>(ctx: &mut Context<E>) {
let id = ctx.symbols.get_or_intern(name);
ctx.symbols[id].set_referenced_by_regular_obj(true);
for prefix in [b"__wrap_", b"__real_"] {
let id = ctx
.symbols
.get_or_intern(&[prefix, name.as_slice()].concat());
let id = ctx.symbols.get_or_intern(&[prefix, name.as_slice()].concat());
ctx.symbols[id].set_referenced_by_regular_obj(true);
}
}
@@ -939,11 +875,8 @@ pub fn run_plugin<E: Arch>(ctx: &mut Context<E>) {
// Object files containing .gnu.offload_lto_.* sections need to be
// given to the LTO backend. Such sections contains code and data for
// peripherails (typically GPUs).
let claim_file = HOOKS
.lock()
.unwrap()
.claim_file
.expect("the plugin registered a claim_file hook");
let claim_file =
HOOKS.lock().unwrap().claim_file.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(file.base.mf.unwrap());
+1 -3
View File
@@ -102,9 +102,7 @@ impl MappedFile {
let file = File::open(path)?;
let display = path.display();
let metadata = file
.metadata()
.unwrap_or_else(|e| fatal!("{display}: fstat failed: {e}"));
let metadata = file.metadata().unwrap_or_else(|e| fatal!("{display}: fstat failed: {e}"));
let size = metadata.len();
// True if `data` is a memory mapping of the file rather than a copy of
+8 -39
View File
@@ -141,12 +141,7 @@ fn preallocate(file: &File, offset: u64, size: u64) {
unsafe {
let mut fs: libc::statfs = std::mem::zeroed();
if libc::fstatfs(file.as_raw_fd(), &mut fs) != 0 || fs.f_type != libc::TMPFS_MAGIC as _ {
libc::fallocate(
file.as_raw_fd(),
0,
offset as libc::off_t,
size as libc::off_t,
);
libc::fallocate(file.as_raw_fd(), 0, offset as libc::off_t, size as libc::off_t);
}
}
}
@@ -190,11 +185,7 @@ impl OutputFile {
#[cfg(not(windows))]
fn publish_output_buffer(&self) {
match &self.storage {
Storage::File {
map: Some(map),
len,
..
} => {
Storage::File { map: Some(map), len, .. } => {
set_output_buffer_range(map.as_ptr() as usize, *len);
}
_ => set_output_buffer_range(0, 0),
@@ -251,12 +242,7 @@ impl OutputFile {
if !overwrite_in_place || std::fs::rename(path, &tmp).is_err() {
return None;
}
match open_options(perm)
.read(true)
.write(true)
.create(true)
.open(&tmp)
{
match open_options(perm).read(true).write(true).create(true).open(&tmp) {
Ok(file) => Some(file),
Err(_) => {
let _ = std::fs::remove_file(&tmp);
@@ -277,8 +263,7 @@ impl OutputFile {
set_permissions(&file, perm)
.unwrap_or_else(|e| fatal!("{}: fchmod failed: {e}", tmp.display()));
file.set_len(size)
.unwrap_or_else(|e| fatal!("{}: ftruncate failed: {e}", tmp.display()));
file.set_len(size).unwrap_or_else(|e| fatal!("{}: ftruncate failed: {e}", tmp.display()));
preallocate(&file, 0, size);
let map = map_file(&file, size)
@@ -286,11 +271,7 @@ impl OutputFile {
let output = OutputFile {
path: path.to_path_buf(),
tmp_path: Some(tmp),
storage: Storage::File {
file,
map,
len: size as usize,
},
storage: Storage::File { file, map, len: size as usize },
perm,
};
#[cfg(not(windows))]
@@ -323,11 +304,7 @@ impl OutputFile {
OutputFile {
path: path.to_path_buf(),
tmp_path: None,
storage: Storage::File {
file,
map: None,
len: 0,
},
storage: Storage::File { file, map: None, len: 0 },
perm,
}
}
@@ -358,11 +335,7 @@ impl OutputFile {
pub fn buf(&mut self) -> &mut [u8] {
match &mut self.storage {
Storage::File {
map: Some(map),
len,
..
} => &mut map[..*len],
Storage::File { map: Some(map), len, .. } => &mut map[..*len],
Storage::File { map: None, .. } => &mut [],
Storage::Memory(vec) => &mut vec[..],
}
@@ -454,11 +427,7 @@ impl OutputFile {
std::mem::forget(old);
}
std::fs::rename(&tmp, &self.path).unwrap_or_else(|e| {
fatal!(
"cannot rename {} to {}: {e}",
tmp.display(),
self.path.display()
)
fatal!("cannot rename {} to {}: {e}", tmp.display(), self.path.display())
});
set_tmpfile(None);
}
+162 -413
View File
@@ -52,12 +52,8 @@ pub fn apply_exclude_libs<E: Arch>(ctx: &mut Context<E>) {
}
for file in &mut ctx.objs {
if !file.archive_name.as_os_str().is_empty()
&& (set.contains(
file.archive_name
.file_name()
.unwrap_or_default()
.as_encoded_bytes(),
) || set.contains(b"ALL".as_slice()))
&& (set.contains(file.archive_name.file_name().unwrap_or_default().as_encoded_bytes())
|| set.contains(b"ALL".as_slice()))
{
file.exclude_libs = true;
}
@@ -78,16 +74,10 @@ pub fn create_synthetic_sections<E: Arch>(ctx: &mut Context<E>) {
.iter()
.any(|o| matches!(o, SectionOrder::Section(n) if n == name.as_bytes()))
};
let ehdr_flags = if ctx.args.section_order.is_empty() || find("EHDR") {
SHF_ALLOC as u64
} else {
0
};
let phdr_flags = if ctx.args.section_order.is_empty() || find("PHDR") {
SHF_ALLOC as u64
} else {
0
};
let ehdr_flags =
if ctx.args.section_order.is_empty() || find("EHDR") { SHF_ALLOC as u64 } else { 0 };
let phdr_flags =
if ctx.args.section_order.is_empty() || find("PHDR") { SHF_ALLOC as u64 } else { 0 };
ctx.ehdr = Some(chunks::new_ehdr::<E>(ehdr_flags));
chunks.push(ChunkId::Ehdr);
ctx.phdr = Some(OutputPhdr::<E>::new(phdr_flags));
@@ -269,11 +259,8 @@ fn mark_live_file<E: Arch>(ctx: &Context<E>, id: FileId) -> Vec<FileId> {
// from an archive. resolve_default_symver redirects symbols[] to the
// bare name, so follow the original versioned symbol in symbols2[].
let id2 = file.symbols2[i];
let target = if id2 != SymbolId::NONE {
ctx.symbols[id2].file()
} else {
sym.file()
};
let target =
if id2 != SymbolId::NONE { ctx.symbols[id2].file() } else { sym.file() };
if let Some(target) = target {
if (!target.is_dso() || !ctx.args.allow_shlib_undefined)
&& ctx.file(target).mark_reachable()
@@ -297,12 +284,7 @@ fn mark_live_objects<E: Arch>(ctx: &mut Context<E>) {
// Symbols named on the command line pull in their files, and keep
// their sections under --gc-sections; so does the entry point.
let args = &ctx.args;
let names: Vec<Vec<u8>> = args
.undefined
.iter()
.chain(&args.require_defined)
.cloned()
.collect();
let names: Vec<Vec<u8>> = args.undefined.iter().chain(&args.require_defined).cloned().collect();
for name in names {
let id = ctx.get_symbol(&name);
ctx.symbols[id].set_gc_root(true);
@@ -315,12 +297,7 @@ fn mark_live_objects<E: Arch>(ctx: &mut Context<E>) {
if !ctx.args.undefined_glob.is_empty() {
let roots: Vec<SymbolId> = {
let Context {
objs,
symbols,
args,
..
} = &*ctx;
let Context { objs, symbols, args, .. } = &*ctx;
objs.par_iter()
.flat_map_iter(|file| {
file.base.global_symbols().iter().copied().filter(|&id| {
@@ -368,12 +345,7 @@ fn mark_live_objects<E: Arch>(ctx: &mut Context<E>) {
// `foo@VER1` by removing the version part and see if `foo` has version
// `VER1`. If it does, that's the symbol we are looking for.
fn resolve_default_symver<E: Arch>(ctx: &mut Context<E>) {
let Context {
objs,
dsos,
symbols,
..
} = ctx;
let Context { objs, dsos, symbols, .. } = ctx;
objs.par_iter_mut().for_each(|file| {
let start = file.base.first_global.min(file.base.symbols.len());
for id in &mut file.base.symbols[start..] {
@@ -430,11 +402,8 @@ pub fn gather_symbols<E: Arch>(ctx: &mut Context<E>) {
.par_iter()
.filter(|file| file.base.mf.is_some() && !file.is_lto_input() && !file.sections_parsed)
.map(|file| {
let locals = if file.base.elf_syms.is_empty() {
0
} else {
file.base.first_global.max(1)
};
let locals =
if file.base.elf_syms.is_empty() { 0 } else { file.base.first_global.max(1) };
let fragments = if file.archive_name.as_os_str().is_empty() || file.base.is_reachable()
{
file.crel_fragment_dummy_upper_bound()
@@ -466,13 +435,7 @@ fn current_rank<E: Arch>(ctx: &Context<E>, sym: &Symbol) -> u64 {
/// each symbol's lock before comparing and updating its definition.
fn resolve_symbols_pass<E: Arch>(ctx: &mut Context<E>, files: &[FileId], only_reachable: bool) {
let _t = ctx.timer("resolve_symbols_pass");
let Context {
objs,
dsos,
symbols,
default_version,
..
} = ctx;
let Context { objs, dsos, symbols, default_version, .. } = ctx;
let resolver = SymbolResolver::new(symbols.as_mut_slice(), objs, dsos, *default_version);
files.par_iter().for_each(|&id| match id {
FileId::Obj(id) => {
@@ -493,13 +456,7 @@ fn resolve_symbols_pass<E: Arch>(ctx: &mut Context<E>, files: &[FileId], only_re
/// Resolves the rare hidden-symbol retry while ignoring DSO definitions.
fn resolve_skip_dso_symbols_pass<E: Arch>(ctx: &mut Context<E>) {
let _t = ctx.timer("resolve_symbols_pass");
let Context {
objs,
dsos,
symbols,
default_version,
..
} = ctx;
let Context { objs, dsos, symbols, default_version, .. } = ctx;
let resolver = SymbolResolver::new(symbols.as_mut_slice(), objs, dsos, *default_version);
objs.par_iter().for_each(|file| {
file.resolve_skip_dso_symbols(&resolver, file.id());
@@ -620,8 +577,7 @@ fn parse_input_sections<E: Arch>(ctx: &mut Context<E>) {
sym.record_comdat_owner(pending.priority);
}
});
objs.par_iter_mut()
.for_each(|file| file.pending_comdat_signatures = Vec::new());
objs.par_iter_mut().for_each(|file| file.pending_comdat_signatures = Vec::new());
}
// IR objects name their COMDAT groups per symbol.
@@ -682,8 +638,7 @@ fn parse_input_sections<E: Arch>(ctx: &mut Context<E>) {
}
// Restore sym_idx before the final symbol-resolution pass.
ctx.symbols
.par_for_each_global_mut(|sym| sym.set_sym_idx(u32::MAX));
ctx.symbols.par_for_each_global_mut(|sym| sym.set_sym_idx(u32::MAX));
drop(t);
@@ -705,21 +660,10 @@ fn parse_input_sections<E: Arch>(ctx: &mut Context<E>) {
&& !file.is_lto_input()
&& !file.sections_parsed
})
.map(|file| {
if file.base.elf_syms.is_empty() {
0
} else {
file.base.first_global.max(1)
}
})
.map(|file| if file.base.elf_syms.is_empty() { 0 } else { file.base.first_global.max(1) })
.sum();
{
let Context {
objs,
symbols,
args,
..
} = ctx;
let Context { objs, symbols, args, .. } = ctx;
symbols.with_parallel_appender(maximum, |allocator| {
objs.par_iter_mut().for_each(|file| {
if !file.base.is_reachable() {
@@ -947,12 +891,7 @@ pub fn create_merged_sections<E: Arch>(ctx: &mut Context<E>) {
// original InputSections remain stored but are marked dead.
let t = ctx.timer("convert_mergeable_sections");
{
let Context {
objs,
merged_sections,
args,
..
} = ctx;
let Context { objs, merged_sections, args, .. } = ctx;
let merged = RwLock::new(std::mem::take(merged_sections));
// Keep a cache for each actual worker, including across Rayon jobs.
let caches = WorkerLocal::new(crate::chunks::merged::MergedSectionCache::default);
@@ -972,10 +911,7 @@ pub fn create_merged_sections<E: Arch>(ctx: &mut Context<E>) {
for m in file.merge_infos() {
ctx.merged_sections[m.parent.index()]
.members
.push(SectionRef {
file: file.id(),
shndx: m.shndx,
});
.push(SectionRef { file: file.id(), shndx: m.shndx });
}
}
drop(t);
@@ -996,12 +932,7 @@ pub fn create_merged_sections<E: Arch>(ctx: &mut Context<E>) {
drop(t);
let _t = ctx.timer("reattach_section_pieces");
let Context {
objs,
symbols,
merged_sections,
..
} = ctx;
let Context { objs, symbols, merged_sections, .. } = ctx;
let editor = SymbolEditor::new(symbols.as_mut_slice());
let fragments: Vec<_> = objs
@@ -1031,15 +962,13 @@ pub fn create_merged_sections<E: Arch>(ctx: &mut Context<E>) {
(first, head, fragments)
})
.collect();
objs.par_iter_mut()
.zip(parts)
.for_each(|(file, (first, slots, fragments))| {
file.base.symbols.reserve(fragments.len());
for (i, (slot, fragment)) in slots.iter_mut().zip(fragments).enumerate() {
slot.write(fragment.into_symbol(file));
file.base.symbols.push(SymbolId((first + i) as u32));
}
});
objs.par_iter_mut().zip(parts).for_each(|(file, (first, slots, fragments))| {
file.base.symbols.reserve(fragments.len());
for (i, (slot, fragment)) in slots.iter_mut().zip(fragments).enumerate() {
slot.write(fragment.into_symbol(file));
file.base.symbols.push(SymbolId((first + i) as u32));
}
});
});
}
}
@@ -1047,12 +976,7 @@ pub fn create_merged_sections<E: Arch>(ctx: &mut Context<E>) {
pub fn convert_common_symbols<E: Arch>(ctx: &mut Context<E>) {
let _t = ctx.timer("convert_common_symbols");
let default_version = ctx.default_version;
let Context {
objs,
symbols,
args,
..
} = ctx;
let Context { objs, symbols, args, .. } = ctx;
for file in objs {
file.convert_common_symbols(args, file.id(), symbols, default_version);
}
@@ -1109,13 +1033,9 @@ fn output_name<E: Arch>(
}
if args.z_keep_text_section_prefix {
for prefix in [
".text.hot.",
".text.unknown.",
".text.unlikely.",
".text.startup.",
".text.exit.",
] {
for prefix in
[".text.hot.", ".text.unknown.", ".text.unlikely.", ".text.startup.", ".text.exit."]
{
let stem = &prefix[..prefix.len() - 1];
if name == stem.as_bytes() || name.starts_with(prefix.as_bytes()) {
return BStr::new(stem.as_bytes());
@@ -1196,10 +1116,7 @@ struct CachedOutputSection {
group: usize,
}
type OutputSectionShared<E> = (
HashMap<OutputSectionKey, OutputSectionId>,
Vec<OutputSection<E>>,
);
type OutputSectionShared<E> = (HashMap<OutputSectionKey, OutputSectionId>, Vec<OutputSection<E>>);
// PT_GNU_RELRO segment is a security mechanism to make more pages
// read-only than we could have done without it.
@@ -1255,10 +1172,8 @@ pub fn create_output_sections<E: Arch>(ctx: &mut Context<E>) {
let num_elf_sections = file.num_elf_sections;
let shdrs = &file.base.shdrs;
let extra_shdrs = &file.elf_sections2;
for (member, isec) in file
.sections
.regular_ids_mut(file_id)
.filter(|(_, isec)| isec.is_alive())
for (member, isec) in
file.sections.regular_ids_mut(file_id).filter(|(_, isec)| isec.is_alive())
{
let name = isec.name_in(shstrtab, num_elf_sections);
let sh_type = if isec.is_nobits() {
@@ -1295,11 +1210,7 @@ pub fn create_output_sections<E: Arch>(ctx: &mut Context<E>) {
sections.push(OutputSection::<E>::new(key.0, key.1));
OutputSectionId::new(sections.len() as u32 - 1)
});
CachedOutputSection {
section,
file: None,
group: 0,
}
CachedOutputSection { section, file: None, group: 0 }
});
if cached.file != Some(file_id) {
cached.file = Some(file_id);
@@ -1350,12 +1261,7 @@ pub fn create_output_sections<E: Arch>(ctx: &mut Context<E>) {
let sh_flags = parts.iter().fold(0, |flags, g| flags | g.sh_flags);
let p2align = parts.iter().map(|g| g.p2align).max().unwrap_or(0);
(
OutputSectionId::new(index as u32),
members,
sh_flags,
p2align,
)
(OutputSectionId::new(index as u32), members, sh_flags, p2align)
})
.collect();
for (id, members, sh_flags, p2align) in flattened {
@@ -1438,9 +1344,7 @@ fn start_stop_name<E: Arch>(ctx: &Context<E>, id: ChunkId) -> Option<Cow<'static
if ctx.args.start_stop {
let name = hdr.name.strip_prefix(b".").unwrap_or(hdr.name);
return Some(Cow::Owned(
name.iter()
.map(|&b| if b.is_ascii_alphanumeric() { b } else { b'_' })
.collect(),
name.iter().map(|&b| if b.is_ascii_alphanumeric() { b } else { b'_' }).collect(),
));
}
None
@@ -1586,8 +1490,7 @@ pub fn add_synthetic_symbols<E: Arch>(ctx: &mut Context<E>) {
for i in 0..ctx.objs[obj_id.index()].base.symbols.len() {
let id = ctx.objs[obj_id.index()].base.symbols[i];
if ctx.symbols[id].file() == Some(FileId::Obj(obj_id)) {
ctx.set_symbol_output_chunk(id, ChunkId::Symtab)
.set_imported(false);
ctx.set_symbol_output_chunk(id, ChunkId::Symtab).set_imported(false);
}
}
@@ -1634,18 +1537,15 @@ pub fn apply_section_align<E: Arch>(ctx: &mut Context<E>) {
pub fn check_cet_errors<E: Arch>(ctx: &Context<E>) {
let warning = ctx.args.z_cet_report == CetReportKind::Warning;
let has_feature = |file: &ObjectFile<E>, feature: u32| {
file.gnu_properties
.get(&GNU_PROPERTY_X86_FEATURE_1_AND)
.is_some_and(|v| v & feature != 0)
file.gnu_properties.get(&GNU_PROPERTY_X86_FEATURE_1_AND).is_some_and(|v| v & feature != 0)
};
for file in &ctx.objs {
if ctx.is_internal(file.id()) {
continue;
}
for (feature, name) in [
(GNU_PROPERTY_X86_FEATURE_1_IBT, "IBT"),
(GNU_PROPERTY_X86_FEATURE_1_SHSTK, "SHSTK"),
] {
for (feature, name) in
[(GNU_PROPERTY_X86_FEATURE_1_IBT, "IBT"), (GNU_PROPERTY_X86_FEATURE_1_SHSTK, "SHSTK")]
{
if !has_feature(file, feature) {
if warning {
warn!("{file}: -cet-report=warning: missing GNU_PROPERTY_X86_FEATURE_1_{name}");
@@ -1675,16 +1575,12 @@ pub fn print_dependencies<E: Arch>(ctx: &Context<E>) {
let mut print = |src: &dyn std::fmt::Display, sym: &Symbol, is_weak: bool| {
let kind = if is_weak { 'w' } else { 'u' };
match sym.input_section() {
Some(sec) => writeln!(
out,
"{src}\t{}\t{kind}\t{sym}",
ctx.input_section_display(sec)
),
None => writeln!(
out,
"{src}\t{}\t{kind}\t{sym}",
ctx.file_display(sym.file().unwrap())
),
Some(sec) => {
writeln!(out, "{src}\t{}\t{kind}\t{sym}", ctx.input_section_display(sec))
}
None => {
writeln!(out, "{src}\t{}\t{kind}\t{sym}", ctx.file_display(sym.file().unwrap()))
}
}
};
@@ -1762,25 +1658,15 @@ pub fn write_repro_file<E: Arch>(ctx: &Context<E>) {
let mut name = ctx.args.output.as_os_str().to_os_string();
name.push(".repro.tar");
let path = std::path::PathBuf::from(name);
let mut basedir = ctx
.args
.output
.file_name()
.unwrap_or_default()
.to_os_string();
let mut basedir = ctx.args.output.file_name().unwrap_or_default().to_os_string();
basedir.push(".repro");
let mut tar = crate::util::tar::TarWriter::open(&path, &basedir)
.unwrap_or_else(|e| fatal!("cannot open {}: {e}", path.display()));
let write = |tar: &mut crate::util::tar::TarWriter, name: &std::path::Path, data: &[u8]| {
tar.append(name, data)
.unwrap_or_else(|e| fatal!("{}: write failed: {e}", path.display()));
tar.append(name, data).unwrap_or_else(|e| fatal!("{}: write failed: {e}", path.display()));
};
write(
&mut tar,
std::path::Path::new("response.txt"),
&create_response_file(ctx),
);
write(&mut tar, std::path::Path::new("response.txt"), &create_response_file(ctx));
write(
&mut tar,
std::path::Path::new("version.txt"),
@@ -1842,10 +1728,7 @@ pub fn check_duplicate_symbols<E: Arch>(ctx: &Context<E>) {
continue;
}
}
error!(
"duplicate symbol: {file}: {}: {sym}",
ctx.file_display(owner)
);
error!("duplicate symbol: {file}: {}: {sym}", ctx.file_display(owner));
}
});
crate::error::checkpoint();
@@ -1861,40 +1744,35 @@ pub fn check_symbol_version_conflicts<E: Arch>(ctx: &Context<E>) {
return;
}
let _t = ctx.timer("check_symbol_version_conflicts");
ctx.dynsym
.symbols
.par_iter()
.skip(1)
.flatten()
.for_each(|&id| {
let sym = &ctx.symbols[id];
let Some(FileId::Obj(obj)) = sym.file() else {
return;
};
if sym.is_weak()
|| sym.ver_idx as u32 == VER_NDX_UNSPECIFIED
|| sym.ver_idx as u32 & VERSYM_HIDDEN == 0
{
return;
ctx.dynsym.symbols.par_iter().skip(1).flatten().for_each(|&id| {
let sym = &ctx.symbols[id];
let Some(FileId::Obj(obj)) = sym.file() else {
return;
};
if sym.is_weak()
|| sym.ver_idx as u32 == VER_NDX_UNSPECIFIED
|| sym.ver_idx as u32 & VERSYM_HIDDEN == 0
{
return;
}
let Some(id2) = ctx.symbols.lookup(sym.name()) else {
return;
};
if id2 == id {
return;
}
let sym2 = &ctx.symbols[id2];
if let Some(FileId::Obj(_)) = sym2.file() {
if !sym2.is_weak() && sym2.ver_idx as u32 == (sym.ver_idx as u32 & !VERSYM_HIDDEN) {
let file = &ctx.objs[obj.index()];
error!(
"duplicate symbol: {file}: {}: {}",
ctx.file_display(sym2.file().unwrap()),
crate::util::display(file.base.symbol_name_in(sym.sym_idx() as usize))
);
}
let Some(id2) = ctx.symbols.lookup(sym.name()) else {
return;
};
if id2 == id {
return;
}
let sym2 = &ctx.symbols[id2];
if let Some(FileId::Obj(_)) = sym2.file() {
if !sym2.is_weak() && sym2.ver_idx as u32 == (sym.ver_idx as u32 & !VERSYM_HIDDEN) {
let file = &ctx.objs[obj.index()];
error!(
"duplicate symbol: {file}: {}: {}",
ctx.file_display(sym2.file().unwrap()),
crate::util::display(file.base.symbol_name_in(sym.sym_idx() as usize))
);
}
}
});
}
});
crate::error::checkpoint();
}
@@ -1959,10 +1837,8 @@ pub fn check_shlib_undefined<E: Arch>(ctx: &mut Context<E>) {
// Skip test if we don't have a complete set of shared object files
// 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()
.all(|needed| ctx.dso_sonames.contains(needed))
});
let complete =
ctx.dsos.iter().all(|dso| dso.dt_needed().all(|needed| ctx.dso_sonames.contains(needed)));
if complete {
ctx.dsos.par_iter().for_each(|file| {
@@ -1994,12 +1870,7 @@ pub fn check_shlib_undefined<E: Arch>(ctx: &mut Context<E>) {
for file in &ctx.dsos {
file.base.set_reachable(!file.base.as_needed);
}
let Context {
objs,
dsos,
symbols,
..
} = ctx;
let Context { objs, dsos, symbols, .. } = ctx;
objs.par_iter().for_each(|file| {
for &id in file.base.global_symbols() {
if let Some(FileId::Dso(dso)) = symbols[id].file() {
@@ -2200,10 +2071,7 @@ pub fn sort_debug_info_sections<E: Arch>(ctx: &mut Context<E>) {
// Unless DWARF32 and DWARF64 debug info come from different files, it
// doesn't make sense to sort sections.
let has_dwarf32 = ctx.objs.iter().any(|f| f.is_dwarf32);
let has_dwarf64 = ctx
.objs
.iter()
.any(|f| !f.is_dwarf32 && !f.debug_info_sections.is_empty());
let has_dwarf64 = ctx.objs.iter().any(|f| !f.is_dwarf32 && !f.debug_info_sections.is_empty());
if !has_dwarf32 || !has_dwarf64 {
return;
}
@@ -2250,21 +2118,16 @@ pub fn fixup_ctors_in_init_array<E: Arch>(ctx: &mut Context<E>) {
let _t = ctx.timer("fixup_ctors_in_init_array");
let word = E::WORD_SIZE;
for (osec_name, prefix) in [
(&b".init_array"[..], &b".ctors"[..]),
(&b".fini_array"[..], &b".dtors"[..]),
] {
for (osec_name, prefix) in
[(&b".init_array"[..], &b".ctors"[..]), (&b".fini_array"[..], &b".dtors"[..])]
{
let Some(ChunkId::Output(id)) = ctx.find_chunk_by_name(osec_name) else {
continue;
};
let members = &ctx.output_sections[id.index()].members;
for &m in members {
let section_ref = ctx.input_section(m).section_ref();
if !ctx
.input_section(m)
.name(&ctx.objs[section_ref.file.index()])
.starts_with(prefix)
{
if !ctx.input_section(m).name(&ctx.objs[section_ref.file.index()]).starts_with(prefix) {
continue;
}
let file = &ctx.objs[section_ref.file.index()];
@@ -2328,17 +2191,14 @@ pub fn shuffle_sections<E: Arch>(ctx: &mut Context<E>) {
&& name != b".fini_array"
};
let mode = ctx.args.shuffle_sections;
ctx.output_sections
.par_iter_mut()
.filter(|o| is_eligible(o))
.for_each(|osec| match mode {
ShuffleSections::Shuffle(seed) => {
let s = seed.wrapping_add(xxhash_rust::xxh3::xxh3_64(osec.hdr.name));
shuffle(&mut osec.members, s);
}
ShuffleSections::Reverse => osec.members.reverse(),
ShuffleSections::None => {}
});
ctx.output_sections.par_iter_mut().filter(|o| is_eligible(o)).for_each(|osec| match mode {
ShuffleSections::Shuffle(seed) => {
let s = seed.wrapping_add(xxhash_rust::xxh3::xxh3_64(osec.hdr.name));
shuffle(&mut osec.members, s);
}
ShuffleSections::Reverse => osec.members.reverse(),
ShuffleSections::None => {}
});
}
pub fn add_dynamic_strings<E: Arch>(ctx: &mut Context<E>) {
@@ -2516,11 +2376,7 @@ pub fn claim_unresolved_symbols<E: Arch>(ctx: &mut Context<E>) {
sym.set_weak(false);
sym.set_imported(is_imported);
sym.set_exported(false);
sym.ver_idx = if is_imported {
VER_NDX_UNSPECIFIED as u16
} else {
default_version
};
sym.ver_idx = if is_imported { VER_NDX_UNSPECIFIED as u16 } else { default_version };
};
let visibility = ctx.symbols[id].visibility();
@@ -2575,21 +2431,14 @@ pub fn scan_relocations<E: Arch>(ctx: &mut Context<E>) {
// Scan relocations to find dynamic symbols.
{
let ctx_ref: &Context<E> = ctx;
ctx_ref
.objs
.par_iter()
.for_each(|file| file.scan_relocations(ctx_ref));
ctx_ref.objs.par_iter().for_each(|file| file.scan_relocations(ctx_ref));
}
// Exit if there was a relocation that refers an undefined symbol.
crate::error::checkpoint();
// Word-size absolute relocations (e.g. R_X86_64_64) are handled
// separately because they can be promoted to dynamic relocations.
let results: Vec<(
OutputSectionId,
Vec<crate::chunks::output_section::AbsRel>,
Vec<u64>,
)> = {
let results: Vec<(OutputSectionId, Vec<crate::chunks::output_section::AbsRel>, Vec<u64>)> = {
let ctx_ref: &Context<E> = ctx;
(0..ctx_ref.output_sections.len())
.into_par_iter()
@@ -2669,10 +2518,7 @@ pub fn scan_relocations<E: Arch>(ctx: &mut Context<E>) {
if let Some(&next) = syms.get(i + 64) {
crate::util::prefetch(std::ptr::from_ref(&ctx.symbols[next]).cast());
}
if let Some(aux) = syms
.get(i + 16)
.and_then(|&id| ctx.symbols[id].aux(&ctx.symbols))
{
if let Some(aux) = syms.get(i + 16).and_then(|&id| ctx.symbols[id].aux(&ctx.symbols)) {
crate::util::prefetch(std::ptr::from_ref(aux).cast());
}
let flags = ctx.symbols[id].flags();
@@ -2740,9 +2586,7 @@ pub fn report_undef_errors<E: Arch>(ctx: &Context<E>) {
return;
}
let mut errors: Vec<(SymbolId, Vec<String>)> =
std::mem::take(&mut *ctx.undef_errors.lock().unwrap())
.into_iter()
.collect();
std::mem::take(&mut *ctx.undef_errors.lock().unwrap()).into_iter().collect();
errors.sort_by_key(|e| e.0);
for (id, messages) in errors {
@@ -2752,12 +2596,7 @@ pub fn report_undef_errors<E: Arch>(ctx: &Context<E>) {
msg.push_str(m);
}
if messages.len() > MAX_ERRORS {
writeln!(
msg,
">>> referenced {} more times",
messages.len() - MAX_ERRORS
)
.unwrap();
writeln!(msg, ">>> referenced {} more times", messages.len() - MAX_ERRORS).unwrap();
}
// Remove the trailing '\n' because Error/Warn adds it automatically
msg.pop();
@@ -2774,11 +2613,8 @@ pub fn create_reloc_sections<E: Arch>(ctx: &mut Context<E>) {
let _t = ctx.timer("create_reloc_sections");
// Create .rela.* sections
let ids: Vec<OutputSectionId> = ctx
.chunks
.iter()
.filter_map(|c| c.as_output_section())
.collect();
let ids: Vec<OutputSectionId> =
ctx.chunks.iter().filter_map(|c| c.as_output_section()).collect();
let secs: Vec<reloc::RelocSection<E>> = {
let ctx_ref: &Context<E> = ctx;
ids.par_iter().map(|&id| reloc::new(ctx_ref, id)).collect()
@@ -2825,12 +2661,7 @@ pub fn sort_dynsyms<E: Arch>(ctx: &mut Context<E>) {
.map(|&id| {
let name: &'static [u8] = ctx.symbols[id].name();
let hash = gnu_hash::djb_hash(name);
Entry {
bucket: hash % num_buckets,
name,
id,
hash,
}
Entry { bucket: hash % num_buckets, name, id, hash }
})
.collect();
// SAFETY: .dynsym contains each symbol once and every symbol has aux.
@@ -2877,21 +2708,15 @@ pub fn sort_dynsyms<E: Arch>(ctx: &mut Context<E>) {
*total = end;
end += size;
}
dynstr_entries[1..]
.par_chunks_mut(1024)
.zip(offsets)
.for_each(|(entries, mut off)| {
for entry in entries {
entry.offset = off;
off += entry.name.len() as u64 + 1;
}
});
dynstr_entries[1..].par_chunks_mut(1024).zip(offsets).for_each(|(entries, mut off)| {
for entry in entries {
entry.offset = off;
off += entry.name.len() as u64 + 1;
}
});
ctx.dynstr.hdr.shdr.sh_size.set(end);
ctx.dynsym.dynstr_entries = dynstr_entries;
ctx.dynsym.symbols[1..]
.par_iter_mut()
.zip(syms)
.for_each(|(slot, id)| *slot = Some(id));
ctx.dynsym.symbols[1..].par_iter_mut().zip(syms).for_each(|(slot, id)| *slot = Some(id));
// ELF's symbol table sh_info holds the offset of the first global symbol.
ctx.dynsym.hdr.shdr.sh_info.set(num_locals as u32 + 1);
@@ -2917,19 +2742,11 @@ pub fn create_output_symtab<E: Arch>(ctx: &mut Context<E>) {
let obj_plans: Vec<crate::input_files::SymtabPlan> = {
let ctx_ref: &Context<E> = ctx;
ctx_ref
.objs
.par_iter()
.map(|f| f.plan_symtab(ctx_ref, f.id()))
.collect()
ctx_ref.objs.par_iter().map(|f| f.plan_symtab(ctx_ref, f.id())).collect()
};
let dso_plans: Vec<crate::input_files::SymtabPlan> = {
let ctx_ref: &Context<E> = ctx;
ctx_ref
.dsos
.par_iter()
.map(|f| f.plan_symtab(ctx_ref, f.id()))
.collect()
ctx_ref.dsos.par_iter().map(|f| f.plan_symtab(ctx_ref, f.id())).collect()
};
for (file, plan) in ctx.objs.iter_mut().zip(obj_plans) {
file.base.apply_symtab_plan(plan);
@@ -2965,16 +2782,10 @@ pub fn apply_version_script<E: Arch>(ctx: &mut Context<E>) {
}
if v.is_cpp {
if !cpp_matcher.add(v.pattern, priority) {
fatal!(
"invalid version pattern: {}",
crate::util::display(v.pattern)
);
fatal!("invalid version pattern: {}", crate::util::display(v.pattern));
}
} else if has_wildcard(v.pattern) && !matcher.add(v.pattern, priority) {
fatal!(
"invalid version pattern: {}",
crate::util::display(v.pattern)
);
fatal!("invalid version pattern: {}", crate::util::display(v.pattern));
}
}
@@ -3075,11 +2886,7 @@ pub fn parse_symbol_version<E: Arch>(ctx: &mut Context<E>) {
);
continue;
};
let ver_idx = if is_default {
ver_idx
} else {
ver_idx | VERSYM_HIDDEN as u16
};
let ver_idx = if is_default { ver_idx } else { ver_idx | VERSYM_HIDDEN as u16 };
ctx.symbols[id].ver_idx = ver_idx;
// If both symbol `foo` and `foo@VERSION` are defined, `foo@VERSION`
@@ -3176,35 +2983,30 @@ pub fn compute_import_export<E: Arch>(ctx: &mut Context<E>) {
.par_iter()
.flat_map_iter(|file| {
let file_id = FileId::Obj(file.id());
file.base
.global_symbols()
.iter()
.copied()
.enumerate()
.filter_map(move |(i, id)| {
let sym = &ctx_ref.symbols[id];
file.base.global_symbols().iter().copied().enumerate().filter_map(move |(i, id)| {
let sym = &ctx_ref.symbols[id];
// If we are using a symbol in a DSO, we need to import it.
if let Some(FileId::Dso(_)) = sym.file() {
// Shared symbols remain weak only if every undefined
// reference is weak. Fragment dummies have no ElfSym.
let strong = file
.base
.elf_syms
.get(file.base.first_global + i)
.is_some_and(|esym| esym.is_undef() && !esym.is_weak());
return Some((id, true, false, strong));
}
// If we are using a symbol in a DSO, we need to import it.
if let Some(FileId::Dso(_)) = sym.file() {
// Shared symbols remain weak only if every undefined
// reference is weak. Fragment dummies have no ElfSym.
let strong = file
.base
.elf_syms
.get(file.base.first_global + i)
.is_some_and(|esym| esym.is_undef() && !esym.is_weak());
return Some((id, true, false, strong));
}
// If we have a definition of a symbol, we may want to export it.
if sym.file() == Some(file_id) && should_export(ctx_ref, sym) {
// Exported symbols are marked as imported as well by default
// for DSOs.
let imported = ctx_ref.args.shared && !is_protected(ctx_ref, sym);
return Some((id, imported, true, false));
}
None
})
// If we have a definition of a symbol, we may want to export it.
if sym.file() == Some(file_id) && should_export(ctx_ref, sym) {
// Exported symbols are marked as imported as well by default
// for DSOs.
let imported = ctx_ref.args.shared && !is_protected(ctx_ref, sym);
return Some((id, imported, true, false));
}
None
})
})
.collect()
};
@@ -3517,9 +3319,8 @@ fn sort_output_sections_by_order<E: Arch>(ctx: &mut Context<E>) {
}
let name = hdr.name;
let order = &ctx.args.section_order;
if let Some(i) = order
.iter()
.position(|o| matches!(o, SectionOrder::Section(n) if n == name))
if let Some(i) =
order.iter().position(|o| matches!(o, SectionOrder::Section(n) if n == name))
{
return i as i64;
}
@@ -3530,10 +3331,7 @@ fn sort_output_sections_by_order<E: Arch>(ctx: &mut Context<E>) {
{
return i as i64;
}
error!(
"--section-order: missing section specification for {}",
hdr.name
);
error!("--section-order: missing section specification for {}", hdr.name);
0
};
// It is an error if a section order cannot be determined by a given
@@ -3717,12 +3515,8 @@ fn set_virtual_addresses_regular<E: Arch>(ctx: &mut Context<E>) {
}
fn set_virtual_addresses_by_order<E: Arch>(ctx: &mut Context<E>) {
let vec: Vec<ChunkId> = ctx
.chunks
.iter()
.copied()
.filter(|&c| ctx.chunk_header(c).is_alloc())
.collect();
let vec: Vec<ChunkId> =
ctx.chunks.iter().copied().filter(|&c| ctx.chunk_header(c).is_alloc()).collect();
let mut addr = ctx.args.image_base;
let page_size = ctx.args.page_size;
let mut i = 0;
@@ -3796,19 +3590,13 @@ fn set_file_offsets<E: Arch>(ctx: &mut Context<E>) -> u64 {
let first = ctx.chunk_header(ctx.chunks[i]).shdr;
if first.sh_flags.get() & SHF_ALLOC as u64 == 0 {
fileoff = align_to(fileoff, first.sh_addralign.get());
ctx.chunk_header_mut(ctx.chunks[i])
.shdr
.sh_offset
.set(fileoff);
ctx.chunk_header_mut(ctx.chunks[i]).shdr.sh_offset.set(fileoff);
fileoff += first.sh_size.get();
i += 1;
continue;
}
if first.sh_type.get() == SHT_NOBITS {
ctx.chunk_header_mut(ctx.chunks[i])
.shdr
.sh_offset
.set(fileoff);
ctx.chunk_header_mut(ctx.chunks[i]).shdr.sh_offset.set(fileoff);
i += 1;
continue;
}
@@ -3824,10 +3612,7 @@ fn set_file_offsets<E: Arch>(ctx: &mut Context<E>) -> u64 {
loop {
let shdr = ctx.chunk_header(ctx.chunks[i]).shdr;
let offset = fileoff + shdr.sh_addr.get() - first.sh_addr.get();
ctx.chunk_header_mut(ctx.chunks[i])
.shdr
.sh_offset
.set(offset);
ctx.chunk_header_mut(ctx.chunks[i]).shdr.sh_offset.set(offset);
i += 1;
if i >= ctx.chunks.len() {
break;
@@ -3866,10 +3651,7 @@ fn set_file_offsets<E: Arch>(ctx: &mut Context<E>) -> u64 {
if shdr.sh_flags.get() & SHF_ALLOC as u64 == 0 || shdr.sh_type.get() != SHT_NOBITS {
break;
}
ctx.chunk_header_mut(ctx.chunks[i])
.shdr
.sh_offset
.set(fileoff);
ctx.chunk_header_mut(ctx.chunks[i]).shdr.sh_offset.set(fileoff);
i += 1;
}
}
@@ -3903,10 +3685,8 @@ pub fn compute_section_headers<E: Arch>(ctx: &mut Context<E>) {
ctx.chunks = chunks
.into_iter()
.filter(|&id| {
matches!(
id,
ChunkId::Output(_) | ChunkId::GdbIndex | ChunkId::Placeholder(_)
) || ctx.chunk_header(id).shdr.sh_size.get() != 0
matches!(id, ChunkId::Output(_) | ChunkId::GdbIndex | ChunkId::Placeholder(_))
|| ctx.chunk_header(id).shdr.sh_size.get() != 0
})
.collect();
@@ -3983,12 +3763,7 @@ pub fn set_osec_offsets<E: Arch>(ctx: &mut Context<E>) -> u64 {
fn num_irelative_relocs<E: Arch>(ctx: &Context<E>) -> u64 {
let mut n = 0u64;
n += ctx
.got
.got_syms
.iter()
.filter(|&&id| ctx.symbols[id].is_ifunc())
.count() as u64;
n += ctx.got.got_syms.iter().filter(|&&id| ctx.symbols[id].is_ifunc()).count() as u64;
n
}
@@ -4039,10 +3814,7 @@ pub fn fix_synthetic_symbols<E: Arch>(ctx: &mut Context<E>) {
.filter(|&c| !c.is_header() && ctx.chunk_header(c).is_alloc())
.collect();
let find = |ctx: &Context<E>, name: &[u8]| {
sections
.iter()
.copied()
.find(|&c| ctx.chunk_header(c).name == name)
sections.iter().copied().find(|&c| ctx.chunk_header(c).name == name)
};
let first = sections.first().copied();
@@ -4080,10 +3852,7 @@ pub fn fix_synthetic_symbols<E: Arch>(ctx: &mut Context<E>) {
} else {
// If the symbols are not ncessary, we turn them to absolute
// symbols at address 0.
for sym in [ctx.syms.rel_iplt_start, ctx.syms.rel_iplt_end]
.into_iter()
.flatten()
{
for sym in [ctx.syms.rel_iplt_start, ctx.syms.rel_iplt_end].into_iter().flatten() {
ctx.symbols[sym].clear_origin();
}
}
@@ -4131,11 +3900,7 @@ pub fn fix_synthetic_symbols<E: Arch>(ctx: &mut Context<E>) {
// _GLOBAL_OFFSET_TABLE_. I don't know why, but for the sake of
// compatibility with existing code, it must be set to the beginning of
// .got.plt instead of .got only on i386 and x86-64.
let got = if E::IS_X86 {
ChunkId::GotPlt
} else {
ChunkId::Got
};
let got = if E::IS_X86 { ChunkId::GotPlt } else { ChunkId::Got };
start(ctx, ctx.syms.global_offset_table, Some(got), 0);
// _PROCEDURE_LINKAGE_TABLE_. We need this on SPARC.
@@ -4187,12 +3952,7 @@ pub fn fix_synthetic_symbols<E: Arch>(ctx: &mut Context<E>) {
}
let sym = ctx.get_symbol(label.as_bytes());
if ctx.symbols[sym].file() == ctx.internal_obj.map(FileId::Obj) {
start(
ctx,
Some(sym),
Some(ChunkId::Ppc64SaveRestore),
(i * 4) as i64,
);
start(ctx, Some(sym), Some(ChunkId::Ppc64SaveRestore), (i * 4) as i64);
}
}
}
@@ -4509,10 +4269,8 @@ pub fn write_separate_debug_file<E: Arch>(ctx: &mut Context<E>) {
// Reverse-compute a CRC32 value so that the CRC32 checksum embedded to
// the .gnu_debuglink section in the main executable matches with the
// debug info file's CRC32 checksum.
let trailer = crc32_solve(
crc32_parallel(output.buf()),
ctx.gnu_debuglink.as_ref().unwrap().crc32,
);
let trailer =
crc32_solve(crc32_parallel(output.buf()), ctx.gnu_debuglink.as_ref().unwrap().crc32);
let len = output.len();
output.extend(trailer.len());
output.buf()[len..].copy_from_slice(&trailer);
@@ -4615,24 +4373,15 @@ pub fn show_stats<E: Arch>(ctx: &Context<E>) {
stats.extend([
(
"total_input_bytes",
crate::mapped_file::file_pool()
.iter()
.map(|mf| mf.size() as i64)
.sum(),
),
(
"input_sections",
ctx.objs.iter().map(|file| file.sections.len() as i64).sum(),
crate::mapped_file::file_pool().iter().map(|mf| mf.size() as i64).sum(),
),
("input_sections", ctx.objs.iter().map(|file| file.sections.len() as i64).sum()),
("output_chunks", ctx.chunks.len() as i64),
("num_objs", ctx.objs.len() as i64),
("num_dsos", ctx.dsos.len() as i64),
(
"merged_strings",
ctx.merged_sections
.iter()
.map(|section| section.fragments.len() as i64)
.sum(),
ctx.merged_sections.iter().map(|section| section.fragments.len() as i64).sum(),
),
]);
if E::NEEDS_THUNK {
+4 -10
View File
@@ -93,10 +93,7 @@ fn new_shared_file<E: Arch>(
mf: &'static MappedFile,
) -> SharedFile<E> {
if rctx.is_static {
fatal!(
"{}: attempted static link of a dynamic object",
mf.name.display()
);
fatal!("{}: attempted static link of a dynamic object", mf.name.display());
}
check_machine_type(ctx, mf);
let mut file = SharedFile::<E>::new(mf, &mut ctx.symbol_bin());
@@ -180,13 +177,11 @@ pub fn read_file<E: Arch>(ctx: &mut Context<E>, rctx: &mut ReaderContext, mf: &'
match get_file_type(ctx, mf) {
FileType::ElfObj => {
let file = new_object_file(ctx, rctx, mf, Path::new(""));
ctx.pending_files
.push(Loaded::Obj(rctx.pos.clone(), Box::new(file)));
ctx.pending_files.push(Loaded::Obj(rctx.pos.clone(), Box::new(file)));
}
FileType::ElfDso => {
let file = new_shared_file(ctx, rctx, mf);
ctx.pending_files
.push(Loaded::Dso(rctx.pos.clone(), Box::new(file)));
ctx.pending_files.push(Loaded::Dso(rctx.pos.clone(), Box::new(file)));
}
FileType::Ar | FileType::ThinAr => {
for child in archive_file::read_archive_members(&ctx.args.chroot, mf) {
@@ -424,8 +419,7 @@ pub fn read_input_files<E: Arch>(ctx: &mut Context<E>, jobs: Vec<ReaderJob>) {
lto_jobs.sort_by(|(a, ..), (b, ..)| a.pos.cmp(&b.pos));
for (rctx, mf, archive_name) in lto_jobs {
if let Some(file) = new_lto_object(ctx, &rctx, mf, archive_name) {
ctx.pending_files
.push(Loaded::Obj(rctx.pos, Box::new(file)));
ctx.pending_files.push(Loaded::Obj(rctx.pos, Box::new(file)));
}
}
+1 -3
View File
@@ -109,9 +109,7 @@ fn create_comdat_group_sections<E: Arch>(ctx: &mut Context<E>) {
}
for sec in sections {
ctx.comdat_group_sections.push(sec);
ctx.chunks.push(ChunkId::ComdatGroup(
ctx.comdat_group_sections.len() as u32 - 1,
));
ctx.chunks.push(ChunkId::ComdatGroup(ctx.comdat_group_sections.len() as u32 - 1));
}
}
+1 -4
View File
@@ -55,7 +55,6 @@
//! makes the linker implementation a bit simpler because we don't need to
//! worry about oscillation.
use rayon::prelude::*;
use crate::arch::Arch;
@@ -117,9 +116,7 @@ pub fn shrink_sections<E: Arch>(ctx: &mut Context<E>) {
let Context { objs, .. } = ctx;
objs.par_iter_mut().zip(shrunk).for_each(|(file, shrunk)| {
for (shndx, deltas) in shrunk {
let isec = file
.section_mut(shndx as usize)
.expect("no such input section");
let isec = file.section_mut(shndx as usize).expect("no such input section");
isec.sh_size -= deltas.last().unwrap().delta as u64;
isec.set_r_deltas(deltas.into_boxed_slice());
}
+6 -21
View File
@@ -25,10 +25,7 @@ pub fn fork_child() {
eprintln!("mold: pipe failed");
std::process::exit(1);
}
(
OwnedFd::from_raw_fd(pipefd[0]),
OwnedFd::from_raw_fd(pipefd[1]),
)
(OwnedFd::from_raw_fd(pipefd[0]), OwnedFd::from_raw_fd(pipefd[1]))
};
// SAFETY: this runs before the linker starts its worker threads. The
// parent only waits for completion and exits; the child continues linking.
@@ -109,11 +106,7 @@ extern "C" fn on_signal(
let msg = b"mold: failed to write to an output file. Disk full?\n";
// SAFETY: write is async-signal-safe.
unsafe {
libc::write(
libc::STDERR_FILENO,
msg.as_ptr() as *const libc::c_void,
msg.len(),
);
libc::write(libc::STDERR_FILENO, msg.as_ptr() as *const libc::c_void, msg.len());
}
}
crate::output_file::cleanup();
@@ -159,13 +152,9 @@ pub fn process_run_subcommand(argv: &[std::ffi::OsString]) -> ! {
self_path.parent().map(|p| p.join("mold-wrapper.so")),
// If not found, search $(MOLD_LIBDIR)/mold, which is /usr/local/lib/mold
// by default.
Some(std::path::PathBuf::from(
"/usr/local/lib/mold/mold-wrapper.so",
)),
Some(std::path::PathBuf::from("/usr/local/lib/mold/mold-wrapper.so")),
// Look for ../lib/mold/mold-wrapper.so
self_path
.parent()
.map(|p| p.join("../lib/mold/mold-wrapper.so")),
self_path.parent().map(|p| p.join("../lib/mold/mold-wrapper.so")),
];
// Get the mold-wrapper.so path
let Some(dso) = candidates.into_iter().flatten().find(|p| p.is_file()) else {
@@ -177,14 +166,10 @@ pub fn process_run_subcommand(argv: &[std::ffi::OsString]) -> ! {
std::env::set_var("MOLD_PATH", &self_path);
use std::os::unix::process::CommandExt;
let cmd = std::path::Path::new(&argv[2])
.file_name()
.unwrap_or_default();
let cmd = std::path::Path::new(&argv[2]).file_name().unwrap_or_default();
// If ld, ld.lld or ld.gold is specified, run mold instead
let err = if cmd == "ld" || cmd == "ld.lld" || cmd == "ld.gold" {
std::process::Command::new(&self_path)
.args(&argv[3..])
.exec()
std::process::Command::new(&self_path).args(&argv[3..]).exec()
} else {
std::process::Command::new(&argv[2]).args(&argv[3..]).exec()
};
+11 -33
View File
@@ -134,14 +134,8 @@ pub struct AddrFlags {
}
impl AddrFlags {
pub const NO_PLT: AddrFlags = AddrFlags {
no_plt: true,
no_opd: false,
};
pub const NO_OPD: AddrFlags = AddrFlags {
no_plt: false,
no_opd: true,
};
pub const NO_PLT: AddrFlags = AddrFlags { no_plt: true, no_opd: false };
pub const NO_OPD: AddrFlags = AddrFlags { no_plt: false, no_opd: true };
}
// Rarely used fields for dynamic symbols. Because mold allocates tens of
@@ -772,8 +766,7 @@ impl Symbol {
#[inline]
pub fn has_plt(&self, symbols: &SymbolTable) -> bool {
self.aux(symbols)
.is_some_and(|a| a.plt_idx != u32::MAX || a.pltgot_idx != u32::MAX)
self.aux(symbols).is_some_and(|a| a.plt_idx != u32::MAX || a.pltgot_idx != u32::MAX)
}
#[inline]
@@ -820,8 +813,7 @@ impl Symbol {
&self,
ctx: &'a Context<E>,
) -> Option<&'a InputSection<E>> {
self.input_section()
.map(|section| ctx.input_section(section))
self.input_section().map(|section| ctx.input_section(section))
}
#[inline]
@@ -1035,11 +1027,7 @@ impl Symbol {
if self.has_copyrel() {
std::hint::cold_path();
let chunk = if self.is_copyrel_readonly() {
&ctx.copyrel_relro
} else {
&ctx.copyrel
};
let chunk = if self.is_copyrel_readonly() { &ctx.copyrel_relro } else { &ctx.copyrel };
return chunk.hdr.shdr.sh_addr.get() + self.value;
}
@@ -1628,8 +1616,7 @@ impl SymbolTable {
.collect();
let count: usize = fresh.iter().map(Vec::len).sum();
self.aux
.par_extend((0..count).into_par_iter().map(|_| SymbolAux::default()));
self.aux.par_extend((0..count).into_par_iter().map(|_| SymbolAux::default()));
let mut next = first;
let parts: Vec<_> = fresh
.into_iter()
@@ -1660,9 +1647,7 @@ impl SymbolTable {
pub fn lookup(&self, key: &[u8]) -> Option<SymbolId> {
let hash = hash_key(key);
self.shards[shard_of(hash)]
.get(&Query { hash, key })
.copied()
self.shards[shard_of(hash)].get(&Query { hash, key }).copied()
}
/// Interns the keys recorded in `bins` all at once and hands each slot
@@ -1679,9 +1664,8 @@ impl SymbolTable {
const BLOCK_SIZE: usize = 256;
let count: usize = bins.iter().flat_map(|bin| &bin.0).map(Vec::len).sum();
let first = self.symbols.len();
let capacity = count
.saturating_add(NUM_SHARDS * (BLOCK_SIZE - 1))
.saturating_add(additional_capacity);
let capacity =
count.saturating_add(NUM_SHARDS * (BLOCK_SIZE - 1)).saturating_add(additional_capacity);
let old_capacity = self.symbols.capacity();
self.symbols.reserve(capacity);
if self.symbols.capacity() != old_capacity {
@@ -1824,10 +1808,7 @@ impl SymbolTable {
// SAFETY: reserve made the entire tail available, even though it
// is outside the vector's initialized length.
slots: AtomicPtr::new(unsafe {
self.symbols
.as_mut_ptr()
.add(first)
.cast::<MaybeUninit<Symbol>>()
self.symbols.as_mut_ptr().add(first).cast::<MaybeUninit<Symbol>>()
}),
first,
next: AtomicUsize::new(0),
@@ -1853,10 +1834,7 @@ impl SymbolTable {
/// The ids of all named (global) symbols.
pub fn global_ids(&self) -> impl Iterator<Item = SymbolId> + '_ {
self.globals
.iter()
.flatten()
.flat_map(|range| range.clone().map(SymbolId))
self.globals.iter().flatten().flat_map(|range| range.clone().map(SymbolId))
}
/// Applies `f` to all named symbols in parallel, one task per map shard.
+16 -33
View File
@@ -186,9 +186,7 @@ pub fn create_range_extension_thunks<E: Arch>(ctx: &mut Context<E>, id: OutputSe
// Initialize input sections with a dummy offset so that we can
// distinguish sections whose addresses have been assigned from those
// whose addresses have not.
members
.par_iter()
.for_each(|&member| ctx.input_section(member).set_offset(UNPLACED));
members.par_iter().for_each(|&member| ctx.input_section(member).set_offset(UNPLACED));
let distance = E::branch_distance() as u64;
let batch = batch_size::<E>();
@@ -230,11 +228,7 @@ pub fn create_range_extension_thunks<E: Arch>(ctx: &mut Context<E>, id: OutputSe
});
// Find the first section that is within branch range of C.
let c_offset = if c == d {
offset
} else {
ctx.input_section(members[c]).offset()
};
let c_offset = if c == d { offset } else { ctx.input_section(members[c]).offset() };
a += members[a..b].partition_point(|&member| {
ctx.input_section(member).offset() < c_offset.saturating_sub(distance)
});
@@ -276,12 +270,7 @@ pub fn create_range_extension_thunks<E: Arch>(ctx: &mut Context<E>, id: OutputSe
})
};
// Add symbols to the thunk
let mut thunk = Thunk {
offset,
symbols,
offsets: Vec::new(),
name: String::new(),
};
let mut thunk = Thunk { offset, symbols, offsets: Vec::new(), name: String::new() };
// Now that we know the number of symbols in the thunk, we can compute
// the thunk's size.
thunk.offsets = thunk.fixed_offsets::<E>();
@@ -306,10 +295,7 @@ pub fn create_range_extension_thunks<E: Arch>(ctx: &mut Context<E>, id: OutputSe
thunks.par_iter_mut().for_each(|thunk| {
thunk.symbols.sort_by_key(|&id| {
let sym = &ctx.symbols[id];
(
sym.file().map_or(0, |f| ctx.file(f).priority),
sym.sym_idx(),
)
(sym.file().map_or(0, |f| ctx.file(f).priority), sym.sym_idx())
});
});
}
@@ -342,22 +328,19 @@ pub fn remove_redundant_thunks<E: Arch>(ctx: &mut Context<E>) {
{
let ctx: &Context<E> = ctx;
for &id in &sections {
ctx.output_sections[id.index()]
.members
.par_iter()
.for_each(|&m| {
let isec = ctx.input_section(m);
let file = &ctx.objs[isec.file.index()];
for rel in isec.rels(file) {
if !rel.is_func_call::<E>() {
continue;
}
let sym = &ctx.symbols[file.base.symbols[rel.r_sym() as usize]];
if !sym.is_marked() && requires_thunk(ctx, isec, rel, sym, false) {
sym.mark();
}
ctx.output_sections[id.index()].members.par_iter().for_each(|&m| {
let isec = ctx.input_section(m);
let file = &ctx.objs[isec.file.index()];
for rel in isec.rels(file) {
if !rel.is_func_call::<E>() {
continue;
}
});
let sym = &ctx.symbols[file.base.symbols[rel.r_sym() as usize]];
if !sym.is_marked() && requires_thunk(ctx, isec, rel, sym, false) {
sym.mark();
}
}
});
}
}
+3 -4
View File
@@ -167,10 +167,9 @@ pub fn tp_addr<E: Arch>(phdr: &ElfPhdr<E>) -> u64 {
// On x86, SPARC and s390x, TP (%gs on i386, %fs on x86-64, %g7 on SPARC
// and %a0/%a1 on s390x) refers to past the end of the TLS block for
// historical reasons. TLVs are accessed with negative offsets from TP.
Family::X86_64 | Family::I386 | Family::Sparc64 | Family::S390x => align_to(
phdr.p_vaddr().get() + phdr.p_memsz().get(),
phdr.p_align().get(),
),
Family::X86_64 | Family::I386 | Family::Sparc64 | Family::S390x => {
align_to(phdr.p_vaddr().get() + phdr.p_memsz().get(), phdr.p_align().get())
}
// On ARM and SH4, the runtime appends two words at the beginning
// of TLV template image when copying TLVs to the TLS block, so we need
// to offset it.
-1
View File
@@ -142,7 +142,6 @@ fn zlib_compress(input: &[u8], level: u32) -> Vec<u8> {
impl Compressor {
pub fn zlib(input: &[u8], level: u32) -> Compressor {
// Compress each shard
let (shards, adlers): (Vec<Vec<u8>>, Vec<u32>) = input
.par_chunks(SHARD_SIZE)
+5 -17
View File
@@ -65,10 +65,7 @@ fn allocate_entries<T>(bufsize: usize) -> *mut Entry<T> {
)
};
if entries == libc::MAP_FAILED {
panic!(
"mmap of {bufsize} bytes failed: {}",
std::io::Error::last_os_error()
);
panic!("mmap of {bufsize} bytes failed: {}", std::io::Error::last_os_error());
}
entries.cast()
}
@@ -146,10 +143,7 @@ unsafe impl<T: Send + Sync> Sync for ConcurrentMap<T> {}
impl<T> Default for ConcurrentMap<T> {
/// A map without buckets, to be replaced before use.
fn default() -> Self {
ConcurrentMap {
entries: ptr::null_mut(),
nbuckets: 0,
}
ConcurrentMap { entries: ptr::null_mut(), nbuckets: 0 }
}
}
@@ -168,16 +162,12 @@ impl<T> ConcurrentMap<T> {
}
fn bufsize(nbuckets: usize) -> usize {
std::mem::size_of::<Entry<T>>()
.checked_mul(nbuckets)
.expect("table size overflow")
std::mem::size_of::<Entry<T>>().checked_mul(nbuckets).expect("table size overflow")
}
/// The number of entries, counted.
fn len(&self) -> usize {
(0..self.nbuckets)
.filter(|&idx| self.is_occupied(idx))
.count()
(0..self.nbuckets).filter(|&idx| self.is_occupied(idx)).count()
}
fn entry(&self, idx: usize) -> &Entry<T> {
@@ -458,9 +448,7 @@ impl<T> FrozenMap<T> {
(0..NUM_SHARDS)
.into_par_iter()
.flat_map_iter(|shard| {
self.sorted_entries(shard)
.into_iter()
.map(|id| self.entry_ref(id))
self.sorted_entries(shard).into_iter().map(|id| self.entry_ref(id))
})
.collect()
}
+1 -4
View File
@@ -51,10 +51,7 @@ mod tests {
#[test]
fn rust_legacy_with_suffix() {
assert_eq!(
demangle_rust(b"_ZN2ns7versionEv").as_deref(),
Some("ns::versionv")
);
assert_eq!(demangle_rust(b"_ZN2ns7versionEv").as_deref(), Some("ns::versionv"));
assert_eq!(demangle_rust(b"_ZN3foo3barE").as_deref(), Some("foo::bar"));
assert_eq!(demangle_rust(b"_ZN3foo").as_deref(), None);
assert_eq!(demangle_rust(b"main"), None);
+1 -5
View File
@@ -191,11 +191,7 @@ impl<E: Endian> U24<E> {
#[inline(always)]
pub fn set(&mut self, value: u32) {
let bytes = if E::IS_LITTLE {
value.to_le_bytes()
} else {
value.to_be_bytes()
};
let bytes = if E::IS_LITTLE { value.to_le_bytes() } else { value.to_be_bytes() };
if E::IS_LITTLE {
self.bytes.copy_from_slice(&bytes[..3]);
} else {
+12 -41
View File
@@ -328,13 +328,7 @@ struct TrieNode {
impl Default for TrieNode {
fn default() -> Self {
TrieNode {
value: -1,
suffix_link: -1,
first_child: -1,
next_sibling: -1,
ch: 0,
}
TrieNode { value: -1, suffix_link: -1, first_child: -1, next_sibling: -1, ch: 0 }
}
}
@@ -366,11 +360,7 @@ impl AhoCorasick {
}
let child = self.nodes.len() as i32;
let sibling = self.nodes[node as usize].first_child;
self.nodes.push(TrieNode {
next_sibling: sibling,
ch,
..TrieNode::default()
});
self.nodes.push(TrieNode { next_sibling: sibling, ch, ..TrieNode::default() });
self.nodes[node as usize].first_child = child;
if node == 0 {
self.root_children[ch as usize] = child;
@@ -542,24 +532,15 @@ impl GlobBuilder {
return true;
}
if is_literal(pat) {
self.glob.exacts.push(Literal {
pat: pat.to_vec(),
value,
});
self.glob.exacts.push(Literal { pat: pat.to_vec(), value });
return true;
}
if let Some(prefix) = pat.strip_suffix(b"*").filter(|p| is_literal(p)) {
self.glob.prefixes.push(Literal {
pat: prefix.to_vec(),
value,
});
self.glob.prefixes.push(Literal { pat: prefix.to_vec(), value });
return true;
}
if let Some(suffix) = pat.strip_prefix(b"*").filter(|p| is_literal(p)) {
self.glob.suffixes.push(Literal {
pat: suffix.to_vec(),
value,
});
self.glob.suffixes.push(Literal { pat: suffix.to_vec(), value });
return true;
}
// If the pattern requires only a single substring search, the
@@ -585,8 +566,7 @@ impl GlobBuilder {
}
// For duplicate names, retain the largest value.
glob.exacts
.sort_by(|a, b| a.pat.cmp(&b.pat).then(b.value.cmp(&a.value)));
glob.exacts.sort_by(|a, b| a.pat.cmp(&b.pat).then(b.value.cmp(&a.value)));
glob.exacts.dedup_by(|a, b| a.pat == b.pat);
if glob.patterns.len() >= 64 {
glob.nfa = Nfa::compile(&glob.patterns);
@@ -674,24 +654,15 @@ mod tests {
#[test]
fn shared_priorities_preserve_highest_match() {
let mut g = GlobBuilder::default();
for (pattern, priority) in [
("*", 0),
("*inner*", 2),
("prefix*", 2),
("*suffix", 2),
("exact", 3),
("p?efix*", 4),
] {
for (pattern, priority) in
[("*", 0), ("*inner*", 2), ("prefix*", 2), ("*suffix", 2), ("exact", 3), ("p?efix*", 4)]
{
assert!(g.add(pattern.as_bytes(), priority));
}
let g = g.build();
for (name, expected) in [
("none", 0),
("hasinnersuffix", 2),
("suffix", 2),
("exact", 3),
("prefixsuffix", 4),
] {
for (name, expected) in
[("none", 0), ("hasinnersuffix", 2), ("suffix", 2), ("exact", 3), ("prefixsuffix", 4)]
{
assert_eq!(g.find(name.as_bytes()), expected);
}
let mut g = GlobBuilder::default();
+2 -8
View File
@@ -36,20 +36,14 @@ impl HyperLogLog {
#[inline]
pub fn cardinality(&self) -> u64 {
let z: f64 = self
.registers
.iter()
.map(|&r| 2f64.powi(-i32::from(r)))
.sum();
let z: f64 = self.registers.iter().map(|&r| 2f64.powi(-i32::from(r))).sum();
(Self::ALPHA * (Self::NUM_REGISTERS * Self::NUM_REGISTERS) as f64 / z) as u64
}
}
impl Default for HyperLogLog {
fn default() -> Self {
HyperLogLog {
registers: [0; Self::NUM_REGISTERS],
}
HyperLogLog { registers: [0; Self::NUM_REGISTERS] }
}
}
+3 -11
View File
@@ -212,11 +212,7 @@ pub(crate) fn read_sleb(bytes: &mut &[u8]) -> i64 {
}
shift += 7;
if byte & 0x80 == 0 {
return if shift < 64 {
sign_extend(value, shift)
} else {
value as i64
};
return if shift < 64 { sign_extend(value, shift) } else { value as i64 };
}
}
}
@@ -243,18 +239,14 @@ pub(crate) fn leak_bytes(bytes: Vec<u8>) -> &'static [u8] {
/// Normalizes a path lexically, resolving `.` and `..` components without
/// consulting the file system.
pub(crate) fn path_clean(path: &str) -> String {
clean_path(std::path::Path::new(path))
.to_string_lossy()
.into_owned()
clean_path(std::path::Path::new(path)).to_string_lossy().into_owned()
}
/// Converts bytes from a response file or linker script to an OS string.
/// Unix paths can contain arbitrary non-NUL bytes.
pub(crate) fn os_str(bytes: &[u8]) -> &std::ffi::OsStr {
use bstr::ByteSlice;
bytes
.to_os_str()
.unwrap_or_else(|_| crate::fatal!("invalid OS string: {}", display(bytes)))
bytes.to_os_str().unwrap_or_else(|_| crate::fatal!("invalid OS string: {}", display(bytes)))
}
/// Normalizes an OS path without resolving symlinks.
+10 -19
View File
@@ -44,10 +44,8 @@ pub(crate) fn stable_partition<T: Copy + Send + Sync>(
} else {
values.par_iter().map(&pred).collect()
};
let counts: Vec<usize> = matches
.par_chunks(BLOCK)
.map(|chunk| chunk.iter().filter(|&&v| v).count())
.collect();
let counts: Vec<usize> =
matches.par_chunks(BLOCK).map(|chunk| chunk.iter().filter(|&&v| v).count()).collect();
let num_matches = counts.iter().sum();
if num_matches == 0 || num_matches == values.len() {
return num_matches;
@@ -66,11 +64,8 @@ pub(crate) fn stable_partition<T: Copy + Send + Sync>(
(matched, unmatched)
})
.collect();
parts
.into_par_iter()
.zip(values.par_chunks(BLOCK))
.zip(matches.par_chunks(BLOCK))
.for_each(|(((yes, no), input), flags)| {
parts.into_par_iter().zip(values.par_chunks(BLOCK)).zip(matches.par_chunks(BLOCK)).for_each(
|(((yes, no), input), flags)| {
let (mut y, mut n) = (0, 0);
for (&value, &matched) in input.iter().zip(flags) {
if matched {
@@ -81,7 +76,8 @@ pub(crate) fn stable_partition<T: Copy + Send + Sync>(
n += 1;
}
}
});
},
);
values
.par_chunks_mut(BLOCK)
.zip(output.par_chunks(BLOCK))
@@ -95,15 +91,10 @@ mod tests {
#[test]
fn background_job_joins_on_one_worker() {
rayon::ThreadPoolBuilder::new()
.num_threads(1)
.build()
.unwrap()
.install(|| {
let job =
Background::spawn("test", || (0..100usize).into_par_iter().sum::<usize>());
assert_eq!(job.join(), 4950);
});
rayon::ThreadPoolBuilder::new().num_threads(1).build().unwrap().install(|| {
let job = Background::spawn("test", || (0..100usize).into_par_iter().sum::<usize>());
assert_eq!(job.join(), 4950);
});
}
#[test]
+9 -39
View File
@@ -19,11 +19,7 @@ pub struct Counter {
impl Counter {
pub const fn new(name: &'static str) -> Counter {
Counter {
name,
value: AtomicI64::new(0),
registered: Once::new(),
}
Counter { name, value: AtomicI64::new(0), registered: Once::new() }
}
pub fn enable() {
@@ -152,13 +148,7 @@ fn rusage() -> (f64, f64) {
// SAFETY: all FILETIME pointers are valid outputs and the pseudo-handle
// returned by GetCurrentProcess is always valid in this process.
unsafe {
GetProcessTimes(
GetCurrentProcess(),
&mut creation,
&mut exit,
&mut kernel,
&mut user,
);
GetProcessTimes(GetCurrentProcess(), &mut creation, &mut exit, &mut kernel, &mut user);
}
let to_secs = |time: FileTime| {
let ticks = (u64::from(time.high) << 32) | u64::from(time.low);
@@ -175,9 +165,7 @@ impl Default for Timers {
impl Timers {
pub fn new() -> Self {
Timers {
records: Some(Arc::new(Mutex::new(Vec::new()))),
}
Timers { records: Some(Arc::new(Mutex::new(Vec::new()))) }
}
/// Skips clock reads, system calls and shared recording when --perf is off.
@@ -186,11 +174,7 @@ impl Timers {
}
fn inactive(&self) -> Timer {
Timer {
timers: self.clone(),
index: 0,
stopped: true,
}
Timer { timers: self.clone(), index: 0, stopped: true }
}
/// Starts a timer whose nesting is inferred from completed time intervals.
@@ -216,11 +200,7 @@ impl Timers {
parent,
children: Vec::new(),
});
Timer {
timers: self.clone(),
index,
stopped: false,
}
Timer { timers: self.clone(), index, stopped: false }
}
fn stop(&self, index: usize) {
@@ -282,10 +262,7 @@ impl Timer {
}
pub fn handle(&self) -> TimerHandle {
TimerHandle {
timers: self.timers.clone(),
parent: self.index,
}
TimerHandle { timers: self.timers.clone(), parent: self.index }
}
pub fn stop(&mut self) {
@@ -326,9 +303,7 @@ mod tests {
let mut records = timers.records.as_ref().unwrap().lock().unwrap();
let start = records[0].start;
for (record, (begin, end)) in
records
.iter_mut()
.zip([(0, 100), (10, 40), (15, 25), (20, 70), (50, 80), (60, 65)])
records.iter_mut().zip([(0, 100), (10, 40), (15, 25), (20, 70), (50, 80), (60, 65)])
{
record.start = start + std::time::Duration::from_millis(begin);
record.end = Some(start + std::time::Duration::from_millis(end));
@@ -336,10 +311,7 @@ mod tests {
nest_records(&mut records);
let children: Vec<_> = records.iter().map(|r| r.children.clone()).collect();
drop(records);
assert_eq!(
children,
[vec![1, 3, 4, 5], vec![2], vec![], vec![], vec![], vec![]]
);
assert_eq!(children, [vec![1, 3, 4, 5], vec![2], vec![], vec![], vec![], vec![]]);
}
#[test]
@@ -348,9 +320,7 @@ mod tests {
let mut root = timers.start("root");
let child = root.child("child");
let handle = root.handle();
std::thread::spawn(move || drop(handle.child("background")))
.join()
.unwrap();
std::thread::spawn(move || drop(handle.child("background"))).join().unwrap();
drop(child);
root.stop();
root.stop();
+3 -8
View File
@@ -78,24 +78,19 @@ fn encode_path(basedir: &OsStr, path: &Path) -> Vec<u8> {
impl TarWriter {
pub fn open(output_path: &Path, basedir: &OsStr) -> io::Result<TarWriter> {
Ok(TarWriter {
out: File::create(output_path)?,
basedir: basedir.to_os_string(),
})
Ok(TarWriter { out: File::create(output_path)?, basedir: basedir.to_os_string() })
}
pub fn append(&mut self, path: &Path, data: &[u8]) -> io::Result<()> {
let attr = encode_path(&self.basedir, path);
// Write PAX header
self.out
.write_all(&ustar_header(b"/", b"", attr.len() as u64, b'x'))?;
self.out.write_all(&ustar_header(b"/", b"", attr.len() as u64, b'x'))?;
// Write pathname
self.out.write_all(&attr)?;
self.pad()?;
// Write Ustar header
self.out
.write_all(&ustar_header(b"", b"0000664", data.len() as u64, b'0'))?;
self.out.write_all(&ustar_header(b"", b"0000664", data.len() as u64, b'0'))?;
// Write file contents
self.out.write_all(data)?;
self.pad()?;
+6 -21
View File
@@ -10,34 +10,24 @@ pub(crate) struct WorkerLocal<T>(Vec<Worker<T>>);
impl<T> WorkerLocal<T> {
pub fn new(init: impl Fn() -> T) -> Self {
Self(
(0..=rayon::current_num_threads())
.map(|_| Worker(Mutex::new(init())))
.collect(),
)
Self((0..=rayon::current_num_threads()).map(|_| Worker(Mutex::new(init()))).collect())
}
pub fn get(&self) -> MutexGuard<'_, T> {
let fallback = self.0.len() - 1;
let worker = rayon::current_thread_index()
.unwrap_or(fallback)
.min(fallback);
let worker = rayon::current_thread_index().unwrap_or(fallback).min(fallback);
self.0[worker].0.lock().unwrap()
}
pub fn into_values(self) -> impl Iterator<Item = T> {
self.0
.into_iter()
.map(|worker| worker.0.into_inner().unwrap())
self.0.into_iter().map(|worker| worker.0.into_inner().unwrap())
}
pub fn take(&mut self) -> impl Iterator<Item = T> + '_
where
T: Default,
{
self.0
.iter_mut()
.map(|worker| std::mem::take(worker.0.get_mut().unwrap()))
self.0.iter_mut().map(|worker| std::mem::take(worker.0.get_mut().unwrap()))
}
}
@@ -48,16 +38,11 @@ mod tests {
#[test]
fn reuses_worker_storage_and_keeps_an_outside_slot() {
let pool = rayon::ThreadPoolBuilder::new()
.num_threads(2)
.build()
.unwrap();
let pool = rayon::ThreadPoolBuilder::new().num_threads(2).build().unwrap();
let mut slots = pool.install(|| {
let slots = WorkerLocal::new(Vec::new);
for batch in 0..3 {
(0..100)
.into_par_iter()
.for_each(|i| slots.get().push(batch * 100 + i));
(0..100).into_par_iter().for_each(|i| slots.get().push(batch * 100 + i));
}
slots
});
+43 -173
View File
@@ -30,86 +30,22 @@ struct TargetSpec {
// Keep this list in the same order as mold's test/CMakeLists.txt.
const TARGETS: &[TargetSpec] = &[
TargetSpec {
machine: "x86_64",
triple: "x86_64-linux-gnu",
qemu: "qemu-x86_64",
},
TargetSpec {
machine: "i686",
triple: "i686-linux-gnu",
qemu: "qemu-i386",
},
TargetSpec {
machine: "aarch64",
triple: "aarch64-linux-gnu",
qemu: "qemu-aarch64",
},
TargetSpec {
machine: "aarch64_be",
triple: "aarch64_be-linux-gnu",
qemu: "qemu-aarch64_be",
},
TargetSpec {
machine: "arm",
triple: "arm-linux-gnueabihf",
qemu: "qemu-arm",
},
TargetSpec {
machine: "armeb",
triple: "armeb-linux-gnueabihf",
qemu: "qemu-armeb",
},
TargetSpec {
machine: "riscv64",
triple: "riscv64-linux-gnu",
qemu: "qemu-riscv64",
},
TargetSpec {
machine: "riscv32",
triple: "riscv32-linux-gnu",
qemu: "qemu-riscv32",
},
TargetSpec {
machine: "ppc",
triple: "powerpc-linux-gnu",
qemu: "qemu-ppc",
},
TargetSpec {
machine: "ppc64",
triple: "powerpc64-linux-gnu",
qemu: "qemu-ppc64",
},
TargetSpec {
machine: "ppc64le",
triple: "powerpc64le-linux-gnu",
qemu: "qemu-ppc64le",
},
TargetSpec {
machine: "sparc64",
triple: "sparc64-linux-gnu",
qemu: "qemu-sparc64",
},
TargetSpec {
machine: "s390x",
triple: "s390x-linux-gnu",
qemu: "qemu-s390x",
},
TargetSpec {
machine: "sh4",
triple: "sh4-linux-gnu",
qemu: "qemu-sh4",
},
TargetSpec {
machine: "sh4aeb",
triple: "sh4aeb-linux-gnu",
qemu: "qemu-sh4eb",
},
TargetSpec {
machine: "m68k",
triple: "m68k-linux-gnu",
qemu: "qemu-m68k",
},
TargetSpec { machine: "x86_64", triple: "x86_64-linux-gnu", qemu: "qemu-x86_64" },
TargetSpec { machine: "i686", triple: "i686-linux-gnu", qemu: "qemu-i386" },
TargetSpec { machine: "aarch64", triple: "aarch64-linux-gnu", qemu: "qemu-aarch64" },
TargetSpec { machine: "aarch64_be", triple: "aarch64_be-linux-gnu", qemu: "qemu-aarch64_be" },
TargetSpec { machine: "arm", triple: "arm-linux-gnueabihf", qemu: "qemu-arm" },
TargetSpec { machine: "armeb", triple: "armeb-linux-gnueabihf", qemu: "qemu-armeb" },
TargetSpec { machine: "riscv64", triple: "riscv64-linux-gnu", qemu: "qemu-riscv64" },
TargetSpec { machine: "riscv32", triple: "riscv32-linux-gnu", qemu: "qemu-riscv32" },
TargetSpec { machine: "ppc", triple: "powerpc-linux-gnu", qemu: "qemu-ppc" },
TargetSpec { machine: "ppc64", triple: "powerpc64-linux-gnu", qemu: "qemu-ppc64" },
TargetSpec { machine: "ppc64le", triple: "powerpc64le-linux-gnu", qemu: "qemu-ppc64le" },
TargetSpec { machine: "sparc64", triple: "sparc64-linux-gnu", qemu: "qemu-sparc64" },
TargetSpec { machine: "s390x", triple: "s390x-linux-gnu", qemu: "qemu-s390x" },
TargetSpec { machine: "sh4", triple: "sh4-linux-gnu", qemu: "qemu-sh4" },
TargetSpec { machine: "sh4aeb", triple: "sh4aeb-linux-gnu", qemu: "qemu-sh4eb" },
TargetSpec { machine: "m68k", triple: "m68k-linux-gnu", qemu: "qemu-m68k" },
TargetSpec {
machine: "loongarch64",
triple: "loongarch64-linux-gnu",
@@ -127,21 +63,11 @@ struct Target {
impl Target {
fn native(machine: String) -> Target {
Target {
label: machine.clone(),
machine,
triple: None,
cpu: None,
}
Target { label: machine.clone(), machine, triple: None, cpu: None }
}
fn cross(machine: String, triple: String) -> Target {
Target {
label: machine.clone(),
machine,
triple: Some(triple),
cpu: None,
}
Target { label: machine.clone(), machine, triple: Some(triple), cpu: None }
}
}
@@ -228,10 +154,7 @@ fn usage() -> ! {
}
fn parse_usize(value: Option<String>) -> usize {
value
.and_then(|s| s.parse().ok())
.filter(|&n| n != 0)
.unwrap_or_else(|| usage())
value.and_then(|s| s.parse().ok()).filter(|&n| n != 0).unwrap_or_else(|| usage())
}
fn parse_options() -> Options {
@@ -293,14 +216,7 @@ fn parse_options() -> Options {
}
}
Options {
jobs,
mode,
cpu,
patterns,
timeout,
list,
}
Options { jobs, mode, cpu, patterns, timeout, list }
}
fn canonical_machine(machine: &str) -> String {
@@ -383,10 +299,7 @@ fn all_targets(native: &str) -> (Vec<Target>, Vec<&'static TargetSpec>) {
if spec.machine == native {
targets.push(Target::native(native.to_owned()));
} else if command_exists(spec.qemu) && command_exists(&format!("{}-gcc", spec.triple)) {
targets.push(Target::cross(
spec.machine.to_owned(),
spec.triple.to_owned(),
));
targets.push(Target::cross(spec.machine.to_owned(), spec.triple.to_owned()));
} else {
unavailable.push(spec);
}
@@ -452,10 +365,7 @@ fn clear_results(dir: &Path) -> io::Result<()> {
fs::create_dir_all(dir)?;
for entry in fs::read_dir(dir)? {
let path = entry?.path();
if matches!(
path.extension().and_then(OsStr::to_str),
Some("log" | "status")
) {
if matches!(path.extension().and_then(OsStr::to_str), Some("log" | "status")) {
fs::remove_file(path)?;
}
}
@@ -558,9 +468,8 @@ fn log_says_skipped(path: &Path) -> bool {
fn run_process(root: &Path, job: &TestJob, timeout: Duration) -> Result<Outcome, String> {
let log = File::create(&job.log)
.map_err(|err| format!("cannot create {}: {err}", job.log.display()))?;
let stderr = log
.try_clone()
.map_err(|err| format!("cannot clone {}: {err}", job.log.display()))?;
let stderr =
log.try_clone().map_err(|err| format!("cannot clone {}: {err}", job.log.display()))?;
let mut command = Command::new(&job.script);
command
.current_dir(root)
@@ -578,15 +487,11 @@ fn run_process(root: &Path, job: &TestJob, timeout: Duration) -> Result<Outcome,
// A timeout must also kill compiler and QEMU children.
#[cfg(unix)]
command.process_group(0);
let mut child = command
.spawn()
.map_err(|err| format!("cannot run {}: {err}", job.script.display()))?;
let mut child =
command.spawn().map_err(|err| format!("cannot run {}: {err}", job.script.display()))?;
let start = Instant::now();
loop {
match child
.try_wait()
.map_err(|err| format!("cannot wait for test: {err}"))?
{
match child.try_wait().map_err(|err| format!("cannot wait for test: {err}"))? {
Some(status) => {
return Ok(if !status.success() {
Outcome::Fail
@@ -620,10 +525,7 @@ fn run_job(root: &Path, job: &TestJob, timeout: Duration) -> TestResult {
// Keep failed test directories for diagnosis, but do not retain the
// successful tests' potentially large temporary files.
if matches!(outcome, Outcome::Pass | Outcome::Skip) {
let dir = root
.join("out/test")
.join(&job.target.label)
.join(&job.name);
let dir = root.join("out/test").join(&job.target.label).join(&job.name);
match fs::remove_dir_all(&dir) {
Ok(()) => {}
Err(err) if err.kind() == io::ErrorKind::NotFound => {}
@@ -635,11 +537,7 @@ fn run_job(root: &Path, job: &TestJob, timeout: Duration) -> TestResult {
}
if let Err(err) = fs::write(&job.status_file, format!("{}\n", outcome.status())) {
eprintln!(
"{}: cannot write {}: {err}",
job.name,
job.status_file.display()
);
eprintln!("{}: cannot write {}: {err}", job.name, job.status_file.display());
}
TestResult {
target: Arc::clone(&job.target),
@@ -681,11 +579,7 @@ fn run_jobs(root: &Path, jobs: Vec<TestJob>, options: &Options) -> Vec<TestResul
"FAIL {}:{}{} ({})",
result.target.label,
result.name,
if result.outcome == Outcome::Timeout {
" [timeout]"
} else {
""
},
if result.outcome == Outcome::Timeout { " [timeout]" } else { "" },
result.log.display()
);
}
@@ -708,11 +602,8 @@ fn print_inventory(jobs: &[TestJob], unavailable: &[&TargetSpec]) {
}
println!("total: tests={}", jobs.len());
if !unavailable.is_empty() {
let targets = unavailable
.iter()
.map(|target| target.machine)
.collect::<Vec<_>>()
.join(", ");
let targets =
unavailable.iter().map(|target| target.machine).collect::<Vec<_>>().join(", ");
println!("unavailable: {targets}");
}
}
@@ -720,30 +611,18 @@ fn print_inventory(jobs: &[TestJob], unavailable: &[&TargetSpec]) {
fn print_summary(results: &[TestResult]) -> bool {
let mut by_target: BTreeMap<&str, Counts> = BTreeMap::new();
for result in results {
by_target
.entry(&result.target.label)
.or_default()
.add(result.outcome);
by_target.entry(&result.target.label).or_default().add(result.outcome);
}
let mut total = Counts::default();
for (target, counts) in &by_target {
println!(
"{target}: pass={} skip={} fail={}",
counts.pass, counts.skip, counts.fail
);
println!("{target}: pass={} skip={} fail={}", counts.pass, counts.skip, counts.fail);
total.merge(counts);
}
if by_target.len() > 1 {
println!(
"total: pass={} skip={} fail={}",
total.pass, total.skip, total.fail
);
println!("total: pass={} skip={} fail={}", total.pass, total.skip, total.fail);
} else {
println!(
"pass={} skip={} fail={}",
total.pass, total.skip, total.fail
);
println!("pass={} skip={} fail={}", total.pass, total.skip, total.fail);
}
total.fail == 0
}
@@ -755,28 +634,19 @@ pub fn run(cases_dirs: &[PathBuf], mold: &Path) -> ExitCode {
std::process::exit(1);
});
let (targets, unavailable) = selected_targets(&options);
let jobs = make_jobs(
cases_dirs,
&work_dir,
targets,
&options.patterns,
!options.list,
)
.unwrap_or_else(|err| {
eprintln!("mold-tests: {err}");
std::process::exit(1);
});
let jobs = make_jobs(cases_dirs, &work_dir, targets, &options.patterns, !options.list)
.unwrap_or_else(|err| {
eprintln!("mold-tests: {err}");
std::process::exit(1);
});
if options.list {
print_inventory(&jobs, &unavailable);
return ExitCode::SUCCESS;
}
if options.mode == Mode::All && !unavailable.is_empty() {
let targets = unavailable
.iter()
.map(|target| target.machine)
.collect::<Vec<_>>()
.join(", ");
let targets =
unavailable.iter().map(|target| target.machine).collect::<Vec<_>>().join(", ");
eprintln!("skipping targets without both compiler and QEMU: {targets}");
}