diff --git a/llvm/test/tools/llvm-objcopy/MachO/Inputs/code-signature-check.py b/llvm/test/tools/llvm-objcopy/MachO/Inputs/code-signature-check.py new file mode 120000 --- /dev/null +++ b/llvm/test/tools/llvm-objcopy/MachO/Inputs/code-signature-check.py @@ -0,0 +1 @@ +../../../../../../lld/test/MachO/Inputs/code-signature-check.py \ No newline at end of file diff --git a/llvm/test/tools/llvm-objcopy/MachO/code_signature_lc.test b/llvm/test/tools/llvm-objcopy/MachO/code_signature_lc.test --- a/llvm/test/tools/llvm-objcopy/MachO/code_signature_lc.test +++ b/llvm/test/tools/llvm-objcopy/MachO/code_signature_lc.test @@ -1,40 +1,253 @@ # RUN: yaml2obj %s -o %t ## Verify that the input file is valid and contains the expected load command. -# RUN: llvm-objdump --private-headers %t | FileCheck %s +# RUN: llvm-objdump --private-headers %t | FileCheck %s --check-prefix=CHECK # CHECK: cmd LC_CODE_SIGNATURE # CHECK-NEXT: cmdsize 16 -# CHECK-NEXT: dataoff 128 -# CHECK-NEXT: datasize 16 +# CHECK-NEXT: dataoff 16544 +# CHECK-NEXT: datasize 280 # RUN: llvm-objcopy %t %t.copy -# RUN: cmp %t %t.copy +# RUN: obj2yaml %t > %t.yaml +# RUN: obj2yaml %t.copy > %t.copy.yaml + +## Verify that the copy still includes the load command +# RUN: cat %t.copy.yaml | FileCheck %s --check-prefix=CHECK-COPY +# CHECK-COPY: - cmd: LC_CODE_SIGNATURE +# CHECK-COPY-NEXT: cmdsize: 16 +# CHECK-COPY-NEXT: dataoff: 16544 +# CHECK-COPY-NEXT: datasize: 304 + +## Remove information changed by regeneration of load command: +## - __LINKEDIT segment filesize may change +## - LC_CODE_SIGNATURE command dataoff and datasize may change +## - __LINKEDIT data locations may change + +# RUN: sed -e '/__LINKEDIT/,+4d' \ +# RUN: -e '/LC_CODE_SIGNATURE/,+3d' \ +# RUN: -e '/n_strx/d' \ +# RUN: -e '/dyld_stub_binder/d' %t.yaml > %t.clean.yaml + +# RUN: sed -e '/__LINKEDIT/,+4d' \ +# RUN: -e '/LC_CODE_SIGNATURE/,+3d' \ +# RUN: -e '/n_strx/d' \ +# RUN: -e '/dyld_stub_binder/d' %t.copy.yaml > %t.copy.clean.yaml + +## Verify the remainder of the object file remains unchanged +# RUN: diff %t.clean.yaml %t.copy.clean.yaml + +## Verify the new signature is valid +# RUN: %python %p/Inputs/code-signature-check.py %t.copy 16544 304 0 16544 --- !mach-o FileHeader: magic: 0xFEEDFACF - cputype: 0x01000007 - cpusubtype: 0x80000003 - filetype: 0x00000002 - ncmds: 2 - sizeofcmds: 88 - flags: 0x00218085 - reserved: 0x00000000 + cputype: 0x1000007 + cpusubtype: 0x3 + filetype: 0x2 + ncmds: 15 + sizeofcmds: 760 + flags: 0x200085 + reserved: 0x0 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: 16384 + fileoff: 0 + filesize: 16384 + maxprot: 5 + initprot: 5 + nsects: 2 + flags: 0 + Sections: + - sectname: __text + segname: __TEXT + addr: 0x100003FA0 + size: 15 + offset: 0x3FA0 + align: 4 + reloff: 0x0 + nreloc: 0 + flags: 0x80000400 + reserved1: 0x0 + reserved2: 0x0 + reserved3: 0x0 + content: 554889E531C0C745FC000000005DC3 + - sectname: __unwind_info + segname: __TEXT + addr: 0x100003FB0 + size: 72 + offset: 0x3FB0 + align: 2 + reloff: 0x0 + nreloc: 0 + flags: 0x0 + reserved1: 0x0 + reserved2: 0x0 + reserved3: 0x0 + content: 010000001C000000000000001C000000000000001C00000002000000A03F00003400000034000000B03F00000000000034000000030000000C000100100001000000000000000001 - cmd: LC_SEGMENT_64 cmdsize: 72 segname: __LINKEDIT - vmaddr: 4294979584 - vmsize: 4096 - fileoff: 120 - filesize: 24 - maxprot: 7 + vmaddr: 4294983680 + vmsize: 16384 + fileoff: 16384 + filesize: 440 + maxprot: 1 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: 16384 + export_size: 48 + - cmd: LC_SYMTAB + cmdsize: 24 + symoff: 16440 + nsyms: 3 + stroff: 16488 + strsize: 48 + - cmd: LC_DYSYMTAB + cmdsize: 80 + ilocalsym: 0 + nlocalsym: 0 + iextdefsym: 0 + nextdefsym: 2 + iundefsym: 2 + 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 + Content: '/usr/lib/dyld' + ZeroPadBytes: 7 + - cmd: LC_UUID + cmdsize: 24 + uuid: 42759668-1CBA-3094-8E2D-F01E1A66E815 + - cmd: LC_BUILD_VERSION + cmdsize: 32 + platform: 1 + minos: 720896 + sdk: 721664 + ntools: 1 + Tools: + - tool: 3 + version: 42600704 + - cmd: LC_SOURCE_VERSION + cmdsize: 16 + version: 0 + - cmd: LC_MAIN + cmdsize: 24 + entryoff: 16288 + stacksize: 0 + - cmd: LC_LOAD_DYLIB + cmdsize: 56 + dylib: + name: 24 + timestamp: 2 + current_version: 84698117 + compatibility_version: 65536 + Content: '/usr/lib/libSystem.B.dylib' + ZeroPadBytes: 6 + - cmd: LC_FUNCTION_STARTS + cmdsize: 16 + dataoff: 16432 + datasize: 8 + - cmd: LC_DATA_IN_CODE + cmdsize: 16 + dataoff: 16440 + datasize: 0 - cmd: LC_CODE_SIGNATURE cmdsize: 16 - dataoff: 128 - datasize: 16 + dataoff: 16544 + datasize: 280 +LinkEditData: + ExportTrie: + TerminalSize: 0 + NodeOffset: 0 + Name: '' + Flags: 0x0 + Address: 0x0 + Other: 0x0 + ImportName: '' + Children: + - TerminalSize: 0 + NodeOffset: 5 + Name: _ + Flags: 0x0 + Address: 0x0 + Other: 0x0 + ImportName: '' + Children: + - TerminalSize: 2 + NodeOffset: 33 + Name: _mh_execute_header + Flags: 0x0 + Address: 0x0 + Other: 0x0 + ImportName: '' + - TerminalSize: 3 + NodeOffset: 37 + Name: main + Flags: 0x0 + Address: 0x3FA0 + Other: 0x0 + ImportName: '' + NameList: + - n_strx: 2 + n_type: 0xF + n_sect: 1 + n_desc: 16 + n_value: 4294967296 + - n_strx: 22 + n_type: 0xF + n_sect: 1 + n_desc: 0 + n_value: 4294983584 + - n_strx: 28 + n_type: 0x1 + n_sect: 0 + n_desc: 256 + n_value: 0 + StringTable: + - ' ' + - __mh_execute_header + - _main + - dyld_stub_binder + - '' + - '' + - '' ... diff --git a/llvm/tools/llvm-objcopy/MachO/MachOLayoutBuilder.h b/llvm/tools/llvm-objcopy/MachO/MachOLayoutBuilder.h --- a/llvm/tools/llvm-objcopy/MachO/MachOLayoutBuilder.h +++ b/llvm/tools/llvm-objcopy/MachO/MachOLayoutBuilder.h @@ -18,6 +18,7 @@ class MachOLayoutBuilder { Object &O; + const StringRef OutputFilename; bool Is64Bit; uint64_t PageSize; @@ -37,8 +38,10 @@ bool Is64Bit); public: - MachOLayoutBuilder(Object &O, bool Is64Bit, uint64_t PageSize) - : O(O), Is64Bit(Is64Bit), PageSize(PageSize), + MachOLayoutBuilder(Object &O, bool Is64Bit, uint64_t PageSize, + const StringRef OutputFilename) + : O(O), OutputFilename(OutputFilename), Is64Bit(Is64Bit), + PageSize(PageSize), StrTableBuilder(getStringTableBuilderKind(O, Is64Bit)) {} // Recomputes and updates fields in the given object such as file offsets. 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 @@ -7,6 +7,8 @@ //===----------------------------------------------------------------------===// #include "MachOLayoutBuilder.h" +#include "../CommonConfig.h" +#include "llvm/Object/MachO.h" #include "llvm/Support/Alignment.h" #include "llvm/Support/Errc.h" #include "llvm/Support/ErrorHandling.h" @@ -262,10 +264,25 @@ sizeof(uint32_t) * O.IndirectSymTable.Symbols.size(); uint64_t StartOfCodeSignature = StartOfSymbolStrings + StrTableBuilder.getSize(); - if (O.CodeSignatureCommandIndex) + uint32_t CodeSignatureSectionSize = 0; + if (O.CodeSignatureCommandIndex) { StartOfCodeSignature = alignTo(StartOfCodeSignature, 16); + uint32_t TextSegmentFileOff = 0; + uint32_t TextSegmentFileSize = 0; + if (O.TextSegmentCommandIndex) { + const LoadCommand &TextSegmentLoadCommand = + O.LoadCommands[*O.TextSegmentCommandIndex]; + TextSegmentFileOff = *TextSegmentLoadCommand.getSegmentFileOffset(); + TextSegmentFileSize = *TextSegmentLoadCommand.getSegmentFileSize(); + } + object::CodeSignatureSection sectionBuilder = object::CodeSignatureSection( + StartOfCodeSignature, OutputFilename, + static_cast(O.Header.FileType), + TextSegmentFileOff, TextSegmentFileSize); + CodeSignatureSectionSize = sectionBuilder.getSize(); + } uint64_t LinkEditSize = - (StartOfCodeSignature + O.CodeSignature.Data.size()) - StartOfLinkEdit; + (StartOfCodeSignature + CodeSignatureSectionSize) - StartOfLinkEdit; // Now we have determined the layout of the contents of the __LINKEDIT // segment. Update its load command. @@ -293,7 +310,7 @@ switch (cmd) { case MachO::LC_CODE_SIGNATURE: MLC.linkedit_data_command_data.dataoff = StartOfCodeSignature; - MLC.linkedit_data_command_data.datasize = O.CodeSignature.Data.size(); + MLC.linkedit_data_command_data.datasize = CodeSignatureSectionSize; break; case MachO::LC_SYMTAB: MLC.symtab_command_data.symoff = StartOfSymbols; diff --git a/llvm/tools/llvm-objcopy/MachO/MachOObjcopy.cpp b/llvm/tools/llvm-objcopy/MachO/MachOObjcopy.cpp --- a/llvm/tools/llvm-objcopy/MachO/MachOObjcopy.cpp +++ b/llvm/tools/llvm-objcopy/MachO/MachOObjcopy.cpp @@ -404,7 +404,8 @@ PageSize = 4096; } - MachOWriter Writer(**O, In.is64Bit(), In.isLittleEndian(), PageSize, Out); + MachOWriter Writer(**O, In.is64Bit(), In.isLittleEndian(), PageSize, Out, + Config.OutputFilename); if (auto E = Writer.finalize()) return E; return Writer.write(); diff --git a/llvm/tools/llvm-objcopy/MachO/MachOReader.cpp b/llvm/tools/llvm-objcopy/MachO/MachOReader.cpp --- a/llvm/tools/llvm-objcopy/MachO/MachOReader.cpp +++ b/llvm/tools/llvm-objcopy/MachO/MachOReader.cpp @@ -116,6 +116,7 @@ Error MachOReader::readLoadCommands(Object &O) const { // For MachO sections indices start from 1. uint32_t NextSectionIndex = 1; + static const char TextSegmentName[] = "__TEXT"; for (auto LoadCmd : MachOObj.load_commands()) { LoadCommand LC; switch (LoadCmd.C.cmd) { @@ -123,6 +124,11 @@ O.CodeSignatureCommandIndex = O.LoadCommands.size(); break; case MachO::LC_SEGMENT: + if (StringRef( + reinterpret_cast(LoadCmd.Ptr) + ->segname) == TextSegmentName) + O.TextSegmentCommandIndex = O.LoadCommands.size(); + if (Expected>> Sections = extractSections( LoadCmd, MachOObj, NextSectionIndex)) @@ -131,6 +137,11 @@ return Sections.takeError(); break; case MachO::LC_SEGMENT_64: + if (StringRef( + reinterpret_cast(LoadCmd.Ptr) + ->segname) == TextSegmentName) + O.TextSegmentCommandIndex = O.LoadCommands.size(); + if (Expected>> Sections = extractSections( LoadCmd, MachOObj, NextSectionIndex)) diff --git a/llvm/tools/llvm-objcopy/MachO/MachOWriter.h b/llvm/tools/llvm-objcopy/MachO/MachOWriter.h --- a/llvm/tools/llvm-objcopy/MachO/MachOWriter.h +++ b/llvm/tools/llvm-objcopy/MachO/MachOWriter.h @@ -20,6 +20,7 @@ class MachOWriter { Object &O; + const StringRef OutputFilename; bool Is64Bit; bool IsLittleEndian; uint64_t PageSize; @@ -54,9 +55,10 @@ public: MachOWriter(Object &O, bool Is64Bit, bool IsLittleEndian, uint64_t PageSize, - raw_ostream &Out) - : O(O), Is64Bit(Is64Bit), IsLittleEndian(IsLittleEndian), - PageSize(PageSize), Out(Out), LayoutBuilder(O, Is64Bit, PageSize) {} + raw_ostream &Out, const StringRef OutputFilename) + : O(O), OutputFilename(OutputFilename), Is64Bit(Is64Bit), + IsLittleEndian(IsLittleEndian), PageSize(PageSize), Out(Out), + LayoutBuilder(O, Is64Bit, PageSize, OutputFilename) {} size_t totalSize() const; Error finalize(); diff --git a/llvm/tools/llvm-objcopy/MachO/MachOWriter.cpp b/llvm/tools/llvm-objcopy/MachO/MachOWriter.cpp --- a/llvm/tools/llvm-objcopy/MachO/MachOWriter.cpp +++ b/llvm/tools/llvm-objcopy/MachO/MachOWriter.cpp @@ -7,6 +7,7 @@ //===----------------------------------------------------------------------===// #include "MachOWriter.h" +#include "../CommonConfig.h" #include "MachOLayoutBuilder.h" #include "Object.h" #include "llvm/ADT/STLExtras.h" @@ -424,7 +425,26 @@ } void MachOWriter::writeCodeSignatureData() { - return writeLinkData(O.CodeSignatureCommandIndex, O.CodeSignature); + if (O.CodeSignatureCommandIndex) { + MachO::linkedit_data_command &CodeSignatureLoadCommand = + O.LoadCommands[*O.CodeSignatureCommandIndex] + .MachOLoadCommand.linkedit_data_command_data; + + uint32_t TextSegmentFileOff = 0; + uint32_t TextSegmentFileSize = 0; + if (O.TextSegmentCommandIndex) { + const LoadCommand &TextSegmentLoadCommand = + O.LoadCommands[*O.TextSegmentCommandIndex]; + TextSegmentFileOff = *TextSegmentLoadCommand.getSegmentFileOffset(); + TextSegmentFileSize = *TextSegmentLoadCommand.getSegmentFileSize(); + } + object::CodeSignatureSection signatureBuilder = + object::CodeSignatureSection( + CodeSignatureLoadCommand.dataoff, OutputFilename, + static_cast(O.Header.FileType), + TextSegmentFileOff, TextSegmentFileSize); + signatureBuilder.write(reinterpret_cast(Buf->getBufferStart())); + } } void MachOWriter::writeDataInCodeData() { diff --git a/llvm/tools/llvm-objcopy/MachO/Object.h b/llvm/tools/llvm-objcopy/MachO/Object.h --- a/llvm/tools/llvm-objcopy/MachO/Object.h +++ b/llvm/tools/llvm-objcopy/MachO/Object.h @@ -98,6 +98,12 @@ // corresponding content inside the binary. std::vector> Sections; + // Returns the segment fileoff if the load command is a segment command + Optional getSegmentFileOffset() const; + + // Returns the segment filesize if the load command is a segment command + Optional getSegmentFileSize() const; + // Returns the segment name if the load command is a segment command. Optional getSegmentName() const; @@ -333,6 +339,9 @@ Optional LinkerOptimizationHintCommandIndex; /// The index LC_FUNCTION_STARTS load comamnd if present. Optional FunctionStartsCommandIndex; + /// The index of the LC_SEGMENT or LC_SEGMENT_64 load command + /// corresponding to the __TEXT segment + Optional TextSegmentCommandIndex; BumpPtrAllocator Alloc; StringSaver NewSectionsContents; diff --git a/llvm/tools/llvm-objcopy/MachO/Object.cpp b/llvm/tools/llvm-objcopy/MachO/Object.cpp --- a/llvm/tools/llvm-objcopy/MachO/Object.cpp +++ b/llvm/tools/llvm-objcopy/MachO/Object.cpp @@ -169,6 +169,30 @@ strnlen(SegName, sizeof(MachO::segment_command::segname))); } +Optional LoadCommand::getSegmentFileOffset() const { + const MachO::macho_load_command &MLC = MachOLoadCommand; + switch (MLC.load_command_data.cmd) { + case MachO::LC_SEGMENT: + return MLC.segment_command_data.fileoff; + case MachO::LC_SEGMENT_64: + return MLC.segment_command_64_data.fileoff; + default: + return None; + } +} + +Optional LoadCommand::getSegmentFileSize() const { + const MachO::macho_load_command &MLC = MachOLoadCommand; + switch (MLC.load_command_data.cmd) { + case MachO::LC_SEGMENT: + return MLC.segment_command_data.filesize; + case MachO::LC_SEGMENT_64: + return MLC.segment_command_64_data.filesize; + default: + return None; + } +} + Optional LoadCommand::getSegmentName() const { const MachO::macho_load_command &MLC = MachOLoadCommand; switch (MLC.load_command_data.cmd) {