From adb774424eb1b81056d98939e5946b53cdc96bf3 Mon Sep 17 00:00:00 2001 From: vishruth-thimmaiah Date: Fri, 10 Apr 2026 03:28:58 +0530 Subject: [PATCH 1/7] tests Signed-off-by: vishruth-thimmaiah --- wild/tests/sources/elf/relocatables-with-groups/a.cc | 5 +++++ wild/tests/sources/elf/relocatables-with-groups/b.cc | 3 +++ .../relocatables-with-groups.c | 10 ++++++++++ 3 files changed, 18 insertions(+) create mode 100644 wild/tests/sources/elf/relocatables-with-groups/a.cc create mode 100644 wild/tests/sources/elf/relocatables-with-groups/b.cc create mode 100644 wild/tests/sources/elf/relocatables-with-groups/relocatables-with-groups.c diff --git a/wild/tests/sources/elf/relocatables-with-groups/a.cc b/wild/tests/sources/elf/relocatables-with-groups/a.cc new file mode 100644 index 000000000..ff9be6a67 --- /dev/null +++ b/wild/tests/sources/elf/relocatables-with-groups/a.cc @@ -0,0 +1,5 @@ +inline int foo() { return 21; } + +inline int bar() { return 7; } + +extern "C" int use_a() { return foo(); } diff --git a/wild/tests/sources/elf/relocatables-with-groups/b.cc b/wild/tests/sources/elf/relocatables-with-groups/b.cc new file mode 100644 index 000000000..5954cff3f --- /dev/null +++ b/wild/tests/sources/elf/relocatables-with-groups/b.cc @@ -0,0 +1,3 @@ +inline int foo() { return 7; } + +extern "C" int use_b() { return foo(); } diff --git a/wild/tests/sources/elf/relocatables-with-groups/relocatables-with-groups.c b/wild/tests/sources/elf/relocatables-with-groups/relocatables-with-groups.c new file mode 100644 index 000000000..f38c688fc --- /dev/null +++ b/wild/tests/sources/elf/relocatables-with-groups/relocatables-with-groups.c @@ -0,0 +1,10 @@ +//#EnableLinker:lld +//#Object:runtime.c +//#Relocatable:a.cc,b.cc + +#include "../common/runtime.h" + +int use_a(); +int use_b(); + +void _start() { exit_syscall(use_a() + use_b()); } From 832a56dc412312e116fa5ba765849e976fbd3892 Mon Sep 17 00:00:00 2001 From: vishruth-thimmaiah Date: Mon, 13 Apr 2026 18:16:29 +0530 Subject: [PATCH 2/7] fix: proper handling of .group sections in partial link mode Retains .group sections when linking partially. We also have to retain multiple sections with the same name, if they are in different groups. Signed-off-by: vishruth-thimmaiah --- libwild/src/elf.rs | 53 +++++++- libwild/src/elf_writer.rs | 210 +++++++++++++++++++++++++++---- libwild/src/layout.rs | 36 +++++- libwild/src/layout_rules.rs | 1 + libwild/src/output_section_id.rs | 13 +- libwild/src/platform.rs | 9 ++ libwild/src/resolution.rs | 18 +++ 7 files changed, 310 insertions(+), 30 deletions(-) diff --git a/libwild/src/elf.rs b/libwild/src/elf.rs index fd6ff3dff..3ae534be4 100644 --- a/libwild/src/elf.rs +++ b/libwild/src/elf.rs @@ -65,6 +65,7 @@ use crate::platform::Symbol as _; use crate::platform::VerneedTable as _; use crate::program_segments::ProgramSegments; use crate::resolution::LoadedMetrics; +use crate::resolution::SectionSlot; use crate::string_merging::MergedStringStartAddresses; use crate::string_merging::MergedStringsSection; use crate::symbol::UnversionedSymbolName; @@ -1878,8 +1879,7 @@ impl platform::Platform for Elf { secnames::STRTAB_SECTION_NAME | secnames::SYMTAB_SECTION_NAME | secnames::SHSTRTAB_SECTION_NAME - | secnames::SYMTAB_SHNDX_SECTION_NAME - | secnames::GROUP_SECTION_NAME => { + | secnames::SYMTAB_SHNDX_SECTION_NAME => { return SectionRuleOutcome::Discard; } secnames::RISCV_ATTRIBUTES_SECTION_NAME => return SectionRuleOutcome::RiscVAttribute, @@ -1889,6 +1889,7 @@ impl platform::Platform for Elf { output_section_id::NOTE_ABI_TAG, )); } + secnames::GROUP_SECTION_NAME => return SectionRuleOutcome::Group, _ => {} } @@ -1942,6 +1943,54 @@ impl platform::Platform for Elf { fn default_symtab_entry() -> Self::SymtabEntry { Default::default() } + + fn process_group_sections<'data, A: Arch>( + object: &mut ObjectLayoutState<'data, Self>, + common: &mut layout::CommonGroupState<'data, Self>, + group_section_indices: Vec, + resources: &layout::GraphResources<'data, '_, Self>, + ) -> Result { + let e = LittleEndian; + for index in group_section_indices { + let group_header = object.object.section(index)?; + let symbol_index = object::SymbolIndex(group_header.sh_info.get(e) as usize); + let symbol_id = object.symbol_id_range.input_to_id(symbol_index); + let raw_data = object.object.raw_section_data(group_header)?; + + if raw_data.len() < 4 || raw_data.len() % 4 != 0 { + continue; + } + + if u32::from_le_bytes(raw_data[0..4].try_into().unwrap()) != object::elf::GRP_COMDAT { + continue; + } + + if !resources.symbol_db.is_canonical(symbol_id) { + object.sections[index.0] = SectionSlot::Discard; + let num_members = (raw_data.len() - 4) / 4; + for i in 0..num_members { + let member_idx = + u32::from_le_bytes(raw_data[4 + i * 4..4 + i * 4 + 4].try_into().unwrap()); + let member_idx = member_idx as usize; + if member_idx < object.sections.len() { + object.sections[member_idx] = SectionSlot::Discard; + } + } + continue; + } + + let SectionSlot::UnloadedGroup(idx) = object.sections[index.0] else { + continue; + }; + + let part_id = object.section_part_id(idx, &resources.symbol_db.section_part_ids); + + let section = layout::Section::create(group_header, object, part_id)?; + common.section_loaded(part_id, group_header, section, resources.output_sections()); + object.sections[index.0] = SectionSlot::LoadedGroup { section, symbol_id }; + } + Ok(()) + } } /// Marks the symbol version associated with the dynamic symbol `GLIBC_ABI_DT_RELR` as needed. diff --git a/libwild/src/elf_writer.rs b/libwild/src/elf_writer.rs index eb410a86e..da31d591f 100644 --- a/libwild/src/elf_writer.rs +++ b/libwild/src/elf_writer.rs @@ -121,6 +121,7 @@ use linker_utils::relaxation::opt_input_to_output; use linker_utils::utils::slice_from_all_bytes_mut; use object::LittleEndian; use object::SymbolIndex; +use object::elf::GRP_COMDAT; use object::elf::NT_GNU_BUILD_ID; use object::elf::NT_GNU_PROPERTY_TYPE_0; use object::elf::STT_TLS; @@ -243,10 +244,10 @@ fn write_file_contents<'data, A: Arch>( timing_phase!("Write data to file"); let mut section_buffers = split_output_into_sections(layout, &mut sized_output.out); - let sym_index_map = if layout.args().should_output_partial_object() { - build_sym_index_map(layout) + let partial_object_info = if layout.args().should_output_partial_object() { + Some(build_partial_object_info(layout)) } else { - Vec::new() + None }; let mut writable_buckets = split_buffers_by_alignment(&mut section_buffers, layout); @@ -271,7 +272,7 @@ fn write_file_contents<'data, A: Arch>( &mut table_writer, layout, &sized_output.trace, - &sym_index_map, + &partial_object_info, ) .with_context(|| format!("Failed copying from {file} to output file"))?; } @@ -473,13 +474,15 @@ fn write_file<'data, A: Arch>( table_writer: &mut TableWriter, layout: &ElfLayout<'data>, trace: &TraceOutput, - sym_index_map: &[Option], + partial_info: &Option, ) -> Result { match file { FileLayout::Object(s) => { - write_object::(s, buffers, table_writer, layout, trace, sym_index_map)?; + write_object::(s, buffers, table_writer, layout, trace, partial_info)?; + } + FileLayout::Prelude(s) => { + write_prelude::(s, buffers, table_writer, layout, partial_info)?; } - FileLayout::Prelude(s) => write_prelude::(s, buffers, table_writer, layout)?, FileLayout::Epilogue(s) => write_epilogue::(s, buffers, table_writer, layout)?, FileLayout::SyntheticSymbols(s) => write_synthetic_symbols::(s, table_writer, layout)?, FileLayout::LinkerScript(s) => write_linker_script_state::(s, table_writer, layout)?, @@ -1447,12 +1450,17 @@ fn write_object<'data, A: Arch>( table_writer: &mut TableWriter, layout: &ElfLayout<'data>, trace: &TraceOutput, - sym_index_map: &[Option], + partial_info: &Option, ) -> Result { verbose_timing_phase!("Write object", file_id = object.file_id.as_u32()); let _span = debug_span!("write_file", filename = %object.input).entered(); let _file_span = layout.args().common().trace_span_for_file(object.file_id); + + let sym_index_map = partial_info + .as_ref() + .map(|info| info.symbol_index_map.as_slice()); + for (i, sec) in object.sections.iter().enumerate() { let section_index = object::SectionIndex(i); @@ -1474,6 +1482,11 @@ fn write_object<'data, A: Arch>( SectionSlot::FrameData(section_index) => { write_eh_frame_data::(object, *section_index, layout, table_writer, trace)?; } + SectionSlot::LoadedGroup { section: sec, .. } + if layout.args().should_output_partial_object() => + { + write_group_section(object, layout, sec, section_index, buffers)?; + } _ => (), } } @@ -1516,14 +1529,20 @@ fn write_object<'data, A: Arch>( if layout.args().should_output_partial_object() { write_symbols(object, &mut table_writer.debug_symbol_writer, layout)?; - write_rela_sections(object, buffers, layout, sym_index_map)?; + write_rela_sections(object, buffers, layout, sym_index_map.unwrap())?; } else if !layout.args().should_strip_all() { write_symbols(object, &mut table_writer.debug_symbol_writer, layout)?; } Ok(()) } -fn build_sym_index_map(layout: &ElfLayout<'_>) -> Vec> { +struct PartialObjectInfo { + symbol_index_map: Vec>, + rela_targets: hashbrown::HashMap, + group_symbols: hashbrown::HashMap, +} + +fn build_partial_object_info(layout: &ElfLayout<'_>) -> PartialObjectInfo { let section_sym_indices = build_section_sym_indices(layout); let num_all_locals = (layout @@ -1535,6 +1554,9 @@ fn build_sym_index_map(layout: &ElfLayout<'_>) -> Vec> { let total_syms = layout.symbol_db.num_symbols(); let mut map: Vec> = vec![None; total_syms]; + let mut rela_targets = hashbrown::HashMap::new(); + let mut group_symbols_raw = hashbrown::HashMap::new(); + // TODO: Use a ShardedWriter to parallelize this loop for group in &layout.group_layouts { let mut group_global_base = num_all_locals + group.symtab_global_start_index; @@ -1545,6 +1567,40 @@ fn build_sym_index_map(layout: &ElfLayout<'_>) -> Vec> { continue; }; + for (sec_idx, slot) in object.sections.iter().enumerate() { + let sec_output_id = match slot { + SectionSlot::Loaded(_) | SectionSlot::MergeStrings(_) => Some( + object + .section_part_id( + object::SectionIndex(sec_idx), + &layout.symbol_db.section_part_ids, + ) + .output_section_id(), + ), + _ => None, + }; + if let Some(sec_output_id) = sec_output_id { + let e = object::LittleEndian; + if let Ok(header) = object.object.section(object::SectionIndex(sec_idx)) + && header.sh_type.get(e) == object::elf::SHT_RELA + { + let target_sec_idx = object::SectionIndex(header.sh_info.get(e) as usize); + let target_output_id = + output_section_idx_for_input(object, layout, target_sec_idx); + rela_targets.insert(sec_output_id, target_output_id); + } + } + + if let SectionSlot::LoadedGroup { symbol_id, .. } = slot { + let symbol_id = layout.symbol_db.definition(*symbol_id); + let part_id = object.section_part_id( + object::SectionIndex(sec_idx), + &layout.symbol_db.section_part_ids, + ); + group_symbols_raw.insert(part_id.output_section_id(), symbol_id); + } + } + for ((sym_index, sym), flags) in object .object .enumerate_symbols() @@ -1616,7 +1672,18 @@ fn build_sym_index_map(layout: &ElfLayout<'_>) -> Vec> { } } - map + let mut group_symbols = hashbrown::HashMap::new(); + for (sec_id, sym_id) in group_symbols_raw { + if let Some(sym_idx) = map.get(sym_id.as_usize()).copied().flatten() { + group_symbols.insert(sec_id, sym_idx); + } + } + + PartialObjectInfo { + symbol_index_map: map, + rela_targets, + group_symbols, + } } fn build_section_sym_indices(layout: &ElfLayout<'_>) -> OutputSectionMap { @@ -1656,13 +1723,20 @@ fn write_rela_sections<'data>( continue; } - let Some(section_id) = layout - .output_sections - .custom_name_to_id(output_section_id::SectionName(section_name)) - else { + let part_id = if let Some(sec_slot) = object.sections.get(sec_idx.0) { + match *sec_slot { + SectionSlot::Loaded(_) + | SectionSlot::LoadedDebugInfo(_) + | SectionSlot::MergeStrings(_) + | SectionSlot::LoadedGroup { .. } => { + object.section_part_id(sec_idx, &layout.symbol_db.section_part_ids) + } + SectionSlot::FrameData(..) => part_id::EH_FRAME, + _ => continue, + } + } else { continue; }; - let part_id = section_id.part_id_with_alignment(crate::alignment::RELA_ENTRY); let target_sec_idx = object::SectionIndex(header.sh_info.get(e) as usize); let section_address = object.section_resolutions[target_sec_idx.0] @@ -1890,6 +1964,72 @@ fn write_section_reversed<'data, A: Arch>( Ok(()) } +fn write_group_section<'data>( + object: &ObjectLayout<'data, Elf>, + layout: &ElfLayout<'data>, + section: &Section, + section_index: object::SectionIndex, + buffers: &mut OutputSectionPartMap<&mut [u8]>, +) -> Result { + let group_header = object.object.section(section_index)?; + let raw_data = object.object.raw_section_data(group_header)?; + + let part_id = object.section_part_id(section_index, &layout.symbol_db.section_part_ids); + + if raw_data.len() < 4 || raw_data.len() % 4 != 0 { + return Ok(()); + } + + let allocation_size = section.capacity(part_id, &layout.output_sections) as usize; + let section_buffer = buffers.get_mut(part_id); + let out = section_buffer + .split_off_mut(..allocation_size) + .with_context(|| format!("Insufficient buffer for group section {:?}", section_index))?; + + out[..4].copy_from_slice(&GRP_COMDAT.to_le_bytes()); + + let num_members = (raw_data.len() - 4) / 4; + let mut out_pos = 4; + for i in 0..num_members { + let member_idx_raw = + u32::from_le_bytes(raw_data[4 + i * 4..4 + i * 4 + 4].try_into().unwrap()); + let member_idx = object::SectionIndex(member_idx_raw as usize); + let output_section_header_idx = output_section_idx_for_input(object, layout, member_idx); + let idx_bytes = output_section_header_idx.to_le_bytes(); + out[out_pos..out_pos + 4].copy_from_slice(&idx_bytes); + out_pos += 4; + } + out[out_pos..].fill(0); + + Ok(()) +} + +fn output_section_idx_for_input<'data>( + object: &ObjectLayout<'data, Elf>, + layout: &ElfLayout<'data>, + input_section_idx: object::SectionIndex, +) -> u32 { + let Some(slot) = object.sections.get(input_section_idx.0) else { + return 0; + }; + let Some(section_id) = (match slot { + SectionSlot::Loaded(_) + | SectionSlot::LoadedGroup { .. } + | SectionSlot::LoadedDebugInfo(_) => Some( + object + .section_part_id(input_section_idx, &layout.symbol_db.section_part_ids) + .output_section_id(), + ), + _ => return 0, + }) else { + return 0; + }; + layout + .output_sections + .output_index_of_section(section_id) + .unwrap_or(0) +} + fn write_debug_section<'data, A: Arch>( object: &ObjectLayout<'data, Elf>, layout: &ElfLayout<'data>, @@ -2025,7 +2165,8 @@ fn write_symbols<'data>( match &object.sections[section_index.0] { SectionSlot::Loaded(_) | SectionSlot::LoadedDebugInfo(_) - | SectionSlot::MergeStrings(_) => object + | SectionSlot::MergeStrings(_) + | SectionSlot::LoadedGroup { .. } => object .section_part_id(section_index, &layout.symbol_db.section_part_ids) .output_section_id(), SectionSlot::FrameData(..) => output_section_id::EH_FRAME, @@ -3358,6 +3499,7 @@ fn write_prelude<'data, A: Arch>( buffers: &mut OutputSectionPartMap<&mut [u8]>, table_writer: &mut TableWriter, layout: &ElfLayout<'data>, + partial_info: &Option, ) -> Result { verbose_timing_phase!("Write prelude"); @@ -3369,7 +3511,11 @@ fn write_prelude<'data, A: Arch>( let mut program_headers = ProgramHeaderWriter::new(buffers.get_mut(part_id::PROGRAM_HEADERS)); write_program_headers(&mut program_headers, layout)?; - write_section_headers(buffers.get_mut(part_id::SECTION_HEADERS), layout)?; + write_section_headers( + buffers.get_mut(part_id::SECTION_HEADERS), + layout, + partial_info, + )?; write_section_header_strings( buffers.get_mut(part_id::SHSTRTAB), @@ -4994,7 +5140,11 @@ impl<'out> DynamicEntriesWriter<'out> { } } -fn write_section_headers(out: &mut [u8], layout: &ElfLayout) -> Result { +fn write_section_headers( + out: &mut [u8], + layout: &ElfLayout, + partial_info: &Option, +) -> Result { let entries: &mut [SectionHeader] = slice_from_all_bytes_mut(out); let output_sections = &layout.output_sections; let mut entries = entries.iter_mut(); @@ -5104,13 +5254,23 @@ fn write_section_headers(out: &mut [u8], layout: &ElfLayout) -> Result { { link = symtab_idx; } - if let Some(section_name) = output_sections.name(section_id) - && let Some(target_name) = section_name.0.strip_prefix(b".rela") - && let Some(target_id) = output_sections - .custom_name_to_id(crate::output_section_id::SectionName(target_name)) - && let Some(target_idx) = output_sections.output_index_of_section(target_id) + if let Some(info) = partial_info + && let Some(target_idx) = info.rela_targets.get(§ion_id) + { + info_value = *target_idx; + } + } + + if layout.args().should_output_partial_object() && section_type == sht::GROUP { + if let Some(symtab_idx) = + output_sections.output_index_of_section(output_section_id::SYMTAB_LOCAL) + { + link = symtab_idx; + } + if let Some(info) = partial_info + && let Some(sig_sym_idx) = info.group_symbols.get(§ion_id) { - info_value = target_idx; + info_value = *sig_sym_idx; } } diff --git a/libwild/src/layout.rs b/libwild/src/layout.rs index 7ff41f0de..18ea75bd6 100644 --- a/libwild/src/layout.rs +++ b/libwild/src/layout.rs @@ -1044,7 +1044,7 @@ impl<'data, P: Platform> CommonGroupState<'data, P> { } /// Allocate resources and update attributes based on a section having been loaded. - fn section_loaded( + pub(crate) fn section_loaded( &mut self, part_id: PartId, header: &P::SectionHeader, @@ -1196,6 +1196,12 @@ pub(crate) struct GraphResources<'data, 'scope, P: Platform> { pub(crate) layout_resources_ext: P::LayoutResourcesExt<'data>, } +impl<'data, 'scope, P: Platform> GraphResources<'data, 'scope, P> { + pub(crate) fn output_sections(&self) -> &'scope OutputSections<'data, P> { + self.output_sections + } +} + pub(crate) struct FinaliseLayoutResources<'scope, 'data, P: Platform> { pub(crate) symbol_db: &'scope SymbolDb<'data, P>, pub(crate) per_symbol_flags: &'scope PerSymbolFlags, @@ -2544,7 +2550,7 @@ impl<'data, P: Platform> std::fmt::Display for ObjectLayout<'data, P> { } impl Section { - fn create<'data, P: Platform>( + pub(crate) fn create<'data, P: Platform>( header: &P::SectionHeader, object_state: &ObjectLayoutState<'data, P>, _part_id: PartId, @@ -3503,6 +3509,22 @@ impl<'data, P: Platform> ObjectLayoutState<'data, P> { queue: &mut LocalWorkQueue, scope: &Scope<'scope>, ) -> Result { + let mut group_sections = Vec::new(); + for section in &self.sections { + if let SectionSlot::UnloadedGroup(index) = section { + group_sections.push(*index); + } + } + + if resources.symbol_db.args.should_output_partial_object() { + ::process_group_sections::( + self, + common, + group_sections, + resources, + )?; + } + let mut frame_section_index = None; let mut note_gnu_property_section = None; let mut riscv_attributes_section = None; @@ -3626,6 +3648,8 @@ impl<'data, P: Platform> ObjectLayoutState<'data, P> { | SectionSlot::FrameData(..) | SectionSlot::LoadedDebugInfo(..) | SectionSlot::NoteGnuProperty(..) + | SectionSlot::UnloadedGroup(..) + | SectionSlot::LoadedGroup { .. } | SectionSlot::RiscvVAttributes(..) => {} SectionSlot::MergeStrings(_) => { // We currently always load everything in merge-string sections. i.e. we don't GC @@ -3792,6 +3816,14 @@ impl<'data, P: Platform> ObjectLayoutState<'data, P> { let address = P::frame_data_base_address(memory_offsets); SectionResolution { address } } + &mut SectionSlot::LoadedGroup { + ref mut section, .. + } => { + let address = *memory_offsets.get(part_id); + *memory_offsets.get_mut(part_id) += + section.capacity(part_id, resources.output_sections); + SectionResolution { address } + } _ => SectionResolution::none(), }; section_resolutions.push(resolution); diff --git a/libwild/src/layout_rules.rs b/libwild/src/layout_rules.rs index 5b774f0a2..ec5b91730 100644 --- a/libwild/src/layout_rules.rs +++ b/libwild/src/layout_rules.rs @@ -103,6 +103,7 @@ pub(crate) enum SectionRuleOutcome { Debug, RiscVAttribute, SortedSection(SectionOutputInfo), + Group, } #[derive(Debug, Clone, Copy, PartialEq, Eq)] diff --git a/libwild/src/output_section_id.rs b/libwild/src/output_section_id.rs index af1eec780..92c85894b 100644 --- a/libwild/src/output_section_id.rs +++ b/libwild/src/output_section_id.rs @@ -52,6 +52,7 @@ pub(crate) struct CustomSectionDetails<'data> { pub(crate) name: SectionName<'data>, pub(crate) index: object::SectionIndex, pub(crate) alignment: Alignment, + pub(crate) is_unique: bool, } #[derive(Clone, Copy, Debug, PartialEq, Eq)] @@ -575,7 +576,17 @@ impl<'data, P: Platform> OutputSections<'data, P> { let location = args .start_address_for_section(custom.name) .map(|address| linker_script::Location { address }); - let section_id = self.add_named_section(custom.name, custom.alignment, location); + let section_id = if custom.is_unique { + self.section_infos.add_new(SectionOutputInfo { + kind: SectionKind::Primary(custom.name), + section_attributes: Default::default(), + min_alignment: custom.alignment, + location, + secondary_order: None, + }) + } else { + self.add_named_section(custom.name, custom.alignment, location) + }; section_part_ids[custom.index.0] = section_id.part_id_with_alignment(custom.alignment); } diff --git a/libwild/src/platform.rs b/libwild/src/platform.rs index c12c6560f..f20546c62 100644 --- a/libwild/src/platform.rs +++ b/libwild/src/platform.rs @@ -659,6 +659,15 @@ pub(crate) trait Platform: _total_sizes: &mut OutputSectionPartMap, ) { } + + fn process_group_sections<'data, A: Arch>( + _object: &mut ObjectLayoutState<'data, Self>, + _common: &mut layout::CommonGroupState<'data, Self>, + _group_section_indices: Vec, + _resources: &layout::GraphResources<'data, '_, Self>, + ) -> Result { + Ok(()) + } } /// Abstracts over the different object file formats that we support (or may support). e.g. ELF. diff --git a/libwild/src/resolution.rs b/libwild/src/resolution.rs index ec50e7af2..aa828d4c6 100644 --- a/libwild/src/resolution.rs +++ b/libwild/src/resolution.rs @@ -661,6 +661,15 @@ pub(crate) enum SectionSlot { // RISC-V attributes section (.riscv.attributes) RiscvVAttributes(object::SectionIndex), + + // Group section (.group) that hasn't been fully processed yet. + UnloadedGroup(object::SectionIndex), + + // Loaded group section with its output section assignment. + LoadedGroup { + section: crate::layout::Section, + symbol_id: SymbolId, + }, } #[derive(Debug, Clone, Copy)] @@ -1177,6 +1186,7 @@ fn resolve_section<'data, P: Platform>( let mut unloaded_section; let mut is_debug_info = false; + let mut is_group = false; let mut must_load = input_section.should_retain() || input_section.is_note(); let part_id: PartId; @@ -1268,6 +1278,11 @@ fn resolve_section<'data, P: Platform>( part_id::UNMAPPED, )); } + SectionRuleOutcome::Group => { + part_id = part_id::CUSTOM_PLACEHOLDER; + is_group = true; + unloaded_section = UnloadedSection::new(); + } }; if part_id == part_id::CUSTOM_PLACEHOLDER { @@ -1275,6 +1290,7 @@ fn resolve_section<'data, P: Platform>( name: SectionName(section_name), alignment, index: input_section_index, + is_unique: is_group || input_section.is_group(), }; obj.custom_sections.push(custom_section); @@ -1297,6 +1313,8 @@ fn resolve_section<'data, P: Platform>( }); SectionSlot::MergeStrings(StringMergeSectionSlot::new()) + } else if is_group { + SectionSlot::UnloadedGroup(input_section_index) } else if is_debug_info { SectionSlot::UnloadedDebugInfo } else if must_load { From 7f98de748eb59495c932989afb3092b022343fd7 Mon Sep 17 00:00:00 2001 From: vishruth-thimmaiah Date: Mon, 20 Apr 2026 04:19:52 +0530 Subject: [PATCH 3/7] fix: convert reloc referencing a discarded section to a NONE type Signed-off-by: vishruth-thimmaiah --- libwild/src/elf_writer.rs | 23 +++++++++++++++++-- .../sources/elf/relocatables-with-groups/a.cc | 2 -- .../sources/elf/relocatables-with-groups/b.cc | 2 +- 3 files changed, 22 insertions(+), 5 deletions(-) diff --git a/libwild/src/elf_writer.rs b/libwild/src/elf_writer.rs index da31d591f..a5209c0d3 100644 --- a/libwild/src/elf_writer.rs +++ b/libwild/src/elf_writer.rs @@ -1768,6 +1768,25 @@ fn write_rela_sections<'data>( let Some(out) = rela_iter.next() else { return; }; + + let section_discarded = sym + .and_then(|s| object.object.symbol(s).ok()) + .filter(|sym_entry| sym_entry.st_type() == object::elf::STT_SECTION) + .and_then(|sym_entry| { + let s = sym.unwrap(); + object.object.symbol_section(sym_entry, s).ok().flatten() + }) + .is_some_and(|sec_idx| { + matches!(object.sections.get(sec_idx.0), Some(SectionSlot::Discard)) + }); + + if section_discarded { + out.r_offset.set(e, section_address + offset); + out.r_addend.set(e, 0); + out.set_r_info(e, false, 0, 0); + return; + } + let sym_idx = sym .and_then(|s| { let symbol_id = object.symbol_id_range.input_to_id(s); @@ -1984,7 +2003,7 @@ fn write_group_section<'data>( let section_buffer = buffers.get_mut(part_id); let out = section_buffer .split_off_mut(..allocation_size) - .with_context(|| format!("Insufficient buffer for group section {:?}", section_index))?; + .with_context(|| format!("Insufficient buffer for group section {section_index:?}"))?; out[..4].copy_from_slice(&GRP_COMDAT.to_le_bytes()); @@ -2475,7 +2494,7 @@ fn write_eh_frame_relocations<'data, A: Arch, R: Relocation>( if rel_offset < next_input_pos as u64 { let is_pc_begin = (rel_offset as usize - input_pos) == elf::FDE_PC_BEGIN_OFFSET; - if is_pc_begin { + if is_pc_begin && rel.raw_type() != object::elf::R_X86_64_NONE { let Some(index) = rel.symbol() else { bail!("Unexpected absolute relocation in .eh_frame pc-begin"); }; diff --git a/wild/tests/sources/elf/relocatables-with-groups/a.cc b/wild/tests/sources/elf/relocatables-with-groups/a.cc index ff9be6a67..798a5ae28 100644 --- a/wild/tests/sources/elf/relocatables-with-groups/a.cc +++ b/wild/tests/sources/elf/relocatables-with-groups/a.cc @@ -1,5 +1,3 @@ inline int foo() { return 21; } -inline int bar() { return 7; } - extern "C" int use_a() { return foo(); } diff --git a/wild/tests/sources/elf/relocatables-with-groups/b.cc b/wild/tests/sources/elf/relocatables-with-groups/b.cc index 5954cff3f..e818945ec 100644 --- a/wild/tests/sources/elf/relocatables-with-groups/b.cc +++ b/wild/tests/sources/elf/relocatables-with-groups/b.cc @@ -1,3 +1,3 @@ -inline int foo() { return 7; } +inline int foo() { return 21; } extern "C" int use_b() { return foo(); } From e0c362a9c000b538c0d153d55362f1490915593d Mon Sep 17 00:00:00 2001 From: vishruth-thimmaiah Date: Tue, 21 Apr 2026 04:25:44 +0530 Subject: [PATCH 4/7] fix: track symbols by name Signed-off-by: vishruth-thimmaiah --- libwild/src/elf.rs | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/libwild/src/elf.rs b/libwild/src/elf.rs index 3ae534be4..3e93dc00b 100644 --- a/libwild/src/elf.rs +++ b/libwild/src/elf.rs @@ -131,6 +131,7 @@ use std::mem::offset_of; use std::num::NonZeroU32; use std::num::NonZeroU64; use std::ops::Range; +use std::sync::Mutex; use std::sync::atomic; use std::sync::atomic::AtomicBool; use std::sync::atomic::Ordering; @@ -666,6 +667,7 @@ impl platform::Platform for Elf { LayoutResourcesExt { sonames: Sonames::new(groups), uses_tlsld: AtomicBool::new(false), + comdat_groups: Mutex::new(hashbrown::HashSet::new()), } } @@ -1965,7 +1967,16 @@ impl platform::Platform for Elf { continue; } - if !resources.symbol_db.is_canonical(symbol_id) { + let sym = object.object.symbol(symbol_index)?; + let sig_name = object.object.symbol_name(sym)?; + let is_first = resources + .layout_resources_ext + .comdat_groups + .lock() + .unwrap() + .insert(sig_name); + + if !is_first { object.sections[index.0] = SectionSlot::Discard; let num_members = (raw_data.len() - 4) / 4; for i in 0..num_members { @@ -4195,6 +4206,7 @@ fn has_complete_deps<'data>( pub(crate) struct LayoutResourcesExt<'data> { sonames: Sonames<'data>, uses_tlsld: AtomicBool, + comdat_groups: Mutex>, } #[derive(Debug)] From 46a36f6e9822680839797378f0b67e948405cecd Mon Sep 17 00:00:00 2001 From: vishruth-thimmaiah Date: Tue, 21 Apr 2026 05:05:49 +0530 Subject: [PATCH 5/7] fix: add sectionRuleOutcome for eh_frame Signed-off-by: vishruth-thimmaiah --- libwild/src/elf.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/libwild/src/elf.rs b/libwild/src/elf.rs index 3e93dc00b..8dada8d7f 100644 --- a/libwild/src/elf.rs +++ b/libwild/src/elf.rs @@ -1886,6 +1886,7 @@ impl platform::Platform for Elf { } secnames::RISCV_ATTRIBUTES_SECTION_NAME => return SectionRuleOutcome::RiscVAttribute, secnames::NOTE_GNU_PROPERTY_SECTION_NAME => return SectionRuleOutcome::NoteGnuProperty, + secnames::EH_FRAME_SECTION_NAME => return SectionRuleOutcome::EhFrame, secnames::NOTE_ABI_TAG_SECTION_NAME => { return SectionRuleOutcome::Section(crate::layout_rules::SectionOutputInfo::keep( output_section_id::NOTE_ABI_TAG, From 3cd28dedc84b3461437507a7591cced77ec5947d Mon Sep 17 00:00:00 2001 From: vishruth-thimmaiah Date: Wed, 22 Apr 2026 02:31:40 +0530 Subject: [PATCH 6/7] fix Signed-off-by: vishruth-thimmaiah --- libwild/src/elf.rs | 1 - libwild/src/elf_writer.rs | 5 +++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/libwild/src/elf.rs b/libwild/src/elf.rs index 8dada8d7f..3e93dc00b 100644 --- a/libwild/src/elf.rs +++ b/libwild/src/elf.rs @@ -1886,7 +1886,6 @@ impl platform::Platform for Elf { } secnames::RISCV_ATTRIBUTES_SECTION_NAME => return SectionRuleOutcome::RiscVAttribute, secnames::NOTE_GNU_PROPERTY_SECTION_NAME => return SectionRuleOutcome::NoteGnuProperty, - secnames::EH_FRAME_SECTION_NAME => return SectionRuleOutcome::EhFrame, secnames::NOTE_ABI_TAG_SECTION_NAME => { return SectionRuleOutcome::Section(crate::layout_rules::SectionOutputInfo::keep( output_section_id::NOTE_ABI_TAG, diff --git a/libwild/src/elf_writer.rs b/libwild/src/elf_writer.rs index a5209c0d3..830224103 100644 --- a/libwild/src/elf_writer.rs +++ b/libwild/src/elf_writer.rs @@ -1771,7 +1771,6 @@ fn write_rela_sections<'data>( let section_discarded = sym .and_then(|s| object.object.symbol(s).ok()) - .filter(|sym_entry| sym_entry.st_type() == object::elf::STT_SECTION) .and_then(|sym_entry| { let s = sym.unwrap(); object.object.symbol_section(sym_entry, s).ok().flatten() @@ -2034,11 +2033,13 @@ fn output_section_idx_for_input<'data>( let Some(section_id) = (match slot { SectionSlot::Loaded(_) | SectionSlot::LoadedGroup { .. } + | SectionSlot::MergeStrings(_) | SectionSlot::LoadedDebugInfo(_) => Some( object .section_part_id(input_section_idx, &layout.symbol_db.section_part_ids) .output_section_id(), ), + SectionSlot::FrameData(..) => Some(crate::output_section_id::EH_FRAME), _ => return 0, }) else { return 0; @@ -2494,7 +2495,7 @@ fn write_eh_frame_relocations<'data, A: Arch, R: Relocation>( if rel_offset < next_input_pos as u64 { let is_pc_begin = (rel_offset as usize - input_pos) == elf::FDE_PC_BEGIN_OFFSET; - if is_pc_begin && rel.raw_type() != object::elf::R_X86_64_NONE { + if is_pc_begin && rel.raw_type() != 0 { let Some(index) = rel.symbol() else { bail!("Unexpected absolute relocation in .eh_frame pc-begin"); }; From 493daedc873f9c281377908200fa40a594c35394 Mon Sep 17 00:00:00 2001 From: vishruth-thimmaiah Date: Tue, 28 Apr 2026 03:03:12 +0530 Subject: [PATCH 7/7] fix reloc nullifying Signed-off-by: vishruth-thimmaiah --- libwild/src/elf_writer.rs | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/libwild/src/elf_writer.rs b/libwild/src/elf_writer.rs index 830224103..7638fb54e 100644 --- a/libwild/src/elf_writer.rs +++ b/libwild/src/elf_writer.rs @@ -1779,13 +1779,6 @@ fn write_rela_sections<'data>( matches!(object.sections.get(sec_idx.0), Some(SectionSlot::Discard)) }); - if section_discarded { - out.r_offset.set(e, section_address + offset); - out.r_addend.set(e, 0); - out.set_r_info(e, false, 0, 0); - return; - } - let sym_idx = sym .and_then(|s| { let symbol_id = object.symbol_id_range.input_to_id(s); @@ -1799,6 +1792,14 @@ fn write_rela_sections<'data>( .flatten() }) .unwrap_or(0); + + if section_discarded && sym_idx == 0 { + out.r_offset.set(e, section_address + offset); + out.r_addend.set(e, 0); + out.set_r_info(e, false, 0, 0); + return; + } + let addend = sym .and_then(|s| { let sym_entry = object.object.symbol(s).ok()?;