Skip to content

Commit d1e3dd5

Browse files
celinvaltedinski
authored andcommitted
Add support to --function to rmc script (#169) (#523)
This changes the compilation to use crate type lib instead of binary when we are running rmc on a single .rs file. This allow us to use any public function as a verification target. We have also changed the tests to use pub main so it is exported and it can be used as the entry point of the proof. Fix cargo-rmc and the current testcase to support library build.
1 parent 178bcf7 commit d1e3dd5

File tree

255 files changed

+311
-292
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

255 files changed

+311
-292
lines changed

rmc-docs/src/install-guide.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,7 @@ Create a test file:
8282

8383
```rust
8484
// File: test.rs
85-
fn main() {
85+
pub fn main() {
8686
assert!(1 == 2);
8787
}
8888
```

scripts/cargo-rmc

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -40,15 +40,20 @@ def main():
4040

4141
if EXIT_CODE_SUCCESS != rmc.goto_to_c(cbmc_runnable_filename, c_runnable_filename, args.verbose, args.dry_run):
4242
return 1
43-
43+
4444
rmc.gen_c_postprocess(c_runnable_filename, args.dry_run)
4545

4646
rmc.cargo_build(args.crate, args.target_dir,
4747
args.verbose, args.debug, args.mangler, args.dry_run, [])
4848

4949
pattern = os.path.join(args.target_dir, "debug", "deps", "*.json")
5050
jsons = glob.glob(pattern)
51-
rmc.ensure(len(jsons) == 1, f"Unexpected number of json outputs: {len(jsons)}")
51+
52+
if not args.dry_run:
53+
rmc.ensure(len(jsons) == 1, f"Unexpected number of json outputs: {len(jsons)}")
54+
else:
55+
# Add a dummy value so dry-run works.
56+
jsons = ["dry-run.json"]
5257

5358
cbmc_filename = os.path.join(args.target_dir, "cbmc.out")
5459
c_filename = os.path.join(args.target_dir, "cbmc.c")
@@ -101,15 +106,15 @@ def parse_args():
101106

102107
exclude_flags = []
103108
rmc_flags.add_flags(parser, {"default-target": "target"}, exclude_flags=exclude_flags)
104-
109+
105110
return parser
106111

107112
# Determine what crate to analyze from flags
108113
def get_crate_from_args(args):
109114
validate(args)
110115
return args.crate or args.crate_flag or "."
111116

112-
# Combine the command line parameter to get a config file;
117+
# Combine the command line parameter to get a config file;
113118
# return None if no_config_toml is set
114119
def get_config_toml(no_config_toml, config_toml, args):
115120
crate = get_crate_from_args(args)
@@ -154,7 +159,7 @@ def parse_args():
154159

155160
# Check for conflicting flags
156161
def validate(args):
157-
rmc.ensure(not (args.crate and args.crate_flag), "Please provide a single crate to verify.")
162+
rmc.ensure(not (args.crate and args.crate_flag), "Please provide a single crate to verify.")
158163
rmc.ensure(not (args.no_config_toml and args.config_toml), "Incompatible flags: --config-toml, --no-config-toml")
159164

160165
# Fix up args before returning
@@ -239,7 +244,7 @@ def extract_flags_from_toml(path):
239244
success = False
240245
for flag in flags:
241246
add_flag(flag, flags[flag])
242-
247+
243248
rmc.ensure(success)
244249
return flag_list
245250

scripts/rmc

Lines changed: 4 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ def main():
3838

3939
if EXIT_CODE_SUCCESS != rmc.goto_to_c(goto_runnable_filename, c_runnable_filename, args.verbose, args.dry_run):
4040
return 1
41-
41+
4242
rmc.gen_c_postprocess(c_runnable_filename, args.dry_run)
4343

4444
json_filename = base + ".json"
@@ -76,7 +76,7 @@ def main():
7676

7777
if "--function" not in args.cbmc_args:
7878
args.cbmc_args.extend(["--function", args.function])
79-
79+
8080
if args.visualize:
8181
# Use a separate set of flags for coverage checking (empty for now)
8282
cover_args = []
@@ -100,14 +100,8 @@ def parse_args():
100100
input_group.add_argument("input", help="Rust file to verify", nargs="?")
101101
input_group.add_argument("--input", help="Rust file to verify", dest="input_flag", metavar="INPUT")
102102

103-
exclude_flags = [
104-
# In the future we hope to make this configurable in the command line.
105-
# For now, however, changing this from "main" breaks rmc.
106-
# Issue: https://github.com/model-checking/rmc/issues/169
107-
"--function"
108-
]
109-
rmc_flags.add_flags(parser, {"default-target": "."}, exclude_flags=exclude_flags)
110-
103+
rmc_flags.add_flags(parser, {"default-target": "."})
104+
111105
return parser
112106

113107
# Check for conflicting flags
@@ -124,11 +118,6 @@ def parse_args():
124118
if args.quiet:
125119
args.verbose = False
126120

127-
# In the future we hope to make this configurable in the command line.
128-
# For now, however, changing this from "main" breaks rmc.
129-
# Issue: https://github.com/model-checking/rmc/issues/169
130-
args.function = "main"
131-
132121
# Add some CBMC flags by default unless `--no-default-checks` is being used
133122
if args.default_checks:
134123
rmc.add_selected_default_cbmc_flags(args)

scripts/rmc.py

Lines changed: 16 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ def edit_output(self, text):
4848
def is_exe(name):
4949
from shutil import which
5050
return which(name) is not None
51-
51+
5252
def ensure_dependencies_in_path():
5353
for program in [RMC_RUSTC_EXE, "symtab2gb", "cbmc", "cbmc-viewer", "goto-instrument", "goto-cc"]:
5454
ensure(is_exe(program), f"Could not find {program} in PATH")
@@ -116,11 +116,11 @@ def run_cmd(cmd, label=None, cwd=None, env=None, output_to=None, quiet=False, ve
116116
process = subprocess.run(
117117
cmd, universal_newlines=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT,
118118
env=env, cwd=cwd)
119-
119+
120120
# Print status
121121
if label != None:
122122
print_rmc_step_status(label, process, verbose)
123-
123+
124124
stdout = process.stdout
125125
for scanner in scanners:
126126
if scanner.match(stdout):
@@ -129,24 +129,26 @@ def run_cmd(cmd, label=None, cwd=None, env=None, output_to=None, quiet=False, ve
129129
# Write to stdout if specified, or if failure, or verbose or debug
130130
if (output_to == "stdout" or process.returncode != EXIT_CODE_SUCCESS or verbose or debug) and not quiet:
131131
print(stdout)
132-
132+
133133
# Write to file if given
134134
if output_to != None and output_to != "stdout":
135135
with open(output_to, "w") as f:
136136
f.write(stdout)
137-
137+
138138
return process.returncode
139139

140140
# Generates a symbol table from a rust file
141141
def compile_single_rust_file(input_filename, output_filename, verbose=False, debug=False, keep_temps=False, mangler="v0", dry_run=False, use_abs=False, abs_type="std", symbol_table_passes=[]):
142142
if not keep_temps:
143143
atexit.register(delete_file, output_filename)
144144

145-
build_cmd = [RMC_RUSTC_EXE,
146-
"-Z", "codegen-backend=gotoc",
145+
build_cmd = [RMC_RUSTC_EXE,
146+
"-Z", "codegen-backend=gotoc",
147147
"-Z", "trim-diagnostic-paths=no",
148-
"-Z", f"symbol-mangling-version={mangler}",
148+
"-Z", f"symbol-mangling-version={mangler}",
149149
"-Z", f"symbol_table_passes={' '.join(symbol_table_passes)}",
150+
"--crate-type=lib",
151+
"-Z", "human_readable_cgu_names",
150152
f"--cfg={RMC_CFG}"]
151153

152154
if use_abs:
@@ -165,20 +167,21 @@ def compile_single_rust_file(input_filename, output_filename, verbose=False, deb
165167
return run_cmd(build_cmd, env=build_env, label="compile", verbose=verbose, debug=debug, dry_run=dry_run)
166168

167169
# Generates a symbol table (and some other artifacts) from a rust crate
168-
def cargo_build(crate, target_dir="target", verbose=False, debug=False, mangler="v0", dry_run=False, symbol_table_passes=[]):
170+
def cargo_build(crate, target_dir, verbose=False, debug=False, mangler="v0", dry_run=False, symbol_table_passes=[]):
169171
ensure(os.path.isdir(crate), f"Invalid path to crate: {crate}")
170172

171173
rustflags = [
172174
"-Z", "codegen-backend=gotoc",
173175
"-Z", "trim-diagnostic-paths=no",
174176
"-Z", f"symbol-mangling-version={mangler}",
175-
"-Z", f"symbol_table_passes={' '.join(symbol_table_passes)}",
177+
"-Z", f"symbol_table_passes={' '.join(symbol_table_passes)}",
178+
"-Z", "human_readable_cgu_names",
176179
f"--cfg={RMC_CFG}"]
177180
rustflags = " ".join(rustflags)
178181
if "RUSTFLAGS" in os.environ:
179182
rustflags = os.environ["RUSTFLAGS"] + " " + rustflags
180183

181-
build_cmd = ["cargo", "build", "--target-dir", target_dir]
184+
build_cmd = ["cargo", "build", "--lib", "--target-dir", str(target_dir)]
182185
build_env = {"RUSTFLAGS": rustflags,
183186
"RUSTC": RMC_RUSTC_EXE,
184187
"PATH": os.environ["PATH"],
@@ -229,7 +232,7 @@ def run_visualize(cbmc_filename, prop_args, cover_args, verbose=False, quiet=Fal
229232
# 2) cbmc --xml-ui --cover location ~/rmc/library/rmc/rmc_lib.c <cbmc_filename> > coverage.xml
230233
# 3) cbmc --xml-ui --show-properties ~/rmc/library/rmc/rmc_lib.c <cbmc_filename> > property.xml
231234
# 4) cbmc-viewer --result results.xml --coverage coverage.xml --property property.xml --srcdir . --goto <cbmc_filename> --reportdir report
232-
235+
233236
def run_cbmc_local(cbmc_args, output_to, dry_run=False):
234237
cbmc_cmd = ["cbmc"] + cbmc_args + [cbmc_filename]
235238
return run_cmd(cbmc_cmd, label="cbmc", output_to=output_to, verbose=verbose, quiet=quiet, dry_run=dry_run)
@@ -266,7 +269,7 @@ def run_goto_instrument(input_filename, output_filename, args, verbose=False, dr
266269
# Generates a C program from a goto program
267270
def goto_to_c(goto_filename, c_filename, verbose=False, dry_run=False):
268271
return run_goto_instrument(goto_filename, c_filename, ["--dump-c"], verbose, dry_run=dry_run)
269-
272+
270273
# Fix remaining issues with output of --gen-c-runnable
271274
def gen_c_postprocess(c_filename, dry_run=False):
272275
if not dry_run:

src/test/cargo-rmc/simple-main/Cargo.toml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,9 @@ name = "empty-main"
55
version = "0.1.0"
66
edition = "2018"
77

8+
[lib]
9+
path="src/main.rs"
10+
811
[dependencies]
912

1013
[workspace]
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
22
// SPDX-License-Identifier: Apache-2.0 OR MIT
3-
fn main() {
3+
pub fn main() {
44
assert!(1 == 2);
55
}

src/test/cbmc/ArithEqualOperators/main.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
// SPDX-License-Identifier: Apache-2.0 OR MIT
33
include!("../../rmc-prelude.rs");
44

5-
fn main() {
5+
pub fn main() {
66
let mut a: u32 = __nondet();
77
a /= 2;
88
let mut b: u32 = __nondet();

src/test/cbmc/ArithOperators/main.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
// SPDX-License-Identifier: Apache-2.0 OR MIT
33
include!("../../rmc-prelude.rs");
44

5-
fn main() {
5+
pub fn main() {
66
let a: u32 = __nondet();
77
assert!(a / 2 <= a);
88
assert!(a / 2 * 2 >= a / 2);

src/test/cbmc/Asm/main_fixme.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
// SPDX-License-Identifier: Apache-2.0 OR MIT
33
#![feature(asm)]
44

5-
fn main() {
5+
pub fn main() {
66
unsafe {
77
asm!("nop");
88
}

src/test/cbmc/Assert/UninitValid/fixme_main.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44

55
use std::mem::{self, MaybeUninit};
66

7-
fn main() {
7+
pub fn main() {
88
// The compiler assumes that variables are properly initialized according to
99
// the requirements of the variable's type (e.g., a variable of reference
1010
// type must be aligned and non-NULL). This is an invariant that must always

0 commit comments

Comments
 (0)