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/ADT/Triple.h" 16 #include "llvm/Object/Archive.h" 17 #include "llvm/Object/Binary.h" 18 #include "llvm/Object/Error.h" 19 #include "llvm/Object/IRObjectFile.h" 20 #include "llvm/Object/MachO.h" 21 #include "llvm/Object/MachOUniversal.h" 22 #include "llvm/Support/FileOutputBuffer.h" 23 24 using namespace llvm; 25 using namespace object; 26 27 // For compatibility with cctools lipo, a file's alignment is calculated as the 28 // minimum aligment of all segments. For object files, the file's alignment is 29 // the maximum alignment of its sections. 30 static uint32_t calculateFileAlignment(const MachOObjectFile &O) { 31 uint32_t P2CurrentAlignment; 32 uint32_t P2MinAlignment = MachOUniversalBinary::MaxSectionAlignment; 33 const bool Is64Bit = O.is64Bit(); 34 35 for (const auto &LC : O.load_commands()) { 36 if (LC.C.cmd != (Is64Bit ? MachO::LC_SEGMENT_64 : MachO::LC_SEGMENT)) 37 continue; 38 if (O.getHeader().filetype == MachO::MH_OBJECT) { 39 unsigned NumberOfSections = 40 (Is64Bit ? O.getSegment64LoadCommand(LC).nsects 41 : O.getSegmentLoadCommand(LC).nsects); 42 P2CurrentAlignment = NumberOfSections ? 2 : P2MinAlignment; 43 for (unsigned SI = 0; SI < NumberOfSections; ++SI) { 44 P2CurrentAlignment = std::max(P2CurrentAlignment, 45 (Is64Bit ? O.getSection64(LC, SI).align 46 : O.getSection(LC, SI).align)); 47 } 48 } else { 49 P2CurrentAlignment = 50 countTrailingZeros(Is64Bit ? O.getSegment64LoadCommand(LC).vmaddr 51 : O.getSegmentLoadCommand(LC).vmaddr); 52 } 53 P2MinAlignment = std::min(P2MinAlignment, P2CurrentAlignment); 54 } 55 // return a value >= 4 byte aligned, and less than MachO MaxSectionAlignment 56 return std::max( 57 static_cast<uint32_t>(2), 58 std::min(P2MinAlignment, static_cast<uint32_t>( 59 MachOUniversalBinary::MaxSectionAlignment))); 60 } 61 62 static uint32_t calculateAlignment(const MachOObjectFile &ObjectFile) { 63 switch (ObjectFile.getHeader().cputype) { 64 case MachO::CPU_TYPE_I386: 65 case MachO::CPU_TYPE_X86_64: 66 case MachO::CPU_TYPE_POWERPC: 67 case MachO::CPU_TYPE_POWERPC64: 68 return 12; // log2 value of page size(4k) for x86 and PPC 69 case MachO::CPU_TYPE_ARM: 70 case MachO::CPU_TYPE_ARM64: 71 case MachO::CPU_TYPE_ARM64_32: 72 return 14; // log2 value of page size(16k) for Darwin ARM 73 default: 74 return calculateFileAlignment(ObjectFile); 75 } 76 } 77 78 Slice::Slice(const MachOObjectFile &O, uint32_t Align) 79 : B(&O), CPUType(O.getHeader().cputype), 80 CPUSubType(O.getHeader().cpusubtype), 81 ArchName(std::string(O.getArchTriple().getArchName())), 82 P2Alignment(Align) {} 83 84 Slice::Slice(const IRObjectFile *IRO, uint32_t CPUType, uint32_t CPUSubType, 85 std::string ArchName, uint32_t Align) 86 : B(IRO), CPUType(CPUType), CPUSubType(CPUSubType), 87 ArchName(std::move(ArchName)), P2Alignment(Align) {} 88 89 Slice::Slice(const MachOObjectFile &O) : Slice(O, calculateAlignment(O)) {} 90 91 using MachoCPUTy = std::pair<unsigned, unsigned>; 92 93 static Expected<MachoCPUTy> getMachoCPUFromTriple(Triple TT) { 94 auto CPU = std::make_pair(MachO::getCPUType(TT), MachO::getCPUSubType(TT)); 95 if (!CPU.first) { 96 return CPU.first.takeError(); 97 } 98 if (!CPU.second) { 99 return CPU.second.takeError(); 100 } 101 return std::make_pair(*CPU.first, *CPU.second); 102 } 103 104 static Expected<MachoCPUTy> getMachoCPUFromTriple(StringRef TT) { 105 return getMachoCPUFromTriple(Triple{TT}); 106 } 107 108 Expected<Slice> Slice::create(const Archive *A, LLVMContext *LLVMCtx) { 109 Error Err = Error::success(); 110 std::unique_ptr<MachOObjectFile> MFO = nullptr; 111 std::unique_ptr<IRObjectFile> IRFO = nullptr; 112 for (const Archive::Child &Child : A->children(Err)) { 113 Expected<std::unique_ptr<Binary>> ChildOrErr = Child.getAsBinary(LLVMCtx); 114 if (!ChildOrErr) 115 return createFileError(A->getFileName(), ChildOrErr.takeError()); 116 Binary *Bin = ChildOrErr.get().get(); 117 if (Bin->isMachOUniversalBinary()) 118 return createStringError(std::errc::invalid_argument, 119 ("archive member " + Bin->getFileName() + 120 " is a fat file (not allowed in an archive)") 121 .str() 122 .c_str()); 123 if (Bin->isMachO()) { 124 MachOObjectFile *O = cast<MachOObjectFile>(Bin); 125 if (IRFO) { 126 return createStringError( 127 std::errc::invalid_argument, 128 "archive member %s is a MachO, while previous archive member " 129 "%s was an IR LLVM object", 130 O->getFileName().str().c_str(), IRFO->getFileName().str().c_str()); 131 } 132 if (MFO && 133 std::tie(MFO->getHeader().cputype, MFO->getHeader().cpusubtype) != 134 std::tie(O->getHeader().cputype, O->getHeader().cpusubtype)) { 135 return createStringError( 136 std::errc::invalid_argument, 137 ("archive member " + O->getFileName() + " cputype (" + 138 Twine(O->getHeader().cputype) + ") and cpusubtype(" + 139 Twine(O->getHeader().cpusubtype) + 140 ") does not match previous archive members cputype (" + 141 Twine(MFO->getHeader().cputype) + ") and cpusubtype(" + 142 Twine(MFO->getHeader().cpusubtype) + 143 ") (all members must match) " + MFO->getFileName()) 144 .str() 145 .c_str()); 146 } 147 if (!MFO) { 148 ChildOrErr.get().release(); 149 MFO.reset(O); 150 } 151 } else if (Bin->isIR()) { 152 IRObjectFile *O = cast<IRObjectFile>(Bin); 153 if (MFO) { 154 return createStringError(std::errc::invalid_argument, 155 "archive member '%s' is an LLVM IR object, " 156 "while previous archive member " 157 "'%s' was a MachO", 158 O->getFileName().str().c_str(), 159 MFO->getFileName().str().c_str()); 160 } 161 if (IRFO) { 162 Expected<MachoCPUTy> CPUO = getMachoCPUFromTriple(O->getTargetTriple()); 163 Expected<MachoCPUTy> CPUFO = 164 getMachoCPUFromTriple(IRFO->getTargetTriple()); 165 if (!CPUO) 166 return CPUO.takeError(); 167 if (!CPUFO) 168 return CPUFO.takeError(); 169 if (*CPUO != *CPUFO) { 170 return createStringError( 171 std::errc::invalid_argument, 172 ("archive member " + O->getFileName() + " cputype (" + 173 Twine(CPUO->first) + ") and cpusubtype(" + Twine(CPUO->second) + 174 ") does not match previous archive members cputype (" + 175 Twine(CPUFO->first) + ") and cpusubtype(" + 176 Twine(CPUFO->second) + ") (all members must match) " + 177 IRFO->getFileName()) 178 .str() 179 .c_str()); 180 } 181 } else { 182 ChildOrErr.get().release(); 183 IRFO.reset(O); 184 } 185 } else 186 return createStringError(std::errc::invalid_argument, 187 ("archive member " + Bin->getFileName() + 188 " is neither a MachO file or an LLVM IR file " 189 "(not allowed in an archive)") 190 .str() 191 .c_str()); 192 } 193 if (Err) 194 return createFileError(A->getFileName(), std::move(Err)); 195 if (!MFO && !IRFO) 196 return createStringError( 197 std::errc::invalid_argument, 198 ("empty archive with no architecture specification: " + 199 A->getFileName() + " (can't determine architecture for it)") 200 .str() 201 .c_str()); 202 203 if (MFO) { 204 Slice ArchiveSlice(*(MFO.get()), MFO->is64Bit() ? 3 : 2); 205 ArchiveSlice.B = A; 206 return ArchiveSlice; 207 } 208 209 // For IR objects 210 Expected<Slice> ArchiveSliceOrErr = Slice::create(IRFO.get(), 0); 211 if (!ArchiveSliceOrErr) 212 return createFileError(A->getFileName(), ArchiveSliceOrErr.takeError()); 213 auto &ArchiveSlice = ArchiveSliceOrErr.get(); 214 ArchiveSlice.B = A; 215 return Slice{std::move(ArchiveSlice)}; 216 } 217 218 Expected<Slice> Slice::create(const IRObjectFile *IRO, uint32_t Align) { 219 Expected<MachoCPUTy> CPUOrErr = getMachoCPUFromTriple(IRO->getTargetTriple()); 220 if (!CPUOrErr) 221 return CPUOrErr.takeError(); 222 unsigned CPUType, CPUSubType; 223 std::tie(CPUType, CPUSubType) = CPUOrErr.get(); 224 // We don't directly use the architecture name of the target triple T, as, 225 // for instance, thumb is treated as ARM by the MachOUniversal object. 226 std::string ArchName( 227 MachOObjectFile::getArchTriple(CPUType, CPUSubType).getArchName()); 228 return Slice{IRO, CPUType, CPUSubType, std::move(ArchName), Align}; 229 } 230 231 static Expected<SmallVector<MachO::fat_arch, 2>> 232 buildFatArchList(ArrayRef<Slice> Slices) { 233 SmallVector<MachO::fat_arch, 2> FatArchList; 234 uint64_t Offset = 235 sizeof(MachO::fat_header) + Slices.size() * sizeof(MachO::fat_arch); 236 237 for (const auto &S : Slices) { 238 Offset = alignTo(Offset, 1ull << S.getP2Alignment()); 239 if (Offset > UINT32_MAX) 240 return createStringError( 241 std::errc::invalid_argument, 242 ("fat file too large to be created because the offset " 243 "field in struct fat_arch is only 32-bits and the offset " + 244 Twine(Offset) + " for " + S.getBinary()->getFileName() + 245 " for architecture " + S.getArchString() + "exceeds that.") 246 .str() 247 .c_str()); 248 249 MachO::fat_arch FatArch; 250 FatArch.cputype = S.getCPUType(); 251 FatArch.cpusubtype = S.getCPUSubType(); 252 FatArch.offset = Offset; 253 FatArch.size = S.getBinary()->getMemoryBufferRef().getBufferSize(); 254 FatArch.align = S.getP2Alignment(); 255 Offset += FatArch.size; 256 FatArchList.push_back(FatArch); 257 } 258 return FatArchList; 259 } 260 261 Error object::writeUniversalBinary(ArrayRef<Slice> Slices, 262 StringRef OutputFileName) { 263 MachO::fat_header FatHeader; 264 FatHeader.magic = MachO::FAT_MAGIC; 265 FatHeader.nfat_arch = Slices.size(); 266 267 Expected<SmallVector<MachO::fat_arch, 2>> FatArchListOrErr = 268 buildFatArchList(Slices); 269 if (!FatArchListOrErr) 270 return FatArchListOrErr.takeError(); 271 SmallVector<MachO::fat_arch, 2> FatArchList = *FatArchListOrErr; 272 273 const bool IsExecutable = any_of(Slices, [](Slice S) { 274 return sys::fs::can_execute(S.getBinary()->getFileName()); 275 }); 276 const uint64_t OutputFileSize = 277 static_cast<uint64_t>(FatArchList.back().offset) + 278 FatArchList.back().size; 279 Expected<std::unique_ptr<FileOutputBuffer>> OutFileOrError = 280 FileOutputBuffer::create(OutputFileName, OutputFileSize, 281 IsExecutable ? FileOutputBuffer::F_executable 282 : 0); 283 if (!OutFileOrError) 284 return createFileError(OutputFileName, OutFileOrError.takeError()); 285 std::unique_ptr<FileOutputBuffer> OutFile = std::move(OutFileOrError.get()); 286 std::memset(OutFile->getBufferStart(), 0, OutputFileSize); 287 288 if (sys::IsLittleEndianHost) 289 MachO::swapStruct(FatHeader); 290 std::memcpy(OutFile->getBufferStart(), &FatHeader, sizeof(MachO::fat_header)); 291 292 for (size_t Index = 0, Size = Slices.size(); Index < Size; ++Index) { 293 MemoryBufferRef BufferRef = Slices[Index].getBinary()->getMemoryBufferRef(); 294 std::copy(BufferRef.getBufferStart(), BufferRef.getBufferEnd(), 295 OutFile->getBufferStart() + FatArchList[Index].offset); 296 } 297 298 // FatArchs written after Slices in order to reduce the number of swaps for 299 // the LittleEndian case 300 if (sys::IsLittleEndianHost) 301 for (MachO::fat_arch &FA : FatArchList) 302 MachO::swapStruct(FA); 303 std::memcpy(OutFile->getBufferStart() + sizeof(MachO::fat_header), 304 FatArchList.begin(), 305 sizeof(MachO::fat_arch) * FatArchList.size()); 306 307 if (Error E = OutFile->commit()) 308 return createFileError(OutputFileName, std::move(E)); 309 310 return Error::success(); 311 } 312