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
49using namespace llvm;
50using namespace llvm::object;
51
52static 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.
56static cl::OptionCategory
57 ClangOffloadWrapperCategory("clang-offload-wrapper options");
58
59static cl::opt<std::string> Output("o", cl::Required,
60 cl::desc("Output filename"),
61 cl::value_desc("filename"),
62 cl::cat(ClangOffloadWrapperCategory));
63
64static cl::list<std::string> Inputs(cl::Positional, cl::OneOrMore,
65 cl::desc("<input files>"),
66 cl::cat(ClangOffloadWrapperCategory));
67
68static 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
73static 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
79static cl::opt<bool> AddOpenMPOffloadNotes(
80 "add-omp-offload-notes",
81 cl::desc("Add LLVMOMPOFFLOAD ELF notes to ELF device images."), cl::Hidden);
82
83namespace {
84
85class 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
102private:
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 // };
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
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 // };
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
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 // };
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
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.
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
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
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
325public:
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
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
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
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
593int 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

source code of clang/tools/clang-offload-wrapper/ClangOffloadWrapper.cpp