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