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: 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 325 public: 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 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