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