// LoadObject - Read in and parse the bitcode file named by FN and return the // module it contains (wrapped in an auto_ptr), or auto_ptr<Module>() and set // Error if an error occurs. std::auto_ptr<Module> Linker::LoadObject(const sys::Path &FN) { std::string ParseErrorMessage; Module *Result = 0; OwningPtr<MemoryBuffer> Buffer; if (error_code ec = MemoryBuffer::getFileOrSTDIN(FN.c_str(), Buffer)) ParseErrorMessage = "Error reading file '" + FN.str() + "'" + ": " + ec.message(); else Result = ParseBitcodeFile(Buffer.get(), Context, &ParseErrorMessage); if (Result) return std::auto_ptr<Module>(Result); Error = "Bitcode file '" + FN.str() + "' could not be loaded"; if (ParseErrorMessage.size()) Error += ": " + ParseErrorMessage; return std::auto_ptr<Module>(); }
// Insert a file into the archive before some other member. This also takes care // of extracting the necessary flags and information from the file. bool Archive::addFileBefore(const sys::Path& filePath, iterator where, std::string* ErrMsg) { bool Exists; if (sys::fs::exists(filePath.str(), Exists) || !Exists) { if (ErrMsg) *ErrMsg = "Can not add a non-existent file to archive"; return true; } ArchiveMember* mbr = new ArchiveMember(this); mbr->data = 0; mbr->path = filePath; const sys::FileStatus *FSInfo = mbr->path.getFileStatus(false, ErrMsg); if (!FSInfo) { delete mbr; return true; } mbr->info = *FSInfo; unsigned flags = 0; bool hasSlash = filePath.str().find('/') != std::string::npos; if (hasSlash) flags |= ArchiveMember::HasPathFlag; if (hasSlash || filePath.str().length() > 15) flags |= ArchiveMember::HasLongFilenameFlag; sys::fs::file_magic type; if (sys::fs::identify_magic(mbr->path.str(), type)) type = sys::fs::file_magic::unknown; switch (type) { case sys::fs::file_magic::bitcode: flags |= ArchiveMember::BitcodeFlag; break; default: break; } mbr->flags = flags; members.insert(where,mbr); return false; }
// Insert a file into the archive before some other member. This also takes care // of extracting the necessary flags and information from the file. bool Archive::addFileBefore(const sys::Path& filePath, iterator where, std::string* ErrMsg) { if (!filePath.exists()) { if (ErrMsg) *ErrMsg = "Can not add a non-existent file to archive"; return true; } ArchiveMember* mbr = new ArchiveMember(this); mbr->data = 0; mbr->path = filePath; const sys::FileStatus *FSInfo = mbr->path.getFileStatus(false, ErrMsg); if (!FSInfo) { delete mbr; return true; } mbr->info = *FSInfo; unsigned flags = 0; bool hasSlash = filePath.str().find('/') != std::string::npos; if (hasSlash) flags |= ArchiveMember::HasPathFlag; if (hasSlash || filePath.str().length() > 15) flags |= ArchiveMember::HasLongFilenameFlag; std::string magic; mbr->path.getMagicNumber(magic,4); switch (sys::IdentifyFileType(magic.c_str(),4)) { case sys::Bitcode_FileType: flags |= ArchiveMember::BitcodeFlag; break; default: break; } mbr->flags = flags; members.insert(where,mbr); return false; }
// Execute the graph viewer. Return true if successful. static bool LLVM_ATTRIBUTE_UNUSED ExecGraphViewer(const sys::Path &ExecPath, std::vector<const char*> &args, const sys::Path &Filename, bool wait, std::string &ErrMsg) { if (wait) { if (sys::Program::ExecuteAndWait(ExecPath, &args[0],0,0,0,0,&ErrMsg)) { errs() << "Error: " << ErrMsg << "\n"; return false; } Filename.eraseFromDisk(); errs() << " done. \n"; } else { sys::Program::ExecuteNoWait(ExecPath, &args[0],0,0,0,&ErrMsg); errs() << "Remember to erase graph file: " << Filename.str() << "\n"; } return true; }
// Get just the externally visible defined symbols from the bitcode bool llvm::GetBitcodeSymbols(const sys::Path& fName, LLVMContext& Context, std::vector<std::string>& symbols, std::string* ErrMsg) { OwningPtr<MemoryBuffer> Buffer; if (error_code ec = MemoryBuffer::getFileOrSTDIN(fName.c_str(), Buffer)) { if (ErrMsg) *ErrMsg = "Could not open file '" + fName.str() + "'" + ": " + ec.message(); return true; } Module *M = ParseBitcodeFile(Buffer.get(), Context, ErrMsg); if (!M) return true; // Get the symbols getSymbols(M, symbols); // Done with the module. delete M; return true; }
/// LinkInFile - opens a bitcode file and links in all objects which /// provide symbols that are currently undefined. /// /// Inputs: /// File - The pathname of the bitcode file. /// /// Outputs: /// ErrorMessage - A C++ string detailing what error occurred, if any. /// /// Return Value: /// TRUE - An error occurred. /// FALSE - No errors. /// bool Linker::LinkInFile(const sys::Path &File, bool &is_native) { is_native = false; // Check for a file of name "-", which means "read standard input" if (File.str() == "-") { std::auto_ptr<Module> M; MemoryBuffer *Buffer = MemoryBuffer::getSTDIN(); if (!Buffer->getBufferSize()) { delete Buffer; Error = "standard input is empty"; } else { M.reset(ParseBitcodeFile(Buffer, Context, &Error)); delete Buffer; if (M.get()) if (!LinkInModule(M.get(), &Error)) return false; } return error("Cannot link stdin: " + Error); } // Make sure we can at least read the file if (!File.canRead()) return error("Cannot find linker input '" + File.str() + "'"); // If its an archive, try to link it in std::string Magic; File.getMagicNumber(Magic, 64); switch (sys::IdentifyFileType(Magic.c_str(), 64)) { default: llvm_unreachable("Bad file type identification"); case sys::Unknown_FileType: return warning("Ignoring file '" + File.str() + "' because does not contain bitcode."); case sys::Archive_FileType: // A user may specify an ar archive without -l, perhaps because it // is not installed as a library. Detect that and link the archive. if (LinkInArchive(File, is_native)) return true; break; case sys::Bitcode_FileType: { verbose("Linking bitcode file '" + File.str() + "'"); std::auto_ptr<Module> M(LoadObject(File)); if (M.get() == 0) return error("Cannot load file '" + File.str() + "': " + Error); if (LinkInModule(M.get(), &Error)) return error("Cannot link file '" + File.str() + "': " + Error); verbose("Linked in file '" + File.str() + "'"); break; } case sys::ELF_Relocatable_FileType: case sys::ELF_SharedObject_FileType: case sys::Mach_O_Object_FileType: case sys::Mach_O_FixedVirtualMemorySharedLib_FileType: case sys::Mach_O_DynamicallyLinkedSharedLib_FileType: case sys::Mach_O_DynamicallyLinkedSharedLibStub_FileType: case sys::COFF_FileType: is_native = true; break; } return false; }
void llvm::DisplayGraph(const sys::Path &Filename, bool wait, GraphProgram::Name program) { std::string ErrMsg; #if HAVE_GRAPHVIZ sys::Path Graphviz(LLVM_PATH_GRAPHVIZ); std::vector<const char*> args; args.push_back(Graphviz.c_str()); args.push_back(Filename.c_str()); args.push_back(0); errs() << "Running 'Graphviz' program... "; if (sys::Program::ExecuteAndWait(Graphviz, &args[0],0,0,0,0,&ErrMsg)) errs() << "Error viewing graph " << Filename.str() << ": " << ErrMsg << "\n"; else Filename.eraseFromDisk(); #elif (HAVE_GV && (HAVE_DOT || HAVE_FDP || HAVE_NEATO || \ HAVE_TWOPI || HAVE_CIRCO)) sys::Path PSFilename = Filename; PSFilename.appendSuffix("ps"); sys::Path prog; // Set default grapher #if HAVE_CIRCO prog = sys::Path(LLVM_PATH_CIRCO); #endif #if HAVE_TWOPI prog = sys::Path(LLVM_PATH_TWOPI); #endif #if HAVE_NEATO prog = sys::Path(LLVM_PATH_NEATO); #endif #if HAVE_FDP prog = sys::Path(LLVM_PATH_FDP); #endif #if HAVE_DOT prog = sys::Path(LLVM_PATH_DOT); #endif // Find which program the user wants #if HAVE_DOT if (program == GraphProgram::DOT) prog = sys::Path(LLVM_PATH_DOT); #endif #if (HAVE_FDP) if (program == GraphProgram::FDP) prog = sys::Path(LLVM_PATH_FDP); #endif #if (HAVE_NEATO) if (program == GraphProgram::NEATO) prog = sys::Path(LLVM_PATH_NEATO); #endif #if (HAVE_TWOPI) if (program == GraphProgram::TWOPI) prog = sys::Path(LLVM_PATH_TWOPI); #endif #if (HAVE_CIRCO) if (program == GraphProgram::CIRCO) prog = sys::Path(LLVM_PATH_CIRCO); #endif std::vector<const char*> args; args.push_back(prog.c_str()); args.push_back("-Tps"); args.push_back("-Nfontname=Courier"); args.push_back("-Gsize=7.5,10"); args.push_back(Filename.c_str()); args.push_back("-o"); args.push_back(PSFilename.c_str()); args.push_back(0); errs() << "Running '" << prog.str() << "' program... "; if (sys::Program::ExecuteAndWait(prog, &args[0], 0, 0, 0, 0, &ErrMsg)) { errs() << "Error viewing graph " << Filename.str() << ": '" << ErrMsg << "\n"; } else { errs() << " done. \n"; sys::Path gv(LLVM_PATH_GV); args.clear(); args.push_back(gv.c_str()); args.push_back(PSFilename.c_str()); args.push_back("--spartan"); args.push_back(0); ErrMsg.clear(); if (wait) { if (sys::Program::ExecuteAndWait(gv, &args[0],0,0,0,0,&ErrMsg)) errs() << "Error viewing graph: " << ErrMsg << "\n"; Filename.eraseFromDisk(); PSFilename.eraseFromDisk(); } else { sys::Program::ExecuteNoWait(gv, &args[0],0,0,0,&ErrMsg); errs() << "Remember to erase graph files: " << Filename.str() << " " << PSFilename.str() << "\n"; } } #elif HAVE_DOTTY sys::Path dotty(LLVM_PATH_DOTTY); std::vector<const char*> args; args.push_back(dotty.c_str()); args.push_back(Filename.c_str()); args.push_back(0); errs() << "Running 'dotty' program... "; if (sys::Program::ExecuteAndWait(dotty, &args[0],0,0,0,0,&ErrMsg)) { errs() << "Error viewing graph " << Filename.str() << ": " << ErrMsg << "\n"; } else { #ifdef __MINGW32__ // Dotty spawns another app and doesn't wait until it returns return; #endif Filename.eraseFromDisk(); } #endif }
// This method allows an ArchiveMember to be replaced with the data for a // different file, presumably as an update to the member. It also makes sure // the flags are reset correctly. bool ArchiveMember::replaceWith(const sys::Path& newFile, std::string* ErrMsg) { bool Exists; if (sys::fs::exists(newFile.str(), Exists) || !Exists) { if (ErrMsg) *ErrMsg = "Can not replace an archive member with a non-existent file"; return true; } data = 0; path = newFile; // SVR4 symbol tables have an empty name if (path.str() == ARFILE_SVR4_SYMTAB_NAME) flags |= SVR4SymbolTableFlag; else flags &= ~SVR4SymbolTableFlag; // BSD4.4 symbol tables have a special name if (path.str() == ARFILE_BSD4_SYMTAB_NAME) flags |= BSD4SymbolTableFlag; else flags &= ~BSD4SymbolTableFlag; // LLVM symbol tables have a very specific name if (path.str() == ARFILE_LLVM_SYMTAB_NAME) flags |= LLVMSymbolTableFlag; else flags &= ~LLVMSymbolTableFlag; // String table name if (path.str() == ARFILE_STRTAB_NAME) flags |= StringTableFlag; else flags &= ~StringTableFlag; // If it has a slash then it has a path bool hasSlash = path.str().find('/') != std::string::npos; if (hasSlash) flags |= HasPathFlag; else flags &= ~HasPathFlag; // If it has a slash or its over 15 chars then its a long filename format if (hasSlash || path.str().length() > 15) flags |= HasLongFilenameFlag; else flags &= ~HasLongFilenameFlag; // Get the signature and status info const char* signature = (const char*) data; SmallString<4> magic; if (!signature) { sys::fs::get_magic(path.str(), magic.capacity(), magic); signature = magic.c_str(); const sys::FileStatus *FSinfo = path.getFileStatus(false, ErrMsg); if (FSinfo) info = *FSinfo; else return true; } // Determine what kind of file it is. switch (sys::IdentifyFileType(signature,4)) { case sys::Bitcode_FileType: flags |= BitcodeFlag; break; default: flags &= ~BitcodeFlag; break; } return false; }
static int CompileSubprocess(const char **argv, int argc, sys::Path &ResourceDir, bool bugreport, bool versionOnly, sys::Path &apiMapPath) { std::vector<char*> llvmArgs; char apim[] = "-clam-apimap"; llvmArgs.push_back((char*)argv[0]); llvmArgs.push_back(apim); llvmArgs.push_back((char*)apiMapPath.c_str()); // Split args into cc1 and LLVM args, separator is -- int cc1_argc; for (cc1_argc=1;cc1_argc<argc;cc1_argc++) { if (StringRef(argv[cc1_argc]) == "--") { for (int i=cc1_argc+1;i<argc;i++) { llvmArgs.push_back((char*)argv[i]); } break; } } // Initialize CompilerInstance from commandline args CompilerInstance Clang; Clang.setLLVMContext(new llvm::LLVMContext); LLVMInitializeClamBCTargetInfo(); LLVMInitializeClamBCTarget(); TextDiagnosticBuffer DiagsBuffer; Diagnostic Diags(&DiagsBuffer); CompilerInvocation::CreateFromArgs(Clang.getInvocation(), argv+1, argv+cc1_argc, Diags); FrontendOptions &FrontendOpts = Clang.getInvocation().getFrontendOpts(); // Handle --version if (FrontendOpts.ShowVersion || versionOnly) { printVersion(outs(), true); exit(0); } DiagnosticOptions &DiagOpts = Clang.getInvocation().getDiagnosticOpts(); DiagOpts.ShowOptionNames = DiagOpts.ShowColors = 1; DiagOpts.MessageLength = 80;// we are writing to a file DiagOpts.Warnings.push_back("all"); DiagOpts.Warnings.push_back("no-pointer-sign"); Clang.createDiagnostics(argc-1, const_cast<char**>(argv+1)); if (!Clang.hasDiagnostics()) return 2; Clang.getInvocation().getHeaderSearchOpts().ResourceDir = ResourceDir.str(); // Set default options LangOptions &LangOpts = Clang.getInvocation().getLangOpts(); // This is a freestanding environment, without libc, etc. LangOpts.Freestanding = 1; HeaderSearchOptions &HeaderSearchOpts = Clang.getInvocation().getHeaderSearchOpts(); HeaderSearchOpts.UseStandardIncludes = 0; if (bugreport) HeaderSearchOpts.Verbose = 1; if (FrontendOpts.ProgramAction != frontend::PrintPreprocessedInput) FrontendOpts.ProgramAction = frontend::EmitBC; if (bugreport) FrontendOpts.ProgramAction = frontend::PrintPreprocessedInput; // Don't bother freeing of memory on exit FrontendOpts.DisableFree = 1; CodeGenOptions &Opts = Clang.getInvocation().getCodeGenOpts(); Opts.Inlining = CodeGenOptions::OnlyAlwaysInlining; // always generate debug info, so that ClamBC backend can output sourcelevel // diagnostics. Opts.DebugInfo = true; // FIXME: once the verifier can work w/o targetdata, and targetdate opts set // DisableLLVMOpts to true! // This is needed to avoid target-specific optimizations Opts.DisableLLVMOpts = false; AnalyzerOptions &AOpts = Clang.getInvocation().getAnalyzerOpts(); AOpts.AnalysisList.push_back(WarnDeadStores); AOpts.AnalysisList.push_back(WarnUninitVals); AOpts.AnalysisList.push_back(SecuritySyntacticChecks); AOpts.AnalysisList.push_back(WarnSizeofPointer); // Set triple Clang.getInvocation().getTargetOpts().Triple = "clambc-generic-generic"; // Set default include Clang.getInvocation().getPreprocessorOpts().Includes.push_back("bytecode.h"); // Set an LLVM error handler. llvm::llvm_install_error_handler(LLVMErrorHandler, static_cast<void*>(&Clang.getDiagnostics())); DiagsBuffer.FlushDiagnostics(Clang.getDiagnostics()); // If there were any errors in processing arguments, exit now. if (Clang.getDiagnostics().getNumErrors()) return 1; // Create the target instance. //TODO: directly create a clambc target Clang.setTarget(TargetInfo::CreateTargetInfo(Clang.getDiagnostics(), Clang.getTargetOpts())); if (!Clang.hasTarget()) return 1; // Inform the target of the language options Clang.getTarget().setForcedLangOptions(Clang.getLangOpts()); if (Clang.getHeaderSearchOpts().Verbose) { llvm::errs() << "clang -cc1 version " CLANG_VERSION_STRING << " based upon " << PACKAGE_STRING << " hosted on " << llvm::sys::getHostTriple() << "\n"; // Convert the invocation back to argument strings. std::vector<std::string> InvocationArgs; Clang.getInvocation().toArgs(InvocationArgs); // Dump the converted arguments. llvm::SmallVector<const char*, 32> Invocation2Args; llvm::errs() << "invocation argv :"; for (unsigned i = 0, e = InvocationArgs.size(); i != e; ++i) { Invocation2Args.push_back(InvocationArgs[i].c_str()); llvm::errs() << " \"" << InvocationArgs[i] << '"'; } llvm::errs() << "\n"; } std::string Input = FrontendOpts.Inputs[0].second; if (Input == "-" && bugreport) return 2; raw_fd_ostream *fd = 0; if (FrontendOpts.ProgramAction == frontend::EmitBC) { // replace output file of compiler with a tempfile, // and save the final output filename. std::string FinalOutput = FrontendOpts.OutputFile; if (FinalOutput.empty()) { if (Input == "-") FinalOutput = "-"; else { sys::Path P(sys::Path(Input).getBasename()); P.appendSuffix("cbc"); FinalOutput = P.str(); } } llvm::raw_fd_ostream *tmpfd; std::string Err2; fd = Clang.createOutputFile(FinalOutput, Err2, false); if (!fd) { Clang.getDiagnostics().Report(clang::diag::err_drv_unable_to_make_temp) << Err2; return 1; } sys::Path P = sys::Path(FinalOutput); P.eraseSuffix(); P.appendSuffix("tmp.bc"); FrontendOpts.OutputFile = P.str(); tmpfd = Clang.createOutputFile(P.str(), Err2, true); if (!tmpfd) { Clang.getDiagnostics().Report(clang::diag::err_drv_unable_to_make_temp) << Err2; return 1; } delete tmpfd; sys::RemoveFileOnSignal(sys::Path(FrontendOpts.OutputFile)); } if (!FrontendOpts.Inputs.empty()) { char srcp[] = "-clambc-src"; llvmArgs.push_back(srcp); llvmArgs.push_back(strdup(Input.c_str())); } // Parse LLVM commandline args cl::ParseCommandLineOptions(llvmArgs.size(), &llvmArgs[0]); std::string re2cpath = getTmpDir(); if (re2cpath.empty()) { llvm::errs()<< "Failed to create temporary file for re2c-out!\n"; return 2; } re2cpath += "/clambc-compiler-re2c-out"; sys::Path TmpRe2C(re2cpath); if (!FrontendOpts.Inputs.empty()) { char re2c_args[] = "--no-generation-date"; char re2c_o[] = "-o"; char name[] = ""; char *args[6] = { name, re2c_args, re2c_o, NULL, NULL, NULL }; args[4] = strdup(Input.c_str()); std::string ErrMsg(""); if (TmpRe2C.createTemporaryFileOnDisk(true, &ErrMsg)) { Clang.getDiagnostics().Report(clang::diag::err_drv_unable_to_make_temp) << ErrMsg; return 1; } sys::RemoveFileOnSignal(TmpRe2C); args[3] = strdup(TmpRe2C.str().c_str()); int ret = re2c_main(5, args); if (ret) { Clang.getDiagnostics().Report(clang::diag::err_drv_command_failed) << "re2c" << ret; return 1; } Input = TmpRe2C.str(); } // Create a file manager object to provide access to and cache the // filesystem. Clang.createFileManager(); // Create the source manager. Clang.createSourceManager(); // Create the preprocessor. Clang.createPreprocessor(); llvm::OwningPtr<FrontendAction> Act(CreateFrontendAction(Clang)); if (Act && Act->BeginSourceFile(Clang, Input, false)) { Act->Execute(); Act->EndSourceFile(); } TmpRe2C.eraseFromDisk();// erase tempfile int ret = Clang.getDiagnostics().getNumErrors() != 0; if (ret) return ret; if (FrontendOpts.ProgramAction != frontend::EmitBC) { // stop processing if not compiling a final .cbc file return 0; } ret = compileInternal(FrontendOpts.OutputFile.c_str(), Opts.OptimizationLevel, Opts.OptimizeSize, argv[0], fd, Clang); // Erase temp file, we need to do this here since OutputFile is a tempfile // only if action was EmitBC sys::Path(FrontendOpts.OutputFile).eraseFromDisk(); return ret; }