[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