diff --git a/lld/MachO/SyntheticSections.h b/lld/MachO/SyntheticSections.h --- a/lld/MachO/SyntheticSections.h +++ b/lld/MachO/SyntheticSections.h @@ -476,6 +476,8 @@ // The code signature comes at the very end of the linked output file. class CodeSignatureSection final : public LinkEditSection { public: + // NOTE: These values are duplicated in llvm-objcopy's MachO/Object.h file + // and any changes here, should be repeated there. static constexpr uint8_t blockSizeShift = 12; static constexpr size_t blockSize = (1 << blockSizeShift); // 4 KiB static constexpr size_t hashSize = 256 / 8; diff --git a/lld/MachO/SyntheticSections.cpp b/lld/MachO/SyntheticSections.cpp --- a/lld/MachO/SyntheticSections.cpp +++ b/lld/MachO/SyntheticSections.cpp @@ -1162,6 +1162,9 @@ size_t slashIndex = fileName.rfind("/"); if (slashIndex != std::string::npos) fileName = fileName.drop_front(slashIndex + 1); + + // NOTE: Any changes to these calculations should be repeated + // in llvm-objcopy's MachOLayoutBuilder::layoutTail. allHeadersSize = alignTo<16>(fixedHeadersSize + fileName.size() + 1); fileNamePad = allHeadersSize - fixedHeadersSize - fileName.size(); } @@ -1175,6 +1178,8 @@ } void CodeSignatureSection::writeHashes(uint8_t *buf) const { + // NOTE: Changes to this functionality should be repeated in llvm-objcopy's + // MachOWriter::writeSignatureData. uint8_t *code = buf; uint8_t *codeEnd = buf + fileOff; uint8_t *hashes = codeEnd + allHeadersSize; @@ -1205,6 +1210,8 @@ } void CodeSignatureSection::writeTo(uint8_t *buf) const { + // NOTE: Changes to this functionality should be repeated in llvm-objcopy's + // MachOWriter::writeSignatureData. uint32_t signatureSize = static_cast(getSize()); auto *superBlob = reinterpret_cast(buf); write32be(&superBlob->magic, CSMAGIC_EMBEDDED_SIGNATURE); 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-ORIGINAL -# CHECK: cmd LC_CODE_SIGNATURE -# CHECK-NEXT: cmdsize 16 -# CHECK-NEXT: dataoff 128 -# CHECK-NEXT: datasize 16 +# CHECK-ORIGINAL: cmd LC_CODE_SIGNATURE +# CHECK-ORIGINAL-NEXT: cmdsize 16 +# CHECK-ORIGINAL-NEXT: dataoff 16544 +# CHECK-ORIGINAL-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 @@ -19,6 +19,7 @@ class MachOLayoutBuilder { Object &O; bool Is64Bit; + StringRef OutputFileName; uint64_t PageSize; // Points to the __LINKEDIT segment if it exists. @@ -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, StringRef OutputFileName, + uint64_t PageSize) + : O(O), Is64Bit(Is64Bit), OutputFileName(OutputFileName), + 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 @@ -262,10 +262,31 @@ 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); + + // Note: These calculations are to be kept in sync with the same + // calculations performed in LLD's CodeSignatureSection. + const uint32_t AllHeadersSize = + alignTo(O.CodeSignature.FixedHeadersSize + OutputFileName.size() + 1, + O.CodeSignature.Align); + const uint32_t BlockCount = + (StartOfCodeSignature + O.CodeSignature.BlockSize - 1) / + O.CodeSignature.BlockSize; + const uint32_t SectionSize = + alignTo(AllHeadersSize + BlockCount * O.CodeSignature.HashSize, + O.CodeSignature.Align); + + O.CodeSignature.StartOffset = StartOfCodeSignature; + O.CodeSignature.AllHeadersSize = AllHeadersSize; + O.CodeSignature.BlockCount = BlockCount; + O.CodeSignature.OutputFileName = OutputFileName; + O.CodeSignature.SectionSize = SectionSize; + CodeSignatureSectionSize = SectionSize; + } 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 +314,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 @@ -20,6 +20,7 @@ #include "llvm/Support/Errc.h" #include "llvm/Support/Error.h" #include "llvm/Support/FileOutputBuffer.h" +#include "llvm/Support/Path.h" #include "llvm/Support/SmallVectorMemoryBuffer.h" using namespace llvm; @@ -404,7 +405,8 @@ PageSize = 4096; } - MachOWriter Writer(**O, In.is64Bit(), In.isLittleEndian(), PageSize, Out); + MachOWriter Writer(**O, In.is64Bit(), In.isLittleEndian(), + sys::path::filename(Config.OutputFilename), PageSize, Out); 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)) @@ -271,10 +282,6 @@ arrayRefFromStringRef(MachOObj.getData().substr(LC.dataoff, LC.datasize)); } -void MachOReader::readCodeSignature(Object &O) const { - return readLinkData(O, O.CodeSignatureCommandIndex, O.CodeSignature); -} - void MachOReader::readDataInCodeData(Object &O) const { return readLinkData(O, O.DataInCodeCommandIndex, O.DataInCode); } @@ -336,7 +343,6 @@ readWeakBindInfo(*Obj); readLazyBindInfo(*Obj); readExportInfo(*Obj); - readCodeSignature(*Obj); readDataInCodeData(*Obj); readLinkerOptimizationHint(*Obj); readFunctionStartsData(*Obj); 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 @@ -53,10 +53,11 @@ void writeTail(); public: - MachOWriter(Object &O, bool Is64Bit, bool IsLittleEndian, uint64_t PageSize, - raw_ostream &Out) + MachOWriter(Object &O, bool Is64Bit, bool IsLittleEndian, + StringRef OutputFileName, uint64_t PageSize, raw_ostream &Out) : O(O), Is64Bit(Is64Bit), IsLittleEndian(IsLittleEndian), - PageSize(PageSize), Out(Out), LayoutBuilder(O, Is64Bit, PageSize) {} + PageSize(PageSize), Out(Out), + LayoutBuilder(O, Is64Bit, OutputFileName, PageSize) {} 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 @@ -14,10 +14,16 @@ #include "llvm/Object/MachO.h" #include "llvm/Support/Errc.h" #include "llvm/Support/ErrorHandling.h" +#include "llvm/Support/SHA256.h" #include +#if defined(__APPLE__) +#include +#endif + using namespace llvm; using namespace llvm::objcopy::macho; +using namespace llvm::support::endian; size_t MachOWriter::headerSize() const { return Is64Bit ? sizeof(MachO::mach_header_64) : sizeof(MachO::mach_header); @@ -424,7 +430,110 @@ } void MachOWriter::writeCodeSignatureData() { - return writeLinkData(O.CodeSignatureCommandIndex, O.CodeSignature); + // NOTE: This CodeSignature section behaviour must be kept in sync with that + // performed in LLD's CodeSignatureSection::write / + // CodeSignatureSection::writeHashes. Furthermore, this call must occur only + // after the rest of the binary has already been written to the buffer. This + // is because the buffer is read from to perform the necessary hashing. + + // The CodeSignature section is the last section in the MachO binary and + // contains a hash of all content in the binary before it. Since llvm-objcopy + // has likely modified the target binary, the hash must be regenerated + // entirely. To generate this hash, we must read from the start of the binary + // (HashReadStart) to just before the start of the CodeSignature section + // (HashReadEnd). + uint8_t *BufferStart = reinterpret_cast(Buf->getBufferStart()); + uint8_t *HashReadStart = BufferStart; + uint8_t *HashReadEnd = BufferStart + O.CodeSignature.StartOffset; + + // The CodeSignature section begins with a header, after which the hashes + // of each page of the binary are written. + uint8_t *HashWriteStart = HashReadEnd + O.CodeSignature.AllHeadersSize; + + uint32_t TextSegmentFileOff = 0; + uint32_t TextSegmentFileSize = 0; + if (O.TextSegmentCommandIndex) { + const LoadCommand &TextSegmentLoadCommand = + O.LoadCommands[*O.TextSegmentCommandIndex]; + TextSegmentFileOff = *TextSegmentLoadCommand.getSegmentFileOffset(); + TextSegmentFileSize = *TextSegmentLoadCommand.getSegmentFileSize(); + } + + const uint32_t FileNamePad = O.CodeSignature.AllHeadersSize - + O.CodeSignature.FixedHeadersSize - + O.CodeSignature.OutputFileName.size(); + + // Write code section header. + auto *SuperBlob = reinterpret_cast(HashReadEnd); + write32be(&SuperBlob->magic, MachO::CSMAGIC_EMBEDDED_SIGNATURE); + write32be(&SuperBlob->length, O.CodeSignature.SectionSize); + write32be(&SuperBlob->count, 1); + auto *BlobIndex = reinterpret_cast(&SuperBlob[1]); + write32be(&BlobIndex->type, MachO::CSSLOT_CODEDIRECTORY); + write32be(&BlobIndex->offset, O.CodeSignature.BlobHeadersSize); + auto *CodeDirectory = reinterpret_cast( + HashReadEnd + O.CodeSignature.BlobHeadersSize); + write32be(&CodeDirectory->magic, MachO::CSMAGIC_CODEDIRECTORY); + write32be(&CodeDirectory->length, + O.CodeSignature.SectionSize - O.CodeSignature.BlobHeadersSize); + write32be(&CodeDirectory->version, MachO::CS_SUPPORTSEXECSEG); + write32be(&CodeDirectory->flags, MachO::CS_ADHOC | MachO::CS_LINKER_SIGNED); + write32be(&CodeDirectory->hashOffset, + sizeof(MachO::CS_CodeDirectory) + + O.CodeSignature.OutputFileName.size() + FileNamePad); + write32be(&CodeDirectory->identOffset, sizeof(MachO::CS_CodeDirectory)); + CodeDirectory->nSpecialSlots = 0; + write32be(&CodeDirectory->nCodeSlots, O.CodeSignature.BlockCount); + write32be(&CodeDirectory->codeLimit, O.CodeSignature.StartOffset); + CodeDirectory->hashSize = static_cast(O.CodeSignature.HashSize); + CodeDirectory->hashType = MachO::kSecCodeSignatureHashSHA256; + CodeDirectory->platform = 0; + CodeDirectory->pageSize = O.CodeSignature.BlockSizeShift; + CodeDirectory->spare2 = 0; + CodeDirectory->scatterOffset = 0; + CodeDirectory->teamOffset = 0; + CodeDirectory->spare3 = 0; + CodeDirectory->codeLimit64 = 0; + write64be(&CodeDirectory->execSegBase, TextSegmentFileOff); + write64be(&CodeDirectory->execSegLimit, TextSegmentFileSize); + write64be(&CodeDirectory->execSegFlags, O.Header.FileType == MachO::MH_EXECUTE + ? MachO::CS_EXECSEG_MAIN_BINARY + : 0); + + auto *Id = reinterpret_cast(&CodeDirectory[1]); + memcpy(Id, O.CodeSignature.OutputFileName.begin(), + O.CodeSignature.OutputFileName.size()); + memset(Id + O.CodeSignature.OutputFileName.size(), 0, FileNamePad); + + // Write the hashes. + uint8_t *CurrHashReadPosition = HashReadStart; + uint8_t *CurrHashWritePosition = HashWriteStart; + while (CurrHashReadPosition < HashReadEnd) { + StringRef Block(reinterpret_cast(CurrHashReadPosition), + std::min(HashReadEnd - CurrHashReadPosition, + static_cast(O.CodeSignature.BlockSize))); + SHA256 Hasher; + Hasher.update(Block); + StringRef Hash = Hasher.final(); + assert(Hash.size() == O.CodeSignature.HashSize); + memcpy(CurrHashWritePosition, Hash.data(), O.CodeSignature.HashSize); + CurrHashReadPosition += O.CodeSignature.BlockSize; + CurrHashWritePosition += O.CodeSignature.HashSize; + } +#if defined(__APPLE__) + // This is macOS-specific work-around and makes no sense for any + // other host OS. See https://openradar.appspot.com/FB8914231 + // + // The macOS kernel maintains a signature-verification cache to + // quickly validate applications at time of execve(2). The trouble + // is that for the kernel creates the cache entry at the time of the + // mmap(2) call, before we have a chance to write either the code to + // sign or the signature header+hashes. The fix is to invalidate + // all cached data associated with the output file, thus discarding + // the bogus prematurely-cached signature. + msync(BufferStart, O.CodeSignature.StartOffset + O.CodeSignature.SectionSize, + MS_INVALIDATE); +#endif } 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; @@ -299,6 +305,43 @@ ArrayRef Data; }; +/// When MachO binaries include a LC_CODE_SIGNATURE load command, +/// the __LINKEDIT data segment will include a section corresponding +/// to the LC_CODE_SIGNATURE load command. This section serves as a signature +/// for the binary. Included in the CodeSignature section is a header followed +/// by a hash of the binary. If present, the CodeSignature section is the +/// last component of the binary. +struct CodeSignatureInfo { + // NOTE: These values are to be kept in sync with those in + // LLD's CodeSignatureSection class. + + static constexpr uint32_t Align = 16; + static constexpr uint8_t BlockSizeShift = 12; + // The binary is read in blocks of the following size. + static constexpr size_t BlockSize = (1 << BlockSizeShift); // 4 KiB + // For each block, a SHA256 hash (256 bits, 32 bytes) is written to + // the CodeSignature section. + static constexpr size_t HashSize = 256 / 8; + static constexpr size_t BlobHeadersSize = llvm::alignTo<8>( + sizeof(llvm::MachO::CS_SuperBlob) + sizeof(llvm::MachO::CS_BlobIndex)); + // The size of the entire header depends upon the filename the binary is being + // written to, but the rest of the header is fixed in size. + static constexpr uint32_t FixedHeadersSize = + BlobHeadersSize + sizeof(llvm::MachO::CS_CodeDirectory); + + // The offset relative to the start of the binary where + // the CodeSignature section should begin. + uint32_t StartOffset; + // The size of the entire header, output file name size included. + uint32_t AllHeadersSize; + // The number of blocks required to hash the binary. + uint32_t BlockCount; + StringRef OutputFileName; + // The size of the entire CodeSignature section, including both the header and + // hashes. + uint32_t SectionSize; +}; + struct Object { MachHeader Header; std::vector LoadCommands; @@ -315,7 +358,7 @@ LinkData DataInCode; LinkData LinkerOptimizationHint; LinkData FunctionStarts; - LinkData CodeSignature; + CodeSignatureInfo CodeSignature; Optional SwiftVersion; @@ -333,6 +376,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) {