diff --git a/LINKER_SCRIPT_SUPPORT.md b/LINKER_SCRIPT_SUPPORT.md index f80b6d4fd..05a962699 100644 --- a/LINKER_SCRIPT_SUPPORT.md +++ b/LINKER_SCRIPT_SUPPORT.md @@ -58,7 +58,7 @@ end lists the features required to link the Linux kernel. | `BYTE(expr)`, `SHORT(expr)`, `LONG(expr)`, `QUAD(expr)` output data | โŒ | | | `SUBALIGN(n)` forced input alignment | โŒ | | | `ONLY_IF_RO` / `ONLY_IF_RW` output section constraints | โŒ | | -| `:phdr` output section phdrs | ๐Ÿงช | Only a single `:phdr` specifier is supported per output section. | +| `:phdr` output section phdrs | โœ… | | ## Expressions and Functions diff --git a/libwild/src/elf.rs b/libwild/src/elf.rs index 8e427d508..6f342854b 100644 --- a/libwild/src/elf.rs +++ b/libwild/src/elf.rs @@ -38,6 +38,7 @@ use crate::output_kind::OutputKind; use crate::output_section_id; use crate::output_section_id::CustomSectionIds; use crate::output_section_id::NUM_BUILT_IN_SECTIONS; +use crate::output_section_id::OrderEvent; use crate::output_section_id::OutputOrder; use crate::output_section_id::OutputOrderBuilder; use crate::output_section_id::OutputSectionId; @@ -68,7 +69,9 @@ use crate::platform::SectionType as _; use crate::platform::Symbol as _; use crate::platform::ThunkConfig; use crate::platform::VerneedTable as _; +use crate::program_segments::ProgramSegmentId; use crate::program_segments::ProgramSegments; +use crate::program_segments::SegmentEntry; use crate::resolution::LoadedMetrics; use crate::string_merging::MergedStringStartAddresses; use crate::string_merging::MergedStringsSection; @@ -829,6 +832,11 @@ impl platform::Platform for Elf { if segment_def.segment_type == pt::PHDR { *keep = !args.nmagic; } + if segment_def.segment_type == pt::RISCV_ATTRIBUTES + && args.arch != Architecture::RiscV64 + { + *keep = false; + } } } @@ -840,6 +848,20 @@ impl platform::Platform for Elf { &[STACK_SEGMENT_DEF] } + fn get_segment_flags_for_section(section_flags: &Self::SectionFlags) -> u32 { + let mut flags = 0; + if section_flags.contains(shf::ALLOC) { + flags |= pf::READABLE.0; + } + if section_flags.contains(shf::WRITE) { + flags |= pf::WRITABLE.0; + } + if section_flags.contains(shf::EXECINSTR) { + flags |= pf::EXECUTABLE.0; + } + flags + } + fn create_linker_defined_symbols( symbols: &mut crate::parsing::InternalSymbolsBuilder, output_kind: OutputKind, @@ -976,9 +998,9 @@ impl platform::Platform for Elf { min_alignment: d.min_alignment, location_info: None, secondary_order: None, - phdr_name: None, region_name: None, fill: None, + phdrs: Vec::new(), }) .collect() } @@ -2006,7 +2028,6 @@ impl platform::Platform for Elf { output_sections: &OutputSections<'data, Self>, secondary: &OutputSectionMap>, linker_scripts: &[&SequencedLinkerScript<'data, Self>], - phdr_map: &mut hashbrown::HashMap<&[u8], Vec>, location_counters: &[crate::layout_rules::LocationCounter<'data>], ) -> Result<(OutputOrder<'data>, ProgramSegments)> { let mut builder = OutputOrderBuilder::::new( @@ -2017,90 +2038,189 @@ impl platform::Platform for Elf { location_counters, ); - let mut insert_re = false; - let mut insert_rw = false; - let mut insert_r = false; - let mut first_load = false; + let mut segments_map = HashMap::new(); + let mut ordered_sections = Vec::new(); + + let mut num_phdrs = 0; + + let mut segment_has_explicit_flags = Vec::with_capacity(num_phdrs); for script in linker_scripts { + num_phdrs += script.parsed.program_headers.len(); for phdr in &script.parsed.program_headers { - let ptype = expression_eval::evaluate_const(&phdr.ptype)?; + let ptype = expression_eval::evaluate_const(&phdr.ptype)? as u32; let flags = phdr .flags .as_ref() .map(|f| expression_eval::evaluate_const(f).map(|c| c as u32)) - .transpose()?; - if let Some(sections) = phdr_map.get_mut(phdr.name) { - let flags = flags - .or_else(|| { - let section_info = - output_sections.section_infos.get(*sections.first()?); - let flags = section_info.section_attributes.flags(); - let mut pflags = SegmentFlags(0); - if flags.contains(shf::ALLOC) { - pflags |= pf::READABLE; - } - if flags.contains(shf::WRITE) { - pflags |= pf::WRITABLE; - } - if flags.contains(shf::EXECINSTR) { - pflags |= pf::EXECUTABLE; - } - Some(pflags.0) - }) - .unwrap_or(0); - if ptype == 1 && !first_load { - sections.splice( - 0..0, - [ - output_section_id::FILE_HEADER, - output_section_id::PROGRAM_HEADERS, - output_section_id::SECTION_HEADERS, - ], - ); - first_load = true; + .transpose()? + .unwrap_or(0); + + let id = builder.add_custom_segment( + ::from_linker_script( + ptype, flags, + ), + ); + segment_has_explicit_flags.push(phdr.flags.is_some()); + segments_map.insert( + phdr.name, + SegmentEntry { + id, + ptype, + flags, + is_emitted: false, + }, + ); + } + + for id in &script.parsed.ordered_sections { + let info = output_sections.section_infos.get(*id); + for phdr in &info.phdrs { + if phdr == b"NONE" { + continue; + } + let segment = segments_map.get_mut(phdr).with_context(|| { + format!( + "Section {} assigned to non-existent phdr `{}`", + output_sections.display_name(*id), + String::from_utf8_lossy(phdr) + ) + })?; + segment.is_emitted = true; + if segment_has_explicit_flags[segment.id.as_usize()] { + continue; } - if ptype == 1 { - let is_write = (flags & 2) != 0; - let is_exec = (flags & 1) != 0; + segment.flags |= + Self::get_segment_flags_for_section(&info.section_attributes.flags); + builder.get_segment_mut(segment.id).segment_flags |= + SegmentFlags(segment.flags); + } + ordered_sections.push(*id); + } + } - if is_exec && !is_write && !insert_re { - sections.extend(&custom.exec); - insert_re = true; - } - if is_write && !insert_rw { - sections.extend(&custom.tdata); - sections.extend(&custom.tbss); - sections.extend(&custom.data); - sections.extend(&custom.bss); - insert_rw = true; - } - if !is_write && !is_exec && !insert_r { - sections.extend(&custom.ro); - insert_r = true; - } + let mut first_load = None; + let mut starts = vec![None; num_phdrs]; + let mut ends = vec![None; num_phdrs]; + let mut first_exec: Option<&SegmentEntry> = None; + let mut first_rw: Option<&SegmentEntry> = None; + let mut first_ro: Option<&SegmentEntry> = None; + + for (pos, id) in ordered_sections.iter().enumerate() { + let phdrs = &output_sections.section_infos.get(*id).phdrs; + for &phdr_name in phdrs { + let Some(entry) = segments_map.get(phdr_name) else { + continue; + }; + let seg_idx = entry.id.as_usize(); + if entry.ptype == pt::LOAD.0 && first_load.is_none() { + first_load = Some(entry.id); + } + if ends[seg_idx].is_none() { + starts[seg_idx] = Some(pos); + } + ends[seg_idx] = Some(pos); + if entry.ptype == pt::LOAD.0 { + if (entry.flags & pf::EXECUTABLE.0) != 0 + && first_exec.is_none_or(|e| e.flags & pf::WRITABLE.0 != 0) + { + first_exec = Some(entry); + } else if (entry.flags & pf::WRITABLE.0) != 0 + && first_rw.is_none_or(|e| e.flags & pf::EXECUTABLE.0 != 0) + { + first_rw = Some(entry); + } else if first_ro.is_none_or(|e| e.flags != pf::READABLE.0) { + first_ro = Some(entry); } + } + } + } - builder.add_segment_with_sections( - ptype as u32, - flags, - sections, - output_sections, - ); + if first_load.is_none() { + bail!("Final Link failed: bad value"); + } + + for &hdr_id in &[ + output_section_id::FILE_HEADER, + output_section_id::PROGRAM_HEADERS, + output_section_id::SECTION_HEADERS, + ] { + builder.push_event(OrderEvent::Section(hdr_id)); + } + + let update_flags = |builder: &mut OutputOrderBuilder, + sections: &[OutputSectionId], + segment: ProgramSegmentId| { + if segment_has_explicit_flags[segment.as_usize()] { + return; + } + for §ion_id in sections { + let info = output_sections.section_infos.get(section_id); + let flags = Self::get_segment_flags_for_section(&info.section_attributes.flags); + builder.get_segment_mut(segment).segment_flags |= SegmentFlags(flags); + } + }; + + for (pos, section_id) in ordered_sections.iter().enumerate() { + let section_id = *section_id; + + for (seg_idx, segment) in starts.iter().enumerate().take(num_phdrs) { + if *segment == Some(pos) { + builder.push_event(OrderEvent::SegmentStart(ProgramSegmentId::new(seg_idx))); + } + } + + builder.add_section(section_id); + + for (seg_idx, segment) in ends.iter().enumerate().take(num_phdrs) { + if *segment == Some(pos) { + let seg_id = ProgramSegmentId::new(seg_idx); + if Some(seg_id) == first_exec.map_or(first_load, |e| Some(e.id)) { + update_flags(&mut builder, &custom.exec, seg_id); + builder.add_sections(&custom.exec); + } + if Some(seg_id) == first_rw.map_or(first_load, |e| Some(e.id)) { + update_flags(&mut builder, &custom.tdata, seg_id); + update_flags(&mut builder, &custom.tbss, seg_id); + update_flags(&mut builder, &custom.data, seg_id); + update_flags(&mut builder, &custom.bss, seg_id); + builder.add_sections(&custom.tdata); + builder.add_sections(&custom.tbss); + builder.add_sections(&custom.data); + builder.add_sections(&custom.bss); + } + if Some(seg_id) == first_ro.map_or(first_load, |e| Some(e.id)) { + update_flags(&mut builder, &custom.ro, seg_id); + builder.add_sections(&custom.ro); + } + builder.push_event(OrderEvent::SegmentEnd(seg_id)); } } } for &id in &custom.nonalloc { - builder.add_section(id); + if id != output_section_id::RISCV_ATTRIBUTES { + builder.add_section(id); + } } - builder.add_segment_with_sections( - pt::RISCV_ATTRIBUTES.0, - pf::READABLE.0, - &[output_section_id::RISCV_ATTRIBUTES], - output_sections, + for segment in segments_map.values() { + if !segment.is_emitted { + builder.push_event(OrderEvent::SegmentStart(segment.id)); + builder.push_event(OrderEvent::SegmentEnd(segment.id)); + } + } + + let riscv_segment = builder.add_custom_segment( + ::from_linker_script( + pt::RISCV_ATTRIBUTES.0, + pf::READABLE.0, + ), ); + builder.push_event(OrderEvent::SegmentStart(riscv_segment)); + builder.add_section(output_section_id::RISCV_ATTRIBUTES); + builder.push_event(OrderEvent::SegmentEnd(riscv_segment)); + Ok(builder.build()) } diff --git a/libwild/src/expression_eval.rs b/libwild/src/expression_eval.rs index 20a883e1e..3dc8d077a 100644 --- a/libwild/src/expression_eval.rs +++ b/libwild/src/expression_eval.rs @@ -783,6 +783,7 @@ mod tests { memory_regions: Vec::new(), program_headers: Vec::new(), location_counters: Vec::new(), + ordered_sections: Vec::new(), }, symbol_id_range: SymbolIdRange::empty(), file_id: FileId::new(0, 0), diff --git a/libwild/src/layout.rs b/libwild/src/layout.rs index d65c2d2df..8567b344d 100644 --- a/libwild/src/layout.rs +++ b/libwild/src/layout.rs @@ -1938,6 +1938,9 @@ fn compute_segment_layout<'data, P: Platform>( let section_info = output_sections.output_info(section_id); if active_segments.iter().all(|s| s.is_none()) { + if output_order.has_custom_phdrs() { + continue; + } ensure!( section_layout.mem_offset == 0, "Expected zero address for section {} not present in any program segment.", @@ -2006,13 +2009,24 @@ fn compute_segment_layout<'data, P: Platform>( .map(|&id| { let r = &complete[id.as_usize()]; - let sizes = OutputRecordLayout { - file_size: r.file_end - r.file_start, - mem_size: r.mem_end - r.mem_start, - alignment: r.alignment, - file_offset: r.file_start, - mem_offset: r.mem_start, - lma_offset: r.lma_start, + let sizes = if r.file_start <= r.file_end { + OutputRecordLayout { + file_size: r.file_end - r.file_start, + mem_size: r.mem_end - r.mem_start, + alignment: r.alignment, + file_offset: r.file_start, + mem_offset: r.mem_start, + lma_offset: r.lma_start, + } + } else { + OutputRecordLayout { + file_size: 0, + mem_size: 0, + alignment: r.alignment, + file_offset: 0, + mem_offset: 0, + lma_offset: 0, + } }; if program_segments.is_tls_segment(id) { @@ -2023,7 +2037,11 @@ fn compute_segment_layout<'data, P: Platform>( }) .collect_vec(); - segments.sort_by_key(|s| program_segments.order_key(s.id, s.sizes.mem_offset)); + if output_order.has_custom_phdrs() { + segments.sort_by_key(|s| s.id); + } else { + segments.sort_by_key(|s| program_segments.order_key(s.id, s.sizes.mem_offset)); + } Ok(SegmentLayouts { segments, @@ -3389,34 +3407,41 @@ impl<'data, P: Platform> PreludeLayoutState<'data, P> { output_sections.output_section_indexes = output_section_indexes; // Determine which program segments contain sections that we're keeping. - let mut keep_segments = program_segments - .iter() - .map(|details| details.always_keep()) - .collect_vec(); - let mut active_segments = Vec::with_capacity(4); - for event in output_order { - match event { - OrderEvent::SegmentStart(segment_id) => active_segments.push(segment_id), - OrderEvent::SegmentEnd(segment_id) => active_segments.retain(|a| *a != segment_id), - OrderEvent::Section(section_id) => { - if *keep_sections.get(section_id) { - for segment_id in &active_segments { - keep_segments[segment_id.as_usize()] = true; + let mut keep_segments = if program_segments.has_custom_phdrs() { + vec![true; program_segments.len()] + } else { + let mut keep_segments = program_segments + .iter() + .map(|details| details.always_keep()) + .collect_vec(); + let mut active_segments = Vec::with_capacity(4); + for event in output_order { + match event { + OrderEvent::SegmentStart(segment_id) => active_segments.push(segment_id), + OrderEvent::SegmentEnd(segment_id) => { + active_segments.retain(|a| *a != segment_id); + } + OrderEvent::Section(section_id) => { + if *keep_sections.get(section_id) { + for segment_id in &active_segments { + keep_segments[segment_id.as_usize()] = true; + } + active_segments.clear(); } - active_segments.clear(); } + OrderEvent::SetLocation(..) + | OrderEvent::SetLocationRelative(..) + | OrderEvent::SetSectionAddress(_) => {} } - OrderEvent::SetLocation(..) - | OrderEvent::SetLocationRelative(..) - | OrderEvent::SetSectionAddress(_) => {} } - } - - if !resources.symbol_db.args.should_output_partial_object() { - // Always keep the program headers segment even though we don't emit any sections in it. - keep_segments[0] = true; - } + if !resources.symbol_db.args.should_output_partial_object() { + // Always keep the program headers segment even though we don't emit any sections in + // it. + keep_segments[0] = true; + } + keep_segments + }; P::update_segment_keep_list( program_segments, &mut keep_segments, @@ -5838,16 +5863,12 @@ fn test_no_disallowed_overlaps() { use crate::elf::Elf; use crate::output_section_id::OrderEvent; - let mut output_sections = OutputSections::::with_base_address(0x1000); - let (output_order, program_segments) = output_sections - .output_order( - crate::output_kind::OutputKind::StaticExecutable( - crate::args::RelocationModel::NonRelocatable, - ), - &[], - &[], - ) - .unwrap(); + let output_kind = crate::output_kind::OutputKind::StaticExecutable( + crate::args::RelocationModel::NonRelocatable, + ); + let mut output_sections = OutputSections::::with_base_address(0x1000, output_kind); + let (output_order, program_segments) = + output_sections.output_order(output_kind, &[], &[]).unwrap(); let mut args = crate::args::elf::ElfArgs::default(); if args.arch == crate::arch::Architecture::Unsupported { args.arch = crate::arch::Architecture::X86_64; @@ -5992,7 +6013,7 @@ fn verify_consistent_allocation_handling( output_kind: OutputKind, args: &P::Args, ) -> Result { - let output_sections = OutputSections::with_base_address(0); + let output_sections = OutputSections::with_base_address(0, output_kind); let (output_order, _program_segments) = output_sections.output_order(output_kind, &[], &[])?; let mut mem_sizes = output_sections.new_part_map(); P::allocate_resolution(flags, &mut mem_sizes, output_kind, args); diff --git a/libwild/src/layout_rules.rs b/libwild/src/layout_rules.rs index efd16f958..224b36608 100644 --- a/libwild/src/layout_rules.rs +++ b/libwild/src/layout_rules.rs @@ -109,6 +109,20 @@ pub(crate) enum SectionRuleOutcome { SortedSection(SectionOutputInfo), } +impl SectionRuleOutcome { + pub(crate) fn section_rule_from_id( + section_id: OutputSectionId, + output_info: SectionOutputInfo, + ) -> SectionRuleOutcome { + match section_id { + output_section_id::EH_FRAME => SectionRuleOutcome::EhFrame, + output_section_id::NOTE_GNU_PROPERTY => SectionRuleOutcome::NoteGnuProperty, + output_section_id::RISCV_ATTRIBUTES => SectionRuleOutcome::RiscVAttribute, + _ => crate::layout_rules::SectionRuleOutcome::Section(output_info), + } + } +} + #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub(crate) struct SectionOutputInfo { pub(crate) section_id: OutputSectionId, @@ -171,6 +185,7 @@ impl<'data> LayoutRulesBuilder<'data> { let mut memory_regions = Vec::new(); let mut program_headers = Vec::new(); let mut location_counters = Vec::new(); + let mut ordered_sections = Vec::new(); let mut current_section_id = None; let mut loc = SymbolLoc::FirstSection; @@ -196,7 +211,7 @@ impl<'data> LayoutRulesBuilder<'data> { symbol_defs.push(crate::parsing::InternalSymDefInfo::new(placement, name)); } else if let linker_script::Command::Sections(sections) = cmd { let mut section_start_lc_idx = last_lc_idx; - + let mut prev_phdrs = Vec::new(); for sec_cmd in §ions.commands { match sec_cmd { SectionCommand::Section(sec) => { @@ -241,14 +256,18 @@ impl<'data> LayoutRulesBuilder<'data> { }) .transpose()?; + if !sec.phdrs.is_empty() { + prev_phdrs = sec.phdrs.clone(); + } let primary_section_id = output_sections.add_named_section( SectionName(sec.output_section_name), min_alignment, - sec.phdr, sec.region, - Some(location_info), + Some(&location_info), fill_value, + prev_phdrs.clone(), ); + ordered_sections.push(primary_section_id); current_section_id = Some(primary_section_id); loc = SymbolLoc::SectionEnd(primary_section_id); @@ -291,10 +310,10 @@ impl<'data> LayoutRulesBuilder<'data> { sorted: pattern.sorted, }; - let outcome = - crate::layout_rules::SectionRuleOutcome::Section( - output_info, - ); + let outcome = SectionRuleOutcome::section_rule_from_id( + primary_section_id, + output_info, + ); self.add_section_rule(SectionRule::new( pattern.name, @@ -420,6 +439,7 @@ impl<'data> LayoutRulesBuilder<'data> { memory_regions, program_headers, location_counters, + ordered_sections, }) } diff --git a/libwild/src/lib.rs b/libwild/src/lib.rs index 7e1498fc2..8728812c4 100644 --- a/libwild/src/lib.rs +++ b/libwild/src/lib.rs @@ -299,7 +299,7 @@ impl Linker { let mut output = file_writer::Output::new(args, output_kind); let mut output_sections = - OutputSections::with_base_address(A::start_memory_address(output_kind)); + OutputSections::with_base_address(A::start_memory_address(output_kind), output_kind); output_sections.set_rosegment(args.rosegment()); let mut layout_rules_builder = LayoutRulesBuilder::default(); diff --git a/libwild/src/linker_script.rs b/libwild/src/linker_script.rs index a11e67270..e4a90fb84 100644 --- a/libwild/src/linker_script.rs +++ b/libwild/src/linker_script.rs @@ -108,7 +108,7 @@ pub(crate) struct Section<'a> { pub(crate) commands: Vec>, pub(crate) alignment: Option, pub(crate) start_address_expression: Option>, - pub(crate) phdr: Option<&'a [u8]>, + pub(crate) phdrs: Vec<&'a [u8]>, pub(crate) at_address: Option>, pub(crate) region: Option<&'a [u8]>, pub(crate) fill: Option>, @@ -1125,13 +1125,13 @@ fn parse_section_command<'input>( skip_comments_and_whitespace(input)?; - let mut phdr = None; + let mut phdrs = vec![]; let mut region = None; let mut fill = None; while let Some(prefix) = opt(alt((b":", b">", b"="))).parse_next(input)? { skip_comments_and_whitespace(input)?; match prefix { - b":" => phdr = Some(parse_token(input)?), + b":" => phdrs.push(parse_token(input)?), b">" => region = Some(parse_token(input)?), b"=" => fill = Some(parse_fill(input)?), _ => unreachable!(), @@ -1139,7 +1139,6 @@ fn parse_section_command<'input>( skip_comments_and_whitespace(input)?; } - skip_comments_and_whitespace(input)?; opt(',').parse_next(input)?; skip_comments_and_whitespace(input)?; @@ -1148,7 +1147,7 @@ fn parse_section_command<'input>( commands, alignment, start_address_expression, - phdr, + phdrs, at_address, region, fill, @@ -1530,7 +1529,7 @@ mod tests { ], alignment: None, start_address_expression: None, - phdr: None, + phdrs: vec![], at_address: None, region: None, fill: None, @@ -1554,7 +1553,7 @@ mod tests { })], alignment: Some(Alignment::new(8).unwrap()), start_address_expression: Some(Expression::Number(0)), - phdr: None, + phdrs: vec![], at_address: None, region: None, fill: None, @@ -1622,7 +1621,7 @@ mod tests { ], alignment: Some(Alignment::new(8).unwrap()), start_address_expression: None, - phdr: None, + phdrs: vec![], at_address: None, region: None, fill: None, @@ -1774,7 +1773,7 @@ mod tests { ], alignment: None, start_address_expression: None, - phdr: None, + phdrs: vec![], at_address: None, region: None, fill: None, @@ -1798,7 +1797,7 @@ mod tests { })], alignment: None, start_address_expression: None, - phdr: None, + phdrs: vec![], at_address: None, region: None, fill: None, @@ -1822,7 +1821,7 @@ mod tests { })], alignment: None, start_address_expression: None, - phdr: None, + phdrs: vec![], at_address: None, region: None, fill: None, @@ -1854,7 +1853,7 @@ mod tests { })], alignment: None, start_address_expression: None, - phdr: None, + phdrs: vec![], at_address: None, region: None, fill: None, @@ -1897,7 +1896,7 @@ mod tests { })], alignment: None, start_address_expression: None, - phdr: None, + phdrs: vec![], at_address: None, region: None, fill: None, diff --git a/libwild/src/macho.rs b/libwild/src/macho.rs index 5194fecbd..6f6b1e46f 100644 --- a/libwild/src/macho.rs +++ b/libwild/src/macho.rs @@ -1151,9 +1151,9 @@ impl platform::Platform for MachO { min_alignment: d.min_alignment, location_info: None, secondary_order: None, - phdr_name: None, region_name: None, fill: None, + phdrs: Vec::new(), }) .collect() } diff --git a/libwild/src/output_section_id.rs b/libwild/src/output_section_id.rs index 61c1afa9c..4d4eaaec3 100644 --- a/libwild/src/output_section_id.rs +++ b/libwild/src/output_section_id.rs @@ -169,6 +169,8 @@ pub(crate) struct OutputSections<'data, P: Platform> { init_fini_by_priority: HashMap<(OutputSectionId, u16), OutputSectionId>, rosegment: bool, + + output_kind: OutputKind, } /// Encodes the order of output sections and the start and end of each program segment. This struct @@ -177,6 +179,7 @@ pub(crate) struct OutputSections<'data, P: Platform> { pub(crate) struct OutputOrder<'data> { events: Vec>, num_location_counters: usize, + has_custom_phdrs: bool, } pub(crate) struct OutputOrderDisplay<'a, 'data, P: Platform> { @@ -212,7 +215,7 @@ impl<'scope, 'data, P: Platform> OutputOrderBuilder<'scope, 'data, P> { ) -> Self { Self { events: Vec::new(), - program_segments: ProgramSegments::empty(), + program_segments: ProgramSegments::empty(has_custom_phdrs), output_sections, active_segment_kinds: vec![None; P::program_segment_defs().len()], active_segment_regions: vec![None; P::program_segment_defs().len()], @@ -438,51 +441,19 @@ impl<'scope, 'data, P: Platform> OutputOrderBuilder<'scope, 'data, P> { (stop, start) } - pub(crate) fn add_segment_with_sections( + pub(crate) fn push_event(&mut self, event: OrderEvent<'data>) { + self.events.push(event); + } + + pub(crate) fn add_custom_segment( &mut self, - ptype: u32, - pflags: u32, - sections: &[OutputSectionId], - output_sections: &OutputSections<'data, P>, + segment_def: P::ProgramSegmentDef, ) -> ProgramSegmentId { - let segment_id = self - .program_segments - .add_segment(P::ProgramSegmentDef::from_linker_script(ptype, pflags)); - self.events.push(OrderEvent::SegmentStart(segment_id)); - for section in sections { - let section_info = output_sections.section_infos.get(*section); - if let Some(ref loc_info) = section_info.location_info { - let (lc_start, lc_stop) = loc_info.location_counters; - self.emit_location_counters(lc_start, lc_stop); - } - self.events.push(OrderEvent::Section(*section)); - - let secondaries: &Vec = self.secondary.get(*section); - let mut keyed: Vec<(u16, OutputSectionId)> = secondaries - .iter() - .map(|&sid| { - let key_pri = match output_sections.secondary_order(sid) { - Some(crate::output_section_id::SecondaryOrder::InitFini { priority }) => { - priority - } - None => u16::MAX, - }; - (key_pri, sid) - }) - .collect(); - keyed.sort_by_key(|(pri, _sid)| *pri); - - for (_pri, sid) in keyed { - let sec_info = output_sections.output_info(sid); - if let Some(ref loc_info) = sec_info.location_info { - let (lc_start, lc_stop) = loc_info.location_counters; - self.emit_location_counters(lc_start, lc_stop); - } - self.events.push(OrderEvent::Section(sid)); - } - } - self.events.push(OrderEvent::SegmentEnd(segment_id)); - segment_id + self.program_segments.add_segment(segment_def) + } + + pub(crate) fn get_segment_mut(&mut self, id: ProgramSegmentId) -> &mut P::ProgramSegmentDef { + self.program_segments.segment_def_mut(id) } pub(crate) fn add_sections(&mut self, sections: &[OutputSectionId]) { @@ -512,6 +483,7 @@ impl<'scope, 'data, P: Platform> OutputOrderBuilder<'scope, 'data, P> { OutputOrder { events: self.events, num_location_counters: self.location_counters.len(), + has_custom_phdrs: self.has_custom_phdrs, }, self.program_segments, ) @@ -594,9 +566,9 @@ pub(crate) struct SectionOutputInfo<'data, P: Platform> { pub(crate) min_alignment: Alignment, pub(crate) location_info: Option>, pub(crate) secondary_order: Option, - pub(crate) phdr_name: Option<&'data [u8]>, pub(crate) region_name: Option<&'data [u8]>, pub(crate) fill: Option<[u8; 4]>, + pub(crate) phdrs: Vec<&'data [u8]>, } impl OutputSectionId { @@ -755,12 +727,17 @@ impl<'data, P: Platform> OutputSections<'data, P> { custom.name, custom.alignment, None, + location_info.as_ref(), None, - location_info, - None, + Vec::new(), ); - section_part_ids[custom.index.0] = section_id.part_id_with_alignment(custom.alignment); + let part_id = if section_id.is_regular() { + section_id.part_id_with_alignment(custom.alignment) + } else { + section_id.base_part_id() + }; + section_part_ids[custom.index.0] = part_id; } } @@ -793,25 +770,61 @@ impl<'data, P: Platform> OutputSections<'data, P> { &mut self, name: SectionName<'data>, min_alignment: Alignment, - phdr_name: Option<&'data [u8]>, region_name: Option<&'data [u8]>, - location_info: Option>, + location_info: Option<&SectionLocationInfo<'data>>, fill: Option<[u8; 4]>, + phdrs: Vec<&'data [u8]>, ) -> OutputSectionId { - *self.custom_by_name.entry(name).or_insert_with(|| { - self.section_infos.add_new(SectionOutputInfo { - kind: SectionKind::Primary(name), - // Section flags and type will be filled in based on the attributes of the sections - // that get placed into this output section. - section_attributes: Default::default(), - min_alignment, - location_info, - secondary_order: None, - phdr_name, - region_name, - fill, + let mut resolved_id = None; + if !self.output_kind.is_partial_object() + && let Some(builtin_id) = (0..NUM_SINGLE_PART_SECTIONS as usize) + .map(OutputSectionId::from_usize) + .find(|&bid| self.name(bid) == Some(name)) + { + resolved_id = Some(builtin_id); + } + + *self + .custom_by_name + .entry(name) + .and_modify(|s| { + let info = self.section_infos.get_mut(*s); + info.min_alignment = info.min_alignment.max(min_alignment); + info.region_name = region_name.or(info.region_name); + if location_info.is_some() { + info.location_info = location_info.cloned(); + } + info.fill = fill.or(info.fill); + if !phdrs.is_empty() { + info.phdrs = phdrs.clone(); + } + }) + .or_insert_with(|| { + if let Some(builtin_id) = resolved_id { + let info = self.section_infos.get_mut(builtin_id); + info.min_alignment = info.min_alignment.max(min_alignment); + info.region_name = region_name.or(info.region_name); + if location_info.is_some() { + info.location_info = location_info.cloned(); + } + info.fill = fill.or(info.fill); + if !phdrs.is_empty() { + info.phdrs = phdrs.clone(); + } + builtin_id + } else { + self.section_infos.add_new(SectionOutputInfo { + kind: SectionKind::Primary(name), + section_attributes: Default::default(), + min_alignment, + location_info: location_info.cloned(), + secondary_order: None, + region_name, + fill, + phdrs, + }) + } }) - }) } pub(crate) fn add_secondary_section( @@ -830,13 +843,13 @@ impl<'data, P: Platform> OutputSections<'data, P> { min_alignment, location_info, secondary_order, - phdr_name: None, region_name: primary_info.region_name, fill: primary_info.fill, + phdrs: Vec::new(), }) } - pub(crate) fn with_base_address(base_address: u64) -> Self { + pub(crate) fn with_base_address(base_address: u64, output_kind: OutputKind) -> Self { let section_infos = P::built_in_section_infos(); let base_address = Expression::Number(base_address); @@ -847,6 +860,7 @@ impl<'data, P: Platform> OutputSections<'data, P> { output_section_indexes: Default::default(), init_fini_by_priority: HashMap::new(), rosegment: true, + output_kind, } } @@ -898,9 +912,6 @@ impl<'data, P: Platform> OutputSections<'data, P> { let mut secondary: OutputSectionMap> = self.new_section_map(); - let mut phdr_map: Option>> = - has_custom_phdrs.then(HashMap::new); - self.section_infos.for_each(|id, info| { if let SectionKind::Secondary(primary) = info.kind { secondary.get_mut(primary).push(id); @@ -908,20 +919,15 @@ impl<'data, P: Platform> OutputSections<'data, P> { } if has_custom_phdrs { - if let Some(phdr_name) = info.phdr_name { - phdr_map - .as_mut() - .unwrap() - .entry(phdr_name) - .or_default() - .push(id); + if !info.phdrs.is_empty() { return; } - if id == FILE_HEADER - || id == PROGRAM_HEADERS - || id == SECTION_HEADERS - || id == RISCV_ATTRIBUTES + if matches!(id, FILE_HEADER | PROGRAM_HEADERS | SECTION_HEADERS) { + return; + } else if id.as_usize() < NUM_BUILT_IN_SECTIONS + && let Some(name) = self.name(id) + && self.custom_name_to_id(name).is_some() { return; } @@ -929,21 +935,22 @@ impl<'data, P: Platform> OutputSections<'data, P> { return; } - if info.section_attributes.is_executable() { + let attr = info.section_attributes; + if attr.is_executable() { custom.exec.push(id); - } else if info.section_attributes.is_tls() { - if info.section_attributes.is_no_bits() { + } else if attr.is_tls() { + if attr.is_no_bits() { custom.tbss.push(id); } else { custom.tdata.push(id); } - } else if !info.section_attributes.is_writable() { - if info.section_attributes.is_alloc() { + } else if !attr.is_writable() { + if attr.is_alloc() { custom.ro.push(id); } else { custom.nonalloc.push(id); } - } else if info.section_attributes.is_no_bits() { + } else if attr.is_no_bits() { custom.bss.push(id); } else { custom.data.push(id); @@ -957,7 +964,6 @@ impl<'data, P: Platform> OutputSections<'data, P> { self, &secondary, linker_scripts, - &mut phdr_map.unwrap(), location_counters, ) } else { @@ -1087,7 +1093,10 @@ impl<'data, P: Platform> OutputSections<'data, P> { pub(crate) fn for_testing() -> OutputSections<'static, crate::elf::Elf> { use crate::elf::Elf; - let mut output_sections = OutputSections::::with_base_address(0x1000); + let output_kind = crate::output_kind::OutputKind::StaticExecutable( + crate::args::RelocationModel::NonRelocatable, + ); + let mut output_sections = OutputSections::::with_base_address(0x1000, output_kind); let mut add_name = |name: &'static str| { output_sections.add_named_section( SectionName(name.as_bytes()), @@ -1095,7 +1104,7 @@ impl<'data, P: Platform> OutputSections<'data, P> { None, None, None, - None, + Vec::new(), ) }; add_name("ro"); @@ -1133,6 +1142,10 @@ impl<'data> OutputOrder<'data> { self.num_location_counters } + pub(crate) fn has_custom_phdrs(&self) -> bool { + self.has_custom_phdrs + } + pub(crate) fn display<'a, P: Platform>( &'a self, sections: &'a OutputSections<'data, P>, diff --git a/libwild/src/parsing.rs b/libwild/src/parsing.rs index 7ce0d341c..7df93b05a 100644 --- a/libwild/src/parsing.rs +++ b/libwild/src/parsing.rs @@ -58,6 +58,7 @@ pub(crate) struct ProcessedLinkerScript<'data, P: Platform> { pub(crate) memory_regions: Vec>, pub(crate) program_headers: Vec>, pub(crate) location_counters: Vec>, + pub(crate) ordered_sections: Vec, } #[derive(Debug)] diff --git a/libwild/src/platform.rs b/libwild/src/platform.rs index eeee7ac7f..2364bdb5a 100644 --- a/libwild/src/platform.rs +++ b/libwild/src/platform.rs @@ -798,7 +798,6 @@ pub(crate) trait Platform: output_sections: &OutputSections<'data, Self>, secondary: &OutputSectionMap>, _linker_scripts: &[&SequencedLinkerScript<'data, Self>], - _phdr_map: &mut hashbrown::HashMap<&[u8], Vec>, location_counters: &[crate::layout_rules::LocationCounter<'data>], ) -> Result<(OutputOrder<'data>, ProgramSegments)> { Ok(Self::build_output_order_and_program_segments( @@ -879,6 +878,10 @@ pub(crate) trait Platform: ) -> Self::ObjectLayoutStateExt<'data> { Default::default() } + + fn get_segment_flags_for_section(_section_flags: &Self::SectionFlags) -> u32 { + 0 + } } /// Abstracts over the different object file formats that we support (or may support). e.g. ELF. diff --git a/libwild/src/program_segments.rs b/libwild/src/program_segments.rs index 8871a473c..b2f1b7904 100644 --- a/libwild/src/program_segments.rs +++ b/libwild/src/program_segments.rs @@ -7,12 +7,14 @@ pub(crate) struct ProgramSegmentId(u8); #[derive(Debug)] pub(crate) struct ProgramSegments { program_segment_details: Vec, + has_custom_phdrs: bool, } impl ProgramSegments { - pub(crate) fn empty() -> ProgramSegments { + pub(crate) fn empty(has_custom_phdrs: bool) -> ProgramSegments { Self { program_segment_details: Vec::new(), + has_custom_phdrs, } } @@ -24,6 +26,10 @@ impl ProgramSegments { &self.program_segment_details[segment_id.as_usize()] } + pub(crate) fn segment_def_mut(&mut self, segment_id: ProgramSegmentId) -> &mut T { + &mut self.program_segment_details[segment_id.as_usize()] + } + pub(crate) fn add_segment(&mut self, segment_def: T) -> ProgramSegmentId { let id = ProgramSegmentId::new(self.program_segment_details.len()); self.program_segment_details.push(segment_def); @@ -53,6 +59,10 @@ impl ProgramSegments { pub(crate) fn iter(&self) -> impl Iterator { self.program_segment_details.iter().copied() } + + pub(crate) fn has_custom_phdrs(&self) -> bool { + self.has_custom_phdrs + } } impl ProgramSegmentId { @@ -85,3 +95,11 @@ impl<'a, T: platform::ProgramSegmentDef> IntoIterator for &'a ProgramSegments self.program_segment_details.iter() } } + +#[derive(Clone, Copy, Debug)] +pub(crate) struct SegmentEntry { + pub(crate) id: crate::program_segments::ProgramSegmentId, + pub(crate) ptype: u32, + pub(crate) flags: u32, + pub(crate) is_emitted: bool, +} diff --git a/libwild/src/wasm.rs b/libwild/src/wasm.rs index 3dda54f82..57abafc84 100644 --- a/libwild/src/wasm.rs +++ b/libwild/src/wasm.rs @@ -4436,9 +4436,9 @@ impl platform::Platform for Wasm { min_alignment: crate::alignment::MIN, location_info: None, secondary_order: None, - phdr_name: None, region_name: None, fill: None, + phdrs: Vec::new(), }) .collect() } diff --git a/wild/tests/integration_tests.rs b/wild/tests/integration_tests.rs index 9b2da2df2..c641a2084 100644 --- a/wild/tests/integration_tests.rs +++ b/wild/tests/integration_tests.rs @@ -5046,9 +5046,7 @@ impl Assertions { let sh_addr = section.address(); let sh_size = section.size(); - let Some((sh_offset, sh_filesz)) = section.file_range() else { - continue; - }; + let (sh_offset, sh_filesz) = section.file_range().unwrap_or((0, 0)); let is_alloc = match section.flags() { object::SectionFlags::Elf { sh_flags, .. } => { @@ -5066,7 +5064,7 @@ impl Assertions { && sh_addr + sh_size <= p_vaddr + p_memsz && p_memsz > 0; let in_file = sh_offset >= p_offset - && sh_offset + sh_filesz <= p_offset + p_filesz + && sh_offset.saturating_add(sh_filesz) <= p_offset.saturating_add(p_filesz) && p_filesz > 0 && sh_filesz > 0; diff --git a/wild/tests/sources/elf/linker-script-phdrs/linker-script-phdrs-no-load.ld b/wild/tests/sources/elf/linker-script-phdrs/linker-script-phdrs-no-load.ld new file mode 100644 index 000000000..e2f682993 --- /dev/null +++ b/wild/tests/sources/elf/linker-script-phdrs/linker-script-phdrs-no-load.ld @@ -0,0 +1,21 @@ +PHDRS { + text PT_DYNAMIC; +} + +SECTIONS { + .text : { + *(.text*) + } :text + + .data : { + *(.data*) + } + + .bss : { + *(.bss*) + } + + .rodata : { + *(.rodata*) + } +} diff --git a/wild/tests/sources/elf/linker-script-phdrs/linker-script-phdrs-single-load-with-flag.ld b/wild/tests/sources/elf/linker-script-phdrs/linker-script-phdrs-single-load-with-flag.ld new file mode 100644 index 000000000..5b6739b6b --- /dev/null +++ b/wild/tests/sources/elf/linker-script-phdrs/linker-script-phdrs-single-load-with-flag.ld @@ -0,0 +1,21 @@ +PHDRS { + text PT_LOAD FLAGS(5); +} + +SECTIONS { + .text : { + *(.text*) + } :text + + .data : { + *(.data*) + } + + .bss : { + *(.bss*) + } + + .rodata : { + *(.rodata*) + } +} diff --git a/wild/tests/sources/elf/linker-script-phdrs/linker-script-phdrs-single-load.ld b/wild/tests/sources/elf/linker-script-phdrs/linker-script-phdrs-single-load.ld new file mode 100644 index 000000000..47bcf7b44 --- /dev/null +++ b/wild/tests/sources/elf/linker-script-phdrs/linker-script-phdrs-single-load.ld @@ -0,0 +1,21 @@ +PHDRS { + text PT_LOAD; +} + +SECTIONS { + .text : { + *(.text*) + } :text + + .data : { + *(.data*) + } + + .bss : { + *(.bss*) + } + + .rodata : { + *(.rodata*) + } +} diff --git a/wild/tests/sources/elf/linker-script-phdrs/linker-script-phdrs.c b/wild/tests/sources/elf/linker-script-phdrs/linker-script-phdrs.c index 912230c10..7e1da7d15 100644 --- a/wild/tests/sources/elf/linker-script-phdrs/linker-script-phdrs.c +++ b/wild/tests/sources/elf/linker-script-phdrs/linker-script-phdrs.c @@ -1,13 +1,18 @@ -//#Config:default +//#AbstractConfig:default //#Mode:dynamic //#RunEnabled:false //#CompArgs:-fPIE -fPIC -//#LinkArgs:-shared -z now -T ./linker-script-phdrs.ld --defsym=is_riscv=0 //#DiffIgnore:section.got +// Skip the .custom section, since it is forcefully excluded from any segments. +//#DiffIgnore:section-diff-failed..custom + +//#Config:nophdrs:default +//#LinkArgs:-shared -z now -T ./linker-script-phdrs.ld --defsym=is_riscv=0 //#ExpectProgramHeader:LOAD flags=RX,sections=[.text] -//#ExpectProgramHeader:LOAD flags=RW,sections=[*] -//#ExpectProgramHeader:LOAD flags=R,sections=[.rodata,*] -//#NoProgramHeader:DYNAMIC +//#ExpectProgramHeader:DYNAMIC flags=RW,sections=[.dynamic,*] +//#ExpectProgramHeader:LOAD flags=RW,sections=[.bss,*] +//#ExpectProgramHeader:LOAD flags=R,sections=[.rodata,.dynamic,*] +//#ExpectProgramHeader:LOAD sections=[] //#NoProgramHeader:PHDR //#NoProgramHeader:NOTE //#NoProgramHeader:GNU_STACK @@ -15,13 +20,28 @@ //#NoProgramHeader:GNU_PROPERTY //#SkipArch:riscv64 -//#Config:riscv:default +//#Config:riscv:nophdrs //#Arch:riscv64 //#ExpectProgramHeader:RISCV_ATTRIBUTES flags=R,sections=[.riscv.attributes] //#LinkArgs:-shared -z now -T ./linker-script-phdrs.ld --defsym=is_riscv=1 +//#Config:single-load:default +//#LinkArgs:-shared -z now -T ./linker-script-phdrs-single-load.ld +//#ExpectProgramHeader:LOAD flags=RWX,sections=[.text,.rodata,.dynamic,*] + +//#Config:single-load-with-flag:default +//#LinkArgs:-shared -z now -T ./linker-script-phdrs-single-load-with-flag.ld +//#ExpectProgramHeader:LOAD flags=RX,sections=[.text,.rodata,.dynamic,*] + +//#Config:no-load:default +//#LinkArgs:-shared -z now -T ./linker-script-phdrs-no-load.ld +//#ExpectError:(?i-u)final link failed: bad value + const char message[] = "Hello PHDRS"; +char message2[10]; -int foo(void) { return 42; } +int foo(void) { return message2[0] + 42; } const char* bar() { return &message[0]; } + +__attribute__((used, section(".custom"))) int baz() { return 42; } diff --git a/wild/tests/sources/elf/linker-script-phdrs/linker-script-phdrs.ld b/wild/tests/sources/elf/linker-script-phdrs/linker-script-phdrs.ld index 49e04e67b..187258ca7 100644 --- a/wild/tests/sources/elf/linker-script-phdrs/linker-script-phdrs.ld +++ b/wild/tests/sources/elf/linker-script-phdrs/linker-script-phdrs.ld @@ -1,5 +1,7 @@ PHDRS { text PT_LOAD; + dynamic PT_DYNAMIC; + empty PT_LOAD; data PT_LOAD FLAGS(6); /* R + W */ rodata PT_LOAD FLAGS(4); /* R */ } @@ -17,12 +19,20 @@ SECTIONS { .bss : { *(.bss*) - } :data + } + + .custom : { + *(.custom) + } :NONE + + .dynamic : { + *(.dynamic) + } :rodata :dynamic .rodata : { *(.rodata*) - } :rodata + } :rodata :NONE } -ASSERT(start_of_sections == 0x40 + ((3 + is_riscv) * 0x38), "Wrong start of sections"); -ASSERT(SIZEOF_HEADERS == 0x40 + ((3 + is_riscv) * 0x38), "Wrong size of headers"); +ASSERT(start_of_sections == 0x40 + ((5 + is_riscv) * 0x38), "Wrong start of sections"); +ASSERT(SIZEOF_HEADERS == 0x40 + ((5 + is_riscv) * 0x38), "Wrong size of headers"); diff --git a/wild/tests/sources/elf/script-sort/script-sort.ld b/wild/tests/sources/elf/script-sort/script-sort.ld index 3b2b48da3..25b9ce706 100644 --- a/wild/tests/sources/elf/script-sort/script-sort.ld +++ b/wild/tests/sources/elf/script-sort/script-sort.ld @@ -14,7 +14,7 @@ SECTIONS /* Ensure valid alignment for subsequent sections */ - . = 0x410000; + . = 0x420000; .eh_frame : { *(.eh_frame) } .note.gnu.property : { *(.note.gnu.property) }