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>(args&: InputFile, args&: ToolPath, args&: 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 | TheTriple: InputFile->makeTriple(), InputFileName: InputFile->getFileName(), Features: nullptr, |
77 | /* IsPIC */ true, DwCtx: DWARFContext::create(Obj: *InputFile), |
78 | Logger: {.Out: llvm::outs(), .Err: 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(TargetBuilder: std::unique_ptr<MCPlusBuilder>( |
85 | createMCPlusBuilder(Arch: BC->TheTriple->getArch(), Analysis: BC->MIA.get(), |
86 | Info: BC->MII.get(), RegInfo: BC->MRI.get(), STI: 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(Path: Filename)) |
93 | return errorCodeToError(EC: make_error_code(E: errc::no_such_file_or_directory)); |
94 | |
95 | if (ProfileReader) { |
96 | // Already exists |
97 | return make_error<StringError>( |
98 | Args: Twine("multiple profiles specified: " ) + ProfileReader->getFilename() + |
99 | " and " + Filename, Args: inconvertibleErrorCode()); |
100 | } |
101 | |
102 | ProfileReader = std::make_unique<DataReader>(args&: Filename); |
103 | return Error::success(); |
104 | } |
105 | |
106 | void MachORewriteInstance::preprocessProfileData() { |
107 | if (!ProfileReader) |
108 | return; |
109 | if (Error E = ProfileReader->preprocessProfile(BC&: *BC.get())) |
110 | report_error(Message: "cannot pre-process profile" , E: std::move(E)); |
111 | } |
112 | |
113 | void MachORewriteInstance::processProfileDataPreCFG() { |
114 | if (!ProfileReader) |
115 | return; |
116 | if (Error E = ProfileReader->readProfilePreCFG(BC&: *BC.get())) |
117 | report_error(Message: "cannot read profile pre-CFG" , E: std::move(E)); |
118 | } |
119 | |
120 | void MachORewriteInstance::processProfileData() { |
121 | if (!ProfileReader) |
122 | return; |
123 | if (Error E = ProfileReader->readProfile(BC&: *BC.get())) |
124 | report_error(Message: "cannot read profile" , E: 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(E: SectionName.takeError(), Message: "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(OS&: outs()); |
145 | } |
146 | } |
147 | |
148 | namespace { |
149 | |
150 | struct DataInCodeRegion { |
151 | explicit DataInCodeRegion(DiceRef D) { |
152 | D.getOffset(Result&: Offset); |
153 | D.getLength(Result&: Length); |
154 | D.getKind(Result&: 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(n: NumberOfEntries); |
169 | for (auto I = O.begin_dices(), E = O.end_dices(); I != E; ++I) |
170 | DataInCode.emplace_back(args: *I); |
171 | llvm::stable_sort(Range&: DataInCode, C: [](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(L: LC); |
184 | StartOffset = LCMain.entryoff; |
185 | break; |
186 | } |
187 | case MachO::LC_SEGMENT: { |
188 | MachO::segment_command LCSeg = O.getSegmentLoadCommand(L: LC); |
189 | StringRef SegmentName(LCSeg.segname, |
190 | strnlen(string: LCSeg.segname, maxlen: 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(L: LC); |
197 | StringRef SegmentName(LCSeg.segname, |
198 | strnlen(string: LCSeg.segname, maxlen: 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(ValOrErr: S.getType(), Msg: "cannot get symbol type" ); |
218 | if (Type == SymbolRef::ST_Function) |
219 | FunctionSymbols.push_back(x: S); |
220 | } |
221 | if (FunctionSymbols.empty()) |
222 | return; |
223 | llvm::stable_sort( |
224 | Range&: FunctionSymbols, C: [](const SymbolRef &LHS, const SymbolRef &RHS) { |
225 | return cantFail(ValOrErr: LHS.getValue()) < cantFail(ValOrErr: RHS.getValue()); |
226 | }); |
227 | for (size_t Index = 0; Index < FunctionSymbols.size(); ++Index) { |
228 | const uint64_t Address = cantFail(ValOrErr: 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(ValOrErr: FunctionSymbols[Index].getName(), Msg: "cannot get symbol name" ) |
240 | .str(); |
241 | // Uniquify names of local symbols. |
242 | if (!(cantFail(ValOrErr: FunctionSymbols[Index].getFlags()) & SymbolRef::SF_Global)) |
243 | SymbolName = NR.uniquify(Name: SymbolName); |
244 | |
245 | section_iterator S = cantFail(ValOrErr: 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(ValOrErr: FunctionSymbols[NFIndex].getValue()) == Address) |
252 | ++NFIndex; |
253 | if (NFIndex < FunctionSymbols.size() && |
254 | S == cantFail(ValOrErr: FunctionSymbols[NFIndex].getSection())) |
255 | EndAddress = cantFail(ValOrErr: FunctionSymbols[NFIndex].getValue()); |
256 | |
257 | const uint64_t SymbolSize = EndAddress - Address; |
258 | const auto It = BC->getBinaryFunctions().find(x: Address); |
259 | if (It == BC->getBinaryFunctions().end()) { |
260 | BinaryFunction *Function = BC->createBinaryFunction( |
261 | Name: std::move(SymbolName), Section&: *Section, Address, Size: SymbolSize); |
262 | if (!opts::Instrument) |
263 | Function->setOutputAddress(Function->getAddress()); |
264 | |
265 | } else { |
266 | It->second.addAlternativeName(NewName: std::move(SymbolName)); |
267 | } |
268 | } |
269 | |
270 | const std::vector<DataInCodeRegion> DataInCode = readDataInCode(O: *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 | first: DataInCode.cbegin(), last: DataInCode.cend(), val: Function.getFileOffset(), |
297 | comp: [](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(O: *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(E: Function.disassemble()); |
313 | if (opts::PrintDisasm) |
314 | Function.print(OS&: outs(), Annotation: "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(E: 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(OS&: outs(), Annotation: "after building cfg" ); |
335 | } |
336 | } |
337 | |
338 | void MachORewriteInstance::runOptimizationPasses() { |
339 | BinaryFunctionPassManager Manager(*BC); |
340 | if (opts::Instrument) { |
341 | Manager.registerPass(Pass: std::make_unique<PatchEntries>()); |
342 | Manager.registerPass(Pass: std::make_unique<Instrumentation>(args&: opts::NeverPrint)); |
343 | } |
344 | |
345 | Manager.registerPass(Pass: std::make_unique<ShortenInstructions>(args&: opts::NeverPrint)); |
346 | |
347 | Manager.registerPass(Pass: std::make_unique<RemoveNops>(args&: opts::NeverPrint)); |
348 | |
349 | Manager.registerPass(Pass: std::make_unique<NormalizeCFG>(args&: opts::PrintNormalized)); |
350 | |
351 | Manager.registerPass( |
352 | Pass: std::make_unique<ReorderBasicBlocks>(args&: opts::PrintReordered)); |
353 | Manager.registerPass( |
354 | Pass: std::make_unique<FixupBranches>(args&: opts::PrintAfterBranchFixup)); |
355 | // This pass should always run last.* |
356 | Manager.registerPass( |
357 | Pass: std::make_unique<FinalizeFunctions>(args&: opts::PrintFinalized)); |
358 | |
359 | BC->logBOLTErrorsAndQuitOnFatal(E: 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(status: 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 | Message: (Twine("Cannot find section for function " ) + Function->getOneName()) |
387 | .str(), |
388 | EC: 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(SectionName: "__bolt" ); |
401 | if (!BOLT) { |
402 | llvm::errs() << "Cannot find __bolt section\n" ; |
403 | exit(status: 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(Value: Addr, Align: 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(Name: Function->getOneName(), Address: Addr, Size: 0, Alignment: 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 | args: opts::OutputFilename + ".bolt.o" , args&: EC, args: sys::fs::OF_None); |
431 | check_error(EC, Message: "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>(args&: TempOut->os()); |
438 | raw_pwrite_stream *OS = BOS.get(); |
439 | auto Streamer = BC->createStreamer(OS&: *OS); |
440 | |
441 | emitBinaryContext(Streamer&: *Streamer, BC&: *BC, OrgSecPrefix: getOrgSecPrefix()); |
442 | Streamer->finish(); |
443 | |
444 | std::unique_ptr<MemoryBuffer> ObjectMemBuffer = |
445 | MemoryBuffer::getMemBuffer(InputData: BOS->str(), BufferName: "in-memory object file" , RequiresNullTerminator: false); |
446 | std::unique_ptr<object::ObjectFile> Obj = cantFail( |
447 | ValOrErr: object::ObjectFile::createObjectFile(Object: ObjectMemBuffer->getMemBufferRef()), |
448 | Msg: "error creating in-memory object" ); |
449 | assert(Obj && "createObjectFile cannot return nullptr" ); |
450 | |
451 | auto EFMM = std::make_unique<ExecutableFileMemoryManager>(args&: *BC); |
452 | EFMM->setNewSecPrefix(getNewSecPrefix()); |
453 | EFMM->setOrgSecPrefix(getOrgSecPrefix()); |
454 | |
455 | Linker = std::make_unique<JITLinkLinker>(args&: *BC, args: std::move(EFMM)); |
456 | Linker->loadObject(Obj: ObjectMemBuffer->getMemBufferRef(), |
457 | MapSections: [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(SectionName: "__counters" , MapSection); |
465 | mapInstrumentationSection(SectionName: "__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(status: 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(Ptr: reinterpret_cast<char *>(Section->getAllocAddress()), |
498 | Size: Section->getOutputSize(), Offset: Section->getInputFileOffset()); |
499 | } |
500 | |
501 | void MachORewriteInstance::rewriteFile() { |
502 | std::error_code EC; |
503 | Out = std::make_unique<ToolOutputFile>(args&: opts::OutputFilename, args&: EC, |
504 | args: sys::fs::OF_None); |
505 | check_error(EC, Message: "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(Ptr: reinterpret_cast<char *>(Function.getImageAddress()), |
519 | Size: Function.getImageSize(), Offset: Function.getFileOffset()); |
520 | } |
521 | |
522 | for (const BinaryFunction *Function : BC->getInjectedBinaryFunctions()) { |
523 | OS.pwrite(Ptr: reinterpret_cast<char *>(Function->getImageAddress()), |
524 | Size: Function->getImageSize(), Offset: Function->getFileOffset()); |
525 | } |
526 | |
527 | writeInstrumentationSection(SectionName: "__counters" , OS); |
528 | writeInstrumentationSection(SectionName: "__tables" , OS); |
529 | |
530 | // TODO: Refactor addRuntimeLibSections to work properly on Mach-O and |
531 | // use it here. |
532 | writeInstrumentationSection(SectionName: "I__setup" , OS); |
533 | writeInstrumentationSection(SectionName: "I__fini" , OS); |
534 | writeInstrumentationSection(SectionName: "I__data" , OS); |
535 | writeInstrumentationSection(SectionName: "I__text" , OS); |
536 | writeInstrumentationSection(SectionName: "I__cstring" , OS); |
537 | writeInstrumentationSection(SectionName: "I__literal16" , OS); |
538 | |
539 | Out->keep(); |
540 | EC = sys::fs::setPermissions( |
541 | Path: opts::OutputFilename, |
542 | Permissions: static_cast<sys::fs::perms>(sys::fs::perms::all_all & |
543 | ~sys::fs::getUmask())); |
544 | check_error(EC, Message: "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 | |