1 //===- MachOUniversalWriter.cpp - MachO universal binary writer---*- C++-*-===// 2 // 3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4 // See https://llvm.org/LICENSE.txt for license information. 5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6 // 7 //===----------------------------------------------------------------------===// 8 // 9 // Defines the Slice class and writeUniversalBinary function for writing a MachO 10 // universal binary file. 11 // 12 //===----------------------------------------------------------------------===// 13 14 #include "llvm/Object/MachOUniversalWriter.h" 15 #include "llvm/Object/Archive.h" 16 #include "llvm/Object/Binary.h" 17 #include "llvm/Object/Error.h" 18 #include "llvm/Object/MachO.h" 19 #include "llvm/Object/MachOUniversal.h" 20 #include "llvm/Support/FileOutputBuffer.h" 21 22 using namespace llvm; 23 using namespace object; 24 25 // For compatibility with cctools lipo, a file's alignment is calculated as the 26 // minimum aligment of all segments. For object files, the file's alignment is 27 // the maximum alignment of its sections. 28 static uint32_t calculateFileAlignment(const MachOObjectFile &O) { 29 uint32_t P2CurrentAlignment; 30 uint32_t P2MinAlignment = MachOUniversalBinary::MaxSectionAlignment; 31 const bool Is64Bit = O.is64Bit(); 32 33 for (const auto &LC : O.load_commands()) { 34 if (LC.C.cmd != (Is64Bit ? MachO::LC_SEGMENT_64 : MachO::LC_SEGMENT)) 35 continue; 36 if (O.getHeader().filetype == MachO::MH_OBJECT) { 37 unsigned NumberOfSections = 38 (Is64Bit ? O.getSegment64LoadCommand(LC).nsects 39 : O.getSegmentLoadCommand(LC).nsects); 40 P2CurrentAlignment = NumberOfSections ? 2 : P2MinAlignment; 41 for (unsigned SI = 0; SI < NumberOfSections; ++SI) { 42 P2CurrentAlignment = std::max(P2CurrentAlignment, 43 (Is64Bit ? O.getSection64(LC, SI).align 44 : O.getSection(LC, SI).align)); 45 } 46 } else { 47 P2CurrentAlignment = 48 countTrailingZeros(Is64Bit ? O.getSegment64LoadCommand(LC).vmaddr 49 : O.getSegmentLoadCommand(LC).vmaddr); 50 } 51 P2MinAlignment = std::min(P2MinAlignment, P2CurrentAlignment); 52 } 53 // return a value >= 4 byte aligned, and less than MachO MaxSectionAlignment 54 return std::max( 55 static_cast<uint32_t>(2), 56 std::min(P2MinAlignment, static_cast<uint32_t>( 57 MachOUniversalBinary::MaxSectionAlignment))); 58 } 59 60 static uint32_t calculateAlignment(const MachOObjectFile &ObjectFile) { 61 switch (ObjectFile.getHeader().cputype) { 62 case MachO::CPU_TYPE_I386: 63 case MachO::CPU_TYPE_X86_64: 64 case MachO::CPU_TYPE_POWERPC: 65 case MachO::CPU_TYPE_POWERPC64: 66 return 12; // log2 value of page size(4k) for x86 and PPC 67 case MachO::CPU_TYPE_ARM: 68 case MachO::CPU_TYPE_ARM64: 69 case MachO::CPU_TYPE_ARM64_32: 70 return 14; // log2 value of page size(16k) for Darwin ARM 71 default: 72 return calculateFileAlignment(ObjectFile); 73 } 74 } 75 76 Slice::Slice(const MachOObjectFile &O, uint32_t Align) 77 : B(&O), CPUType(O.getHeader().cputype), 78 CPUSubType(O.getHeader().cpusubtype), 79 ArchName(std::string(O.getArchTriple().getArchName())), 80 P2Alignment(Align) {} 81 82 Slice::Slice(const MachOObjectFile &O) : Slice(O, calculateAlignment(O)) {} 83 84 Expected<Slice> Slice::create(const Archive *A) { 85 Error Err = Error::success(); 86 std::unique_ptr<MachOObjectFile> FO = nullptr; 87 for (const Archive::Child &Child : A->children(Err)) { 88 Expected<std::unique_ptr<Binary>> ChildOrErr = Child.getAsBinary(); 89 if (!ChildOrErr) 90 return createFileError(A->getFileName(), ChildOrErr.takeError()); 91 Binary *Bin = ChildOrErr.get().get(); 92 if (Bin->isMachOUniversalBinary()) 93 return createStringError(std::errc::invalid_argument, 94 ("archive member " + Bin->getFileName() + 95 " is a fat file (not allowed in an archive)") 96 .str() 97 .c_str()); 98 if (!Bin->isMachO()) 99 return createStringError( 100 std::errc::invalid_argument, 101 ("archive member " + Bin->getFileName() + 102 " is not a MachO file (not allowed in an archive)") 103 .str() 104 .c_str()); 105 MachOObjectFile *O = cast<MachOObjectFile>(Bin); 106 if (FO && std::tie(FO->getHeader().cputype, FO->getHeader().cpusubtype) != 107 std::tie(O->getHeader().cputype, O->getHeader().cpusubtype)) { 108 return createStringError( 109 std::errc::invalid_argument, 110 ("archive member " + O->getFileName() + " cputype (" + 111 Twine(O->getHeader().cputype) + ") and cpusubtype(" + 112 Twine(O->getHeader().cpusubtype) + 113 ") does not match previous archive members cputype (" + 114 Twine(FO->getHeader().cputype) + ") and cpusubtype(" + 115 Twine(FO->getHeader().cpusubtype) + ") (all members must match) " + 116 FO->getFileName()) 117 .str() 118 .c_str()); 119 } 120 if (!FO) { 121 ChildOrErr.get().release(); 122 FO.reset(O); 123 } 124 } 125 if (Err) 126 return createFileError(A->getFileName(), std::move(Err)); 127 if (!FO) 128 return createStringError( 129 std::errc::invalid_argument, 130 ("empty archive with no architecture specification: " + 131 A->getFileName() + " (can't determine architecture for it)") 132 .str() 133 .c_str()); 134 135 Slice ArchiveSlice = Slice(*(FO.get()), FO->is64Bit() ? 3 : 2); 136 ArchiveSlice.B = A; 137 return ArchiveSlice; 138 } 139 140 static Expected<SmallVector<MachO::fat_arch, 2>> 141 buildFatArchList(ArrayRef<Slice> Slices) { 142 SmallVector<MachO::fat_arch, 2> FatArchList; 143 uint64_t Offset = 144 sizeof(MachO::fat_header) + Slices.size() * sizeof(MachO::fat_arch); 145 146 for (const auto &S : Slices) { 147 Offset = alignTo(Offset, 1ull << S.getP2Alignment()); 148 if (Offset > UINT32_MAX) 149 return createStringError( 150 std::errc::invalid_argument, 151 ("fat file too large to be created because the offset " 152 "field in struct fat_arch is only 32-bits and the offset " + 153 Twine(Offset) + " for " + S.getBinary()->getFileName() + 154 " for architecture " + S.getArchString() + "exceeds that.") 155 .str() 156 .c_str()); 157 158 MachO::fat_arch FatArch; 159 FatArch.cputype = S.getCPUType(); 160 FatArch.cpusubtype = S.getCPUSubType(); 161 FatArch.offset = Offset; 162 FatArch.size = S.getBinary()->getMemoryBufferRef().getBufferSize(); 163 FatArch.align = S.getP2Alignment(); 164 Offset += FatArch.size; 165 FatArchList.push_back(FatArch); 166 } 167 return FatArchList; 168 } 169 170 Error object::writeUniversalBinary(ArrayRef<Slice> Slices, 171 StringRef OutputFileName) { 172 MachO::fat_header FatHeader; 173 FatHeader.magic = MachO::FAT_MAGIC; 174 FatHeader.nfat_arch = Slices.size(); 175 176 Expected<SmallVector<MachO::fat_arch, 2>> FatArchListOrErr = 177 buildFatArchList(Slices); 178 if (!FatArchListOrErr) 179 return FatArchListOrErr.takeError(); 180 SmallVector<MachO::fat_arch, 2> FatArchList = *FatArchListOrErr; 181 182 const bool IsExecutable = any_of(Slices, [](Slice S) { 183 return sys::fs::can_execute(S.getBinary()->getFileName()); 184 }); 185 const uint64_t OutputFileSize = 186 static_cast<uint64_t>(FatArchList.back().offset) + 187 FatArchList.back().size; 188 Expected<std::unique_ptr<FileOutputBuffer>> OutFileOrError = 189 FileOutputBuffer::create(OutputFileName, OutputFileSize, 190 IsExecutable ? FileOutputBuffer::F_executable 191 : 0); 192 if (!OutFileOrError) 193 return createFileError(OutputFileName, OutFileOrError.takeError()); 194 std::unique_ptr<FileOutputBuffer> OutFile = std::move(OutFileOrError.get()); 195 std::memset(OutFile->getBufferStart(), 0, OutputFileSize); 196 197 if (sys::IsLittleEndianHost) 198 MachO::swapStruct(FatHeader); 199 std::memcpy(OutFile->getBufferStart(), &FatHeader, sizeof(MachO::fat_header)); 200 201 for (size_t Index = 0, Size = Slices.size(); Index < Size; ++Index) { 202 MemoryBufferRef BufferRef = Slices[Index].getBinary()->getMemoryBufferRef(); 203 std::copy(BufferRef.getBufferStart(), BufferRef.getBufferEnd(), 204 OutFile->getBufferStart() + FatArchList[Index].offset); 205 } 206 207 // FatArchs written after Slices in order to reduce the number of swaps for 208 // the LittleEndian case 209 if (sys::IsLittleEndianHost) 210 for (MachO::fat_arch &FA : FatArchList) 211 MachO::swapStruct(FA); 212 std::memcpy(OutFile->getBufferStart() + sizeof(MachO::fat_header), 213 FatArchList.begin(), 214 sizeof(MachO::fat_arch) * FatArchList.size()); 215 216 if (Error E = OutFile->commit()) 217 return createFileError(OutputFileName, std::move(E)); 218 219 return Error::success(); 220 } 221