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