[llvm] a299b24 - Regenerate LC_CODE_SIGNATURE during llvm-objcopy operations
Daniel RodrÃguez Troitiño via llvm-commits
llvm-commits at lists.llvm.org
Tue Oct 26 14:52:30 PDT 2021
Author: Nuri Amari
Date: 2021-10-26T14:51:13-07:00
New Revision: a299b24712ccd0769ffa0d5b65cbc98211fd4d99
URL: https://github.com/llvm/llvm-project/commit/a299b24712ccd0769ffa0d5b65cbc98211fd4d99
DIFF: https://github.com/llvm/llvm-project/commit/a299b24712ccd0769ffa0d5b65cbc98211fd4d99.diff
LOG: Regenerate LC_CODE_SIGNATURE during llvm-objcopy operations
**Context:**
This is a second attempt at introducing signature regeneration to llvm-objcopy. In this diff: https://reviews.llvm.org/D109840, a script was introduced to test
the validity of a code signature. In this diff: https://reviews.llvm.org/D109803 (now reverted), an effort was made to extract the signature generation behavior out of LLD into a common location for use in llvm-objcopy. In this diff: https://reviews.llvm.org/D109972 it was decided that there was no appropriate common location and that a small amount of duplication to bring signature generation to llvm-objcopy would be better. This diff introduces this duplication.
**Summary**
Prior to this change, if a LC_CODE_SIGNATURE load command
was included in the binary passed to llvm-objcopy, the command and
associated section were simply copied and included verbatim in the
new binary. If rest of the binary was modified at all, this results
in an invalid Mach-O file. This change regenerates the signature
rather than copying it.
The code_signature_lc.test test was modified to include the yaml
representation of a small signed MachO executable in order to
effectively test the signature generation.
Reviewed By: alexander-shaposhnikov, #lld-macho
Differential Revision: https://reviews.llvm.org/D111164
Added:
llvm/test/tools/llvm-objcopy/MachO/Inputs/code-signature-check.py
llvm/test/tools/llvm-objcopy/MachO/code_signature_lc_update.test
Modified:
lld/MachO/SyntheticSections.cpp
lld/MachO/SyntheticSections.h
llvm/test/tools/llvm-objcopy/MachO/code_signature_lc.test
llvm/tools/llvm-objcopy/MachO/MachOLayoutBuilder.cpp
llvm/tools/llvm-objcopy/MachO/MachOLayoutBuilder.h
llvm/tools/llvm-objcopy/MachO/MachOObjcopy.cpp
llvm/tools/llvm-objcopy/MachO/MachOReader.cpp
llvm/tools/llvm-objcopy/MachO/MachOWriter.cpp
llvm/tools/llvm-objcopy/MachO/MachOWriter.h
llvm/tools/llvm-objcopy/MachO/Object.cpp
llvm/tools/llvm-objcopy/MachO/Object.h
Removed:
################################################################################
diff --git a/lld/MachO/SyntheticSections.cpp b/lld/MachO/SyntheticSections.cpp
index d815e8d53785d..88b0167f7dec5 100644
--- a/lld/MachO/SyntheticSections.cpp
+++ b/lld/MachO/SyntheticSections.cpp
@@ -1169,6 +1169,9 @@ CodeSignatureSection::CodeSignatureSection()
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();
}
@@ -1182,6 +1185,8 @@ uint64_t CodeSignatureSection::getRawSize() const {
}
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;
@@ -1212,6 +1217,8 @@ void CodeSignatureSection::writeHashes(uint8_t *buf) const {
}
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<uint32_t>(getSize());
auto *superBlob = reinterpret_cast<CS_SuperBlob *>(buf);
write32be(&superBlob->magic, CSMAGIC_EMBEDDED_SIGNATURE);
diff --git a/lld/MachO/SyntheticSections.h b/lld/MachO/SyntheticSections.h
index bbb7adc37cb35..0495ee433d3e2 100644
--- a/lld/MachO/SyntheticSections.h
+++ b/lld/MachO/SyntheticSections.h
@@ -476,6 +476,8 @@ class IndirectSymtabSection final : public LinkEditSection {
// 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/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 100644
index 0000000000000..75c01a2174c7e
--- /dev/null
+++ b/llvm/test/tools/llvm-objcopy/MachO/Inputs/code-signature-check.py
@@ -0,0 +1,257 @@
+"""Checks the validity of MachO binary signatures
+
+MachO binaries sometimes include a LC_CODE_SIGNATURE load command
+and corresponding section in the __LINKEDIT segment that together
+work to "sign" the binary. This script is used to check the validity
+of this signature.
+
+Usage:
+ ./code-signature-check.py my_binary 800 300 0 800
+
+Arguments:
+ binary - The MachO binary to be tested
+ offset - The offset from the start of the binary to where the code signature section begins
+ size - The size of the code signature section in the binary
+ code_offset - The point in the binary to begin hashing
+ code_size - The length starting from code_offset to hash
+"""
+
+import argparse
+import collections
+import hashlib
+import itertools
+import struct
+import sys
+import typing
+
+class CodeDirectoryVersion:
+ SUPPORTSSCATTER = 0x20100
+ SUPPORTSTEAMID = 0x20200
+ SUPPORTSCODELIMIT64 = 0x20300
+ SUPPORTSEXECSEG = 0x20400
+
+class CodeDirectory:
+ @staticmethod
+ def make(buf: memoryview) -> typing.Union['CodeDirectoryBase', 'CodeDirectoryV20100', 'CodeDirectoryV20200', 'CodeDirectoryV20300', 'CodeDirectoryV20400']:
+ _magic, _length, version = struct.unpack_from(">III", buf, 0)
+ subtype = {
+ CodeDirectoryVersion.SUPPORTSSCATTER: CodeDirectoryV20100,
+ CodeDirectoryVersion.SUPPORTSTEAMID: CodeDirectoryV20200,
+ CodeDirectoryVersion.SUPPORTSCODELIMIT64: CodeDirectoryV20300,
+ CodeDirectoryVersion.SUPPORTSEXECSEG: CodeDirectoryV20400,
+ }.get(version, CodeDirectoryBase)
+
+ return subtype._make(struct.unpack_from(subtype._format(), buf, 0))
+
+class CodeDirectoryBase(typing.NamedTuple):
+ magic: int
+ length: int
+ version: int
+ flags: int
+ hashOffset: int
+ identOffset: int
+ nSpecialSlots: int
+ nCodeSlots: int
+ codeLimit: int
+ hashSize: int
+ hashType: int
+ platform: int
+ pageSize: int
+ spare2: int
+
+ @staticmethod
+ def _format() -> str:
+ return ">IIIIIIIIIBBBBI"
+
+class CodeDirectoryV20100(typing.NamedTuple):
+ magic: int
+ length: int
+ version: int
+ flags: int
+ hashOffset: int
+ identOffset: int
+ nSpecialSlots: int
+ nCodeSlots: int
+ codeLimit: int
+ hashSize: int
+ hashType: int
+ platform: int
+ pageSize: int
+ spare2: int
+
+ scatterOffset: int
+
+ @staticmethod
+ def _format() -> str:
+ return CodeDirectoryBase._format() + "I"
+
+class CodeDirectoryV20200(typing.NamedTuple):
+ magic: int
+ length: int
+ version: int
+ flags: int
+ hashOffset: int
+ identOffset: int
+ nSpecialSlots: int
+ nCodeSlots: int
+ codeLimit: int
+ hashSize: int
+ hashType: int
+ platform: int
+ pageSize: int
+ spare2: int
+
+ scatterOffset: int
+
+ teamOffset: int
+
+ @staticmethod
+ def _format() -> str:
+ return CodeDirectoryV20100._format() + "I"
+
+class CodeDirectoryV20300(typing.NamedTuple):
+ magic: int
+ length: int
+ version: int
+ flags: int
+ hashOffset: int
+ identOffset: int
+ nSpecialSlots: int
+ nCodeSlots: int
+ codeLimit: int
+ hashSize: int
+ hashType: int
+ platform: int
+ pageSize: int
+ spare2: int
+
+ scatterOffset: int
+
+ teamOffset: int
+
+ spare3: int
+ codeLimit64: int
+
+ @staticmethod
+ def _format() -> str:
+ return CodeDirectoryV20200._format() + "IQ"
+
+class CodeDirectoryV20400(typing.NamedTuple):
+ magic: int
+ length: int
+ version: int
+ flags: int
+ hashOffset: int
+ identOffset: int
+ nSpecialSlots: int
+ nCodeSlots: int
+ codeLimit: int
+ hashSize: int
+ hashType: int
+ platform: int
+ pageSize: int
+ spare2: int
+
+ scatterOffset: int
+
+ teamOffset: int
+
+ spare3: int
+ codeLimit64: int
+
+ execSegBase: int
+ execSegLimit: int
+ execSegFlags: int
+
+ @staticmethod
+ def _format() -> str:
+ return CodeDirectoryV20300._format() + "QQQ"
+
+class CodeDirectoryBlobIndex(typing.NamedTuple):
+ type_: int
+ offset: int
+
+ @staticmethod
+ def make(buf: memoryview) -> 'CodeDirectoryBlobIndex':
+ return CodeDirectoryBlobIndex._make(struct.unpack_from(CodeDirectoryBlobIndex.__format(), buf, 0))
+
+ @staticmethod
+ def bytesize() -> int:
+ return struct.calcsize(CodeDirectoryBlobIndex.__format())
+
+ @staticmethod
+ def __format() -> str:
+ return ">II"
+
+class CodeDirectorySuperBlob(typing.NamedTuple):
+ magic: int
+ length: int
+ count: int
+ blob_indices: typing.List[CodeDirectoryBlobIndex]
+
+ @staticmethod
+ def make(buf: memoryview) -> 'CodeDirectorySuperBlob':
+ super_blob_layout = ">III"
+ super_blob = struct.unpack_from(super_blob_layout, buf, 0)
+
+ offset = struct.calcsize(super_blob_layout)
+ blob_indices = []
+ for idx in range(super_blob[2]):
+ blob_indices.append(CodeDirectoryBlobIndex.make(buf[offset:]))
+ offset += CodeDirectoryBlobIndex.bytesize()
+
+ return CodeDirectorySuperBlob(*super_blob, blob_indices)
+
+def unpack_null_terminated_string(buf: memoryview) -> str:
+ b = bytes(itertools.takewhile(lambda b: b != 0, buf))
+ return b.decode()
+
+def main():
+ parser = argparse.ArgumentParser()
+ parser.add_argument('binary', type=argparse.FileType('rb'), help='The file to analyze')
+ parser.add_argument('offset', type=int, help='Offset to start of Code Directory data')
+ parser.add_argument('size', type=int, help='Size of Code Directory data')
+ parser.add_argument('code_offset', type=int, help='Offset to start of code pages to hash')
+ parser.add_argument('code_size', type=int, help='Size of the code pages to hash')
+
+ args = parser.parse_args()
+
+ args.binary.seek(args.offset)
+ super_blob_bytes = args.binary.read(args.size)
+ super_blob_mem = memoryview(super_blob_bytes)
+
+ super_blob = CodeDirectorySuperBlob.make(super_blob_mem)
+ print(super_blob)
+
+ for blob_index in super_blob.blob_indices:
+ code_directory_offset = blob_index.offset
+ code_directory = CodeDirectory.make(super_blob_mem[code_directory_offset:])
+ print(code_directory)
+
+ ident_offset = code_directory_offset + code_directory.identOffset
+ print("Code Directory ID: " + unpack_null_terminated_string(super_blob_mem[ident_offset:]))
+
+ code_offset = args.code_offset
+ code_end = code_offset + args.code_size
+ page_size = 1 << code_directory.pageSize
+ args.binary.seek(code_offset)
+
+ hashes_offset = code_directory_offset + code_directory.hashOffset
+ for idx in range(code_directory.nCodeSlots):
+ hash_bytes = bytes(super_blob_mem[hashes_offset:hashes_offset+code_directory.hashSize])
+ hashes_offset += code_directory.hashSize
+
+ hasher = hashlib.sha256()
+ read_size = min(page_size, code_end - code_offset)
+ hasher.update(args.binary.read(read_size))
+ calculated_hash_bytes = hasher.digest()
+ code_offset += read_size
+
+ print("%s <> %s" % (hash_bytes.hex(), calculated_hash_bytes.hex()))
+
+ if hash_bytes != calculated_hash_bytes:
+ sys.exit(-1)
+
+
+if __name__ == '__main__':
+ main()
diff --git a/llvm/test/tools/llvm-objcopy/MachO/code_signature_lc.test b/llvm/test/tools/llvm-objcopy/MachO/code_signature_lc.test
index 92f94219cb2bb..da828a425634a 100644
--- 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/test/tools/llvm-objcopy/MachO/code_signature_lc_update.test b/llvm/test/tools/llvm-objcopy/MachO/code_signature_lc_update.test
new file mode 100644
index 0000000000000..c25491ed3f9e1
--- /dev/null
+++ b/llvm/test/tools/llvm-objcopy/MachO/code_signature_lc_update.test
@@ -0,0 +1,284 @@
+# RUN: yaml2obj %s -o %t
+
+# RUN: llvm-objdump --private-headers %t | FileCheck %s --check-prefix=CHECK-ORIGINAL
+
+## Check offset, size and index of text segment command
+# CHECK-ORIGINAL: Load command 1
+# CHECK-ORIGINAL-NEXT cmdsize
+# CHECK-ORIGINAL-NEXT segname __TEXT
+# CHECK-ORIGINAL-NEXT vmaddr
+# CHECK-ORIGINAL-NEXT vmsize
+# CHECK-ORIGINAL-NEXT fileoff 0
+# CHECK-ORIGINAL-NEXT filesize 16384
+
+## Check offset and index of code signature command
+# CHECK-ORIGINAL: Load command 14
+# CHECK-ORIGINAL-NEXT: cmd LC_CODE_SIGNATURE
+# CHECK-ORIGINAL-NEXT: cmdsize 16
+# CHECK-ORIGINAL-NEXT: dataoff 16544
+# CHECK-ORIGINAL-NEXT: datasize 280
+
+# RUN: llvm-install-name-tool -prepend_rpath abcd %t
+
+# RUN: llvm-objdump --private-headers %t | FileCheck %s --check-prefix=CHECK-PREPEND
+## Verify that the binary contains the new RPATH command, as the first command
+# CHECK-PREPEND: Load command 0
+# CHECK-PREPEND-NEXT: cmd LC_RPATH
+# CHECK-PREPEND-NEXT: cmdsize
+# CHECK-PREPEND-NEXT: path abcd
+
+## Verify the text segment command index increased by 1
+# CHECK-PREPEND: Load command 2
+# CHECK-PREPEND-NEXT cmdsize
+# CHECK-PREPEND-NEXT segname __TEXT
+# CHECK-PREPEND-NEXT vmaddr
+# CHECK-PREPEND-NEXT vmsize
+# CHECK-PREPEND-NEXT fileoff 0
+# CHECK-PREPEND-NEXT filesize 16384
+
+## Verify the code signature command index increased by 1
+# CHECK-PREPEND: Load command 15
+# CHECK-PREPEND-NEXT: cmd LC_CODE_SIGNATURE
+# CHECK-PREPEND-NEXT: cmdsize 16
+# CHECK-PREPEND-NEXT: dataoff 16544
+# CHECK-PREPEND-NEXT: datasize 320
+
+## Verify the new signature is valid
+# RUN: %python %p/Inputs/code-signature-check.py %t 16544 320 0 16544 | FileCheck %s --check-prefix=CHECK-TEXT-SEGMENT
+## Verify the text segment offset and text segment size values included in the signature header are accurate
+# CHECK-TEXT-SEGMENT: execSegBase=0, execSegLimit=16384
+
+# RUN: llvm-install-name-tool -delete_rpath abcd %t
+
+# RUN: llvm-objdump --private-headers %t | FileCheck %s --check-prefix=CHECK-REMOVE
+
+## Verify text segment command index returned to orignal
+# CHECK-REMOVE: Load command 1
+# CHECK-REMOVE-NEXT cmdsize
+# CHECK-REMOVE-NEXT segname __TEXT
+# CHECK-REMOVE-NEXT vmaddr
+# CHECK-REMOVE-NEXT vmsize
+# CHECK-REMOVE-NEXT fileoff 0
+# CHECK-REMOVE-NEXT filesize 16384
+
+## Verify text segment command index returned to original
+# CHECK-REMOVE: Load command 14
+# CHECK-REMOVE-NEXT: cmd LC_CODE_SIGNATURE
+# CHECK-REMOVE-NEXT: cmdsize 16
+# CHECK-REMOVE-NEXT: dataoff 16544
+# CHECK-REMOVE-NEXT: datasize 320
+
+## Verify the new signature is valid and text segment values are accurate
+# RUN: %python %p/Inputs/code-signature-check.py %t 16544 320 0 16544 | FileCheck %s --check-prefix=CHECK-TEXT-SEGMENT
+
+--- !mach-o
+FileHeader:
+ magic: 0xFEEDFACF
+ 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: 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: 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.cpp b/llvm/tools/llvm-objcopy/MachO/MachOLayoutBuilder.cpp
index 3230e5ee94159..537de292a367c 100644
--- a/llvm/tools/llvm-objcopy/MachO/MachOLayoutBuilder.cpp
+++ b/llvm/tools/llvm-objcopy/MachO/MachOLayoutBuilder.cpp
@@ -262,10 +262,31 @@ Error MachOLayoutBuilder::layoutTail(uint64_t Offset) {
sizeof(uint32_t) * O.IndirectSymTable.Symbols.size();
uint64_t StartOfCodeSignature =
StartOfSymbolStrings + StrTableBuilder.getSize();
- if (O.CodeSignatureCommandIndex)
+ uint32_t CodeSignatureSize = 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(CodeSignature.FixedHeadersSize + OutputFileName.size() + 1,
+ CodeSignature.Align);
+ const uint32_t BlockCount =
+ (StartOfCodeSignature + CodeSignature.BlockSize - 1) /
+ CodeSignature.BlockSize;
+ const uint32_t Size =
+ alignTo(AllHeadersSize + BlockCount * CodeSignature.HashSize,
+ CodeSignature.Align);
+
+ CodeSignature.StartOffset = StartOfCodeSignature;
+ CodeSignature.AllHeadersSize = AllHeadersSize;
+ CodeSignature.BlockCount = BlockCount;
+ CodeSignature.OutputFileName = OutputFileName;
+ CodeSignature.Size = Size;
+ CodeSignatureSize = Size;
+ }
uint64_t LinkEditSize =
- (StartOfCodeSignature + O.CodeSignature.Data.size()) - StartOfLinkEdit;
+ StartOfCodeSignature + CodeSignatureSize - StartOfLinkEdit;
// Now we have determined the layout of the contents of the __LINKEDIT
// segment. Update its load command.
@@ -293,7 +314,7 @@ Error MachOLayoutBuilder::layoutTail(uint64_t Offset) {
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 = CodeSignatureSize;
break;
case MachO::LC_SYMTAB:
MLC.symtab_command_data.symoff = StartOfSymbols;
diff --git a/llvm/tools/llvm-objcopy/MachO/MachOLayoutBuilder.h b/llvm/tools/llvm-objcopy/MachO/MachOLayoutBuilder.h
index 5fe6683e27f35..44d03b4af7e83 100644
--- a/llvm/tools/llvm-objcopy/MachO/MachOLayoutBuilder.h
+++ b/llvm/tools/llvm-objcopy/MachO/MachOLayoutBuilder.h
@@ -16,10 +16,49 @@ namespace llvm {
namespace objcopy {
namespace macho {
+/// 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 Size;
+};
+
class MachOLayoutBuilder {
Object &O;
bool Is64Bit;
+ StringRef OutputFileName;
uint64_t PageSize;
+ CodeSignatureInfo CodeSignature;
// Points to the __LINKEDIT segment if it exists.
MachO::macho_load_command *LinkEditLoadCommand = nullptr;
@@ -37,14 +76,18 @@ class MachOLayoutBuilder {
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.
Error layout();
StringTableBuilder &getStringTableBuilder() { return StrTableBuilder; }
+
+ const CodeSignatureInfo &getCodeSignature() { return CodeSignature; }
};
} // end namespace macho
diff --git a/llvm/tools/llvm-objcopy/MachO/MachOObjcopy.cpp b/llvm/tools/llvm-objcopy/MachO/MachOObjcopy.cpp
index 1b05779e03d03..ee15a230727eb 100644
--- 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 @@ Error objcopy::macho::executeObjcopyOnBinary(const CommonConfig &Config,
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
index 7d1c29b42c2e4..c0377e79b5d1f 100644
--- a/llvm/tools/llvm-objcopy/MachO/MachOReader.cpp
+++ b/llvm/tools/llvm-objcopy/MachO/MachOReader.cpp
@@ -116,6 +116,7 @@ Expected<std::vector<std::unique_ptr<Section>>> static extractSections(
Error MachOReader::readLoadCommands(Object &O) const {
// For MachO sections indices start from 1.
uint32_t NextSectionIndex = 1;
+ static constexpr char TextSegmentName[] = "__TEXT";
for (auto LoadCmd : MachOObj.load_commands()) {
LoadCommand LC;
switch (LoadCmd.C.cmd) {
@@ -123,6 +124,11 @@ Error MachOReader::readLoadCommands(Object &O) const {
O.CodeSignatureCommandIndex = O.LoadCommands.size();
break;
case MachO::LC_SEGMENT:
+ if (StringRef(
+ reinterpret_cast<MachO::segment_command const *>(LoadCmd.Ptr)
+ ->segname) == TextSegmentName)
+ O.TextSegmentCommandIndex = O.LoadCommands.size();
+
if (Expected<std::vector<std::unique_ptr<Section>>> Sections =
extractSections<MachO::section, MachO::segment_command>(
LoadCmd, MachOObj, NextSectionIndex))
@@ -131,6 +137,11 @@ Error MachOReader::readLoadCommands(Object &O) const {
return Sections.takeError();
break;
case MachO::LC_SEGMENT_64:
+ if (StringRef(
+ reinterpret_cast<MachO::segment_command_64 const *>(LoadCmd.Ptr)
+ ->segname) == TextSegmentName)
+ O.TextSegmentCommandIndex = O.LoadCommands.size();
+
if (Expected<std::vector<std::unique_ptr<Section>>> Sections =
extractSections<MachO::section_64, MachO::segment_command_64>(
LoadCmd, MachOObj, NextSectionIndex))
@@ -271,10 +282,6 @@ void MachOReader::readLinkData(Object &O, Optional<size_t> LCIndex,
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 @@ Expected<std::unique_ptr<Object>> MachOReader::create() const {
readWeakBindInfo(*Obj);
readLazyBindInfo(*Obj);
readExportInfo(*Obj);
- readCodeSignature(*Obj);
readDataInCodeData(*Obj);
readLinkerOptimizationHint(*Obj);
readFunctionStartsData(*Obj);
diff --git a/llvm/tools/llvm-objcopy/MachO/MachOWriter.cpp b/llvm/tools/llvm-objcopy/MachO/MachOWriter.cpp
index 295098ed41183..6b805ddf5b4bf 100644
--- 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 <memory>
+#if defined(__APPLE__)
+#include <sys/mman.h>
+#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);
@@ -423,8 +429,147 @@ void MachOWriter::writeLinkData(Optional<size_t> LCIndex, const LinkData &LD) {
memcpy(Out, LD.Data.data(), LD.Data.size());
}
+static uint64_t
+getSegmentFileOffset(const LoadCommand &TextSegmentLoadCommand) {
+ const MachO::macho_load_command &MLC =
+ TextSegmentLoadCommand.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 0;
+ }
+}
+
+static uint64_t getSegmentFileSize(const LoadCommand &TextSegmentLoadCommand) {
+ const MachO::macho_load_command &MLC =
+ TextSegmentLoadCommand.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 0;
+ }
+}
+
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).
+
+ const CodeSignatureInfo &CodeSignature = LayoutBuilder.getCodeSignature();
+
+ uint8_t *BufferStart = reinterpret_cast<uint8_t *>(Buf->getBufferStart());
+ uint8_t *HashReadStart = BufferStart;
+ uint8_t *HashReadEnd = BufferStart + 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 + CodeSignature.AllHeadersSize;
+
+ uint32_t TextSegmentFileOff = 0;
+ uint32_t TextSegmentFileSize = 0;
+ if (O.TextSegmentCommandIndex) {
+ const LoadCommand &TextSegmentLoadCommand =
+ O.LoadCommands[*O.TextSegmentCommandIndex];
+ assert(TextSegmentLoadCommand.MachOLoadCommand.load_command_data.cmd ==
+ MachO::LC_SEGMENT ||
+ TextSegmentLoadCommand.MachOLoadCommand.load_command_data.cmd ==
+ MachO::LC_SEGMENT_64);
+ assert(StringRef(TextSegmentLoadCommand.MachOLoadCommand
+ .segment_command_data.segname) == "__TEXT");
+ TextSegmentFileOff = getSegmentFileOffset(TextSegmentLoadCommand);
+ TextSegmentFileSize = getSegmentFileSize(TextSegmentLoadCommand);
+ }
+
+ const uint32_t FileNamePad = CodeSignature.AllHeadersSize -
+ CodeSignature.FixedHeadersSize -
+ CodeSignature.OutputFileName.size();
+
+ // Write code section header.
+ auto *SuperBlob = reinterpret_cast<MachO::CS_SuperBlob *>(HashReadEnd);
+ write32be(&SuperBlob->magic, MachO::CSMAGIC_EMBEDDED_SIGNATURE);
+ write32be(&SuperBlob->length, CodeSignature.Size);
+ write32be(&SuperBlob->count, 1);
+ auto *BlobIndex = reinterpret_cast<MachO::CS_BlobIndex *>(&SuperBlob[1]);
+ write32be(&BlobIndex->type, MachO::CSSLOT_CODEDIRECTORY);
+ write32be(&BlobIndex->offset, CodeSignature.BlobHeadersSize);
+ auto *CodeDirectory = reinterpret_cast<MachO::CS_CodeDirectory *>(
+ HashReadEnd + CodeSignature.BlobHeadersSize);
+ write32be(&CodeDirectory->magic, MachO::CSMAGIC_CODEDIRECTORY);
+ write32be(&CodeDirectory->length,
+ CodeSignature.Size - 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) +
+ CodeSignature.OutputFileName.size() + FileNamePad);
+ write32be(&CodeDirectory->identOffset, sizeof(MachO::CS_CodeDirectory));
+ CodeDirectory->nSpecialSlots = 0;
+ write32be(&CodeDirectory->nCodeSlots, CodeSignature.BlockCount);
+ write32be(&CodeDirectory->codeLimit, CodeSignature.StartOffset);
+ CodeDirectory->hashSize = static_cast<uint8_t>(CodeSignature.HashSize);
+ CodeDirectory->hashType = MachO::kSecCodeSignatureHashSHA256;
+ CodeDirectory->platform = 0;
+ CodeDirectory->pageSize = 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<char *>(&CodeDirectory[1]);
+ memcpy(Id, CodeSignature.OutputFileName.begin(),
+ CodeSignature.OutputFileName.size());
+ memset(Id + CodeSignature.OutputFileName.size(), 0, FileNamePad);
+
+ // Write the hashes.
+ uint8_t *CurrHashReadPosition = HashReadStart;
+ uint8_t *CurrHashWritePosition = HashWriteStart;
+ while (CurrHashReadPosition < HashReadEnd) {
+ StringRef Block(reinterpret_cast<char *>(CurrHashReadPosition),
+ std::min(HashReadEnd - CurrHashReadPosition,
+ static_cast<ssize_t>(CodeSignature.BlockSize)));
+ SHA256 Hasher;
+ Hasher.update(Block);
+ StringRef Hash = Hasher.final();
+ assert(Hash.size() == CodeSignature.HashSize);
+ memcpy(CurrHashWritePosition, Hash.data(), CodeSignature.HashSize);
+ CurrHashReadPosition += CodeSignature.BlockSize;
+ CurrHashWritePosition += 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, CodeSignature.StartOffset + CodeSignature.Size,
+ MS_INVALIDATE);
+#endif
}
void MachOWriter::writeDataInCodeData() {
diff --git a/llvm/tools/llvm-objcopy/MachO/MachOWriter.h b/llvm/tools/llvm-objcopy/MachO/MachOWriter.h
index c8c06d644e9f7..e6c4bc716da77 100644
--- a/llvm/tools/llvm-objcopy/MachO/MachOWriter.h
+++ b/llvm/tools/llvm-objcopy/MachO/MachOWriter.h
@@ -53,10 +53,11 @@ class MachOWriter {
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/Object.cpp b/llvm/tools/llvm-objcopy/MachO/Object.cpp
index b4f98fa84cb59..b0557a538490e 100644
--- a/llvm/tools/llvm-objcopy/MachO/Object.cpp
+++ b/llvm/tools/llvm-objcopy/MachO/Object.cpp
@@ -29,10 +29,24 @@ void SymbolTable::removeSymbols(
}
void Object::updateLoadCommandIndexes() {
+ static constexpr char TextSegmentName[] = "__TEXT";
// Update indices of special load commands
for (size_t Index = 0, Size = LoadCommands.size(); Index < Size; ++Index) {
LoadCommand &LC = LoadCommands[Index];
switch (LC.MachOLoadCommand.load_command_data.cmd) {
+ case MachO::LC_CODE_SIGNATURE:
+ CodeSignatureCommandIndex = Index;
+ break;
+ case MachO::LC_SEGMENT:
+ if (StringRef(LC.MachOLoadCommand.segment_command_data.segname) ==
+ TextSegmentName)
+ TextSegmentCommandIndex = Index;
+ break;
+ case MachO::LC_SEGMENT_64:
+ if (StringRef(LC.MachOLoadCommand.segment_command_64_data.segname) ==
+ TextSegmentName)
+ TextSegmentCommandIndex = Index;
+ break;
case MachO::LC_SYMTAB:
SymTabCommandIndex = Index;
break;
diff --git a/llvm/tools/llvm-objcopy/MachO/Object.h b/llvm/tools/llvm-objcopy/MachO/Object.h
index 207502e2241b8..55cd5b23700cb 100644
--- a/llvm/tools/llvm-objcopy/MachO/Object.h
+++ b/llvm/tools/llvm-objcopy/MachO/Object.h
@@ -315,7 +315,6 @@ struct Object {
LinkData DataInCode;
LinkData LinkerOptimizationHint;
LinkData FunctionStarts;
- LinkData CodeSignature;
Optional<uint32_t> SwiftVersion;
@@ -333,6 +332,9 @@ struct Object {
Optional<size_t> LinkerOptimizationHintCommandIndex;
/// The index LC_FUNCTION_STARTS load comamnd if present.
Optional<size_t> FunctionStartsCommandIndex;
+ /// The index of the LC_SEGMENT or LC_SEGMENT_64 load command
+ /// corresponding to the __TEXT segment.
+ Optional<size_t> TextSegmentCommandIndex;
BumpPtrAllocator Alloc;
StringSaver NewSectionsContents;
More information about the llvm-commits
mailing list