brintos

brintos / llvm-project-archived public Read only

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