From c4c7b0d946c2df969b4bc3b06da27ea246d5450e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Rodri=CC=81guez=20Troitin=CC=83o?= Date: Thu, 12 Sep 2024 17:33:41 -0700 Subject: [PATCH] [ObjCopy] Respect requirements of LC_ENCRYPTION_INFO commands LLD (and other Mach-O linkers) when preparing an encryptable binary make space to leave all the load commands in an non-encrypted page (see [1]) When using objcopy of a small encryptable binary, the code was not respecting this fact, and the encryptable segments were not kept beyond the first page. This was obvious for small or empty binaries. The changes introduced here keep track if a `LC_ENCRYPTION_INFO` or `LC_ENCRYPTION_INFO_64` has been seen, and in such case, it adds a full page of offset in order to leave the load commands in its own page (similar to what LLD is doing). [1]: https://github.com/llvm/llvm-project/blob/d8e792931226b15d9d2424ecd24ccfe13adc2367/lld/MachO/SyntheticSections.cpp#L90-L93 --- llvm/lib/ObjCopy/MachO/MachOLayoutBuilder.cpp | 5 + llvm/lib/ObjCopy/MachO/MachOObject.cpp | 4 + llvm/lib/ObjCopy/MachO/MachOObject.h | 3 + llvm/lib/ObjCopy/MachO/MachOReader.cpp | 4 + .../MachO/strip-with-encryption-info.test | 217 ++++++++++++++++++ 5 files changed, 233 insertions(+) create mode 100644 llvm/test/tools/llvm-objcopy/MachO/strip-with-encryption-info.test diff --git a/llvm/lib/ObjCopy/MachO/MachOLayoutBuilder.cpp b/llvm/lib/ObjCopy/MachO/MachOLayoutBuilder.cpp index 93bc6631e64c8..d4eb6a9b9fc0b 100644 --- a/llvm/lib/ObjCopy/MachO/MachOLayoutBuilder.cpp +++ b/llvm/lib/ObjCopy/MachO/MachOLayoutBuilder.cpp @@ -116,6 +116,11 @@ uint64_t MachOLayoutBuilder::layoutSegments() { const bool IsObjectFile = O.Header.FileType == MachO::HeaderFileType::MH_OBJECT; uint64_t Offset = IsObjectFile ? (HeaderSize + O.Header.SizeOfCmds) : 0; + if (O.EncryptionInfoCommandIndex) { + // If we are emitting an encryptable binary, our load commands must have a + // separate (non-encrypted) page to themselves. + Offset = alignToPowerOf2(HeaderSize + O.Header.SizeOfCmds, PageSize); + } for (LoadCommand &LC : O.LoadCommands) { auto &MLC = LC.MachOLoadCommand; StringRef Segname; diff --git a/llvm/lib/ObjCopy/MachO/MachOObject.cpp b/llvm/lib/ObjCopy/MachO/MachOObject.cpp index d593d6788e112..fe4b1cf4b12ae 100644 --- a/llvm/lib/ObjCopy/MachO/MachOObject.cpp +++ b/llvm/lib/ObjCopy/MachO/MachOObject.cpp @@ -85,6 +85,10 @@ void Object::updateLoadCommandIndexes() { case MachO::LC_DYLD_EXPORTS_TRIE: ExportsTrieCommandIndex = Index; break; + case MachO::LC_ENCRYPTION_INFO: + case MachO::LC_ENCRYPTION_INFO_64: + EncryptionInfoCommandIndex = Index; + break; } } } diff --git a/llvm/lib/ObjCopy/MachO/MachOObject.h b/llvm/lib/ObjCopy/MachO/MachOObject.h index b3303fd291c82..b616677b83dcb 100644 --- a/llvm/lib/ObjCopy/MachO/MachOObject.h +++ b/llvm/lib/ObjCopy/MachO/MachOObject.h @@ -340,6 +340,9 @@ struct Object { /// The index of the LC_SEGMENT or LC_SEGMENT_64 load command /// corresponding to the __TEXT segment. std::optional TextSegmentCommandIndex; + /// The index of the LC_ENCRYPTION_INFO or LC_ENCRYPTION_INFO_64 load command + /// if present. + std::optional EncryptionInfoCommandIndex; BumpPtrAllocator Alloc; StringSaver NewSectionsContents; diff --git a/llvm/lib/ObjCopy/MachO/MachOReader.cpp b/llvm/lib/ObjCopy/MachO/MachOReader.cpp index 2b344f36d8e78..ef0e0262f9395 100644 --- a/llvm/lib/ObjCopy/MachO/MachOReader.cpp +++ b/llvm/lib/ObjCopy/MachO/MachOReader.cpp @@ -184,6 +184,10 @@ Error MachOReader::readLoadCommands(Object &O) const { case MachO::LC_DYLD_CHAINED_FIXUPS: O.ChainedFixupsCommandIndex = O.LoadCommands.size(); break; + case MachO::LC_ENCRYPTION_INFO: + case MachO::LC_ENCRYPTION_INFO_64: + O.EncryptionInfoCommandIndex = O.LoadCommands.size(); + break; } #define HANDLE_LOAD_COMMAND(LCName, LCValue, LCStruct) \ case MachO::LCName: \ diff --git a/llvm/test/tools/llvm-objcopy/MachO/strip-with-encryption-info.test b/llvm/test/tools/llvm-objcopy/MachO/strip-with-encryption-info.test new file mode 100644 index 0000000000000..19b06b1ec02c8 --- /dev/null +++ b/llvm/test/tools/llvm-objcopy/MachO/strip-with-encryption-info.test @@ -0,0 +1,217 @@ +# RUN: rm -rf %t && mkdir %t +# RUN: yaml2obj %s -o %t/original +# RUN: llvm-strip --strip-all %t/original -o %t/stripped +# RUN: llvm-readobj --macho-segment %t/stripped | FileCheck %s + +# CHECK-LABEL: Name: __PAGEZERO +# CHECK: fileoff: 16384 + +# CHECK-LABEL: Name: __TEXT +# CHECK: fileoff: 16384 + +# The YAML below is the following code +# int main(int argc, char **argv) { return 0; } +# Compiled on macOS against the macOS SDK and passing `-Wl,-encryptable` +# Contents are removed, since they are not important for the test. We need a +# small text segment (smaller than a page). +--- !mach-o +FileHeader: + magic: 0xFEEDFACF + cputype: 0x100000C + cpusubtype: 0x0 + filetype: 0x2 + ncmds: 15 + sizeofcmds: 696 + 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: 32768 + fileoff: 0 + filesize: 32768 + maxprot: 5 + initprot: 5 + nsects: 2 + flags: 0 + Sections: + - sectname: __text + segname: __TEXT + addr: 0x100004000 + size: 32 + offset: 0x4000 + align: 2 + reloff: 0x0 + nreloc: 0 + flags: 0x80000400 + reserved1: 0x0 + reserved2: 0x0 + reserved3: 0x0 + - sectname: __unwind_info + segname: __TEXT + addr: 0x100004020 + size: 4152 + offset: 0x4020 + align: 2 + reloff: 0x0 + nreloc: 0 + flags: 0x0 + reserved1: 0x0 + reserved2: 0x0 + reserved3: 0x0 + - cmd: LC_SEGMENT_64 + cmdsize: 72 + segname: __LINKEDIT + vmaddr: 4295000064 + vmsize: 592 + fileoff: 32768 + filesize: 592 + maxprot: 1 + initprot: 1 + nsects: 0 + flags: 0 + - cmd: LC_DYLD_CHAINED_FIXUPS + cmdsize: 16 + dataoff: 32768 + datasize: 48 + - cmd: LC_DYLD_EXPORTS_TRIE + cmdsize: 16 + dataoff: 32816 + datasize: 48 + - cmd: LC_SYMTAB + cmdsize: 24 + symoff: 32872 + nsyms: 2 + stroff: 32904 + strsize: 32 + - cmd: LC_DYSYMTAB + cmdsize: 80 + ilocalsym: 0 + nlocalsym: 0 + iextdefsym: 0 + nextdefsym: 2 + iundefsym: 2 + nundefsym: 0 + 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_ENCRYPTION_INFO_64 + cmdsize: 24 + cryptoff: 16384 + cryptsize: 16384 + cryptid: 0 + pad: 0 + - cmd: LC_LOAD_DYLINKER + cmdsize: 32 + name: 12 + Content: '/usr/lib/dyld' + ZeroPadBytes: 7 + - cmd: LC_UUID + cmdsize: 24 + uuid: 4C4C4447-5555-3144-A18A-01E9EB7E7D92 + - cmd: LC_BUILD_VERSION + cmdsize: 32 + platform: 1 + minos: 983040 + sdk: 983552 + ntools: 1 + Tools: + - tool: 4 + version: 1310720 + - cmd: LC_MAIN + cmdsize: 24 + entryoff: 16384 + stacksize: 0 + - cmd: LC_FUNCTION_STARTS + cmdsize: 16 + dataoff: 32864 + datasize: 8 + - cmd: LC_DATA_IN_CODE + cmdsize: 16 + dataoff: 32872 + datasize: 0 + - cmd: LC_CODE_SIGNATURE + cmdsize: 16 + dataoff: 32944 + datasize: 416 +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: 4 + NodeOffset: 33 + Name: main + Flags: 0x0 + Address: 0x4000 + Other: 0x0 + ImportName: '' + - TerminalSize: 2 + NodeOffset: 39 + Name: _mh_execute_header + Flags: 0x0 + Address: 0x0 + Other: 0x0 + ImportName: '' + NameList: + - n_strx: 2 + n_type: 0xF + n_sect: 1 + n_desc: 0 + n_value: 4294983680 + - n_strx: 8 + n_type: 0xF + n_sect: 1 + n_desc: 16 + n_value: 4294967296 + StringTable: + - ' ' + - _main + - __mh_execute_header + - '' + - '' + - '' + - '' + FunctionStarts: [ 0x4000 ] + ChainedFixups: [ 0x0, 0x0, 0x0, 0x0, 0x20, 0x0, 0x0, 0x0, 0x30, 0x0, + 0x0, 0x0, 0x30, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x3, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0 ] +... +