1 //===-- clang-offload-wrapper/ClangOffloadWrapper.cpp -----------*- C++ -*-===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8 ///
9 /// \file
10 /// Implementation of the offload wrapper tool. It takes offload target binaries
11 /// as input and creates wrapper bitcode file containing target binaries
12 /// packaged as data. Wrapper bitcode also includes initialization code which
13 /// registers target binaries in offloading runtime at program startup.
14 ///
15 //===----------------------------------------------------------------------===//
16 
17 #include "clang/Basic/Version.h"
18 #include "llvm/ADT/ArrayRef.h"
19 #include "llvm/ADT/Triple.h"
20 #include "llvm/BinaryFormat/ELF.h"
21 #include "llvm/Bitcode/BitcodeWriter.h"
22 #include "llvm/IR/Constants.h"
23 #include "llvm/IR/GlobalVariable.h"
24 #include "llvm/IR/IRBuilder.h"
25 #include "llvm/IR/LLVMContext.h"
26 #include "llvm/IR/Module.h"
27 #include "llvm/Object/ELFObjectFile.h"
28 #include "llvm/Object/ObjectFile.h"
29 #include "llvm/Support/CommandLine.h"
30 #include "llvm/Support/EndianStream.h"
31 #include "llvm/Support/Errc.h"
32 #include "llvm/Support/Error.h"
33 #include "llvm/Support/ErrorOr.h"
34 #include "llvm/Support/FileSystem.h"
35 #include "llvm/Support/MemoryBuffer.h"
36 #include "llvm/Support/Path.h"
37 #include "llvm/Support/Program.h"
38 #include "llvm/Support/Signals.h"
39 #include "llvm/Support/ToolOutputFile.h"
40 #include "llvm/Support/VCSRevision.h"
41 #include "llvm/Support/WithColor.h"
42 #include "llvm/Support/raw_ostream.h"
43 #include "llvm/Transforms/Utils/ModuleUtils.h"
44 #include <cassert>
45 #include <cstdint>
46 
47 #define OPENMP_OFFLOAD_IMAGE_VERSION "1.0"
48 
49 using namespace llvm;
50 using namespace llvm::object;
51 
52 static cl::opt<bool> Help("h", cl::desc("Alias for -help"), cl::Hidden);
53 
54 // Mark all our options with this category, everything else (except for -version
55 // and -help) will be hidden.
56 static cl::OptionCategory
57     ClangOffloadWrapperCategory("clang-offload-wrapper options");
58 
59 static cl::opt<std::string> Output("o", cl::Required,
60                                    cl::desc("Output filename"),
61                                    cl::value_desc("filename"),
62                                    cl::cat(ClangOffloadWrapperCategory));
63 
64 static cl::list<std::string> Inputs(cl::Positional, cl::OneOrMore,
65                                     cl::desc("<input files>"),
66                                     cl::cat(ClangOffloadWrapperCategory));
67 
68 static cl::opt<std::string>
69     Target("target", cl::Required,
70            cl::desc("Target triple for the output module"),
71            cl::value_desc("triple"), cl::cat(ClangOffloadWrapperCategory));
72 
73 static cl::opt<bool> SaveTemps(
74     "save-temps",
75     cl::desc("Save temporary files that may be produced by the tool. "
76              "This option forces print-out of the temporary files' names."),
77     cl::Hidden);
78 
79 static cl::opt<bool> AddOpenMPOffloadNotes(
80     "add-omp-offload-notes",
81     cl::desc("Add LLVMOMPOFFLOAD ELF notes to ELF device images."), cl::Hidden);
82 
83 namespace {
84 
85 class BinaryWrapper {
86   LLVMContext C;
87   Module M;
88 
89   StructType *EntryTy = nullptr;
90   StructType *ImageTy = nullptr;
91   StructType *DescTy = nullptr;
92 
93   std::string ToolName;
94   std::string ObjcopyPath;
95   // Temporary file names that may be created during adding notes
96   // to ELF offload images. Use -save-temps to keep them and also
97   // see their names. A temporary file's name includes the name
98   // of the original input ELF image, so you can easily match
99   // them, if you have multiple inputs.
100   std::vector<std::string> TempFiles;
101 
102 private:
getSizeTTy()103   IntegerType *getSizeTTy() {
104     switch (M.getDataLayout().getPointerTypeSize(Type::getInt8PtrTy(C))) {
105     case 4u:
106       return Type::getInt32Ty(C);
107     case 8u:
108       return Type::getInt64Ty(C);
109     }
110     llvm_unreachable("unsupported pointer type size");
111   }
112 
113   // struct __tgt_offload_entry {
114   //   void *addr;
115   //   char *name;
116   //   size_t size;
117   //   int32_t flags;
118   //   int32_t reserved;
119   // };
getEntryTy()120   StructType *getEntryTy() {
121     if (!EntryTy)
122       EntryTy = StructType::create("__tgt_offload_entry", Type::getInt8PtrTy(C),
123                                    Type::getInt8PtrTy(C), getSizeTTy(),
124                                    Type::getInt32Ty(C), Type::getInt32Ty(C));
125     return EntryTy;
126   }
127 
getEntryPtrTy()128   PointerType *getEntryPtrTy() { return PointerType::getUnqual(getEntryTy()); }
129 
130   // struct __tgt_device_image {
131   //   void *ImageStart;
132   //   void *ImageEnd;
133   //   __tgt_offload_entry *EntriesBegin;
134   //   __tgt_offload_entry *EntriesEnd;
135   // };
getDeviceImageTy()136   StructType *getDeviceImageTy() {
137     if (!ImageTy)
138       ImageTy = StructType::create("__tgt_device_image", Type::getInt8PtrTy(C),
139                                    Type::getInt8PtrTy(C), getEntryPtrTy(),
140                                    getEntryPtrTy());
141     return ImageTy;
142   }
143 
getDeviceImagePtrTy()144   PointerType *getDeviceImagePtrTy() {
145     return PointerType::getUnqual(getDeviceImageTy());
146   }
147 
148   // struct __tgt_bin_desc {
149   //   int32_t NumDeviceImages;
150   //   __tgt_device_image *DeviceImages;
151   //   __tgt_offload_entry *HostEntriesBegin;
152   //   __tgt_offload_entry *HostEntriesEnd;
153   // };
getBinDescTy()154   StructType *getBinDescTy() {
155     if (!DescTy)
156       DescTy = StructType::create("__tgt_bin_desc", Type::getInt32Ty(C),
157                                   getDeviceImagePtrTy(), getEntryPtrTy(),
158                                   getEntryPtrTy());
159     return DescTy;
160   }
161 
getBinDescPtrTy()162   PointerType *getBinDescPtrTy() {
163     return PointerType::getUnqual(getBinDescTy());
164   }
165 
166   /// Creates binary descriptor for the given device images. Binary descriptor
167   /// is an object that is passed to the offloading runtime at program startup
168   /// and it describes all device images available in the executable or shared
169   /// library. It is defined as follows
170   ///
171   /// __attribute__((visibility("hidden")))
172   /// extern __tgt_offload_entry *__start_omp_offloading_entries;
173   /// __attribute__((visibility("hidden")))
174   /// extern __tgt_offload_entry *__stop_omp_offloading_entries;
175   ///
176   /// static const char Image0[] = { <Bufs.front() contents> };
177   ///  ...
178   /// static const char ImageN[] = { <Bufs.back() contents> };
179   ///
180   /// static const __tgt_device_image Images[] = {
181   ///   {
182   ///     Image0,                            /*ImageStart*/
183   ///     Image0 + sizeof(Image0),           /*ImageEnd*/
184   ///     __start_omp_offloading_entries,    /*EntriesBegin*/
185   ///     __stop_omp_offloading_entries      /*EntriesEnd*/
186   ///   },
187   ///   ...
188   ///   {
189   ///     ImageN,                            /*ImageStart*/
190   ///     ImageN + sizeof(ImageN),           /*ImageEnd*/
191   ///     __start_omp_offloading_entries,    /*EntriesBegin*/
192   ///     __stop_omp_offloading_entries      /*EntriesEnd*/
193   ///   }
194   /// };
195   ///
196   /// static const __tgt_bin_desc BinDesc = {
197   ///   sizeof(Images) / sizeof(Images[0]),  /*NumDeviceImages*/
198   ///   Images,                              /*DeviceImages*/
199   ///   __start_omp_offloading_entries,      /*HostEntriesBegin*/
200   ///   __stop_omp_offloading_entries        /*HostEntriesEnd*/
201   /// };
202   ///
203   /// Global variable that represents BinDesc is returned.
createBinDesc(ArrayRef<ArrayRef<char>> Bufs)204   GlobalVariable *createBinDesc(ArrayRef<ArrayRef<char>> Bufs) {
205     // Create external begin/end symbols for the offload entries table.
206     auto *EntriesB = new GlobalVariable(
207         M, getEntryTy(), /*isConstant*/ true, GlobalValue::ExternalLinkage,
208         /*Initializer*/ nullptr, "__start_omp_offloading_entries");
209     EntriesB->setVisibility(GlobalValue::HiddenVisibility);
210     auto *EntriesE = new GlobalVariable(
211         M, getEntryTy(), /*isConstant*/ true, GlobalValue::ExternalLinkage,
212         /*Initializer*/ nullptr, "__stop_omp_offloading_entries");
213     EntriesE->setVisibility(GlobalValue::HiddenVisibility);
214 
215     // We assume that external begin/end symbols that we have created above will
216     // be defined by the linker. But linker will do that only if linker inputs
217     // have section with "omp_offloading_entries" name which is not guaranteed.
218     // So, we just create dummy zero sized object in the offload entries section
219     // to force linker to define those symbols.
220     auto *DummyInit =
221         ConstantAggregateZero::get(ArrayType::get(getEntryTy(), 0u));
222     auto *DummyEntry = new GlobalVariable(
223         M, DummyInit->getType(), true, GlobalVariable::ExternalLinkage,
224         DummyInit, "__dummy.omp_offloading.entry");
225     DummyEntry->setSection("omp_offloading_entries");
226     DummyEntry->setVisibility(GlobalValue::HiddenVisibility);
227 
228     auto *Zero = ConstantInt::get(getSizeTTy(), 0u);
229     Constant *ZeroZero[] = {Zero, Zero};
230 
231     // Create initializer for the images array.
232     SmallVector<Constant *, 4u> ImagesInits;
233     ImagesInits.reserve(Bufs.size());
234     for (ArrayRef<char> Buf : Bufs) {
235       auto *Data = ConstantDataArray::get(C, Buf);
236       auto *Image = new GlobalVariable(M, Data->getType(), /*isConstant*/ true,
237                                        GlobalVariable::InternalLinkage, Data,
238                                        ".omp_offloading.device_image");
239       Image->setUnnamedAddr(GlobalValue::UnnamedAddr::Global);
240 
241       auto *Size = ConstantInt::get(getSizeTTy(), Buf.size());
242       Constant *ZeroSize[] = {Zero, Size};
243 
244       auto *ImageB = ConstantExpr::getGetElementPtr(Image->getValueType(),
245                                                     Image, ZeroZero);
246       auto *ImageE = ConstantExpr::getGetElementPtr(Image->getValueType(),
247                                                     Image, ZeroSize);
248 
249       ImagesInits.push_back(ConstantStruct::get(getDeviceImageTy(), ImageB,
250                                                 ImageE, EntriesB, EntriesE));
251     }
252 
253     // Then create images array.
254     auto *ImagesData = ConstantArray::get(
255         ArrayType::get(getDeviceImageTy(), ImagesInits.size()), ImagesInits);
256 
257     auto *Images =
258         new GlobalVariable(M, ImagesData->getType(), /*isConstant*/ true,
259                            GlobalValue::InternalLinkage, ImagesData,
260                            ".omp_offloading.device_images");
261     Images->setUnnamedAddr(GlobalValue::UnnamedAddr::Global);
262 
263     auto *ImagesB = ConstantExpr::getGetElementPtr(Images->getValueType(),
264                                                    Images, ZeroZero);
265 
266     // And finally create the binary descriptor object.
267     auto *DescInit = ConstantStruct::get(
268         getBinDescTy(),
269         ConstantInt::get(Type::getInt32Ty(C), ImagesInits.size()), ImagesB,
270         EntriesB, EntriesE);
271 
272     return new GlobalVariable(M, DescInit->getType(), /*isConstant*/ true,
273                               GlobalValue::InternalLinkage, DescInit,
274                               ".omp_offloading.descriptor");
275   }
276 
createRegisterFunction(GlobalVariable * BinDesc)277   void createRegisterFunction(GlobalVariable *BinDesc) {
278     auto *FuncTy = FunctionType::get(Type::getVoidTy(C), /*isVarArg*/ false);
279     auto *Func = Function::Create(FuncTy, GlobalValue::InternalLinkage,
280                                   ".omp_offloading.descriptor_reg", &M);
281     Func->setSection(".text.startup");
282 
283     // Get __tgt_register_lib function declaration.
284     auto *RegFuncTy = FunctionType::get(Type::getVoidTy(C), getBinDescPtrTy(),
285                                         /*isVarArg*/ false);
286     FunctionCallee RegFuncC =
287         M.getOrInsertFunction("__tgt_register_lib", RegFuncTy);
288 
289     // Construct function body
290     IRBuilder<> Builder(BasicBlock::Create(C, "entry", Func));
291     Builder.CreateCall(RegFuncC, BinDesc);
292     Builder.CreateRetVoid();
293 
294     // Add this function to constructors.
295     // Set priority to 1 so that __tgt_register_lib is executed AFTER
296     // __tgt_register_requires (we want to know what requirements have been
297     // asked for before we load a libomptarget plugin so that by the time the
298     // plugin is loaded it can report how many devices there are which can
299     // satisfy these requirements).
300     appendToGlobalCtors(M, Func, /*Priority*/ 1);
301   }
302 
createUnregisterFunction(GlobalVariable * BinDesc)303   void createUnregisterFunction(GlobalVariable *BinDesc) {
304     auto *FuncTy = FunctionType::get(Type::getVoidTy(C), /*isVarArg*/ false);
305     auto *Func = Function::Create(FuncTy, GlobalValue::InternalLinkage,
306                                   ".omp_offloading.descriptor_unreg", &M);
307     Func->setSection(".text.startup");
308 
309     // Get __tgt_unregister_lib function declaration.
310     auto *UnRegFuncTy = FunctionType::get(Type::getVoidTy(C), getBinDescPtrTy(),
311                                           /*isVarArg*/ false);
312     FunctionCallee UnRegFuncC =
313         M.getOrInsertFunction("__tgt_unregister_lib", UnRegFuncTy);
314 
315     // Construct function body
316     IRBuilder<> Builder(BasicBlock::Create(C, "entry", Func));
317     Builder.CreateCall(UnRegFuncC, BinDesc);
318     Builder.CreateRetVoid();
319 
320     // Add this function to global destructors.
321     // Match priority of __tgt_register_lib
322     appendToGlobalDtors(M, Func, /*Priority*/ 1);
323   }
324 
325 public:
BinaryWrapper(StringRef Target,StringRef ToolName)326   BinaryWrapper(StringRef Target, StringRef ToolName)
327       : M("offload.wrapper.object", C), ToolName(ToolName) {
328     M.setTargetTriple(Target);
329     // Look for llvm-objcopy in the same directory, from which
330     // clang-offload-wrapper is invoked. This helps OpenMP offload
331     // LIT tests.
332 
333     // This just needs to be some symbol in the binary; C++ doesn't
334     // allow taking the address of ::main however.
335     void *P = (void *)(intptr_t)&Help;
336     std::string COWPath = sys::fs::getMainExecutable(ToolName.str().c_str(), P);
337     if (!COWPath.empty()) {
338       auto COWDir = sys::path::parent_path(COWPath);
339       ErrorOr<std::string> ObjcopyPathOrErr =
340           sys::findProgramByName("llvm-objcopy", {COWDir});
341       if (ObjcopyPathOrErr) {
342         ObjcopyPath = *ObjcopyPathOrErr;
343         return;
344       }
345 
346       // Otherwise, look through PATH environment.
347     }
348 
349     ErrorOr<std::string> ObjcopyPathOrErr =
350         sys::findProgramByName("llvm-objcopy");
351     if (!ObjcopyPathOrErr) {
352       WithColor::warning(errs(), ToolName)
353           << "cannot find llvm-objcopy[.exe] in PATH; ELF notes cannot be "
354              "added.\n";
355       return;
356     }
357 
358     ObjcopyPath = *ObjcopyPathOrErr;
359   }
360 
~BinaryWrapper()361   ~BinaryWrapper() {
362     if (TempFiles.empty())
363       return;
364 
365     StringRef ToolNameRef(ToolName);
366     auto warningOS = [ToolNameRef]() -> raw_ostream & {
367       return WithColor::warning(errs(), ToolNameRef);
368     };
369 
370     for (auto &F : TempFiles) {
371       if (SaveTemps) {
372         warningOS() << "keeping temporary file " << F << "\n";
373         continue;
374       }
375 
376       auto EC = sys::fs::remove(F, false);
377       if (EC)
378         warningOS() << "cannot remove temporary file " << F << ": "
379                     << EC.message().c_str() << "\n";
380     }
381   }
382 
wrapBinaries(ArrayRef<ArrayRef<char>> Binaries)383   const Module &wrapBinaries(ArrayRef<ArrayRef<char>> Binaries) {
384     GlobalVariable *Desc = createBinDesc(Binaries);
385     assert(Desc && "no binary descriptor");
386     createRegisterFunction(Desc);
387     createUnregisterFunction(Desc);
388     return M;
389   }
390 
addELFNotes(std::unique_ptr<MemoryBuffer> Buf,StringRef OriginalFileName)391   std::unique_ptr<MemoryBuffer> addELFNotes(std::unique_ptr<MemoryBuffer> Buf,
392                                             StringRef OriginalFileName) {
393     // Cannot add notes, if llvm-objcopy is not available.
394     //
395     // I did not find a clean way to add a new notes section into an existing
396     // ELF file. llvm-objcopy seems to recreate a new ELF from scratch,
397     // and we just try to use llvm-objcopy here.
398     if (ObjcopyPath.empty())
399       return Buf;
400 
401     StringRef ToolNameRef(ToolName);
402 
403     // Helpers to emit warnings.
404     auto warningOS = [ToolNameRef]() -> raw_ostream & {
405       return WithColor::warning(errs(), ToolNameRef);
406     };
407     auto handleErrorAsWarning = [&warningOS](Error E) {
408       logAllUnhandledErrors(std::move(E), warningOS());
409     };
410 
411     Expected<std::unique_ptr<ObjectFile>> BinOrErr =
412         ObjectFile::createELFObjectFile(Buf->getMemBufferRef(),
413                                         /*InitContent=*/false);
414     if (Error E = BinOrErr.takeError()) {
415       consumeError(std::move(E));
416       // This warning is questionable, but let it be here,
417       // assuming that most OpenMP offload models use ELF offload images.
418       warningOS() << OriginalFileName
419                   << " is not an ELF image, so notes cannot be added to it.\n";
420       return Buf;
421     }
422 
423     // If we fail to add the note section, we just pass through the original
424     // ELF image for wrapping. At some point we should enforce the note section
425     // and start emitting errors vs warnings.
426     support::endianness Endianness;
427     if (isa<ELF64LEObjectFile>(BinOrErr->get()) ||
428         isa<ELF32LEObjectFile>(BinOrErr->get())) {
429       Endianness = support::little;
430     } else if (isa<ELF64BEObjectFile>(BinOrErr->get()) ||
431                isa<ELF32BEObjectFile>(BinOrErr->get())) {
432       Endianness = support::big;
433     } else {
434       warningOS() << OriginalFileName
435                   << " is an ELF image of unrecognized format.\n";
436       return Buf;
437     }
438 
439     // Create temporary file for the data of a new SHT_NOTE section.
440     // We fill it in with data and then pass to llvm-objcopy invocation
441     // for reading.
442     Twine NotesFileModel = OriginalFileName + Twine(".elfnotes.%%%%%%%.tmp");
443     Expected<sys::fs::TempFile> NotesTemp =
444         sys::fs::TempFile::create(NotesFileModel);
445     if (Error E = NotesTemp.takeError()) {
446       handleErrorAsWarning(createFileError(NotesFileModel, std::move(E)));
447       return Buf;
448     }
449     TempFiles.push_back(NotesTemp->TmpName);
450 
451     // Create temporary file for the updated ELF image.
452     // This is an empty file that we pass to llvm-objcopy invocation
453     // for writing.
454     Twine ELFFileModel = OriginalFileName + Twine(".elfwithnotes.%%%%%%%.tmp");
455     Expected<sys::fs::TempFile> ELFTemp =
456         sys::fs::TempFile::create(ELFFileModel);
457     if (Error E = ELFTemp.takeError()) {
458       handleErrorAsWarning(createFileError(ELFFileModel, std::move(E)));
459       return Buf;
460     }
461     TempFiles.push_back(ELFTemp->TmpName);
462 
463     // Keep the new ELF image file to reserve the name for the future
464     // llvm-objcopy invocation.
465     std::string ELFTmpFileName = ELFTemp->TmpName;
466     if (Error E = ELFTemp->keep(ELFTmpFileName)) {
467       handleErrorAsWarning(createFileError(ELFTmpFileName, std::move(E)));
468       return Buf;
469     }
470 
471     // Write notes to the *elfnotes*.tmp file.
472     raw_fd_ostream NotesOS(NotesTemp->FD, false);
473 
474     struct NoteTy {
475       // Note name is a null-terminated "LLVMOMPOFFLOAD".
476       std::string Name;
477       // Note type defined in llvm/include/llvm/BinaryFormat/ELF.h.
478       uint32_t Type = 0;
479       // Each note has type-specific associated data.
480       std::string Desc;
481 
482       NoteTy(std::string &&Name, uint32_t Type, std::string &&Desc)
483           : Name(std::move(Name)), Type(Type), Desc(std::move(Desc)) {}
484     };
485 
486     // So far we emit just three notes.
487     SmallVector<NoteTy, 3> Notes;
488     // Version of the offload image identifying the structure of the ELF image.
489     // Version 1.0 does not have any specific requirements.
490     // We may come up with some structure that has to be honored by all
491     // offload implementations in future (e.g. to let libomptarget
492     // get some information from the offload image).
493     Notes.emplace_back("LLVMOMPOFFLOAD", ELF::NT_LLVM_OPENMP_OFFLOAD_VERSION,
494                        OPENMP_OFFLOAD_IMAGE_VERSION);
495     // This is a producer identification string. We are LLVM!
496     Notes.emplace_back("LLVMOMPOFFLOAD", ELF::NT_LLVM_OPENMP_OFFLOAD_PRODUCER,
497                        "LLVM");
498     // This is a producer version. Use the same format that is used
499     // by clang to report the LLVM version.
500     Notes.emplace_back("LLVMOMPOFFLOAD",
501                        ELF::NT_LLVM_OPENMP_OFFLOAD_PRODUCER_VERSION,
502                        LLVM_VERSION_STRING
503 #ifdef LLVM_REVISION
504                        " " LLVM_REVISION
505 #endif
506     );
507 
508     // Return the amount of padding required for a blob of N bytes
509     // to be aligned to Alignment bytes.
510     auto getPadAmount = [](uint32_t N, uint32_t Alignment) -> uint32_t {
511       uint32_t Mod = (N % Alignment);
512       if (Mod == 0)
513         return 0;
514       return Alignment - Mod;
515     };
516     auto emitPadding = [&getPadAmount](raw_ostream &OS, uint32_t Size) {
517       for (uint32_t I = 0; I < getPadAmount(Size, 4); ++I)
518         OS << '\0';
519     };
520 
521     // Put notes into the file.
522     for (auto &N : Notes) {
523       assert(!N.Name.empty() && "We should not create notes with empty names.");
524       // Name must be null-terminated.
525       if (N.Name.back() != '\0')
526         N.Name += '\0';
527       uint32_t NameSz = N.Name.size();
528       uint32_t DescSz = N.Desc.size();
529       // A note starts with three 4-byte values:
530       //   NameSz
531       //   DescSz
532       //   Type
533       // These three fields are endian-sensitive.
534       support::endian::write<uint32_t>(NotesOS, NameSz, Endianness);
535       support::endian::write<uint32_t>(NotesOS, DescSz, Endianness);
536       support::endian::write<uint32_t>(NotesOS, N.Type, Endianness);
537       // Next, we have a null-terminated Name padded to a 4-byte boundary.
538       NotesOS << N.Name;
539       emitPadding(NotesOS, NameSz);
540       if (DescSz == 0)
541         continue;
542       // Finally, we have a descriptor, which is an arbitrary flow of bytes.
543       NotesOS << N.Desc;
544       emitPadding(NotesOS, DescSz);
545     }
546     NotesOS.flush();
547 
548     // Keep the notes file.
549     std::string NotesTmpFileName = NotesTemp->TmpName;
550     if (Error E = NotesTemp->keep(NotesTmpFileName)) {
551       handleErrorAsWarning(createFileError(NotesTmpFileName, std::move(E)));
552       return Buf;
553     }
554 
555     // Run llvm-objcopy like this:
556     //   llvm-objcopy --add-section=.note.openmp=<notes-tmp-file-name> \
557     //       <orig-file-name> <elf-tmp-file-name>
558     //
559     // This will add a SHT_NOTE section on top of the original ELF.
560     std::vector<StringRef> Args;
561     Args.push_back(ObjcopyPath);
562     std::string Option("--add-section=.note.openmp=" + NotesTmpFileName);
563     Args.push_back(Option);
564     Args.push_back(OriginalFileName);
565     Args.push_back(ELFTmpFileName);
566     bool ExecutionFailed = false;
567     std::string ErrMsg;
568     (void)sys::ExecuteAndWait(ObjcopyPath, Args,
569                               /*Env=*/llvm::None, /*Redirects=*/{},
570                               /*SecondsToWait=*/0,
571                               /*MemoryLimit=*/0, &ErrMsg, &ExecutionFailed);
572 
573     if (ExecutionFailed) {
574       warningOS() << ErrMsg << "\n";
575       return Buf;
576     }
577 
578     // Substitute the original ELF with new one.
579     ErrorOr<std::unique_ptr<MemoryBuffer>> BufOrErr =
580         MemoryBuffer::getFile(ELFTmpFileName);
581     if (!BufOrErr) {
582       handleErrorAsWarning(
583           createFileError(ELFTmpFileName, BufOrErr.getError()));
584       return Buf;
585     }
586 
587     return std::move(*BufOrErr);
588   }
589 };
590 
591 } // anonymous namespace
592 
main(int argc,const char ** argv)593 int main(int argc, const char **argv) {
594   sys::PrintStackTraceOnErrorSignal(argv[0]);
595 
596   cl::HideUnrelatedOptions(ClangOffloadWrapperCategory);
597   cl::SetVersionPrinter([](raw_ostream &OS) {
598     OS << clang::getClangToolFullVersion("clang-offload-wrapper") << '\n';
599   });
600   cl::ParseCommandLineOptions(
601       argc, argv,
602       "A tool to create a wrapper bitcode for offload target binaries. Takes "
603       "offload\ntarget binaries as input and produces bitcode file containing "
604       "target binaries packaged\nas data and initialization code which "
605       "registers target binaries in offload runtime.\n");
606 
607   if (Help) {
608     cl::PrintHelpMessage();
609     return 0;
610   }
611 
612   auto reportError = [argv](Error E) {
613     logAllUnhandledErrors(std::move(E), WithColor::error(errs(), argv[0]));
614   };
615 
616   if (Triple(Target).getArch() == Triple::UnknownArch) {
617     reportError(createStringError(
618         errc::invalid_argument, "'" + Target + "': unsupported target triple"));
619     return 1;
620   }
621 
622   BinaryWrapper Wrapper(Target, argv[0]);
623 
624   // Read device binaries.
625   SmallVector<std::unique_ptr<MemoryBuffer>, 4u> Buffers;
626   SmallVector<ArrayRef<char>, 4u> Images;
627   Buffers.reserve(Inputs.size());
628   Images.reserve(Inputs.size());
629   for (const std::string &File : Inputs) {
630     ErrorOr<std::unique_ptr<MemoryBuffer>> BufOrErr =
631         MemoryBuffer::getFileOrSTDIN(File);
632     if (!BufOrErr) {
633       reportError(createFileError(File, BufOrErr.getError()));
634       return 1;
635     }
636     std::unique_ptr<MemoryBuffer> Buffer(std::move(*BufOrErr));
637     if (File != "-" && AddOpenMPOffloadNotes) {
638       // Adding ELF notes for STDIN is not supported yet.
639       Buffer = Wrapper.addELFNotes(std::move(Buffer), File);
640     }
641     const std::unique_ptr<MemoryBuffer> &Buf =
642         Buffers.emplace_back(std::move(Buffer));
643     Images.emplace_back(Buf->getBufferStart(), Buf->getBufferSize());
644   }
645 
646   // Create the output file to write the resulting bitcode to.
647   std::error_code EC;
648   ToolOutputFile Out(Output, EC, sys::fs::OF_None);
649   if (EC) {
650     reportError(createFileError(Output, EC));
651     return 1;
652   }
653 
654   // Create a wrapper for device binaries and write its bitcode to the file.
655   WriteBitcodeToFile(
656       Wrapper.wrapBinaries(makeArrayRef(Images.data(), Images.size())),
657       Out.os());
658   if (Out.os().has_error()) {
659     reportError(createFileError(Output, Out.os().error()));
660     return 1;
661   }
662 
663   // Success.
664   Out.keep();
665   return 0;
666 }
667