MachODump.cpp revision fd7aa38e304a09fa0ef51b85b773b649b7e58c5e
1//===-- MachODump.cpp - Object file dumping utility for llvm --------------===// 2// 3// The LLVM Compiler Infrastructure 4// 5// This file is distributed under the University of Illinois Open Source 6// License. See LICENSE.TXT for details. 7// 8//===----------------------------------------------------------------------===// 9// 10// This file implements the MachO-specific dumper for llvm-objdump. 11// 12//===----------------------------------------------------------------------===// 13 14#include "llvm-objdump.h" 15#include "MCFunction.h" 16#include "llvm/ADT/OwningPtr.h" 17#include "llvm/ADT/STLExtras.h" 18#include "llvm/ADT/Triple.h" 19#include "llvm/DebugInfo/DIContext.h" 20#include "llvm/MC/MCAsmInfo.h" 21#include "llvm/MC/MCDisassembler.h" 22#include "llvm/MC/MCInst.h" 23#include "llvm/MC/MCInstPrinter.h" 24#include "llvm/MC/MCInstrAnalysis.h" 25#include "llvm/MC/MCInstrDesc.h" 26#include "llvm/MC/MCInstrInfo.h" 27#include "llvm/MC/MCRegisterInfo.h" 28#include "llvm/MC/MCSubtargetInfo.h" 29#include "llvm/Object/MachO.h" 30#include "llvm/Support/Casting.h" 31#include "llvm/Support/CommandLine.h" 32#include "llvm/Support/Debug.h" 33#include "llvm/Support/Format.h" 34#include "llvm/Support/GraphWriter.h" 35#include "llvm/Support/MachO.h" 36#include "llvm/Support/MemoryBuffer.h" 37#include "llvm/Support/TargetRegistry.h" 38#include "llvm/Support/TargetSelect.h" 39#include "llvm/Support/raw_ostream.h" 40#include "llvm/Support/system_error.h" 41#include <algorithm> 42#include <cstring> 43using namespace llvm; 44using namespace object; 45 46static cl::opt<bool> 47 CFG("cfg", cl::desc("Create a CFG for every symbol in the object file and" 48 " write it to a graphviz file (MachO-only)")); 49 50static cl::opt<bool> 51 UseDbg("g", cl::desc("Print line information from debug info if available")); 52 53static cl::opt<std::string> 54 DSYMFile("dsym", cl::desc("Use .dSYM file for debug info")); 55 56static const Target *GetTarget(const MachOObjectFile *MachOObj) { 57 // Figure out the target triple. 58 if (TripleName.empty()) { 59 llvm::Triple TT("unknown-unknown-unknown"); 60 TT.setArch(Triple::ArchType(MachOObj->getArch())); 61 TripleName = TT.str(); 62 } 63 64 // Get the target specific parser. 65 std::string Error; 66 const Target *TheTarget = TargetRegistry::lookupTarget(TripleName, Error); 67 if (TheTarget) 68 return TheTarget; 69 70 errs() << "llvm-objdump: error: unable to get target for '" << TripleName 71 << "', see --version and --triple.\n"; 72 return 0; 73} 74 75struct SymbolSorter { 76 bool operator()(const SymbolRef &A, const SymbolRef &B) { 77 SymbolRef::Type AType, BType; 78 A.getType(AType); 79 B.getType(BType); 80 81 uint64_t AAddr, BAddr; 82 if (AType != SymbolRef::ST_Function) 83 AAddr = 0; 84 else 85 A.getAddress(AAddr); 86 if (BType != SymbolRef::ST_Function) 87 BAddr = 0; 88 else 89 B.getAddress(BAddr); 90 return AAddr < BAddr; 91 } 92}; 93 94// Print additional information about an address, if available. 95static void DumpAddress(uint64_t Address, ArrayRef<SectionRef> Sections, 96 const MachOObjectFile *MachOObj, raw_ostream &OS) { 97 for (unsigned i = 0; i != Sections.size(); ++i) { 98 uint64_t SectAddr = 0, SectSize = 0; 99 Sections[i].getAddress(SectAddr); 100 Sections[i].getSize(SectSize); 101 uint64_t addr = SectAddr; 102 if (SectAddr <= Address && 103 SectAddr + SectSize > Address) { 104 StringRef bytes, name; 105 Sections[i].getContents(bytes); 106 Sections[i].getName(name); 107 // Print constant strings. 108 if (!name.compare("__cstring")) 109 OS << '"' << bytes.substr(addr, bytes.find('\0', addr)) << '"'; 110 // Print constant CFStrings. 111 if (!name.compare("__cfstring")) 112 OS << "@\"" << bytes.substr(addr, bytes.find('\0', addr)) << '"'; 113 } 114 } 115} 116 117typedef std::map<uint64_t, MCFunction*> FunctionMapTy; 118typedef SmallVector<MCFunction, 16> FunctionListTy; 119static void createMCFunctionAndSaveCalls(StringRef Name, 120 const MCDisassembler *DisAsm, 121 MemoryObject &Object, uint64_t Start, 122 uint64_t End, 123 MCInstrAnalysis *InstrAnalysis, 124 uint64_t Address, 125 raw_ostream &DebugOut, 126 FunctionMapTy &FunctionMap, 127 FunctionListTy &Functions) { 128 SmallVector<uint64_t, 16> Calls; 129 MCFunction f = 130 MCFunction::createFunctionFromMC(Name, DisAsm, Object, Start, End, 131 InstrAnalysis, DebugOut, Calls); 132 Functions.push_back(f); 133 FunctionMap[Address] = &Functions.back(); 134 135 // Add the gathered callees to the map. 136 for (unsigned i = 0, e = Calls.size(); i != e; ++i) 137 FunctionMap.insert(std::make_pair(Calls[i], (MCFunction*)0)); 138} 139 140// Write a graphviz file for the CFG inside an MCFunction. 141static void emitDOTFile(const char *FileName, const MCFunction &f, 142 MCInstPrinter *IP) { 143 // Start a new dot file. 144 std::string Error; 145 raw_fd_ostream Out(FileName, Error); 146 if (!Error.empty()) { 147 errs() << "llvm-objdump: warning: " << Error << '\n'; 148 return; 149 } 150 151 Out << "digraph " << f.getName() << " {\n"; 152 Out << "graph [ rankdir = \"LR\" ];\n"; 153 for (MCFunction::iterator i = f.begin(), e = f.end(); i != e; ++i) { 154 bool hasPreds = false; 155 // Only print blocks that have predecessors. 156 // FIXME: Slow. 157 for (MCFunction::iterator pi = f.begin(), pe = f.end(); pi != pe; 158 ++pi) 159 if (pi->second.contains(i->first)) { 160 hasPreds = true; 161 break; 162 } 163 164 if (!hasPreds && i != f.begin()) 165 continue; 166 167 Out << '"' << i->first << "\" [ label=\"<a>"; 168 // Print instructions. 169 for (unsigned ii = 0, ie = i->second.getInsts().size(); ii != ie; 170 ++ii) { 171 // Escape special chars and print the instruction in mnemonic form. 172 std::string Str; 173 raw_string_ostream OS(Str); 174 IP->printInst(&i->second.getInsts()[ii].Inst, OS, ""); 175 Out << DOT::EscapeString(OS.str()) << '|'; 176 } 177 Out << "<o>\" shape=\"record\" ];\n"; 178 179 // Add edges. 180 for (MCBasicBlock::succ_iterator si = i->second.succ_begin(), 181 se = i->second.succ_end(); si != se; ++si) 182 Out << i->first << ":o -> " << *si <<":a\n"; 183 } 184 Out << "}\n"; 185} 186 187static void 188getSectionsAndSymbols(const macho::Header Header, 189 MachOObjectFile *MachOObj, 190 std::vector<SectionRef> &Sections, 191 std::vector<SymbolRef> &Symbols, 192 SmallVectorImpl<uint64_t> &FoundFns) { 193 error_code ec; 194 for (symbol_iterator SI = MachOObj->begin_symbols(), 195 SE = MachOObj->end_symbols(); SI != SE; SI.increment(ec)) 196 Symbols.push_back(*SI); 197 198 for (section_iterator SI = MachOObj->begin_sections(), 199 SE = MachOObj->end_sections(); SI != SE; SI.increment(ec)) { 200 SectionRef SR = *SI; 201 StringRef SectName; 202 SR.getName(SectName); 203 Sections.push_back(*SI); 204 } 205 206 MachOObjectFile::LoadCommandInfo Command = 207 MachOObj->getFirstLoadCommandInfo(); 208 for (unsigned i = 0; i != Header.NumLoadCommands; ++i) { 209 if (Command.C.Type == macho::LCT_FunctionStarts) { 210 // We found a function starts segment, parse the addresses for later 211 // consumption. 212 macho::LinkeditDataLoadCommand LLC = 213 MachOObj->getLinkeditDataLoadCommand(Command); 214 215 MachOObj->ReadULEB128s(LLC.DataOffset, FoundFns); 216 } 217 Command = MachOObj->getNextLoadCommandInfo(Command); 218 } 219} 220 221static void DisassembleInputMachO2(StringRef Filename, 222 MachOObjectFile *MachOOF); 223 224void llvm::DisassembleInputMachO(StringRef Filename) { 225 OwningPtr<MemoryBuffer> Buff; 226 227 if (error_code ec = MemoryBuffer::getFileOrSTDIN(Filename, Buff)) { 228 errs() << "llvm-objdump: " << Filename << ": " << ec.message() << "\n"; 229 return; 230 } 231 232 OwningPtr<MachOObjectFile> MachOOF(static_cast<MachOObjectFile*>( 233 ObjectFile::createMachOObjectFile(Buff.take()))); 234 235 DisassembleInputMachO2(Filename, MachOOF.get()); 236} 237 238static void DisassembleInputMachO2(StringRef Filename, 239 MachOObjectFile *MachOOF) { 240 const Target *TheTarget = GetTarget(MachOOF); 241 if (!TheTarget) { 242 // GetTarget prints out stuff. 243 return; 244 } 245 OwningPtr<const MCInstrInfo> InstrInfo(TheTarget->createMCInstrInfo()); 246 OwningPtr<MCInstrAnalysis> 247 InstrAnalysis(TheTarget->createMCInstrAnalysis(InstrInfo.get())); 248 249 // Set up disassembler. 250 OwningPtr<const MCAsmInfo> AsmInfo(TheTarget->createMCAsmInfo(TripleName)); 251 OwningPtr<const MCSubtargetInfo> 252 STI(TheTarget->createMCSubtargetInfo(TripleName, "", "")); 253 OwningPtr<const MCDisassembler> DisAsm(TheTarget->createMCDisassembler(*STI)); 254 OwningPtr<const MCRegisterInfo> MRI(TheTarget->createMCRegInfo(TripleName)); 255 int AsmPrinterVariant = AsmInfo->getAssemblerDialect(); 256 OwningPtr<MCInstPrinter> 257 IP(TheTarget->createMCInstPrinter(AsmPrinterVariant, *AsmInfo, *InstrInfo, 258 *MRI, *STI)); 259 260 if (!InstrAnalysis || !AsmInfo || !STI || !DisAsm || !IP) { 261 errs() << "error: couldn't initialize disassembler for target " 262 << TripleName << '\n'; 263 return; 264 } 265 266 outs() << '\n' << Filename << ":\n\n"; 267 268 macho::Header Header = MachOOF->getHeader(); 269 270 std::vector<SectionRef> Sections; 271 std::vector<SymbolRef> Symbols; 272 SmallVector<uint64_t, 8> FoundFns; 273 274 getSectionsAndSymbols(Header, MachOOF, Sections, Symbols, FoundFns); 275 276 // Make a copy of the unsorted symbol list. FIXME: duplication 277 std::vector<SymbolRef> UnsortedSymbols(Symbols); 278 // Sort the symbols by address, just in case they didn't come in that way. 279 std::sort(Symbols.begin(), Symbols.end(), SymbolSorter()); 280 281#ifndef NDEBUG 282 raw_ostream &DebugOut = DebugFlag ? dbgs() : nulls(); 283#else 284 raw_ostream &DebugOut = nulls(); 285#endif 286 287 OwningPtr<DIContext> diContext; 288 ObjectFile *DbgObj = MachOOF; 289 // Try to find debug info and set up the DIContext for it. 290 if (UseDbg) { 291 // A separate DSym file path was specified, parse it as a macho file, 292 // get the sections and supply it to the section name parsing machinery. 293 if (!DSYMFile.empty()) { 294 OwningPtr<MemoryBuffer> Buf; 295 if (error_code ec = MemoryBuffer::getFileOrSTDIN(DSYMFile.c_str(), Buf)) { 296 errs() << "llvm-objdump: " << Filename << ": " << ec.message() << '\n'; 297 return; 298 } 299 DbgObj = ObjectFile::createMachOObjectFile(Buf.take()); 300 } 301 302 // Setup the DIContext 303 diContext.reset(DIContext::getDWARFContext(DbgObj)); 304 } 305 306 FunctionMapTy FunctionMap; 307 FunctionListTy Functions; 308 309 for (unsigned SectIdx = 0; SectIdx != Sections.size(); SectIdx++) { 310 StringRef SectName; 311 if (Sections[SectIdx].getName(SectName) || 312 SectName != "__text") 313 continue; // Skip non-text sections 314 315 DataRefImpl DR = Sections[SectIdx].getRawDataRefImpl(); 316 StringRef SegmentName = MachOOF->getSectionFinalSegmentName(DR); 317 if (SegmentName != "__TEXT") 318 continue; 319 320 // Insert the functions from the function starts segment into our map. 321 uint64_t VMAddr; 322 Sections[SectIdx].getAddress(VMAddr); 323 for (unsigned i = 0, e = FoundFns.size(); i != e; ++i) { 324 StringRef SectBegin; 325 Sections[SectIdx].getContents(SectBegin); 326 uint64_t Offset = (uint64_t)SectBegin.data(); 327 FunctionMap.insert(std::make_pair(VMAddr + FoundFns[i]-Offset, 328 (MCFunction*)0)); 329 } 330 331 StringRef Bytes; 332 Sections[SectIdx].getContents(Bytes); 333 StringRefMemoryObject memoryObject(Bytes); 334 bool symbolTableWorked = false; 335 336 // Parse relocations. 337 std::vector<std::pair<uint64_t, SymbolRef> > Relocs; 338 error_code ec; 339 for (relocation_iterator RI = Sections[SectIdx].begin_relocations(), 340 RE = Sections[SectIdx].end_relocations(); RI != RE; RI.increment(ec)) { 341 uint64_t RelocOffset, SectionAddress; 342 RI->getAddress(RelocOffset); 343 Sections[SectIdx].getAddress(SectionAddress); 344 RelocOffset -= SectionAddress; 345 346 SymbolRef RelocSym; 347 RI->getSymbol(RelocSym); 348 349 Relocs.push_back(std::make_pair(RelocOffset, RelocSym)); 350 } 351 array_pod_sort(Relocs.begin(), Relocs.end()); 352 353 // Disassemble symbol by symbol. 354 for (unsigned SymIdx = 0; SymIdx != Symbols.size(); SymIdx++) { 355 StringRef SymName; 356 Symbols[SymIdx].getName(SymName); 357 358 SymbolRef::Type ST; 359 Symbols[SymIdx].getType(ST); 360 if (ST != SymbolRef::ST_Function) 361 continue; 362 363 // Make sure the symbol is defined in this section. 364 bool containsSym = false; 365 Sections[SectIdx].containsSymbol(Symbols[SymIdx], containsSym); 366 if (!containsSym) 367 continue; 368 369 // Start at the address of the symbol relative to the section's address. 370 uint64_t SectionAddress = 0; 371 uint64_t Start = 0; 372 Sections[SectIdx].getAddress(SectionAddress); 373 Symbols[SymIdx].getAddress(Start); 374 Start -= SectionAddress; 375 376 // Stop disassembling either at the beginning of the next symbol or at 377 // the end of the section. 378 bool containsNextSym = false; 379 uint64_t NextSym = 0; 380 uint64_t NextSymIdx = SymIdx+1; 381 while (Symbols.size() > NextSymIdx) { 382 SymbolRef::Type NextSymType; 383 Symbols[NextSymIdx].getType(NextSymType); 384 if (NextSymType == SymbolRef::ST_Function) { 385 Sections[SectIdx].containsSymbol(Symbols[NextSymIdx], 386 containsNextSym); 387 Symbols[NextSymIdx].getAddress(NextSym); 388 NextSym -= SectionAddress; 389 break; 390 } 391 ++NextSymIdx; 392 } 393 394 uint64_t SectSize; 395 Sections[SectIdx].getSize(SectSize); 396 uint64_t End = containsNextSym ? NextSym : SectSize; 397 uint64_t Size; 398 399 symbolTableWorked = true; 400 401 if (!CFG) { 402 // Normal disassembly, print addresses, bytes and mnemonic form. 403 StringRef SymName; 404 Symbols[SymIdx].getName(SymName); 405 406 outs() << SymName << ":\n"; 407 DILineInfo lastLine; 408 for (uint64_t Index = Start; Index < End; Index += Size) { 409 MCInst Inst; 410 411 if (DisAsm->getInstruction(Inst, Size, memoryObject, Index, 412 DebugOut, nulls())) { 413 uint64_t SectAddress = 0; 414 Sections[SectIdx].getAddress(SectAddress); 415 outs() << format("%8" PRIx64 ":\t", SectAddress + Index); 416 417 DumpBytes(StringRef(Bytes.data() + Index, Size)); 418 IP->printInst(&Inst, outs(), ""); 419 420 // Print debug info. 421 if (diContext) { 422 DILineInfo dli = 423 diContext->getLineInfoForAddress(SectAddress + Index); 424 // Print valid line info if it changed. 425 if (dli != lastLine && dli.getLine() != 0) 426 outs() << "\t## " << dli.getFileName() << ':' 427 << dli.getLine() << ':' << dli.getColumn(); 428 lastLine = dli; 429 } 430 outs() << "\n"; 431 } else { 432 errs() << "llvm-objdump: warning: invalid instruction encoding\n"; 433 if (Size == 0) 434 Size = 1; // skip illegible bytes 435 } 436 } 437 } else { 438 // Create CFG and use it for disassembly. 439 StringRef SymName; 440 Symbols[SymIdx].getName(SymName); 441 createMCFunctionAndSaveCalls( 442 SymName, DisAsm.get(), memoryObject, Start, End, 443 InstrAnalysis.get(), Start, DebugOut, FunctionMap, Functions); 444 } 445 } 446 if (!CFG && !symbolTableWorked) { 447 // Reading the symbol table didn't work, disassemble the whole section. 448 uint64_t SectAddress; 449 Sections[SectIdx].getAddress(SectAddress); 450 uint64_t SectSize; 451 Sections[SectIdx].getSize(SectSize); 452 uint64_t InstSize; 453 for (uint64_t Index = 0; Index < SectSize; Index += InstSize) { 454 MCInst Inst; 455 456 if (DisAsm->getInstruction(Inst, InstSize, memoryObject, Index, 457 DebugOut, nulls())) { 458 outs() << format("%8" PRIx64 ":\t", SectAddress + Index); 459 DumpBytes(StringRef(Bytes.data() + Index, InstSize)); 460 IP->printInst(&Inst, outs(), ""); 461 outs() << "\n"; 462 } else { 463 errs() << "llvm-objdump: warning: invalid instruction encoding\n"; 464 if (InstSize == 0) 465 InstSize = 1; // skip illegible bytes 466 } 467 } 468 } 469 470 if (CFG) { 471 if (!symbolTableWorked) { 472 // Reading the symbol table didn't work, create a big __TEXT symbol. 473 uint64_t SectSize = 0, SectAddress = 0; 474 Sections[SectIdx].getSize(SectSize); 475 Sections[SectIdx].getAddress(SectAddress); 476 createMCFunctionAndSaveCalls("__TEXT", DisAsm.get(), memoryObject, 477 0, SectSize, 478 InstrAnalysis.get(), 479 SectAddress, DebugOut, 480 FunctionMap, Functions); 481 } 482 for (std::map<uint64_t, MCFunction*>::iterator mi = FunctionMap.begin(), 483 me = FunctionMap.end(); mi != me; ++mi) 484 if (mi->second == 0) { 485 // Create functions for the remaining callees we have gathered, 486 // but we didn't find a name for them. 487 uint64_t SectSize = 0; 488 Sections[SectIdx].getSize(SectSize); 489 490 SmallVector<uint64_t, 16> Calls; 491 MCFunction f = 492 MCFunction::createFunctionFromMC("unknown", DisAsm.get(), 493 memoryObject, mi->first, 494 SectSize, 495 InstrAnalysis.get(), DebugOut, 496 Calls); 497 Functions.push_back(f); 498 mi->second = &Functions.back(); 499 for (unsigned i = 0, e = Calls.size(); i != e; ++i) { 500 std::pair<uint64_t, MCFunction*> p(Calls[i], (MCFunction*)0); 501 if (FunctionMap.insert(p).second) 502 mi = FunctionMap.begin(); 503 } 504 } 505 506 DenseSet<uint64_t> PrintedBlocks; 507 for (unsigned ffi = 0, ffe = Functions.size(); ffi != ffe; ++ffi) { 508 MCFunction &f = Functions[ffi]; 509 for (MCFunction::iterator fi = f.begin(), fe = f.end(); fi != fe; ++fi){ 510 if (!PrintedBlocks.insert(fi->first).second) 511 continue; // We already printed this block. 512 513 // We assume a block has predecessors when it's the first block after 514 // a symbol. 515 bool hasPreds = FunctionMap.find(fi->first) != FunctionMap.end(); 516 517 // See if this block has predecessors. 518 // FIXME: Slow. 519 for (MCFunction::iterator pi = f.begin(), pe = f.end(); pi != pe; 520 ++pi) 521 if (pi->second.contains(fi->first)) { 522 hasPreds = true; 523 break; 524 } 525 526 uint64_t SectSize = 0, SectAddress; 527 Sections[SectIdx].getSize(SectSize); 528 Sections[SectIdx].getAddress(SectAddress); 529 530 // No predecessors, this is a data block. Print as .byte directives. 531 if (!hasPreds) { 532 uint64_t End = llvm::next(fi) == fe ? SectSize : 533 llvm::next(fi)->first; 534 outs() << "# " << End-fi->first << " bytes of data:\n"; 535 for (unsigned pos = fi->first; pos != End; ++pos) { 536 outs() << format("%8x:\t", SectAddress + pos); 537 DumpBytes(StringRef(Bytes.data() + pos, 1)); 538 outs() << format("\t.byte 0x%02x\n", (uint8_t)Bytes[pos]); 539 } 540 continue; 541 } 542 543 if (fi->second.contains(fi->first)) // Print a header for simple loops 544 outs() << "# Loop begin:\n"; 545 546 DILineInfo lastLine; 547 // Walk over the instructions and print them. 548 for (unsigned ii = 0, ie = fi->second.getInsts().size(); ii != ie; 549 ++ii) { 550 const MCDecodedInst &Inst = fi->second.getInsts()[ii]; 551 552 // If there's a symbol at this address, print its name. 553 if (FunctionMap.find(SectAddress + Inst.Address) != 554 FunctionMap.end()) 555 outs() << FunctionMap[SectAddress + Inst.Address]-> getName() 556 << ":\n"; 557 558 outs() << format("%8" PRIx64 ":\t", SectAddress + Inst.Address); 559 DumpBytes(StringRef(Bytes.data() + Inst.Address, Inst.Size)); 560 561 if (fi->second.contains(fi->first)) // Indent simple loops. 562 outs() << '\t'; 563 564 IP->printInst(&Inst.Inst, outs(), ""); 565 566 // Look for relocations inside this instructions, if there is one 567 // print its target and additional information if available. 568 for (unsigned j = 0; j != Relocs.size(); ++j) 569 if (Relocs[j].first >= SectAddress + Inst.Address && 570 Relocs[j].first < SectAddress + Inst.Address + Inst.Size) { 571 StringRef SymName; 572 uint64_t Addr; 573 Relocs[j].second.getAddress(Addr); 574 Relocs[j].second.getName(SymName); 575 576 outs() << "\t# " << SymName << ' '; 577 DumpAddress(Addr, Sections, MachOOF, outs()); 578 } 579 580 // If this instructions contains an address, see if we can evaluate 581 // it and print additional information. 582 uint64_t targ = InstrAnalysis->evaluateBranch(Inst.Inst, 583 Inst.Address, 584 Inst.Size); 585 if (targ != -1ULL) 586 DumpAddress(targ, Sections, MachOOF, outs()); 587 588 // Print debug info. 589 if (diContext) { 590 DILineInfo dli = 591 diContext->getLineInfoForAddress(SectAddress + Inst.Address); 592 // Print valid line info if it changed. 593 if (dli != lastLine && dli.getLine() != 0) 594 outs() << "\t## " << dli.getFileName() << ':' 595 << dli.getLine() << ':' << dli.getColumn(); 596 lastLine = dli; 597 } 598 599 outs() << '\n'; 600 } 601 } 602 603 emitDOTFile((f.getName().str() + ".dot").c_str(), f, IP.get()); 604 } 605 } 606 } 607} 608