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/SmallVectorMemoryBuffer.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 Archive &A, uint32_t CPUType, uint32_t CPUSubType, 79 std::string ArchName, uint32_t Align) 80 : B(&A), CPUType(CPUType), CPUSubType(CPUSubType), 81 ArchName(std::move(ArchName)), P2Alignment(Align) {} 82 83 Slice::Slice(const MachOObjectFile &O, uint32_t Align) 84 : B(&O), CPUType(O.getHeader().cputype), 85 CPUSubType(O.getHeader().cpusubtype), 86 ArchName(std::string(O.getArchTriple().getArchName())), 87 P2Alignment(Align) {} 88 89 Slice::Slice(const IRObjectFile &IRO, uint32_t CPUType, uint32_t CPUSubType, 90 std::string ArchName, uint32_t Align) 91 : B(&IRO), CPUType(CPUType), CPUSubType(CPUSubType), 92 ArchName(std::move(ArchName)), P2Alignment(Align) {} 93 94 Slice::Slice(const MachOObjectFile &O) : Slice(O, calculateAlignment(O)) {} 95 96 using MachoCPUTy = std::pair<unsigned, unsigned>; 97 98 static Expected<MachoCPUTy> getMachoCPUFromTriple(Triple TT) { 99 auto CPU = std::make_pair(MachO::getCPUType(TT), MachO::getCPUSubType(TT)); 100 if (!CPU.first) { 101 return CPU.first.takeError(); 102 } 103 if (!CPU.second) { 104 return CPU.second.takeError(); 105 } 106 return std::make_pair(*CPU.first, *CPU.second); 107 } 108 109 static Expected<MachoCPUTy> getMachoCPUFromTriple(StringRef TT) { 110 return getMachoCPUFromTriple(Triple{TT}); 111 } 112 113 Expected<Slice> Slice::create(const Archive &A, LLVMContext *LLVMCtx) { 114 Error Err = Error::success(); 115 std::unique_ptr<MachOObjectFile> MFO = nullptr; 116 std::unique_ptr<IRObjectFile> IRFO = nullptr; 117 for (const Archive::Child &Child : A.children(Err)) { 118 Expected<std::unique_ptr<Binary>> ChildOrErr = Child.getAsBinary(LLVMCtx); 119 if (!ChildOrErr) 120 return createFileError(A.getFileName(), ChildOrErr.takeError()); 121 Binary *Bin = ChildOrErr.get().get(); 122 if (Bin->isMachOUniversalBinary()) 123 return createStringError(std::errc::invalid_argument, 124 ("archive member " + Bin->getFileName() + 125 " is a fat file (not allowed in an archive)") 126 .str() 127 .c_str()); 128 if (Bin->isMachO()) { 129 MachOObjectFile *O = cast<MachOObjectFile>(Bin); 130 if (IRFO) { 131 return createStringError( 132 std::errc::invalid_argument, 133 "archive member %s is a MachO, while previous archive member " 134 "%s was an IR LLVM object", 135 O->getFileName().str().c_str(), IRFO->getFileName().str().c_str()); 136 } 137 if (MFO && 138 std::tie(MFO->getHeader().cputype, MFO->getHeader().cpusubtype) != 139 std::tie(O->getHeader().cputype, O->getHeader().cpusubtype)) { 140 return createStringError( 141 std::errc::invalid_argument, 142 ("archive member " + O->getFileName() + " cputype (" + 143 Twine(O->getHeader().cputype) + ") and cpusubtype(" + 144 Twine(O->getHeader().cpusubtype) + 145 ") does not match previous archive members cputype (" + 146 Twine(MFO->getHeader().cputype) + ") and cpusubtype(" + 147 Twine(MFO->getHeader().cpusubtype) + 148 ") (all members must match) " + MFO->getFileName()) 149 .str() 150 .c_str()); 151 } 152 if (!MFO) { 153 ChildOrErr.get().release(); 154 MFO.reset(O); 155 } 156 } else if (Bin->isIR()) { 157 IRObjectFile *O = cast<IRObjectFile>(Bin); 158 if (MFO) { 159 return createStringError(std::errc::invalid_argument, 160 "archive member '%s' is an LLVM IR object, " 161 "while previous archive member " 162 "'%s' was a MachO", 163 O->getFileName().str().c_str(), 164 MFO->getFileName().str().c_str()); 165 } 166 if (IRFO) { 167 Expected<MachoCPUTy> CPUO = getMachoCPUFromTriple(O->getTargetTriple()); 168 Expected<MachoCPUTy> CPUFO = 169 getMachoCPUFromTriple(IRFO->getTargetTriple()); 170 if (!CPUO) 171 return CPUO.takeError(); 172 if (!CPUFO) 173 return CPUFO.takeError(); 174 if (*CPUO != *CPUFO) { 175 return createStringError( 176 std::errc::invalid_argument, 177 ("archive member " + O->getFileName() + " cputype (" + 178 Twine(CPUO->first) + ") and cpusubtype(" + Twine(CPUO->second) + 179 ") does not match previous archive members cputype (" + 180 Twine(CPUFO->first) + ") and cpusubtype(" + 181 Twine(CPUFO->second) + ") (all members must match) " + 182 IRFO->getFileName()) 183 .str() 184 .c_str()); 185 } 186 } else { 187 ChildOrErr.get().release(); 188 IRFO.reset(O); 189 } 190 } else 191 return createStringError(std::errc::invalid_argument, 192 ("archive member " + Bin->getFileName() + 193 " is neither a MachO file or an LLVM IR file " 194 "(not allowed in an archive)") 195 .str() 196 .c_str()); 197 } 198 if (Err) 199 return createFileError(A.getFileName(), std::move(Err)); 200 if (!MFO && !IRFO) 201 return createStringError( 202 std::errc::invalid_argument, 203 ("empty archive with no architecture specification: " + 204 A.getFileName() + " (can't determine architecture for it)") 205 .str() 206 .c_str()); 207 208 if (MFO) { 209 Slice ArchiveSlice(*(MFO.get()), MFO->is64Bit() ? 3 : 2); 210 ArchiveSlice.B = &A; 211 return ArchiveSlice; 212 } 213 214 // For IR objects 215 Expected<Slice> ArchiveSliceOrErr = Slice::create(*IRFO, 0); 216 if (!ArchiveSliceOrErr) 217 return createFileError(A.getFileName(), ArchiveSliceOrErr.takeError()); 218 auto &ArchiveSlice = ArchiveSliceOrErr.get(); 219 ArchiveSlice.B = &A; 220 return std::move(ArchiveSlice); 221 } 222 223 Expected<Slice> Slice::create(const IRObjectFile &IRO, uint32_t Align) { 224 Expected<MachoCPUTy> CPUOrErr = getMachoCPUFromTriple(IRO.getTargetTriple()); 225 if (!CPUOrErr) 226 return CPUOrErr.takeError(); 227 unsigned CPUType, CPUSubType; 228 std::tie(CPUType, CPUSubType) = CPUOrErr.get(); 229 // We don't directly use the architecture name of the target triple T, as, 230 // for instance, thumb is treated as ARM by the MachOUniversal object. 231 std::string ArchName( 232 MachOObjectFile::getArchTriple(CPUType, CPUSubType).getArchName()); 233 return Slice{IRO, CPUType, CPUSubType, std::move(ArchName), Align}; 234 } 235 236 static Expected<SmallVector<MachO::fat_arch, 2>> 237 buildFatArchList(ArrayRef<Slice> Slices) { 238 SmallVector<MachO::fat_arch, 2> FatArchList; 239 uint64_t Offset = 240 sizeof(MachO::fat_header) + Slices.size() * sizeof(MachO::fat_arch); 241 242 for (const auto &S : Slices) { 243 Offset = alignTo(Offset, 1ull << S.getP2Alignment()); 244 if (Offset > UINT32_MAX) 245 return createStringError( 246 std::errc::invalid_argument, 247 ("fat file too large to be created because the offset " 248 "field in struct fat_arch is only 32-bits and the offset " + 249 Twine(Offset) + " for " + S.getBinary()->getFileName() + 250 " for architecture " + S.getArchString() + "exceeds that.") 251 .str() 252 .c_str()); 253 254 MachO::fat_arch FatArch; 255 FatArch.cputype = S.getCPUType(); 256 FatArch.cpusubtype = S.getCPUSubType(); 257 FatArch.offset = Offset; 258 FatArch.size = S.getBinary()->getMemoryBufferRef().getBufferSize(); 259 FatArch.align = S.getP2Alignment(); 260 Offset += FatArch.size; 261 FatArchList.push_back(FatArch); 262 } 263 return FatArchList; 264 } 265 266 Error object::writeUniversalBinaryToStream(ArrayRef<Slice> Slices, 267 raw_ostream &Out) { 268 MachO::fat_header FatHeader; 269 FatHeader.magic = MachO::FAT_MAGIC; 270 FatHeader.nfat_arch = Slices.size(); 271 272 Expected<SmallVector<MachO::fat_arch, 2>> FatArchListOrErr = 273 buildFatArchList(Slices); 274 if (!FatArchListOrErr) 275 return FatArchListOrErr.takeError(); 276 SmallVector<MachO::fat_arch, 2> FatArchList = *FatArchListOrErr; 277 278 if (sys::IsLittleEndianHost) 279 MachO::swapStruct(FatHeader); 280 Out.write(reinterpret_cast<const char *>(&FatHeader), 281 sizeof(MachO::fat_header)); 282 283 if (sys::IsLittleEndianHost) 284 for (MachO::fat_arch &FA : FatArchList) 285 MachO::swapStruct(FA); 286 Out.write(reinterpret_cast<const char *>(FatArchList.data()), 287 sizeof(MachO::fat_arch) * FatArchList.size()); 288 289 if (sys::IsLittleEndianHost) 290 for (MachO::fat_arch &FA : FatArchList) 291 MachO::swapStruct(FA); 292 293 size_t Offset = 294 sizeof(MachO::fat_header) + sizeof(MachO::fat_arch) * FatArchList.size(); 295 for (size_t Index = 0, Size = Slices.size(); Index < Size; ++Index) { 296 MemoryBufferRef BufferRef = Slices[Index].getBinary()->getMemoryBufferRef(); 297 assert((Offset <= FatArchList[Index].offset) && "Incorrect slice offset"); 298 Out.write_zeros(FatArchList[Index].offset - Offset); 299 Out.write(BufferRef.getBufferStart(), BufferRef.getBufferSize()); 300 Offset = FatArchList[Index].offset + BufferRef.getBufferSize(); 301 } 302 303 Out.flush(); 304 return Error::success(); 305 } 306 307 Error object::writeUniversalBinary(ArrayRef<Slice> Slices, 308 StringRef OutputFileName) { 309 const bool IsExecutable = any_of(Slices, [](Slice S) { 310 return sys::fs::can_execute(S.getBinary()->getFileName()); 311 }); 312 unsigned Mode = sys::fs::all_read | sys::fs::all_write; 313 if (IsExecutable) 314 Mode |= sys::fs::all_exe; 315 Expected<sys::fs::TempFile> Temp = sys::fs::TempFile::create( 316 OutputFileName + ".temp-universal-%%%%%%", Mode); 317 if (!Temp) 318 return Temp.takeError(); 319 raw_fd_ostream Out(Temp->FD, false); 320 if (Error E = writeUniversalBinaryToStream(Slices, Out)) { 321 if (Error DiscardError = Temp->discard()) 322 return joinErrors(std::move(E), std::move(DiscardError)); 323 return E; 324 } 325 return Temp->keep(OutputFileName); 326 } 327