Coverage Report

Created: 2021-08-24 07:12

/Users/buildslave/jenkins/workspace/coverage/llvm-project/clang/tools/clang-offload-wrapper/ClangOffloadWrapper.cpp
Line
Count
Source (jump to first uncovered line)
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
4
#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:
103
15
  IntegerType *getSizeTTy() {
104
15
    switch (M.getDataLayout().getPointerTypeSize(Type::getInt8PtrTy(C))) {
105
0
    case 4u:
106
0
      return Type::getInt32Ty(C);
107
15
    case 8u:
108
15
      return Type::getInt64Ty(C);
109
15
    }
110
0
    llvm_unreachable("unsupported pointer type size");
111
0
  }
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
  // };
120
35
  StructType *getEntryTy() {
121
35
    if (!EntryTy)
122
5
      EntryTy = StructType::create("__tgt_offload_entry", Type::getInt8PtrTy(C),
123
5
                                   Type::getInt8PtrTy(C), getSizeTTy(),
124
5
                                   Type::getInt32Ty(C), Type::getInt32Ty(C));
125
35
    return EntryTy;
126
35
  }
127
128
20
  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
  // };
136
15
  StructType *getDeviceImageTy() {
137
15
    if (!ImageTy)
138
5
      ImageTy = StructType::create("__tgt_device_image", Type::getInt8PtrTy(C),
139
5
                                   Type::getInt8PtrTy(C), getEntryPtrTy(),
140
5
                                   getEntryPtrTy());
141
15
    return ImageTy;
142
15
  }
143
144
5
  PointerType *getDeviceImagePtrTy() {
145
5
    return PointerType::getUnqual(getDeviceImageTy());
146
5
  }
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
  // };
154
15
  StructType *getBinDescTy() {
155
15
    if (!DescTy)
156
5
      DescTy = StructType::create("__tgt_bin_desc", Type::getInt32Ty(C),
157
5
                                  getDeviceImagePtrTy(), getEntryPtrTy(),
158
5
                                  getEntryPtrTy());
159
15
    return DescTy;
160
15
  }
161
162
10
  PointerType *getBinDescPtrTy() {
163
10
    return PointerType::getUnqual(getBinDescTy());
164
10
  }
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.
204
5
  GlobalVariable *createBinDesc(ArrayRef<ArrayRef<char>> Bufs) {
205
    // Create external begin/end symbols for the offload entries table.
206
5
    auto *EntriesB = new GlobalVariable(
207
5
        M, getEntryTy(), /*isConstant*/ true, GlobalValue::ExternalLinkage,
208
5
        /*Initializer*/ nullptr, "__start_omp_offloading_entries");
209
5
    EntriesB->setVisibility(GlobalValue::HiddenVisibility);
210
5
    auto *EntriesE = new GlobalVariable(
211
5
        M, getEntryTy(), /*isConstant*/ true, GlobalValue::ExternalLinkage,
212
5
        /*Initializer*/ nullptr, "__stop_omp_offloading_entries");
213
5
    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
5
    auto *DummyInit =
221
5
        ConstantAggregateZero::get(ArrayType::get(getEntryTy(), 0u));
222
5
    auto *DummyEntry = new GlobalVariable(
223
5
        M, DummyInit->getType(), true, GlobalVariable::ExternalLinkage,
224
5
        DummyInit, "__dummy.omp_offloading.entry");
225
5
    DummyEntry->setSection("omp_offloading_entries");
226
5
    DummyEntry->setVisibility(GlobalValue::HiddenVisibility);
227
228
5
    auto *Zero = ConstantInt::get(getSizeTTy(), 0u);
229
5
    Constant *ZeroZero[] = {Zero, Zero};
230
231
    // Create initializer for the images array.
232
5
    SmallVector<Constant *, 4u> ImagesInits;
233
5
    ImagesInits.reserve(Bufs.size());
234
5
    for (ArrayRef<char> Buf : Bufs) {
235
5
      auto *Data = ConstantDataArray::get(C, Buf);
236
5
      auto *Image = new GlobalVariable(M, Data->getType(), /*isConstant*/ true,
237
5
                                       GlobalVariable::InternalLinkage, Data,
238
5
                                       ".omp_offloading.device_image");
239
5
      Image->setUnnamedAddr(GlobalValue::UnnamedAddr::Global);
240
241
5
      auto *Size = ConstantInt::get(getSizeTTy(), Buf.size());
242
5
      Constant *ZeroSize[] = {Zero, Size};
243
244
5
      auto *ImageB = ConstantExpr::getGetElementPtr(Image->getValueType(),
245
5
                                                    Image, ZeroZero);
246
5
      auto *ImageE = ConstantExpr::getGetElementPtr(Image->getValueType(),
247
5
                                                    Image, ZeroSize);
248
249
5
      ImagesInits.push_back(ConstantStruct::get(getDeviceImageTy(), ImageB,
250
5
                                                ImageE, EntriesB, EntriesE));
251
5
    }
252
253
    // Then create images array.
254
5
    auto *ImagesData = ConstantArray::get(
255
5
        ArrayType::get(getDeviceImageTy(), ImagesInits.size()), ImagesInits);
256
257
5
    auto *Images =
258
5
        new GlobalVariable(M, ImagesData->getType(), /*isConstant*/ true,
259
5
                           GlobalValue::InternalLinkage, ImagesData,
260
5
                           ".omp_offloading.device_images");
261
5
    Images->setUnnamedAddr(GlobalValue::UnnamedAddr::Global);
262
263
5
    auto *ImagesB = ConstantExpr::getGetElementPtr(Images->getValueType(),
264
5
                                                   Images, ZeroZero);
265
266
    // And finally create the binary descriptor object.
267
5
    auto *DescInit = ConstantStruct::get(
268
5
        getBinDescTy(),
269
5
        ConstantInt::get(Type::getInt32Ty(C), ImagesInits.size()), ImagesB,
270
5
        EntriesB, EntriesE);
271
272
5
    return new GlobalVariable(M, DescInit->getType(), /*isConstant*/ true,
273
5
                              GlobalValue::InternalLinkage, DescInit,
274
5
                              ".omp_offloading.descriptor");
275
5
  }
276
277
5
  void createRegisterFunction(GlobalVariable *BinDesc) {
278
5
    auto *FuncTy = FunctionType::get(Type::getVoidTy(C), /*isVarArg*/ false);
279
5
    auto *Func = Function::Create(FuncTy, GlobalValue::InternalLinkage,
280
5
                                  ".omp_offloading.descriptor_reg", &M);
281
5
    Func->setSection(".text.startup");
282
283
    // Get __tgt_register_lib function declaration.
284
5
    auto *RegFuncTy = FunctionType::get(Type::getVoidTy(C), getBinDescPtrTy(),
285
5
                                        /*isVarArg*/ false);
286
5
    FunctionCallee RegFuncC =
287
5
        M.getOrInsertFunction("__tgt_register_lib", RegFuncTy);
288
289
    // Construct function body
290
5
    IRBuilder<> Builder(BasicBlock::Create(C, "entry", Func));
291
5
    Builder.CreateCall(RegFuncC, BinDesc);
292
5
    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
5
    appendToGlobalCtors(M, Func, /*Priority*/ 1);
301
5
  }
302
303
5
  void createUnregisterFunction(GlobalVariable *BinDesc) {
304
5
    auto *FuncTy = FunctionType::get(Type::getVoidTy(C), /*isVarArg*/ false);
305
5
    auto *Func = Function::Create(FuncTy, GlobalValue::InternalLinkage,
306
5
                                  ".omp_offloading.descriptor_unreg", &M);
307
5
    Func->setSection(".text.startup");
308
309
    // Get __tgt_unregister_lib function declaration.
310
5
    auto *UnRegFuncTy = FunctionType::get(Type::getVoidTy(C), getBinDescPtrTy(),
311
5
                                          /*isVarArg*/ false);
312
5
    FunctionCallee UnRegFuncC =
313
5
        M.getOrInsertFunction("__tgt_unregister_lib", UnRegFuncTy);
314
315
    // Construct function body
316
5
    IRBuilder<> Builder(BasicBlock::Create(C, "entry", Func));
317
5
    Builder.CreateCall(UnRegFuncC, BinDesc);
318
5
    Builder.CreateRetVoid();
319
320
    // Add this function to global destructors.
321
    // Match priority of __tgt_register_lib
322
5
    appendToGlobalDtors(M, Func, /*Priority*/ 1);
323
5
  }
324
325
public:
326
  BinaryWrapper(StringRef Target, StringRef ToolName)
327
5
      : M("offload.wrapper.object", C), ToolName(ToolName) {
328
5
    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
5
    void *P = (void *)(intptr_t)&Help;
336
5
    std::string COWPath = sys::fs::getMainExecutable(ToolName.str().c_str(), P);
337
5
    if (!COWPath.empty()) {
338
5
      auto COWDir = sys::path::parent_path(COWPath);
339
5
      ErrorOr<std::string> ObjcopyPathOrErr =
340
5
          sys::findProgramByName("llvm-objcopy", {COWDir});
341
5
      if (ObjcopyPathOrErr) {
342
5
        ObjcopyPath = *ObjcopyPathOrErr;
343
5
        return;
344
5
      }
345
346
      // Otherwise, look through PATH environment.
347
5
    }
348
349
0
    ErrorOr<std::string> ObjcopyPathOrErr =
350
0
        sys::findProgramByName("llvm-objcopy");
351
0
    if (!ObjcopyPathOrErr) {
352
0
      WithColor::warning(errs(), ToolName)
353
0
          << "cannot find llvm-objcopy[.exe] in PATH; ELF notes cannot be "
354
0
             "added.\n";
355
0
      return;
356
0
    }
357
358
0
    ObjcopyPath = *ObjcopyPathOrErr;
359
0
  }
360
361
5
  ~BinaryWrapper() {
362
5
    if (TempFiles.empty())
363
1
      return;
364
365
4
    StringRef ToolNameRef(ToolName);
366
4
    auto warningOS = [ToolNameRef]() -> raw_ostream & {
367
0
      return WithColor::warning(errs(), ToolNameRef);
368
0
    };
369
370
8
    for (auto &F : TempFiles) {
371
8
      if (SaveTemps) {
372
0
        warningOS() << "keeping temporary file " << F << "\n";
373
0
        continue;
374
0
      }
375
376
8
      auto EC = sys::fs::remove(F, false);
377
8
      if (EC)
378
0
        warningOS() << "cannot remove temporary file " << F << ": "
379
0
                    << EC.message().c_str() << "\n";
380
8
    }
381
4
  }
382
383
5
  const Module &wrapBinaries(ArrayRef<ArrayRef<char>> Binaries) {
384
5
    GlobalVariable *Desc = createBinDesc(Binaries);
385
5
    assert(Desc && "no binary descriptor");
386
0
    createRegisterFunction(Desc);
387
5
    createUnregisterFunction(Desc);
388
5
    return M;
389
5
  }
390
391
  std::unique_ptr<MemoryBuffer> addELFNotes(std::unique_ptr<MemoryBuffer> Buf,
392
5
                                            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
5
    if (ObjcopyPath.empty())
399
0
      return Buf;
400
401
5
    StringRef ToolNameRef(ToolName);
402
403
    // Helpers to emit warnings.
404
5
    auto warningOS = [ToolNameRef]() -> raw_ostream & {
405
1
      return WithColor::warning(errs(), ToolNameRef);
406
1
    };
407
5
    auto handleErrorAsWarning = [&warningOS](Error E) {
408
0
      logAllUnhandledErrors(std::move(E), warningOS());
409
0
    };
410
411
5
    Expected<std::unique_ptr<ObjectFile>> BinOrErr =
412
5
        ObjectFile::createELFObjectFile(Buf->getMemBufferRef(),
413
5
                                        /*InitContent=*/false);
414
5
    if (Error E = BinOrErr.takeError()) {
415
1
      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
1
      warningOS() << OriginalFileName
419
1
                  << " is not an ELF image, so notes cannot be added to it.\n";
420
1
      return Buf;
421
1
    }
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
4
    support::endianness Endianness;
427
4
    if (isa<ELF64LEObjectFile>(BinOrErr->get()) ||
428
4
        
isa<ELF32LEObjectFile>(BinOrErr->get())3
) {
429
2
      Endianness = support::little;
430
2
    } else if (isa<ELF64BEObjectFile>(BinOrErr->get()) ||
431
2
               
isa<ELF32BEObjectFile>(BinOrErr->get())1
) {
432
2
      Endianness = support::big;
433
2
    } else {
434
0
      warningOS() << OriginalFileName
435
0
                  << " is an ELF image of unrecognized format.\n";
436
0
      return Buf;
437
0
    }
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
4
    Twine NotesFileModel = OriginalFileName + Twine(".elfnotes.%%%%%%%.tmp");
443
4
    Expected<sys::fs::TempFile> NotesTemp =
444
4
        sys::fs::TempFile::create(NotesFileModel);
445
4
    if (Error E = NotesTemp.takeError()) {
446
0
      handleErrorAsWarning(createFileError(NotesFileModel, std::move(E)));
447
0
      return Buf;
448
0
    }
449
4
    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
4
    Twine ELFFileModel = OriginalFileName + Twine(".elfwithnotes.%%%%%%%.tmp");
455
4
    Expected<sys::fs::TempFile> ELFTemp =
456
4
        sys::fs::TempFile::create(ELFFileModel);
457
4
    if (Error E = ELFTemp.takeError()) {
458
0
      handleErrorAsWarning(createFileError(ELFFileModel, std::move(E)));
459
0
      return Buf;
460
0
    }
461
4
    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
4
    std::string ELFTmpFileName = ELFTemp->TmpName;
466
4
    if (Error E = ELFTemp->keep(ELFTmpFileName)) {
467
0
      handleErrorAsWarning(createFileError(ELFTmpFileName, std::move(E)));
468
0
      return Buf;
469
0
    }
470
471
    // Write notes to the *elfnotes*.tmp file.
472
4
    raw_fd_ostream NotesOS(NotesTemp->FD, false);
473
474
4
    struct NoteTy {
475
      // Note name is a null-terminated "LLVMOMPOFFLOAD".
476
4
      std::string Name;
477
      // Note type defined in llvm/include/llvm/BinaryFormat/ELF.h.
478
4
      uint32_t Type = 0;
479
      // Each note has type-specific associated data.
480
4
      std::string Desc;
481
482
4
      NoteTy(std::string &&Name, uint32_t Type, std::string &&Desc)
483
12
          : Name(std::move(Name)), Type(Type), Desc(std::move(Desc)) {}
484
4
    };
485
486
    // So far we emit just three notes.
487
4
    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
4
    Notes.emplace_back("LLVMOMPOFFLOAD", ELF::NT_LLVM_OPENMP_OFFLOAD_VERSION,
494
4
                       OPENMP_OFFLOAD_IMAGE_VERSION);
495
    // This is a producer identification string. We are LLVM!
496
4
    Notes.emplace_back("LLVMOMPOFFLOAD", ELF::NT_LLVM_OPENMP_OFFLOAD_PRODUCER,
497
4
                       "LLVM");
498
    // This is a producer version. Use the same format that is used
499
    // by clang to report the LLVM version.
500
4
    Notes.emplace_back("LLVMOMPOFFLOAD",
501
4
                       ELF::NT_LLVM_OPENMP_OFFLOAD_PRODUCER_VERSION,
502
4
                       LLVM_VERSION_STRING
503
4
#ifdef LLVM_REVISION
504
4
                       " " LLVM_REVISION
505
4
#endif
506
4
    );
507
508
    // Return the amount of padding required for a blob of N bytes
509
    // to be aligned to Alignment bytes.
510
44
    auto getPadAmount = [](uint32_t N, uint32_t Alignment) -> uint32_t {
511
44
      uint32_t Mod = (N % Alignment);
512
44
      if (Mod == 0)
513
4
        return 0;
514
40
      return Alignment - Mod;
515
44
    };
516
24
    auto emitPadding = [&getPadAmount](raw_ostream &OS, uint32_t Size) {
517
44
      for (uint32_t I = 0; I < getPadAmount(Size, 4); 
++I20
)
518
20
        OS << '\0';
519
24
    };
520
521
    // Put notes into the file.
522
12
    for (auto &N : Notes) {
523
12
      assert(!N.Name.empty() && "We should not create notes with empty names.");
524
      // Name must be null-terminated.
525
12
      if (N.Name.back() != '\0')
526
12
        N.Name += '\0';
527
12
      uint32_t NameSz = N.Name.size();
528
12
      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
12
      support::endian::write<uint32_t>(NotesOS, NameSz, Endianness);
535
12
      support::endian::write<uint32_t>(NotesOS, DescSz, Endianness);
536
12
      support::endian::write<uint32_t>(NotesOS, N.Type, Endianness);
537
      // Next, we have a null-terminated Name padded to a 4-byte boundary.
538
12
      NotesOS << N.Name;
539
12
      emitPadding(NotesOS, NameSz);
540
12
      if (DescSz == 0)
541
0
        continue;
542
      // Finally, we have a descriptor, which is an arbitrary flow of bytes.
543
12
      NotesOS << N.Desc;
544
12
      emitPadding(NotesOS, DescSz);
545
12
    }
546
4
    NotesOS.flush();
547
548
    // Keep the notes file.
549
4
    std::string NotesTmpFileName = NotesTemp->TmpName;
550
4
    if (Error E = NotesTemp->keep(NotesTmpFileName)) {
551
0
      handleErrorAsWarning(createFileError(NotesTmpFileName, std::move(E)));
552
0
      return Buf;
553
0
    }
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
4
    std::vector<StringRef> Args;
561
4
    Args.push_back(ObjcopyPath);
562
4
    std::string Option("--add-section=.note.openmp=" + NotesTmpFileName);
563
4
    Args.push_back(Option);
564
4
    Args.push_back(OriginalFileName);
565
4
    Args.push_back(ELFTmpFileName);
566
4
    bool ExecutionFailed = false;
567
4
    std::string ErrMsg;
568
4
    (void)sys::ExecuteAndWait(ObjcopyPath, Args,
569
4
                              /*Env=*/llvm::None, /*Redirects=*/{},
570
4
                              /*SecondsToWait=*/0,
571
4
                              /*MemoryLimit=*/0, &ErrMsg, &ExecutionFailed);
572
573
4
    if (ExecutionFailed) {
574
0
      warningOS() << ErrMsg << "\n";
575
0
      return Buf;
576
0
    }
577
578
    // Substitute the original ELF with new one.
579
4
    ErrorOr<std::unique_ptr<MemoryBuffer>> BufOrErr =
580
4
        MemoryBuffer::getFile(ELFTmpFileName);
581
4
    if (!BufOrErr) {
582
0
      handleErrorAsWarning(
583
0
          createFileError(ELFTmpFileName, BufOrErr.getError()));
584
0
      return Buf;
585
0
    }
586
587
4
    return std::move(*BufOrErr);
588
4
  }
589
};
590
591
} // anonymous namespace
592
593
6
int main(int argc, const char **argv) {
594
6
  sys::PrintStackTraceOnErrorSignal(argv[0]);
595
596
6
  cl::HideUnrelatedOptions(ClangOffloadWrapperCategory);
597
6
  cl::SetVersionPrinter([](raw_ostream &OS) {
598
0
    OS << clang::getClangToolFullVersion("clang-offload-wrapper") << '\n';
599
0
  });
600
6
  cl::ParseCommandLineOptions(
601
6
      argc, argv,
602
6
      "A tool to create a wrapper bitcode for offload target binaries. Takes "
603
6
      "offload\ntarget binaries as input and produces bitcode file containing "
604
6
      "target binaries packaged\nas data and initialization code which "
605
6
      "registers target binaries in offload runtime.\n");
606
607
6
  if (Help) {
608
0
    cl::PrintHelpMessage();
609
0
    return 0;
610
0
  }
611
612
6
  auto reportError = [argv](Error E) {
613
0
    logAllUnhandledErrors(std::move(E), WithColor::error(errs(), argv[0]));
614
0
  };
615
616
6
  if (Triple(Target).getArch() == Triple::UnknownArch) {
617
0
    reportError(createStringError(
618
0
        errc::invalid_argument, "'" + Target + "': unsupported target triple"));
619
0
    return 1;
620
0
  }
621
622
6
  BinaryWrapper Wrapper(Target, argv[0]);
623
624
  // Read device binaries.
625
6
  SmallVector<std::unique_ptr<MemoryBuffer>, 4u> Buffers;
626
6
  SmallVector<ArrayRef<char>, 4u> Images;
627
6
  Buffers.reserve(Inputs.size());
628
6
  Images.reserve(Inputs.size());
629
6
  for (const std::string &File : Inputs) {
630
5
    ErrorOr<std::unique_ptr<MemoryBuffer>> BufOrErr =
631
5
        MemoryBuffer::getFileOrSTDIN(File);
632
5
    if (!BufOrErr) {
633
0
      reportError(createFileError(File, BufOrErr.getError()));
634
0
      return 1;
635
0
    }
636
5
    std::unique_ptr<MemoryBuffer> Buffer(std::move(*BufOrErr));
637
5
    if (File != "-" && AddOpenMPOffloadNotes) {
638
      // Adding ELF notes for STDIN is not supported yet.
639
5
      Buffer = Wrapper.addELFNotes(std::move(Buffer), File);
640
5
    }
641
5
    const std::unique_ptr<MemoryBuffer> &Buf =
642
5
        Buffers.emplace_back(std::move(Buffer));
643
5
    Images.emplace_back(Buf->getBufferStart(), Buf->getBufferSize());
644
5
  }
645
646
  // Create the output file to write the resulting bitcode to.
647
6
  std::error_code EC;
648
6
  ToolOutputFile Out(Output, EC, sys::fs::OF_None);
649
6
  if (EC) {
650
0
    reportError(createFileError(Output, EC));
651
0
    return 1;
652
0
  }
653
654
  // Create a wrapper for device binaries and write its bitcode to the file.
655
6
  WriteBitcodeToFile(
656
6
      Wrapper.wrapBinaries(makeArrayRef(Images.data(), Images.size())),
657
6
      Out.os());
658
6
  if (Out.os().has_error()) {
659
0
    reportError(createFileError(Output, Out.os().error()));
660
0
    return 1;
661
0
  }
662
663
  // Success.
664
6
  Out.keep();
665
6
  return 0;
666
6
}