diff --git a/libwild/src/macho.rs b/libwild/src/macho.rs index 98e6d80b5..aad4dd876 100644 --- a/libwild/src/macho.rs +++ b/libwild/src/macho.rs @@ -54,8 +54,11 @@ const LE: Endianness = Endianness::Little; /// offsets right after that (1GiB). pub(crate) const MACHO_START_MEM_ADDRESS: u64 = 0x1_0000_0000; +/// The command alignment is 8B for 64-bit platforms. +pub(crate) const MACHO_COMMAND_ALIGNMENT: usize = 8; + /// A path to the default dynamic linker. -pub(crate) const DYLINKER_PATH: &str = "/usr/lib/dyld"; +pub(crate) const DYLINKER_PATH: &[u8] = b"/usr/lib/dyld"; pub(crate) const DEFAULT_SEGMENT_COUNT: usize = 4; pub(crate) const CHAINED_FIXUP_TABLE_SIZE: u64 = (size_of::() + size_of::() * (DEFAULT_SEGMENT_COUNT + 1 + 1)) as u64; @@ -729,9 +732,7 @@ impl platform::ProgramSegmentDef for ProgramSegmentDef { | output_section_id::DYLD_CHAINED_FIXUPS => SegmentType::LoadCommands, output_section_id::TEXT | output_section_id::CSTRING => SegmentType::TextSections, output_section_id::DATA => SegmentType::DataSections, - output_section_id::CHAINED_FIXUP_TABLE | output_section_id::STRTAB => { - SegmentType::LinkeditSections - } + output_section_id::CHAINED_FIXUP_TABLE => SegmentType::LinkeditSections, _ => SegmentType::Unused, }; @@ -1167,7 +1168,8 @@ impl platform::Platform for MachO { sizes.increment(part_id::ENTRY_POINT, size_of::() as u64); sizes.increment( part_id::INTERP, - ((size_of::() + DYLINKER_PATH.len()).next_multiple_of(8)) as u64, + ((size_of::() + DYLINKER_PATH.len()) + .next_multiple_of(MACHO_COMMAND_ALIGNMENT)) as u64, ); sizes.increment( part_id::DYLD_CHAINED_FIXUPS, @@ -1228,12 +1230,6 @@ impl platform::Platform for MachO { symbol_db: &crate::symbol_db::SymbolDb, ) { common.allocate(part_id::CHAINED_FIXUP_TABLE, CHAINED_FIXUP_TABLE_SIZE); - // TODO: Just a filler for now that will ensure the __LINKEDIT takes 16KiB - find a better - // solution. - common.allocate( - part_id::STRTAB, - MACHO_PAGE_ALIGNMENT.value() - CHAINED_FIXUP_TABLE_SIZE, - ); } fn finalise_prelude_layout<'data>( @@ -1292,7 +1288,6 @@ impl platform::Platform for MachO { builder.add_section(output_section_id::CSTRING); builder.add_section(output_section_id::DATA); // The rest (e.g. symbol table, string table). - builder.add_section(output_section_id::STRTAB); builder.add_section(output_section_id::CHAINED_FIXUP_TABLE); builder.build() @@ -1381,11 +1376,6 @@ const SECTION_DEFINITIONS: [BuiltInSectionDetails; NUM_BUILT_IN_SECTIONS] = { target_segment_type: Some(SegmentType::LinkeditSections), ..DEFAULT_DEFS }; - defs[output_section_id::STRTAB.as_usize()] = BuiltInSectionDetails { - kind: SectionKind::Primary(SectionName(b"STRING_TABLE")), - target_segment_type: Some(SegmentType::LinkeditSections), - ..DEFAULT_DEFS - }; // Multi-part generated sections defs[output_section_id::SYMTAB_GLOBAL.as_usize()] = BuiltInSectionDetails { kind: SectionKind::Secondary(output_section_id::SYMTAB_LOCAL), diff --git a/libwild/src/macho_writer.rs b/libwild/src/macho_writer.rs index b525a0560..751dcbdd8 100644 --- a/libwild/src/macho_writer.rs +++ b/libwild/src/macho_writer.rs @@ -1,9 +1,5 @@ -// TODO -#![allow(unused_variables)] -#![allow(unused)] - -use crate::alignment::MACHO_PAGE_ALIGNMENT; use crate::bail; +use crate::ensure; use crate::error; use crate::error::Context; use crate::error::Result; @@ -26,6 +22,7 @@ use crate::macho::DyldChainedFixupsImporstFormat; use crate::macho::DylinkerCommand; use crate::macho::EntryPointCommand; use crate::macho::FileHeader; +use crate::macho::MACHO_COMMAND_ALIGNMENT; use crate::macho::MACHO_START_MEM_ADDRESS; use crate::macho::MachO; use crate::macho::SectionEntry; @@ -34,9 +31,6 @@ use crate::macho::SegmentSectionsInfo; use crate::macho::SegmentType; use crate::macho::get_segment_sections; use crate::output_section_id; -use crate::output_section_id::LINK_EDIT_SEGMENT; -use crate::output_section_id::OrderEvent; -use crate::output_section_id::OutputSectionId; use crate::output_section_id::SectionName; use crate::output_section_part_map::OutputSectionPartMap; use crate::output_trace::TraceOutput; @@ -48,7 +42,6 @@ use crate::resolution::SectionSlot; use crate::timing_phase; use crate::verbose_timing_phase; use object::BigEndian; -use object::Endian; use object::Endianness; use object::U32; use object::from_bytes_mut; @@ -67,7 +60,6 @@ use object::macho::SEG_TEXT; use object::slice_from_bytes_mut; use rayon::iter::IntoParallelIterator; use rayon::iter::ParallelIterator; -use std::io::Write; use tracing::debug_span; use zerocopy::FromZeros; @@ -102,7 +94,7 @@ fn write_file<'data, A: Arch>( file: &FileLayout<'data, MachO>, buffers: &mut OutputSectionPartMap<&mut [u8]>, layout: &MachOLayout<'data>, - trace: &TraceOutput, + _trace: &TraceOutput, ) -> Result { match file { FileLayout::Object(s) => { @@ -128,11 +120,6 @@ fn write_prelude<'data, A: Arch>( .0; populate_file_header::(layout, &prelude.header_info, header); - let pagezero_command: &mut SegmentCommand = - from_bytes_mut(buffers.get_mut(part_id::PAGEZERO_SEGMENT)) - .map_err(|_| error!("Invalid PAGEZERO segment allocation"))? - .0; - write_pagezero_command::(pagezero_command); write_segment_commands::(layout, buffers)?; let entry_point_command: &mut EntryPointCommand = @@ -171,9 +158,6 @@ fn write_prelude<'data, A: Arch>( .map_err(|_| error!("Invalid chained fixups starts allocation"))?; write_chained_fixup_table::(chained_fixups_header, starts_in_image)?; - // TODO: remove - buffers.get_mut(part_id::STRTAB).write_all(b"x")?; - Ok(()) } @@ -197,20 +181,6 @@ fn populate_file_header>( header.reserved = U32::new(LE, 0); } -fn write_pagezero_command>(command: &mut SegmentCommand) { - command.cmd.set(LE, LC_SEGMENT_64); - command.cmdsize.set(LE, size_of::() as u32); - command.segname[..SEG_PAGEZERO.len()].copy_from_slice(SEG_PAGEZERO.as_bytes()); - command.vmaddr.set(LE, 0); - command.vmsize.set(LE, MACHO_START_MEM_ADDRESS); - command.fileoff.set(LE, 0); - command.filesize.set(LE, 0); - command.maxprot.set(LE, 0); - command.initprot.set(LE, 0); - command.nsects.set(LE, 0); - command.flags.set(LE, 0); -} - fn split_segment_command_buffer( bytes: &mut [u8], section_count: usize, @@ -219,9 +189,10 @@ fn split_segment_command_buffer( from_bytes_mut(bytes).map_err(|_| error!("Invalid segment command allocation"))?; let (sections, rest) = slice_from_bytes_mut(rest, section_count) .map_err(|_| error!("Invalid segment section allocation"))?; - if !rest.is_empty() { - return Err(error!("Trailing bytes in segment command allocation")); - } + ensure!( + rest.is_empty(), + "Trailing bytes in segment command allocation" + ); Ok((command, sections)) } @@ -229,100 +200,146 @@ fn write_segment_commands>( layout: &MachOLayout, buffers: &mut OutputSectionPartMap<&mut [u8]>, ) -> Result { - for (part_id, seg_name, segment_type, segment_sections_type) in [ - ( - part_id::TEXT_SEGMENT, - SEG_TEXT, - SegmentType::Text, - SegmentType::TextSections, - ), - ( - part_id::DATA_SEGMENT, - SEG_DATA, - SegmentType::DataSections, - SegmentType::DataSections, - ), - ( - part_id::LINK_EDIT_SEGMENT, - SEG_LINKEDIT, - SegmentType::LinkeditSections, - SegmentType::LinkeditSections, - ), - ] { - // TODO: write comments - let segment_sections = get_segment_sections(layout, segment_sections_type).segment_sections; - let segment_size = get_segment_sections(layout, segment_type).segment_size; - - let section_count = if segment_sections_type == SegmentType::LinkeditSections { - 0 - } else { - segment_sections.len() - }; - let (segment_cmd, sections) = - split_segment_command_buffer(buffers.get_mut(part_id), section_count)?; - - let prot_flags = layout - .output_sections - .section_flags(part_id.output_section_id()) - .raw(); - - segment_cmd.cmd.set(LE, LC_SEGMENT_64); - segment_cmd.cmdsize.set( - LE, - (size_of::() + size_of::() * section_count) as u32, - ); - segment_cmd.segname[..seg_name.len()].copy_from_slice(seg_name.as_bytes()); - segment_cmd.segname[seg_name.len()..].zero(); - segment_cmd.vmaddr.set(LE, segment_size.mem_offset); - segment_cmd.vmsize.set( - LE, - segment_size - .mem_size - .next_multiple_of(MACHO_PAGE_ALIGNMENT.value()), - ); - segment_cmd.fileoff.set(LE, segment_size.file_offset as u64); - segment_cmd.filesize.set( - LE, - segment_size - .file_size - .next_multiple_of(MACHO_PAGE_ALIGNMENT.value() as usize) as u64, - ); - segment_cmd.maxprot.set(LE, prot_flags); - segment_cmd.initprot.set(LE, prot_flags); - segment_cmd.nsects.set(LE, segment_sections.len() as u32); - segment_cmd.flags.set(LE, 0); - - // The sections in __LINKEDIT are actually hidden and must be hidden (not exposed in the - // SEGMENT). - if segment_sections_type == SegmentType::LinkeditSections { - segment_cmd.nsects.set(LE, 0); - } else { - segment_cmd.nsects.set(LE, segment_sections.len() as u32); - for (section, (size, section_name, section_flags)) in - sections.iter_mut().zip(segment_sections) - { - let section_name = section_name - .ok_or_else(|| error!("section name must be known"))? - .0; - - section.segname[..seg_name.len()].copy_from_slice(seg_name.as_bytes()); - section.segname[seg_name.len()..].zero(); - section.sectname[..section_name.len()].copy_from_slice(section_name); - section.sectname[section_name.len()..].zero(); - section.addr.set(LE, size.mem_offset); - section.size.set(LE, size.mem_size); - section.offset.set(LE, size.file_offset as u32); - // TODO - section.align.set(LE, 0); - section.reloff.set(LE, 0); - section.nreloc.set(LE, 0); - section.flags.set(LE, section_flags.raw()); - section.reserved1.set(LE, 0); - section.reserved2.set(LE, 0); - section.reserved3.set(LE, 0); - } - } + let pagezero_segment = + split_segment_command_buffer(buffers.get_mut(part_id::PAGEZERO_SEGMENT), 0)?.0; + write_segment( + layout, + part_id::PAGEZERO_SEGMENT, + SEG_PAGEZERO, + pagezero_segment, + 0, + 0, + 0, + MACHO_START_MEM_ADDRESS, + 0, + ); + + let text_segment_sections = + get_segment_sections(layout, SegmentType::TextSections).segment_sections; + // The __TEXT segment in the layout includes also all the commands! + let text_segment_size = get_segment_sections(layout, SegmentType::Text).segment_size; + let (text_segment, text_sections) = split_segment_command_buffer( + buffers.get_mut(part_id::TEXT_SEGMENT), + text_segment_sections.len(), + )?; + write_segment( + layout, + part_id::TEXT_SEGMENT, + SEG_TEXT, + text_segment, + text_segment_size.file_offset as u64, + text_segment_size.file_size as u64, + text_segment_size.mem_offset, + text_segment_size.mem_size, + text_segment_sections.len(), + ); + write_sections(SEG_TEXT, text_sections, &text_segment_sections)?; + + let data_segment_sections = + get_segment_sections(layout, SegmentType::DataSections).segment_sections; + let data_segment_size = get_segment_sections(layout, SegmentType::DataSections).segment_size; + let (data_segment, data_sections) = split_segment_command_buffer( + buffers.get_mut(part_id::DATA_SEGMENT), + data_segment_sections.len(), + )?; + write_segment( + layout, + part_id::DATA_SEGMENT, + SEG_DATA, + data_segment, + data_segment_size.file_offset as u64, + data_segment_size.file_size as u64, + data_segment_size.mem_offset, + data_segment_size.mem_size, + data_segment_sections.len(), + ); + write_sections(SEG_DATA, data_sections, &data_segment_sections)?; + + let linkedit_segment_size = + get_segment_sections(layout, SegmentType::LinkeditSections).segment_size; + let linkedit_segment = + split_segment_command_buffer(buffers.get_mut(part_id::LINK_EDIT_SEGMENT), 0)?.0; + write_segment( + layout, + part_id::LINK_EDIT_SEGMENT, + SEG_LINKEDIT, + linkedit_segment, + linkedit_segment_size.file_offset as u64, + linkedit_segment_size.file_size as u64, + linkedit_segment_size.mem_offset, + linkedit_segment_size.mem_size, + // The sections in the __LINKEDIT are "hidden". + 0, + ); + + Ok(()) +} + +fn write_segment( + layout: &MachOLayout, + part_id: part_id::PartId, + seg_name: &str, + segment_cmd: &mut SegmentCommand, + file_offset: u64, + file_size: u64, + mem_offset: u64, + mem_size: u64, + section_count: usize, +) { + let prot_flags = layout + .output_sections + .section_flags(part_id.output_section_id()) + .raw(); + + segment_cmd.cmd.set(LE, LC_SEGMENT_64); + segment_cmd.cmdsize.set( + LE, + (size_of::() + size_of::() * section_count) as u32, + ); + segment_cmd.segname[..seg_name.len()].copy_from_slice(seg_name.as_bytes()); + segment_cmd.segname[seg_name.len()..].zero(); + segment_cmd.fileoff.set(LE, file_offset); + segment_cmd.filesize.set(LE, file_size); + segment_cmd.vmaddr.set(LE, mem_offset); + segment_cmd.vmsize.set(LE, mem_size); + segment_cmd.maxprot.set(LE, prot_flags); + segment_cmd.initprot.set(LE, prot_flags); + segment_cmd.nsects.set(LE, section_count as u32); + segment_cmd.flags.set(LE, 0); +} + +fn write_sections( + seg_name: &str, + sections: &mut [SectionEntry], + segment_sections: &[( + OutputRecordLayout, + Option>, + crate::macho::SectionFlags, + )], +) -> Result { + for (section, (size, section_name, section_flags)) in sections.iter_mut().zip(segment_sections) + { + let section_name = section_name + .ok_or_else(|| error!("section name must be known"))? + .0; + + section.segname[..seg_name.len()].copy_from_slice(seg_name.as_bytes()); + section.segname[seg_name.len()..].zero(); + section.sectname[..section_name.len()].copy_from_slice(section_name); + section.sectname[section_name.len()..].zero(); + section.addr.set(LE, size.mem_offset); + section.size.set(LE, size.mem_size); + section.offset.set(LE, size.file_offset as u32); + // TODO + section.align.set(LE, 0); + section.reloff.set(LE, 0); + section.nreloc.set(LE, 0); + section.flags.set(LE, section_flags.raw()); + section.reserved1.set(LE, 0); + section.reserved2.set(LE, 0); + section.reserved3.set(LE, 0); } + Ok(()) } @@ -414,17 +431,15 @@ fn write_dylinker_command>( command.cmd.set(LE, LC_LOAD_DYLINKER); command.cmdsize.set( LE, - ((size_of::() + DYLINKER_PATH.len()).next_multiple_of(8)) as u32, + ((size_of::() + DYLINKER_PATH.len()) + .next_multiple_of(MACHO_COMMAND_ALIGNMENT)) as u32, ); command .name .offset .set(LE, size_of::() as u32); - let path_buffer_len = DYLINKER_PATH.len() + 1; - - path_buffer[0..DYLINKER_PATH.len()].copy_from_slice(DYLINKER_PATH.as_bytes()); - // The string size is always a multiple of 8B. + path_buffer[0..DYLINKER_PATH.len()].copy_from_slice(DYLINKER_PATH); path_buffer[DYLINKER_PATH.len()..].zero(); }