diff --git a/crates/examples/src/objcopy.rs b/crates/examples/src/objcopy.rs index 3dda5d20..6b1f86cd 100644 --- a/crates/examples/src/objcopy.rs +++ b/crates/examples/src/objcopy.rs @@ -161,5 +161,17 @@ pub fn copy(in_data: &[u8]) -> Vec { }); } + if let Some(in_build_version) = match &in_object { + object::File::MachO32(file) => file.build_version().unwrap(), + object::File::MachO64(file) => file.build_version().unwrap(), + _ => None, + } { + let mut out_build_version = object::write::MachOBuildVersion::default(); + out_build_version.platform = in_build_version.platform.get(in_object.endianness()); + out_build_version.minos = in_build_version.minos.get(in_object.endianness()); + out_build_version.sdk = in_build_version.sdk.get(in_object.endianness()); + out_object.set_macho_build_version(out_build_version); + } + out_object.write().unwrap() } diff --git a/crates/examples/testfiles/macho/base-aarch64.o.objcopy b/crates/examples/testfiles/macho/base-aarch64.o.objcopy index 1473d57b..3acaee40 100644 --- a/crates/examples/testfiles/macho/base-aarch64.o.objcopy +++ b/crates/examples/testfiles/macho/base-aarch64.o.objcopy @@ -5,8 +5,8 @@ MachHeader { CpuSubtype: 0x0 CPU_SUBTYPE_ARM64_ALL (0x0) FileType: MH_OBJECT (0x1) - NumberOfCmds: 2 - SizeOfCmds: 0x150 + NumberOfCmds: 4 + SizeOfCmds: 0x1B8 Flags: MH_SUBSECTIONS_VIA_SYMBOLS (0x2000) } SegmentCommand { @@ -15,7 +15,7 @@ SegmentCommand { SegmentName: "" VmAddress: 0x0 VmSize: 0x68 - FileOffset: 0x170 + FileOffset: 0x1D8 FileSize: 0x68 MaxProt: 0x7 VM_PROT_READ (0x1) @@ -33,9 +33,9 @@ SegmentCommand { SegmentName: "__TEXT" Address: 0x0 Size: 0x34 - Offset: 0x170 + Offset: 0x1D8 Align: 0x2 - RelocationOffset: 0x260 + RelocationOffset: 0x240 NumberOfRelocations: 0x3 Flags: 0x80000400 S_REGULAR (0x0) @@ -72,7 +72,7 @@ SegmentCommand { SegmentName: "__TEXT" Address: 0x34 Size: 0xD - Offset: 0x1A4 + Offset: 0x20C Align: 0x0 RelocationOffset: 0x0 NumberOfRelocations: 0x0 @@ -84,9 +84,9 @@ SegmentCommand { SegmentName: "__LD" Address: 0x48 Size: 0x20 - Offset: 0x1B8 + Offset: 0x220 Align: 0x3 - RelocationOffset: 0x278 + RelocationOffset: 0x258 NumberOfRelocations: 0x1 Flags: 0x2000000 S_REGULAR (0x0) @@ -101,12 +101,20 @@ SegmentCommand { } } } +BuildVersionCommand { + Cmd: LC_BUILD_VERSION (0x32) + CmdSize: 0x18 + Platform: PLATFORM_MACOS (0x1) + MinOs: 0xD0000 + Sdk: 0xD0300 + NumberOfTools: 0x0 +} SymtabCommand { Cmd: LC_SYMTAB (0x2) CmdSize: 0x18 - SymbolOffset: 0x1D8 + SymbolOffset: 0x260 NumberOfSymbols: 0x6 - StringOffset: 0x238 + StringOffset: 0x2C0 StringSize: 0x28 Nlist { Index: 0 @@ -162,3 +170,25 @@ SymtabCommand { Value: 0x0 } } +DysymtabCommand { + Cmd: LC_DYSYMTAB (0xB) + CmdSize: 0x50 + IndexOfLocalSymbols: 0 + NumberOfLocalSymbols: 4 + IndexOfExternallyDefinedSymbols: 4 + NumberOfExternallyDefinedSymbols: 1 + IndexOfUndefinedSymbols: 5 + NumberOfUndefinedSymbols: 1 + TocOffset: 0x0 + NumberOfTocEntries: 0 + ModuleTableOffset: 0x0 + NumberOfModuleTableEntries: 0 + ExternalRefSymbolOffset: 0x0 + NumberOfExternalRefSymbols: 0 + IndirectSymbolOffset: 0x0 + NumberOfIndirectSymbols: 0 + ExternalRelocationOffset: 0x0 + NumberOfExternalRelocations: 0 + LocalRelocationOffset: 0x0 + NumberOfLocalRelocations: 0 +} diff --git a/crates/examples/testfiles/macho/base-x86_64.o.objcopy b/crates/examples/testfiles/macho/base-x86_64.o.objcopy index ee077284..4b66798b 100644 --- a/crates/examples/testfiles/macho/base-x86_64.o.objcopy +++ b/crates/examples/testfiles/macho/base-x86_64.o.objcopy @@ -5,8 +5,8 @@ MachHeader { CpuSubtype: 0x3 CPU_SUBTYPE_X86_64_ALL (0x3) FileType: MH_OBJECT (0x1) - NumberOfCmds: 2 - SizeOfCmds: 0x1A0 + NumberOfCmds: 4 + SizeOfCmds: 0x208 Flags: MH_SUBSECTIONS_VIA_SYMBOLS (0x2000) } SegmentCommand { @@ -15,7 +15,7 @@ SegmentCommand { SegmentName: "" VmAddress: 0x0 VmSize: 0x98 - FileOffset: 0x1C0 + FileOffset: 0x228 FileSize: 0x98 MaxProt: 0x7 VM_PROT_READ (0x1) @@ -33,9 +33,9 @@ SegmentCommand { SegmentName: "__TEXT" Address: 0x0 Size: 0x25 - Offset: 0x1C0 + Offset: 0x228 Align: 0x4 - RelocationOffset: 0x288 + RelocationOffset: 0x2C0 NumberOfRelocations: 0x2 Flags: 0x80000400 S_REGULAR (0x0) @@ -64,7 +64,7 @@ SegmentCommand { SegmentName: "__TEXT" Address: 0x25 Size: 0xD - Offset: 0x1E5 + Offset: 0x24D Align: 0x0 RelocationOffset: 0x0 NumberOfRelocations: 0x0 @@ -76,9 +76,9 @@ SegmentCommand { SegmentName: "__LD" Address: 0x38 Size: 0x20 - Offset: 0x1F8 + Offset: 0x260 Align: 0x3 - RelocationOffset: 0x298 + RelocationOffset: 0x2D0 NumberOfRelocations: 0x1 Flags: 0x2000000 S_REGULAR (0x0) @@ -98,7 +98,7 @@ SegmentCommand { SegmentName: "__TEXT" Address: 0x58 Size: 0x40 - Offset: 0x218 + Offset: 0x280 Align: 0x3 RelocationOffset: 0x0 NumberOfRelocations: 0x0 @@ -109,13 +109,21 @@ SegmentCommand { S_ATTR_LIVE_SUPPORT (0x8000000) } } +BuildVersionCommand { + Cmd: LC_BUILD_VERSION (0x32) + CmdSize: 0x18 + Platform: PLATFORM_MACOS (0x1) + MinOs: 0xC0000 + Sdk: 0xD0100 + NumberOfTools: 0x0 +} SymtabCommand { Cmd: LC_SYMTAB (0x2) CmdSize: 0x18 - SymbolOffset: 0x258 + SymbolOffset: 0x2D8 NumberOfSymbols: 0x2 - StringOffset: 0x278 - StringSize: 0xF + StringOffset: 0x2F8 + StringSize: 0x10 Nlist { Index: 0 String: "_main" (0x1) @@ -138,3 +146,25 @@ SymtabCommand { Value: 0x0 } } +DysymtabCommand { + Cmd: LC_DYSYMTAB (0xB) + CmdSize: 0x50 + IndexOfLocalSymbols: 0 + NumberOfLocalSymbols: 0 + IndexOfExternallyDefinedSymbols: 0 + NumberOfExternallyDefinedSymbols: 1 + IndexOfUndefinedSymbols: 1 + NumberOfUndefinedSymbols: 1 + TocOffset: 0x0 + NumberOfTocEntries: 0 + ModuleTableOffset: 0x0 + NumberOfModuleTableEntries: 0 + ExternalRefSymbolOffset: 0x0 + NumberOfExternalRefSymbols: 0 + IndirectSymbolOffset: 0x0 + NumberOfIndirectSymbols: 0 + ExternalRelocationOffset: 0x0 + NumberOfExternalRelocations: 0 + LocalRelocationOffset: 0x0 + NumberOfLocalRelocations: 0 +} diff --git a/crates/examples/testfiles/macho/reloc-aarch64.o.objcopy b/crates/examples/testfiles/macho/reloc-aarch64.o.objcopy index 6314560c..4e5e9464 100644 --- a/crates/examples/testfiles/macho/reloc-aarch64.o.objcopy +++ b/crates/examples/testfiles/macho/reloc-aarch64.o.objcopy @@ -5,8 +5,8 @@ MachHeader { CpuSubtype: 0x0 CPU_SUBTYPE_ARM64_ALL (0x0) FileType: MH_OBJECT (0x1) - NumberOfCmds: 2 - SizeOfCmds: 0x100 + NumberOfCmds: 3 + SizeOfCmds: 0x150 Flags: MH_SUBSECTIONS_VIA_SYMBOLS (0x2000) } SegmentCommand { @@ -15,7 +15,7 @@ SegmentCommand { SegmentName: "" VmAddress: 0x0 VmSize: 0x48 - FileOffset: 0x120 + FileOffset: 0x170 FileSize: 0x48 MaxProt: 0x7 VM_PROT_READ (0x1) @@ -33,9 +33,9 @@ SegmentCommand { SegmentName: "__TEXT" Address: 0x0 Size: 0x28 - Offset: 0x120 + Offset: 0x170 Align: 0x0 - RelocationOffset: 0x1D4 + RelocationOffset: 0x1B8 NumberOfRelocations: 0xD Flags: 0x80000400 S_REGULAR (0x0) @@ -152,9 +152,9 @@ SegmentCommand { SegmentName: "__DATA" Address: 0x28 Size: 0x20 - Offset: 0x148 + Offset: 0x198 Align: 0x0 - RelocationOffset: 0x23C + RelocationOffset: 0x220 NumberOfRelocations: 0x7 Flags: S_REGULAR (0x0) RelocationInfo { @@ -218,10 +218,10 @@ SegmentCommand { SymtabCommand { Cmd: LC_SYMTAB (0x2) CmdSize: 0x18 - SymbolOffset: 0x168 + SymbolOffset: 0x258 NumberOfSymbols: 0x5 - StringOffset: 0x1B8 - StringSize: 0x1B + StringOffset: 0x2A8 + StringSize: 0x20 Nlist { Index: 0 String: "ltmp0" (0x11) @@ -269,3 +269,25 @@ SymtabCommand { Value: 0x0 } } +DysymtabCommand { + Cmd: LC_DYSYMTAB (0xB) + CmdSize: 0x50 + IndexOfLocalSymbols: 0 + NumberOfLocalSymbols: 3 + IndexOfExternallyDefinedSymbols: 3 + NumberOfExternallyDefinedSymbols: 0 + IndexOfUndefinedSymbols: 3 + NumberOfUndefinedSymbols: 2 + TocOffset: 0x0 + NumberOfTocEntries: 0 + ModuleTableOffset: 0x0 + NumberOfModuleTableEntries: 0 + ExternalRefSymbolOffset: 0x0 + NumberOfExternalRefSymbols: 0 + IndirectSymbolOffset: 0x0 + NumberOfIndirectSymbols: 0 + ExternalRelocationOffset: 0x0 + NumberOfExternalRelocations: 0 + LocalRelocationOffset: 0x0 + NumberOfLocalRelocations: 0 +} diff --git a/crates/examples/testfiles/macho/reloc-x86_64.o.objcopy b/crates/examples/testfiles/macho/reloc-x86_64.o.objcopy index e9d2d5f3..14b775e8 100644 --- a/crates/examples/testfiles/macho/reloc-x86_64.o.objcopy +++ b/crates/examples/testfiles/macho/reloc-x86_64.o.objcopy @@ -5,8 +5,8 @@ MachHeader { CpuSubtype: 0x3 CPU_SUBTYPE_X86_64_ALL (0x3) FileType: MH_OBJECT (0x1) - NumberOfCmds: 2 - SizeOfCmds: 0x100 + NumberOfCmds: 3 + SizeOfCmds: 0x150 Flags: 0x0 } SegmentCommand { @@ -15,7 +15,7 @@ SegmentCommand { SegmentName: "" VmAddress: 0x0 VmSize: 0x6B - FileOffset: 0x120 + FileOffset: 0x170 FileSize: 0x6B MaxProt: 0x7 VM_PROT_READ (0x1) @@ -33,9 +33,9 @@ SegmentCommand { SegmentName: "__TEXT" Address: 0x0 Size: 0x53 - Offset: 0x120 + Offset: 0x170 Align: 0x0 - RelocationOffset: 0x1BC + RelocationOffset: 0x1E0 NumberOfRelocations: 0xB Flags: 0x80000400 S_REGULAR (0x0) @@ -136,9 +136,9 @@ SegmentCommand { SegmentName: "__DATA" Address: 0x53 Size: 0x18 - Offset: 0x173 + Offset: 0x1C3 Align: 0x0 - RelocationOffset: 0x214 + RelocationOffset: 0x238 NumberOfRelocations: 0x4 Flags: S_REGULAR (0x0) RelocationInfo { @@ -178,10 +178,10 @@ SegmentCommand { SymtabCommand { Cmd: LC_SYMTAB (0x2) CmdSize: 0x18 - SymbolOffset: 0x190 + SymbolOffset: 0x258 NumberOfSymbols: 0x2 - StringOffset: 0x1B0 - StringSize: 0x9 + StringOffset: 0x278 + StringSize: 0x10 Nlist { Index: 0 String: "_g1" (0x5) @@ -199,3 +199,25 @@ SymtabCommand { Value: 0x5 } } +DysymtabCommand { + Cmd: LC_DYSYMTAB (0xB) + CmdSize: 0x50 + IndexOfLocalSymbols: 0 + NumberOfLocalSymbols: 2 + IndexOfExternallyDefinedSymbols: 2 + NumberOfExternallyDefinedSymbols: 0 + IndexOfUndefinedSymbols: 2 + NumberOfUndefinedSymbols: 0 + TocOffset: 0x0 + NumberOfTocEntries: 0 + ModuleTableOffset: 0x0 + NumberOfModuleTableEntries: 0 + ExternalRefSymbolOffset: 0x0 + NumberOfExternalRefSymbols: 0 + IndirectSymbolOffset: 0x0 + NumberOfIndirectSymbols: 0 + ExternalRelocationOffset: 0x0 + NumberOfExternalRelocations: 0 + LocalRelocationOffset: 0x0 + NumberOfLocalRelocations: 0 +} diff --git a/src/read/macho/file.rs b/src/read/macho/file.rs index 368c28bb..26512cd2 100644 --- a/src/read/macho/file.rs +++ b/src/read/macho/file.rs @@ -162,6 +162,34 @@ where .get(index) .read_error("Invalid Mach-O segment index") } + + /// Returns the endianness. + pub fn endian(&self) -> Mach::Endian { + self.endian + } + + /// Returns the raw data. + pub fn data(&self) -> R { + self.data + } + + /// Returns the raw Mach-O file header. + pub fn raw_header(&self) -> &'data Mach { + self.header + } + + /// Return the `LC_BUILD_VERSION` load command if present. + pub fn build_version(&self) -> Result>> { + let mut commands = self + .header + .load_commands(self.endian, self.data, self.header_offset)?; + while let Some(command) = commands.next()? { + if let Some(build_version) = command.build_version()? { + return Ok(Some(build_version)); + } + } + Ok(None) + } } impl<'data, Mach, R> read::private::Sealed for MachOFile<'data, Mach, R> diff --git a/src/read/macho/load_command.rs b/src/read/macho/load_command.rs index e9af89d8..7ba407a0 100644 --- a/src/read/macho/load_command.rs +++ b/src/read/macho/load_command.rs @@ -247,6 +247,15 @@ impl<'data, E: Endian> LoadCommandData<'data, E> { Ok(None) } } + + /// Try to parse this command as a `BuildVersionCommand`. + pub fn build_version(self) -> Result>> { + if self.cmd == macho::LC_BUILD_VERSION { + Some(self.data()).transpose() + } else { + Ok(None) + } + } } /// A `LoadCommand` that has been interpreted according to its `cmd` field. diff --git a/src/write/macho.rs b/src/write/macho.rs index df8acef3..ccc88bd9 100644 --- a/src/write/macho.rs +++ b/src/write/macho.rs @@ -18,7 +18,6 @@ struct SectionOffsets { #[derive(Default, Clone, Copy)] struct SymbolOffsets { - emit: bool, index: usize, str_id: Option, } @@ -333,12 +332,6 @@ impl<'a> Object<'a> { let mut ncmds = 0; let command_offset = offset; - let build_version_offset = offset; - if let Some(version) = &self.macho_build_version { - offset += version.cmdsize() as usize; - ncmds += 1; - } - // Calculate size of segment command and section headers. let segment_command_offset = offset; let segment_command_len = @@ -346,12 +339,25 @@ impl<'a> Object<'a> { offset += segment_command_len; ncmds += 1; + // Calculate size of build version. + let build_version_offset = offset; + if let Some(version) = &self.macho_build_version { + offset += version.cmdsize() as usize; + ncmds += 1; + } + // Calculate size of symtab command. let symtab_command_offset = offset; let symtab_command_len = mem::size_of::>(); offset += symtab_command_len; ncmds += 1; + // Calculate size of dysymtab command. + let dysymtab_command_offset = offset; + let dysymtab_command_len = mem::size_of::>(); + offset += dysymtab_command_len; + ncmds += 1; + let sizeofcmds = offset - command_offset; // Calculate size of section data. @@ -379,10 +385,12 @@ impl<'a> Object<'a> { } } - // Count symbols and add symbol strings to strtab. + // Partition symbols and add symbol strings to strtab. let mut strtab = StringTable::default(); let mut symbol_offsets = vec![SymbolOffsets::default(); self.symbols.len()]; - let mut nsyms = 0; + let mut local_symbols = vec![]; + let mut external_symbols = vec![]; + let mut undefined_symbols = vec![]; for (index, symbol) in self.symbols.iter().enumerate() { // The unified API allows creating symbols that we don't emit, so filter // them out here. @@ -399,26 +407,32 @@ impl<'a> Object<'a> { ))); } } - symbol_offsets[index].emit = true; - symbol_offsets[index].index = nsyms; - nsyms += 1; if !symbol.name.is_empty() { symbol_offsets[index].str_id = Some(strtab.add(&symbol.name)); } + if symbol.is_undefined() { + undefined_symbols.push(index); + } else if symbol.is_local() { + local_symbols.push(index); + } else { + external_symbols.push(index); + } } - // Calculate size of symtab. - offset = align(offset, pointer_align); - let symtab_offset = offset; - let symtab_len = nsyms * macho.nlist_size(); - offset += symtab_len; + external_symbols.sort_by_key(|index| &*self.symbols[*index].name); + undefined_symbols.sort_by_key(|index| &*self.symbols[*index].name); - // Calculate size of strtab. - let strtab_offset = offset; - // Start with null name. - let mut strtab_data = vec![0]; - strtab.write(1, &mut strtab_data); - offset += strtab_data.len(); + // Count symbols. + let mut nsyms = 0; + for index in local_symbols + .iter() + .copied() + .chain(external_symbols.iter().copied()) + .chain(undefined_symbols.iter().copied()) + { + symbol_offsets[index].index = nsyms; + nsyms += 1; + } // Calculate size of relocations. for (index, section) in self.sections.iter().enumerate() { @@ -428,7 +442,7 @@ impl<'a> Object<'a> { .map(|reloc| 1 + usize::from(reloc.addend != 0)) .sum(); if count != 0 { - offset = align(offset, 4); + offset = align(offset, pointer_align); section_offsets[index].reloc_offset = offset; section_offsets[index].reloc_count = count; let len = count * mem::size_of::>(); @@ -436,6 +450,20 @@ impl<'a> Object<'a> { } } + // Calculate size of symtab. + offset = align(offset, pointer_align); + let symtab_offset = offset; + let symtab_len = nsyms * macho.nlist_size(); + offset += symtab_len; + + // Calculate size of strtab. + let strtab_offset = offset; + // Start with null name. + let mut strtab_data = vec![0]; + strtab.write(1, &mut strtab_data); + write_align(&mut strtab_data, pointer_align); + offset += strtab_data.len(); + // Start writing. buffer .reserve(offset) @@ -480,18 +508,6 @@ impl<'a> Object<'a> { }, ); - if let Some(version) = &self.macho_build_version { - debug_assert_eq!(build_version_offset, buffer.len()); - buffer.write(&macho::BuildVersionCommand { - cmd: U32::new(endian, macho::LC_BUILD_VERSION), - cmdsize: U32::new(endian, version.cmdsize()), - platform: U32::new(endian, version.platform), - minos: U32::new(endian, version.minos), - sdk: U32::new(endian, version.sdk), - ntools: U32::new(endian, 0), - }); - } - // Write segment command. debug_assert_eq!(segment_command_offset, buffer.len()); macho.write_segment_command( @@ -574,6 +590,19 @@ impl<'a> Object<'a> { ); } + // Write build version. + if let Some(version) = &self.macho_build_version { + debug_assert_eq!(build_version_offset, buffer.len()); + buffer.write(&macho::BuildVersionCommand { + cmd: U32::new(endian, macho::LC_BUILD_VERSION), + cmdsize: U32::new(endian, version.cmdsize()), + platform: U32::new(endian, version.platform), + minos: U32::new(endian, version.minos), + sdk: U32::new(endian, version.sdk), + ntools: U32::new(endian, 0), + }); + } + // Write symtab command. debug_assert_eq!(symtab_command_offset, buffer.len()); let symtab_command = macho::SymtabCommand { @@ -586,6 +615,35 @@ impl<'a> Object<'a> { }; buffer.write(&symtab_command); + // Write dysymtab command. + debug_assert_eq!(dysymtab_command_offset, buffer.len()); + let dysymtab_command = macho::DysymtabCommand { + cmd: U32::new(endian, macho::LC_DYSYMTAB), + cmdsize: U32::new(endian, dysymtab_command_len as u32), + ilocalsym: U32::new(endian, 0), + nlocalsym: U32::new(endian, local_symbols.len() as u32), + iextdefsym: U32::new(endian, local_symbols.len() as u32), + nextdefsym: U32::new(endian, external_symbols.len() as u32), + iundefsym: U32::new( + endian, + local_symbols.len() as u32 + external_symbols.len() as u32, + ), + nundefsym: U32::new(endian, undefined_symbols.len() as u32), + tocoff: U32::default(), + ntoc: U32::default(), + modtaboff: U32::default(), + nmodtab: U32::default(), + extrefsymoff: U32::default(), + nextrefsyms: U32::default(), + indirectsymoff: U32::default(), + nindirectsyms: U32::default(), + extreloff: U32::default(), + nextrel: U32::default(), + locreloff: U32::default(), + nlocrel: U32::default(), + }; + buffer.write(&dysymtab_command); + // Write section data. for (index, section) in self.sections.iter().enumerate() { if !section.is_bss() { @@ -595,80 +653,10 @@ impl<'a> Object<'a> { } debug_assert_eq!(segment_file_offset + segment_file_size, buffer.len()); - // Write symtab. - write_align(buffer, pointer_align); - debug_assert_eq!(symtab_offset, buffer.len()); - for (index, symbol) in self.symbols.iter().enumerate() { - if !symbol_offsets[index].emit { - continue; - } - // TODO: N_STAB - let (mut n_type, n_sect) = match symbol.section { - SymbolSection::Undefined => (macho::N_UNDF | macho::N_EXT, 0), - SymbolSection::Absolute => (macho::N_ABS, 0), - SymbolSection::Section(id) => (macho::N_SECT, id.0 + 1), - SymbolSection::None | SymbolSection::Common => { - return Err(Error(format!( - "unimplemented symbol `{}` section {:?}", - symbol.name().unwrap_or(""), - symbol.section - ))); - } - }; - match symbol.scope { - SymbolScope::Unknown | SymbolScope::Compilation => {} - SymbolScope::Linkage => { - n_type |= macho::N_EXT | macho::N_PEXT; - } - SymbolScope::Dynamic => { - n_type |= macho::N_EXT; - } - } - - let n_desc = if let SymbolFlags::MachO { n_desc } = symbol.flags { - n_desc - } else { - let mut n_desc = 0; - if symbol.weak { - if symbol.is_undefined() { - n_desc |= macho::N_WEAK_REF; - } else { - n_desc |= macho::N_WEAK_DEF; - } - } - n_desc - }; - - let n_value = match symbol.section.id() { - Some(section) => section_offsets[section.0].address + symbol.value, - None => symbol.value, - }; - - let n_strx = symbol_offsets[index] - .str_id - .map(|id| strtab.get_offset(id)) - .unwrap_or(0); - - macho.write_nlist( - buffer, - Nlist { - n_strx: n_strx as u32, - n_type, - n_sect: n_sect as u8, - n_desc, - n_value, - }, - ); - } - - // Write strtab. - debug_assert_eq!(strtab_offset, buffer.len()); - buffer.write_bytes(&strtab_data); - // Write relocations. for (index, section) in self.sections.iter().enumerate() { if !section.relocations.is_empty() { - write_align(buffer, 4); + write_align(buffer, pointer_align); debug_assert_eq!(section_offsets[index].reloc_offset, buffer.len()); for reloc in §ion.relocations { let r_length = match reloc.size { @@ -787,6 +775,79 @@ impl<'a> Object<'a> { } } + // Write symtab. + write_align(buffer, pointer_align); + debug_assert_eq!(symtab_offset, buffer.len()); + for index in local_symbols + .iter() + .copied() + .chain(external_symbols.iter().copied()) + .chain(undefined_symbols.iter().copied()) + { + let symbol = &self.symbols[index]; + // TODO: N_STAB + let (mut n_type, n_sect) = match symbol.section { + SymbolSection::Undefined => (macho::N_UNDF | macho::N_EXT, 0), + SymbolSection::Absolute => (macho::N_ABS, 0), + SymbolSection::Section(id) => (macho::N_SECT, id.0 + 1), + SymbolSection::None | SymbolSection::Common => { + return Err(Error(format!( + "unimplemented symbol `{}` section {:?}", + symbol.name().unwrap_or(""), + symbol.section + ))); + } + }; + match symbol.scope { + SymbolScope::Unknown | SymbolScope::Compilation => {} + SymbolScope::Linkage => { + n_type |= macho::N_EXT | macho::N_PEXT; + } + SymbolScope::Dynamic => { + n_type |= macho::N_EXT; + } + } + + let n_desc = if let SymbolFlags::MachO { n_desc } = symbol.flags { + n_desc + } else { + let mut n_desc = 0; + if symbol.weak { + if symbol.is_undefined() { + n_desc |= macho::N_WEAK_REF; + } else { + n_desc |= macho::N_WEAK_DEF; + } + } + n_desc + }; + + let n_value = match symbol.section.id() { + Some(section) => section_offsets[section.0].address + symbol.value, + None => symbol.value, + }; + + let n_strx = symbol_offsets[index] + .str_id + .map(|id| strtab.get_offset(id)) + .unwrap_or(0); + + macho.write_nlist( + buffer, + Nlist { + n_strx: n_strx as u32, + n_type, + n_sect: n_sect as u8, + n_desc, + n_value, + }, + ); + } + + // Write strtab. + debug_assert_eq!(strtab_offset, buffer.len()); + buffer.write_bytes(&strtab_data); + debug_assert_eq!(offset, buffer.len()); Ok(()) diff --git a/tests/round_trip/macho.rs b/tests/round_trip/macho.rs index f45d3db1..787f03ad 100644 --- a/tests/round_trip/macho.rs +++ b/tests/round_trip/macho.rs @@ -33,7 +33,7 @@ fn issue_552_section_file_alignment() { Endianness::Little, ); - // Odd number of sections ensures that the starting file offset is not a multiple of 32. + // The starting file offset is not a multiple of 32 (checked later). // Length of 32 ensures that the file offset of the end of this section is still not a // multiple of 32. let section = object.add_section(vec![], vec![], object::SectionKind::ReadOnlyDataWithRel); @@ -44,20 +44,21 @@ fn issue_552_section_file_alignment() { let section = object.add_section(vec![], vec![], object::SectionKind::ReadOnlyData); object.append_section_data(section, &vec![0u8; 1], 32); - let section = object.add_section(vec![], vec![], object::SectionKind::Text); - object.append_section_data(section, &vec![0u8; 1], 1); - let bytes = &*object.write().unwrap(); + std::fs::write(&"align.o", &bytes).unwrap(); let object = read::File::parse(bytes).unwrap(); let mut sections = object.sections(); let section = sections.next().unwrap(); - assert_eq!(section.file_range(), Some((368, 32))); + let offset = section.file_range().unwrap().0; + // Check file offset is not aligned to 32. + assert_ne!(offset % 32, 0); assert_eq!(section.address(), 0); assert_eq!(section.size(), 32); let section = sections.next().unwrap(); - assert_eq!(section.file_range(), Some((400, 1))); + // Check there is no padding. + assert_eq!(section.file_range(), Some((offset + 32, 1))); assert_eq!(section.address(), 32); assert_eq!(section.size(), 1); } diff --git a/tests/round_trip/tls.rs b/tests/round_trip/tls.rs index 999e2f18..0af90bd7 100644 --- a/tests/round_trip/tls.rs +++ b/tests/round_trip/tls.rs @@ -218,32 +218,32 @@ fn macho_x86_64_tls() { let symbol = symbols.next().unwrap(); println!("{:?}", symbol); - assert_eq!(symbol.name(), Ok("_tls1")); + let tls1_init_symbol = symbol.index(); + assert_eq!(symbol.name(), Ok("_tls1$tlv$init")); assert_eq!(symbol.kind(), SymbolKind::Tls); - assert_eq!(symbol.section_index(), Some(thread_vars_index)); - assert_eq!(symbol.scope(), SymbolScope::Linkage); + assert_eq!(symbol.section_index(), Some(thread_data_index)); + assert_eq!(symbol.scope(), SymbolScope::Compilation); assert_eq!(symbol.is_weak(), false); assert_eq!(symbol.is_undefined(), false); let symbol = symbols.next().unwrap(); println!("{:?}", symbol); - let tls1_init_symbol = symbol.index(); - assert_eq!(symbol.name(), Ok("_tls1$tlv$init")); + let tls2_init_symbol = symbol.index(); + assert_eq!(symbol.name(), Ok("_tls2$tlv$init")); assert_eq!(symbol.kind(), SymbolKind::Tls); - assert_eq!(symbol.section_index(), Some(thread_data_index)); + assert_eq!(symbol.section_index(), Some(thread_bss_index)); assert_eq!(symbol.scope(), SymbolScope::Compilation); assert_eq!(symbol.is_weak(), false); assert_eq!(symbol.is_undefined(), false); let symbol = symbols.next().unwrap(); println!("{:?}", symbol); - let tlv_bootstrap_symbol = symbol.index(); - assert_eq!(symbol.name(), Ok("__tlv_bootstrap")); - assert_eq!(symbol.kind(), SymbolKind::Unknown); - assert_eq!(symbol.section_index(), None); - assert_eq!(symbol.scope(), SymbolScope::Unknown); + assert_eq!(symbol.name(), Ok("_tls1")); + assert_eq!(symbol.kind(), SymbolKind::Tls); + assert_eq!(symbol.section_index(), Some(thread_vars_index)); + assert_eq!(symbol.scope(), SymbolScope::Linkage); assert_eq!(symbol.is_weak(), false); - assert_eq!(symbol.is_undefined(), true); + assert_eq!(symbol.is_undefined(), false); let symbol = symbols.next().unwrap(); println!("{:?}", symbol); @@ -256,13 +256,13 @@ fn macho_x86_64_tls() { let symbol = symbols.next().unwrap(); println!("{:?}", symbol); - let tls2_init_symbol = symbol.index(); - assert_eq!(symbol.name(), Ok("_tls2$tlv$init")); - assert_eq!(symbol.kind(), SymbolKind::Tls); - assert_eq!(symbol.section_index(), Some(thread_bss_index)); - assert_eq!(symbol.scope(), SymbolScope::Compilation); + let tlv_bootstrap_symbol = symbol.index(); + assert_eq!(symbol.name(), Ok("__tlv_bootstrap")); + assert_eq!(symbol.kind(), SymbolKind::Unknown); + assert_eq!(symbol.section_index(), None); + assert_eq!(symbol.scope(), SymbolScope::Unknown); assert_eq!(symbol.is_weak(), false); - assert_eq!(symbol.is_undefined(), false); + assert_eq!(symbol.is_undefined(), true); let mut relocations = thread_vars.relocations();