def replay_error_trace(verifier_output, args): if args.verifier != 'corral': print "Replay for verifiers other than 'corral' currently unsupported; skipping replay" return print "Attempting to replay error trace." missing_definitions = detect_missing_definitions(args.bc_file) if '__SMACK_code' in missing_definitions: print "warning: inline Boogie code found; replay may fail" arguments, return_values = extract_values(verifier_output) with open(args.replay_harness, 'w') as f: f.write(harness(arguments, return_values, missing_definitions)) print "Generated replay harness:", args.replay_harness stubs_bc = temporary_file('stubs', '.bc', args) try_command(['clang', '-c', '-emit-llvm', '-o', stubs_bc, args.replay_harness]) try_command(['clang', '-Wl,-e,_smack_replay_main', '-o', args.replay_exe_file, args.bc_file, stubs_bc]) print "Generated replay executable:", args.replay_exe_file try: if 'error reached!' in try_command(["./" + args.replay_exe_file]): print "Error-trace replay successful." return True else: print "Error-trace replay failed." except Exception as err: print "Error-trace replay caught", err.message return False
def llvm_to_bpl(args): """Translate the LLVM bitcode file to a Boogie source file.""" cmd = ['llvm2bpl', args.linked_bc_file, '-bpl', args.bpl_file] cmd += ['-warnings'] cmd += ['-source-loc-syms'] for ep in args.entry_points: cmd += ['-entry-points', ep] if args.debug: cmd += ['-debug'] if args.debug_only: cmd += ['-debug-only', args.debug_only] if args.ll_file: cmd += ['-ll', args.ll_file] if "impls" in args.mem_mod:cmd += ['-mem-mod-impls'] if args.static_unroll: cmd += ['-static-unroll'] if args.bit_precise: cmd += ['-bit-precise'] if args.timing_annotations: cmd += ['-timing-annotations'] if args.bit_precise_pointers: cmd += ['-bit-precise-pointers'] if args.no_byte_access_inference: cmd += ['-no-byte-access-inference'] if args.no_memory_splitting: cmd += ['-no-memory-splitting'] if args.memory_safety: cmd += ['-memory-safety'] if args.integer_overflow: cmd += ['-integer-overflow'] if args.float: cmd += ['-float'] if args.modular: cmd += ['-modular'] try_command(cmd, console=True) append_prelude(args) annotate_bpl(args) property_selection(args) transform_bpl(args)
def llvm_to_bpl(args): """Translate the LLVM bitcode file to a Boogie source file.""" cmd = ['llvm2bpl', args.linked_bc_file, '-bpl', args.bpl_file] cmd += ['-warn-type', args.warn] if sys.stdout.isatty(): cmd += ['-colored-warnings'] cmd += ['-source-loc-syms'] for ep in args.entry_points: cmd += ['-entry-points', ep] if args.debug: cmd += ['-debug'] if args.debug_only: cmd += ['-debug-only', args.debug_only] if args.ll_file: cmd += ['-ll', args.ll_file] if "impls" in args.mem_mod: cmd += ['-mem-mod-impls'] if args.static_unroll: cmd += ['-static-unroll'] if args.bit_precise: cmd += ['-bit-precise'] if args.timing_annotations: cmd += ['-timing-annotations'] if args.bit_precise_pointers: cmd += ['-bit-precise-pointers'] if args.no_byte_access_inference: cmd += ['-no-byte-access-inference'] if args.no_memory_splitting: cmd += ['-no-memory-splitting'] if args.memory_safety: cmd += ['-memory-safety'] if args.integer_overflow: cmd += ['-integer-overflow'] if args.float: cmd += ['-float'] if args.modular: cmd += ['-modular'] try_command(cmd, console=True) annotate_bpl(args) property_selection(args) transform_bpl(args)
def llvm_frontend(args): """Generate Boogie code from LLVM bitcodes.""" try_command(['llvm-link', '-o', args.bc_file] + args.input_files) try_command(['llvm-link', '-o', args.linked_bc_file, args.bc_file] + build_libs(args)) llvm_to_bpl(args)
def detect_missing_definitions(bc_file): missing = [] try: try_command(['clang', bc_file]) except Exception as err: for line in err.message.split("\n"): m = re.search(r'\"_(.*)\", referenced from:', line) or re.search(r'undefined reference to `(.*)\'', line) if m: missing.append(m.group(1)) return missing
def detect_missing_definitions(bc_file): missing = [] try: try_command(['clang', bc_file]) except Exception as err: for line in err.message.split("\n"): m = re.search(r'\"_(.*)\", referenced from:', line) or re.search( r'undefined reference to `(.*)\'', line) if m: missing.append(m.group(1)) return missing
def link_bc_files(bitcodes, libs, args): """Link generated LLVM bitcode and relevant smack libraries.""" smack_libs = default_build_libs(args) for build_lib in libs: smack_libs += build_lib(args) try_command(['llvm-link', '-o', args.bc_file] + bitcodes) try_command(['llvm-link', '-o', args.linked_bc_file, args.bc_file] + smack_libs) # import here to avoid a circular import from top import llvm_to_bpl llvm_to_bpl(args)
def fortran_compile_to_bc(input_file, compile_command, args): """Compile a FORTRAN source file to LLVM IR.""" # This method only exists as a hack to get flang to work # with SMACK. When we update to the latest flang on LLVM 5, # this method will no longer be necessary. The hack is # self-contained in this method. # The Debug Info Version in flang is incompatible with # the version that clang uses. The workaround is to use # sed to change the file so llvm-link gives a warning # and not an error. # compile to human-readable format in order to tweak the IR compile_command[1] = '-S' ll = temporary_file( os.path.splitext(os.path.basename(input_file))[0], '.ll', args) try_command(compile_command + ['-o', ll, input_file], console=True) # change the throw level of 'Debug Info Version' from error to warning in the IR try_command([ 'sed', '-i', 's/i32 1, !\"Debug Info Version\"/i32 2, !\"Debug Info Version\"/g', ll ]) try_command(['llvm-as', ll]) try_command(['rm', ll]) bc = '.'.join(ll.split('.')[:-1] + ['bc']) return bc
def build_libs(args): """Generate LLVM bitcodes for SMACK libraries.""" bitcodes = [] libs = ['smack.c'] if args.pthread: libs += ['pthread.c'] for c in map(lambda c: os.path.join(smack_lib(), c), libs): bc = temporary_file(os.path.splitext(os.path.basename(c))[0], '.bc', args) try_command(default_clang_compile_command(args, True) + ['-o', bc, c]) bitcodes.append(bc) return bitcodes
def target_selection(args): """Determine the target architecture based on flags and source files.""" # TODO more possible clang flags that determine the target? if not re.search('-target', args.clang_options): src = args.input_files[0] if os.path.splitext(src)[1] == '.bc': ll = temporary_file(os.path.splitext(os.path.basename(src))[0], '.ll', args) try_command(['llvm-dis', '-o', ll, src]) src = ll if os.path.splitext(src)[1] == '.ll': with open(src, 'r') as f: for line in f: triple = re.findall('^target triple = "(.*)"', line) if len(triple) > 0: args.clang_options += (" -target %s" % triple[0]) break
def build_libs(args): """Generate LLVM bitcodes for SMACK libraries.""" bitcodes = [] libs = ['smack.c'] if args.pthread: libs += ['pthread.c'] if args.memory_safety or args.signed_integer_overflow: libs += ['string.c'] if args.float: libs += ['math.c'] for c in map(lambda c: os.path.join(smack_lib(), c), libs): bc = temporary_file(os.path.splitext(os.path.basename(c))[0], '.bc', args) try_command(default_clang_compile_command(args, True) + ['-o', bc, c]) bitcodes.append(bc) return bitcodes
def clang_objc_frontend(input_file, args): """Generate LLVM IR from Objective-C language source(s).""" compile_command = default_clang_compile_command(args) if sys.platform in ['linux', 'linux2']: objc_flags = try_command(['gnustep-config', '--objc-flags']) compile_command += objc_flags.split() elif sys.platform == 'darwin': sys.exit("Objective-C not yet supported on macOS") else: sys.exit("Objective-C not supported for this operating system.") return compile_to_bc(input_file,compile_command,args)
def clang_objc_frontend(input_file, args): """Generate LLVM IR from Objective-C language source(s).""" compile_command = default_clang_compile_command(args) if sys.platform in ['linux', 'linux2']: objc_flags = try_command(['gnustep-config', '--objc-flags']) compile_command += objc_flags.split() elif sys.platform == 'darwin': sys.exit("Objective-C not yet supported on macOS") else: sys.exit("Objective-C not supported for this operating system.") return compile_to_bc(input_file, compile_command, args)
def json_compilation_database_frontend(input_file, args): """Generate Boogie code from a JSON compilation database.""" if len(args.input_files) > 1: raise RuntimeError("Expected a single JSON compilation database.") output_flags = re.compile(r"-o ([^ ]*)[.]o\b") optimization_flags = re.compile(r"-O[1-9]\b") with open(input_file) as f: for cc in json.load(f): if 'objects' in cc: # TODO what to do when there are multiple linkings? bit_codes = map(lambda f: re.sub('[.]o$', '.bc', f), cc['objects']) try_command(['llvm-link', '-o', args.bc_file] + bit_codes) try_command( ['llvm-link', '-o', args.linked_bc_file, args.bc_file] + build_libs(args)) else: out_file = output_flags.findall(cc['command'])[0] + '.bc' command = cc['command'] command = output_flags.sub(r"-o \1.bc", command) command = optimization_flags.sub("-O0", command) command = command + " -emit-llvm" try_command(command.split(), cc['directory'], console=True) llvm_to_bpl(args)
def json_compilation_database_frontend(input_file, args): """Generate Boogie code from a JSON compilation database.""" if len(args.input_files) > 1: raise RuntimeError("Expected a single JSON compilation database.") output_flags = re.compile(r"-o ([^ ]*)[.]o\b") optimization_flags = re.compile(r"-O[1-9]\b") with open(input_file) as f: for cc in json.load(f): if 'objects' in cc: # TODO what to do when there are multiple linkings? bit_codes = map(lambda f: re.sub('[.]o$','.bc',f), cc['objects']) try_command(['llvm-link', '-o', args.bc_file] + bit_codes) try_command(['llvm-link', '-o', args.linked_bc_file, args.bc_file] + build_libs(args)) else: out_file = output_flags.findall(cc['command'])[0] + '.bc' command = cc['command'] command = output_flags.sub(r"-o \1.bc", command) command = optimization_flags.sub("-O0", command) command = command + " -emit-llvm" try_command(command.split(),cc['directory'], console=True) llvm_to_bpl(args)
def replay_error_trace(verifier_output, args): if args.verifier != 'corral': print "Replay for verifiers other than 'corral' currently unsupported; skipping replay" return print "Attempting to replay error trace." missing_definitions = detect_missing_definitions(args.bc_file) if '__SMACK_code' in missing_definitions: print "warning: inline Boogie code found; replay may fail" arguments, return_values = extract_values(verifier_output) with open(args.replay_harness, 'w') as f: f.write(harness(arguments, return_values, missing_definitions)) print "Generated replay harness:", args.replay_harness stubs_bc = temporary_file('stubs', '.bc', args) try_command( ['clang', '-c', '-emit-llvm', '-o', stubs_bc, args.replay_harness]) try_command([ 'clang', '-Wl,-e,_smack_replay_main', '-o', args.replay_exe_file, args.bc_file, stubs_bc ]) print "Generated replay executable:", args.replay_exe_file try: if 'error reached!' in try_command(["./" + args.replay_exe_file]): print "Error-trace replay successful." return True else: print "Error-trace replay failed." except Exception as err: print "Error-trace replay caught", err.message return False
def main(*args, **kwargs): """ main function for command line pypeople tool. """ calledVia = None command = 'help' cmdOpts = [] if len(sys.argv) > 0: calledVia = sys.argv[0] if len(sys.argv) > 1: command = sys.argv[1] if len(sys.argv) > 2: cmdOpts = sys.argv[2:] returnString = utils.try_command(command, cmdOpts) print(returnString)
def clang_frontend(args): """Generate Boogie code from C-language source(s).""" bitcodes = [] compile_command = default_clang_compile_command(args) for c in args.input_files: bc = temporary_file(os.path.splitext(os.path.basename(c))[0], '.bc', args) try_command(compile_command + ['-o', bc, c], console=True) bitcodes.append(bc) try_command(['llvm-link', '-o', args.bc_file] + bitcodes) try_command(['llvm-link', '-o', args.linked_bc_file, args.bc_file] + build_libs(args)) llvm_to_bpl(args)
def fortran_compile_to_bc(input_file, compile_command, args): """Compile a FORTRAN source file to LLVM IR.""" # This method only exists as a hack to get flang to work # with SMACK. When we update to the latest flang on LLVM 5, # this method will no longer be necessary. The hack is # self-contained in this method. # The Debug Info Version in flang is incompatible with # the version that clang uses. The workaround is to use # sed to change the file so llvm-link gives a warning # and not an error. # compile to human-readable format in order to tweak the IR compile_command[1] = '-S' ll = temporary_file(os.path.splitext(os.path.basename(input_file))[0], '.ll', args) try_command(compile_command + ['-o', ll, input_file], console=True) # change the throw level of 'Debug Info Version' from error to warning in the IR try_command(['sed', '-i', 's/i32 1, !\"Debug Info Version\"/i32 2, !\"Debug Info Version\"/g', ll]) try_command(['llvm-as', ll]) try_command(['rm', ll]) bc = '.'.join(ll.split('.')[:-1] + ['bc']) return bc
def verify_bpl(args): """Verify the Boogie source file with a back-end verifier.""" if args.verifier == 'svcomp': verify_bpl_svcomp(args) return elif args.verifier == 'boogie' or args.modular: command = ["boogie"] command += [args.bpl_file] command += ["/nologo", "/noinfer", "/doModSetAnalysis"] command += ["/timeLimit:%s" % args.time_limit] command += ["/errorLimit:%s" % args.max_violations] if not args.modular: command += ["/loopUnroll:%d" % args.unroll] elif args.verifier == 'corral': command = ["corral"] command += [args.bpl_file] command += ["/tryCTrace", "/noTraceOnDisk", "/printDataValues:1"] command += ["/k:%d" % args.context_bound] command += ["/useProverEvaluate"] command += ["/timeLimit:%s" % args.time_limit] command += ["/cex:%s" % args.max_violations] command += ["/maxStaticLoopBound:%d" % args.loop_limit] command += ["/recursionBound:%d" % args.unroll] elif args.verifier == 'symbooglix': command = ['symbooglix'] command += [args.bpl_file] command += ["--file-logging=0"] command += ["--entry-points=%s" % ",".join(args.entry_points)] command += ["--timeout=%d" % args.time_limit] command += ["--max-loop-depth=%d" % args.unroll] if (args.bit_precise or args.float) and args.verifier != 'symbooglix': x = "bopt:" if args.verifier != 'boogie' else "" command += ["/%sproverOpt:OPTIMIZE_FOR_BV=true" % x] command += ["/%sboolControlVC" % x] if args.verifier_options: command += args.verifier_options.split() verifier_output = try_command(command, timeout=args.time_limit) verifier_output = transform_out(args, verifier_output) result = verification_result(verifier_output) if args.smackd: print smackdOutput(verifier_output) elif result == 'verified': print results(args)[result] else: if result == 'error' or result == 'invalid-deref' or result == 'invalid-free' or result == 'invalid-memtrack' or result == 'overflow': error = error_trace(verifier_output, args) if args.error_file: with open(args.error_file, 'w') as f: f.write(error) if not args.quiet: print error if args.replay: replay_error_trace(verifier_output, args) sys.exit(results(args)[result])
def verify_bpl(args): """Verify the Boogie source file with a back-end verifier.""" if args.verifier == 'svcomp': verify_bpl_svcomp(args) return elif args.verifier == 'boogie' or args.modular: command = ["boogie"] command += [args.bpl_file] command += ["/nologo", "/noinfer", "/doModSetAnalysis"] command += ["/timeLimit:%s" % args.time_limit] command += ["/errorLimit:%s" % args.max_violations] if not args.modular: command += ["/loopUnroll:%d" % args.unroll] elif args.verifier == 'corral': command = ["corral"] command += [args.bpl_file] command += ["/tryCTrace", "/noTraceOnDisk", "/printDataValues:1"] command += ["/k:%d" % args.context_bound] command += ["/useProverEvaluate"] command += ["/timeLimit:%s" % args.time_limit] command += ["/cex:%s" % args.max_violations] command += ["/maxStaticLoopBound:%d" % args.loop_limit] command += ["/recursionBound:%d" % args.unroll] else: # Duality! command = ["corral", args.bpl_file] command += ["/tryCTrace", "/noTraceOnDisk", "/useDuality", "/oldStratifiedInlining"] command += ["/recursionBound:1073741824", "/k:1"] if args.bit_precise: x = "bopt:" if args.verifier != 'boogie' else "" command += ["/%sproverOpt:OPTIMIZE_FOR_BV=true" % x] command += ["/%sz3opt:smt.relevancy=0" % x] command += ["/%sz3opt:smt.bv.enable_int2bv=true" % x] command += ["/%sboolControlVC" % x] if args.verifier_options: command += args.verifier_options.split() verifier_output = try_command(command, timeout=args.time_limit) result = verification_result(verifier_output) if args.smackd: print smackdOutput(verifier_output) elif result == 'verified': print results(args)[result] else: if result == 'error' or result == 'invalid-deref' or result == 'invalid-free' or result == 'invalid-memtrack' or result == 'overflow': error = error_trace(verifier_output, args) if args.error_file: with open(args.error_file, 'w') as f: f.write(error) if not args.quiet: print error if args.replay: replay_error_trace(verifier_output, args) sys.exit(results(args)[result])
def d_compile_to_bc(input_file, compile_command, args): """Compile a D source file to LLVM IR.""" bc = temporary_file( os.path.splitext(os.path.basename(input_file))[0], '.bc', args) try_command(compile_command + ['-of=' + bc, input_file], console=True) return bc
def d_compile_to_bc(input_file, compile_command, args): """Compile a D source file to LLVM IR.""" bc = temporary_file(os.path.splitext(os.path.basename(input_file))[0], '.bc', args) try_command(compile_command + ['-of=' + bc, input_file], console=True) return bc
def verify_bpl(args): """Verify the Boogie source file with a back-end verifier.""" if args.verifier == 'svcomp': verify_bpl_svcomp(args) return elif args.verifier == 'boogie' or args.modular: command = ["boogie"] command += [args.bpl_file] command += ["/nologo", "/noinfer", "/doModSetAnalysis"] command += ["/timeLimit:%s" % args.time_limit] command += ["/errorLimit:%s" % args.max_violations] if not args.modular: command += ["/loopUnroll:%d" % args.unroll] elif args.verifier == 'corral': command = ["corral"] command += [args.bpl_file] command += ["/tryCTrace", "/noTraceOnDisk", "/printDataValues:1"] command += ["/k:%d" % args.context_bound] command += ["/useProverEvaluate"] command += ["/timeLimit:%s" % args.time_limit] command += ["/cex:%s" % args.max_violations] command += ["/maxStaticLoopBound:%d" % args.loop_limit] command += ["/recursionBound:%d" % args.unroll] elif args.verifier == 'symbooglix': command = ['symbooglix'] command += [args.bpl_file] command += ["--file-logging=0"] command += ["--entry-points=%s" % ",".join(args.entry_points)] command += ["--timeout=%d" % args.time_limit] command += ["--max-loop-depth=%d" % args.unroll] else: # Duality! command = ["corral", args.bpl_file] command += ["/tryCTrace", "/noTraceOnDisk", "/useDuality", "/oldStratifiedInlining"] command += ["/recursionBound:1073741824", "/k:1"] if (args.bit_precise or args.float) and args.verifier != 'symbooglix': x = "bopt:" if args.verifier != 'boogie' else "" command += ["/%sproverOpt:OPTIMIZE_FOR_BV=true" % x] command += ["/%sboolControlVC" % x] if args.verifier_options: command += args.verifier_options.split() verifier_output = try_command(command, timeout=args.time_limit) verifier_output = transform_out(args, verifier_output) result = verification_result(verifier_output) if args.smackd: print smackdOutput(verifier_output) elif result == 'verified': print results(args)[result] else: if result == 'error' or result == 'invalid-deref' or result == 'invalid-free' or result == 'invalid-memtrack' or result == 'overflow': error = error_trace(verifier_output, args) if args.error_file: with open(args.error_file, 'w') as f: f.write(error) if not args.quiet: print error if args.replay: replay_error_trace(verifier_output, args) sys.exit(results(args)[result])