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