10f3f357eSJacob Lambert //===- OffloadBundler.cpp - File Bundling and Unbundling ------------------===// 20f3f357eSJacob Lambert // 30f3f357eSJacob Lambert // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 40f3f357eSJacob Lambert // See https://llvm.org/LICENSE.txt for license information. 50f3f357eSJacob Lambert // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 60f3f357eSJacob Lambert // 70f3f357eSJacob Lambert //===----------------------------------------------------------------------===// 80f3f357eSJacob Lambert /// 90f3f357eSJacob Lambert /// \file 100f3f357eSJacob Lambert /// This file implements an offload bundling API that bundles different files 110f3f357eSJacob Lambert /// that relate with the same source code but different targets into a single 120f3f357eSJacob Lambert /// one. Also the implements the opposite functionality, i.e. unbundle files 130f3f357eSJacob Lambert /// previous created by this API. 140f3f357eSJacob Lambert /// 150f3f357eSJacob Lambert //===----------------------------------------------------------------------===// 160f3f357eSJacob Lambert 175b11caa8Sraghavmedicherla #include "clang/Driver/OffloadBundler.h" 180f3f357eSJacob Lambert #include "clang/Basic/Cuda.h" 19844b84afSYaxun (Sam) Liu #include "clang/Basic/TargetID.h" 200f3f357eSJacob Lambert #include "clang/Basic/Version.h" 210f3f357eSJacob Lambert #include "llvm/ADT/ArrayRef.h" 220f3f357eSJacob Lambert #include "llvm/ADT/SmallString.h" 230f3f357eSJacob Lambert #include "llvm/ADT/SmallVector.h" 240f3f357eSJacob Lambert #include "llvm/ADT/StringMap.h" 250f3f357eSJacob Lambert #include "llvm/ADT/StringRef.h" 260f3f357eSJacob Lambert #include "llvm/ADT/Triple.h" 270f3f357eSJacob Lambert #include "llvm/Object/Archive.h" 280f3f357eSJacob Lambert #include "llvm/Object/ArchiveWriter.h" 290f3f357eSJacob Lambert #include "llvm/Object/Binary.h" 300f3f357eSJacob Lambert #include "llvm/Object/ObjectFile.h" 310f3f357eSJacob Lambert #include "llvm/Support/Casting.h" 320f3f357eSJacob Lambert #include "llvm/Support/Debug.h" 33e68fc86bSBenjamin Kramer #include "llvm/Support/EndianStream.h" 340f3f357eSJacob Lambert #include "llvm/Support/Errc.h" 350f3f357eSJacob Lambert #include "llvm/Support/Error.h" 360f3f357eSJacob Lambert #include "llvm/Support/ErrorOr.h" 370f3f357eSJacob Lambert #include "llvm/Support/FileSystem.h" 380f3f357eSJacob Lambert #include "llvm/Support/Host.h" 390f3f357eSJacob Lambert #include "llvm/Support/MemoryBuffer.h" 400f3f357eSJacob Lambert #include "llvm/Support/Path.h" 410f3f357eSJacob Lambert #include "llvm/Support/Program.h" 420f3f357eSJacob Lambert #include "llvm/Support/Signals.h" 430f3f357eSJacob Lambert #include "llvm/Support/StringSaver.h" 440f3f357eSJacob Lambert #include "llvm/Support/WithColor.h" 450f3f357eSJacob Lambert #include "llvm/Support/raw_ostream.h" 460f3f357eSJacob Lambert #include <algorithm> 470f3f357eSJacob Lambert #include <cassert> 480f3f357eSJacob Lambert #include <cstddef> 490f3f357eSJacob Lambert #include <cstdint> 500f3f357eSJacob Lambert #include <forward_list> 510f3f357eSJacob Lambert #include <memory> 520f3f357eSJacob Lambert #include <set> 530f3f357eSJacob Lambert #include <string> 540f3f357eSJacob Lambert #include <system_error> 550f3f357eSJacob Lambert #include <utility> 560f3f357eSJacob Lambert 570f3f357eSJacob Lambert using namespace llvm; 580f3f357eSJacob Lambert using namespace llvm::object; 590f3f357eSJacob Lambert using namespace clang; 600f3f357eSJacob Lambert 610f3f357eSJacob Lambert /// Magic string that marks the existence of offloading data. 620f3f357eSJacob Lambert #define OFFLOAD_BUNDLER_MAGIC_STR "__CLANG_OFFLOAD_BUNDLE__" 630f3f357eSJacob Lambert 640f3f357eSJacob Lambert OffloadTargetInfo::OffloadTargetInfo(const StringRef Target, 650f3f357eSJacob Lambert const OffloadBundlerConfig &BC) 660f3f357eSJacob Lambert : BundlerConfig(BC) { 670f3f357eSJacob Lambert 680f3f357eSJacob Lambert // TODO: Add error checking from ClangOffloadBundler.cpp 690f3f357eSJacob Lambert auto TargetFeatures = Target.split(':'); 700f3f357eSJacob Lambert auto TripleOrGPU = TargetFeatures.first.rsplit('-'); 710f3f357eSJacob Lambert 725b11caa8Sraghavmedicherla if (clang::StringToCudaArch(TripleOrGPU.second) != clang::CudaArch::UNKNOWN) { 730f3f357eSJacob Lambert auto KindTriple = TripleOrGPU.first.split('-'); 740f3f357eSJacob Lambert this->OffloadKind = KindTriple.first; 750f3f357eSJacob Lambert this->Triple = llvm::Triple(KindTriple.second); 76844b84afSYaxun (Sam) Liu this->TargetID = Target.substr(Target.find(TripleOrGPU.second)); 770f3f357eSJacob Lambert } else { 780f3f357eSJacob Lambert auto KindTriple = TargetFeatures.first.split('-'); 790f3f357eSJacob Lambert this->OffloadKind = KindTriple.first; 800f3f357eSJacob Lambert this->Triple = llvm::Triple(KindTriple.second); 81844b84afSYaxun (Sam) Liu this->TargetID = ""; 820f3f357eSJacob Lambert } 830f3f357eSJacob Lambert } 840f3f357eSJacob Lambert 850f3f357eSJacob Lambert bool OffloadTargetInfo::hasHostKind() const { 860f3f357eSJacob Lambert return this->OffloadKind == "host"; 870f3f357eSJacob Lambert } 880f3f357eSJacob Lambert 890f3f357eSJacob Lambert bool OffloadTargetInfo::isOffloadKindValid() const { 900f3f357eSJacob Lambert return OffloadKind == "host" || OffloadKind == "openmp" || 910f3f357eSJacob Lambert OffloadKind == "hip" || OffloadKind == "hipv4"; 920f3f357eSJacob Lambert } 930f3f357eSJacob Lambert 940f3f357eSJacob Lambert bool OffloadTargetInfo::isOffloadKindCompatible( 950f3f357eSJacob Lambert const StringRef TargetOffloadKind) const { 960f3f357eSJacob Lambert if (OffloadKind == TargetOffloadKind) 970f3f357eSJacob Lambert return true; 980f3f357eSJacob Lambert if (BundlerConfig.HipOpenmpCompatible) { 995b11caa8Sraghavmedicherla bool HIPCompatibleWithOpenMP = OffloadKind.startswith_insensitive("hip") && 1000f3f357eSJacob Lambert TargetOffloadKind == "openmp"; 1010f3f357eSJacob Lambert bool OpenMPCompatibleWithHIP = 1020f3f357eSJacob Lambert OffloadKind == "openmp" && 1030f3f357eSJacob Lambert TargetOffloadKind.startswith_insensitive("hip"); 1040f3f357eSJacob Lambert return HIPCompatibleWithOpenMP || OpenMPCompatibleWithHIP; 1050f3f357eSJacob Lambert } 1060f3f357eSJacob Lambert return false; 1070f3f357eSJacob Lambert } 1080f3f357eSJacob Lambert 1090f3f357eSJacob Lambert bool OffloadTargetInfo::isTripleValid() const { 1100f3f357eSJacob Lambert return !Triple.str().empty() && Triple.getArch() != Triple::UnknownArch; 1110f3f357eSJacob Lambert } 1120f3f357eSJacob Lambert 1130f3f357eSJacob Lambert bool OffloadTargetInfo::operator==(const OffloadTargetInfo &Target) const { 1140f3f357eSJacob Lambert return OffloadKind == Target.OffloadKind && 115844b84afSYaxun (Sam) Liu Triple.isCompatibleWith(Target.Triple) && TargetID == Target.TargetID; 1160f3f357eSJacob Lambert } 1170f3f357eSJacob Lambert 118844b84afSYaxun (Sam) Liu std::string OffloadTargetInfo::str() const { 119844b84afSYaxun (Sam) Liu return Twine(OffloadKind + "-" + Triple.str() + "-" + TargetID).str(); 1200f3f357eSJacob Lambert } 1210f3f357eSJacob Lambert 1220f3f357eSJacob Lambert static StringRef getDeviceFileExtension(StringRef Device, 1230f3f357eSJacob Lambert StringRef BundleFileName) { 1240f3f357eSJacob Lambert if (Device.contains("gfx")) 1250f3f357eSJacob Lambert return ".bc"; 1260f3f357eSJacob Lambert if (Device.contains("sm_")) 1270f3f357eSJacob Lambert return ".cubin"; 1280f3f357eSJacob Lambert return sys::path::extension(BundleFileName); 1290f3f357eSJacob Lambert } 1300f3f357eSJacob Lambert 1310f3f357eSJacob Lambert static std::string getDeviceLibraryFileName(StringRef BundleFileName, 1320f3f357eSJacob Lambert StringRef Device) { 1330f3f357eSJacob Lambert StringRef LibName = sys::path::stem(BundleFileName); 1340f3f357eSJacob Lambert StringRef Extension = getDeviceFileExtension(Device, BundleFileName); 1350f3f357eSJacob Lambert 1360f3f357eSJacob Lambert std::string Result; 1370f3f357eSJacob Lambert Result += LibName; 1380f3f357eSJacob Lambert Result += Extension; 1390f3f357eSJacob Lambert return Result; 1400f3f357eSJacob Lambert } 1410f3f357eSJacob Lambert 142844b84afSYaxun (Sam) Liu /// @brief Checks if a code object \p CodeObjectInfo is compatible with a given 143844b84afSYaxun (Sam) Liu /// target \p TargetInfo. 144844b84afSYaxun (Sam) Liu /// @link https://clang.llvm.org/docs/ClangOffloadBundler.html#bundle-entry-id 145844b84afSYaxun (Sam) Liu bool isCodeObjectCompatible(const OffloadTargetInfo &CodeObjectInfo, 146844b84afSYaxun (Sam) Liu const OffloadTargetInfo &TargetInfo) { 147844b84afSYaxun (Sam) Liu 148844b84afSYaxun (Sam) Liu // Compatible in case of exact match. 149844b84afSYaxun (Sam) Liu if (CodeObjectInfo == TargetInfo) { 150844b84afSYaxun (Sam) Liu DEBUG_WITH_TYPE("CodeObjectCompatibility", 151844b84afSYaxun (Sam) Liu dbgs() << "Compatible: Exact match: \t[CodeObject: " 152844b84afSYaxun (Sam) Liu << CodeObjectInfo.str() 153844b84afSYaxun (Sam) Liu << "]\t:\t[Target: " << TargetInfo.str() << "]\n"); 154844b84afSYaxun (Sam) Liu return true; 155844b84afSYaxun (Sam) Liu } 156844b84afSYaxun (Sam) Liu 157844b84afSYaxun (Sam) Liu // Incompatible if Kinds or Triples mismatch. 158844b84afSYaxun (Sam) Liu if (!CodeObjectInfo.isOffloadKindCompatible(TargetInfo.OffloadKind) || 159844b84afSYaxun (Sam) Liu !CodeObjectInfo.Triple.isCompatibleWith(TargetInfo.Triple)) { 160844b84afSYaxun (Sam) Liu DEBUG_WITH_TYPE( 161844b84afSYaxun (Sam) Liu "CodeObjectCompatibility", 162844b84afSYaxun (Sam) Liu dbgs() << "Incompatible: Kind/Triple mismatch \t[CodeObject: " 163844b84afSYaxun (Sam) Liu << CodeObjectInfo.str() << "]\t:\t[Target: " << TargetInfo.str() 164844b84afSYaxun (Sam) Liu << "]\n"); 165844b84afSYaxun (Sam) Liu return false; 166844b84afSYaxun (Sam) Liu } 167844b84afSYaxun (Sam) Liu 168844b84afSYaxun (Sam) Liu // Incompatible if target IDs are incompatible. 169844b84afSYaxun (Sam) Liu if (!clang::isCompatibleTargetID(CodeObjectInfo.TargetID, 170844b84afSYaxun (Sam) Liu TargetInfo.TargetID)) { 171844b84afSYaxun (Sam) Liu DEBUG_WITH_TYPE( 172844b84afSYaxun (Sam) Liu "CodeObjectCompatibility", 173844b84afSYaxun (Sam) Liu dbgs() << "Incompatible: target IDs are incompatible \t[CodeObject: " 174844b84afSYaxun (Sam) Liu << CodeObjectInfo.str() << "]\t:\t[Target: " << TargetInfo.str() 175844b84afSYaxun (Sam) Liu << "]\n"); 176844b84afSYaxun (Sam) Liu return false; 177844b84afSYaxun (Sam) Liu } 178844b84afSYaxun (Sam) Liu 179844b84afSYaxun (Sam) Liu DEBUG_WITH_TYPE( 180844b84afSYaxun (Sam) Liu "CodeObjectCompatibility", 181844b84afSYaxun (Sam) Liu dbgs() << "Compatible: Code Objects are compatible \t[CodeObject: " 182844b84afSYaxun (Sam) Liu << CodeObjectInfo.str() << "]\t:\t[Target: " << TargetInfo.str() 183844b84afSYaxun (Sam) Liu << "]\n"); 184844b84afSYaxun (Sam) Liu return true; 185844b84afSYaxun (Sam) Liu } 186844b84afSYaxun (Sam) Liu 187b6942a28SBenjamin Kramer namespace { 1880f3f357eSJacob Lambert /// Generic file handler interface. 1890f3f357eSJacob Lambert class FileHandler { 1900f3f357eSJacob Lambert public: 1910f3f357eSJacob Lambert struct BundleInfo { 1920f3f357eSJacob Lambert StringRef BundleID; 1930f3f357eSJacob Lambert }; 1940f3f357eSJacob Lambert 1950f3f357eSJacob Lambert FileHandler() {} 1960f3f357eSJacob Lambert 1970f3f357eSJacob Lambert virtual ~FileHandler() {} 1980f3f357eSJacob Lambert 1990f3f357eSJacob Lambert /// Update the file handler with information from the header of the bundled 2000f3f357eSJacob Lambert /// file. 2010f3f357eSJacob Lambert virtual Error ReadHeader(MemoryBuffer &Input) = 0; 2020f3f357eSJacob Lambert 2030f3f357eSJacob Lambert /// Read the marker of the next bundled to be read in the file. The bundle 20437a3e98cSKazu Hirata /// name is returned if there is one in the file, or `std::nullopt` if there 20537a3e98cSKazu Hirata /// are no more bundles to be read. 2062c5d49cfSFangrui Song virtual Expected<std::optional<StringRef>> 2070f3f357eSJacob Lambert ReadBundleStart(MemoryBuffer &Input) = 0; 2080f3f357eSJacob Lambert 2090f3f357eSJacob Lambert /// Read the marker that closes the current bundle. 2100f3f357eSJacob Lambert virtual Error ReadBundleEnd(MemoryBuffer &Input) = 0; 2110f3f357eSJacob Lambert 2120f3f357eSJacob Lambert /// Read the current bundle and write the result into the stream \a OS. 2130f3f357eSJacob Lambert virtual Error ReadBundle(raw_ostream &OS, MemoryBuffer &Input) = 0; 2140f3f357eSJacob Lambert 2150f3f357eSJacob Lambert /// Write the header of the bundled file to \a OS based on the information 2160f3f357eSJacob Lambert /// gathered from \a Inputs. 2170f3f357eSJacob Lambert virtual Error WriteHeader(raw_fd_ostream &OS, 2180f3f357eSJacob Lambert ArrayRef<std::unique_ptr<MemoryBuffer>> Inputs) = 0; 2190f3f357eSJacob Lambert 2200f3f357eSJacob Lambert /// Write the marker that initiates a bundle for the triple \a TargetTriple to 2210f3f357eSJacob Lambert /// \a OS. 2220f3f357eSJacob Lambert virtual Error WriteBundleStart(raw_fd_ostream &OS, 2230f3f357eSJacob Lambert StringRef TargetTriple) = 0; 2240f3f357eSJacob Lambert 2250f3f357eSJacob Lambert /// Write the marker that closes a bundle for the triple \a TargetTriple to \a 2260f3f357eSJacob Lambert /// OS. 2270f3f357eSJacob Lambert virtual Error WriteBundleEnd(raw_fd_ostream &OS, StringRef TargetTriple) = 0; 2280f3f357eSJacob Lambert 2290f3f357eSJacob Lambert /// Write the bundle from \a Input into \a OS. 2300f3f357eSJacob Lambert virtual Error WriteBundle(raw_fd_ostream &OS, MemoryBuffer &Input) = 0; 2310f3f357eSJacob Lambert 2320f3f357eSJacob Lambert /// List bundle IDs in \a Input. 2330f3f357eSJacob Lambert virtual Error listBundleIDs(MemoryBuffer &Input) { 2340f3f357eSJacob Lambert if (Error Err = ReadHeader(Input)) 2350f3f357eSJacob Lambert return Err; 2360f3f357eSJacob Lambert return forEachBundle(Input, [&](const BundleInfo &Info) -> Error { 2370f3f357eSJacob Lambert llvm::outs() << Info.BundleID << '\n'; 2380f3f357eSJacob Lambert Error Err = listBundleIDsCallback(Input, Info); 2390f3f357eSJacob Lambert if (Err) 2400f3f357eSJacob Lambert return Err; 2410f3f357eSJacob Lambert return Error::success(); 2420f3f357eSJacob Lambert }); 2430f3f357eSJacob Lambert } 2440f3f357eSJacob Lambert 2450f3f357eSJacob Lambert /// For each bundle in \a Input, do \a Func. 2460f3f357eSJacob Lambert Error forEachBundle(MemoryBuffer &Input, 2470f3f357eSJacob Lambert std::function<Error(const BundleInfo &)> Func) { 2480f3f357eSJacob Lambert while (true) { 2492c5d49cfSFangrui Song Expected<std::optional<StringRef>> CurTripleOrErr = 2502c5d49cfSFangrui Song ReadBundleStart(Input); 2510f3f357eSJacob Lambert if (!CurTripleOrErr) 2520f3f357eSJacob Lambert return CurTripleOrErr.takeError(); 2530f3f357eSJacob Lambert 2540f3f357eSJacob Lambert // No more bundles. 2550f3f357eSJacob Lambert if (!*CurTripleOrErr) 2560f3f357eSJacob Lambert break; 2570f3f357eSJacob Lambert 2580f3f357eSJacob Lambert StringRef CurTriple = **CurTripleOrErr; 2590f3f357eSJacob Lambert assert(!CurTriple.empty()); 2600f3f357eSJacob Lambert 2610f3f357eSJacob Lambert BundleInfo Info{CurTriple}; 2620f3f357eSJacob Lambert if (Error Err = Func(Info)) 2630f3f357eSJacob Lambert return Err; 2640f3f357eSJacob Lambert } 2650f3f357eSJacob Lambert return Error::success(); 2660f3f357eSJacob Lambert } 2670f3f357eSJacob Lambert 2680f3f357eSJacob Lambert protected: 2690f3f357eSJacob Lambert virtual Error listBundleIDsCallback(MemoryBuffer &Input, 2700f3f357eSJacob Lambert const BundleInfo &Info) { 2710f3f357eSJacob Lambert return Error::success(); 2720f3f357eSJacob Lambert } 2730f3f357eSJacob Lambert }; 2740f3f357eSJacob Lambert 2750f3f357eSJacob Lambert /// Handler for binary files. The bundled file will have the following format 2760f3f357eSJacob Lambert /// (all integers are stored in little-endian format): 2770f3f357eSJacob Lambert /// 2780f3f357eSJacob Lambert /// "OFFLOAD_BUNDLER_MAGIC_STR" (ASCII encoding of the string) 2790f3f357eSJacob Lambert /// 2800f3f357eSJacob Lambert /// NumberOfOffloadBundles (8-byte integer) 2810f3f357eSJacob Lambert /// 2820f3f357eSJacob Lambert /// OffsetOfBundle1 (8-byte integer) 2830f3f357eSJacob Lambert /// SizeOfBundle1 (8-byte integer) 2840f3f357eSJacob Lambert /// NumberOfBytesInTripleOfBundle1 (8-byte integer) 2850f3f357eSJacob Lambert /// TripleOfBundle1 (byte length defined before) 2860f3f357eSJacob Lambert /// 2870f3f357eSJacob Lambert /// ... 2880f3f357eSJacob Lambert /// 2890f3f357eSJacob Lambert /// OffsetOfBundleN (8-byte integer) 2900f3f357eSJacob Lambert /// SizeOfBundleN (8-byte integer) 2910f3f357eSJacob Lambert /// NumberOfBytesInTripleOfBundleN (8-byte integer) 2920f3f357eSJacob Lambert /// TripleOfBundleN (byte length defined before) 2930f3f357eSJacob Lambert /// 2940f3f357eSJacob Lambert /// Bundle1 2950f3f357eSJacob Lambert /// ... 2960f3f357eSJacob Lambert /// BundleN 2970f3f357eSJacob Lambert 2980f3f357eSJacob Lambert /// Read 8-byte integers from a buffer in little-endian format. 2990f3f357eSJacob Lambert static uint64_t Read8byteIntegerFromBuffer(StringRef Buffer, size_t pos) { 300e68fc86bSBenjamin Kramer return llvm::support::endian::read64le(Buffer.data() + pos); 3010f3f357eSJacob Lambert } 3020f3f357eSJacob Lambert 3030f3f357eSJacob Lambert /// Write 8-byte integers to a buffer in little-endian format. 3040f3f357eSJacob Lambert static void Write8byteIntegerToBuffer(raw_fd_ostream &OS, uint64_t Val) { 305e68fc86bSBenjamin Kramer llvm::support::endian::write(OS, Val, llvm::support::little); 3060f3f357eSJacob Lambert } 3070f3f357eSJacob Lambert 3080f3f357eSJacob Lambert class BinaryFileHandler final : public FileHandler { 3090f3f357eSJacob Lambert /// Information about the bundles extracted from the header. 3100f3f357eSJacob Lambert struct BinaryBundleInfo final : public BundleInfo { 3110f3f357eSJacob Lambert /// Size of the bundle. 3120f3f357eSJacob Lambert uint64_t Size = 0u; 3130f3f357eSJacob Lambert /// Offset at which the bundle starts in the bundled file. 3140f3f357eSJacob Lambert uint64_t Offset = 0u; 3150f3f357eSJacob Lambert 3160f3f357eSJacob Lambert BinaryBundleInfo() {} 3170f3f357eSJacob Lambert BinaryBundleInfo(uint64_t Size, uint64_t Offset) 3180f3f357eSJacob Lambert : Size(Size), Offset(Offset) {} 3190f3f357eSJacob Lambert }; 3200f3f357eSJacob Lambert 3210f3f357eSJacob Lambert /// Map between a triple and the corresponding bundle information. 3220f3f357eSJacob Lambert StringMap<BinaryBundleInfo> BundlesInfo; 3230f3f357eSJacob Lambert 3240f3f357eSJacob Lambert /// Iterator for the bundle information that is being read. 3250f3f357eSJacob Lambert StringMap<BinaryBundleInfo>::iterator CurBundleInfo; 3260f3f357eSJacob Lambert StringMap<BinaryBundleInfo>::iterator NextBundleInfo; 3270f3f357eSJacob Lambert 3280f3f357eSJacob Lambert /// Current bundle target to be written. 3290f3f357eSJacob Lambert std::string CurWriteBundleTarget; 3300f3f357eSJacob Lambert 3310f3f357eSJacob Lambert /// Configuration options and arrays for this bundler job 3320f3f357eSJacob Lambert const OffloadBundlerConfig &BundlerConfig; 3330f3f357eSJacob Lambert 3340f3f357eSJacob Lambert public: 3350f3f357eSJacob Lambert // TODO: Add error checking from ClangOffloadBundler.cpp 3360f3f357eSJacob Lambert BinaryFileHandler(const OffloadBundlerConfig &BC) : BundlerConfig(BC) {} 3370f3f357eSJacob Lambert 3380f3f357eSJacob Lambert ~BinaryFileHandler() final {} 3390f3f357eSJacob Lambert 3400f3f357eSJacob Lambert Error ReadHeader(MemoryBuffer &Input) final { 3410f3f357eSJacob Lambert StringRef FC = Input.getBuffer(); 3420f3f357eSJacob Lambert 3430f3f357eSJacob Lambert // Initialize the current bundle with the end of the container. 3440f3f357eSJacob Lambert CurBundleInfo = BundlesInfo.end(); 3450f3f357eSJacob Lambert 3460f3f357eSJacob Lambert // Check if buffer is smaller than magic string. 3470f3f357eSJacob Lambert size_t ReadChars = sizeof(OFFLOAD_BUNDLER_MAGIC_STR) - 1; 3480f3f357eSJacob Lambert if (ReadChars > FC.size()) 3490f3f357eSJacob Lambert return Error::success(); 3500f3f357eSJacob Lambert 3510f3f357eSJacob Lambert // Check if no magic was found. 3520f3f357eSJacob Lambert StringRef Magic(FC.data(), sizeof(OFFLOAD_BUNDLER_MAGIC_STR) - 1); 3530f3f357eSJacob Lambert if (!Magic.equals(OFFLOAD_BUNDLER_MAGIC_STR)) 3540f3f357eSJacob Lambert return Error::success(); 3550f3f357eSJacob Lambert 3560f3f357eSJacob Lambert // Read number of bundles. 3570f3f357eSJacob Lambert if (ReadChars + 8 > FC.size()) 3580f3f357eSJacob Lambert return Error::success(); 3590f3f357eSJacob Lambert 3600f3f357eSJacob Lambert uint64_t NumberOfBundles = Read8byteIntegerFromBuffer(FC, ReadChars); 3610f3f357eSJacob Lambert ReadChars += 8; 3620f3f357eSJacob Lambert 3630f3f357eSJacob Lambert // Read bundle offsets, sizes and triples. 3640f3f357eSJacob Lambert for (uint64_t i = 0; i < NumberOfBundles; ++i) { 3650f3f357eSJacob Lambert 3660f3f357eSJacob Lambert // Read offset. 3670f3f357eSJacob Lambert if (ReadChars + 8 > FC.size()) 3680f3f357eSJacob Lambert return Error::success(); 3690f3f357eSJacob Lambert 3700f3f357eSJacob Lambert uint64_t Offset = Read8byteIntegerFromBuffer(FC, ReadChars); 3710f3f357eSJacob Lambert ReadChars += 8; 3720f3f357eSJacob Lambert 3730f3f357eSJacob Lambert // Read size. 3740f3f357eSJacob Lambert if (ReadChars + 8 > FC.size()) 3750f3f357eSJacob Lambert return Error::success(); 3760f3f357eSJacob Lambert 3770f3f357eSJacob Lambert uint64_t Size = Read8byteIntegerFromBuffer(FC, ReadChars); 3780f3f357eSJacob Lambert ReadChars += 8; 3790f3f357eSJacob Lambert 3800f3f357eSJacob Lambert // Read triple size. 3810f3f357eSJacob Lambert if (ReadChars + 8 > FC.size()) 3820f3f357eSJacob Lambert return Error::success(); 3830f3f357eSJacob Lambert 3840f3f357eSJacob Lambert uint64_t TripleSize = Read8byteIntegerFromBuffer(FC, ReadChars); 3850f3f357eSJacob Lambert ReadChars += 8; 3860f3f357eSJacob Lambert 3870f3f357eSJacob Lambert // Read triple. 3880f3f357eSJacob Lambert if (ReadChars + TripleSize > FC.size()) 3890f3f357eSJacob Lambert return Error::success(); 3900f3f357eSJacob Lambert 3910f3f357eSJacob Lambert StringRef Triple(&FC.data()[ReadChars], TripleSize); 3920f3f357eSJacob Lambert ReadChars += TripleSize; 3930f3f357eSJacob Lambert 3940f3f357eSJacob Lambert // Check if the offset and size make sense. 3950f3f357eSJacob Lambert if (!Offset || Offset + Size > FC.size()) 3960f3f357eSJacob Lambert return Error::success(); 3970f3f357eSJacob Lambert 3980f3f357eSJacob Lambert assert(BundlesInfo.find(Triple) == BundlesInfo.end() && 3990f3f357eSJacob Lambert "Triple is duplicated??"); 4000f3f357eSJacob Lambert BundlesInfo[Triple] = BinaryBundleInfo(Size, Offset); 4010f3f357eSJacob Lambert } 4020f3f357eSJacob Lambert // Set the iterator to where we will start to read. 4030f3f357eSJacob Lambert CurBundleInfo = BundlesInfo.end(); 4040f3f357eSJacob Lambert NextBundleInfo = BundlesInfo.begin(); 4050f3f357eSJacob Lambert return Error::success(); 4060f3f357eSJacob Lambert } 4070f3f357eSJacob Lambert 4082c5d49cfSFangrui Song Expected<std::optional<StringRef>> 4092c5d49cfSFangrui Song ReadBundleStart(MemoryBuffer &Input) final { 4100f3f357eSJacob Lambert if (NextBundleInfo == BundlesInfo.end()) 4110c2f6e36SFangrui Song return std::nullopt; 4120f3f357eSJacob Lambert CurBundleInfo = NextBundleInfo++; 4130f3f357eSJacob Lambert return CurBundleInfo->first(); 4140f3f357eSJacob Lambert } 4150f3f357eSJacob Lambert 4160f3f357eSJacob Lambert Error ReadBundleEnd(MemoryBuffer &Input) final { 4170f3f357eSJacob Lambert assert(CurBundleInfo != BundlesInfo.end() && "Invalid reader info!"); 4180f3f357eSJacob Lambert return Error::success(); 4190f3f357eSJacob Lambert } 4200f3f357eSJacob Lambert 4210f3f357eSJacob Lambert Error ReadBundle(raw_ostream &OS, MemoryBuffer &Input) final { 4220f3f357eSJacob Lambert assert(CurBundleInfo != BundlesInfo.end() && "Invalid reader info!"); 4230f3f357eSJacob Lambert StringRef FC = Input.getBuffer(); 4240f3f357eSJacob Lambert OS.write(FC.data() + CurBundleInfo->second.Offset, 4250f3f357eSJacob Lambert CurBundleInfo->second.Size); 4260f3f357eSJacob Lambert return Error::success(); 4270f3f357eSJacob Lambert } 4280f3f357eSJacob Lambert 4290f3f357eSJacob Lambert Error WriteHeader(raw_fd_ostream &OS, 4300f3f357eSJacob Lambert ArrayRef<std::unique_ptr<MemoryBuffer>> Inputs) final { 4310f3f357eSJacob Lambert 4320f3f357eSJacob Lambert // Compute size of the header. 4330f3f357eSJacob Lambert uint64_t HeaderSize = 0; 4340f3f357eSJacob Lambert 4350f3f357eSJacob Lambert HeaderSize += sizeof(OFFLOAD_BUNDLER_MAGIC_STR) - 1; 4360f3f357eSJacob Lambert HeaderSize += 8; // Number of Bundles 4370f3f357eSJacob Lambert 4380f3f357eSJacob Lambert for (auto &T : BundlerConfig.TargetNames) { 4390f3f357eSJacob Lambert HeaderSize += 3 * 8; // Bundle offset, Size of bundle and size of triple. 4400f3f357eSJacob Lambert HeaderSize += T.size(); // The triple. 4410f3f357eSJacob Lambert } 4420f3f357eSJacob Lambert 4430f3f357eSJacob Lambert // Write to the buffer the header. 4440f3f357eSJacob Lambert OS << OFFLOAD_BUNDLER_MAGIC_STR; 4450f3f357eSJacob Lambert 4460f3f357eSJacob Lambert Write8byteIntegerToBuffer(OS, BundlerConfig.TargetNames.size()); 4470f3f357eSJacob Lambert 4480f3f357eSJacob Lambert unsigned Idx = 0; 4490f3f357eSJacob Lambert for (auto &T : BundlerConfig.TargetNames) { 4500f3f357eSJacob Lambert MemoryBuffer &MB = *Inputs[Idx++]; 4510f3f357eSJacob Lambert HeaderSize = alignTo(HeaderSize, BundlerConfig.BundleAlignment); 4520f3f357eSJacob Lambert // Bundle offset. 4530f3f357eSJacob Lambert Write8byteIntegerToBuffer(OS, HeaderSize); 4540f3f357eSJacob Lambert // Size of the bundle (adds to the next bundle's offset) 4550f3f357eSJacob Lambert Write8byteIntegerToBuffer(OS, MB.getBufferSize()); 4560f3f357eSJacob Lambert BundlesInfo[T] = BinaryBundleInfo(MB.getBufferSize(), HeaderSize); 4570f3f357eSJacob Lambert HeaderSize += MB.getBufferSize(); 4580f3f357eSJacob Lambert // Size of the triple 4590f3f357eSJacob Lambert Write8byteIntegerToBuffer(OS, T.size()); 4600f3f357eSJacob Lambert // Triple 4610f3f357eSJacob Lambert OS << T; 4620f3f357eSJacob Lambert } 4630f3f357eSJacob Lambert return Error::success(); 4640f3f357eSJacob Lambert } 4650f3f357eSJacob Lambert 4660f3f357eSJacob Lambert Error WriteBundleStart(raw_fd_ostream &OS, StringRef TargetTriple) final { 4670f3f357eSJacob Lambert CurWriteBundleTarget = TargetTriple.str(); 4680f3f357eSJacob Lambert return Error::success(); 4690f3f357eSJacob Lambert } 4700f3f357eSJacob Lambert 4710f3f357eSJacob Lambert Error WriteBundleEnd(raw_fd_ostream &OS, StringRef TargetTriple) final { 4720f3f357eSJacob Lambert return Error::success(); 4730f3f357eSJacob Lambert } 4740f3f357eSJacob Lambert 4750f3f357eSJacob Lambert Error WriteBundle(raw_fd_ostream &OS, MemoryBuffer &Input) final { 4760f3f357eSJacob Lambert auto BI = BundlesInfo[CurWriteBundleTarget]; 4770f3f357eSJacob Lambert OS.seek(BI.Offset); 4780f3f357eSJacob Lambert OS.write(Input.getBufferStart(), Input.getBufferSize()); 4790f3f357eSJacob Lambert return Error::success(); 4800f3f357eSJacob Lambert } 4810f3f357eSJacob Lambert }; 4820f3f357eSJacob Lambert 4830f3f357eSJacob Lambert // This class implements a list of temporary files that are removed upon 4840f3f357eSJacob Lambert // object destruction. 4850f3f357eSJacob Lambert class TempFileHandlerRAII { 4860f3f357eSJacob Lambert public: 4870f3f357eSJacob Lambert ~TempFileHandlerRAII() { 4880f3f357eSJacob Lambert for (const auto &File : Files) 4890f3f357eSJacob Lambert sys::fs::remove(File); 4900f3f357eSJacob Lambert } 4910f3f357eSJacob Lambert 4920f3f357eSJacob Lambert // Creates temporary file with given contents. 4932c5d49cfSFangrui Song Expected<StringRef> Create(std::optional<ArrayRef<char>> Contents) { 4940f3f357eSJacob Lambert SmallString<128u> File; 4950f3f357eSJacob Lambert if (std::error_code EC = 4960f3f357eSJacob Lambert sys::fs::createTemporaryFile("clang-offload-bundler", "tmp", File)) 4970f3f357eSJacob Lambert return createFileError(File, EC); 4980f3f357eSJacob Lambert Files.push_front(File); 4990f3f357eSJacob Lambert 5000f3f357eSJacob Lambert if (Contents) { 5010f3f357eSJacob Lambert std::error_code EC; 5020f3f357eSJacob Lambert raw_fd_ostream OS(File, EC); 5030f3f357eSJacob Lambert if (EC) 5040f3f357eSJacob Lambert return createFileError(File, EC); 5050f3f357eSJacob Lambert OS.write(Contents->data(), Contents->size()); 5060f3f357eSJacob Lambert } 5070f3f357eSJacob Lambert return Files.front().str(); 5080f3f357eSJacob Lambert } 5090f3f357eSJacob Lambert 5100f3f357eSJacob Lambert private: 5110f3f357eSJacob Lambert std::forward_list<SmallString<128u>> Files; 5120f3f357eSJacob Lambert }; 5130f3f357eSJacob Lambert 5140f3f357eSJacob Lambert /// Handler for object files. The bundles are organized by sections with a 5150f3f357eSJacob Lambert /// designated name. 5160f3f357eSJacob Lambert /// 5170f3f357eSJacob Lambert /// To unbundle, we just copy the contents of the designated section. 5180f3f357eSJacob Lambert class ObjectFileHandler final : public FileHandler { 5190f3f357eSJacob Lambert 5200f3f357eSJacob Lambert /// The object file we are currently dealing with. 5210f3f357eSJacob Lambert std::unique_ptr<ObjectFile> Obj; 5220f3f357eSJacob Lambert 5230f3f357eSJacob Lambert /// Return the input file contents. 5240f3f357eSJacob Lambert StringRef getInputFileContents() const { return Obj->getData(); } 5250f3f357eSJacob Lambert 5260f3f357eSJacob Lambert /// Return bundle name (<kind>-<triple>) if the provided section is an offload 5270f3f357eSJacob Lambert /// section. 5282c5d49cfSFangrui Song static Expected<std::optional<StringRef>> 5292c5d49cfSFangrui Song IsOffloadSection(SectionRef CurSection) { 5300f3f357eSJacob Lambert Expected<StringRef> NameOrErr = CurSection.getName(); 5310f3f357eSJacob Lambert if (!NameOrErr) 5320f3f357eSJacob Lambert return NameOrErr.takeError(); 5330f3f357eSJacob Lambert 5340f3f357eSJacob Lambert // If it does not start with the reserved suffix, just skip this section. 5350f3f357eSJacob Lambert if (!NameOrErr->startswith(OFFLOAD_BUNDLER_MAGIC_STR)) 5360c2f6e36SFangrui Song return std::nullopt; 5370f3f357eSJacob Lambert 5380f3f357eSJacob Lambert // Return the triple that is right after the reserved prefix. 5390f3f357eSJacob Lambert return NameOrErr->substr(sizeof(OFFLOAD_BUNDLER_MAGIC_STR) - 1); 5400f3f357eSJacob Lambert } 5410f3f357eSJacob Lambert 5420f3f357eSJacob Lambert /// Total number of inputs. 5430f3f357eSJacob Lambert unsigned NumberOfInputs = 0; 5440f3f357eSJacob Lambert 5450f3f357eSJacob Lambert /// Total number of processed inputs, i.e, inputs that were already 5460f3f357eSJacob Lambert /// read from the buffers. 5470f3f357eSJacob Lambert unsigned NumberOfProcessedInputs = 0; 5480f3f357eSJacob Lambert 5490f3f357eSJacob Lambert /// Iterator of the current and next section. 5500f3f357eSJacob Lambert section_iterator CurrentSection; 5510f3f357eSJacob Lambert section_iterator NextSection; 5520f3f357eSJacob Lambert 5530f3f357eSJacob Lambert /// Configuration options and arrays for this bundler job 5540f3f357eSJacob Lambert const OffloadBundlerConfig &BundlerConfig; 5550f3f357eSJacob Lambert 5560f3f357eSJacob Lambert public: 5570f3f357eSJacob Lambert // TODO: Add error checking from ClangOffloadBundler.cpp 5580f3f357eSJacob Lambert ObjectFileHandler(std::unique_ptr<ObjectFile> ObjIn, 5590f3f357eSJacob Lambert const OffloadBundlerConfig &BC) 5600f3f357eSJacob Lambert : Obj(std::move(ObjIn)), CurrentSection(Obj->section_begin()), 5610f3f357eSJacob Lambert NextSection(Obj->section_begin()), BundlerConfig(BC) {} 5620f3f357eSJacob Lambert 5630f3f357eSJacob Lambert ~ObjectFileHandler() final {} 5640f3f357eSJacob Lambert 5650f3f357eSJacob Lambert Error ReadHeader(MemoryBuffer &Input) final { return Error::success(); } 5660f3f357eSJacob Lambert 5672c5d49cfSFangrui Song Expected<std::optional<StringRef>> 5682c5d49cfSFangrui Song ReadBundleStart(MemoryBuffer &Input) final { 5690f3f357eSJacob Lambert while (NextSection != Obj->section_end()) { 5700f3f357eSJacob Lambert CurrentSection = NextSection; 5710f3f357eSJacob Lambert ++NextSection; 5720f3f357eSJacob Lambert 5730f3f357eSJacob Lambert // Check if the current section name starts with the reserved prefix. If 5740f3f357eSJacob Lambert // so, return the triple. 5752c5d49cfSFangrui Song Expected<std::optional<StringRef>> TripleOrErr = 5760f3f357eSJacob Lambert IsOffloadSection(*CurrentSection); 5770f3f357eSJacob Lambert if (!TripleOrErr) 5780f3f357eSJacob Lambert return TripleOrErr.takeError(); 5790f3f357eSJacob Lambert if (*TripleOrErr) 5800f3f357eSJacob Lambert return **TripleOrErr; 5810f3f357eSJacob Lambert } 5820c2f6e36SFangrui Song return std::nullopt; 5830f3f357eSJacob Lambert } 5840f3f357eSJacob Lambert 5850f3f357eSJacob Lambert Error ReadBundleEnd(MemoryBuffer &Input) final { return Error::success(); } 5860f3f357eSJacob Lambert 5870f3f357eSJacob Lambert Error ReadBundle(raw_ostream &OS, MemoryBuffer &Input) final { 5880f3f357eSJacob Lambert Expected<StringRef> ContentOrErr = CurrentSection->getContents(); 5890f3f357eSJacob Lambert if (!ContentOrErr) 5900f3f357eSJacob Lambert return ContentOrErr.takeError(); 5910f3f357eSJacob Lambert StringRef Content = *ContentOrErr; 5920f3f357eSJacob Lambert 5930f3f357eSJacob Lambert // Copy fat object contents to the output when extracting host bundle. 5940f3f357eSJacob Lambert if (Content.size() == 1u && Content.front() == 0) 5950f3f357eSJacob Lambert Content = StringRef(Input.getBufferStart(), Input.getBufferSize()); 5960f3f357eSJacob Lambert 5970f3f357eSJacob Lambert OS.write(Content.data(), Content.size()); 5980f3f357eSJacob Lambert return Error::success(); 5990f3f357eSJacob Lambert } 6000f3f357eSJacob Lambert 6010f3f357eSJacob Lambert Error WriteHeader(raw_fd_ostream &OS, 6020f3f357eSJacob Lambert ArrayRef<std::unique_ptr<MemoryBuffer>> Inputs) final { 6030f3f357eSJacob Lambert assert(BundlerConfig.HostInputIndex != ~0u && 6040f3f357eSJacob Lambert "Host input index not defined."); 6050f3f357eSJacob Lambert 6060f3f357eSJacob Lambert // Record number of inputs. 6070f3f357eSJacob Lambert NumberOfInputs = Inputs.size(); 6080f3f357eSJacob Lambert return Error::success(); 6090f3f357eSJacob Lambert } 6100f3f357eSJacob Lambert 6110f3f357eSJacob Lambert Error WriteBundleStart(raw_fd_ostream &OS, StringRef TargetTriple) final { 6120f3f357eSJacob Lambert ++NumberOfProcessedInputs; 6130f3f357eSJacob Lambert return Error::success(); 6140f3f357eSJacob Lambert } 6150f3f357eSJacob Lambert 6160f3f357eSJacob Lambert Error WriteBundleEnd(raw_fd_ostream &OS, StringRef TargetTriple) final { 6170f3f357eSJacob Lambert assert(NumberOfProcessedInputs <= NumberOfInputs && 6180f3f357eSJacob Lambert "Processing more inputs that actually exist!"); 6190f3f357eSJacob Lambert assert(BundlerConfig.HostInputIndex != ~0u && 6200f3f357eSJacob Lambert "Host input index not defined."); 6210f3f357eSJacob Lambert 6220f3f357eSJacob Lambert // If this is not the last output, we don't have to do anything. 6230f3f357eSJacob Lambert if (NumberOfProcessedInputs != NumberOfInputs) 6240f3f357eSJacob Lambert return Error::success(); 6250f3f357eSJacob Lambert 6260f3f357eSJacob Lambert // We will use llvm-objcopy to add target objects sections to the output 6270f3f357eSJacob Lambert // fat object. These sections should have 'exclude' flag set which tells 6280f3f357eSJacob Lambert // link editor to remove them from linker inputs when linking executable or 6290f3f357eSJacob Lambert // shared library. 6300f3f357eSJacob Lambert 6310f3f357eSJacob Lambert assert(BundlerConfig.ObjcopyPath != "" && 6320f3f357eSJacob Lambert "llvm-objcopy path not specified"); 6330f3f357eSJacob Lambert 6340f3f357eSJacob Lambert // We write to the output file directly. So, we close it and use the name 6350f3f357eSJacob Lambert // to pass down to llvm-objcopy. 6360f3f357eSJacob Lambert OS.close(); 6370f3f357eSJacob Lambert 6380f3f357eSJacob Lambert // Temporary files that need to be removed. 6390f3f357eSJacob Lambert TempFileHandlerRAII TempFiles; 6400f3f357eSJacob Lambert 6410f3f357eSJacob Lambert // Compose llvm-objcopy command line for add target objects' sections with 6420f3f357eSJacob Lambert // appropriate flags. 6430f3f357eSJacob Lambert BumpPtrAllocator Alloc; 6440f3f357eSJacob Lambert StringSaver SS{Alloc}; 6450f3f357eSJacob Lambert SmallVector<StringRef, 8u> ObjcopyArgs{"llvm-objcopy"}; 6460f3f357eSJacob Lambert 6470f3f357eSJacob Lambert for (unsigned I = 0; I < NumberOfInputs; ++I) { 6480f3f357eSJacob Lambert StringRef InputFile = BundlerConfig.InputFileNames[I]; 6490f3f357eSJacob Lambert if (I == BundlerConfig.HostInputIndex) { 6500f3f357eSJacob Lambert // Special handling for the host bundle. We do not need to add a 6510f3f357eSJacob Lambert // standard bundle for the host object since we are going to use fat 6520f3f357eSJacob Lambert // object as a host object. Therefore use dummy contents (one zero byte) 6530f3f357eSJacob Lambert // when creating section for the host bundle. 6540f3f357eSJacob Lambert Expected<StringRef> TempFileOrErr = TempFiles.Create(ArrayRef<char>(0)); 6550f3f357eSJacob Lambert if (!TempFileOrErr) 6560f3f357eSJacob Lambert return TempFileOrErr.takeError(); 6570f3f357eSJacob Lambert InputFile = *TempFileOrErr; 6580f3f357eSJacob Lambert } 6590f3f357eSJacob Lambert 6605b11caa8Sraghavmedicherla ObjcopyArgs.push_back( 6615b11caa8Sraghavmedicherla SS.save(Twine("--add-section=") + OFFLOAD_BUNDLER_MAGIC_STR + 6625b11caa8Sraghavmedicherla BundlerConfig.TargetNames[I] + "=" + InputFile)); 6635b11caa8Sraghavmedicherla ObjcopyArgs.push_back( 6645b11caa8Sraghavmedicherla SS.save(Twine("--set-section-flags=") + OFFLOAD_BUNDLER_MAGIC_STR + 6655b11caa8Sraghavmedicherla BundlerConfig.TargetNames[I] + "=readonly,exclude")); 6660f3f357eSJacob Lambert } 6670f3f357eSJacob Lambert ObjcopyArgs.push_back("--"); 6680f3f357eSJacob Lambert ObjcopyArgs.push_back( 6690f3f357eSJacob Lambert BundlerConfig.InputFileNames[BundlerConfig.HostInputIndex]); 6700f3f357eSJacob Lambert ObjcopyArgs.push_back(BundlerConfig.OutputFileNames.front()); 6710f3f357eSJacob Lambert 6720f3f357eSJacob Lambert if (Error Err = executeObjcopy(BundlerConfig.ObjcopyPath, ObjcopyArgs)) 6730f3f357eSJacob Lambert return Err; 6740f3f357eSJacob Lambert 6750f3f357eSJacob Lambert return Error::success(); 6760f3f357eSJacob Lambert } 6770f3f357eSJacob Lambert 6780f3f357eSJacob Lambert Error WriteBundle(raw_fd_ostream &OS, MemoryBuffer &Input) final { 6790f3f357eSJacob Lambert return Error::success(); 6800f3f357eSJacob Lambert } 6810f3f357eSJacob Lambert 6820f3f357eSJacob Lambert private: 6830f3f357eSJacob Lambert Error executeObjcopy(StringRef Objcopy, ArrayRef<StringRef> Args) { 6840f3f357eSJacob Lambert // If the user asked for the commands to be printed out, we do that 6850f3f357eSJacob Lambert // instead of executing it. 6860f3f357eSJacob Lambert if (BundlerConfig.PrintExternalCommands) { 6870f3f357eSJacob Lambert errs() << "\"" << Objcopy << "\""; 6880f3f357eSJacob Lambert for (StringRef Arg : drop_begin(Args, 1)) 6890f3f357eSJacob Lambert errs() << " \"" << Arg << "\""; 6900f3f357eSJacob Lambert errs() << "\n"; 6910f3f357eSJacob Lambert } else { 6920f3f357eSJacob Lambert if (sys::ExecuteAndWait(Objcopy, Args)) 6930f3f357eSJacob Lambert return createStringError(inconvertibleErrorCode(), 6940f3f357eSJacob Lambert "'llvm-objcopy' tool failed"); 6950f3f357eSJacob Lambert } 6960f3f357eSJacob Lambert return Error::success(); 6970f3f357eSJacob Lambert } 6980f3f357eSJacob Lambert }; 6990f3f357eSJacob Lambert 7000f3f357eSJacob Lambert /// Handler for text files. The bundled file will have the following format. 7010f3f357eSJacob Lambert /// 7020f3f357eSJacob Lambert /// "Comment OFFLOAD_BUNDLER_MAGIC_STR__START__ triple" 7030f3f357eSJacob Lambert /// Bundle 1 7040f3f357eSJacob Lambert /// "Comment OFFLOAD_BUNDLER_MAGIC_STR__END__ triple" 7050f3f357eSJacob Lambert /// ... 7060f3f357eSJacob Lambert /// "Comment OFFLOAD_BUNDLER_MAGIC_STR__START__ triple" 7070f3f357eSJacob Lambert /// Bundle N 7080f3f357eSJacob Lambert /// "Comment OFFLOAD_BUNDLER_MAGIC_STR__END__ triple" 7090f3f357eSJacob Lambert class TextFileHandler final : public FileHandler { 7100f3f357eSJacob Lambert /// String that begins a line comment. 7110f3f357eSJacob Lambert StringRef Comment; 7120f3f357eSJacob Lambert 7130f3f357eSJacob Lambert /// String that initiates a bundle. 7140f3f357eSJacob Lambert std::string BundleStartString; 7150f3f357eSJacob Lambert 7160f3f357eSJacob Lambert /// String that closes a bundle. 7170f3f357eSJacob Lambert std::string BundleEndString; 7180f3f357eSJacob Lambert 7190f3f357eSJacob Lambert /// Number of chars read from input. 7200f3f357eSJacob Lambert size_t ReadChars = 0u; 7210f3f357eSJacob Lambert 7220f3f357eSJacob Lambert protected: 7230f3f357eSJacob Lambert Error ReadHeader(MemoryBuffer &Input) final { return Error::success(); } 7240f3f357eSJacob Lambert 7252c5d49cfSFangrui Song Expected<std::optional<StringRef>> 7262c5d49cfSFangrui Song ReadBundleStart(MemoryBuffer &Input) final { 7270f3f357eSJacob Lambert StringRef FC = Input.getBuffer(); 7280f3f357eSJacob Lambert 7290f3f357eSJacob Lambert // Find start of the bundle. 7300f3f357eSJacob Lambert ReadChars = FC.find(BundleStartString, ReadChars); 7310f3f357eSJacob Lambert if (ReadChars == FC.npos) 7320c2f6e36SFangrui Song return std::nullopt; 7330f3f357eSJacob Lambert 7340f3f357eSJacob Lambert // Get position of the triple. 7350f3f357eSJacob Lambert size_t TripleStart = ReadChars = ReadChars + BundleStartString.size(); 7360f3f357eSJacob Lambert 7370f3f357eSJacob Lambert // Get position that closes the triple. 7380f3f357eSJacob Lambert size_t TripleEnd = ReadChars = FC.find("\n", ReadChars); 7390f3f357eSJacob Lambert if (TripleEnd == FC.npos) 7400c2f6e36SFangrui Song return std::nullopt; 7410f3f357eSJacob Lambert 7420f3f357eSJacob Lambert // Next time we read after the new line. 7430f3f357eSJacob Lambert ++ReadChars; 7440f3f357eSJacob Lambert 7450f3f357eSJacob Lambert return StringRef(&FC.data()[TripleStart], TripleEnd - TripleStart); 7460f3f357eSJacob Lambert } 7470f3f357eSJacob Lambert 7480f3f357eSJacob Lambert Error ReadBundleEnd(MemoryBuffer &Input) final { 7490f3f357eSJacob Lambert StringRef FC = Input.getBuffer(); 7500f3f357eSJacob Lambert 7510f3f357eSJacob Lambert // Read up to the next new line. 7520f3f357eSJacob Lambert assert(FC[ReadChars] == '\n' && "The bundle should end with a new line."); 7530f3f357eSJacob Lambert 7540f3f357eSJacob Lambert size_t TripleEnd = ReadChars = FC.find("\n", ReadChars + 1); 7550f3f357eSJacob Lambert if (TripleEnd != FC.npos) 7560f3f357eSJacob Lambert // Next time we read after the new line. 7570f3f357eSJacob Lambert ++ReadChars; 7580f3f357eSJacob Lambert 7590f3f357eSJacob Lambert return Error::success(); 7600f3f357eSJacob Lambert } 7610f3f357eSJacob Lambert 7620f3f357eSJacob Lambert Error ReadBundle(raw_ostream &OS, MemoryBuffer &Input) final { 7630f3f357eSJacob Lambert StringRef FC = Input.getBuffer(); 7640f3f357eSJacob Lambert size_t BundleStart = ReadChars; 7650f3f357eSJacob Lambert 7660f3f357eSJacob Lambert // Find end of the bundle. 7670f3f357eSJacob Lambert size_t BundleEnd = ReadChars = FC.find(BundleEndString, ReadChars); 7680f3f357eSJacob Lambert 7690f3f357eSJacob Lambert StringRef Bundle(&FC.data()[BundleStart], BundleEnd - BundleStart); 7700f3f357eSJacob Lambert OS << Bundle; 7710f3f357eSJacob Lambert 7720f3f357eSJacob Lambert return Error::success(); 7730f3f357eSJacob Lambert } 7740f3f357eSJacob Lambert 7750f3f357eSJacob Lambert Error WriteHeader(raw_fd_ostream &OS, 7760f3f357eSJacob Lambert ArrayRef<std::unique_ptr<MemoryBuffer>> Inputs) final { 7770f3f357eSJacob Lambert return Error::success(); 7780f3f357eSJacob Lambert } 7790f3f357eSJacob Lambert 7800f3f357eSJacob Lambert Error WriteBundleStart(raw_fd_ostream &OS, StringRef TargetTriple) final { 7810f3f357eSJacob Lambert OS << BundleStartString << TargetTriple << "\n"; 7820f3f357eSJacob Lambert return Error::success(); 7830f3f357eSJacob Lambert } 7840f3f357eSJacob Lambert 7850f3f357eSJacob Lambert Error WriteBundleEnd(raw_fd_ostream &OS, StringRef TargetTriple) final { 7860f3f357eSJacob Lambert OS << BundleEndString << TargetTriple << "\n"; 7870f3f357eSJacob Lambert return Error::success(); 7880f3f357eSJacob Lambert } 7890f3f357eSJacob Lambert 7900f3f357eSJacob Lambert Error WriteBundle(raw_fd_ostream &OS, MemoryBuffer &Input) final { 7910f3f357eSJacob Lambert OS << Input.getBuffer(); 7920f3f357eSJacob Lambert return Error::success(); 7930f3f357eSJacob Lambert } 7940f3f357eSJacob Lambert 7950f3f357eSJacob Lambert public: 7960f3f357eSJacob Lambert TextFileHandler(StringRef Comment) : Comment(Comment), ReadChars(0) { 7970f3f357eSJacob Lambert BundleStartString = 7980f3f357eSJacob Lambert "\n" + Comment.str() + " " OFFLOAD_BUNDLER_MAGIC_STR "__START__ "; 7990f3f357eSJacob Lambert BundleEndString = 8000f3f357eSJacob Lambert "\n" + Comment.str() + " " OFFLOAD_BUNDLER_MAGIC_STR "__END__ "; 8010f3f357eSJacob Lambert } 8020f3f357eSJacob Lambert 8030f3f357eSJacob Lambert Error listBundleIDsCallback(MemoryBuffer &Input, 8040f3f357eSJacob Lambert const BundleInfo &Info) final { 8050f3f357eSJacob Lambert // TODO: To list bundle IDs in a bundled text file we need to go through 8060f3f357eSJacob Lambert // all bundles. The format of bundled text file may need to include a 8070f3f357eSJacob Lambert // header if the performance of listing bundle IDs of bundled text file is 8080f3f357eSJacob Lambert // important. 8090f3f357eSJacob Lambert ReadChars = Input.getBuffer().find(BundleEndString, ReadChars); 8100f3f357eSJacob Lambert if (Error Err = ReadBundleEnd(Input)) 8110f3f357eSJacob Lambert return Err; 8120f3f357eSJacob Lambert return Error::success(); 8130f3f357eSJacob Lambert } 8140f3f357eSJacob Lambert }; 815b6942a28SBenjamin Kramer } // namespace 8160f3f357eSJacob Lambert 8170f3f357eSJacob Lambert /// Return an appropriate object file handler. We use the specific object 8180f3f357eSJacob Lambert /// handler if we know how to deal with that format, otherwise we use a default 8190f3f357eSJacob Lambert /// binary file handler. 8200f3f357eSJacob Lambert static std::unique_ptr<FileHandler> 8210f3f357eSJacob Lambert CreateObjectFileHandler(MemoryBuffer &FirstInput, 8220f3f357eSJacob Lambert const OffloadBundlerConfig &BundlerConfig) { 8230f3f357eSJacob Lambert // Check if the input file format is one that we know how to deal with. 8240f3f357eSJacob Lambert Expected<std::unique_ptr<Binary>> BinaryOrErr = createBinary(FirstInput); 8250f3f357eSJacob Lambert 8260f3f357eSJacob Lambert // We only support regular object files. If failed to open the input as a 8270f3f357eSJacob Lambert // known binary or this is not an object file use the default binary handler. 8280f3f357eSJacob Lambert if (errorToBool(BinaryOrErr.takeError()) || !isa<ObjectFile>(*BinaryOrErr)) 8290f3f357eSJacob Lambert return std::make_unique<BinaryFileHandler>(BundlerConfig); 8300f3f357eSJacob Lambert 8310f3f357eSJacob Lambert // Otherwise create an object file handler. The handler will be owned by the 8320f3f357eSJacob Lambert // client of this function. 8330f3f357eSJacob Lambert return std::make_unique<ObjectFileHandler>( 8340f3f357eSJacob Lambert std::unique_ptr<ObjectFile>(cast<ObjectFile>(BinaryOrErr->release())), 8350f3f357eSJacob Lambert BundlerConfig); 8360f3f357eSJacob Lambert } 8370f3f357eSJacob Lambert 8380f3f357eSJacob Lambert /// Return an appropriate handler given the input files and options. 8390f3f357eSJacob Lambert static Expected<std::unique_ptr<FileHandler>> 8400f3f357eSJacob Lambert CreateFileHandler(MemoryBuffer &FirstInput, 8410f3f357eSJacob Lambert const OffloadBundlerConfig &BundlerConfig) { 8420f3f357eSJacob Lambert std::string FilesType = BundlerConfig.FilesType; 8430f3f357eSJacob Lambert 8440f3f357eSJacob Lambert if (FilesType == "i") 8450f3f357eSJacob Lambert return std::make_unique<TextFileHandler>(/*Comment=*/"//"); 8460f3f357eSJacob Lambert if (FilesType == "ii") 8470f3f357eSJacob Lambert return std::make_unique<TextFileHandler>(/*Comment=*/"//"); 8480f3f357eSJacob Lambert if (FilesType == "cui") 8490f3f357eSJacob Lambert return std::make_unique<TextFileHandler>(/*Comment=*/"//"); 850*e8f41fdbSYaxun (Sam) Liu if (FilesType == "hipi") 851*e8f41fdbSYaxun (Sam) Liu return std::make_unique<TextFileHandler>(/*Comment=*/"//"); 8520f3f357eSJacob Lambert // TODO: `.d` should be eventually removed once `-M` and its variants are 8530f3f357eSJacob Lambert // handled properly in offload compilation. 8540f3f357eSJacob Lambert if (FilesType == "d") 8550f3f357eSJacob Lambert return std::make_unique<TextFileHandler>(/*Comment=*/"#"); 8560f3f357eSJacob Lambert if (FilesType == "ll") 8570f3f357eSJacob Lambert return std::make_unique<TextFileHandler>(/*Comment=*/";"); 8580f3f357eSJacob Lambert if (FilesType == "bc") 8590f3f357eSJacob Lambert return std::make_unique<BinaryFileHandler>(BundlerConfig); 8600f3f357eSJacob Lambert if (FilesType == "s") 8610f3f357eSJacob Lambert return std::make_unique<TextFileHandler>(/*Comment=*/"#"); 8620f3f357eSJacob Lambert if (FilesType == "o") 8630f3f357eSJacob Lambert return CreateObjectFileHandler(FirstInput, BundlerConfig); 8640f3f357eSJacob Lambert if (FilesType == "a") 8650f3f357eSJacob Lambert return CreateObjectFileHandler(FirstInput, BundlerConfig); 8660f3f357eSJacob Lambert if (FilesType == "gch") 8670f3f357eSJacob Lambert return std::make_unique<BinaryFileHandler>(BundlerConfig); 8680f3f357eSJacob Lambert if (FilesType == "ast") 8690f3f357eSJacob Lambert return std::make_unique<BinaryFileHandler>(BundlerConfig); 8700f3f357eSJacob Lambert 8710f3f357eSJacob Lambert return createStringError(errc::invalid_argument, 8720f3f357eSJacob Lambert "'" + FilesType + "': invalid file type specified"); 8730f3f357eSJacob Lambert } 8740f3f357eSJacob Lambert 8750f3f357eSJacob Lambert // List bundle IDs. Return true if an error was found. 8765b11caa8Sraghavmedicherla Error OffloadBundler::ListBundleIDsInFile( 8775b11caa8Sraghavmedicherla StringRef InputFileName, const OffloadBundlerConfig &BundlerConfig) { 8780f3f357eSJacob Lambert // Open Input file. 8790f3f357eSJacob Lambert ErrorOr<std::unique_ptr<MemoryBuffer>> CodeOrErr = 8800f3f357eSJacob Lambert MemoryBuffer::getFileOrSTDIN(InputFileName); 8810f3f357eSJacob Lambert if (std::error_code EC = CodeOrErr.getError()) 8820f3f357eSJacob Lambert return createFileError(InputFileName, EC); 8830f3f357eSJacob Lambert 8840f3f357eSJacob Lambert MemoryBuffer &Input = **CodeOrErr; 8850f3f357eSJacob Lambert 8860f3f357eSJacob Lambert // Select the right files handler. 8870f3f357eSJacob Lambert Expected<std::unique_ptr<FileHandler>> FileHandlerOrErr = 8880f3f357eSJacob Lambert CreateFileHandler(Input, BundlerConfig); 8890f3f357eSJacob Lambert if (!FileHandlerOrErr) 8900f3f357eSJacob Lambert return FileHandlerOrErr.takeError(); 8910f3f357eSJacob Lambert 8920f3f357eSJacob Lambert std::unique_ptr<FileHandler> &FH = *FileHandlerOrErr; 8930f3f357eSJacob Lambert assert(FH); 8940f3f357eSJacob Lambert return FH->listBundleIDs(Input); 8950f3f357eSJacob Lambert } 8960f3f357eSJacob Lambert 8970f3f357eSJacob Lambert /// Bundle the files. Return true if an error was found. 8980f3f357eSJacob Lambert Error OffloadBundler::BundleFiles() { 8990f3f357eSJacob Lambert std::error_code EC; 9000f3f357eSJacob Lambert 9010f3f357eSJacob Lambert // Create output file. 9025b11caa8Sraghavmedicherla raw_fd_ostream OutputFile(BundlerConfig.OutputFileNames.front(), EC, 9035b11caa8Sraghavmedicherla sys::fs::OF_None); 9040f3f357eSJacob Lambert if (EC) 9050f3f357eSJacob Lambert return createFileError(BundlerConfig.OutputFileNames.front(), EC); 9060f3f357eSJacob Lambert 9070f3f357eSJacob Lambert // Open input files. 9080f3f357eSJacob Lambert SmallVector<std::unique_ptr<MemoryBuffer>, 8u> InputBuffers; 9090f3f357eSJacob Lambert InputBuffers.reserve(BundlerConfig.InputFileNames.size()); 9100f3f357eSJacob Lambert for (auto &I : BundlerConfig.InputFileNames) { 9110f3f357eSJacob Lambert ErrorOr<std::unique_ptr<MemoryBuffer>> CodeOrErr = 9120f3f357eSJacob Lambert MemoryBuffer::getFileOrSTDIN(I); 9130f3f357eSJacob Lambert if (std::error_code EC = CodeOrErr.getError()) 9140f3f357eSJacob Lambert return createFileError(I, EC); 9150f3f357eSJacob Lambert InputBuffers.emplace_back(std::move(*CodeOrErr)); 9160f3f357eSJacob Lambert } 9170f3f357eSJacob Lambert 9180f3f357eSJacob Lambert // Get the file handler. We use the host buffer as reference. 9190f3f357eSJacob Lambert assert((BundlerConfig.HostInputIndex != ~0u || BundlerConfig.AllowNoHost) && 9200f3f357eSJacob Lambert "Host input index undefined??"); 9215b11caa8Sraghavmedicherla Expected<std::unique_ptr<FileHandler>> FileHandlerOrErr = CreateFileHandler( 9225b11caa8Sraghavmedicherla *InputBuffers[BundlerConfig.AllowNoHost ? 0 9230f3f357eSJacob Lambert : BundlerConfig.HostInputIndex], 9240f3f357eSJacob Lambert BundlerConfig); 9250f3f357eSJacob Lambert if (!FileHandlerOrErr) 9260f3f357eSJacob Lambert return FileHandlerOrErr.takeError(); 9270f3f357eSJacob Lambert 9280f3f357eSJacob Lambert std::unique_ptr<FileHandler> &FH = *FileHandlerOrErr; 9290f3f357eSJacob Lambert assert(FH); 9300f3f357eSJacob Lambert 9310f3f357eSJacob Lambert // Write header. 9320f3f357eSJacob Lambert if (Error Err = FH->WriteHeader(OutputFile, InputBuffers)) 9330f3f357eSJacob Lambert return Err; 9340f3f357eSJacob Lambert 9350f3f357eSJacob Lambert // Write all bundles along with the start/end markers. If an error was found 9360f3f357eSJacob Lambert // writing the end of the bundle component, abort the bundle writing. 9370f3f357eSJacob Lambert auto Input = InputBuffers.begin(); 9380f3f357eSJacob Lambert for (auto &Triple : BundlerConfig.TargetNames) { 9390f3f357eSJacob Lambert if (Error Err = FH->WriteBundleStart(OutputFile, Triple)) 9400f3f357eSJacob Lambert return Err; 9410f3f357eSJacob Lambert if (Error Err = FH->WriteBundle(OutputFile, **Input)) 9420f3f357eSJacob Lambert return Err; 9430f3f357eSJacob Lambert if (Error Err = FH->WriteBundleEnd(OutputFile, Triple)) 9440f3f357eSJacob Lambert return Err; 9450f3f357eSJacob Lambert ++Input; 9460f3f357eSJacob Lambert } 9470f3f357eSJacob Lambert return Error::success(); 9480f3f357eSJacob Lambert } 9490f3f357eSJacob Lambert 9500f3f357eSJacob Lambert // Unbundle the files. Return true if an error was found. 9510f3f357eSJacob Lambert Error OffloadBundler::UnbundleFiles() { 9520f3f357eSJacob Lambert // Open Input file. 9530f3f357eSJacob Lambert ErrorOr<std::unique_ptr<MemoryBuffer>> CodeOrErr = 9540f3f357eSJacob Lambert MemoryBuffer::getFileOrSTDIN(BundlerConfig.InputFileNames.front()); 9550f3f357eSJacob Lambert if (std::error_code EC = CodeOrErr.getError()) 9560f3f357eSJacob Lambert return createFileError(BundlerConfig.InputFileNames.front(), EC); 9570f3f357eSJacob Lambert 9580f3f357eSJacob Lambert MemoryBuffer &Input = **CodeOrErr; 9590f3f357eSJacob Lambert 9600f3f357eSJacob Lambert // Select the right files handler. 9610f3f357eSJacob Lambert Expected<std::unique_ptr<FileHandler>> FileHandlerOrErr = 9620f3f357eSJacob Lambert CreateFileHandler(Input, BundlerConfig); 9630f3f357eSJacob Lambert if (!FileHandlerOrErr) 9640f3f357eSJacob Lambert return FileHandlerOrErr.takeError(); 9650f3f357eSJacob Lambert 9660f3f357eSJacob Lambert std::unique_ptr<FileHandler> &FH = *FileHandlerOrErr; 9670f3f357eSJacob Lambert assert(FH); 9680f3f357eSJacob Lambert 9690f3f357eSJacob Lambert // Read the header of the bundled file. 9700f3f357eSJacob Lambert if (Error Err = FH->ReadHeader(Input)) 9710f3f357eSJacob Lambert return Err; 9720f3f357eSJacob Lambert 9730f3f357eSJacob Lambert // Create a work list that consist of the map triple/output file. 9740f3f357eSJacob Lambert StringMap<StringRef> Worklist; 9750f3f357eSJacob Lambert auto Output = BundlerConfig.OutputFileNames.begin(); 9760f3f357eSJacob Lambert for (auto &Triple : BundlerConfig.TargetNames) { 9770f3f357eSJacob Lambert Worklist[Triple] = *Output; 9780f3f357eSJacob Lambert ++Output; 9790f3f357eSJacob Lambert } 9800f3f357eSJacob Lambert 9810f3f357eSJacob Lambert // Read all the bundles that are in the work list. If we find no bundles we 9820f3f357eSJacob Lambert // assume the file is meant for the host target. 9830f3f357eSJacob Lambert bool FoundHostBundle = false; 9840f3f357eSJacob Lambert while (!Worklist.empty()) { 9852c5d49cfSFangrui Song Expected<std::optional<StringRef>> CurTripleOrErr = 9862c5d49cfSFangrui Song FH->ReadBundleStart(Input); 9870f3f357eSJacob Lambert if (!CurTripleOrErr) 9880f3f357eSJacob Lambert return CurTripleOrErr.takeError(); 9890f3f357eSJacob Lambert 9900f3f357eSJacob Lambert // We don't have more bundles. 9910f3f357eSJacob Lambert if (!*CurTripleOrErr) 9920f3f357eSJacob Lambert break; 9930f3f357eSJacob Lambert 9940f3f357eSJacob Lambert StringRef CurTriple = **CurTripleOrErr; 9950f3f357eSJacob Lambert assert(!CurTriple.empty()); 9960f3f357eSJacob Lambert 997844b84afSYaxun (Sam) Liu auto Output = Worklist.begin(); 998844b84afSYaxun (Sam) Liu for (auto E = Worklist.end(); Output != E; Output++) { 999844b84afSYaxun (Sam) Liu if (isCodeObjectCompatible( 1000844b84afSYaxun (Sam) Liu OffloadTargetInfo(CurTriple, BundlerConfig), 1001844b84afSYaxun (Sam) Liu OffloadTargetInfo((*Output).first(), BundlerConfig))) { 1002844b84afSYaxun (Sam) Liu break; 1003844b84afSYaxun (Sam) Liu } 1004844b84afSYaxun (Sam) Liu } 1005844b84afSYaxun (Sam) Liu 10060f3f357eSJacob Lambert if (Output == Worklist.end()) 10070f3f357eSJacob Lambert continue; 10080f3f357eSJacob Lambert // Check if the output file can be opened and copy the bundle to it. 10090f3f357eSJacob Lambert std::error_code EC; 1010844b84afSYaxun (Sam) Liu raw_fd_ostream OutputFile((*Output).second, EC, sys::fs::OF_None); 10110f3f357eSJacob Lambert if (EC) 1012844b84afSYaxun (Sam) Liu return createFileError((*Output).second, EC); 10130f3f357eSJacob Lambert if (Error Err = FH->ReadBundle(OutputFile, Input)) 10140f3f357eSJacob Lambert return Err; 10150f3f357eSJacob Lambert if (Error Err = FH->ReadBundleEnd(Input)) 10160f3f357eSJacob Lambert return Err; 10170f3f357eSJacob Lambert Worklist.erase(Output); 10180f3f357eSJacob Lambert 10190f3f357eSJacob Lambert // Record if we found the host bundle. 10200f3f357eSJacob Lambert auto OffloadInfo = OffloadTargetInfo(CurTriple, BundlerConfig); 10210f3f357eSJacob Lambert if (OffloadInfo.hasHostKind()) 10220f3f357eSJacob Lambert FoundHostBundle = true; 10230f3f357eSJacob Lambert } 10240f3f357eSJacob Lambert 10250f3f357eSJacob Lambert if (!BundlerConfig.AllowMissingBundles && !Worklist.empty()) { 10260f3f357eSJacob Lambert std::string ErrMsg = "Can't find bundles for"; 10270f3f357eSJacob Lambert std::set<StringRef> Sorted; 10280f3f357eSJacob Lambert for (auto &E : Worklist) 10290f3f357eSJacob Lambert Sorted.insert(E.first()); 10300f3f357eSJacob Lambert unsigned I = 0; 10310f3f357eSJacob Lambert unsigned Last = Sorted.size() - 1; 10320f3f357eSJacob Lambert for (auto &E : Sorted) { 10330f3f357eSJacob Lambert if (I != 0 && Last > 1) 10340f3f357eSJacob Lambert ErrMsg += ","; 10350f3f357eSJacob Lambert ErrMsg += " "; 10360f3f357eSJacob Lambert if (I == Last && I != 0) 10370f3f357eSJacob Lambert ErrMsg += "and "; 10380f3f357eSJacob Lambert ErrMsg += E.str(); 10390f3f357eSJacob Lambert ++I; 10400f3f357eSJacob Lambert } 10410f3f357eSJacob Lambert return createStringError(inconvertibleErrorCode(), ErrMsg); 10420f3f357eSJacob Lambert } 10430f3f357eSJacob Lambert 10440f3f357eSJacob Lambert // If no bundles were found, assume the input file is the host bundle and 10450f3f357eSJacob Lambert // create empty files for the remaining targets. 10460f3f357eSJacob Lambert if (Worklist.size() == BundlerConfig.TargetNames.size()) { 10470f3f357eSJacob Lambert for (auto &E : Worklist) { 10480f3f357eSJacob Lambert std::error_code EC; 10490f3f357eSJacob Lambert raw_fd_ostream OutputFile(E.second, EC, sys::fs::OF_None); 10500f3f357eSJacob Lambert if (EC) 10510f3f357eSJacob Lambert return createFileError(E.second, EC); 10520f3f357eSJacob Lambert 10530f3f357eSJacob Lambert // If this entry has a host kind, copy the input file to the output file. 10540f3f357eSJacob Lambert auto OffloadInfo = OffloadTargetInfo(E.getKey(), BundlerConfig); 10550f3f357eSJacob Lambert if (OffloadInfo.hasHostKind()) 10560f3f357eSJacob Lambert OutputFile.write(Input.getBufferStart(), Input.getBufferSize()); 10570f3f357eSJacob Lambert } 10580f3f357eSJacob Lambert return Error::success(); 10590f3f357eSJacob Lambert } 10600f3f357eSJacob Lambert 10610f3f357eSJacob Lambert // If we found elements, we emit an error if none of those were for the host 10620f3f357eSJacob Lambert // in case host bundle name was provided in command line. 10630f3f357eSJacob Lambert if (!FoundHostBundle && BundlerConfig.HostInputIndex != ~0u) 10640f3f357eSJacob Lambert return createStringError(inconvertibleErrorCode(), 10650f3f357eSJacob Lambert "Can't find bundle for the host target"); 10660f3f357eSJacob Lambert 10670f3f357eSJacob Lambert // If we still have any elements in the worklist, create empty files for them. 10680f3f357eSJacob Lambert for (auto &E : Worklist) { 10690f3f357eSJacob Lambert std::error_code EC; 10700f3f357eSJacob Lambert raw_fd_ostream OutputFile(E.second, EC, sys::fs::OF_None); 10710f3f357eSJacob Lambert if (EC) 10720f3f357eSJacob Lambert return createFileError(E.second, EC); 10730f3f357eSJacob Lambert } 10740f3f357eSJacob Lambert 10750f3f357eSJacob Lambert return Error::success(); 10760f3f357eSJacob Lambert } 10770f3f357eSJacob Lambert 10780f3f357eSJacob Lambert static Archive::Kind getDefaultArchiveKindForHost() { 10790f3f357eSJacob Lambert return Triple(sys::getDefaultTargetTriple()).isOSDarwin() ? Archive::K_DARWIN 10800f3f357eSJacob Lambert : Archive::K_GNU; 10810f3f357eSJacob Lambert } 10820f3f357eSJacob Lambert 10830f3f357eSJacob Lambert /// @brief Computes a list of targets among all given targets which are 10840f3f357eSJacob Lambert /// compatible with this code object 1085b978fa28SSimon Pilgrim /// @param [in] CodeObjectInfo Code Object 1086b978fa28SSimon Pilgrim /// @param [out] CompatibleTargets List of all compatible targets among all 10870f3f357eSJacob Lambert /// given targets 10880f3f357eSJacob Lambert /// @return false, if no compatible target is found. 10890f3f357eSJacob Lambert static bool 10900f3f357eSJacob Lambert getCompatibleOffloadTargets(OffloadTargetInfo &CodeObjectInfo, 10910f3f357eSJacob Lambert SmallVectorImpl<StringRef> &CompatibleTargets, 10920f3f357eSJacob Lambert const OffloadBundlerConfig &BundlerConfig) { 10930f3f357eSJacob Lambert if (!CompatibleTargets.empty()) { 10940f3f357eSJacob Lambert DEBUG_WITH_TYPE("CodeObjectCompatibility", 10950f3f357eSJacob Lambert dbgs() << "CompatibleTargets list should be empty\n"); 10960f3f357eSJacob Lambert return false; 10970f3f357eSJacob Lambert } 10980f3f357eSJacob Lambert for (auto &Target : BundlerConfig.TargetNames) { 10990f3f357eSJacob Lambert auto TargetInfo = OffloadTargetInfo(Target, BundlerConfig); 11000f3f357eSJacob Lambert if (isCodeObjectCompatible(CodeObjectInfo, TargetInfo)) 11010f3f357eSJacob Lambert CompatibleTargets.push_back(Target); 11020f3f357eSJacob Lambert } 11030f3f357eSJacob Lambert return !CompatibleTargets.empty(); 11040f3f357eSJacob Lambert } 11050f3f357eSJacob Lambert 11060f3f357eSJacob Lambert /// UnbundleArchive takes an archive file (".a") as input containing bundled 11070f3f357eSJacob Lambert /// code object files, and a list of offload targets (not host), and extracts 11080f3f357eSJacob Lambert /// the code objects into a new archive file for each offload target. Each 11090f3f357eSJacob Lambert /// resulting archive file contains all code object files corresponding to that 11100f3f357eSJacob Lambert /// particular offload target. The created archive file does not 11110f3f357eSJacob Lambert /// contain an index of the symbols and code object files are named as 11120f3f357eSJacob Lambert /// <<Parent Bundle Name>-<CodeObject's GPUArch>>, with ':' replaced with '_'. 11130f3f357eSJacob Lambert Error OffloadBundler::UnbundleArchive() { 11140f3f357eSJacob Lambert std::vector<std::unique_ptr<MemoryBuffer>> ArchiveBuffers; 11150f3f357eSJacob Lambert 11160f3f357eSJacob Lambert /// Map of target names with list of object files that will form the device 11170f3f357eSJacob Lambert /// specific archive for that target 11180f3f357eSJacob Lambert StringMap<std::vector<NewArchiveMember>> OutputArchivesMap; 11190f3f357eSJacob Lambert 11200f3f357eSJacob Lambert // Map of target names and output archive filenames 11210f3f357eSJacob Lambert StringMap<StringRef> TargetOutputFileNameMap; 11220f3f357eSJacob Lambert 11230f3f357eSJacob Lambert auto Output = BundlerConfig.OutputFileNames.begin(); 11240f3f357eSJacob Lambert for (auto &Target : BundlerConfig.TargetNames) { 11250f3f357eSJacob Lambert TargetOutputFileNameMap[Target] = *Output; 11260f3f357eSJacob Lambert ++Output; 11270f3f357eSJacob Lambert } 11280f3f357eSJacob Lambert 11290f3f357eSJacob Lambert StringRef IFName = BundlerConfig.InputFileNames.front(); 11300f3f357eSJacob Lambert 11310f3f357eSJacob Lambert ErrorOr<std::unique_ptr<MemoryBuffer>> BufOrErr = 11320f3f357eSJacob Lambert MemoryBuffer::getFileOrSTDIN(IFName, true, false); 11330f3f357eSJacob Lambert if (std::error_code EC = BufOrErr.getError()) 11340f3f357eSJacob Lambert return createFileError(BundlerConfig.InputFileNames.front(), EC); 11350f3f357eSJacob Lambert 11360f3f357eSJacob Lambert ArchiveBuffers.push_back(std::move(*BufOrErr)); 11370f3f357eSJacob Lambert Expected<std::unique_ptr<llvm::object::Archive>> LibOrErr = 11380f3f357eSJacob Lambert Archive::create(ArchiveBuffers.back()->getMemBufferRef()); 11390f3f357eSJacob Lambert if (!LibOrErr) 11400f3f357eSJacob Lambert return LibOrErr.takeError(); 11410f3f357eSJacob Lambert 11420f3f357eSJacob Lambert auto Archive = std::move(*LibOrErr); 11430f3f357eSJacob Lambert 11440f3f357eSJacob Lambert Error ArchiveErr = Error::success(); 11450f3f357eSJacob Lambert auto ChildEnd = Archive->child_end(); 11460f3f357eSJacob Lambert 11470f3f357eSJacob Lambert /// Iterate over all bundled code object files in the input archive. 11480f3f357eSJacob Lambert for (auto ArchiveIter = Archive->child_begin(ArchiveErr); 11490f3f357eSJacob Lambert ArchiveIter != ChildEnd; ++ArchiveIter) { 11500f3f357eSJacob Lambert if (ArchiveErr) 11510f3f357eSJacob Lambert return ArchiveErr; 11520f3f357eSJacob Lambert auto ArchiveChildNameOrErr = (*ArchiveIter).getName(); 11530f3f357eSJacob Lambert if (!ArchiveChildNameOrErr) 11540f3f357eSJacob Lambert return ArchiveChildNameOrErr.takeError(); 11550f3f357eSJacob Lambert 11560f3f357eSJacob Lambert StringRef BundledObjectFile = sys::path::filename(*ArchiveChildNameOrErr); 11570f3f357eSJacob Lambert 11580f3f357eSJacob Lambert auto CodeObjectBufferRefOrErr = (*ArchiveIter).getMemoryBufferRef(); 11590f3f357eSJacob Lambert if (!CodeObjectBufferRefOrErr) 11600f3f357eSJacob Lambert return CodeObjectBufferRefOrErr.takeError(); 11610f3f357eSJacob Lambert 11620f3f357eSJacob Lambert auto CodeObjectBuffer = 11630f3f357eSJacob Lambert MemoryBuffer::getMemBuffer(*CodeObjectBufferRefOrErr, false); 11640f3f357eSJacob Lambert 11650f3f357eSJacob Lambert Expected<std::unique_ptr<FileHandler>> FileHandlerOrErr = 11660f3f357eSJacob Lambert CreateFileHandler(*CodeObjectBuffer, BundlerConfig); 11670f3f357eSJacob Lambert if (!FileHandlerOrErr) 11680f3f357eSJacob Lambert return FileHandlerOrErr.takeError(); 11690f3f357eSJacob Lambert 11700f3f357eSJacob Lambert std::unique_ptr<FileHandler> &FileHandler = *FileHandlerOrErr; 11710f3f357eSJacob Lambert assert(FileHandler && 11720f3f357eSJacob Lambert "FileHandle creation failed for file in the archive!"); 11730f3f357eSJacob Lambert 1174d22f050eSGregory Alfonso if (Error ReadErr = FileHandler->ReadHeader(*CodeObjectBuffer)) 11750f3f357eSJacob Lambert return ReadErr; 11760f3f357eSJacob Lambert 11772c5d49cfSFangrui Song Expected<std::optional<StringRef>> CurBundleIDOrErr = 11780f3f357eSJacob Lambert FileHandler->ReadBundleStart(*CodeObjectBuffer); 11790f3f357eSJacob Lambert if (!CurBundleIDOrErr) 11800f3f357eSJacob Lambert return CurBundleIDOrErr.takeError(); 11810f3f357eSJacob Lambert 11822c5d49cfSFangrui Song std::optional<StringRef> OptionalCurBundleID = *CurBundleIDOrErr; 11830f3f357eSJacob Lambert // No device code in this child, skip. 11847430894aSFangrui Song if (!OptionalCurBundleID) 11850f3f357eSJacob Lambert continue; 11860f3f357eSJacob Lambert StringRef CodeObject = *OptionalCurBundleID; 11870f3f357eSJacob Lambert 11880f3f357eSJacob Lambert // Process all bundle entries (CodeObjects) found in this child of input 11890f3f357eSJacob Lambert // archive. 11900f3f357eSJacob Lambert while (!CodeObject.empty()) { 11910f3f357eSJacob Lambert SmallVector<StringRef> CompatibleTargets; 11920f3f357eSJacob Lambert auto CodeObjectInfo = OffloadTargetInfo(CodeObject, BundlerConfig); 11930f3f357eSJacob Lambert if (CodeObjectInfo.hasHostKind()) { 11940f3f357eSJacob Lambert // Do nothing, we don't extract host code yet. 11955b11caa8Sraghavmedicherla } else if (getCompatibleOffloadTargets(CodeObjectInfo, CompatibleTargets, 11960f3f357eSJacob Lambert BundlerConfig)) { 11970f3f357eSJacob Lambert std::string BundleData; 11980f3f357eSJacob Lambert raw_string_ostream DataStream(BundleData); 1199d22f050eSGregory Alfonso if (Error Err = FileHandler->ReadBundle(DataStream, *CodeObjectBuffer)) 12000f3f357eSJacob Lambert return Err; 12010f3f357eSJacob Lambert 12020f3f357eSJacob Lambert for (auto &CompatibleTarget : CompatibleTargets) { 12030f3f357eSJacob Lambert SmallString<128> BundledObjectFileName; 12040f3f357eSJacob Lambert BundledObjectFileName.assign(BundledObjectFile); 12050f3f357eSJacob Lambert auto OutputBundleName = 12060f3f357eSJacob Lambert Twine(llvm::sys::path::stem(BundledObjectFileName) + "-" + 12070f3f357eSJacob Lambert CodeObject + 12080f3f357eSJacob Lambert getDeviceLibraryFileName(BundledObjectFileName, 1209844b84afSYaxun (Sam) Liu CodeObjectInfo.TargetID)) 12100f3f357eSJacob Lambert .str(); 12110f3f357eSJacob Lambert // Replace ':' in optional target feature list with '_' to ensure 12120f3f357eSJacob Lambert // cross-platform validity. 12130f3f357eSJacob Lambert std::replace(OutputBundleName.begin(), OutputBundleName.end(), ':', 12140f3f357eSJacob Lambert '_'); 12150f3f357eSJacob Lambert 12160f3f357eSJacob Lambert std::unique_ptr<MemoryBuffer> MemBuf = MemoryBuffer::getMemBufferCopy( 12170f3f357eSJacob Lambert DataStream.str(), OutputBundleName); 12180f3f357eSJacob Lambert ArchiveBuffers.push_back(std::move(MemBuf)); 12190f3f357eSJacob Lambert llvm::MemoryBufferRef MemBufRef = 12200f3f357eSJacob Lambert MemoryBufferRef(*(ArchiveBuffers.back())); 12210f3f357eSJacob Lambert 12220f3f357eSJacob Lambert // For inserting <CompatibleTarget, list<CodeObject>> entry in 12230f3f357eSJacob Lambert // OutputArchivesMap. 12240f3f357eSJacob Lambert if (OutputArchivesMap.find(CompatibleTarget) == 12250f3f357eSJacob Lambert OutputArchivesMap.end()) { 12260f3f357eSJacob Lambert 12270f3f357eSJacob Lambert std::vector<NewArchiveMember> ArchiveMembers; 12280f3f357eSJacob Lambert ArchiveMembers.push_back(NewArchiveMember(MemBufRef)); 12290f3f357eSJacob Lambert OutputArchivesMap.insert_or_assign(CompatibleTarget, 12300f3f357eSJacob Lambert std::move(ArchiveMembers)); 12310f3f357eSJacob Lambert } else { 12320f3f357eSJacob Lambert OutputArchivesMap[CompatibleTarget].push_back( 12330f3f357eSJacob Lambert NewArchiveMember(MemBufRef)); 12340f3f357eSJacob Lambert } 12350f3f357eSJacob Lambert } 12360f3f357eSJacob Lambert } 12370f3f357eSJacob Lambert 1238d22f050eSGregory Alfonso if (Error Err = FileHandler->ReadBundleEnd(*CodeObjectBuffer)) 12390f3f357eSJacob Lambert return Err; 12400f3f357eSJacob Lambert 12412c5d49cfSFangrui Song Expected<std::optional<StringRef>> NextTripleOrErr = 12420f3f357eSJacob Lambert FileHandler->ReadBundleStart(*CodeObjectBuffer); 12430f3f357eSJacob Lambert if (!NextTripleOrErr) 12440f3f357eSJacob Lambert return NextTripleOrErr.takeError(); 12450f3f357eSJacob Lambert 1246a9481170SKazu Hirata CodeObject = ((*NextTripleOrErr).has_value()) ? **NextTripleOrErr : ""; 12470f3f357eSJacob Lambert } // End of processing of all bundle entries of this child of input archive. 12480f3f357eSJacob Lambert } // End of while over children of input archive. 12490f3f357eSJacob Lambert 12500f3f357eSJacob Lambert assert(!ArchiveErr && "Error occurred while reading archive!"); 12510f3f357eSJacob Lambert 12520f3f357eSJacob Lambert /// Write out an archive for each target 12530f3f357eSJacob Lambert for (auto &Target : BundlerConfig.TargetNames) { 12540f3f357eSJacob Lambert StringRef FileName = TargetOutputFileNameMap[Target]; 12550f3f357eSJacob Lambert StringMapIterator<std::vector<llvm::NewArchiveMember>> CurArchiveMembers = 12560f3f357eSJacob Lambert OutputArchivesMap.find(Target); 12570f3f357eSJacob Lambert if (CurArchiveMembers != OutputArchivesMap.end()) { 12580f3f357eSJacob Lambert if (Error WriteErr = writeArchive(FileName, CurArchiveMembers->getValue(), 12590f3f357eSJacob Lambert true, getDefaultArchiveKindForHost(), 12600f3f357eSJacob Lambert true, false, nullptr)) 12610f3f357eSJacob Lambert return WriteErr; 12620f3f357eSJacob Lambert } else if (!BundlerConfig.AllowMissingBundles) { 12630f3f357eSJacob Lambert std::string ErrMsg = 12640f3f357eSJacob Lambert Twine("no compatible code object found for the target '" + Target + 12650f3f357eSJacob Lambert "' in heterogeneous archive library: " + IFName) 12660f3f357eSJacob Lambert .str(); 12670f3f357eSJacob Lambert return createStringError(inconvertibleErrorCode(), ErrMsg); 12680f3f357eSJacob Lambert } else { // Create an empty archive file if no compatible code object is 12690f3f357eSJacob Lambert // found and "allow-missing-bundles" is enabled. It ensures that 12700f3f357eSJacob Lambert // the linker using output of this step doesn't complain about 12710f3f357eSJacob Lambert // the missing input file. 12720f3f357eSJacob Lambert std::vector<llvm::NewArchiveMember> EmptyArchive; 12730f3f357eSJacob Lambert EmptyArchive.clear(); 12740f3f357eSJacob Lambert if (Error WriteErr = writeArchive(FileName, EmptyArchive, true, 12750f3f357eSJacob Lambert getDefaultArchiveKindForHost(), true, 12760f3f357eSJacob Lambert false, nullptr)) 12770f3f357eSJacob Lambert return WriteErr; 12780f3f357eSJacob Lambert } 12790f3f357eSJacob Lambert } 12800f3f357eSJacob Lambert 12810f3f357eSJacob Lambert return Error::success(); 12820f3f357eSJacob Lambert } 1283