mirror of
https://github.com/rui314/mold.git
synced 2026-11-08 17:48:41 -10:00
Keep SHF_LINK_ORDER sections alive with their linked sections
An SHF_LINK_ORDER section such as __patchable_function_entries holds metadata for the section that its sh_link refers to, and usually nothing else refers to it. --gc-sections therefore discarded all of them except .ARM.exidx, for which there was an ARM-specific edge from a section to its unwind index. Replace that edge with a generic one from a section to the SHF_LINK_ORDER sections linked to it, as GNU ld and lld do.
This commit is contained in:
1 parent
b2c3d1dc67
commit
49129fbd59
5 files changed
+38
-69
No files matched your search
+4
-4
@@ -209,10 +209,10 @@ fn visit_section<'scope, E: Target>(
|
||||
}
|
||||
}
|
||||
|
||||
if E::FAMILY == Family::Arm32
|
||||
&& let Some(exidx) = isec.exidx()
|
||||
{
|
||||
mark(file.section_at(exidx));
|
||||
// SHF_LINK_ORDER sections, such as .ARM.exidx, are metadata for the
|
||||
// sections they are linked to and are alive as long as those are.
|
||||
for shndx in file.link_order_dependents(isec.shndx) {
|
||||
mark(file.section_at(shndx));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+19
-18
@@ -731,6 +731,10 @@ pub struct ObjectFile<E: Target> {
|
||||
pub sframe_sections: Vec<u32>,
|
||||
pub sframe_fdes: Vec<SFrameFde>,
|
||||
|
||||
/// SHF_LINK_ORDER sections as pairs of sh_link and section index,
|
||||
/// sorted by sh_link.
|
||||
link_order_sections: Vec<(u32, u32)>,
|
||||
|
||||
pub exclude_libs: bool,
|
||||
pub gnu_properties: BTreeMap<u32, u32>,
|
||||
pub needs_executable_stack: bool,
|
||||
@@ -1035,6 +1039,7 @@ impl<E: Target> ObjectFile<E> {
|
||||
eh_frame_sections: Vec::new(),
|
||||
sframe_sections: Vec::new(),
|
||||
sframe_fdes: Vec::new(),
|
||||
link_order_sections: Vec::new(),
|
||||
exclude_libs: false,
|
||||
gnu_properties: BTreeMap::new(),
|
||||
needs_executable_stack: false,
|
||||
@@ -1147,6 +1152,16 @@ impl<E: Target> ObjectFile<E> {
|
||||
self.section(shndx as usize).expect("no such input section")
|
||||
}
|
||||
|
||||
/// The indices of the SHF_LINK_ORDER sections linked to the section
|
||||
/// at `shndx`.
|
||||
pub fn link_order_dependents(&self, shndx: u32) -> impl Iterator<Item = u32> + '_ {
|
||||
let begin = self.link_order_sections.partition_point(|&(link, _)| link < shndx);
|
||||
self.link_order_sections[begin..]
|
||||
.iter()
|
||||
.take_while(move |&&(link, _)| link == shndx)
|
||||
.map(|&(_, i)| i)
|
||||
}
|
||||
|
||||
/// Iterates over relocations without materializing a deferred CREL table.
|
||||
#[inline(always)]
|
||||
pub(crate) fn relocation_iter(
|
||||
@@ -1700,6 +1715,9 @@ impl<E: Target> ObjectFile<E> {
|
||||
if E::FAMILY == Family::Ppc32 && name == b".got2" {
|
||||
self.got2 = Some(i as u32);
|
||||
}
|
||||
if flags & SHF_LINK_ORDER as u64 != 0 {
|
||||
self.link_order_sections.push((shdr.sh_link.get(), i as u32));
|
||||
}
|
||||
|
||||
if args.gdb_index {
|
||||
// Save debug sections for --gdb-index.
|
||||
@@ -1759,24 +1777,7 @@ impl<E: Target> ObjectFile<E> {
|
||||
isec.set_relsec(i as u32, has_relocs);
|
||||
}
|
||||
|
||||
// Attach .arm.exidx sections to their corresponding sections
|
||||
if E::FAMILY == Family::Arm32 {
|
||||
let pairs: Vec<(usize, usize)> = self
|
||||
.input_sections()
|
||||
.filter(|isec| isec.sh_type(self) == SHT_ARM_EXIDX)
|
||||
.map(|isec| {
|
||||
(
|
||||
self.base.shdrs[isec.shndx as usize].sh_link.get() as usize,
|
||||
isec.shndx as usize,
|
||||
)
|
||||
})
|
||||
.collect();
|
||||
for (target, exidx) in pairs {
|
||||
if let Some(isec) = self.section_mut(target) {
|
||||
isec.set_exidx(exidx as u32);
|
||||
}
|
||||
}
|
||||
}
|
||||
self.link_order_sections.sort_unstable();
|
||||
}
|
||||
|
||||
// Relocations are usually sorted by r_offset in relocation tables,
|
||||
|
||||
+1
-46
@@ -139,21 +139,6 @@ const NO_FDE: u32 = u32::MAX;
|
||||
|
||||
/// Target-specific state embedded in an input section.
|
||||
pub trait InputSectionExtra: fmt::Debug + Default + Send + Sync + 'static {
|
||||
/// Creates empty metadata before target-specific parsing.
|
||||
#[inline]
|
||||
fn empty() -> Self {
|
||||
Self::default()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn exidx(&self) -> Option<u32> {
|
||||
None
|
||||
}
|
||||
|
||||
fn set_exidx(&mut self, _exidx: u32) {
|
||||
unreachable!("only ARM32 input sections have exidx metadata")
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn r_deltas(&self) -> &[RelocDelta] {
|
||||
&[]
|
||||
@@ -166,27 +151,6 @@ pub trait InputSectionExtra: fmt::Debug + Default + Send + Sync + 'static {
|
||||
|
||||
impl InputSectionExtra for () {}
|
||||
|
||||
/// ARM32's link from a code section to its `.ARM.exidx` section.
|
||||
/// `u32::MAX` means that there is no associated section.
|
||||
impl InputSectionExtra for u32 {
|
||||
#[inline]
|
||||
fn empty() -> Self {
|
||||
Self::MAX
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn exidx(&self) -> Option<u32> {
|
||||
(*self != Self::MAX).then_some(*self)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn set_exidx(&mut self, exidx: u32) {
|
||||
debug_assert_ne!(exidx, 0);
|
||||
debug_assert_ne!(exidx, Self::MAX);
|
||||
*self = exidx;
|
||||
}
|
||||
}
|
||||
|
||||
/// Relaxation bookkeeping embedded only for RISC-V and LoongArch.
|
||||
impl InputSectionExtra for Box<[RelocDelta]> {
|
||||
#[inline]
|
||||
@@ -316,7 +280,7 @@ impl<E: Target> InputSection<E> {
|
||||
relsec_idx: NO_RELSEC,
|
||||
fde_begin: NO_FDE,
|
||||
flags: AtomicU8::new(IS_ALIVE),
|
||||
extra: E::InputSectionExtra::empty(),
|
||||
extra: E::InputSectionExtra::default(),
|
||||
};
|
||||
|
||||
// Sections may have been compressed. We usually uncompress them
|
||||
@@ -717,15 +681,6 @@ impl<E: Target> InputSection<E> {
|
||||
self.set_offset(leader.encode());
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn exidx(&self) -> Option<u32> {
|
||||
self.extra.exidx()
|
||||
}
|
||||
|
||||
pub fn set_exidx(&mut self, exidx: u32) {
|
||||
self.extra.set_exidx(exidx);
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn r_deltas(&self) -> &[RelocDelta] {
|
||||
self.extra.r_deltas()
|
||||
|
||||
+1
-1
@@ -274,7 +274,7 @@ impl<const LE: bool> Target for Arm32Target<LE> {
|
||||
type Chdr = Elf32Chdr<Self>;
|
||||
type Rel = ElfRelNoAddend<Self>;
|
||||
|
||||
type InputSectionExtra = u32;
|
||||
type InputSectionExtra = ();
|
||||
|
||||
const NAME: &'static str = if Self::IS_LITTLE { "arm32" } else { "arm32be" };
|
||||
const FAMILY: Family = Family::Arm32;
|
||||
|
||||
Executable
+13
@@ -0,0 +1,13 @@
|
||||
#!/usr/bin/env bash
|
||||
. $(dirname $0)/common.inc
|
||||
|
||||
cat <<EOF | $CC -c -o $t/a.o -xc - -ffunction-sections -fpatchable-function-entry=1
|
||||
int foo() { return 3; }
|
||||
int bar() { return 5; }
|
||||
int main() { return foo() - 3; }
|
||||
EOF
|
||||
|
||||
$CC -B. -o $t/exe $t/a.o -Wl,--gc-sections
|
||||
$QEMU $t/exe
|
||||
readelf -W --sections $t/exe | grep -F __patchable_function_entries
|
||||
nm $t/exe | not grep -w bar
|
||||
Reference in new issue
Block a user