1 //===- bolt/Rewrite/MachORewriteInstance.cpp - MachO rewriter -------------===// 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 #include "bolt/Rewrite/MachORewriteInstance.h" 10 #include "bolt/Core/BinaryContext.h" 11 #include "bolt/Core/BinaryEmitter.h" 12 #include "bolt/Core/BinaryFunction.h" 13 #include "bolt/Core/JumpTable.h" 14 #include "bolt/Core/MCPlusBuilder.h" 15 #include "bolt/Passes/Instrumentation.h" 16 #include "bolt/Passes/PatchEntries.h" 17 #include "bolt/Profile/DataReader.h" 18 #include "bolt/Rewrite/BinaryPassManager.h" 19 #include "bolt/Rewrite/ExecutableFileMemoryManager.h" 20 #include "bolt/Rewrite/JITLinkLinker.h" 21 #include "bolt/Rewrite/RewriteInstance.h" 22 #include "bolt/RuntimeLibs/InstrumentationRuntimeLibrary.h" 23 #include "bolt/Utils/Utils.h" 24 #include "llvm/MC/MCObjectStreamer.h" 25 #include "llvm/Support/Errc.h" 26 #include "llvm/Support/FileSystem.h" 27 #include "llvm/Support/ToolOutputFile.h" 28 #include <memory> 29 #include <optional> 30 31 namespace opts { 32 33 using namespace llvm; 34 extern cl::opt<unsigned> AlignText; 35 //FIXME! Upstream change 36 //extern cl::opt<bool> CheckOverlappingElements; 37 extern cl::opt<bool> ForcePatch; 38 extern cl::opt<bool> Instrument; 39 extern cl::opt<bool> InstrumentCalls; 40 extern cl::opt<bolt::JumpTableSupportLevel> JumpTables; 41 extern cl::opt<bool> KeepTmp; 42 extern cl::opt<bool> NeverPrint; 43 extern cl::opt<std::string> OutputFilename; 44 extern cl::opt<bool> PrintAfterBranchFixup; 45 extern cl::opt<bool> PrintFinalized; 46 extern cl::opt<bool> PrintNormalized; 47 extern cl::opt<bool> PrintReordered; 48 extern cl::opt<bool> PrintSections; 49 extern cl::opt<bool> PrintDisasm; 50 extern cl::opt<bool> PrintCFG; 51 extern cl::opt<std::string> RuntimeInstrumentationLib; 52 extern cl::opt<unsigned> Verbosity; 53 } // namespace opts 54 55 namespace llvm { 56 namespace bolt { 57 58 #define DEBUG_TYPE "bolt" 59 60 Expected<std::unique_ptr<MachORewriteInstance>> 61 MachORewriteInstance::create(object::MachOObjectFile *InputFile, 62 StringRef ToolPath) { 63 Error Err = Error::success(); 64 auto MachORI = 65 std::make_unique<MachORewriteInstance>(InputFile, ToolPath, Err); 66 if (Err) 67 return std::move(Err); 68 return std::move(MachORI); 69 } 70 71 MachORewriteInstance::MachORewriteInstance(object::MachOObjectFile *InputFile, 72 StringRef ToolPath, Error &Err) 73 : InputFile(InputFile), ToolPath(ToolPath) { 74 ErrorAsOutParameter EAO(&Err); 75 Relocation::Arch = InputFile->makeTriple().getArch(); 76 auto BCOrErr = BinaryContext::createBinaryContext( 77 InputFile->makeTriple(), InputFile->getFileName(), nullptr, 78 /* IsPIC */ true, DWARFContext::create(*InputFile), 79 {llvm::outs(), llvm::errs()}); 80 if (Error E = BCOrErr.takeError()) { 81 Err = std::move(E); 82 return; 83 } 84 BC = std::move(BCOrErr.get()); 85 BC->initializeTarget(std::unique_ptr<MCPlusBuilder>( 86 createMCPlusBuilder(BC->TheTriple->getArch(), BC->MIA.get(), 87 BC->MII.get(), BC->MRI.get(), BC->STI.get()))); 88 if (opts::Instrument) 89 BC->setRuntimeLibrary(std::make_unique<InstrumentationRuntimeLibrary>()); 90 } 91 92 Error MachORewriteInstance::setProfile(StringRef Filename) { 93 if (!sys::fs::exists(Filename)) 94 return errorCodeToError(make_error_code(errc::no_such_file_or_directory)); 95 96 if (ProfileReader) { 97 // Already exists 98 return make_error<StringError>( 99 Twine("multiple profiles specified: ") + ProfileReader->getFilename() + 100 " and " + Filename, inconvertibleErrorCode()); 101 } 102 103 ProfileReader = std::make_unique<DataReader>(Filename); 104 return Error::success(); 105 } 106 107 void MachORewriteInstance::preprocessProfileData() { 108 if (!ProfileReader) 109 return; 110 if (Error E = ProfileReader->preprocessProfile(*BC.get())) 111 report_error("cannot pre-process profile", std::move(E)); 112 } 113 114 void MachORewriteInstance::processProfileDataPreCFG() { 115 if (!ProfileReader) 116 return; 117 if (Error E = ProfileReader->readProfilePreCFG(*BC.get())) 118 report_error("cannot read profile pre-CFG", std::move(E)); 119 } 120 121 void MachORewriteInstance::processProfileData() { 122 if (!ProfileReader) 123 return; 124 if (Error E = ProfileReader->readProfile(*BC.get())) 125 report_error("cannot read profile", std::move(E)); 126 } 127 128 void MachORewriteInstance::readSpecialSections() { 129 for (const object::SectionRef &Section : InputFile->sections()) { 130 Expected<StringRef> SectionName = Section.getName();; 131 check_error(SectionName.takeError(), "cannot get section name"); 132 // Only register sections with names. 133 if (!SectionName->empty()) { 134 BC->registerSection(Section); 135 LLVM_DEBUG( 136 dbgs() << "BOLT-DEBUG: registering section " << *SectionName 137 << " @ 0x" << Twine::utohexstr(Section.getAddress()) << ":0x" 138 << Twine::utohexstr(Section.getAddress() + Section.getSize()) 139 << "\n"); 140 } 141 } 142 143 if (opts::PrintSections) { 144 outs() << "BOLT-INFO: Sections from original binary:\n"; 145 BC->printSections(outs()); 146 } 147 } 148 149 namespace { 150 151 struct DataInCodeRegion { 152 explicit DataInCodeRegion(DiceRef D) { 153 D.getOffset(Offset); 154 D.getLength(Length); 155 D.getKind(Kind); 156 } 157 158 uint32_t Offset; 159 uint16_t Length; 160 uint16_t Kind; 161 }; 162 163 std::vector<DataInCodeRegion> readDataInCode(const MachOObjectFile &O) { 164 const MachO::linkedit_data_command DataInCodeLC = 165 O.getDataInCodeLoadCommand(); 166 const uint32_t NumberOfEntries = 167 DataInCodeLC.datasize / sizeof(MachO::data_in_code_entry); 168 std::vector<DataInCodeRegion> DataInCode; 169 DataInCode.reserve(NumberOfEntries); 170 for (auto I = O.begin_dices(), E = O.end_dices(); I != E; ++I) 171 DataInCode.emplace_back(*I); 172 llvm::stable_sort(DataInCode, [](DataInCodeRegion LHS, DataInCodeRegion RHS) { 173 return LHS.Offset < RHS.Offset; 174 }); 175 return DataInCode; 176 } 177 178 std::optional<uint64_t> readStartAddress(const MachOObjectFile &O) { 179 std::optional<uint64_t> StartOffset; 180 std::optional<uint64_t> TextVMAddr; 181 for (const object::MachOObjectFile::LoadCommandInfo &LC : O.load_commands()) { 182 switch (LC.C.cmd) { 183 case MachO::LC_MAIN: { 184 MachO::entry_point_command LCMain = O.getEntryPointCommand(LC); 185 StartOffset = LCMain.entryoff; 186 break; 187 } 188 case MachO::LC_SEGMENT: { 189 MachO::segment_command LCSeg = O.getSegmentLoadCommand(LC); 190 StringRef SegmentName(LCSeg.segname, 191 strnlen(LCSeg.segname, sizeof(LCSeg.segname))); 192 if (SegmentName == "__TEXT") 193 TextVMAddr = LCSeg.vmaddr; 194 break; 195 } 196 case MachO::LC_SEGMENT_64: { 197 MachO::segment_command_64 LCSeg = O.getSegment64LoadCommand(LC); 198 StringRef SegmentName(LCSeg.segname, 199 strnlen(LCSeg.segname, sizeof(LCSeg.segname))); 200 if (SegmentName == "__TEXT") 201 TextVMAddr = LCSeg.vmaddr; 202 break; 203 } 204 default: 205 continue; 206 } 207 } 208 return (TextVMAddr && StartOffset) 209 ? std::optional<uint64_t>(*TextVMAddr + *StartOffset) 210 : std::nullopt; 211 } 212 213 } // anonymous namespace 214 215 void MachORewriteInstance::discoverFileObjects() { 216 std::vector<SymbolRef> FunctionSymbols; 217 for (const SymbolRef &S : InputFile->symbols()) { 218 SymbolRef::Type Type = cantFail(S.getType(), "cannot get symbol type"); 219 if (Type == SymbolRef::ST_Function) 220 FunctionSymbols.push_back(S); 221 } 222 if (FunctionSymbols.empty()) 223 return; 224 llvm::stable_sort( 225 FunctionSymbols, [](const SymbolRef &LHS, const SymbolRef &RHS) { 226 return cantFail(LHS.getValue()) < cantFail(RHS.getValue()); 227 }); 228 for (size_t Index = 0; Index < FunctionSymbols.size(); ++Index) { 229 const uint64_t Address = cantFail(FunctionSymbols[Index].getValue()); 230 ErrorOr<BinarySection &> Section = BC->getSectionForAddress(Address); 231 // TODO: It happens for some symbols (e.g. __mh_execute_header). 232 // Add proper logic to handle them correctly. 233 if (!Section) { 234 errs() << "BOLT-WARNING: no section found for address " << Address 235 << "\n"; 236 continue; 237 } 238 239 std::string SymbolName = 240 cantFail(FunctionSymbols[Index].getName(), "cannot get symbol name") 241 .str(); 242 // Uniquify names of local symbols. 243 if (!(cantFail(FunctionSymbols[Index].getFlags()) & SymbolRef::SF_Global)) 244 SymbolName = NR.uniquify(SymbolName); 245 246 section_iterator S = cantFail(FunctionSymbols[Index].getSection()); 247 uint64_t EndAddress = S->getAddress() + S->getSize(); 248 249 size_t NFIndex = Index + 1; 250 // Skip aliases. 251 while (NFIndex < FunctionSymbols.size() && 252 cantFail(FunctionSymbols[NFIndex].getValue()) == Address) 253 ++NFIndex; 254 if (NFIndex < FunctionSymbols.size() && 255 S == cantFail(FunctionSymbols[NFIndex].getSection())) 256 EndAddress = cantFail(FunctionSymbols[NFIndex].getValue()); 257 258 const uint64_t SymbolSize = EndAddress - Address; 259 const auto It = BC->getBinaryFunctions().find(Address); 260 if (It == BC->getBinaryFunctions().end()) { 261 BinaryFunction *Function = BC->createBinaryFunction( 262 std::move(SymbolName), *Section, Address, SymbolSize); 263 if (!opts::Instrument) 264 Function->setOutputAddress(Function->getAddress()); 265 266 } else { 267 It->second.addAlternativeName(std::move(SymbolName)); 268 } 269 } 270 271 const std::vector<DataInCodeRegion> DataInCode = readDataInCode(*InputFile); 272 273 for (auto &BFI : BC->getBinaryFunctions()) { 274 BinaryFunction &Function = BFI.second; 275 Function.setMaxSize(Function.getSize()); 276 277 ErrorOr<ArrayRef<uint8_t>> FunctionData = Function.getData(); 278 if (!FunctionData) { 279 errs() << "BOLT-ERROR: corresponding section is non-executable or " 280 << "empty for function " << Function << '\n'; 281 continue; 282 } 283 284 // Treat zero-sized functions as non-simple ones. 285 if (Function.getSize() == 0) { 286 Function.setSimple(false); 287 continue; 288 } 289 290 // Offset of the function in the file. 291 const auto *FileBegin = 292 reinterpret_cast<const uint8_t *>(InputFile->getData().data()); 293 Function.setFileOffset(FunctionData->begin() - FileBegin); 294 295 // Treat functions which contain data in code as non-simple ones. 296 const auto It = std::lower_bound( 297 DataInCode.cbegin(), DataInCode.cend(), Function.getFileOffset(), 298 [](DataInCodeRegion D, uint64_t Offset) { return D.Offset < Offset; }); 299 if (It != DataInCode.cend() && 300 It->Offset + It->Length <= 301 Function.getFileOffset() + Function.getMaxSize()) 302 Function.setSimple(false); 303 } 304 305 BC->StartFunctionAddress = readStartAddress(*InputFile); 306 } 307 308 void MachORewriteInstance::disassembleFunctions() { 309 for (auto &BFI : BC->getBinaryFunctions()) { 310 BinaryFunction &Function = BFI.second; 311 if (!Function.isSimple()) 312 continue; 313 BC->logBOLTErrorsAndQuitOnFatal(Function.disassemble()); 314 if (opts::PrintDisasm) 315 Function.print(outs(), "after disassembly"); 316 } 317 } 318 319 void MachORewriteInstance::buildFunctionsCFG() { 320 for (auto &BFI : BC->getBinaryFunctions()) { 321 BinaryFunction &Function = BFI.second; 322 if (!Function.isSimple()) 323 continue; 324 BC->logBOLTErrorsAndQuitOnFatal(Function.buildCFG(/*AllocId*/ 0)); 325 } 326 } 327 328 void MachORewriteInstance::postProcessFunctions() { 329 for (auto &BFI : BC->getBinaryFunctions()) { 330 BinaryFunction &Function = BFI.second; 331 if (Function.empty()) 332 continue; 333 Function.postProcessCFG(); 334 if (opts::PrintCFG) 335 Function.print(outs(), "after building cfg"); 336 } 337 } 338 339 void MachORewriteInstance::runOptimizationPasses() { 340 BinaryFunctionPassManager Manager(*BC); 341 if (opts::Instrument) { 342 Manager.registerPass(std::make_unique<PatchEntries>()); 343 Manager.registerPass(std::make_unique<Instrumentation>(opts::NeverPrint)); 344 } 345 346 Manager.registerPass(std::make_unique<ShortenInstructions>(opts::NeverPrint)); 347 348 Manager.registerPass(std::make_unique<RemoveNops>(opts::NeverPrint)); 349 350 Manager.registerPass(std::make_unique<NormalizeCFG>(opts::PrintNormalized)); 351 352 Manager.registerPass( 353 std::make_unique<ReorderBasicBlocks>(opts::PrintReordered)); 354 Manager.registerPass( 355 std::make_unique<FixupBranches>(opts::PrintAfterBranchFixup)); 356 // This pass should always run last.* 357 Manager.registerPass( 358 std::make_unique<FinalizeFunctions>(opts::PrintFinalized)); 359 360 BC->logBOLTErrorsAndQuitOnFatal(Manager.runPasses()); 361 } 362 363 void MachORewriteInstance::mapInstrumentationSection( 364 StringRef SectionName, BOLTLinker::SectionMapper MapSection) { 365 if (!opts::Instrument) 366 return; 367 ErrorOr<BinarySection &> Section = BC->getUniqueSectionByName(SectionName); 368 if (!Section) { 369 llvm::errs() << "Cannot find " + SectionName + " section\n"; 370 exit(1); 371 } 372 if (!Section->hasValidSectionID()) 373 return; 374 MapSection(*Section, Section->getAddress()); 375 } 376 377 void MachORewriteInstance::mapCodeSections( 378 BOLTLinker::SectionMapper MapSection) { 379 for (BinaryFunction *Function : BC->getAllBinaryFunctions()) { 380 if (!Function->isEmitted()) 381 continue; 382 if (Function->getOutputAddress() == 0) 383 continue; 384 ErrorOr<BinarySection &> FuncSection = Function->getCodeSection(); 385 if (!FuncSection) 386 report_error( 387 (Twine("Cannot find section for function ") + Function->getOneName()) 388 .str(), 389 FuncSection.getError()); 390 391 FuncSection->setOutputAddress(Function->getOutputAddress()); 392 LLVM_DEBUG(dbgs() << "BOLT: mapping 0x" 393 << Twine::utohexstr(FuncSection->getAllocAddress()) << " to 0x" 394 << Twine::utohexstr(Function->getOutputAddress()) << '\n'); 395 MapSection(*FuncSection, Function->getOutputAddress()); 396 Function->setImageAddress(FuncSection->getAllocAddress()); 397 Function->setImageSize(FuncSection->getOutputSize()); 398 } 399 400 if (opts::Instrument) { 401 ErrorOr<BinarySection &> BOLT = BC->getUniqueSectionByName("__bolt"); 402 if (!BOLT) { 403 llvm::errs() << "Cannot find __bolt section\n"; 404 exit(1); 405 } 406 uint64_t Addr = BOLT->getAddress(); 407 for (BinaryFunction *Function : BC->getAllBinaryFunctions()) { 408 if (!Function->isEmitted()) 409 continue; 410 if (Function->getOutputAddress() != 0) 411 continue; 412 ErrorOr<BinarySection &> FuncSection = Function->getCodeSection(); 413 assert(FuncSection && "cannot find section for function"); 414 Addr = llvm::alignTo(Addr, 4); 415 FuncSection->setOutputAddress(Addr); 416 MapSection(*FuncSection, Addr); 417 Function->setFileOffset(Addr - BOLT->getAddress() + 418 BOLT->getInputFileOffset()); 419 Function->setImageAddress(FuncSection->getAllocAddress()); 420 Function->setImageSize(FuncSection->getOutputSize()); 421 BC->registerNameAtAddress(Function->getOneName(), Addr, 0, 0); 422 Addr += FuncSection->getOutputSize(); 423 } 424 } 425 } 426 427 void MachORewriteInstance::emitAndLink() { 428 std::error_code EC; 429 std::unique_ptr<::llvm::ToolOutputFile> TempOut = 430 std::make_unique<::llvm::ToolOutputFile>( 431 opts::OutputFilename + ".bolt.o", EC, sys::fs::OF_None); 432 check_error(EC, "cannot create output object file"); 433 434 if (opts::KeepTmp) 435 TempOut->keep(); 436 437 std::unique_ptr<buffer_ostream> BOS = 438 std::make_unique<buffer_ostream>(TempOut->os()); 439 raw_pwrite_stream *OS = BOS.get(); 440 auto Streamer = BC->createStreamer(*OS); 441 442 emitBinaryContext(*Streamer, *BC, getOrgSecPrefix()); 443 Streamer->finish(); 444 445 std::unique_ptr<MemoryBuffer> ObjectMemBuffer = 446 MemoryBuffer::getMemBuffer(BOS->str(), "in-memory object file", false); 447 std::unique_ptr<object::ObjectFile> Obj = cantFail( 448 object::ObjectFile::createObjectFile(ObjectMemBuffer->getMemBufferRef()), 449 "error creating in-memory object"); 450 assert(Obj && "createObjectFile cannot return nullptr"); 451 452 auto EFMM = std::make_unique<ExecutableFileMemoryManager>(*BC); 453 EFMM->setNewSecPrefix(getNewSecPrefix()); 454 EFMM->setOrgSecPrefix(getOrgSecPrefix()); 455 456 Linker = std::make_unique<JITLinkLinker>(*BC, std::move(EFMM)); 457 Linker->loadObject(ObjectMemBuffer->getMemBufferRef(), 458 [this](auto MapSection) { 459 // Assign addresses to all sections. If key corresponds 460 // to the object created by ourselves, call our regular 461 // mapping function. If we are loading additional objects 462 // as part of runtime libraries for instrumentation, 463 // treat them as extra sections. 464 mapCodeSections(MapSection); 465 mapInstrumentationSection("__counters", MapSection); 466 mapInstrumentationSection("__tables", MapSection); 467 }); 468 469 // TODO: Refactor addRuntimeLibSections to work properly on Mach-O 470 // and use it here. 471 // if (auto *RtLibrary = BC->getRuntimeLibrary()) { 472 // RtLibrary->link(*BC, ToolPath, *Linker, [this](auto MapSection) { 473 // mapInstrumentationSection("I__setup", MapSection); 474 // mapInstrumentationSection("I__fini", MapSection); 475 // mapInstrumentationSection("I__data", MapSection); 476 // mapInstrumentationSection("I__text", MapSection); 477 // mapInstrumentationSection("I__cstring", MapSection); 478 // mapInstrumentationSection("I__literal16", MapSection); 479 // }); 480 // } 481 } 482 483 void MachORewriteInstance::writeInstrumentationSection(StringRef SectionName, 484 raw_pwrite_stream &OS) { 485 if (!opts::Instrument) 486 return; 487 ErrorOr<BinarySection &> Section = BC->getUniqueSectionByName(SectionName); 488 if (!Section) { 489 llvm::errs() << "Cannot find " + SectionName + " section\n"; 490 exit(1); 491 } 492 if (!Section->hasValidSectionID()) 493 return; 494 assert(Section->getInputFileOffset() && 495 "Section input offset cannot be zero"); 496 assert(Section->getAllocAddress() && "Section alloc address cannot be zero"); 497 assert(Section->getOutputSize() && "Section output size cannot be zero"); 498 OS.pwrite(reinterpret_cast<char *>(Section->getAllocAddress()), 499 Section->getOutputSize(), Section->getInputFileOffset()); 500 } 501 502 void MachORewriteInstance::rewriteFile() { 503 std::error_code EC; 504 Out = std::make_unique<ToolOutputFile>(opts::OutputFilename, EC, 505 sys::fs::OF_None); 506 check_error(EC, "cannot create output executable file"); 507 raw_fd_ostream &OS = Out->os(); 508 OS << InputFile->getData(); 509 510 for (auto &BFI : BC->getBinaryFunctions()) { 511 BinaryFunction &Function = BFI.second; 512 if (!Function.isSimple()) 513 continue; 514 assert(Function.isEmitted() && "Simple function has not been emitted"); 515 if (!opts::Instrument && (Function.getImageSize() > Function.getMaxSize())) 516 continue; 517 if (opts::Verbosity >= 2) 518 outs() << "BOLT: rewriting function \"" << Function << "\"\n"; 519 OS.pwrite(reinterpret_cast<char *>(Function.getImageAddress()), 520 Function.getImageSize(), Function.getFileOffset()); 521 } 522 523 for (const BinaryFunction *Function : BC->getInjectedBinaryFunctions()) { 524 OS.pwrite(reinterpret_cast<char *>(Function->getImageAddress()), 525 Function->getImageSize(), Function->getFileOffset()); 526 } 527 528 writeInstrumentationSection("__counters", OS); 529 writeInstrumentationSection("__tables", OS); 530 531 // TODO: Refactor addRuntimeLibSections to work properly on Mach-O and 532 // use it here. 533 writeInstrumentationSection("I__setup", OS); 534 writeInstrumentationSection("I__fini", OS); 535 writeInstrumentationSection("I__data", OS); 536 writeInstrumentationSection("I__text", OS); 537 writeInstrumentationSection("I__cstring", OS); 538 writeInstrumentationSection("I__literal16", OS); 539 540 Out->keep(); 541 EC = sys::fs::setPermissions( 542 opts::OutputFilename, 543 static_cast<sys::fs::perms>(sys::fs::perms::all_all & 544 ~sys::fs::getUmask())); 545 check_error(EC, "cannot set permissions of output file"); 546 } 547 548 void MachORewriteInstance::adjustCommandLineOptions() { 549 //FIXME! Upstream change 550 // opts::CheckOverlappingElements = false; 551 if (!opts::AlignText.getNumOccurrences()) 552 opts::AlignText = BC->PageAlign; 553 if (opts::Instrument.getNumOccurrences()) 554 opts::ForcePatch = true; 555 opts::JumpTables = JTS_MOVE; 556 opts::InstrumentCalls = false; 557 opts::RuntimeInstrumentationLib = "libbolt_rt_instr_osx.a"; 558 } 559 560 void MachORewriteInstance::run() { 561 adjustCommandLineOptions(); 562 563 readSpecialSections(); 564 565 discoverFileObjects(); 566 567 preprocessProfileData(); 568 569 disassembleFunctions(); 570 571 processProfileDataPreCFG(); 572 573 buildFunctionsCFG(); 574 575 processProfileData(); 576 577 postProcessFunctions(); 578 579 runOptimizationPasses(); 580 581 emitAndLink(); 582 583 rewriteFile(); 584 } 585 586 MachORewriteInstance::~MachORewriteInstance() {} 587 588 } // namespace bolt 589 } // namespace llvm 590