1*bdd1243dSDimitry Andric //===- OffloadBundler.cpp - File Bundling and Unbundling ------------------===// 2*bdd1243dSDimitry Andric // 3*bdd1243dSDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4*bdd1243dSDimitry Andric // See https://llvm.org/LICENSE.txt for license information. 5*bdd1243dSDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6*bdd1243dSDimitry Andric // 7*bdd1243dSDimitry Andric //===----------------------------------------------------------------------===// 8*bdd1243dSDimitry Andric /// 9*bdd1243dSDimitry Andric /// \file 10*bdd1243dSDimitry Andric /// This file implements an offload bundling API that bundles different files 11*bdd1243dSDimitry Andric /// that relate with the same source code but different targets into a single 12*bdd1243dSDimitry Andric /// one. Also the implements the opposite functionality, i.e. unbundle files 13*bdd1243dSDimitry Andric /// previous created by this API. 14*bdd1243dSDimitry Andric /// 15*bdd1243dSDimitry Andric //===----------------------------------------------------------------------===// 16*bdd1243dSDimitry Andric 17*bdd1243dSDimitry Andric #include "clang/Driver/OffloadBundler.h" 18*bdd1243dSDimitry Andric #include "clang/Basic/Cuda.h" 19*bdd1243dSDimitry Andric #include "clang/Basic/TargetID.h" 20*bdd1243dSDimitry Andric #include "clang/Basic/Version.h" 21*bdd1243dSDimitry Andric #include "llvm/ADT/ArrayRef.h" 22*bdd1243dSDimitry Andric #include "llvm/ADT/SmallString.h" 23*bdd1243dSDimitry Andric #include "llvm/ADT/SmallVector.h" 24*bdd1243dSDimitry Andric #include "llvm/ADT/StringMap.h" 25*bdd1243dSDimitry Andric #include "llvm/ADT/StringRef.h" 26*bdd1243dSDimitry Andric #include "llvm/ADT/Triple.h" 27*bdd1243dSDimitry Andric #include "llvm/Object/Archive.h" 28*bdd1243dSDimitry Andric #include "llvm/Object/ArchiveWriter.h" 29*bdd1243dSDimitry Andric #include "llvm/Object/Binary.h" 30*bdd1243dSDimitry Andric #include "llvm/Object/ObjectFile.h" 31*bdd1243dSDimitry Andric #include "llvm/Support/Casting.h" 32*bdd1243dSDimitry Andric #include "llvm/Support/Debug.h" 33*bdd1243dSDimitry Andric #include "llvm/Support/EndianStream.h" 34*bdd1243dSDimitry Andric #include "llvm/Support/Errc.h" 35*bdd1243dSDimitry Andric #include "llvm/Support/Error.h" 36*bdd1243dSDimitry Andric #include "llvm/Support/ErrorOr.h" 37*bdd1243dSDimitry Andric #include "llvm/Support/FileSystem.h" 38*bdd1243dSDimitry Andric #include "llvm/Support/Host.h" 39*bdd1243dSDimitry Andric #include "llvm/Support/MemoryBuffer.h" 40*bdd1243dSDimitry Andric #include "llvm/Support/Path.h" 41*bdd1243dSDimitry Andric #include "llvm/Support/Program.h" 42*bdd1243dSDimitry Andric #include "llvm/Support/Signals.h" 43*bdd1243dSDimitry Andric #include "llvm/Support/StringSaver.h" 44*bdd1243dSDimitry Andric #include "llvm/Support/WithColor.h" 45*bdd1243dSDimitry Andric #include "llvm/Support/raw_ostream.h" 46*bdd1243dSDimitry Andric #include <algorithm> 47*bdd1243dSDimitry Andric #include <cassert> 48*bdd1243dSDimitry Andric #include <cstddef> 49*bdd1243dSDimitry Andric #include <cstdint> 50*bdd1243dSDimitry Andric #include <forward_list> 51*bdd1243dSDimitry Andric #include <memory> 52*bdd1243dSDimitry Andric #include <set> 53*bdd1243dSDimitry Andric #include <string> 54*bdd1243dSDimitry Andric #include <system_error> 55*bdd1243dSDimitry Andric #include <utility> 56*bdd1243dSDimitry Andric 57*bdd1243dSDimitry Andric using namespace llvm; 58*bdd1243dSDimitry Andric using namespace llvm::object; 59*bdd1243dSDimitry Andric using namespace clang; 60*bdd1243dSDimitry Andric 61*bdd1243dSDimitry Andric /// Magic string that marks the existence of offloading data. 62*bdd1243dSDimitry Andric #define OFFLOAD_BUNDLER_MAGIC_STR "__CLANG_OFFLOAD_BUNDLE__" 63*bdd1243dSDimitry Andric 64*bdd1243dSDimitry Andric OffloadTargetInfo::OffloadTargetInfo(const StringRef Target, 65*bdd1243dSDimitry Andric const OffloadBundlerConfig &BC) 66*bdd1243dSDimitry Andric : BundlerConfig(BC) { 67*bdd1243dSDimitry Andric 68*bdd1243dSDimitry Andric // TODO: Add error checking from ClangOffloadBundler.cpp 69*bdd1243dSDimitry Andric auto TargetFeatures = Target.split(':'); 70*bdd1243dSDimitry Andric auto TripleOrGPU = TargetFeatures.first.rsplit('-'); 71*bdd1243dSDimitry Andric 72*bdd1243dSDimitry Andric if (clang::StringToCudaArch(TripleOrGPU.second) != clang::CudaArch::UNKNOWN) { 73*bdd1243dSDimitry Andric auto KindTriple = TripleOrGPU.first.split('-'); 74*bdd1243dSDimitry Andric this->OffloadKind = KindTriple.first; 75*bdd1243dSDimitry Andric this->Triple = llvm::Triple(KindTriple.second); 76*bdd1243dSDimitry Andric this->TargetID = Target.substr(Target.find(TripleOrGPU.second)); 77*bdd1243dSDimitry Andric } else { 78*bdd1243dSDimitry Andric auto KindTriple = TargetFeatures.first.split('-'); 79*bdd1243dSDimitry Andric this->OffloadKind = KindTriple.first; 80*bdd1243dSDimitry Andric this->Triple = llvm::Triple(KindTriple.second); 81*bdd1243dSDimitry Andric this->TargetID = ""; 82*bdd1243dSDimitry Andric } 83*bdd1243dSDimitry Andric } 84*bdd1243dSDimitry Andric 85*bdd1243dSDimitry Andric bool OffloadTargetInfo::hasHostKind() const { 86*bdd1243dSDimitry Andric return this->OffloadKind == "host"; 87*bdd1243dSDimitry Andric } 88*bdd1243dSDimitry Andric 89*bdd1243dSDimitry Andric bool OffloadTargetInfo::isOffloadKindValid() const { 90*bdd1243dSDimitry Andric return OffloadKind == "host" || OffloadKind == "openmp" || 91*bdd1243dSDimitry Andric OffloadKind == "hip" || OffloadKind == "hipv4"; 92*bdd1243dSDimitry Andric } 93*bdd1243dSDimitry Andric 94*bdd1243dSDimitry Andric bool OffloadTargetInfo::isOffloadKindCompatible( 95*bdd1243dSDimitry Andric const StringRef TargetOffloadKind) const { 96*bdd1243dSDimitry Andric if (OffloadKind == TargetOffloadKind) 97*bdd1243dSDimitry Andric return true; 98*bdd1243dSDimitry Andric if (BundlerConfig.HipOpenmpCompatible) { 99*bdd1243dSDimitry Andric bool HIPCompatibleWithOpenMP = OffloadKind.startswith_insensitive("hip") && 100*bdd1243dSDimitry Andric TargetOffloadKind == "openmp"; 101*bdd1243dSDimitry Andric bool OpenMPCompatibleWithHIP = 102*bdd1243dSDimitry Andric OffloadKind == "openmp" && 103*bdd1243dSDimitry Andric TargetOffloadKind.startswith_insensitive("hip"); 104*bdd1243dSDimitry Andric return HIPCompatibleWithOpenMP || OpenMPCompatibleWithHIP; 105*bdd1243dSDimitry Andric } 106*bdd1243dSDimitry Andric return false; 107*bdd1243dSDimitry Andric } 108*bdd1243dSDimitry Andric 109*bdd1243dSDimitry Andric bool OffloadTargetInfo::isTripleValid() const { 110*bdd1243dSDimitry Andric return !Triple.str().empty() && Triple.getArch() != Triple::UnknownArch; 111*bdd1243dSDimitry Andric } 112*bdd1243dSDimitry Andric 113*bdd1243dSDimitry Andric bool OffloadTargetInfo::operator==(const OffloadTargetInfo &Target) const { 114*bdd1243dSDimitry Andric return OffloadKind == Target.OffloadKind && 115*bdd1243dSDimitry Andric Triple.isCompatibleWith(Target.Triple) && TargetID == Target.TargetID; 116*bdd1243dSDimitry Andric } 117*bdd1243dSDimitry Andric 118*bdd1243dSDimitry Andric std::string OffloadTargetInfo::str() const { 119*bdd1243dSDimitry Andric return Twine(OffloadKind + "-" + Triple.str() + "-" + TargetID).str(); 120*bdd1243dSDimitry Andric } 121*bdd1243dSDimitry Andric 122*bdd1243dSDimitry Andric static StringRef getDeviceFileExtension(StringRef Device, 123*bdd1243dSDimitry Andric StringRef BundleFileName) { 124*bdd1243dSDimitry Andric if (Device.contains("gfx")) 125*bdd1243dSDimitry Andric return ".bc"; 126*bdd1243dSDimitry Andric if (Device.contains("sm_")) 127*bdd1243dSDimitry Andric return ".cubin"; 128*bdd1243dSDimitry Andric return sys::path::extension(BundleFileName); 129*bdd1243dSDimitry Andric } 130*bdd1243dSDimitry Andric 131*bdd1243dSDimitry Andric static std::string getDeviceLibraryFileName(StringRef BundleFileName, 132*bdd1243dSDimitry Andric StringRef Device) { 133*bdd1243dSDimitry Andric StringRef LibName = sys::path::stem(BundleFileName); 134*bdd1243dSDimitry Andric StringRef Extension = getDeviceFileExtension(Device, BundleFileName); 135*bdd1243dSDimitry Andric 136*bdd1243dSDimitry Andric std::string Result; 137*bdd1243dSDimitry Andric Result += LibName; 138*bdd1243dSDimitry Andric Result += Extension; 139*bdd1243dSDimitry Andric return Result; 140*bdd1243dSDimitry Andric } 141*bdd1243dSDimitry Andric 142*bdd1243dSDimitry Andric /// @brief Checks if a code object \p CodeObjectInfo is compatible with a given 143*bdd1243dSDimitry Andric /// target \p TargetInfo. 144*bdd1243dSDimitry Andric /// @link https://clang.llvm.org/docs/ClangOffloadBundler.html#bundle-entry-id 145*bdd1243dSDimitry Andric bool isCodeObjectCompatible(const OffloadTargetInfo &CodeObjectInfo, 146*bdd1243dSDimitry Andric const OffloadTargetInfo &TargetInfo) { 147*bdd1243dSDimitry Andric 148*bdd1243dSDimitry Andric // Compatible in case of exact match. 149*bdd1243dSDimitry Andric if (CodeObjectInfo == TargetInfo) { 150*bdd1243dSDimitry Andric DEBUG_WITH_TYPE("CodeObjectCompatibility", 151*bdd1243dSDimitry Andric dbgs() << "Compatible: Exact match: \t[CodeObject: " 152*bdd1243dSDimitry Andric << CodeObjectInfo.str() 153*bdd1243dSDimitry Andric << "]\t:\t[Target: " << TargetInfo.str() << "]\n"); 154*bdd1243dSDimitry Andric return true; 155*bdd1243dSDimitry Andric } 156*bdd1243dSDimitry Andric 157*bdd1243dSDimitry Andric // Incompatible if Kinds or Triples mismatch. 158*bdd1243dSDimitry Andric if (!CodeObjectInfo.isOffloadKindCompatible(TargetInfo.OffloadKind) || 159*bdd1243dSDimitry Andric !CodeObjectInfo.Triple.isCompatibleWith(TargetInfo.Triple)) { 160*bdd1243dSDimitry Andric DEBUG_WITH_TYPE( 161*bdd1243dSDimitry Andric "CodeObjectCompatibility", 162*bdd1243dSDimitry Andric dbgs() << "Incompatible: Kind/Triple mismatch \t[CodeObject: " 163*bdd1243dSDimitry Andric << CodeObjectInfo.str() << "]\t:\t[Target: " << TargetInfo.str() 164*bdd1243dSDimitry Andric << "]\n"); 165*bdd1243dSDimitry Andric return false; 166*bdd1243dSDimitry Andric } 167*bdd1243dSDimitry Andric 168*bdd1243dSDimitry Andric // Incompatible if target IDs are incompatible. 169*bdd1243dSDimitry Andric if (!clang::isCompatibleTargetID(CodeObjectInfo.TargetID, 170*bdd1243dSDimitry Andric TargetInfo.TargetID)) { 171*bdd1243dSDimitry Andric DEBUG_WITH_TYPE( 172*bdd1243dSDimitry Andric "CodeObjectCompatibility", 173*bdd1243dSDimitry Andric dbgs() << "Incompatible: target IDs are incompatible \t[CodeObject: " 174*bdd1243dSDimitry Andric << CodeObjectInfo.str() << "]\t:\t[Target: " << TargetInfo.str() 175*bdd1243dSDimitry Andric << "]\n"); 176*bdd1243dSDimitry Andric return false; 177*bdd1243dSDimitry Andric } 178*bdd1243dSDimitry Andric 179*bdd1243dSDimitry Andric DEBUG_WITH_TYPE( 180*bdd1243dSDimitry Andric "CodeObjectCompatibility", 181*bdd1243dSDimitry Andric dbgs() << "Compatible: Code Objects are compatible \t[CodeObject: " 182*bdd1243dSDimitry Andric << CodeObjectInfo.str() << "]\t:\t[Target: " << TargetInfo.str() 183*bdd1243dSDimitry Andric << "]\n"); 184*bdd1243dSDimitry Andric return true; 185*bdd1243dSDimitry Andric } 186*bdd1243dSDimitry Andric 187*bdd1243dSDimitry Andric namespace { 188*bdd1243dSDimitry Andric /// Generic file handler interface. 189*bdd1243dSDimitry Andric class FileHandler { 190*bdd1243dSDimitry Andric public: 191*bdd1243dSDimitry Andric struct BundleInfo { 192*bdd1243dSDimitry Andric StringRef BundleID; 193*bdd1243dSDimitry Andric }; 194*bdd1243dSDimitry Andric 195*bdd1243dSDimitry Andric FileHandler() {} 196*bdd1243dSDimitry Andric 197*bdd1243dSDimitry Andric virtual ~FileHandler() {} 198*bdd1243dSDimitry Andric 199*bdd1243dSDimitry Andric /// Update the file handler with information from the header of the bundled 200*bdd1243dSDimitry Andric /// file. 201*bdd1243dSDimitry Andric virtual Error ReadHeader(MemoryBuffer &Input) = 0; 202*bdd1243dSDimitry Andric 203*bdd1243dSDimitry Andric /// Read the marker of the next bundled to be read in the file. The bundle 204*bdd1243dSDimitry Andric /// name is returned if there is one in the file, or `std::nullopt` if there 205*bdd1243dSDimitry Andric /// are no more bundles to be read. 206*bdd1243dSDimitry Andric virtual Expected<std::optional<StringRef>> 207*bdd1243dSDimitry Andric ReadBundleStart(MemoryBuffer &Input) = 0; 208*bdd1243dSDimitry Andric 209*bdd1243dSDimitry Andric /// Read the marker that closes the current bundle. 210*bdd1243dSDimitry Andric virtual Error ReadBundleEnd(MemoryBuffer &Input) = 0; 211*bdd1243dSDimitry Andric 212*bdd1243dSDimitry Andric /// Read the current bundle and write the result into the stream \a OS. 213*bdd1243dSDimitry Andric virtual Error ReadBundle(raw_ostream &OS, MemoryBuffer &Input) = 0; 214*bdd1243dSDimitry Andric 215*bdd1243dSDimitry Andric /// Write the header of the bundled file to \a OS based on the information 216*bdd1243dSDimitry Andric /// gathered from \a Inputs. 217*bdd1243dSDimitry Andric virtual Error WriteHeader(raw_fd_ostream &OS, 218*bdd1243dSDimitry Andric ArrayRef<std::unique_ptr<MemoryBuffer>> Inputs) = 0; 219*bdd1243dSDimitry Andric 220*bdd1243dSDimitry Andric /// Write the marker that initiates a bundle for the triple \a TargetTriple to 221*bdd1243dSDimitry Andric /// \a OS. 222*bdd1243dSDimitry Andric virtual Error WriteBundleStart(raw_fd_ostream &OS, 223*bdd1243dSDimitry Andric StringRef TargetTriple) = 0; 224*bdd1243dSDimitry Andric 225*bdd1243dSDimitry Andric /// Write the marker that closes a bundle for the triple \a TargetTriple to \a 226*bdd1243dSDimitry Andric /// OS. 227*bdd1243dSDimitry Andric virtual Error WriteBundleEnd(raw_fd_ostream &OS, StringRef TargetTriple) = 0; 228*bdd1243dSDimitry Andric 229*bdd1243dSDimitry Andric /// Write the bundle from \a Input into \a OS. 230*bdd1243dSDimitry Andric virtual Error WriteBundle(raw_fd_ostream &OS, MemoryBuffer &Input) = 0; 231*bdd1243dSDimitry Andric 232*bdd1243dSDimitry Andric /// List bundle IDs in \a Input. 233*bdd1243dSDimitry Andric virtual Error listBundleIDs(MemoryBuffer &Input) { 234*bdd1243dSDimitry Andric if (Error Err = ReadHeader(Input)) 235*bdd1243dSDimitry Andric return Err; 236*bdd1243dSDimitry Andric return forEachBundle(Input, [&](const BundleInfo &Info) -> Error { 237*bdd1243dSDimitry Andric llvm::outs() << Info.BundleID << '\n'; 238*bdd1243dSDimitry Andric Error Err = listBundleIDsCallback(Input, Info); 239*bdd1243dSDimitry Andric if (Err) 240*bdd1243dSDimitry Andric return Err; 241*bdd1243dSDimitry Andric return Error::success(); 242*bdd1243dSDimitry Andric }); 243*bdd1243dSDimitry Andric } 244*bdd1243dSDimitry Andric 245*bdd1243dSDimitry Andric /// For each bundle in \a Input, do \a Func. 246*bdd1243dSDimitry Andric Error forEachBundle(MemoryBuffer &Input, 247*bdd1243dSDimitry Andric std::function<Error(const BundleInfo &)> Func) { 248*bdd1243dSDimitry Andric while (true) { 249*bdd1243dSDimitry Andric Expected<std::optional<StringRef>> CurTripleOrErr = 250*bdd1243dSDimitry Andric ReadBundleStart(Input); 251*bdd1243dSDimitry Andric if (!CurTripleOrErr) 252*bdd1243dSDimitry Andric return CurTripleOrErr.takeError(); 253*bdd1243dSDimitry Andric 254*bdd1243dSDimitry Andric // No more bundles. 255*bdd1243dSDimitry Andric if (!*CurTripleOrErr) 256*bdd1243dSDimitry Andric break; 257*bdd1243dSDimitry Andric 258*bdd1243dSDimitry Andric StringRef CurTriple = **CurTripleOrErr; 259*bdd1243dSDimitry Andric assert(!CurTriple.empty()); 260*bdd1243dSDimitry Andric 261*bdd1243dSDimitry Andric BundleInfo Info{CurTriple}; 262*bdd1243dSDimitry Andric if (Error Err = Func(Info)) 263*bdd1243dSDimitry Andric return Err; 264*bdd1243dSDimitry Andric } 265*bdd1243dSDimitry Andric return Error::success(); 266*bdd1243dSDimitry Andric } 267*bdd1243dSDimitry Andric 268*bdd1243dSDimitry Andric protected: 269*bdd1243dSDimitry Andric virtual Error listBundleIDsCallback(MemoryBuffer &Input, 270*bdd1243dSDimitry Andric const BundleInfo &Info) { 271*bdd1243dSDimitry Andric return Error::success(); 272*bdd1243dSDimitry Andric } 273*bdd1243dSDimitry Andric }; 274*bdd1243dSDimitry Andric 275*bdd1243dSDimitry Andric /// Handler for binary files. The bundled file will have the following format 276*bdd1243dSDimitry Andric /// (all integers are stored in little-endian format): 277*bdd1243dSDimitry Andric /// 278*bdd1243dSDimitry Andric /// "OFFLOAD_BUNDLER_MAGIC_STR" (ASCII encoding of the string) 279*bdd1243dSDimitry Andric /// 280*bdd1243dSDimitry Andric /// NumberOfOffloadBundles (8-byte integer) 281*bdd1243dSDimitry Andric /// 282*bdd1243dSDimitry Andric /// OffsetOfBundle1 (8-byte integer) 283*bdd1243dSDimitry Andric /// SizeOfBundle1 (8-byte integer) 284*bdd1243dSDimitry Andric /// NumberOfBytesInTripleOfBundle1 (8-byte integer) 285*bdd1243dSDimitry Andric /// TripleOfBundle1 (byte length defined before) 286*bdd1243dSDimitry Andric /// 287*bdd1243dSDimitry Andric /// ... 288*bdd1243dSDimitry Andric /// 289*bdd1243dSDimitry Andric /// OffsetOfBundleN (8-byte integer) 290*bdd1243dSDimitry Andric /// SizeOfBundleN (8-byte integer) 291*bdd1243dSDimitry Andric /// NumberOfBytesInTripleOfBundleN (8-byte integer) 292*bdd1243dSDimitry Andric /// TripleOfBundleN (byte length defined before) 293*bdd1243dSDimitry Andric /// 294*bdd1243dSDimitry Andric /// Bundle1 295*bdd1243dSDimitry Andric /// ... 296*bdd1243dSDimitry Andric /// BundleN 297*bdd1243dSDimitry Andric 298*bdd1243dSDimitry Andric /// Read 8-byte integers from a buffer in little-endian format. 299*bdd1243dSDimitry Andric static uint64_t Read8byteIntegerFromBuffer(StringRef Buffer, size_t pos) { 300*bdd1243dSDimitry Andric return llvm::support::endian::read64le(Buffer.data() + pos); 301*bdd1243dSDimitry Andric } 302*bdd1243dSDimitry Andric 303*bdd1243dSDimitry Andric /// Write 8-byte integers to a buffer in little-endian format. 304*bdd1243dSDimitry Andric static void Write8byteIntegerToBuffer(raw_fd_ostream &OS, uint64_t Val) { 305*bdd1243dSDimitry Andric llvm::support::endian::write(OS, Val, llvm::support::little); 306*bdd1243dSDimitry Andric } 307*bdd1243dSDimitry Andric 308*bdd1243dSDimitry Andric class BinaryFileHandler final : public FileHandler { 309*bdd1243dSDimitry Andric /// Information about the bundles extracted from the header. 310*bdd1243dSDimitry Andric struct BinaryBundleInfo final : public BundleInfo { 311*bdd1243dSDimitry Andric /// Size of the bundle. 312*bdd1243dSDimitry Andric uint64_t Size = 0u; 313*bdd1243dSDimitry Andric /// Offset at which the bundle starts in the bundled file. 314*bdd1243dSDimitry Andric uint64_t Offset = 0u; 315*bdd1243dSDimitry Andric 316*bdd1243dSDimitry Andric BinaryBundleInfo() {} 317*bdd1243dSDimitry Andric BinaryBundleInfo(uint64_t Size, uint64_t Offset) 318*bdd1243dSDimitry Andric : Size(Size), Offset(Offset) {} 319*bdd1243dSDimitry Andric }; 320*bdd1243dSDimitry Andric 321*bdd1243dSDimitry Andric /// Map between a triple and the corresponding bundle information. 322*bdd1243dSDimitry Andric StringMap<BinaryBundleInfo> BundlesInfo; 323*bdd1243dSDimitry Andric 324*bdd1243dSDimitry Andric /// Iterator for the bundle information that is being read. 325*bdd1243dSDimitry Andric StringMap<BinaryBundleInfo>::iterator CurBundleInfo; 326*bdd1243dSDimitry Andric StringMap<BinaryBundleInfo>::iterator NextBundleInfo; 327*bdd1243dSDimitry Andric 328*bdd1243dSDimitry Andric /// Current bundle target to be written. 329*bdd1243dSDimitry Andric std::string CurWriteBundleTarget; 330*bdd1243dSDimitry Andric 331*bdd1243dSDimitry Andric /// Configuration options and arrays for this bundler job 332*bdd1243dSDimitry Andric const OffloadBundlerConfig &BundlerConfig; 333*bdd1243dSDimitry Andric 334*bdd1243dSDimitry Andric public: 335*bdd1243dSDimitry Andric // TODO: Add error checking from ClangOffloadBundler.cpp 336*bdd1243dSDimitry Andric BinaryFileHandler(const OffloadBundlerConfig &BC) : BundlerConfig(BC) {} 337*bdd1243dSDimitry Andric 338*bdd1243dSDimitry Andric ~BinaryFileHandler() final {} 339*bdd1243dSDimitry Andric 340*bdd1243dSDimitry Andric Error ReadHeader(MemoryBuffer &Input) final { 341*bdd1243dSDimitry Andric StringRef FC = Input.getBuffer(); 342*bdd1243dSDimitry Andric 343*bdd1243dSDimitry Andric // Initialize the current bundle with the end of the container. 344*bdd1243dSDimitry Andric CurBundleInfo = BundlesInfo.end(); 345*bdd1243dSDimitry Andric 346*bdd1243dSDimitry Andric // Check if buffer is smaller than magic string. 347*bdd1243dSDimitry Andric size_t ReadChars = sizeof(OFFLOAD_BUNDLER_MAGIC_STR) - 1; 348*bdd1243dSDimitry Andric if (ReadChars > FC.size()) 349*bdd1243dSDimitry Andric return Error::success(); 350*bdd1243dSDimitry Andric 351*bdd1243dSDimitry Andric // Check if no magic was found. 352*bdd1243dSDimitry Andric StringRef Magic(FC.data(), sizeof(OFFLOAD_BUNDLER_MAGIC_STR) - 1); 353*bdd1243dSDimitry Andric if (!Magic.equals(OFFLOAD_BUNDLER_MAGIC_STR)) 354*bdd1243dSDimitry Andric return Error::success(); 355*bdd1243dSDimitry Andric 356*bdd1243dSDimitry Andric // Read number of bundles. 357*bdd1243dSDimitry Andric if (ReadChars + 8 > FC.size()) 358*bdd1243dSDimitry Andric return Error::success(); 359*bdd1243dSDimitry Andric 360*bdd1243dSDimitry Andric uint64_t NumberOfBundles = Read8byteIntegerFromBuffer(FC, ReadChars); 361*bdd1243dSDimitry Andric ReadChars += 8; 362*bdd1243dSDimitry Andric 363*bdd1243dSDimitry Andric // Read bundle offsets, sizes and triples. 364*bdd1243dSDimitry Andric for (uint64_t i = 0; i < NumberOfBundles; ++i) { 365*bdd1243dSDimitry Andric 366*bdd1243dSDimitry Andric // Read offset. 367*bdd1243dSDimitry Andric if (ReadChars + 8 > FC.size()) 368*bdd1243dSDimitry Andric return Error::success(); 369*bdd1243dSDimitry Andric 370*bdd1243dSDimitry Andric uint64_t Offset = Read8byteIntegerFromBuffer(FC, ReadChars); 371*bdd1243dSDimitry Andric ReadChars += 8; 372*bdd1243dSDimitry Andric 373*bdd1243dSDimitry Andric // Read size. 374*bdd1243dSDimitry Andric if (ReadChars + 8 > FC.size()) 375*bdd1243dSDimitry Andric return Error::success(); 376*bdd1243dSDimitry Andric 377*bdd1243dSDimitry Andric uint64_t Size = Read8byteIntegerFromBuffer(FC, ReadChars); 378*bdd1243dSDimitry Andric ReadChars += 8; 379*bdd1243dSDimitry Andric 380*bdd1243dSDimitry Andric // Read triple size. 381*bdd1243dSDimitry Andric if (ReadChars + 8 > FC.size()) 382*bdd1243dSDimitry Andric return Error::success(); 383*bdd1243dSDimitry Andric 384*bdd1243dSDimitry Andric uint64_t TripleSize = Read8byteIntegerFromBuffer(FC, ReadChars); 385*bdd1243dSDimitry Andric ReadChars += 8; 386*bdd1243dSDimitry Andric 387*bdd1243dSDimitry Andric // Read triple. 388*bdd1243dSDimitry Andric if (ReadChars + TripleSize > FC.size()) 389*bdd1243dSDimitry Andric return Error::success(); 390*bdd1243dSDimitry Andric 391*bdd1243dSDimitry Andric StringRef Triple(&FC.data()[ReadChars], TripleSize); 392*bdd1243dSDimitry Andric ReadChars += TripleSize; 393*bdd1243dSDimitry Andric 394*bdd1243dSDimitry Andric // Check if the offset and size make sense. 395*bdd1243dSDimitry Andric if (!Offset || Offset + Size > FC.size()) 396*bdd1243dSDimitry Andric return Error::success(); 397*bdd1243dSDimitry Andric 398*bdd1243dSDimitry Andric assert(BundlesInfo.find(Triple) == BundlesInfo.end() && 399*bdd1243dSDimitry Andric "Triple is duplicated??"); 400*bdd1243dSDimitry Andric BundlesInfo[Triple] = BinaryBundleInfo(Size, Offset); 401*bdd1243dSDimitry Andric } 402*bdd1243dSDimitry Andric // Set the iterator to where we will start to read. 403*bdd1243dSDimitry Andric CurBundleInfo = BundlesInfo.end(); 404*bdd1243dSDimitry Andric NextBundleInfo = BundlesInfo.begin(); 405*bdd1243dSDimitry Andric return Error::success(); 406*bdd1243dSDimitry Andric } 407*bdd1243dSDimitry Andric 408*bdd1243dSDimitry Andric Expected<std::optional<StringRef>> 409*bdd1243dSDimitry Andric ReadBundleStart(MemoryBuffer &Input) final { 410*bdd1243dSDimitry Andric if (NextBundleInfo == BundlesInfo.end()) 411*bdd1243dSDimitry Andric return std::nullopt; 412*bdd1243dSDimitry Andric CurBundleInfo = NextBundleInfo++; 413*bdd1243dSDimitry Andric return CurBundleInfo->first(); 414*bdd1243dSDimitry Andric } 415*bdd1243dSDimitry Andric 416*bdd1243dSDimitry Andric Error ReadBundleEnd(MemoryBuffer &Input) final { 417*bdd1243dSDimitry Andric assert(CurBundleInfo != BundlesInfo.end() && "Invalid reader info!"); 418*bdd1243dSDimitry Andric return Error::success(); 419*bdd1243dSDimitry Andric } 420*bdd1243dSDimitry Andric 421*bdd1243dSDimitry Andric Error ReadBundle(raw_ostream &OS, MemoryBuffer &Input) final { 422*bdd1243dSDimitry Andric assert(CurBundleInfo != BundlesInfo.end() && "Invalid reader info!"); 423*bdd1243dSDimitry Andric StringRef FC = Input.getBuffer(); 424*bdd1243dSDimitry Andric OS.write(FC.data() + CurBundleInfo->second.Offset, 425*bdd1243dSDimitry Andric CurBundleInfo->second.Size); 426*bdd1243dSDimitry Andric return Error::success(); 427*bdd1243dSDimitry Andric } 428*bdd1243dSDimitry Andric 429*bdd1243dSDimitry Andric Error WriteHeader(raw_fd_ostream &OS, 430*bdd1243dSDimitry Andric ArrayRef<std::unique_ptr<MemoryBuffer>> Inputs) final { 431*bdd1243dSDimitry Andric 432*bdd1243dSDimitry Andric // Compute size of the header. 433*bdd1243dSDimitry Andric uint64_t HeaderSize = 0; 434*bdd1243dSDimitry Andric 435*bdd1243dSDimitry Andric HeaderSize += sizeof(OFFLOAD_BUNDLER_MAGIC_STR) - 1; 436*bdd1243dSDimitry Andric HeaderSize += 8; // Number of Bundles 437*bdd1243dSDimitry Andric 438*bdd1243dSDimitry Andric for (auto &T : BundlerConfig.TargetNames) { 439*bdd1243dSDimitry Andric HeaderSize += 3 * 8; // Bundle offset, Size of bundle and size of triple. 440*bdd1243dSDimitry Andric HeaderSize += T.size(); // The triple. 441*bdd1243dSDimitry Andric } 442*bdd1243dSDimitry Andric 443*bdd1243dSDimitry Andric // Write to the buffer the header. 444*bdd1243dSDimitry Andric OS << OFFLOAD_BUNDLER_MAGIC_STR; 445*bdd1243dSDimitry Andric 446*bdd1243dSDimitry Andric Write8byteIntegerToBuffer(OS, BundlerConfig.TargetNames.size()); 447*bdd1243dSDimitry Andric 448*bdd1243dSDimitry Andric unsigned Idx = 0; 449*bdd1243dSDimitry Andric for (auto &T : BundlerConfig.TargetNames) { 450*bdd1243dSDimitry Andric MemoryBuffer &MB = *Inputs[Idx++]; 451*bdd1243dSDimitry Andric HeaderSize = alignTo(HeaderSize, BundlerConfig.BundleAlignment); 452*bdd1243dSDimitry Andric // Bundle offset. 453*bdd1243dSDimitry Andric Write8byteIntegerToBuffer(OS, HeaderSize); 454*bdd1243dSDimitry Andric // Size of the bundle (adds to the next bundle's offset) 455*bdd1243dSDimitry Andric Write8byteIntegerToBuffer(OS, MB.getBufferSize()); 456*bdd1243dSDimitry Andric BundlesInfo[T] = BinaryBundleInfo(MB.getBufferSize(), HeaderSize); 457*bdd1243dSDimitry Andric HeaderSize += MB.getBufferSize(); 458*bdd1243dSDimitry Andric // Size of the triple 459*bdd1243dSDimitry Andric Write8byteIntegerToBuffer(OS, T.size()); 460*bdd1243dSDimitry Andric // Triple 461*bdd1243dSDimitry Andric OS << T; 462*bdd1243dSDimitry Andric } 463*bdd1243dSDimitry Andric return Error::success(); 464*bdd1243dSDimitry Andric } 465*bdd1243dSDimitry Andric 466*bdd1243dSDimitry Andric Error WriteBundleStart(raw_fd_ostream &OS, StringRef TargetTriple) final { 467*bdd1243dSDimitry Andric CurWriteBundleTarget = TargetTriple.str(); 468*bdd1243dSDimitry Andric return Error::success(); 469*bdd1243dSDimitry Andric } 470*bdd1243dSDimitry Andric 471*bdd1243dSDimitry Andric Error WriteBundleEnd(raw_fd_ostream &OS, StringRef TargetTriple) final { 472*bdd1243dSDimitry Andric return Error::success(); 473*bdd1243dSDimitry Andric } 474*bdd1243dSDimitry Andric 475*bdd1243dSDimitry Andric Error WriteBundle(raw_fd_ostream &OS, MemoryBuffer &Input) final { 476*bdd1243dSDimitry Andric auto BI = BundlesInfo[CurWriteBundleTarget]; 477*bdd1243dSDimitry Andric OS.seek(BI.Offset); 478*bdd1243dSDimitry Andric OS.write(Input.getBufferStart(), Input.getBufferSize()); 479*bdd1243dSDimitry Andric return Error::success(); 480*bdd1243dSDimitry Andric } 481*bdd1243dSDimitry Andric }; 482*bdd1243dSDimitry Andric 483*bdd1243dSDimitry Andric // This class implements a list of temporary files that are removed upon 484*bdd1243dSDimitry Andric // object destruction. 485*bdd1243dSDimitry Andric class TempFileHandlerRAII { 486*bdd1243dSDimitry Andric public: 487*bdd1243dSDimitry Andric ~TempFileHandlerRAII() { 488*bdd1243dSDimitry Andric for (const auto &File : Files) 489*bdd1243dSDimitry Andric sys::fs::remove(File); 490*bdd1243dSDimitry Andric } 491*bdd1243dSDimitry Andric 492*bdd1243dSDimitry Andric // Creates temporary file with given contents. 493*bdd1243dSDimitry Andric Expected<StringRef> Create(std::optional<ArrayRef<char>> Contents) { 494*bdd1243dSDimitry Andric SmallString<128u> File; 495*bdd1243dSDimitry Andric if (std::error_code EC = 496*bdd1243dSDimitry Andric sys::fs::createTemporaryFile("clang-offload-bundler", "tmp", File)) 497*bdd1243dSDimitry Andric return createFileError(File, EC); 498*bdd1243dSDimitry Andric Files.push_front(File); 499*bdd1243dSDimitry Andric 500*bdd1243dSDimitry Andric if (Contents) { 501*bdd1243dSDimitry Andric std::error_code EC; 502*bdd1243dSDimitry Andric raw_fd_ostream OS(File, EC); 503*bdd1243dSDimitry Andric if (EC) 504*bdd1243dSDimitry Andric return createFileError(File, EC); 505*bdd1243dSDimitry Andric OS.write(Contents->data(), Contents->size()); 506*bdd1243dSDimitry Andric } 507*bdd1243dSDimitry Andric return Files.front().str(); 508*bdd1243dSDimitry Andric } 509*bdd1243dSDimitry Andric 510*bdd1243dSDimitry Andric private: 511*bdd1243dSDimitry Andric std::forward_list<SmallString<128u>> Files; 512*bdd1243dSDimitry Andric }; 513*bdd1243dSDimitry Andric 514*bdd1243dSDimitry Andric /// Handler for object files. The bundles are organized by sections with a 515*bdd1243dSDimitry Andric /// designated name. 516*bdd1243dSDimitry Andric /// 517*bdd1243dSDimitry Andric /// To unbundle, we just copy the contents of the designated section. 518*bdd1243dSDimitry Andric class ObjectFileHandler final : public FileHandler { 519*bdd1243dSDimitry Andric 520*bdd1243dSDimitry Andric /// The object file we are currently dealing with. 521*bdd1243dSDimitry Andric std::unique_ptr<ObjectFile> Obj; 522*bdd1243dSDimitry Andric 523*bdd1243dSDimitry Andric /// Return the input file contents. 524*bdd1243dSDimitry Andric StringRef getInputFileContents() const { return Obj->getData(); } 525*bdd1243dSDimitry Andric 526*bdd1243dSDimitry Andric /// Return bundle name (<kind>-<triple>) if the provided section is an offload 527*bdd1243dSDimitry Andric /// section. 528*bdd1243dSDimitry Andric static Expected<std::optional<StringRef>> 529*bdd1243dSDimitry Andric IsOffloadSection(SectionRef CurSection) { 530*bdd1243dSDimitry Andric Expected<StringRef> NameOrErr = CurSection.getName(); 531*bdd1243dSDimitry Andric if (!NameOrErr) 532*bdd1243dSDimitry Andric return NameOrErr.takeError(); 533*bdd1243dSDimitry Andric 534*bdd1243dSDimitry Andric // If it does not start with the reserved suffix, just skip this section. 535*bdd1243dSDimitry Andric if (!NameOrErr->startswith(OFFLOAD_BUNDLER_MAGIC_STR)) 536*bdd1243dSDimitry Andric return std::nullopt; 537*bdd1243dSDimitry Andric 538*bdd1243dSDimitry Andric // Return the triple that is right after the reserved prefix. 539*bdd1243dSDimitry Andric return NameOrErr->substr(sizeof(OFFLOAD_BUNDLER_MAGIC_STR) - 1); 540*bdd1243dSDimitry Andric } 541*bdd1243dSDimitry Andric 542*bdd1243dSDimitry Andric /// Total number of inputs. 543*bdd1243dSDimitry Andric unsigned NumberOfInputs = 0; 544*bdd1243dSDimitry Andric 545*bdd1243dSDimitry Andric /// Total number of processed inputs, i.e, inputs that were already 546*bdd1243dSDimitry Andric /// read from the buffers. 547*bdd1243dSDimitry Andric unsigned NumberOfProcessedInputs = 0; 548*bdd1243dSDimitry Andric 549*bdd1243dSDimitry Andric /// Iterator of the current and next section. 550*bdd1243dSDimitry Andric section_iterator CurrentSection; 551*bdd1243dSDimitry Andric section_iterator NextSection; 552*bdd1243dSDimitry Andric 553*bdd1243dSDimitry Andric /// Configuration options and arrays for this bundler job 554*bdd1243dSDimitry Andric const OffloadBundlerConfig &BundlerConfig; 555*bdd1243dSDimitry Andric 556*bdd1243dSDimitry Andric public: 557*bdd1243dSDimitry Andric // TODO: Add error checking from ClangOffloadBundler.cpp 558*bdd1243dSDimitry Andric ObjectFileHandler(std::unique_ptr<ObjectFile> ObjIn, 559*bdd1243dSDimitry Andric const OffloadBundlerConfig &BC) 560*bdd1243dSDimitry Andric : Obj(std::move(ObjIn)), CurrentSection(Obj->section_begin()), 561*bdd1243dSDimitry Andric NextSection(Obj->section_begin()), BundlerConfig(BC) {} 562*bdd1243dSDimitry Andric 563*bdd1243dSDimitry Andric ~ObjectFileHandler() final {} 564*bdd1243dSDimitry Andric 565*bdd1243dSDimitry Andric Error ReadHeader(MemoryBuffer &Input) final { return Error::success(); } 566*bdd1243dSDimitry Andric 567*bdd1243dSDimitry Andric Expected<std::optional<StringRef>> 568*bdd1243dSDimitry Andric ReadBundleStart(MemoryBuffer &Input) final { 569*bdd1243dSDimitry Andric while (NextSection != Obj->section_end()) { 570*bdd1243dSDimitry Andric CurrentSection = NextSection; 571*bdd1243dSDimitry Andric ++NextSection; 572*bdd1243dSDimitry Andric 573*bdd1243dSDimitry Andric // Check if the current section name starts with the reserved prefix. If 574*bdd1243dSDimitry Andric // so, return the triple. 575*bdd1243dSDimitry Andric Expected<std::optional<StringRef>> TripleOrErr = 576*bdd1243dSDimitry Andric IsOffloadSection(*CurrentSection); 577*bdd1243dSDimitry Andric if (!TripleOrErr) 578*bdd1243dSDimitry Andric return TripleOrErr.takeError(); 579*bdd1243dSDimitry Andric if (*TripleOrErr) 580*bdd1243dSDimitry Andric return **TripleOrErr; 581*bdd1243dSDimitry Andric } 582*bdd1243dSDimitry Andric return std::nullopt; 583*bdd1243dSDimitry Andric } 584*bdd1243dSDimitry Andric 585*bdd1243dSDimitry Andric Error ReadBundleEnd(MemoryBuffer &Input) final { return Error::success(); } 586*bdd1243dSDimitry Andric 587*bdd1243dSDimitry Andric Error ReadBundle(raw_ostream &OS, MemoryBuffer &Input) final { 588*bdd1243dSDimitry Andric Expected<StringRef> ContentOrErr = CurrentSection->getContents(); 589*bdd1243dSDimitry Andric if (!ContentOrErr) 590*bdd1243dSDimitry Andric return ContentOrErr.takeError(); 591*bdd1243dSDimitry Andric StringRef Content = *ContentOrErr; 592*bdd1243dSDimitry Andric 593*bdd1243dSDimitry Andric // Copy fat object contents to the output when extracting host bundle. 594*bdd1243dSDimitry Andric if (Content.size() == 1u && Content.front() == 0) 595*bdd1243dSDimitry Andric Content = StringRef(Input.getBufferStart(), Input.getBufferSize()); 596*bdd1243dSDimitry Andric 597*bdd1243dSDimitry Andric OS.write(Content.data(), Content.size()); 598*bdd1243dSDimitry Andric return Error::success(); 599*bdd1243dSDimitry Andric } 600*bdd1243dSDimitry Andric 601*bdd1243dSDimitry Andric Error WriteHeader(raw_fd_ostream &OS, 602*bdd1243dSDimitry Andric ArrayRef<std::unique_ptr<MemoryBuffer>> Inputs) final { 603*bdd1243dSDimitry Andric assert(BundlerConfig.HostInputIndex != ~0u && 604*bdd1243dSDimitry Andric "Host input index not defined."); 605*bdd1243dSDimitry Andric 606*bdd1243dSDimitry Andric // Record number of inputs. 607*bdd1243dSDimitry Andric NumberOfInputs = Inputs.size(); 608*bdd1243dSDimitry Andric return Error::success(); 609*bdd1243dSDimitry Andric } 610*bdd1243dSDimitry Andric 611*bdd1243dSDimitry Andric Error WriteBundleStart(raw_fd_ostream &OS, StringRef TargetTriple) final { 612*bdd1243dSDimitry Andric ++NumberOfProcessedInputs; 613*bdd1243dSDimitry Andric return Error::success(); 614*bdd1243dSDimitry Andric } 615*bdd1243dSDimitry Andric 616*bdd1243dSDimitry Andric Error WriteBundleEnd(raw_fd_ostream &OS, StringRef TargetTriple) final { 617*bdd1243dSDimitry Andric assert(NumberOfProcessedInputs <= NumberOfInputs && 618*bdd1243dSDimitry Andric "Processing more inputs that actually exist!"); 619*bdd1243dSDimitry Andric assert(BundlerConfig.HostInputIndex != ~0u && 620*bdd1243dSDimitry Andric "Host input index not defined."); 621*bdd1243dSDimitry Andric 622*bdd1243dSDimitry Andric // If this is not the last output, we don't have to do anything. 623*bdd1243dSDimitry Andric if (NumberOfProcessedInputs != NumberOfInputs) 624*bdd1243dSDimitry Andric return Error::success(); 625*bdd1243dSDimitry Andric 626*bdd1243dSDimitry Andric // We will use llvm-objcopy to add target objects sections to the output 627*bdd1243dSDimitry Andric // fat object. These sections should have 'exclude' flag set which tells 628*bdd1243dSDimitry Andric // link editor to remove them from linker inputs when linking executable or 629*bdd1243dSDimitry Andric // shared library. 630*bdd1243dSDimitry Andric 631*bdd1243dSDimitry Andric assert(BundlerConfig.ObjcopyPath != "" && 632*bdd1243dSDimitry Andric "llvm-objcopy path not specified"); 633*bdd1243dSDimitry Andric 634*bdd1243dSDimitry Andric // We write to the output file directly. So, we close it and use the name 635*bdd1243dSDimitry Andric // to pass down to llvm-objcopy. 636*bdd1243dSDimitry Andric OS.close(); 637*bdd1243dSDimitry Andric 638*bdd1243dSDimitry Andric // Temporary files that need to be removed. 639*bdd1243dSDimitry Andric TempFileHandlerRAII TempFiles; 640*bdd1243dSDimitry Andric 641*bdd1243dSDimitry Andric // Compose llvm-objcopy command line for add target objects' sections with 642*bdd1243dSDimitry Andric // appropriate flags. 643*bdd1243dSDimitry Andric BumpPtrAllocator Alloc; 644*bdd1243dSDimitry Andric StringSaver SS{Alloc}; 645*bdd1243dSDimitry Andric SmallVector<StringRef, 8u> ObjcopyArgs{"llvm-objcopy"}; 646*bdd1243dSDimitry Andric 647*bdd1243dSDimitry Andric for (unsigned I = 0; I < NumberOfInputs; ++I) { 648*bdd1243dSDimitry Andric StringRef InputFile = BundlerConfig.InputFileNames[I]; 649*bdd1243dSDimitry Andric if (I == BundlerConfig.HostInputIndex) { 650*bdd1243dSDimitry Andric // Special handling for the host bundle. We do not need to add a 651*bdd1243dSDimitry Andric // standard bundle for the host object since we are going to use fat 652*bdd1243dSDimitry Andric // object as a host object. Therefore use dummy contents (one zero byte) 653*bdd1243dSDimitry Andric // when creating section for the host bundle. 654*bdd1243dSDimitry Andric Expected<StringRef> TempFileOrErr = TempFiles.Create(ArrayRef<char>(0)); 655*bdd1243dSDimitry Andric if (!TempFileOrErr) 656*bdd1243dSDimitry Andric return TempFileOrErr.takeError(); 657*bdd1243dSDimitry Andric InputFile = *TempFileOrErr; 658*bdd1243dSDimitry Andric } 659*bdd1243dSDimitry Andric 660*bdd1243dSDimitry Andric ObjcopyArgs.push_back( 661*bdd1243dSDimitry Andric SS.save(Twine("--add-section=") + OFFLOAD_BUNDLER_MAGIC_STR + 662*bdd1243dSDimitry Andric BundlerConfig.TargetNames[I] + "=" + InputFile)); 663*bdd1243dSDimitry Andric ObjcopyArgs.push_back( 664*bdd1243dSDimitry Andric SS.save(Twine("--set-section-flags=") + OFFLOAD_BUNDLER_MAGIC_STR + 665*bdd1243dSDimitry Andric BundlerConfig.TargetNames[I] + "=readonly,exclude")); 666*bdd1243dSDimitry Andric } 667*bdd1243dSDimitry Andric ObjcopyArgs.push_back("--"); 668*bdd1243dSDimitry Andric ObjcopyArgs.push_back( 669*bdd1243dSDimitry Andric BundlerConfig.InputFileNames[BundlerConfig.HostInputIndex]); 670*bdd1243dSDimitry Andric ObjcopyArgs.push_back(BundlerConfig.OutputFileNames.front()); 671*bdd1243dSDimitry Andric 672*bdd1243dSDimitry Andric if (Error Err = executeObjcopy(BundlerConfig.ObjcopyPath, ObjcopyArgs)) 673*bdd1243dSDimitry Andric return Err; 674*bdd1243dSDimitry Andric 675*bdd1243dSDimitry Andric return Error::success(); 676*bdd1243dSDimitry Andric } 677*bdd1243dSDimitry Andric 678*bdd1243dSDimitry Andric Error WriteBundle(raw_fd_ostream &OS, MemoryBuffer &Input) final { 679*bdd1243dSDimitry Andric return Error::success(); 680*bdd1243dSDimitry Andric } 681*bdd1243dSDimitry Andric 682*bdd1243dSDimitry Andric private: 683*bdd1243dSDimitry Andric Error executeObjcopy(StringRef Objcopy, ArrayRef<StringRef> Args) { 684*bdd1243dSDimitry Andric // If the user asked for the commands to be printed out, we do that 685*bdd1243dSDimitry Andric // instead of executing it. 686*bdd1243dSDimitry Andric if (BundlerConfig.PrintExternalCommands) { 687*bdd1243dSDimitry Andric errs() << "\"" << Objcopy << "\""; 688*bdd1243dSDimitry Andric for (StringRef Arg : drop_begin(Args, 1)) 689*bdd1243dSDimitry Andric errs() << " \"" << Arg << "\""; 690*bdd1243dSDimitry Andric errs() << "\n"; 691*bdd1243dSDimitry Andric } else { 692*bdd1243dSDimitry Andric if (sys::ExecuteAndWait(Objcopy, Args)) 693*bdd1243dSDimitry Andric return createStringError(inconvertibleErrorCode(), 694*bdd1243dSDimitry Andric "'llvm-objcopy' tool failed"); 695*bdd1243dSDimitry Andric } 696*bdd1243dSDimitry Andric return Error::success(); 697*bdd1243dSDimitry Andric } 698*bdd1243dSDimitry Andric }; 699*bdd1243dSDimitry Andric 700*bdd1243dSDimitry Andric /// Handler for text files. The bundled file will have the following format. 701*bdd1243dSDimitry Andric /// 702*bdd1243dSDimitry Andric /// "Comment OFFLOAD_BUNDLER_MAGIC_STR__START__ triple" 703*bdd1243dSDimitry Andric /// Bundle 1 704*bdd1243dSDimitry Andric /// "Comment OFFLOAD_BUNDLER_MAGIC_STR__END__ triple" 705*bdd1243dSDimitry Andric /// ... 706*bdd1243dSDimitry Andric /// "Comment OFFLOAD_BUNDLER_MAGIC_STR__START__ triple" 707*bdd1243dSDimitry Andric /// Bundle N 708*bdd1243dSDimitry Andric /// "Comment OFFLOAD_BUNDLER_MAGIC_STR__END__ triple" 709*bdd1243dSDimitry Andric class TextFileHandler final : public FileHandler { 710*bdd1243dSDimitry Andric /// String that begins a line comment. 711*bdd1243dSDimitry Andric StringRef Comment; 712*bdd1243dSDimitry Andric 713*bdd1243dSDimitry Andric /// String that initiates a bundle. 714*bdd1243dSDimitry Andric std::string BundleStartString; 715*bdd1243dSDimitry Andric 716*bdd1243dSDimitry Andric /// String that closes a bundle. 717*bdd1243dSDimitry Andric std::string BundleEndString; 718*bdd1243dSDimitry Andric 719*bdd1243dSDimitry Andric /// Number of chars read from input. 720*bdd1243dSDimitry Andric size_t ReadChars = 0u; 721*bdd1243dSDimitry Andric 722*bdd1243dSDimitry Andric protected: 723*bdd1243dSDimitry Andric Error ReadHeader(MemoryBuffer &Input) final { return Error::success(); } 724*bdd1243dSDimitry Andric 725*bdd1243dSDimitry Andric Expected<std::optional<StringRef>> 726*bdd1243dSDimitry Andric ReadBundleStart(MemoryBuffer &Input) final { 727*bdd1243dSDimitry Andric StringRef FC = Input.getBuffer(); 728*bdd1243dSDimitry Andric 729*bdd1243dSDimitry Andric // Find start of the bundle. 730*bdd1243dSDimitry Andric ReadChars = FC.find(BundleStartString, ReadChars); 731*bdd1243dSDimitry Andric if (ReadChars == FC.npos) 732*bdd1243dSDimitry Andric return std::nullopt; 733*bdd1243dSDimitry Andric 734*bdd1243dSDimitry Andric // Get position of the triple. 735*bdd1243dSDimitry Andric size_t TripleStart = ReadChars = ReadChars + BundleStartString.size(); 736*bdd1243dSDimitry Andric 737*bdd1243dSDimitry Andric // Get position that closes the triple. 738*bdd1243dSDimitry Andric size_t TripleEnd = ReadChars = FC.find("\n", ReadChars); 739*bdd1243dSDimitry Andric if (TripleEnd == FC.npos) 740*bdd1243dSDimitry Andric return std::nullopt; 741*bdd1243dSDimitry Andric 742*bdd1243dSDimitry Andric // Next time we read after the new line. 743*bdd1243dSDimitry Andric ++ReadChars; 744*bdd1243dSDimitry Andric 745*bdd1243dSDimitry Andric return StringRef(&FC.data()[TripleStart], TripleEnd - TripleStart); 746*bdd1243dSDimitry Andric } 747*bdd1243dSDimitry Andric 748*bdd1243dSDimitry Andric Error ReadBundleEnd(MemoryBuffer &Input) final { 749*bdd1243dSDimitry Andric StringRef FC = Input.getBuffer(); 750*bdd1243dSDimitry Andric 751*bdd1243dSDimitry Andric // Read up to the next new line. 752*bdd1243dSDimitry Andric assert(FC[ReadChars] == '\n' && "The bundle should end with a new line."); 753*bdd1243dSDimitry Andric 754*bdd1243dSDimitry Andric size_t TripleEnd = ReadChars = FC.find("\n", ReadChars + 1); 755*bdd1243dSDimitry Andric if (TripleEnd != FC.npos) 756*bdd1243dSDimitry Andric // Next time we read after the new line. 757*bdd1243dSDimitry Andric ++ReadChars; 758*bdd1243dSDimitry Andric 759*bdd1243dSDimitry Andric return Error::success(); 760*bdd1243dSDimitry Andric } 761*bdd1243dSDimitry Andric 762*bdd1243dSDimitry Andric Error ReadBundle(raw_ostream &OS, MemoryBuffer &Input) final { 763*bdd1243dSDimitry Andric StringRef FC = Input.getBuffer(); 764*bdd1243dSDimitry Andric size_t BundleStart = ReadChars; 765*bdd1243dSDimitry Andric 766*bdd1243dSDimitry Andric // Find end of the bundle. 767*bdd1243dSDimitry Andric size_t BundleEnd = ReadChars = FC.find(BundleEndString, ReadChars); 768*bdd1243dSDimitry Andric 769*bdd1243dSDimitry Andric StringRef Bundle(&FC.data()[BundleStart], BundleEnd - BundleStart); 770*bdd1243dSDimitry Andric OS << Bundle; 771*bdd1243dSDimitry Andric 772*bdd1243dSDimitry Andric return Error::success(); 773*bdd1243dSDimitry Andric } 774*bdd1243dSDimitry Andric 775*bdd1243dSDimitry Andric Error WriteHeader(raw_fd_ostream &OS, 776*bdd1243dSDimitry Andric ArrayRef<std::unique_ptr<MemoryBuffer>> Inputs) final { 777*bdd1243dSDimitry Andric return Error::success(); 778*bdd1243dSDimitry Andric } 779*bdd1243dSDimitry Andric 780*bdd1243dSDimitry Andric Error WriteBundleStart(raw_fd_ostream &OS, StringRef TargetTriple) final { 781*bdd1243dSDimitry Andric OS << BundleStartString << TargetTriple << "\n"; 782*bdd1243dSDimitry Andric return Error::success(); 783*bdd1243dSDimitry Andric } 784*bdd1243dSDimitry Andric 785*bdd1243dSDimitry Andric Error WriteBundleEnd(raw_fd_ostream &OS, StringRef TargetTriple) final { 786*bdd1243dSDimitry Andric OS << BundleEndString << TargetTriple << "\n"; 787*bdd1243dSDimitry Andric return Error::success(); 788*bdd1243dSDimitry Andric } 789*bdd1243dSDimitry Andric 790*bdd1243dSDimitry Andric Error WriteBundle(raw_fd_ostream &OS, MemoryBuffer &Input) final { 791*bdd1243dSDimitry Andric OS << Input.getBuffer(); 792*bdd1243dSDimitry Andric return Error::success(); 793*bdd1243dSDimitry Andric } 794*bdd1243dSDimitry Andric 795*bdd1243dSDimitry Andric public: 796*bdd1243dSDimitry Andric TextFileHandler(StringRef Comment) : Comment(Comment), ReadChars(0) { 797*bdd1243dSDimitry Andric BundleStartString = 798*bdd1243dSDimitry Andric "\n" + Comment.str() + " " OFFLOAD_BUNDLER_MAGIC_STR "__START__ "; 799*bdd1243dSDimitry Andric BundleEndString = 800*bdd1243dSDimitry Andric "\n" + Comment.str() + " " OFFLOAD_BUNDLER_MAGIC_STR "__END__ "; 801*bdd1243dSDimitry Andric } 802*bdd1243dSDimitry Andric 803*bdd1243dSDimitry Andric Error listBundleIDsCallback(MemoryBuffer &Input, 804*bdd1243dSDimitry Andric const BundleInfo &Info) final { 805*bdd1243dSDimitry Andric // TODO: To list bundle IDs in a bundled text file we need to go through 806*bdd1243dSDimitry Andric // all bundles. The format of bundled text file may need to include a 807*bdd1243dSDimitry Andric // header if the performance of listing bundle IDs of bundled text file is 808*bdd1243dSDimitry Andric // important. 809*bdd1243dSDimitry Andric ReadChars = Input.getBuffer().find(BundleEndString, ReadChars); 810*bdd1243dSDimitry Andric if (Error Err = ReadBundleEnd(Input)) 811*bdd1243dSDimitry Andric return Err; 812*bdd1243dSDimitry Andric return Error::success(); 813*bdd1243dSDimitry Andric } 814*bdd1243dSDimitry Andric }; 815*bdd1243dSDimitry Andric } // namespace 816*bdd1243dSDimitry Andric 817*bdd1243dSDimitry Andric /// Return an appropriate object file handler. We use the specific object 818*bdd1243dSDimitry Andric /// handler if we know how to deal with that format, otherwise we use a default 819*bdd1243dSDimitry Andric /// binary file handler. 820*bdd1243dSDimitry Andric static std::unique_ptr<FileHandler> 821*bdd1243dSDimitry Andric CreateObjectFileHandler(MemoryBuffer &FirstInput, 822*bdd1243dSDimitry Andric const OffloadBundlerConfig &BundlerConfig) { 823*bdd1243dSDimitry Andric // Check if the input file format is one that we know how to deal with. 824*bdd1243dSDimitry Andric Expected<std::unique_ptr<Binary>> BinaryOrErr = createBinary(FirstInput); 825*bdd1243dSDimitry Andric 826*bdd1243dSDimitry Andric // We only support regular object files. If failed to open the input as a 827*bdd1243dSDimitry Andric // known binary or this is not an object file use the default binary handler. 828*bdd1243dSDimitry Andric if (errorToBool(BinaryOrErr.takeError()) || !isa<ObjectFile>(*BinaryOrErr)) 829*bdd1243dSDimitry Andric return std::make_unique<BinaryFileHandler>(BundlerConfig); 830*bdd1243dSDimitry Andric 831*bdd1243dSDimitry Andric // Otherwise create an object file handler. The handler will be owned by the 832*bdd1243dSDimitry Andric // client of this function. 833*bdd1243dSDimitry Andric return std::make_unique<ObjectFileHandler>( 834*bdd1243dSDimitry Andric std::unique_ptr<ObjectFile>(cast<ObjectFile>(BinaryOrErr->release())), 835*bdd1243dSDimitry Andric BundlerConfig); 836*bdd1243dSDimitry Andric } 837*bdd1243dSDimitry Andric 838*bdd1243dSDimitry Andric /// Return an appropriate handler given the input files and options. 839*bdd1243dSDimitry Andric static Expected<std::unique_ptr<FileHandler>> 840*bdd1243dSDimitry Andric CreateFileHandler(MemoryBuffer &FirstInput, 841*bdd1243dSDimitry Andric const OffloadBundlerConfig &BundlerConfig) { 842*bdd1243dSDimitry Andric std::string FilesType = BundlerConfig.FilesType; 843*bdd1243dSDimitry Andric 844*bdd1243dSDimitry Andric if (FilesType == "i") 845*bdd1243dSDimitry Andric return std::make_unique<TextFileHandler>(/*Comment=*/"//"); 846*bdd1243dSDimitry Andric if (FilesType == "ii") 847*bdd1243dSDimitry Andric return std::make_unique<TextFileHandler>(/*Comment=*/"//"); 848*bdd1243dSDimitry Andric if (FilesType == "cui") 849*bdd1243dSDimitry Andric return std::make_unique<TextFileHandler>(/*Comment=*/"//"); 850*bdd1243dSDimitry Andric if (FilesType == "hipi") 851*bdd1243dSDimitry Andric return std::make_unique<TextFileHandler>(/*Comment=*/"//"); 852*bdd1243dSDimitry Andric // TODO: `.d` should be eventually removed once `-M` and its variants are 853*bdd1243dSDimitry Andric // handled properly in offload compilation. 854*bdd1243dSDimitry Andric if (FilesType == "d") 855*bdd1243dSDimitry Andric return std::make_unique<TextFileHandler>(/*Comment=*/"#"); 856*bdd1243dSDimitry Andric if (FilesType == "ll") 857*bdd1243dSDimitry Andric return std::make_unique<TextFileHandler>(/*Comment=*/";"); 858*bdd1243dSDimitry Andric if (FilesType == "bc") 859*bdd1243dSDimitry Andric return std::make_unique<BinaryFileHandler>(BundlerConfig); 860*bdd1243dSDimitry Andric if (FilesType == "s") 861*bdd1243dSDimitry Andric return std::make_unique<TextFileHandler>(/*Comment=*/"#"); 862*bdd1243dSDimitry Andric if (FilesType == "o") 863*bdd1243dSDimitry Andric return CreateObjectFileHandler(FirstInput, BundlerConfig); 864*bdd1243dSDimitry Andric if (FilesType == "a") 865*bdd1243dSDimitry Andric return CreateObjectFileHandler(FirstInput, BundlerConfig); 866*bdd1243dSDimitry Andric if (FilesType == "gch") 867*bdd1243dSDimitry Andric return std::make_unique<BinaryFileHandler>(BundlerConfig); 868*bdd1243dSDimitry Andric if (FilesType == "ast") 869*bdd1243dSDimitry Andric return std::make_unique<BinaryFileHandler>(BundlerConfig); 870*bdd1243dSDimitry Andric 871*bdd1243dSDimitry Andric return createStringError(errc::invalid_argument, 872*bdd1243dSDimitry Andric "'" + FilesType + "': invalid file type specified"); 873*bdd1243dSDimitry Andric } 874*bdd1243dSDimitry Andric 875*bdd1243dSDimitry Andric // List bundle IDs. Return true if an error was found. 876*bdd1243dSDimitry Andric Error OffloadBundler::ListBundleIDsInFile( 877*bdd1243dSDimitry Andric StringRef InputFileName, const OffloadBundlerConfig &BundlerConfig) { 878*bdd1243dSDimitry Andric // Open Input file. 879*bdd1243dSDimitry Andric ErrorOr<std::unique_ptr<MemoryBuffer>> CodeOrErr = 880*bdd1243dSDimitry Andric MemoryBuffer::getFileOrSTDIN(InputFileName); 881*bdd1243dSDimitry Andric if (std::error_code EC = CodeOrErr.getError()) 882*bdd1243dSDimitry Andric return createFileError(InputFileName, EC); 883*bdd1243dSDimitry Andric 884*bdd1243dSDimitry Andric MemoryBuffer &Input = **CodeOrErr; 885*bdd1243dSDimitry Andric 886*bdd1243dSDimitry Andric // Select the right files handler. 887*bdd1243dSDimitry Andric Expected<std::unique_ptr<FileHandler>> FileHandlerOrErr = 888*bdd1243dSDimitry Andric CreateFileHandler(Input, BundlerConfig); 889*bdd1243dSDimitry Andric if (!FileHandlerOrErr) 890*bdd1243dSDimitry Andric return FileHandlerOrErr.takeError(); 891*bdd1243dSDimitry Andric 892*bdd1243dSDimitry Andric std::unique_ptr<FileHandler> &FH = *FileHandlerOrErr; 893*bdd1243dSDimitry Andric assert(FH); 894*bdd1243dSDimitry Andric return FH->listBundleIDs(Input); 895*bdd1243dSDimitry Andric } 896*bdd1243dSDimitry Andric 897*bdd1243dSDimitry Andric /// Bundle the files. Return true if an error was found. 898*bdd1243dSDimitry Andric Error OffloadBundler::BundleFiles() { 899*bdd1243dSDimitry Andric std::error_code EC; 900*bdd1243dSDimitry Andric 901*bdd1243dSDimitry Andric // Create output file. 902*bdd1243dSDimitry Andric raw_fd_ostream OutputFile(BundlerConfig.OutputFileNames.front(), EC, 903*bdd1243dSDimitry Andric sys::fs::OF_None); 904*bdd1243dSDimitry Andric if (EC) 905*bdd1243dSDimitry Andric return createFileError(BundlerConfig.OutputFileNames.front(), EC); 906*bdd1243dSDimitry Andric 907*bdd1243dSDimitry Andric // Open input files. 908*bdd1243dSDimitry Andric SmallVector<std::unique_ptr<MemoryBuffer>, 8u> InputBuffers; 909*bdd1243dSDimitry Andric InputBuffers.reserve(BundlerConfig.InputFileNames.size()); 910*bdd1243dSDimitry Andric for (auto &I : BundlerConfig.InputFileNames) { 911*bdd1243dSDimitry Andric ErrorOr<std::unique_ptr<MemoryBuffer>> CodeOrErr = 912*bdd1243dSDimitry Andric MemoryBuffer::getFileOrSTDIN(I); 913*bdd1243dSDimitry Andric if (std::error_code EC = CodeOrErr.getError()) 914*bdd1243dSDimitry Andric return createFileError(I, EC); 915*bdd1243dSDimitry Andric InputBuffers.emplace_back(std::move(*CodeOrErr)); 916*bdd1243dSDimitry Andric } 917*bdd1243dSDimitry Andric 918*bdd1243dSDimitry Andric // Get the file handler. We use the host buffer as reference. 919*bdd1243dSDimitry Andric assert((BundlerConfig.HostInputIndex != ~0u || BundlerConfig.AllowNoHost) && 920*bdd1243dSDimitry Andric "Host input index undefined??"); 921*bdd1243dSDimitry Andric Expected<std::unique_ptr<FileHandler>> FileHandlerOrErr = CreateFileHandler( 922*bdd1243dSDimitry Andric *InputBuffers[BundlerConfig.AllowNoHost ? 0 923*bdd1243dSDimitry Andric : BundlerConfig.HostInputIndex], 924*bdd1243dSDimitry Andric BundlerConfig); 925*bdd1243dSDimitry Andric if (!FileHandlerOrErr) 926*bdd1243dSDimitry Andric return FileHandlerOrErr.takeError(); 927*bdd1243dSDimitry Andric 928*bdd1243dSDimitry Andric std::unique_ptr<FileHandler> &FH = *FileHandlerOrErr; 929*bdd1243dSDimitry Andric assert(FH); 930*bdd1243dSDimitry Andric 931*bdd1243dSDimitry Andric // Write header. 932*bdd1243dSDimitry Andric if (Error Err = FH->WriteHeader(OutputFile, InputBuffers)) 933*bdd1243dSDimitry Andric return Err; 934*bdd1243dSDimitry Andric 935*bdd1243dSDimitry Andric // Write all bundles along with the start/end markers. If an error was found 936*bdd1243dSDimitry Andric // writing the end of the bundle component, abort the bundle writing. 937*bdd1243dSDimitry Andric auto Input = InputBuffers.begin(); 938*bdd1243dSDimitry Andric for (auto &Triple : BundlerConfig.TargetNames) { 939*bdd1243dSDimitry Andric if (Error Err = FH->WriteBundleStart(OutputFile, Triple)) 940*bdd1243dSDimitry Andric return Err; 941*bdd1243dSDimitry Andric if (Error Err = FH->WriteBundle(OutputFile, **Input)) 942*bdd1243dSDimitry Andric return Err; 943*bdd1243dSDimitry Andric if (Error Err = FH->WriteBundleEnd(OutputFile, Triple)) 944*bdd1243dSDimitry Andric return Err; 945*bdd1243dSDimitry Andric ++Input; 946*bdd1243dSDimitry Andric } 947*bdd1243dSDimitry Andric return Error::success(); 948*bdd1243dSDimitry Andric } 949*bdd1243dSDimitry Andric 950*bdd1243dSDimitry Andric // Unbundle the files. Return true if an error was found. 951*bdd1243dSDimitry Andric Error OffloadBundler::UnbundleFiles() { 952*bdd1243dSDimitry Andric // Open Input file. 953*bdd1243dSDimitry Andric ErrorOr<std::unique_ptr<MemoryBuffer>> CodeOrErr = 954*bdd1243dSDimitry Andric MemoryBuffer::getFileOrSTDIN(BundlerConfig.InputFileNames.front()); 955*bdd1243dSDimitry Andric if (std::error_code EC = CodeOrErr.getError()) 956*bdd1243dSDimitry Andric return createFileError(BundlerConfig.InputFileNames.front(), EC); 957*bdd1243dSDimitry Andric 958*bdd1243dSDimitry Andric MemoryBuffer &Input = **CodeOrErr; 959*bdd1243dSDimitry Andric 960*bdd1243dSDimitry Andric // Select the right files handler. 961*bdd1243dSDimitry Andric Expected<std::unique_ptr<FileHandler>> FileHandlerOrErr = 962*bdd1243dSDimitry Andric CreateFileHandler(Input, BundlerConfig); 963*bdd1243dSDimitry Andric if (!FileHandlerOrErr) 964*bdd1243dSDimitry Andric return FileHandlerOrErr.takeError(); 965*bdd1243dSDimitry Andric 966*bdd1243dSDimitry Andric std::unique_ptr<FileHandler> &FH = *FileHandlerOrErr; 967*bdd1243dSDimitry Andric assert(FH); 968*bdd1243dSDimitry Andric 969*bdd1243dSDimitry Andric // Read the header of the bundled file. 970*bdd1243dSDimitry Andric if (Error Err = FH->ReadHeader(Input)) 971*bdd1243dSDimitry Andric return Err; 972*bdd1243dSDimitry Andric 973*bdd1243dSDimitry Andric // Create a work list that consist of the map triple/output file. 974*bdd1243dSDimitry Andric StringMap<StringRef> Worklist; 975*bdd1243dSDimitry Andric auto Output = BundlerConfig.OutputFileNames.begin(); 976*bdd1243dSDimitry Andric for (auto &Triple : BundlerConfig.TargetNames) { 977*bdd1243dSDimitry Andric Worklist[Triple] = *Output; 978*bdd1243dSDimitry Andric ++Output; 979*bdd1243dSDimitry Andric } 980*bdd1243dSDimitry Andric 981*bdd1243dSDimitry Andric // Read all the bundles that are in the work list. If we find no bundles we 982*bdd1243dSDimitry Andric // assume the file is meant for the host target. 983*bdd1243dSDimitry Andric bool FoundHostBundle = false; 984*bdd1243dSDimitry Andric while (!Worklist.empty()) { 985*bdd1243dSDimitry Andric Expected<std::optional<StringRef>> CurTripleOrErr = 986*bdd1243dSDimitry Andric FH->ReadBundleStart(Input); 987*bdd1243dSDimitry Andric if (!CurTripleOrErr) 988*bdd1243dSDimitry Andric return CurTripleOrErr.takeError(); 989*bdd1243dSDimitry Andric 990*bdd1243dSDimitry Andric // We don't have more bundles. 991*bdd1243dSDimitry Andric if (!*CurTripleOrErr) 992*bdd1243dSDimitry Andric break; 993*bdd1243dSDimitry Andric 994*bdd1243dSDimitry Andric StringRef CurTriple = **CurTripleOrErr; 995*bdd1243dSDimitry Andric assert(!CurTriple.empty()); 996*bdd1243dSDimitry Andric 997*bdd1243dSDimitry Andric auto Output = Worklist.begin(); 998*bdd1243dSDimitry Andric for (auto E = Worklist.end(); Output != E; Output++) { 999*bdd1243dSDimitry Andric if (isCodeObjectCompatible( 1000*bdd1243dSDimitry Andric OffloadTargetInfo(CurTriple, BundlerConfig), 1001*bdd1243dSDimitry Andric OffloadTargetInfo((*Output).first(), BundlerConfig))) { 1002*bdd1243dSDimitry Andric break; 1003*bdd1243dSDimitry Andric } 1004*bdd1243dSDimitry Andric } 1005*bdd1243dSDimitry Andric 1006*bdd1243dSDimitry Andric if (Output == Worklist.end()) 1007*bdd1243dSDimitry Andric continue; 1008*bdd1243dSDimitry Andric // Check if the output file can be opened and copy the bundle to it. 1009*bdd1243dSDimitry Andric std::error_code EC; 1010*bdd1243dSDimitry Andric raw_fd_ostream OutputFile((*Output).second, EC, sys::fs::OF_None); 1011*bdd1243dSDimitry Andric if (EC) 1012*bdd1243dSDimitry Andric return createFileError((*Output).second, EC); 1013*bdd1243dSDimitry Andric if (Error Err = FH->ReadBundle(OutputFile, Input)) 1014*bdd1243dSDimitry Andric return Err; 1015*bdd1243dSDimitry Andric if (Error Err = FH->ReadBundleEnd(Input)) 1016*bdd1243dSDimitry Andric return Err; 1017*bdd1243dSDimitry Andric Worklist.erase(Output); 1018*bdd1243dSDimitry Andric 1019*bdd1243dSDimitry Andric // Record if we found the host bundle. 1020*bdd1243dSDimitry Andric auto OffloadInfo = OffloadTargetInfo(CurTriple, BundlerConfig); 1021*bdd1243dSDimitry Andric if (OffloadInfo.hasHostKind()) 1022*bdd1243dSDimitry Andric FoundHostBundle = true; 1023*bdd1243dSDimitry Andric } 1024*bdd1243dSDimitry Andric 1025*bdd1243dSDimitry Andric if (!BundlerConfig.AllowMissingBundles && !Worklist.empty()) { 1026*bdd1243dSDimitry Andric std::string ErrMsg = "Can't find bundles for"; 1027*bdd1243dSDimitry Andric std::set<StringRef> Sorted; 1028*bdd1243dSDimitry Andric for (auto &E : Worklist) 1029*bdd1243dSDimitry Andric Sorted.insert(E.first()); 1030*bdd1243dSDimitry Andric unsigned I = 0; 1031*bdd1243dSDimitry Andric unsigned Last = Sorted.size() - 1; 1032*bdd1243dSDimitry Andric for (auto &E : Sorted) { 1033*bdd1243dSDimitry Andric if (I != 0 && Last > 1) 1034*bdd1243dSDimitry Andric ErrMsg += ","; 1035*bdd1243dSDimitry Andric ErrMsg += " "; 1036*bdd1243dSDimitry Andric if (I == Last && I != 0) 1037*bdd1243dSDimitry Andric ErrMsg += "and "; 1038*bdd1243dSDimitry Andric ErrMsg += E.str(); 1039*bdd1243dSDimitry Andric ++I; 1040*bdd1243dSDimitry Andric } 1041*bdd1243dSDimitry Andric return createStringError(inconvertibleErrorCode(), ErrMsg); 1042*bdd1243dSDimitry Andric } 1043*bdd1243dSDimitry Andric 1044*bdd1243dSDimitry Andric // If no bundles were found, assume the input file is the host bundle and 1045*bdd1243dSDimitry Andric // create empty files for the remaining targets. 1046*bdd1243dSDimitry Andric if (Worklist.size() == BundlerConfig.TargetNames.size()) { 1047*bdd1243dSDimitry Andric for (auto &E : Worklist) { 1048*bdd1243dSDimitry Andric std::error_code EC; 1049*bdd1243dSDimitry Andric raw_fd_ostream OutputFile(E.second, EC, sys::fs::OF_None); 1050*bdd1243dSDimitry Andric if (EC) 1051*bdd1243dSDimitry Andric return createFileError(E.second, EC); 1052*bdd1243dSDimitry Andric 1053*bdd1243dSDimitry Andric // If this entry has a host kind, copy the input file to the output file. 1054*bdd1243dSDimitry Andric auto OffloadInfo = OffloadTargetInfo(E.getKey(), BundlerConfig); 1055*bdd1243dSDimitry Andric if (OffloadInfo.hasHostKind()) 1056*bdd1243dSDimitry Andric OutputFile.write(Input.getBufferStart(), Input.getBufferSize()); 1057*bdd1243dSDimitry Andric } 1058*bdd1243dSDimitry Andric return Error::success(); 1059*bdd1243dSDimitry Andric } 1060*bdd1243dSDimitry Andric 1061*bdd1243dSDimitry Andric // If we found elements, we emit an error if none of those were for the host 1062*bdd1243dSDimitry Andric // in case host bundle name was provided in command line. 1063*bdd1243dSDimitry Andric if (!(FoundHostBundle || BundlerConfig.HostInputIndex == ~0u || 1064*bdd1243dSDimitry Andric BundlerConfig.AllowMissingBundles)) 1065*bdd1243dSDimitry Andric return createStringError(inconvertibleErrorCode(), 1066*bdd1243dSDimitry Andric "Can't find bundle for the host target"); 1067*bdd1243dSDimitry Andric 1068*bdd1243dSDimitry Andric // If we still have any elements in the worklist, create empty files for them. 1069*bdd1243dSDimitry Andric for (auto &E : Worklist) { 1070*bdd1243dSDimitry Andric std::error_code EC; 1071*bdd1243dSDimitry Andric raw_fd_ostream OutputFile(E.second, EC, sys::fs::OF_None); 1072*bdd1243dSDimitry Andric if (EC) 1073*bdd1243dSDimitry Andric return createFileError(E.second, EC); 1074*bdd1243dSDimitry Andric } 1075*bdd1243dSDimitry Andric 1076*bdd1243dSDimitry Andric return Error::success(); 1077*bdd1243dSDimitry Andric } 1078*bdd1243dSDimitry Andric 1079*bdd1243dSDimitry Andric static Archive::Kind getDefaultArchiveKindForHost() { 1080*bdd1243dSDimitry Andric return Triple(sys::getDefaultTargetTriple()).isOSDarwin() ? Archive::K_DARWIN 1081*bdd1243dSDimitry Andric : Archive::K_GNU; 1082*bdd1243dSDimitry Andric } 1083*bdd1243dSDimitry Andric 1084*bdd1243dSDimitry Andric /// @brief Computes a list of targets among all given targets which are 1085*bdd1243dSDimitry Andric /// compatible with this code object 1086*bdd1243dSDimitry Andric /// @param [in] CodeObjectInfo Code Object 1087*bdd1243dSDimitry Andric /// @param [out] CompatibleTargets List of all compatible targets among all 1088*bdd1243dSDimitry Andric /// given targets 1089*bdd1243dSDimitry Andric /// @return false, if no compatible target is found. 1090*bdd1243dSDimitry Andric static bool 1091*bdd1243dSDimitry Andric getCompatibleOffloadTargets(OffloadTargetInfo &CodeObjectInfo, 1092*bdd1243dSDimitry Andric SmallVectorImpl<StringRef> &CompatibleTargets, 1093*bdd1243dSDimitry Andric const OffloadBundlerConfig &BundlerConfig) { 1094*bdd1243dSDimitry Andric if (!CompatibleTargets.empty()) { 1095*bdd1243dSDimitry Andric DEBUG_WITH_TYPE("CodeObjectCompatibility", 1096*bdd1243dSDimitry Andric dbgs() << "CompatibleTargets list should be empty\n"); 1097*bdd1243dSDimitry Andric return false; 1098*bdd1243dSDimitry Andric } 1099*bdd1243dSDimitry Andric for (auto &Target : BundlerConfig.TargetNames) { 1100*bdd1243dSDimitry Andric auto TargetInfo = OffloadTargetInfo(Target, BundlerConfig); 1101*bdd1243dSDimitry Andric if (isCodeObjectCompatible(CodeObjectInfo, TargetInfo)) 1102*bdd1243dSDimitry Andric CompatibleTargets.push_back(Target); 1103*bdd1243dSDimitry Andric } 1104*bdd1243dSDimitry Andric return !CompatibleTargets.empty(); 1105*bdd1243dSDimitry Andric } 1106*bdd1243dSDimitry Andric 1107*bdd1243dSDimitry Andric /// UnbundleArchive takes an archive file (".a") as input containing bundled 1108*bdd1243dSDimitry Andric /// code object files, and a list of offload targets (not host), and extracts 1109*bdd1243dSDimitry Andric /// the code objects into a new archive file for each offload target. Each 1110*bdd1243dSDimitry Andric /// resulting archive file contains all code object files corresponding to that 1111*bdd1243dSDimitry Andric /// particular offload target. The created archive file does not 1112*bdd1243dSDimitry Andric /// contain an index of the symbols and code object files are named as 1113*bdd1243dSDimitry Andric /// <<Parent Bundle Name>-<CodeObject's GPUArch>>, with ':' replaced with '_'. 1114*bdd1243dSDimitry Andric Error OffloadBundler::UnbundleArchive() { 1115*bdd1243dSDimitry Andric std::vector<std::unique_ptr<MemoryBuffer>> ArchiveBuffers; 1116*bdd1243dSDimitry Andric 1117*bdd1243dSDimitry Andric /// Map of target names with list of object files that will form the device 1118*bdd1243dSDimitry Andric /// specific archive for that target 1119*bdd1243dSDimitry Andric StringMap<std::vector<NewArchiveMember>> OutputArchivesMap; 1120*bdd1243dSDimitry Andric 1121*bdd1243dSDimitry Andric // Map of target names and output archive filenames 1122*bdd1243dSDimitry Andric StringMap<StringRef> TargetOutputFileNameMap; 1123*bdd1243dSDimitry Andric 1124*bdd1243dSDimitry Andric auto Output = BundlerConfig.OutputFileNames.begin(); 1125*bdd1243dSDimitry Andric for (auto &Target : BundlerConfig.TargetNames) { 1126*bdd1243dSDimitry Andric TargetOutputFileNameMap[Target] = *Output; 1127*bdd1243dSDimitry Andric ++Output; 1128*bdd1243dSDimitry Andric } 1129*bdd1243dSDimitry Andric 1130*bdd1243dSDimitry Andric StringRef IFName = BundlerConfig.InputFileNames.front(); 1131*bdd1243dSDimitry Andric 1132*bdd1243dSDimitry Andric ErrorOr<std::unique_ptr<MemoryBuffer>> BufOrErr = 1133*bdd1243dSDimitry Andric MemoryBuffer::getFileOrSTDIN(IFName, true, false); 1134*bdd1243dSDimitry Andric if (std::error_code EC = BufOrErr.getError()) 1135*bdd1243dSDimitry Andric return createFileError(BundlerConfig.InputFileNames.front(), EC); 1136*bdd1243dSDimitry Andric 1137*bdd1243dSDimitry Andric ArchiveBuffers.push_back(std::move(*BufOrErr)); 1138*bdd1243dSDimitry Andric Expected<std::unique_ptr<llvm::object::Archive>> LibOrErr = 1139*bdd1243dSDimitry Andric Archive::create(ArchiveBuffers.back()->getMemBufferRef()); 1140*bdd1243dSDimitry Andric if (!LibOrErr) 1141*bdd1243dSDimitry Andric return LibOrErr.takeError(); 1142*bdd1243dSDimitry Andric 1143*bdd1243dSDimitry Andric auto Archive = std::move(*LibOrErr); 1144*bdd1243dSDimitry Andric 1145*bdd1243dSDimitry Andric Error ArchiveErr = Error::success(); 1146*bdd1243dSDimitry Andric auto ChildEnd = Archive->child_end(); 1147*bdd1243dSDimitry Andric 1148*bdd1243dSDimitry Andric /// Iterate over all bundled code object files in the input archive. 1149*bdd1243dSDimitry Andric for (auto ArchiveIter = Archive->child_begin(ArchiveErr); 1150*bdd1243dSDimitry Andric ArchiveIter != ChildEnd; ++ArchiveIter) { 1151*bdd1243dSDimitry Andric if (ArchiveErr) 1152*bdd1243dSDimitry Andric return ArchiveErr; 1153*bdd1243dSDimitry Andric auto ArchiveChildNameOrErr = (*ArchiveIter).getName(); 1154*bdd1243dSDimitry Andric if (!ArchiveChildNameOrErr) 1155*bdd1243dSDimitry Andric return ArchiveChildNameOrErr.takeError(); 1156*bdd1243dSDimitry Andric 1157*bdd1243dSDimitry Andric StringRef BundledObjectFile = sys::path::filename(*ArchiveChildNameOrErr); 1158*bdd1243dSDimitry Andric 1159*bdd1243dSDimitry Andric auto CodeObjectBufferRefOrErr = (*ArchiveIter).getMemoryBufferRef(); 1160*bdd1243dSDimitry Andric if (!CodeObjectBufferRefOrErr) 1161*bdd1243dSDimitry Andric return CodeObjectBufferRefOrErr.takeError(); 1162*bdd1243dSDimitry Andric 1163*bdd1243dSDimitry Andric auto CodeObjectBuffer = 1164*bdd1243dSDimitry Andric MemoryBuffer::getMemBuffer(*CodeObjectBufferRefOrErr, false); 1165*bdd1243dSDimitry Andric 1166*bdd1243dSDimitry Andric Expected<std::unique_ptr<FileHandler>> FileHandlerOrErr = 1167*bdd1243dSDimitry Andric CreateFileHandler(*CodeObjectBuffer, BundlerConfig); 1168*bdd1243dSDimitry Andric if (!FileHandlerOrErr) 1169*bdd1243dSDimitry Andric return FileHandlerOrErr.takeError(); 1170*bdd1243dSDimitry Andric 1171*bdd1243dSDimitry Andric std::unique_ptr<FileHandler> &FileHandler = *FileHandlerOrErr; 1172*bdd1243dSDimitry Andric assert(FileHandler && 1173*bdd1243dSDimitry Andric "FileHandle creation failed for file in the archive!"); 1174*bdd1243dSDimitry Andric 1175*bdd1243dSDimitry Andric if (Error ReadErr = FileHandler->ReadHeader(*CodeObjectBuffer)) 1176*bdd1243dSDimitry Andric return ReadErr; 1177*bdd1243dSDimitry Andric 1178*bdd1243dSDimitry Andric Expected<std::optional<StringRef>> CurBundleIDOrErr = 1179*bdd1243dSDimitry Andric FileHandler->ReadBundleStart(*CodeObjectBuffer); 1180*bdd1243dSDimitry Andric if (!CurBundleIDOrErr) 1181*bdd1243dSDimitry Andric return CurBundleIDOrErr.takeError(); 1182*bdd1243dSDimitry Andric 1183*bdd1243dSDimitry Andric std::optional<StringRef> OptionalCurBundleID = *CurBundleIDOrErr; 1184*bdd1243dSDimitry Andric // No device code in this child, skip. 1185*bdd1243dSDimitry Andric if (!OptionalCurBundleID) 1186*bdd1243dSDimitry Andric continue; 1187*bdd1243dSDimitry Andric StringRef CodeObject = *OptionalCurBundleID; 1188*bdd1243dSDimitry Andric 1189*bdd1243dSDimitry Andric // Process all bundle entries (CodeObjects) found in this child of input 1190*bdd1243dSDimitry Andric // archive. 1191*bdd1243dSDimitry Andric while (!CodeObject.empty()) { 1192*bdd1243dSDimitry Andric SmallVector<StringRef> CompatibleTargets; 1193*bdd1243dSDimitry Andric auto CodeObjectInfo = OffloadTargetInfo(CodeObject, BundlerConfig); 1194*bdd1243dSDimitry Andric if (CodeObjectInfo.hasHostKind()) { 1195*bdd1243dSDimitry Andric // Do nothing, we don't extract host code yet. 1196*bdd1243dSDimitry Andric } else if (getCompatibleOffloadTargets(CodeObjectInfo, CompatibleTargets, 1197*bdd1243dSDimitry Andric BundlerConfig)) { 1198*bdd1243dSDimitry Andric std::string BundleData; 1199*bdd1243dSDimitry Andric raw_string_ostream DataStream(BundleData); 1200*bdd1243dSDimitry Andric if (Error Err = FileHandler->ReadBundle(DataStream, *CodeObjectBuffer)) 1201*bdd1243dSDimitry Andric return Err; 1202*bdd1243dSDimitry Andric 1203*bdd1243dSDimitry Andric for (auto &CompatibleTarget : CompatibleTargets) { 1204*bdd1243dSDimitry Andric SmallString<128> BundledObjectFileName; 1205*bdd1243dSDimitry Andric BundledObjectFileName.assign(BundledObjectFile); 1206*bdd1243dSDimitry Andric auto OutputBundleName = 1207*bdd1243dSDimitry Andric Twine(llvm::sys::path::stem(BundledObjectFileName) + "-" + 1208*bdd1243dSDimitry Andric CodeObject + 1209*bdd1243dSDimitry Andric getDeviceLibraryFileName(BundledObjectFileName, 1210*bdd1243dSDimitry Andric CodeObjectInfo.TargetID)) 1211*bdd1243dSDimitry Andric .str(); 1212*bdd1243dSDimitry Andric // Replace ':' in optional target feature list with '_' to ensure 1213*bdd1243dSDimitry Andric // cross-platform validity. 1214*bdd1243dSDimitry Andric std::replace(OutputBundleName.begin(), OutputBundleName.end(), ':', 1215*bdd1243dSDimitry Andric '_'); 1216*bdd1243dSDimitry Andric 1217*bdd1243dSDimitry Andric std::unique_ptr<MemoryBuffer> MemBuf = MemoryBuffer::getMemBufferCopy( 1218*bdd1243dSDimitry Andric DataStream.str(), OutputBundleName); 1219*bdd1243dSDimitry Andric ArchiveBuffers.push_back(std::move(MemBuf)); 1220*bdd1243dSDimitry Andric llvm::MemoryBufferRef MemBufRef = 1221*bdd1243dSDimitry Andric MemoryBufferRef(*(ArchiveBuffers.back())); 1222*bdd1243dSDimitry Andric 1223*bdd1243dSDimitry Andric // For inserting <CompatibleTarget, list<CodeObject>> entry in 1224*bdd1243dSDimitry Andric // OutputArchivesMap. 1225*bdd1243dSDimitry Andric if (OutputArchivesMap.find(CompatibleTarget) == 1226*bdd1243dSDimitry Andric OutputArchivesMap.end()) { 1227*bdd1243dSDimitry Andric 1228*bdd1243dSDimitry Andric std::vector<NewArchiveMember> ArchiveMembers; 1229*bdd1243dSDimitry Andric ArchiveMembers.push_back(NewArchiveMember(MemBufRef)); 1230*bdd1243dSDimitry Andric OutputArchivesMap.insert_or_assign(CompatibleTarget, 1231*bdd1243dSDimitry Andric std::move(ArchiveMembers)); 1232*bdd1243dSDimitry Andric } else { 1233*bdd1243dSDimitry Andric OutputArchivesMap[CompatibleTarget].push_back( 1234*bdd1243dSDimitry Andric NewArchiveMember(MemBufRef)); 1235*bdd1243dSDimitry Andric } 1236*bdd1243dSDimitry Andric } 1237*bdd1243dSDimitry Andric } 1238*bdd1243dSDimitry Andric 1239*bdd1243dSDimitry Andric if (Error Err = FileHandler->ReadBundleEnd(*CodeObjectBuffer)) 1240*bdd1243dSDimitry Andric return Err; 1241*bdd1243dSDimitry Andric 1242*bdd1243dSDimitry Andric Expected<std::optional<StringRef>> NextTripleOrErr = 1243*bdd1243dSDimitry Andric FileHandler->ReadBundleStart(*CodeObjectBuffer); 1244*bdd1243dSDimitry Andric if (!NextTripleOrErr) 1245*bdd1243dSDimitry Andric return NextTripleOrErr.takeError(); 1246*bdd1243dSDimitry Andric 1247*bdd1243dSDimitry Andric CodeObject = ((*NextTripleOrErr).has_value()) ? **NextTripleOrErr : ""; 1248*bdd1243dSDimitry Andric } // End of processing of all bundle entries of this child of input archive. 1249*bdd1243dSDimitry Andric } // End of while over children of input archive. 1250*bdd1243dSDimitry Andric 1251*bdd1243dSDimitry Andric assert(!ArchiveErr && "Error occurred while reading archive!"); 1252*bdd1243dSDimitry Andric 1253*bdd1243dSDimitry Andric /// Write out an archive for each target 1254*bdd1243dSDimitry Andric for (auto &Target : BundlerConfig.TargetNames) { 1255*bdd1243dSDimitry Andric StringRef FileName = TargetOutputFileNameMap[Target]; 1256*bdd1243dSDimitry Andric StringMapIterator<std::vector<llvm::NewArchiveMember>> CurArchiveMembers = 1257*bdd1243dSDimitry Andric OutputArchivesMap.find(Target); 1258*bdd1243dSDimitry Andric if (CurArchiveMembers != OutputArchivesMap.end()) { 1259*bdd1243dSDimitry Andric if (Error WriteErr = writeArchive(FileName, CurArchiveMembers->getValue(), 1260*bdd1243dSDimitry Andric true, getDefaultArchiveKindForHost(), 1261*bdd1243dSDimitry Andric true, false, nullptr)) 1262*bdd1243dSDimitry Andric return WriteErr; 1263*bdd1243dSDimitry Andric } else if (!BundlerConfig.AllowMissingBundles) { 1264*bdd1243dSDimitry Andric std::string ErrMsg = 1265*bdd1243dSDimitry Andric Twine("no compatible code object found for the target '" + Target + 1266*bdd1243dSDimitry Andric "' in heterogeneous archive library: " + IFName) 1267*bdd1243dSDimitry Andric .str(); 1268*bdd1243dSDimitry Andric return createStringError(inconvertibleErrorCode(), ErrMsg); 1269*bdd1243dSDimitry Andric } else { // Create an empty archive file if no compatible code object is 1270*bdd1243dSDimitry Andric // found and "allow-missing-bundles" is enabled. It ensures that 1271*bdd1243dSDimitry Andric // the linker using output of this step doesn't complain about 1272*bdd1243dSDimitry Andric // the missing input file. 1273*bdd1243dSDimitry Andric std::vector<llvm::NewArchiveMember> EmptyArchive; 1274*bdd1243dSDimitry Andric EmptyArchive.clear(); 1275*bdd1243dSDimitry Andric if (Error WriteErr = writeArchive(FileName, EmptyArchive, true, 1276*bdd1243dSDimitry Andric getDefaultArchiveKindForHost(), true, 1277*bdd1243dSDimitry Andric false, nullptr)) 1278*bdd1243dSDimitry Andric return WriteErr; 1279*bdd1243dSDimitry Andric } 1280*bdd1243dSDimitry Andric } 1281*bdd1243dSDimitry Andric 1282*bdd1243dSDimitry Andric return Error::success(); 1283*bdd1243dSDimitry Andric } 1284