diff --git a/llvm/test/tools/llvm-objcopy/MachO/basic-executable-copy.test b/llvm/test/tools/llvm-objcopy/MachO/basic-executable-copy.test new file mode 100644 --- /dev/null +++ b/llvm/test/tools/llvm-objcopy/MachO/basic-executable-copy.test @@ -0,0 +1,291 @@ +## This test verifies that llvm-objcopy copies an executable properly. It +## uses llvm-readobj instead of cmp because some parts of the object +## (e.g., the string table) are not identical; the output file is correct but +## some offsets differ from the input file. +# RUN: yaml2obj %s > %t +# RUN: llvm-objcopy %t %t2 +# RUN: llvm-readobj --file-headers --sections %t2 | FileCheck %s + +--- !mach-o +FileHeader: + magic: 0xFEEDFACF + cputype: 0x01000007 + cpusubtype: 0x80000003 + filetype: 0x00000002 + ncmds: 15 + sizeofcmds: 976 + flags: 0x00200085 + reserved: 0x00000000 +LoadCommands: + - cmd: LC_SEGMENT_64 + cmdsize: 72 + segname: __PAGEZERO + vmaddr: 0 + vmsize: 4294967296 + fileoff: 0 + filesize: 0 + maxprot: 0 + initprot: 0 + nsects: 0 + flags: 0 + - cmd: LC_SEGMENT_64 + cmdsize: 232 + segname: __TEXT + vmaddr: 4294967296 + vmsize: 4096 + fileoff: 0 + filesize: 4096 + maxprot: 7 + initprot: 5 + nsects: 2 + flags: 0 + Sections: + - sectname: __text + segname: __TEXT + addr: 0x0000000100000F70 + size: 58 + offset: 0x00000F70 + align: 4 + reloff: 0x00000000 + nreloc: 0 + flags: 0x80000400 + reserved1: 0x00000000 + reserved2: 0x00000000 + reserved3: 0x00000000 + - sectname: __unwind_info + segname: __TEXT + addr: 0x0000000100000FAC + size: 72 + offset: 0x00000FAC + align: 2 + reloff: 0x00000000 + nreloc: 0 + flags: 0x00000000 + reserved1: 0x00000000 + reserved2: 0x00000000 + reserved3: 0x00000000 + - cmd: LC_SEGMENT_64 + cmdsize: 232 + segname: __DATA + vmaddr: 4294971392 + vmsize: 4096 + fileoff: 4096 + filesize: 4096 + maxprot: 7 + initprot: 3 + nsects: 2 + flags: 0 + Sections: + - sectname: __data + segname: __DATA + addr: 0x0000000100001000 + size: 4 + offset: 0x00001000 + align: 2 + reloff: 0x00000000 + nreloc: 0 + flags: 0x00000000 + reserved1: 0x00000000 + reserved2: 0x00000000 + reserved3: 0x00000000 + - sectname: __common + segname: __DATA + addr: 0x0000000100001004 + size: 4 + offset: 0x00000000 + align: 2 + reloff: 0x00000000 + nreloc: 0 + flags: 0x00000001 + reserved1: 0x00000000 + reserved2: 0x00000000 + reserved3: 0x00000000 + - cmd: LC_SEGMENT_64 + cmdsize: 72 + segname: __LINKEDIT + vmaddr: 4294975488 + vmsize: 4096 + fileoff: 8192 + filesize: 232 + maxprot: 7 + initprot: 1 + nsects: 0 + flags: 0 + - cmd: LC_DYLD_INFO_ONLY + cmdsize: 48 + rebase_off: 0 + rebase_size: 0 + bind_off: 0 + bind_size: 0 + weak_bind_off: 0 + weak_bind_size: 0 + lazy_bind_off: 0 + lazy_bind_size: 0 + export_off: 8192 + export_size: 72 + - cmd: LC_SYMTAB + cmdsize: 24 + symoff: 8272 + nsyms: 6 + stroff: 8368 + strsize: 56 + - cmd: LC_DYSYMTAB + cmdsize: 80 + ilocalsym: 0 + nlocalsym: 0 + iextdefsym: 0 + nextdefsym: 5 + iundefsym: 5 + nundefsym: 1 + tocoff: 0 + ntoc: 0 + modtaboff: 0 + nmodtab: 0 + extrefsymoff: 0 + nextrefsyms: 0 + indirectsymoff: 0 + nindirectsyms: 0 + extreloff: 0 + nextrel: 0 + locreloff: 0 + nlocrel: 0 + - cmd: LC_LOAD_DYLINKER + cmdsize: 32 + name: 12 + PayloadString: '/usr/lib/dyld' + ZeroPadBytes: 7 + - cmd: LC_UUID + cmdsize: 24 + uuid: B6EE4FB7-4E1E-3C7A-80D3-CFBD89DBC0FE + - cmd: LC_BUILD_VERSION + cmdsize: 32 + platform: 1 + minos: 658944 + sdk: 658944 + ntools: 1 + Tools: + - tool: 3 + version: 29491968 + - cmd: LC_SOURCE_VERSION + cmdsize: 16 + version: 0 + - cmd: LC_MAIN + cmdsize: 24 + entryoff: 3984 + stacksize: 0 + - cmd: LC_LOAD_DYLIB + cmdsize: 56 + dylib: + name: 24 + timestamp: 2 + current_version: 82115073 + compatibility_version: 65536 + PayloadString: '/usr/lib/libSystem.B.dylib' + ZeroPadBytes: 6 + - cmd: LC_FUNCTION_STARTS + cmdsize: 16 + dataoff: 8264 + datasize: 8 + - cmd: LC_DATA_IN_CODE + cmdsize: 16 + dataoff: 8272 + datasize: 0 +LinkEditData: + ExportTrie: + TerminalSize: 0 + NodeOffset: 0 + Name: '' + Flags: 0x0000000000000000 + Address: 0x0000000000000000 + Other: 0x0000000000000000 + ImportName: '' + Children: + - TerminalSize: 0 + NodeOffset: 5 + Name: _ + Flags: 0x0000000000000000 + Address: 0x0000000000000000 + Other: 0x0000000000000000 + ImportName: '' + Children: + - TerminalSize: 2 + NodeOffset: 44 + Name: _mh_execute_header + Flags: 0x0000000000000000 + Address: 0x0000000000000000 + Other: 0x0000000000000000 + ImportName: '' + - TerminalSize: 3 + NodeOffset: 48 + Name: foo + Flags: 0x0000000000000000 + Address: 0x0000000000000F70 + Other: 0x0000000000000000 + ImportName: '' + - TerminalSize: 3 + NodeOffset: 53 + Name: main + Flags: 0x0000000000000000 + Address: 0x0000000000000F90 + Other: 0x0000000000000000 + ImportName: '' + - TerminalSize: 3 + NodeOffset: 58 + Name: b + Flags: 0x0000000000000000 + Address: 0x0000000000001000 + Other: 0x0000000000000000 + ImportName: '' + - TerminalSize: 3 + NodeOffset: 63 + Name: a + Flags: 0x0000000000000000 + Address: 0x0000000000001004 + Other: 0x0000000000000000 + ImportName: '' + NameList: + - n_strx: 2 + n_type: 0x0F + n_sect: 1 + n_desc: 16 + n_value: 4294967296 + - n_strx: 22 + n_type: 0x0F + n_sect: 4 + n_desc: 0 + n_value: 4294971396 + - n_strx: 25 + n_type: 0x0F + n_sect: 3 + n_desc: 0 + n_value: 4294971392 + - n_strx: 28 + n_type: 0x0F + n_sect: 1 + n_desc: 0 + n_value: 4294971248 + - n_strx: 33 + n_type: 0x0F + n_sect: 1 + n_desc: 0 + n_value: 4294971280 + - n_strx: 39 + n_type: 0x01 + n_sect: 0 + n_desc: 256 + n_value: 0 + StringTable: + - ' ' + - __mh_execute_header + - _a + - _b + - _foo + - _main + - dyld_stub_binder +... + +# CHECK: FileType: Executable (0x2) +# CHECK: Name: __text +# CHECK: Name: __unwind_info +# CHECK: Name: __data +# CHECK: Name: __common diff --git a/llvm/tools/llvm-objcopy/MachO/MachOLayoutBuilder.cpp b/llvm/tools/llvm-objcopy/MachO/MachOLayoutBuilder.cpp --- a/llvm/tools/llvm-objcopy/MachO/MachOLayoutBuilder.cpp +++ b/llvm/tools/llvm-objcopy/MachO/MachOLayoutBuilder.cpp @@ -17,7 +17,7 @@ uint32_t MachOLayoutBuilder::computeSizeOfCmds() const { uint32_t Size = 0; for (const auto &LC : O.LoadCommands) { - auto &MLC = LC.MachOLoadCommand; + const MachO::macho_load_command &MLC = LC.MachOLoadCommand; auto cmd = MLC.load_command_data.cmd; switch (cmd) { case MachO::LC_SEGMENT: @@ -101,20 +101,25 @@ uint64_t MachOLayoutBuilder::layoutSegments() { auto HeaderSize = Is64Bit ? sizeof(MachO::mach_header_64) : sizeof(MachO::mach_header); - auto Offset = HeaderSize + O.Header.SizeOfCmds; - - // Lay out sections. + const bool IsObjectFile = + O.Header.FileType == MachO::HeaderFileType::MH_OBJECT; + uint64_t Offset = IsObjectFile ? (HeaderSize + O.Header.SizeOfCmds) : 0; for (auto &LC : O.LoadCommands) { - uint64_t FileOff = Offset; auto &MLC = LC.MachOLoadCommand; StringRef Segname; + uint64_t SegmentVmAddr; + uint64_t SegmentVmSize; switch (MLC.load_command_data.cmd) { case MachO::LC_SEGMENT: + SegmentVmAddr = MLC.segment_command_data.vmaddr; + SegmentVmSize = MLC.segment_command_data.vmsize; Segname = StringRef(MLC.segment_command_data.segname, strnlen(MLC.segment_command_data.segname, sizeof(MLC.segment_command_data.segname))); break; case MachO::LC_SEGMENT_64: + SegmentVmAddr = MLC.segment_command_64_data.vmaddr; + SegmentVmSize = MLC.segment_command_64_data.vmsize; Segname = StringRef(MLC.segment_command_64_data.segname, strnlen(MLC.segment_command_64_data.segname, sizeof(MLC.segment_command_64_data.segname))); @@ -131,43 +136,64 @@ } // Update file offsets and sizes of sections. + uint64_t SegOffset = Offset; + uint64_t SegFileSize = 0; uint64_t VMSize = 0; - uint64_t FileOffsetInSegment = 0; for (auto &Sec : LC.Sections) { - if (!Sec.isVirtualSection()) { - auto FilePaddingSize = - OffsetToAlignment(FileOffsetInSegment, 1ull << Sec.Align); - Sec.Offset = Offset + FileOffsetInSegment + FilePaddingSize; - Sec.Size = Sec.Content.size(); - FileOffsetInSegment += FilePaddingSize + Sec.Size; + if (IsObjectFile) { + if (Sec.isVirtualSection()) { + Sec.Offset = 0; + } else { + uint64_t PaddingSize = OffsetToAlignment(SegFileSize, 1 << Sec.Align); + Sec.Offset = SegOffset + SegFileSize + PaddingSize; + Sec.Size = Sec.Content.size(); + SegFileSize += PaddingSize + Sec.Size; + } + VMSize = std::max(VMSize, Sec.Addr + Sec.Size); + } else { + if (Sec.isVirtualSection()) { + Sec.Offset = 0; + VMSize += Sec.Size; + } else { + uint32_t SectOffset = Sec.Addr - SegmentVmAddr; + Sec.Offset = SegOffset + SectOffset; + Sec.Size = Sec.Content.size(); + SegFileSize = std::max(SegFileSize, SectOffset + Sec.Size); + VMSize = std::max(VMSize, SegFileSize); + } } + } - VMSize = std::max(VMSize, Sec.Addr + Sec.Size); + if (IsObjectFile) { + Offset += SegFileSize; + } else { + Offset = alignTo(Offset + SegFileSize, PageSize); + SegFileSize = alignTo(SegFileSize, PageSize); + // Use the original vmsize if the segment is __PAGEZERO. + VMSize = + Segname == "__PAGEZERO" ? SegmentVmSize : alignTo(VMSize, PageSize); } - // TODO: Handle the __PAGEZERO segment. switch (MLC.load_command_data.cmd) { case MachO::LC_SEGMENT: MLC.segment_command_data.cmdsize = sizeof(MachO::segment_command) + sizeof(MachO::section) * LC.Sections.size(); MLC.segment_command_data.nsects = LC.Sections.size(); - MLC.segment_command_data.fileoff = FileOff; + MLC.segment_command_data.fileoff = SegOffset; MLC.segment_command_data.vmsize = VMSize; - MLC.segment_command_data.filesize = FileOffsetInSegment; + MLC.segment_command_data.filesize = SegFileSize; break; case MachO::LC_SEGMENT_64: MLC.segment_command_64_data.cmdsize = sizeof(MachO::segment_command_64) + sizeof(MachO::section_64) * LC.Sections.size(); MLC.segment_command_64_data.nsects = LC.Sections.size(); - MLC.segment_command_64_data.fileoff = FileOff; + MLC.segment_command_64_data.fileoff = SegOffset; MLC.segment_command_64_data.vmsize = VMSize; - MLC.segment_command_64_data.filesize = FileOffsetInSegment; + MLC.segment_command_64_data.filesize = SegFileSize; break; } - - Offset += FileOffsetInSegment; } return Offset;