├── CMakeLists.txt ├── Makefile ├── NseTransform.cpp ├── NseTransform.h ├── README.md └── tool ├── CMakeLists.txt ├── ClangNse.cpp ├── Makefile └── nse-rewrite.sh /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | set(LLVM_LINK_COMPONENTS support) 2 | 3 | add_clang_library(nse 4 | IntegralTypeReplacer.cpp 5 | ) 6 | target_link_libraries(nse 7 | clangAST 8 | clangASTMatchers 9 | clangBasic 10 | clangFrontend 11 | clangLex 12 | clangTooling 13 | ) 14 | 15 | add_subdirectory(tool) 16 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | ##===- tools/extra/native-symbolic-execution-clang/Makefile----------------===## 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 | CLANG_LEVEL := ../../.. 10 | LIBRARYNAME := nse 11 | 12 | DIRS = tool 13 | 14 | include $(CLANG_LEVEL)/Makefile 15 | 16 | CPP.Flags += -I$(PROJ_SRC_DIR)/../clang-modernize/Core 17 | -------------------------------------------------------------------------------- /NseTransform.cpp: -------------------------------------------------------------------------------- 1 | #include "clang/ASTMatchers/ASTMatchers.h" 2 | #include "clang/ASTMatchers/ASTMatchFinder.h" 3 | #include "clang/Basic/SourceManager.h" 4 | #include "clang/Lex/Lexer.h" 5 | #include "clang/AST/ASTContext.h" 6 | #include "NseTransform.h" 7 | 8 | const char *NseInternalClassName = "crv::Internal<"; 9 | const char *NseAssumeFunctionName = "nse_assume"; 10 | const char *NseAssertFunctionName = "nse_assert"; 11 | const char *NseSymbolicFunctionRegex = "nse_symbolic.*"; 12 | const char *NseMakeSymbolicFunctionName = "nse_make_symbolic"; 13 | 14 | const char *IfConditionBindId = "if_condition"; 15 | const char *IfConditionVariableBindId = "if_condition_variable"; 16 | const char *ForConditionBindId = "for_condition"; 17 | const char *WhileConditionBindId = "while_condition"; 18 | const char *LocalVarBindId = "internal_decl"; 19 | const char *GlobalVarBindId = "external_decl"; 20 | const char *FieldBindId = "external_decl"; 21 | const char *MainFunctionBindId = "main_function"; 22 | const char *ParmVarBindId = "parm_var_decl"; 23 | const char *ReturnTypeBindId = "return_type"; 24 | const char *AssumeBindId = "assume"; 25 | const char *AssertBindId = "assert"; 26 | const char *SymbolicBindId = "symbolic"; 27 | const char *MakeSymbolicBindId = "make_symbolic"; 28 | const char *CStyleCastBindId = "c_style_cast"; 29 | 30 | bool IncludesManager::handleBeginSource(CompilerInstance &CI, 31 | StringRef Filename) { 32 | 33 | Includes = new IncludeDirectives(CI); 34 | return true; 35 | } 36 | 37 | StatementMatcher makeIfConditionMatcher() { 38 | return ifStmt( 39 | hasCondition( 40 | expr().bind(IfConditionBindId)), 41 | unless(hasConditionVariableStatement(declStmt()))); 42 | } 43 | 44 | StatementMatcher makeIfConditionVariableMatcher() { 45 | return ifStmt( 46 | hasConditionVariableStatement( 47 | declStmt().bind(IfConditionVariableBindId))); 48 | } 49 | 50 | StatementMatcher makeForConditionMatcher() { 51 | return forStmt( 52 | hasCondition( 53 | expr().bind(ForConditionBindId))); 54 | } 55 | 56 | StatementMatcher makeWhileConditionMatcher() { 57 | return whileStmt( 58 | hasCondition( 59 | expr().bind(WhileConditionBindId))); 60 | } 61 | 62 | StatementMatcher makeLocalVarMatcher() { 63 | return declStmt(has(varDecl())).bind(LocalVarBindId); 64 | } 65 | 66 | DeclarationMatcher makeGlobalVarMatcher() { 67 | return varDecl().bind(GlobalVarBindId); 68 | } 69 | 70 | DeclarationMatcher makeFieldMatcher() { 71 | return fieldDecl().bind(FieldBindId); 72 | } 73 | 74 | DeclarationMatcher makeMainFunctionMatcher() { 75 | return functionDecl(hasName("main")).bind(MainFunctionBindId); 76 | } 77 | 78 | DeclarationMatcher makeParmVarDeclMatcher() { 79 | return parmVarDecl().bind(ParmVarBindId); 80 | } 81 | 82 | DeclarationMatcher makeReturnTypeMatcher() { 83 | return functionDecl().bind(ReturnTypeBindId); 84 | } 85 | 86 | StatementMatcher makeAssumeMatcher() { 87 | return callExpr(callee(functionDecl( 88 | hasName(NseAssumeFunctionName)))).bind(AssumeBindId); 89 | } 90 | 91 | StatementMatcher makeAssertMatcher() { 92 | return callExpr(callee(functionDecl( 93 | hasName(NseAssertFunctionName)))).bind(AssertBindId); 94 | } 95 | 96 | StatementMatcher makeSymbolicMatcher() { 97 | return callExpr(callee(functionDecl( 98 | matchesName(NseSymbolicFunctionRegex)))).bind(SymbolicBindId); 99 | } 100 | 101 | StatementMatcher makeMakeSymbolicMatcher() { 102 | return callExpr(callee(functionDecl(hasName( 103 | NseMakeSymbolicFunctionName)))).bind(MakeSymbolicBindId); 104 | } 105 | 106 | StatementMatcher makeCStyleCastMatcher() { 107 | return cStyleCastExpr().bind(CStyleCastBindId); 108 | } 109 | 110 | void instrumentControlFlow( 111 | const std::string& NseBranchStrategy, 112 | SourceRange SR, 113 | SourceManager &SM, 114 | const LangOptions &LO, 115 | tooling::Replacements &R) { 116 | 117 | CharSourceRange Range = Lexer::makeFileCharRange( 118 | CharSourceRange::getTokenRange(SR), SM, LO); 119 | 120 | R.insert(tooling::Replacement(SM, Range.getBegin(), 0, NseBranchStrategy + "(")); 121 | R.insert(tooling::Replacement(SM, Range.getEnd(), 0, ")")); 122 | } 123 | 124 | void IfConditionReplacer::run(const MatchFinder::MatchResult &Result) { 125 | const Expr *E = Result.Nodes.getNodeAs(IfConditionBindId); 126 | assert(E && "Bad Callback. No node provided"); 127 | 128 | SourceLocation Loc = E->getExprLoc(); 129 | SourceManager &SM = *Result.SourceManager; 130 | if (!Result.Context->getSourceManager().isWrittenInMainFile(Loc)) 131 | { 132 | DEBUG(llvm::errs() << "Ignore file: " << SM.getFilename(Loc) << '\n'); 133 | return; 134 | } 135 | 136 | SourceRange Range = E->getSourceRange(); 137 | instrumentControlFlow(NseBranchStrategy, Range, SM, 138 | Result.Context->getLangOpts(), *Replace); 139 | } 140 | 141 | void IfConditionVariableReplacer::run(const MatchFinder::MatchResult &Result) { 142 | const Expr *E = Result.Nodes.getNodeAs(IfConditionVariableBindId); 143 | 144 | SourceLocation Loc = E->getExprLoc(); 145 | SourceManager &SM = *Result.SourceManager; 146 | if (!Result.Context->getSourceManager().isWrittenInMainFile(Loc)) 147 | { 148 | DEBUG(llvm::errs() << "Ignore file: " << SM.getFilename(Loc) << '\n'); 149 | return; 150 | } 151 | 152 | assert(0 && "Condition variables are currently not supported"); 153 | } 154 | 155 | void ForConditionReplacer::run(const MatchFinder::MatchResult &Result) { 156 | const Expr *E = Result.Nodes.getNodeAs(ForConditionBindId); 157 | assert(E && "Bad Callback. No node provided"); 158 | 159 | SourceLocation Loc = E->getExprLoc(); 160 | SourceManager &SM = *Result.SourceManager; 161 | if (!Result.Context->getSourceManager().isWrittenInMainFile(Loc)) 162 | { 163 | DEBUG(llvm::errs() << "Ignore file: " << SM.getFilename(Loc) << '\n'); 164 | return; 165 | } 166 | 167 | instrumentControlFlow(NseBranchStrategy, E->getSourceRange(), SM, 168 | Result.Context->getLangOpts(), *Replace); 169 | } 170 | 171 | void WhileConditionReplacer::run(const MatchFinder::MatchResult &Result) { 172 | const Expr *E = Result.Nodes.getNodeAs(WhileConditionBindId); 173 | assert(E && "Bad Callback. No node provided"); 174 | 175 | SourceLocation Loc = E->getExprLoc(); 176 | SourceManager &SM = *Result.SourceManager; 177 | if (!Result.Context->getSourceManager().isWrittenInMainFile(Loc)) 178 | { 179 | DEBUG(llvm::errs() << "Ignore file: " << SM.getFilename(Loc) << '\n'); 180 | return; 181 | } 182 | 183 | instrumentControlFlow(NseBranchStrategy, E->getSourceRange(), SM, 184 | Result.Context->getLangOpts(), *Replace); 185 | } 186 | 187 | // TODO: Fix buffer corruption issue, perhaps use clang-apply-replacements? 188 | void addNseHeader( 189 | const FileEntry *File, 190 | tooling::Replacements &Replace, 191 | IncludeDirectives &Includes) { 192 | 193 | const tooling::Replacement &IncludeReplace = Includes.addAngledInclude(File, "crv.h"); 194 | if (IncludeReplace.isApplicable()) { 195 | Replace.insert(IncludeReplace); 196 | } 197 | } 198 | 199 | void instrumentNamedVarDecl( 200 | StringRef NseClassPrefix, 201 | StringRef TypeName, 202 | StringRef VariableName, 203 | SourceRange SR, 204 | SourceManager &SM, 205 | const LangOptions &LO, 206 | tooling::Replacements &Replace) { 207 | 208 | CharSourceRange Range = Lexer::makeFileCharRange( 209 | CharSourceRange::getTokenRange(SR), SM, LO); 210 | 211 | Replace.insert(tooling::Replacement(SM, Range, (NseClassPrefix + TypeName + "> " + VariableName).str())); 212 | } 213 | 214 | void instrumentVarDecl( 215 | StringRef NseClassPrefix, 216 | SourceRange SR, 217 | SourceManager &SM, 218 | const LangOptions &LO, 219 | tooling::Replacements &Replace) { 220 | 221 | const std::string NseClassSuffix = ">"; 222 | CharSourceRange Range = Lexer::makeFileCharRange( 223 | CharSourceRange::getTokenRange(SR), SM, LO); 224 | 225 | Replace.insert(tooling::Replacement(SM, Range.getBegin(), 0, NseClassPrefix)); 226 | Replace.insert(tooling::Replacement(SM, Range.getEnd(), 0, NseClassSuffix)); 227 | } 228 | 229 | /// Non-void fundamental types, pointers and arrays of fundamental types 230 | bool isSupportedType(QualType QT) { 231 | QualType CanonicalType = QT.getTypePtr()->getCanonicalTypeInternal(); 232 | 233 | if (CanonicalType->isVoidType()) 234 | return false; 235 | 236 | if (CanonicalType->isFundamentalType()) 237 | return true; 238 | 239 | if (const PointerType *PtrType = dyn_cast(CanonicalType.getTypePtr())) { 240 | QualType PointeeType = PtrType->getPointeeType(); 241 | return PointeeType->isFundamentalType(); 242 | } else if (const ArrayType *PtrType = dyn_cast(CanonicalType.getTypePtr())) { 243 | QualType ElementType = PtrType->getElementType(); 244 | return ElementType->isFundamentalType(); 245 | } 246 | 247 | return false; 248 | } 249 | 250 | void LocalVarReplacer::run(const MatchFinder::MatchResult &Result) { 251 | const DeclStmt *D = Result.Nodes.getNodeAs(LocalVarBindId); 252 | assert(D && "Bad Callback. No node provided"); 253 | 254 | const VarDecl *V = cast(*D->decl_begin()); 255 | 256 | if (!V->hasLocalStorage() || !isSupportedType(V->getType())) 257 | return; 258 | 259 | SourceLocation Loc = V->getLocation(); 260 | SourceManager &SM = *Result.SourceManager; 261 | if (!Result.Context->getSourceManager().isWrittenInMainFile(Loc)) 262 | { 263 | DEBUG(llvm::errs() << "Ignore file: " << SM.getFilename(Loc) << '\n'); 264 | return; 265 | } 266 | 267 | TypeLoc TL = V->getTypeSourceInfo()->getTypeLoc(); 268 | 269 | if (V->getType()->isArrayType()) 270 | instrumentNamedVarDecl(NseInternalClassName, V->getType().getAsString(), 271 | V->getName(), TL.getSourceRange(), SM, Result.Context->getLangOpts(), *Replace); 272 | else 273 | instrumentVarDecl(NseInternalClassName, TL.getSourceRange(), SM, 274 | Result.Context->getLangOpts(), *Replace); 275 | 276 | //const FileEntry *File = SM.getFileEntryForID(SM.getFileID(TL.getBeginLoc())); 277 | //addNseHeader(File, *Replace, *IM->Includes); 278 | } 279 | 280 | void GlobalVarReplacer::run(const MatchFinder::MatchResult &Result) { 281 | const VarDecl *V = Result.Nodes.getNodeAs(GlobalVarBindId); 282 | assert(V && "Bad Callback. No node provided"); 283 | if (!V->hasGlobalStorage() || !isSupportedType(V->getType())) 284 | return; 285 | 286 | SourceLocation Loc = V->getLocation(); 287 | SourceManager &SM = *Result.SourceManager; 288 | if (!Result.Context->getSourceManager().isWrittenInMainFile(Loc)) 289 | { 290 | DEBUG(llvm::errs() << "Ignore file: " << SM.getFilename(Loc) << '\n'); 291 | return; 292 | } 293 | 294 | GlobalVars.push_back(V); 295 | 296 | TypeLoc TL = V->getTypeSourceInfo()->getTypeLoc(); 297 | if (V->getType()->isArrayType()) 298 | instrumentNamedVarDecl(NseInternalClassName, V->getType().getAsString(), 299 | V->getName(), TL.getSourceRange(), SM, Result.Context->getLangOpts(), *Replace); 300 | else 301 | instrumentVarDecl(NseInternalClassName, TL.getSourceRange(), SM, 302 | Result.Context->getLangOpts(), *Replace); 303 | } 304 | 305 | void FieldReplacer::run(const MatchFinder::MatchResult &Result) { 306 | const FieldDecl *V = Result.Nodes.getNodeAs(GlobalVarBindId); 307 | assert(V && "Bad Callback. No node provided"); 308 | if (!isSupportedType(V->getType())) 309 | return; 310 | 311 | SourceLocation Loc = V->getLocation(); 312 | SourceManager &SM = *Result.SourceManager; 313 | if (!Result.Context->getSourceManager().isWrittenInMainFile(Loc)) 314 | { 315 | DEBUG(llvm::errs() << "Ignore file: " << SM.getFilename(Loc) << '\n'); 316 | return; 317 | } 318 | 319 | TypeLoc TL = V->getTypeSourceInfo()->getTypeLoc(); 320 | if (V->getType()->isArrayType()) 321 | instrumentNamedVarDecl(NseInternalClassName, V->getType().getAsString(), 322 | V->getName(), TL.getSourceRange(), SM, Result.Context->getLangOpts(), *Replace); 323 | else 324 | instrumentVarDecl(NseInternalClassName, TL.getSourceRange(), SM, 325 | Result.Context->getLangOpts(), *Replace); 326 | } 327 | 328 | void MainFunctionReplacer::run(const MatchFinder::MatchResult &Result) { 329 | const FunctionDecl *D = Result.Nodes.getNodeAs(MainFunctionBindId); 330 | assert(D && "Bad Callback. No node provided"); 331 | assert(GlobalVars && "GlobalVars is NULL"); 332 | 333 | SourceManager &SM = *Result.SourceManager; 334 | SourceLocation Loc = D->getLocation(); 335 | if (!Result.Context->getSourceManager().isWrittenInMainFile(Loc)) 336 | { 337 | DEBUG(llvm::errs() << "Ignore file: " << SM.getFilename(Loc) << '\n'); 338 | return; 339 | } 340 | 341 | SourceLocation NameLocBegin = D->getNameInfo().getBeginLoc(); 342 | Replace->insert(tooling::Replacement(SM, NameLocBegin, 4, "nse_main")); 343 | 344 | if (!D->hasBody()) 345 | return; 346 | 347 | DEBUG(llvm::errs() << "MainFunctionReplacer: " << GlobalVars->size() 348 | << " global variables" << "\n"); 349 | 350 | const std::string NseMakeZero = " " + NseNamespace + "::make_zero("; 351 | Stmt *FuncBody = D->getBody(); 352 | SourceLocation BodyLocBegin = FuncBody->getLocStart().getLocWithOffset(1); 353 | std::string MakeInits = "\n"; 354 | for (const VarDecl * V : *GlobalVars) { 355 | if (V->hasInit()) { 356 | APValue* APV = V->evaluateValue(); 357 | assert(APV); 358 | MakeInits += " " + V->getName().str() + " = " + APV->getAsString(*Result.Context, V->getType()) + ";\n"; 359 | } else { 360 | MakeInits += NseMakeZero + V->getName().str() + ");\n"; 361 | } 362 | } 363 | Replace->insert(tooling::Replacement(SM, BodyLocBegin, 0, MakeInits)); 364 | 365 | const std::string NseStrategy = NseNamespace + "::" + Strategy + "()"; 366 | SourceLocation BodyEndLoc = FuncBody->getLocEnd().getLocWithOffset(1); 367 | Replace->insert(tooling::Replacement(SM, BodyEndLoc, 0, 368 | "\n\n" 369 | "int main() {\n" 370 | " bool error = false;\n" 371 | " std::chrono::seconds seconds(std::chrono::seconds::zero());\n" 372 | " {\n" 373 | " smt::NonReentrantTimer timer(seconds);\n" 374 | "\n" 375 | " do {\n" 376 | " nse_main();\n" 377 | " error |= smt::sat == " + NseStrategy + ".check();\n" 378 | " } while (" + NseStrategy + ".find_next_path() && !error);\n" 379 | " }\n" 380 | "\n" 381 | " if (error)\n" 382 | " std::cout << \"Found bug!\" << std::endl;\n" 383 | " else\n" 384 | " std::cout << \"Could not find any bugs.\" << std::endl;\n" 385 | "\n" 386 | " report_statistics(" + NseStrategy + ".solver().stats(), " + NseStrategy + ".stats(), seconds);\n" 387 | "\n" 388 | " return error;\n" 389 | "}")); 390 | } 391 | 392 | // Passes integral parameters passed by value and other types by reference 393 | void ParmVarReplacer::run(const MatchFinder::MatchResult &Result) { 394 | const ParmVarDecl *V = Result.Nodes.getNodeAs(ParmVarBindId); 395 | assert(V && "Bad Callback. No node provided"); 396 | 397 | if (!isSupportedType(V->getType())) 398 | return; 399 | 400 | SourceLocation Loc = V->getLocation(); 401 | SourceManager &SM = *Result.SourceManager; 402 | if (!Result.Context->getSourceManager().isWrittenInMainFile(Loc)) 403 | { 404 | DEBUG(llvm::errs() << "Ignore file: " << SM.getFilename(Loc) << '\n'); 405 | return; 406 | } 407 | 408 | TypeLoc TL = V->getTypeSourceInfo()->getTypeLoc(); 409 | 410 | // recognize pointer decay 411 | if (V->getOriginalType()->isArrayType()) 412 | instrumentNamedVarDecl(NseInternalClassName, V->getType().getAsString(), 413 | V->getName(), TL.getSourceRange(), SM, Result.Context->getLangOpts(), *Replace); 414 | else 415 | instrumentVarDecl(NseInternalClassName, TL.getSourceRange(), SM, 416 | Result.Context->getLangOpts(), *Replace); 417 | } 418 | 419 | void ReturnTypeReplacer::run(const MatchFinder::MatchResult &Result) { 420 | const FunctionDecl *D = Result.Nodes.getNodeAs(ReturnTypeBindId); 421 | assert(D && "Bad Callback. No node provided"); 422 | 423 | if (D->isMain() || !isSupportedType(D->getReturnType())) 424 | return; 425 | 426 | SourceLocation Loc = D->getLocation(); 427 | SourceManager &SM = *Result.SourceManager; 428 | if (!Result.Context->getSourceManager().isWrittenInMainFile(Loc)) 429 | { 430 | DEBUG(llvm::errs() << "Ignore file: " << SM.getFilename(Loc) << '\n'); 431 | return; 432 | } 433 | 434 | TypeLoc TL = D->getTypeSourceInfo()->getTypeLoc(). 435 | IgnoreParens().castAs().getReturnLoc(); 436 | 437 | instrumentVarDecl(NseInternalClassName, TL.getSourceRange(), SM, 438 | Result.Context->getLangOpts(), *Replace); 439 | } 440 | 441 | void AssumeReplacer::run(const MatchFinder::MatchResult &Result) { 442 | const CallExpr *E = Result.Nodes.getNodeAs(AssumeBindId); 443 | assert(E && "Bad Callback. No node provided"); 444 | 445 | SourceManager &SM = *Result.SourceManager; 446 | SourceLocation LocBegin = E->getCallee()->getExprLoc(); 447 | 448 | if (!Result.Context->getSourceManager().isWrittenInMainFile(LocBegin)) 449 | { 450 | DEBUG(llvm::errs() << "Ignore file: " << SM.getFilename(LocBegin) << '\n'); 451 | return; 452 | } 453 | 454 | const std::string NseAssume = NseStrategy + ".add_assertion"; 455 | Replace->insert(tooling::Replacement(SM, LocBegin, 10, NseAssume)); 456 | } 457 | 458 | void AssertReplacer::run(const MatchFinder::MatchResult &Result) { 459 | const CallExpr *E = Result.Nodes.getNodeAs(AssertBindId); 460 | assert(E && "Bad Callback. No node provided"); 461 | 462 | SourceManager &SM = *Result.SourceManager; 463 | SourceLocation LocBegin = E->getCallee()->getExprLoc(); 464 | 465 | if (!Result.Context->getSourceManager().isWrittenInMainFile(LocBegin)) 466 | { 467 | DEBUG(llvm::errs() << "Ignore file: " << SM.getFilename(LocBegin) << '\n'); 468 | return; 469 | } 470 | 471 | const std::string NseAssert = NseStrategy + ".add_error(!"; 472 | Replace->insert(tooling::Replacement(SM, LocBegin, 10, NseAssert)); 473 | Replace->insert(tooling::Replacement(SM, E->getRParenLoc(), 0, ")")); 474 | } 475 | 476 | void SymbolicReplacer::run(const MatchFinder::MatchResult &Result) { 477 | const CallExpr *E = Result.Nodes.getNodeAs(SymbolicBindId); 478 | assert(E && "Bad Callback. No node provided"); 479 | 480 | SourceManager &SM = *Result.SourceManager; 481 | SourceLocation Loc = E->getCallee()->getExprLoc(); 482 | 483 | if (!Result.Context->getSourceManager().isWrittenInMainFile(Loc)) 484 | { 485 | DEBUG(llvm::errs() << "Ignore file: " << SM.getFilename(Loc) << '\n'); 486 | return; 487 | } 488 | 489 | QualType QT = E->getCallReturnType(); 490 | SourceRange SR = E->getCallee()->getSourceRange(); 491 | CharSourceRange Range = Lexer::makeFileCharRange( 492 | CharSourceRange::getTokenRange(SR), SM, Result.Context->getLangOpts()); 493 | 494 | const std::string NseAny = NseNamespace + "::any<"; 495 | Replace->insert(tooling::Replacement(SM, Range, NseAny + QT.getAsString() + ">")); 496 | } 497 | 498 | void MakeSymbolicReplacer::run(const MatchFinder::MatchResult &Result) { 499 | const CallExpr *E = Result.Nodes.getNodeAs(MakeSymbolicBindId); 500 | assert(E && "Bad Callback. No node provided"); 501 | 502 | SourceManager &SM = *Result.SourceManager; 503 | SourceLocation Loc = E->getCallee()->getExprLoc(); 504 | 505 | if (!Result.Context->getSourceManager().isWrittenInMainFile(Loc)) 506 | { 507 | DEBUG(llvm::errs() << "Ignore file: " << SM.getFilename(Loc) << '\n'); 508 | return; 509 | } 510 | 511 | SourceRange SR = E->getCallee()->getSourceRange(); 512 | CharSourceRange Range = Lexer::makeFileCharRange( 513 | CharSourceRange::getTokenRange(SR), SM, Result.Context->getLangOpts()); 514 | 515 | const std::string NseMakeAny = NseNamespace + "::make_any"; 516 | Replace->insert(tooling::Replacement(SM, Range, NseMakeAny)); 517 | } 518 | 519 | void CStyleCastReplacer::run(const MatchFinder::MatchResult &Result) { 520 | const CStyleCastExpr *E = Result.Nodes.getNodeAs(CStyleCastBindId); 521 | assert(E && "Bad Callback. No node provided"); 522 | 523 | SourceManager &SM = *Result.SourceManager; 524 | if (!Result.Context->getSourceManager().isWrittenInMainFile(E->getLocStart())) 525 | { 526 | DEBUG(llvm::errs() << "Ignore file: " << SM.getFilename(E->getLocStart()) << '\n'); 527 | return; 528 | } 529 | 530 | CharSourceRange CR; 531 | CR.setBegin(E->getLocStart()); 532 | CR.setEnd(E->getLocEnd()); 533 | 534 | Replace->insert(tooling::Replacement(SM, CR, NseInternalClassName + 535 | E->getTypeAsWritten().getAsString())); 536 | 537 | SourceRange SR = E->getSubExpr()->getSourceRange(); 538 | 539 | CharSourceRange Range = Lexer::makeFileCharRange( 540 | CharSourceRange::getTokenRange(SR), SM, Result.Context->getLangOpts()); 541 | 542 | Replace->insert(tooling::Replacement(SM, Range.getBegin(), 0, ">::cast(")); 543 | Replace->insert(tooling::Replacement(SM, Range.getEnd(), 0, ")")); 544 | } 545 | -------------------------------------------------------------------------------- /NseTransform.h: -------------------------------------------------------------------------------- 1 | #define DEBUG_TYPE "NseTransform" 2 | 3 | #include "llvm/Support/Debug.h" 4 | #include "clang/ASTMatchers/ASTMatchers.h" 5 | #include "clang/ASTMatchers/ASTMatchFinder.h" 6 | #include "clang/Tooling/Refactoring.h" 7 | #include "IncludeDirectives.h" 8 | 9 | #include 10 | #include 11 | 12 | #ifndef CLANG_CRV_TRANSFORM_H 13 | #define CLANG_CRV_TRANSFORM_H 14 | 15 | using namespace clang; 16 | using namespace clang::ast_matchers; 17 | 18 | extern const char *NseInternalClassName; 19 | extern const char *NseAssumeFunctionName; 20 | extern const char *NseAssertFunctionName; 21 | extern const char *NseSymbolicFunctionRegex; 22 | extern const char *NseMakeSymbolicFunctionName; 23 | 24 | extern const char *IfConditionBindId; 25 | extern const char *IfConditionVariableBindId; 26 | extern const char *ForConditionBindId; 27 | extern const char *LocalVarBindId; 28 | extern const char *GlobalVarBindId; 29 | extern const char *FieldBindId; 30 | extern const char *MainFunctionBindId; 31 | extern const char *FieldBindId; 32 | extern const char *ParmVarBindId; 33 | extern const char *ReturnTypeBindId; 34 | extern const char *CStyleCastBindId; 35 | 36 | StatementMatcher makeIfConditionMatcher(); 37 | StatementMatcher makeIfConditionVariableMatcher(); 38 | StatementMatcher makeForConditionMatcher(); 39 | StatementMatcher makeWhileConditionMatcher(); 40 | StatementMatcher makeLocalVarMatcher(); 41 | DeclarationMatcher makeGlobalVarMatcher(); 42 | DeclarationMatcher makeFieldMatcher(); 43 | DeclarationMatcher makeMainFunctionMatcher(); 44 | DeclarationMatcher makeParmVarDeclMatcher(); 45 | DeclarationMatcher makeReturnTypeMatcher(); 46 | StatementMatcher makeAssumeMatcher(); 47 | StatementMatcher makeAssertMatcher(); 48 | StatementMatcher makeSymbolicMatcher(); 49 | StatementMatcher makeMakeSymbolicMatcher(); 50 | StatementMatcher makeCStyleCastMatcher(); 51 | 52 | void instrumentVarDecl( 53 | StringRef crvClass, 54 | SourceRange SR, 55 | SourceManager &SM, 56 | tooling::Replacements &R); 57 | 58 | void instrumentNamedVarDecl( 59 | StringRef NseClassPrefix, 60 | StringRef TypeName, 61 | StringRef VariableName, 62 | SourceRange SR, 63 | SourceManager &SM, 64 | const LangOptions &LO, 65 | tooling::Replacements &Replace); 66 | 67 | struct IncludesManager : public tooling::SourceFileCallbacks { 68 | IncludesManager() 69 | : Includes(0) {} 70 | 71 | ~IncludesManager() { 72 | delete Includes; 73 | } 74 | 75 | IncludeDirectives* Includes; 76 | 77 | virtual bool handleBeginSource(CompilerInstance &CI, StringRef Filename) 78 | override; 79 | }; 80 | 81 | class IfConditionReplacer : public MatchFinder::MatchCallback { 82 | public : 83 | IfConditionReplacer( 84 | const std::string& NseBranchStrategy, 85 | tooling::Replacements *Replace) 86 | : NseBranchStrategy(NseBranchStrategy), 87 | Replace(Replace) {} 88 | 89 | virtual void run(const MatchFinder::MatchResult &Result) 90 | override; 91 | 92 | private: 93 | const std::string& NseBranchStrategy; 94 | tooling::Replacements *Replace; 95 | }; 96 | 97 | class IfConditionVariableReplacer : public MatchFinder::MatchCallback { 98 | public : 99 | virtual void run(const MatchFinder::MatchResult &Result) 100 | override; 101 | }; 102 | 103 | class ForConditionReplacer : public MatchFinder::MatchCallback { 104 | public : 105 | ForConditionReplacer( 106 | const std::string& NseBranchStrategy, 107 | tooling::Replacements *Replace) 108 | : NseBranchStrategy(NseBranchStrategy), 109 | Replace(Replace) {} 110 | 111 | virtual void run(const MatchFinder::MatchResult &Result) 112 | override; 113 | 114 | private: 115 | const std::string& NseBranchStrategy; 116 | tooling::Replacements *Replace; 117 | }; 118 | 119 | class WhileConditionReplacer : public MatchFinder::MatchCallback { 120 | public : 121 | WhileConditionReplacer( 122 | const std::string& NseBranchStrategy, 123 | tooling::Replacements *Replace) 124 | : NseBranchStrategy(NseBranchStrategy), 125 | Replace(Replace) {} 126 | 127 | virtual void run(const MatchFinder::MatchResult &Result) 128 | override; 129 | 130 | private: 131 | const std::string& NseBranchStrategy; 132 | tooling::Replacements *Replace; 133 | }; 134 | 135 | class LocalVarReplacer : public MatchFinder::MatchCallback { 136 | public : 137 | LocalVarReplacer(tooling::Replacements *Replace) 138 | : Replace(Replace) {} 139 | 140 | virtual void run(const MatchFinder::MatchResult &Result) 141 | override; 142 | 143 | private: 144 | tooling::Replacements *Replace; 145 | }; 146 | 147 | class GlobalVarReplacer : public MatchFinder::MatchCallback { 148 | public : 149 | GlobalVarReplacer(tooling::Replacements *Replace) 150 | : GlobalVars(), Replace(Replace) {} 151 | 152 | virtual void run(const MatchFinder::MatchResult &Result) 153 | override; 154 | 155 | std::vector GlobalVars; 156 | 157 | private: 158 | tooling::Replacements *Replace; 159 | }; 160 | 161 | class FieldReplacer : public MatchFinder::MatchCallback { 162 | public : 163 | FieldReplacer(tooling::Replacements *Replace) 164 | : Replace(Replace) {} 165 | 166 | virtual void run(const MatchFinder::MatchResult &Result) 167 | override; 168 | 169 | private: 170 | tooling::Replacements *Replace; 171 | }; 172 | 173 | class MainFunctionReplacer : public MatchFinder::MatchCallback { 174 | public : 175 | MainFunctionReplacer( 176 | const std::string& NseNamespace, 177 | const std::string& Strategy, 178 | tooling::Replacements *Replace, 179 | std::vector *GlobalVars, 180 | IncludesManager* IM) 181 | : NseNamespace(NseNamespace), 182 | Strategy(Strategy), 183 | Replace(Replace), 184 | GlobalVars(GlobalVars), 185 | IM(IM) {} 186 | 187 | virtual void run(const MatchFinder::MatchResult &Result) 188 | override; 189 | 190 | private: 191 | const std::string& NseNamespace; 192 | const std::string& Strategy; 193 | tooling::Replacements *Replace; 194 | std::vector *GlobalVars; 195 | IncludesManager* IM; 196 | }; 197 | 198 | class ParmVarReplacer : public MatchFinder::MatchCallback { 199 | public : 200 | ParmVarReplacer(tooling::Replacements *Replace) 201 | : Replace(Replace) {} 202 | 203 | virtual void run(const MatchFinder::MatchResult &Result) 204 | override; 205 | 206 | private: 207 | tooling::Replacements *Replace; 208 | }; 209 | 210 | class ReturnTypeReplacer : public MatchFinder::MatchCallback { 211 | public : 212 | ReturnTypeReplacer(tooling::Replacements *Replace) 213 | : Replace(Replace) {} 214 | 215 | virtual void run(const MatchFinder::MatchResult &Result) 216 | override; 217 | 218 | private: 219 | tooling::Replacements *Replace; 220 | }; 221 | 222 | class AssumeReplacer : public MatchFinder::MatchCallback { 223 | public : 224 | AssumeReplacer( 225 | const std::string& NseStrategy, 226 | tooling::Replacements *Replace) 227 | : NseStrategy(NseStrategy), 228 | Replace(Replace) {} 229 | 230 | virtual void run(const MatchFinder::MatchResult &Result) 231 | override; 232 | 233 | private: 234 | const std::string& NseStrategy; 235 | tooling::Replacements *Replace; 236 | }; 237 | 238 | class AssertReplacer : public MatchFinder::MatchCallback { 239 | public : 240 | AssertReplacer( 241 | const std::string& NseStrategy, 242 | tooling::Replacements *Replace) 243 | : NseStrategy(NseStrategy), 244 | Replace(Replace) {} 245 | 246 | virtual void run(const MatchFinder::MatchResult &Result) 247 | override; 248 | 249 | private: 250 | const std::string& NseStrategy; 251 | tooling::Replacements *Replace; 252 | }; 253 | 254 | class SymbolicReplacer : public MatchFinder::MatchCallback { 255 | public : 256 | SymbolicReplacer( 257 | const std::string& NseNamespace, 258 | tooling::Replacements *Replace) 259 | : NseNamespace(NseNamespace), 260 | Replace(Replace) {} 261 | 262 | virtual void run(const MatchFinder::MatchResult &Result) 263 | override; 264 | 265 | private: 266 | const std::string& NseNamespace; 267 | tooling::Replacements *Replace; 268 | }; 269 | 270 | class MakeSymbolicReplacer : public MatchFinder::MatchCallback { 271 | public : 272 | MakeSymbolicReplacer( 273 | const std::string& NseNamespace, 274 | tooling::Replacements *Replace) 275 | : NseNamespace(NseNamespace), 276 | Replace(Replace) {} 277 | 278 | virtual void run(const MatchFinder::MatchResult &Result) 279 | override; 280 | 281 | private: 282 | const std::string& NseNamespace; 283 | tooling::Replacements *Replace; 284 | }; 285 | 286 | class CStyleCastReplacer : public MatchFinder::MatchCallback { 287 | public : 288 | CStyleCastReplacer( 289 | const std::string& NseNamespace, 290 | tooling::Replacements *Replace) 291 | : NseNamespace(NseNamespace), 292 | Replace(Replace) {} 293 | 294 | virtual void run(const MatchFinder::MatchResult &Result) 295 | override; 296 | 297 | private: 298 | const std::string& NseNamespace; 299 | tooling::Replacements *Replace; 300 | }; 301 | 302 | #endif 303 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Clang CRV Front-end 2 | 3 | This is the front-end to CRV, a research project into fast symbolic execution 4 | of sequential and concurrent C++11 code. CRV consists of two parts: an 5 | instrumentation module and a [runtime library][nse]. 6 | 7 | This design allows CRV to leverage advanced C++11 features to symbolically 8 | execute code efficiently. Efficiency is particularly important for things 9 | such as constant propagation, a well-known technique for simplifying symbolic 10 | expressions at runtime. This can give [performance gains][performance-tests] 11 | of several orders of magnitude (e.g. up to a million times faster) but 12 | requires certain source-to-source transformations of the user's program. 13 | 14 | This is where the CRV frond-end comes in: it takes as input a C++11 program 15 | that the user wants to symbolically execute. Given such a program, the 16 | front-end is responsible for instrumenting it so that it can be linked with 17 | the CRV library. The necessary source-to-source transformations are implemented 18 | with Clang's [LibASTMatchers][clang-ast-matchers] library. 19 | 20 | CRV and the front-end evolve together and there is still more work to be done. 21 | If you would like to get involved, feel free to get in touch! 22 | 23 | [nse]: https://github.com/ahorn/smt-kit#native-symbolic-execution 24 | [performance-tests]: https://github.com/ahorn/smt-kit/blob/master/test/crv_performance_test.cpp 25 | 26 | ## Install 27 | 28 | For convenience, here is a possible way to build the project: 29 | 30 | $ svn co http://llvm.org/svn/llvm-project/llvm/trunk llvm 31 | $ cd llvm/tools 32 | $ svn co http://llvm.org/svn/llvm-project/cfe/trunk clang 33 | $ cd ../.. 34 | $ cd llvm/projects 35 | $ svn co http://llvm.org/svn/llvm-project/compiler-rt/trunk compiler-rt 36 | $ cd ../.. 37 | $ cd llvm/tools/clang/tools 38 | $ svn co http://llvm.org/svn/llvm-project/clang-tools-extra/trunk extra 39 | $ cd ../../../.. 40 | $ mkdir -p build/target 41 | $ cd llvm/tools/clang/tools/extra 42 | $ git clone https://github.com/ahorn/native-symbolic-execution-clang.git 43 | $ echo "DIRS += native-symbolic-execution-clang" >> Makefile 44 | $ echo "add_subdirectory(native-symbolic-execution-clang)" >> CMakeLists.txt 45 | $ cd ../../../../../build 46 | $ ../llvm/configure --prefix=`pwd`/target 47 | $ make 48 | $ make check 49 | $ make install 50 | 51 | ## Getting started 52 | 53 | For illustration purposes, let `example.cpp` be the following C++ program: 54 | 55 | ```C++ 56 | short GlobalVariable; 57 | 58 | short foo(short a) { 59 | return a; 60 | } 61 | 62 | int main() { 63 | short i = 0; 64 | 65 | for (; i < 8; i++) {} 66 | if (i == 9) { 67 | GlobalVariable = i; 68 | } 69 | 70 | return 0; 71 | } 72 | ``` 73 | 74 | To see the effects of the source-to-source transformation, 75 | execute the following commands: 76 | 77 | $ /path/to/clang-nse example.cpp -- 78 | $ cat example.cpp 79 | 80 | The output looks as follows: 81 | 82 | ```C++ 83 | crv::External GlobalVariable; 84 | 85 | crv::Internal foo(crv::Internal a) { 86 | return a; 87 | } 88 | 89 | int main() { 90 | crv::Internal i = 0; 91 | 92 | for (; crv::tracer().decide_flip(i < 8); crv::post_increment(i)) {} 93 | if (crv::tracer().decide_flip(i == 9)) { 94 | GlobalVariable = i; 95 | } 96 | 97 | return 0; 98 | } 99 | ``` 100 | 101 | Notice that types are not just replaced by textual substitution. 102 | For example, `GlobalVariable` is treated differently from the local 103 | variable `i`. And unlike `foo`, the return type of `main` is unchanged. 104 | Note that branches in control flow statements are instrumented. In fact, 105 | these illustrate that the CRV library overloads many operators to simplify 106 | the task of writing the front-end. 107 | 108 | ## Clang's AST Matchers 109 | 110 | CRV requires source-to-source transformations of C++11 code. But writing a 111 | C++ parser is notoriously hard. So as opposed to creating our own, we use 112 | Clang -- Apple's compiler of choice. 113 | 114 | The CRV front-end is built on [LibASTMatchers][clang-ast-matchers] which 115 | provide a domain-specific language to Clang's abstract syntax tree (AST). 116 | Clang's AST is very rich and many corner cases are not considered by the 117 | CRV front-end. This is purely for practical reasons and is not due to any 118 | intrinsic limitations of Clang. Any contributions to fix issues are warmly 119 | invited and can be sent as Github pull request. 120 | 121 | In adding support for new features, there are three main sources for help: 122 | 123 | Firstly, the AST of a program can be inspected with clang itself. For example, 124 | the following command: 125 | 126 | $ clang -Xclang -ast-dump -fsyntax-only example.cpp 127 | 128 | produces the AST dump below: 129 | 130 | ``` 131 | TranslationUnitDecl 0x102021cd0 <> 132 | |-TypedefDecl 0x102022210 <> __int128_t '__int128' 133 | |-TypedefDecl 0x102022270 <> __uint128_t 'unsigned __int128' 134 | |-TypedefDecl 0x102022630 <> __builtin_va_list '__va_list_tag [1]' 135 | |-VarDecl 0x102022690 GlobalVariable 'short' 136 | |-FunctionDecl 0x1020227c0 foo 'short (short)' 137 | | |-ParmVarDecl 0x102022700 a 'short' 138 | | `-CompoundStmt 0x1020228c8 139 | | `-ReturnStmt 0x1020228a8 140 | | `-ImplicitCastExpr 0x102022890 'short' 141 | | `-DeclRefExpr 0x102022868 'short' lvalue ParmVar 0x102022700 'a' 'short' 142 | `-FunctionDecl 0x102022940 main 'int (void)' 143 | `-CompoundStmt 0x102053a28 144 | |-DeclStmt 0x102053710 145 | | `-VarDecl 0x102053680 i 'short' 146 | | `-ImplicitCastExpr 0x1020536f8 'short' 147 | | `-IntegerLiteral 0x1020536d8 'int' 0 148 | |-ForStmt 0x102053828 149 | | |-<<>> 150 | | |-<<>> 151 | | |-BinaryOperator 0x1020537a0 '_Bool' '<' 152 | | | |-ImplicitCastExpr 0x102053788 'int' 153 | | | | `-ImplicitCastExpr 0x102053770 'short' 154 | | | | `-DeclRefExpr 0x102053728 'short' lvalue Var 0x102053680 'i' 'short' 155 | | | `-IntegerLiteral 0x102053750 'int' 8 156 | | |-UnaryOperator 0x1020537f0 'short' postfix '++' 157 | | | `-DeclRefExpr 0x1020537c8 'short' lvalue Var 0x102053680 'i' 'short' 158 | | `-CompoundStmt 0x102053810 159 | |-IfStmt 0x1020539b8 160 | | |-<<>> 161 | | |-BinaryOperator 0x1020538e0 '_Bool' '==' 162 | | | |-ImplicitCastExpr 0x1020538c8 'int' 163 | | | | `-ImplicitCastExpr 0x1020538b0 'short' 164 | | | | `-DeclRefExpr 0x102053868 'short' lvalue Var 0x102053680 'i' 'short' 165 | | | `-IntegerLiteral 0x102053890 'int' 9 166 | | |-CompoundStmt 0x102053998 167 | | | `-BinaryOperator 0x102053970 'short' lvalue '=' 168 | | | |-DeclRefExpr 0x102053908 'short' lvalue Var 0x102022690 'GlobalVariable' 'short' 169 | | | `-ImplicitCastExpr 0x102053958 'short' 170 | | | `-DeclRefExpr 0x102053930 'short' lvalue Var 0x102053680 'i' 'short' 171 | | `-<<>> 172 | `-ReturnStmt 0x102053a08 173 | `-IntegerLiteral 0x1020539e8 'int' 0 174 | ``` 175 | 176 | Secondly, there is `clang-query` that is an interactive tool for 177 | writing and testing AST matchers. The following illustrates this: 178 | 179 | $ clang-query example.cpp -- 180 | $ clang-query> match functionDecl(returns(qualType(isInteger()))) 181 | 182 | Match #1: 183 | 184 | example.cpp:3:1: note: "root" binds here 185 | short foo(short a) { 186 | ^~~~~~~~~~~~~~~~~~~~ 187 | 188 | Match #2: 189 | 190 | example.cpp:7:1: note: "root" binds here 191 | int main() { 192 | ^~~~~~~~~~~~ 193 | 2 matches. 194 | 195 | Thirdly, there is a lot of [documentation][clang-doc] including 196 | the [AST Matcher Reference][clang-ast-matchers-ref]. When in doubt 197 | how to use the various kinds of matchers, the source code of 198 | [clang-modernize][clang-modernize] is good starting point. 199 | 200 | [clang-ast-matchers]: http://clang.llvm.org/docs/LibASTMatchers.html 201 | [clang-ast-matchers-ref]: http://clang.llvm.org/docs/LibASTMatchersReference.html 202 | [clang-doc]: http://clang.llvm.org/docs/ 203 | [clang-modernize]: http://clang.llvm.org/extra/clang-modernize.html 204 | -------------------------------------------------------------------------------- /tool/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | set(LLVM_LINK_COMPONENTS support) 2 | 3 | include_directories(${CMAKE_CURRENT_SOURCE_DIR}/..) 4 | 5 | add_clang_executable(clang-nse 6 | ClangNse.cpp 7 | ) 8 | 9 | add_dependencies(clang-nse clang-modernize 10 | clang-headers clang-apply-replacements 11 | ) 12 | 13 | target_link_libraries(clang-nse 14 | clang-modernize 15 | clangAST 16 | clangASTMatchers 17 | clangBasic 18 | clangFormat 19 | clangFrontend 20 | clangLex 21 | clangTooling 22 | nse 23 | ) 24 | 25 | install(TARGETS clang-nse 26 | RUNTIME DESTINATION bin) 27 | -------------------------------------------------------------------------------- /tool/ClangNse.cpp: -------------------------------------------------------------------------------- 1 | //===-- ClangNse.cpp - Main file for the Clang CRV instrumentation tool ---===// 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 | /// \file 11 | /// \brief Instruments C++11 code to symbolically execute it with CRV 12 | /// 13 | /// See user documentation for usage instructions. 14 | /// 15 | //===----------------------------------------------------------------------===// 16 | 17 | #include "clang/Frontend/FrontendActions.h" 18 | #include "clang/Tooling/CommonOptionsParser.h" 19 | #include "clang/Tooling/Tooling.h" 20 | #include "llvm/Support/Signals.h" 21 | 22 | #include "NseTransform.h" 23 | 24 | namespace cl = llvm::cl; 25 | 26 | static cl::OptionCategory NseOptionCategory("Native symbolic execution options"); 27 | 28 | static cl::opt NamespaceOpt( 29 | "namespace", 30 | cl::init("crv"), 31 | cl::desc("C++ namespace of the NSE library (default=crv)."), 32 | cl::cat(NseOptionCategory)); 33 | 34 | static cl::opt BranchOpt( 35 | "branch", 36 | cl::init("branch"), 37 | cl::desc("Name of the function that is called on control-flow statements (default=branch)."), 38 | cl::cat(NseOptionCategory)); 39 | 40 | static cl::opt StrategyOpt( 41 | "strategy", 42 | cl::init("sequential_dfs_checker"), 43 | cl::desc("Extern function that determines the symbolic execution path search strategy (default=sequential_dfs_checker)."), 44 | cl::cat(NseOptionCategory)); 45 | 46 | int main(int argc, const char **argv) { 47 | llvm::sys::PrintStackTraceOnErrorSignal(); 48 | 49 | tooling::CommonOptionsParser OptionsParser(argc, argv, NseOptionCategory); 50 | tooling::RefactoringTool Tool(OptionsParser.getCompilations(), 51 | OptionsParser.getSourcePathList()); 52 | 53 | // fully qualified function name without parenthesis 54 | const std::string NseStrategy = NamespaceOpt + "::" + StrategyOpt + "()"; 55 | const std::string NseBranchStrategy = NseStrategy + "." + BranchOpt; 56 | 57 | IncludesManager IM; 58 | tooling::Replacements *Replace = &Tool.getReplacements(); 59 | IfConditionReplacer IfStmts(NseBranchStrategy, Replace); 60 | IfConditionVariableReplacer IfConditionVariableStmts; 61 | ForConditionReplacer ForStmts(NseBranchStrategy, Replace); 62 | WhileConditionReplacer WhileStmts(NseBranchStrategy, Replace); 63 | LocalVarReplacer LocalVarDecls(Replace); 64 | GlobalVarReplacer GlobalVarDecls(Replace); 65 | FieldReplacer FieldDecls(Replace); 66 | MainFunctionReplacer MainFunction(NamespaceOpt, StrategyOpt, 67 | Replace, &GlobalVarDecls.GlobalVars, &IM); 68 | 69 | ParmVarReplacer ParmVarDecls(Replace); 70 | ReturnTypeReplacer ReturnTypes(Replace); 71 | AssumeReplacer Assumptions(NseStrategy, Replace); 72 | AssertReplacer Assertions(NseStrategy, Replace); 73 | SymbolicReplacer Symbolics(NamespaceOpt, Replace); 74 | MakeSymbolicReplacer MakeSymbolics(NamespaceOpt, Replace); 75 | CStyleCastReplacer CStyleCasts(NamespaceOpt, Replace); 76 | 77 | MatchFinder Finder; 78 | Finder.addMatcher(makeIfConditionMatcher(), &IfStmts); 79 | Finder.addMatcher(makeIfConditionVariableMatcher(), &IfConditionVariableStmts); 80 | Finder.addMatcher(makeForConditionMatcher(), &ForStmts); 81 | Finder.addMatcher(makeWhileConditionMatcher(), &WhileStmts); 82 | Finder.addMatcher(makeLocalVarMatcher(), &LocalVarDecls); 83 | Finder.addMatcher(makeGlobalVarMatcher(), &GlobalVarDecls); 84 | Finder.addMatcher(makeFieldMatcher(), &FieldDecls); 85 | 86 | // requires GlobalVarDecls to run first, so order matters 87 | Finder.addMatcher(makeMainFunctionMatcher(), &MainFunction); 88 | Finder.addMatcher(makeParmVarDeclMatcher(), &ParmVarDecls); 89 | Finder.addMatcher(makeReturnTypeMatcher(), &ReturnTypes); 90 | Finder.addMatcher(makeAssumeMatcher(), &Assumptions); 91 | Finder.addMatcher(makeAssertMatcher(), &Assertions); 92 | Finder.addMatcher(makeSymbolicMatcher(), &Symbolics); 93 | Finder.addMatcher(makeMakeSymbolicMatcher(), &MakeSymbolics); 94 | Finder.addMatcher(makeCStyleCastMatcher(), &CStyleCasts); 95 | 96 | return Tool.runAndSave(tooling::newFrontendActionFactory(&Finder, &IM).get()); 97 | } 98 | -------------------------------------------------------------------------------- /tool/Makefile: -------------------------------------------------------------------------------- 1 | ##===- tools/extra/native-symbolic-execution-clang/tool/Makefile ----------===## 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 | CLANG_LEVEL := ../../../.. 11 | include $(CLANG_LEVEL)/../../Makefile.config 12 | 13 | TOOLNAME = clang-nse 14 | 15 | # No plugins, optimize startup time. 16 | TOOL_NO_EXPORTS = 1 17 | 18 | SOURCES = ClangNse.cpp 19 | 20 | LINK_COMPONENTS := $(TARGETS_TO_BUILD) asmparser bitreader support mc mcparser option 21 | USEDLIBS = nse.a modernizeCore.a clangFormat.a clangTooling.a clangFrontend.a \ 22 | clangSerialization.a clangDriver.a clangRewriteFrontend.a \ 23 | clangRewriteCore.a clangParse.a clangSema.a clangAnalysis.a \ 24 | clangAST.a clangASTMatchers.a clangEdit.a clangLex.a clangBasic.a 25 | 26 | include $(CLANG_LEVEL)/Makefile 27 | 28 | CPP.Flags += -I$(PROJ_SRC_DIR)/.. -I$(PROJ_SRC_DIR)/../../clang-modernize/Core 29 | 30 | # BUILT_SOURCES gets used as a prereq for many top-level targets. However, at 31 | # the point those targets are defined, $(ObjDir) hasn't been defined and so the 32 | # directory to create becomes // which is not what we want. So instead, 33 | # this .objdir recipe is defined at at point where $(ObjDir) is defined and 34 | # it's specialized to $(ObjDir) to ensure it only works on targets we want it 35 | # to. 36 | $(ObjDir)/%.objdir: 37 | $(Verb) $(MKDIR) $(ObjDir)/$* > /dev/null 38 | $(Verb) $(DOTDIR_TIMESTAMP_COMMAND) > $@ 39 | 40 | -------------------------------------------------------------------------------- /tool/nse-rewrite.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | FILENAME=$1 4 | TMP=.${FILENAME}.tmp 5 | CLANG_NSE=clang-nse 6 | CLANG_CPP=/usr/bin/clang++ 7 | 8 | ${CLANG_CPP} -cc1 -rewrite-macros ${FILENAME} > ${TMP} && mv ${TMP} ${FILENAME} 9 | ${CLANG_NSE} ${FILENAME} -- 10 | echo -e "#include \n#include " > ${TMP} && cat ${FILENAME} >> ${TMP} && mv ${TMP} ${FILENAME} 11 | --------------------------------------------------------------------------------