--- /dev/null
+## 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
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:
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)));
}
// 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;