From 45b7c66099971154bccb6466c6609651d72ab6b3 Mon Sep 17 00:00:00 2001 From: David Lattimore Date: Thu, 18 Jun 2026 16:08:00 +1000 Subject: [PATCH 1/6] refactor: Use a separate SectionSlot for sorted sections --- libwild/src/elf_writer.rs | 20 ++------------ libwild/src/layout.rs | 52 +++++++++++++++++++++---------------- libwild/src/layout_rules.rs | 17 ++++++------ libwild/src/resolution.rs | 24 ++++++----------- 4 files changed, 48 insertions(+), 65 deletions(-) diff --git a/libwild/src/elf_writer.rs b/libwild/src/elf_writer.rs index 968f78125..943fe3df4 100644 --- a/libwild/src/elf_writer.rs +++ b/libwild/src/elf_writer.rs @@ -1467,28 +1467,11 @@ fn write_object<'data, A: Arch>( let _span = debug_span!("write_file", filename = %object.input).entered(); let _file_span = layout.args().common().trace_span_for_file(object.file_id); - let Some(crate::layout::FileLayout::Epilogue(epilogue)) = - layout.group_layouts.last().and_then(|g| g.files.last()) - else { - unreachable!("Epilogue is broken and must be the last file in the final layout group"); - }; - for (i, sec) in object.sections.iter().enumerate() { let section_index = object::SectionIndex(i); match sec { SectionSlot::Loaded(sec) => { - let is_harvested = epilogue - .script_sorted_sections - .binary_search_by_key(&(object.file_id, section_index.0), |h| { - (h.file_id, h.section_index.0) - }) - .is_ok(); - - if is_harvested { - continue; - } - write_object_section::( object, layout, @@ -2134,6 +2117,7 @@ fn write_symbols<'data>( if let Some(section_index) = object.object.symbol_section(sym, sym_index)? { match &object.sections[section_index.0] { SectionSlot::Loaded(_) + | SectionSlot::Sorted(_) | SectionSlot::LoadedDebugInfo(_) | SectionSlot::MergeStrings(_) => object .section_part_id(section_index, &layout.symbol_db.section_part_ids) @@ -4058,7 +4042,7 @@ fn write_epilogue>( continue; }; - if let SectionSlot::Loaded(sec) = &object.sections[harvested.section_index.0] { + if let SectionSlot::Sorted(sec) = &object.sections[harvested.section_index.0] { write_object_section::( object, layout, diff --git a/libwild/src/layout.rs b/libwild/src/layout.rs index 8798c3e32..df3ff3ee4 100644 --- a/libwild/src/layout.rs +++ b/libwild/src/layout.rs @@ -1280,18 +1280,6 @@ impl<'data, P: Platform> CommonGroupState<'data, P> { self.mem_sizes.increment(part_id, size); } - /// Allocate resources and update attributes based on a section having been loaded. - fn section_loaded( - &mut self, - part_id: PartId, - header: &P::SectionHeader, - section: Section, - output_sections: &OutputSections

, - ) { - self.allocate(part_id, section.capacity(part_id, output_sections)); - self.store_section_attributes(part_id, header); - } - fn store_section_attributes(&mut self, part_id: PartId, header: &P::SectionHeader) { let existing_attributes = self.section_attributes.get_mut(part_id.output_section_id()); @@ -4056,6 +4044,7 @@ impl<'data, P: Platform> ObjectLayoutState<'data, P> { ); } SectionSlot::Loaded(_) + | SectionSlot::Sorted(_) | SectionSlot::FrameData(..) | SectionSlot::LoadedDebugInfo(..) | SectionSlot::NoteGnuProperty(..) @@ -4109,7 +4098,20 @@ impl<'data, P: Platform> ObjectLayoutState<'data, P> { tracing::debug!(loaded_section = %self.object.section_display_name(section_index), file = %self.input); - common.section_loaded(part_id, header, section, resources.output_sections); + self.sections[section_index.0] = if unloaded.needs_sorting { + self.script_sorted_sections.push(ScriptSortedSectionDetail { + index: section_index, + }); + SectionSlot::Sorted(section) + } else { + common.allocate( + part_id, + section.capacity(part_id, resources.output_sections), + ); + SectionSlot::Loaded(section) + }; + + common.store_section_attributes(part_id, header); if let Some(config) = A::thunk_config() && resources.thunk_layout_builder.is_some() @@ -4126,8 +4128,6 @@ impl<'data, P: Platform> ObjectLayoutState<'data, P> { resources.keep_section(section_id); } - self.sections[section_index.0] = SectionSlot::Loaded(section); - Ok(()) } @@ -4150,7 +4150,11 @@ impl<'data, P: Platform> ObjectLayoutState<'data, P> { // live sections. tracing::debug!(loaded_debug_section = %self.object.section_display_name(section_index),); - common.section_loaded(part_id, header, section, resources.output_sections); + common.allocate( + part_id, + section.capacity(part_id, resources.output_sections), + ); + common.store_section_attributes(part_id, header); self.sections[section_index.0] = SectionSlot::LoadedDebugInfo(section); Ok(()) @@ -4221,7 +4225,7 @@ impl<'data, P: Platform> ObjectLayoutState<'data, P> { { let resolution = match slot { SectionSlot::Loaded(sec) => { - let mut address = *memory_offsets.get(part_id); + let address = *memory_offsets.get(part_id); // TODO: We probably need to be able to handle sections that are ifuncs and // sections that need a TLS GOT struct. @@ -4235,14 +4239,18 @@ impl<'data, P: Platform> ObjectLayoutState<'data, P> { sframe_ranges.push(offset..offset + len); } - if let Ok(idx) = resources + SectionResolution { address } + } + + SectionSlot::Sorted(_) => { + let idx = resources .harvested_sections_registry .binary_search_by_key(&(self.file_id, sec_idx), |s| { (s.file_id, s.section_index.0) }) - { - address = resources.harvested_sections_registry[idx].mem_offset; - } + .context("Internal error: Missing address for sorted section")?; + + let address = resources.harvested_sections_registry[idx].mem_offset; SectionResolution { address } } @@ -5864,7 +5872,7 @@ fn harvest_and_sort_script_sections<'data, P: Platform>( for file in &mut group.files { if let FileLayoutState::Object(obj) = file { for sorted_section in &obj.script_sorted_sections { - if let SectionSlot::Loaded(sec) = &obj.sections[sorted_section.index.0] { + if let SectionSlot::Sorted(sec) = &obj.sections[sorted_section.index.0] { let part_id = obj.section_part_id(sorted_section.index, section_part_ids); let capacity = sec.capacity(part_id, output_sections); sections_out.push(( diff --git a/libwild/src/layout_rules.rs b/libwild/src/layout_rules.rs index 029ac7439..88a17f355 100644 --- a/libwild/src/layout_rules.rs +++ b/libwild/src/layout_rules.rs @@ -105,13 +105,13 @@ pub(crate) enum SectionRuleOutcome { DebugIndex, RiscVAttribute, SortedSection(SectionOutputInfo), - ScriptSortedSection(SectionOutputInfo), } #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub(crate) struct SectionOutputInfo { pub(crate) section_id: OutputSectionId, pub(crate) must_keep: bool, + pub(crate) sorted: bool, } impl SectionOutputInfo { @@ -119,6 +119,7 @@ impl SectionOutputInfo { Self { section_id, must_keep: false, + sorted: false, } } @@ -126,6 +127,7 @@ impl SectionOutputInfo { Self { section_id, must_keep: true, + sorted: false, } } } @@ -262,17 +264,13 @@ impl<'data> LayoutRulesBuilder<'data> { let output_info = SectionOutputInfo { section_id, must_keep: matcher.must_keep, + sorted: pattern.sorted, }; - // If the script requested sorting, tag it for the - // global Harvester instead of standard placement. - let outcome = if pattern.sorted { - SectionRuleOutcome::ScriptSortedSection(output_info) - } else { + let outcome = crate::layout_rules::SectionRuleOutcome::Section( output_info, - ) - }; + ); self.add_section_rule(SectionRule::new( pattern.name, @@ -649,7 +647,8 @@ fn test_section_mapping() { lookup_name(".comment"), SectionRuleOutcome::Section(SectionOutputInfo { section_id: output_section_id::COMMENT, - must_keep: true + must_keep: true, + sorted: false, }) ); } diff --git a/libwild/src/resolution.rs b/libwild/src/resolution.rs index e781ecc8d..4d75aa30e 100644 --- a/libwild/src/resolution.rs +++ b/libwild/src/resolution.rs @@ -698,6 +698,10 @@ pub(crate) enum SectionSlot { /// We've already loaded the section. Loaded(crate::layout::Section), + /// As for `Loaded`, but responsibility for allocating and writing the section is held by the + /// epilogue due to being part of a sorted section. + Sorted(crate::layout::Section), + /// The section contains frame data, e.g. .eh_frame or equivalent. FrameData(object::SectionIndex), @@ -725,6 +729,8 @@ pub(crate) struct UnloadedSection { /// Whether the section has a name that makes it eligible for generation of __start_ / __stop_ /// symbols. In particular, the name of the section doesn't start with a ".". pub(crate) start_stop_eligible: bool, + + pub(crate) needs_sorting: bool, } impl UnloadedSection { @@ -732,6 +738,7 @@ impl UnloadedSection { Self { last_frame_index: None, start_stop_eligible: false, + needs_sorting: false, } } } @@ -1340,6 +1347,7 @@ fn resolve_section<'data, P: Platform>( must_load |= output_info.must_keep; unloaded_section = UnloadedSection::new(); + unloaded_section.needs_sorting = output_info.sorted; } SectionRuleOutcome::SortedSection(output_info) => { part_id = if output_info.section_id.is_regular() { @@ -1360,22 +1368,6 @@ fn resolve_section<'data, P: Platform>( unloaded_section = UnloadedSection::new(); } - SectionRuleOutcome::ScriptSortedSection(output_info) => { - part_id = if output_info.section_id.is_regular() { - output_info.section_id.part_id_with_alignment(alignment) - } else { - output_info.section_id.base_part_id() - }; - - obj.script_sorted_sections.push(ScriptSortedSectionDetail { - index: input_section_index, - }); - - must_load |= output_info.must_keep; - - unloaded_section = UnloadedSection::new(); - } - SectionRuleOutcome::Discard => return Ok((SectionSlot::Discard, part_id::UNMAPPED)), SectionRuleOutcome::NoteGnuStack => { P::validate_stack_section(input_section, obj, args)?; From 202ff48ecb46a0cf724f21dd72d28bcbecfcad90 Mon Sep 17 00:00:00 2001 From: David Lattimore Date: Fri, 26 Jun 2026 12:41:11 +1000 Subject: [PATCH 2/6] refactor: Remove unnecessary attribute from test --- wild/tests/sources/elf/script-sort/script-sort.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/wild/tests/sources/elf/script-sort/script-sort.c b/wild/tests/sources/elf/script-sort/script-sort.c index 74a1c8d9b..21d9a05d1 100644 --- a/wild/tests/sources/elf/script-sort/script-sort.c +++ b/wild/tests/sources/elf/script-sort/script-sort.c @@ -11,9 +11,6 @@ extern int func_a(); extern int func_b(); extern int func_c(); -#if defined(__x86_64__) -__attribute__((force_align_arg_pointer)) -#endif void _start(void) { runtime_init(); From 60cd981edc11b188036e88756e4494d4f1844b8f Mon Sep 17 00:00:00 2001 From: David Lattimore Date: Fri, 26 Jun 2026 12:41:11 +1000 Subject: [PATCH 3/6] refactor: Minor testing and style cleanups of sorted sections --- libwild/src/elf_writer.rs | 2 +- libwild/src/layout.rs | 31 +++++++++---------- libwild/src/linker_script.rs | 1 - .../sources/elf/script-sort/script-sort.c | 10 ++++-- .../sources/elf/script-sort/script-sort.ld | 2 +- 5 files changed, 23 insertions(+), 23 deletions(-) diff --git a/libwild/src/elf_writer.rs b/libwild/src/elf_writer.rs index 943fe3df4..fed548cd3 100644 --- a/libwild/src/elf_writer.rs +++ b/libwild/src/elf_writer.rs @@ -4039,7 +4039,7 @@ fn write_epilogue>( for harvested in &epilogue.script_sorted_sections { let crate::layout::FileLayout::Object(object) = layout.file_layout(harvested.file_id) else { - continue; + unreachable!(); }; if let SectionSlot::Sorted(sec) = &object.sections[harvested.section_index.0] { diff --git a/libwild/src/layout.rs b/libwild/src/layout.rs index df3ff3ee4..734f3003b 100644 --- a/libwild/src/layout.rs +++ b/libwild/src/layout.rs @@ -356,11 +356,6 @@ pub fn compute<'data, P: Platform, A: Arch>( &merged_strings, ); - // At this stage, sections marked for sorting have been harvested but lack concrete memory - // addresses. We perform a two-step finalization: - // Sort the harvested sections according to the requested criteria. - // Linearize them in memory, starting from the base offset of their respective output section - // part, and advancing by the size of each section. let harvested_sections_registry = assign_addresses_to_sorted_sections( &mut group_states, &starting_mem_offsets_by_group, @@ -5836,7 +5831,7 @@ impl<'data, P: Platform> Drop for Layout<'data, P> { fn drop(&mut self) {} } -#[derive(Clone, Debug)] +#[derive(Copy, Clone, Debug)] pub(crate) struct HarvestedSortedSection { pub(crate) file_id: FileId, pub(crate) section_index: object::SectionIndex, @@ -5900,7 +5895,8 @@ fn harvest_and_sort_script_sections<'data, P: Platform>( .map(|(_, harvested)| harvested) .collect() } -// Assigning memory addresses to script sorted sections and returning the finalized registry. + +// Assigning memory addresses to script sorted sections and return the finalized registry. fn assign_addresses_to_sorted_sections( group_states: &mut [GroupState

], starting_mem_offsets_by_group: &[OutputSectionPartMap], @@ -5909,17 +5905,18 @@ fn assign_addresses_to_sorted_sections( let mut harvested_sections_registry = Vec::with_capacity(num_sorted_sections); let mut epilogue_offsets = starting_mem_offsets_by_group.last().unwrap().clone(); - if let FileLayoutState::Epilogue(epilogue_state) = - &mut group_states.last_mut().unwrap().files[0] - { - for sec in &mut epilogue_state.script_sorted_sections { - let offset = epilogue_offsets.get_mut(sec.part_id); - *offset = sec.alignment.align_up(*offset); - sec.mem_offset = *offset; - *offset += sec.size; + let FileLayoutState::Epilogue(epilogue_state) = &mut group_states.last_mut().unwrap().files[0] + else { + unreachable!(); + }; - harvested_sections_registry.push(sec.clone()); - } + for sec in &mut epilogue_state.script_sorted_sections { + let offset = epilogue_offsets.get_mut(sec.part_id); + *offset = sec.alignment.align_up(*offset); + sec.mem_offset = *offset; + *offset += sec.size; + + harvested_sections_registry.push(*sec); } harvested_sections_registry.sort_unstable_by_key(|s| (s.file_id, s.section_index.0)); diff --git a/libwild/src/linker_script.rs b/libwild/src/linker_script.rs index 579382c03..b31fcd248 100644 --- a/libwild/src/linker_script.rs +++ b/libwild/src/linker_script.rs @@ -1245,7 +1245,6 @@ fn parse_pattern<'input>(input: &mut &'input BStr) -> winnow::Result= b_addr) { exit_syscall(101); diff --git a/wild/tests/sources/elf/script-sort/script-sort.ld b/wild/tests/sources/elf/script-sort/script-sort.ld index de7e19298..3b2b48da3 100644 --- a/wild/tests/sources/elf/script-sort/script-sort.ld +++ b/wild/tests/sources/elf/script-sort/script-sort.ld @@ -12,7 +12,7 @@ SECTIONS *(SORT(.text.drop.*)) } - /* plz ensure valid alignment for subsequent sections */ + /* Ensure valid alignment for subsequent sections */ . = 0x410000; From 0a73af0229e8e9586d2682de6d440e4ab0c35378 Mon Sep 17 00:00:00 2001 From: David Lattimore Date: Fri, 26 Jun 2026 13:19:02 +1000 Subject: [PATCH 4/6] refactor: Remove unnecessary arg --- libwild/src/layout.rs | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/libwild/src/layout.rs b/libwild/src/layout.rs index 734f3003b..d367d6a74 100644 --- a/libwild/src/layout.rs +++ b/libwild/src/layout.rs @@ -164,7 +164,6 @@ pub fn compute<'data, P: Platform, A: Arch>( &output_sections, &symbol_db.section_part_ids, ); - let num_sorted_sections = script_sorted_sections.len(); group_states.push(GroupState { files: vec![FileLayoutState::Epilogue(EpilogueLayoutState::new( @@ -356,11 +355,8 @@ pub fn compute<'data, P: Platform, A: Arch>( &merged_strings, ); - let harvested_sections_registry = assign_addresses_to_sorted_sections( - &mut group_states, - &starting_mem_offsets_by_group, - num_sorted_sections, - ); + let harvested_sections_registry = + assign_addresses_to_sorted_sections(&mut group_states, &starting_mem_offsets_by_group); let mut symbol_resolutions = SymbolResolutions { resolutions: Vec::with_capacity(symbol_db.num_symbols()), @@ -5900,9 +5896,7 @@ fn harvest_and_sort_script_sections<'data, P: Platform>( fn assign_addresses_to_sorted_sections( group_states: &mut [GroupState

], starting_mem_offsets_by_group: &[OutputSectionPartMap], - num_sorted_sections: usize, ) -> Vec { - let mut harvested_sections_registry = Vec::with_capacity(num_sorted_sections); let mut epilogue_offsets = starting_mem_offsets_by_group.last().unwrap().clone(); let FileLayoutState::Epilogue(epilogue_state) = &mut group_states.last_mut().unwrap().files[0] @@ -5910,6 +5904,9 @@ fn assign_addresses_to_sorted_sections( unreachable!(); }; + let mut harvested_sections_registry = + Vec::with_capacity(epilogue_state.script_sorted_sections.len()); + for sec in &mut epilogue_state.script_sorted_sections { let offset = epilogue_offsets.get_mut(sec.part_id); *offset = sec.alignment.align_up(*offset); From bec8358d086dcd27bb7db52cfe1de560b8f34412 Mon Sep 17 00:00:00 2001 From: David Lattimore Date: Fri, 26 Jun 2026 16:10:04 +1000 Subject: [PATCH 5/6] refactor: Avoid having two copies of script sorted sections --- libwild/src/elf_writer.rs | 2 +- libwild/src/layout.rs | 70 +++++++++++++++++++++------------------ libwild/src/resolution.rs | 2 +- 3 files changed, 39 insertions(+), 35 deletions(-) diff --git a/libwild/src/elf_writer.rs b/libwild/src/elf_writer.rs index fed548cd3..e68899168 100644 --- a/libwild/src/elf_writer.rs +++ b/libwild/src/elf_writer.rs @@ -4046,7 +4046,7 @@ fn write_epilogue>( write_object_section::( object, layout, - *sec, + sec.section, harvested.section_index, buffers, table_writer, diff --git a/libwild/src/layout.rs b/libwild/src/layout.rs index d367d6a74..e0f288c95 100644 --- a/libwild/src/layout.rs +++ b/libwild/src/layout.rs @@ -355,8 +355,7 @@ pub fn compute<'data, P: Platform, A: Arch>( &merged_strings, ); - let harvested_sections_registry = - assign_addresses_to_sorted_sections(&mut group_states, &starting_mem_offsets_by_group); + assign_addresses_to_sorted_sections(&mut group_states, &starting_mem_offsets_by_group); let mut symbol_resolutions = SymbolResolutions { resolutions: Vec::with_capacity(symbol_db.num_symbols()), @@ -376,7 +375,6 @@ pub fn compute<'data, P: Platform, A: Arch>( let resources = FinaliseLayoutResources { symbol_db: &symbol_db, output_sections: &output_sections, - harvested_sections_registry: &harvested_sections_registry, output_order: &output_order, section_layouts: §ion_layouts, merged_string_start_addresses: &merged_string_start_addresses, @@ -1350,6 +1348,12 @@ pub(crate) struct Section { pub(crate) size: u64, } +#[derive(Debug, Clone, Copy)] +pub(crate) struct SortedSection { + pub(crate) address: u64, + pub(crate) section: Section, +} + #[derive(Debug)] pub(crate) struct GroupLayout<'data, P: Platform> { pub(crate) files: Vec>, @@ -1432,7 +1436,6 @@ pub(crate) struct FinaliseLayoutResources<'scope, 'data, P: Platform> { output_sections: &'scope OutputSections<'data, P>, output_order: &'scope OutputOrder<'data>, pub(crate) section_layouts: &'scope OutputSectionMap, - pub(crate) harvested_sections_registry: &'scope [HarvestedSortedSection], merged_string_start_addresses: &'scope MergedStringStartAddresses, merged_strings: &'scope OutputSectionMap>, dynamic_symbol_definitions: &'scope Vec>, @@ -3850,7 +3853,6 @@ impl<'data, P: Platform> EpilogueLayoutState

{ for sec in &mut self.script_sorted_sections { let offset = memory_offsets.get_mut(sec.part_id); *offset = sec.alignment.align_up(*offset); - sec.mem_offset = *offset; *offset += sec.size; } Ok(EpilogueLayout { @@ -4093,7 +4095,11 @@ impl<'data, P: Platform> ObjectLayoutState<'data, P> { self.script_sorted_sections.push(ScriptSortedSectionDetail { index: section_index, }); - SectionSlot::Sorted(section) + SectionSlot::Sorted(SortedSection { + // Filled in later. + address: 0, + section, + }) } else { common.allocate( part_id, @@ -4212,8 +4218,7 @@ impl<'data, P: Platform> ObjectLayoutState<'data, P> { let section_id_range = self.section_id_range; let object_part_ids = &resources.symbol_db.section_part_ids[section_id_range.as_usize()]; - for (sec_idx, (slot, &part_id)) in self.sections.iter_mut().zip(object_part_ids).enumerate() - { + for (slot, &part_id) in self.sections.iter_mut().zip(object_part_ids) { let resolution = match slot { SectionSlot::Loaded(sec) => { let address = *memory_offsets.get(part_id); @@ -4233,18 +4238,9 @@ impl<'data, P: Platform> ObjectLayoutState<'data, P> { SectionResolution { address } } - SectionSlot::Sorted(_) => { - let idx = resources - .harvested_sections_registry - .binary_search_by_key(&(self.file_id, sec_idx), |s| { - (s.file_id, s.section_index.0) - }) - .context("Internal error: Missing address for sorted section")?; - - let address = resources.harvested_sections_registry[idx].mem_offset; - - SectionResolution { address } - } + SectionSlot::Sorted(sec) => SectionResolution { + address: sec.address, + }, &mut SectionSlot::LoadedDebugInfo(sec) => { let address = *memory_offsets.get(part_id); @@ -5834,7 +5830,6 @@ pub(crate) struct HarvestedSortedSection { pub(crate) part_id: PartId, pub(crate) size: u64, pub(crate) alignment: Alignment, - pub(crate) mem_offset: u64, } fn harvest_and_sort_script_sections<'data, P: Platform>( @@ -5865,7 +5860,7 @@ fn harvest_and_sort_script_sections<'data, P: Platform>( for sorted_section in &obj.script_sorted_sections { if let SectionSlot::Sorted(sec) = &obj.sections[sorted_section.index.0] { let part_id = obj.section_part_id(sorted_section.index, section_part_ids); - let capacity = sec.capacity(part_id, output_sections); + let capacity = sec.section.capacity(part_id, output_sections); sections_out.push(( obj.object .section_name(sorted_section.index) @@ -5876,7 +5871,6 @@ fn harvest_and_sort_script_sections<'data, P: Platform>( part_id, size: capacity, alignment: part_id.alignment(output_sections), - mem_offset: 0, }, )); } @@ -5892,11 +5886,10 @@ fn harvest_and_sort_script_sections<'data, P: Platform>( .collect() } -// Assigning memory addresses to script sorted sections and return the finalized registry. fn assign_addresses_to_sorted_sections( group_states: &mut [GroupState

], starting_mem_offsets_by_group: &[OutputSectionPartMap], -) -> Vec { +) { let mut epilogue_offsets = starting_mem_offsets_by_group.last().unwrap().clone(); let FileLayoutState::Epilogue(epilogue_state) = &mut group_states.last_mut().unwrap().files[0] @@ -5904,19 +5897,30 @@ fn assign_addresses_to_sorted_sections( unreachable!(); }; - let mut harvested_sections_registry = - Vec::with_capacity(epilogue_state.script_sorted_sections.len()); + let mut sorted_sections = core::mem::take(&mut epilogue_state.script_sorted_sections); - for sec in &mut epilogue_state.script_sorted_sections { + for sec in &mut sorted_sections { let offset = epilogue_offsets.get_mut(sec.part_id); *offset = sec.alignment.align_up(*offset); - sec.mem_offset = *offset; - *offset += sec.size; - harvested_sections_registry.push(*sec); + let FileLayoutState::Object(obj) = + &mut group_states[sec.file_id.group()].files[sec.file_id.file()] + else { + unreachable!(); + }; + + let SectionSlot::Sorted(slot) = &mut obj.sections[sec.section_index.0] else { + unreachable!(); + }; + + slot.address = *offset; + *offset += sec.size; } - harvested_sections_registry.sort_unstable_by_key(|s| (s.file_id, s.section_index.0)); + let FileLayoutState::Epilogue(epilogue_state) = &mut group_states.last_mut().unwrap().files[0] + else { + unreachable!(); + }; - harvested_sections_registry + epilogue_state.script_sorted_sections = sorted_sections; } diff --git a/libwild/src/resolution.rs b/libwild/src/resolution.rs index 4d75aa30e..a3e88c7b2 100644 --- a/libwild/src/resolution.rs +++ b/libwild/src/resolution.rs @@ -700,7 +700,7 @@ pub(crate) enum SectionSlot { /// As for `Loaded`, but responsibility for allocating and writing the section is held by the /// epilogue due to being part of a sorted section. - Sorted(crate::layout::Section), + Sorted(crate::layout::SortedSection), /// The section contains frame data, e.g. .eh_frame or equivalent. FrameData(object::SectionIndex), From 743b59e429087f5ce79bc39a719c407eb2767241 Mon Sep 17 00:00:00 2001 From: David Lattimore Date: Fri, 26 Jun 2026 16:41:21 +1000 Subject: [PATCH 6/6] refactor: Store sorted sections on the Layout rather than the Epilogue --- libwild/src/elf_writer.rs | 8 +++---- libwild/src/layout.rs | 49 +++++++++++++++++---------------------- 2 files changed, 25 insertions(+), 32 deletions(-) diff --git a/libwild/src/elf_writer.rs b/libwild/src/elf_writer.rs index e68899168..2249c1dac 100644 --- a/libwild/src/elf_writer.rs +++ b/libwild/src/elf_writer.rs @@ -4036,18 +4036,18 @@ fn write_epilogue>( let build_id_buffer = buffers.get_mut(part_id::NOTE_GNU_BUILD_ID); build_id_buffer.fill(0); - for harvested in &epilogue.script_sorted_sections { - let crate::layout::FileLayout::Object(object) = layout.file_layout(harvested.file_id) + for sorted_section in &layout.script_sorted_sections { + let crate::layout::FileLayout::Object(object) = layout.file_layout(sorted_section.file_id) else { unreachable!(); }; - if let SectionSlot::Sorted(sec) = &object.sections[harvested.section_index.0] { + if let SectionSlot::Sorted(sec) = &object.sections[sorted_section.section_index.0] { write_object_section::( object, layout, sec.section, - harvested.section_index, + sorted_section.section_index, buffers, table_writer, trace, diff --git a/libwild/src/layout.rs b/libwild/src/layout.rs index e0f288c95..5c9905181 100644 --- a/libwild/src/layout.rs +++ b/libwild/src/layout.rs @@ -159,7 +159,8 @@ pub fn compute<'data, P: Platform, A: Arch>( let mut dynamic_symbol_definitions = merge_dynamic_symbol_definitions(&group_states, &symbol_db)?; - let script_sorted_sections = harvest_and_sort_script_sections( + + let mut script_sorted_sections = harvest_and_sort_script_sections( &mut group_states, &output_sections, &symbol_db.section_part_ids, @@ -170,7 +171,6 @@ pub fn compute<'data, P: Platform, A: Arch>( symbol_db.args, symbol_db.output_kind, &mut dynamic_symbol_definitions, - script_sorted_sections, &group_states, ))], queue: LocalWorkQueue::new(epilogue_file_id.group()), @@ -186,6 +186,7 @@ pub fn compute<'data, P: Platform, A: Arch>( symbol_db: &symbol_db, merged_strings: &merged_strings, format_specific: &finalise_sizes_ext, + script_sorted_sections: &script_sorted_sections, }; finalise_all_sizes( @@ -355,7 +356,11 @@ pub fn compute<'data, P: Platform, A: Arch>( &merged_strings, ); - assign_addresses_to_sorted_sections(&mut group_states, &starting_mem_offsets_by_group); + assign_addresses_to_sorted_sections( + &mut group_states, + &starting_mem_offsets_by_group, + &mut script_sorted_sections, + ); let mut symbol_resolutions = SymbolResolutions { resolutions: Vec::with_capacity(symbol_db.num_symbols()), @@ -386,6 +391,7 @@ pub fn compute<'data, P: Platform, A: Arch>( format_specific: &finalise_sizes_ext, thunk_blocks: &thunk_blocks, thunk_block_addresses: &thunk_block_addresses_out, + script_sorted_sections: &script_sorted_sections, }; let group_layouts = compute_symbols_and_layouts( @@ -460,6 +466,7 @@ pub fn compute<'data, P: Platform, A: Arch>( thunk_block_addresses, compressed_debug_sections: OutputSectionMap::with_size(num_sections), gdb_index_data, + script_sorted_sections, }; P::maybe_compress_debug_sections::(&mut layout)?; @@ -474,6 +481,7 @@ pub(crate) struct FinaliseSizesResources<'data, 'scope, P: Platform> { pub(crate) symbol_db: &'scope SymbolDb<'data, P>, merged_strings: &'scope OutputSectionMap>, pub(crate) format_specific: &'scope P::FinaliseSizesExt<'data>, + script_sorted_sections: &'scope [InputSortedSection], } /// Update resolutions for symbol redirects. @@ -721,6 +729,7 @@ pub struct Layout<'data, P: Platform> { pub(crate) compressed_debug_sections: OutputSectionMap>, pub(crate) gdb_index_data: Option>, + pub(crate) script_sorted_sections: Vec, } #[derive(Debug, Default)] @@ -837,7 +846,6 @@ pub(crate) struct SyntheticSymbolsLayoutState<'data, P: Platform> { pub(crate) struct EpilogueLayoutState { format_specific: P::EpilogueLayoutExt, - pub(crate) script_sorted_sections: Vec, } #[derive(Debug)] @@ -870,7 +878,6 @@ pub(crate) struct SyntheticSymbolsLayout<'data, P: Platform> { pub(crate) struct EpilogueLayout { pub(crate) format_specific: P::EpilogueLayoutExt, pub(crate) dynsym_start_index: u32, - pub(crate) script_sorted_sections: Vec, } #[derive(Debug)] @@ -1441,6 +1448,7 @@ pub(crate) struct FinaliseLayoutResources<'scope, 'data, P: Platform> { dynamic_symbol_definitions: &'scope Vec>, segment_layouts: &'scope SegmentLayouts, program_segments: &'scope ProgramSegments, + script_sorted_sections: &'scope [InputSortedSection], pub(crate) format_specific: &'scope P::FinaliseSizesExt<'data>, pub(crate) thunk_blocks: &'scope [crate::thunks::ThunkBlock], @@ -3773,7 +3781,6 @@ impl<'data, P: Platform> EpilogueLayoutState

{ args: &P::Args, output_kind: OutputKind, dynamic_symbol_definitions: &mut [DynamicSymbolDefinition<'data, P>], - script_sorted_sections: Vec, group_states: &[GroupState<'data, P>], ) -> Self { EpilogueLayoutState { @@ -3783,7 +3790,6 @@ impl<'data, P: Platform> EpilogueLayoutState

{ dynamic_symbol_definitions, group_states, ), - script_sorted_sections, } } @@ -3794,7 +3800,7 @@ impl<'data, P: Platform> EpilogueLayoutState

{ resources: &FinaliseSizesResources<'data, '_, P>, ) -> Result { let mut extra_sizes = OutputSectionPartMap::with_size(common.mem_sizes.num_parts()); - for sec in &self.script_sorted_sections { + for sec in resources.script_sorted_sections { extra_sizes.increment(sec.part_id, sec.size); } P::apply_late_size_adjustments_epilogue( @@ -3850,7 +3856,7 @@ impl<'data, P: Platform> EpilogueLayoutState

{ dynsym_start_index, resources.dynamic_symbol_definitions, )?; - for sec in &mut self.script_sorted_sections { + for sec in resources.script_sorted_sections { let offset = memory_offsets.get_mut(sec.part_id); *offset = sec.alignment.align_up(*offset); *offset += sec.size; @@ -3858,7 +3864,6 @@ impl<'data, P: Platform> EpilogueLayoutState

{ Ok(EpilogueLayout { format_specific: self.format_specific, dynsym_start_index, - script_sorted_sections: self.script_sorted_sections, }) } } @@ -5823,8 +5828,9 @@ impl<'data, P: Platform> Drop for Layout<'data, P> { fn drop(&mut self) {} } +/// An input section that needs to be sorted due to a SORT_BY_NAME directive or equivalent. #[derive(Copy, Clone, Debug)] -pub(crate) struct HarvestedSortedSection { +pub(crate) struct InputSortedSection { pub(crate) file_id: FileId, pub(crate) section_index: object::SectionIndex, pub(crate) part_id: PartId, @@ -5836,7 +5842,7 @@ fn harvest_and_sort_script_sections<'data, P: Platform>( group_states: &mut [GroupState<'data, P>], output_sections: &OutputSections

, section_part_ids: &[PartId], -) -> Vec { +) -> Vec { timing_phase!("Harvest and sort script sections"); let has_any_sorting = group_states.iter().any(|g| { @@ -5865,7 +5871,7 @@ fn harvest_and_sort_script_sections<'data, P: Platform>( obj.object .section_name(sorted_section.index) .unwrap_or_default(), - HarvestedSortedSection { + InputSortedSection { file_id: obj.file_id, section_index: sorted_section.index, part_id, @@ -5889,17 +5895,11 @@ fn harvest_and_sort_script_sections<'data, P: Platform>( fn assign_addresses_to_sorted_sections( group_states: &mut [GroupState

], starting_mem_offsets_by_group: &[OutputSectionPartMap], + sorted_sections: &mut [InputSortedSection], ) { let mut epilogue_offsets = starting_mem_offsets_by_group.last().unwrap().clone(); - let FileLayoutState::Epilogue(epilogue_state) = &mut group_states.last_mut().unwrap().files[0] - else { - unreachable!(); - }; - - let mut sorted_sections = core::mem::take(&mut epilogue_state.script_sorted_sections); - - for sec in &mut sorted_sections { + for sec in sorted_sections { let offset = epilogue_offsets.get_mut(sec.part_id); *offset = sec.alignment.align_up(*offset); @@ -5916,11 +5916,4 @@ fn assign_addresses_to_sorted_sections( slot.address = *offset; *offset += sec.size; } - - let FileLayoutState::Epilogue(epilogue_state) = &mut group_states.last_mut().unwrap().files[0] - else { - unreachable!(); - }; - - epilogue_state.script_sorted_sections = sorted_sections; }