Skip to content

Commit bbcff96

Browse files
authored
Restore cross checking code, part 1 (#1575)
Restore the old cross checking code and update the transpiler so it passes CI.
2 parents 4b400b4 + ab05a6f commit bbcff96

File tree

116 files changed

+11834
-3
lines changed

Some content is hidden

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

116 files changed

+11834
-3
lines changed

README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,8 @@ required to lift unsafe Rust into safe Rust types.
2626
However, we are building a [refactoring tool](c2rust-refactor) that reduces the tedium of doing so.
2727
This work is still in the early stages; please get in touch if you're interested!
2828

29+
You can also [cross-check](cross-checks) the translated code against the original ([tutorial](docs/cross-check-tutorial.md)).
30+
2931
Here's the big picture:
3032

3133
![C2Rust overview](docs/c2rust-overview.png "C2Rust overview")

c2rust-transpile/src/build_files/Cargo.toml.hbs

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,4 +40,18 @@ name = "{{name}}"
4040
{{/if}}
4141
{{/each}}
4242

43+
{{#if cross_checks~}}
44+
[dependencies.c2rust-xcheck-plugin]
45+
version = "*"
46+
47+
[dependencies.c2rust-xcheck-derive]
48+
version = "*"
49+
50+
[dependencies.c2rust-xcheck-runtime]
51+
version = "*"
52+
features = ["libc-hash", "fixed-length-array-hash"]
53+
54+
[dependencies.c2rust-xcheck-backend-{{cross_check_backend}}]
55+
version = "*"
56+
{{~/if}}
4357
{{~/if}}

c2rust-transpile/src/build_files/lib.rs.hbs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,16 @@
99
extern crate {{this.ident}};
1010
{{~/each}}
1111

12+
{{#if cross_checks~}}
13+
#![plugin(c2rust_xcheck_plugin({{plugin_args}}))]
14+
#[macro_use] extern crate c2rust_xcheck_derive;
15+
#[macro_use] extern crate c2rust_xcheck_runtime;
16+
extern crate c2rust_xcheck_backend_{{cross_check_backend}};
17+
18+
#[global_allocator]
19+
static C2RUST_ALLOC: ::std::alloc::System = ::std::alloc::System;
20+
{{~/if}}
21+
1222
{{#each modules~}}
1323
{{~#if this.path~}}
1424
#[path = "{{this.path}}"]

c2rust-transpile/src/build_files/mod.rs

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -250,13 +250,24 @@ fn emit_lib_rs(
250250
pragmas: PragmaSet,
251251
crates: &CrateSet,
252252
) -> Option<PathBuf> {
253+
let plugin_args = tcfg
254+
.cross_check_configs
255+
.iter()
256+
.map(|ccc| format!("config_file = \"{}\"", ccc))
257+
.collect::<Vec<String>>()
258+
.join(", ");
259+
253260
let modules = convert_module_list(tcfg, build_dir, modules, ModuleSubset::Libraries);
254261
let crates = convert_dependencies_list(crates.clone(), tcfg.c2rust_dir.as_deref());
255262
let file_name = get_lib_rs_file_name(tcfg);
263+
let rs_xcheck_backend = tcfg.cross_check_backend.replace("-", "_");
256264
let json = json!({
257265
"lib_rs_file": file_name,
258266
"reorganize_definitions": tcfg.reorganize_definitions,
259267
"translate_valist": tcfg.translate_valist,
268+
"cross_checks": tcfg.cross_checks,
269+
"cross_check_backend": rs_xcheck_backend,
270+
"plugin_args": plugin_args,
260271
"modules": modules,
261272
"pragmas": pragmas,
262273
"crates": crates,
@@ -311,6 +322,8 @@ fn emit_cargo_toml<'lcmd>(
311322
"is_library": ccfg.link_cmd.r#type.is_library(),
312323
"lib_rs_file": get_lib_rs_file_name(tcfg),
313324
"binaries": binaries,
325+
"cross_checks": tcfg.cross_checks,
326+
"cross_check_backend": tcfg.cross_check_backend,
314327
"dependencies": dependencies,
315328
});
316329
json.as_object_mut().unwrap().extend(

c2rust-transpile/src/lib.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,9 @@ pub struct TranspilerConfig {
7979
pub fail_on_multiple: bool,
8080
pub filter: Option<Regex>,
8181
pub debug_relooper_labels: bool,
82+
pub cross_checks: bool,
83+
pub cross_check_backend: String,
84+
pub cross_check_configs: Vec<String>,
8285
pub prefix_function_names: Option<String>,
8386
pub translate_asm: bool,
8487
pub use_c_loop_info: bool,

c2rust-transpile/src/translator/main_function.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -299,7 +299,8 @@ impl<'c> Translation<'c> {
299299
};
300300

301301
let block = mk().block(stmts);
302-
Ok(mk().pub_().fn_item(decl, block))
302+
let main_attributes = self.mk_cross_check(mk(), vec!["none"]);
303+
Ok(main_attributes.pub_().fn_item(decl, block))
303304
} else {
304305
Err(TranslationError::generic(
305306
"Cannot translate non-function main entry point",

c2rust-transpile/src/translator/mod.rs

Lines changed: 53 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1228,6 +1228,20 @@ fn arrange_header(t: &Translation, is_binary: bool) -> (Vec<syn::Attribute>, Vec
12281228
out_attrs.push(attr);
12291229
}
12301230

1231+
if t.tcfg.cross_checks {
1232+
let xcheck_plugin_args = t
1233+
.tcfg
1234+
.cross_check_configs
1235+
.iter()
1236+
.map(|config_file| mk().meta_namevalue("config_file", mk().str_lit(config_file)))
1237+
.collect::<Vec<_>>();
1238+
let xcheck_plugin_item = mk().meta_list("c2rust_xcheck_plugin", xcheck_plugin_args);
1239+
let plugin_args = vec![xcheck_plugin_item];
1240+
let plugin_item = mk().meta_list("plugin", plugin_args);
1241+
let attr = mk().attribute(AttrStyle::Inner(Default::default()), plugin_item);
1242+
out_attrs.push(attr);
1243+
}
1244+
12311245
if t.tcfg.emit_no_std {
12321246
let meta = mk().meta_path("no_std");
12331247
let attr = mk().attribute(AttrStyle::Inner(Default::default()), meta);
@@ -1247,6 +1261,24 @@ fn arrange_header(t: &Translation, is_binary: bool) -> (Vec<syn::Attribute>, Vec
12471261
}
12481262
}
12491263

1264+
if t.tcfg.cross_checks {
1265+
out_items.push(
1266+
mk().single_attr("macro_use")
1267+
.extern_crate_item("c2rust_xcheck_derive", None),
1268+
);
1269+
out_items.push(
1270+
mk().single_attr("macro_use")
1271+
.extern_crate_item("c2rust_xcheck_runtime", None),
1272+
);
1273+
// When cross-checking, always use the system allocator
1274+
let sys_alloc_path = vec!["", "std", "alloc", "System"];
1275+
out_items.push(mk().single_attr("global_allocator").static_item(
1276+
"C2RUST_ALLOC",
1277+
mk().path_ty(sys_alloc_path.clone()),
1278+
mk().path_expr(sys_alloc_path),
1279+
));
1280+
}
1281+
12501282
// TODO: switch to `#[expect(unused_imports, reason = ...)]` once
12511283
// we upgrade to a newer nightly (Rust 1.81) that supports it.
12521284
out_items.push(
@@ -1432,6 +1464,10 @@ impl<'c> Translation<'c> {
14321464
"unused_assignments",
14331465
],
14341466
)];
1467+
if self.tcfg.cross_checks {
1468+
features.append(&mut vec!["plugin"]);
1469+
pragmas.push(("cross_check", vec!["yes"]));
1470+
}
14351471

14361472
if self.features.borrow().contains("register_tool") {
14371473
pragmas.push(("register_tool", vec!["c2rust"]));
@@ -1465,6 +1501,14 @@ impl<'c> Translation<'c> {
14651501
))
14661502
}
14671503

1504+
fn mk_cross_check(&self, mk: Builder, args: Vec<&str>) -> Builder {
1505+
if self.tcfg.cross_checks {
1506+
mk.call_attr("cross_check", args)
1507+
} else {
1508+
mk
1509+
}
1510+
}
1511+
14681512
fn static_initializer_is_unsafe(&self, expr_id: Option<CExprId>, qty: CQualTypeId) -> bool {
14691513
// SIMD types are always unsafe in statics
14701514
match self.ast_context.resolve_type(qty.ctype).kind {
@@ -1660,7 +1704,11 @@ impl<'c> Translation<'c> {
16601704
let fn_decl = mk().fn_decl(fn_name.clone(), vec![], None, fn_ty.clone());
16611705
let fn_bare_decl = (vec![], None, fn_ty);
16621706
let fn_block = mk().block(sectioned_static_initializers);
1663-
let fn_item = mk().unsafe_().extern_("C").fn_item(fn_decl, fn_block);
1707+
let fn_attributes = self.mk_cross_check(mk(), vec!["none"]);
1708+
let fn_item = fn_attributes
1709+
.unsafe_()
1710+
.extern_("C")
1711+
.fn_item(fn_decl, fn_block);
16641712

16651713
let static_attributes = mk()
16661714
.single_attr("used")
@@ -2418,7 +2466,10 @@ impl<'c> Translation<'c> {
24182466

24192467
// Only add linkage attributes if the function is `extern`
24202468
let mut mk_ = if is_main {
2421-
mk()
2469+
// Cross-check this function as if it was called `main`
2470+
// FIXME: pass in a vector of NestedMetaItem elements,
2471+
// but strings have to do for now
2472+
self.mk_cross_check(mk(), vec!["entry(djb2=\"main\")", "exit(djb2=\"main\")"])
24222473
} else if (is_global && !is_inline) || is_extern_inline {
24232474
mk_linkage(false, new_name, name).extern_("C").pub_()
24242475
} else if self.cur_file.get().is_some() {

c2rust-transpile/tests/snapshots.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,9 @@ fn config() -> TranspilerConfig {
2222
fail_on_multiple: false,
2323
filter: None,
2424
debug_relooper_labels: false,
25+
cross_checks: false,
26+
cross_check_backend: Default::default(),
27+
cross_check_configs: Default::default(),
2528
prefix_function_names: None,
2629
translate_asm: true,
2730
use_c_loop_info: true,

c2rust/src/bin/c2rust-transpile.rs

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -170,6 +170,15 @@ struct Args {
170170
/// Fail when the control-flow graph generates branching constructs
171171
#[clap(long)]
172172
fail_on_multiple: bool,
173+
174+
#[clap(long, short = 'x')]
175+
cross_checks: bool,
176+
177+
#[clap(long, short = 'X', multiple = true)]
178+
cross_check_config: Vec<String>,
179+
180+
#[clap(long, value_enum, default_value_t)]
181+
cross_check_backend: CrossCheckBackend,
173182
}
174183

175184
// TODO Eventually move this code into `c2rust-transpile`
@@ -200,6 +209,30 @@ impl From<TranslateMacros> for c2rust_transpile::TranslateMacros {
200209
}
201210
}
202211

212+
#[derive(Default, Debug, ValueEnum, Clone)]
213+
pub enum CrossCheckBackend {
214+
DynamicDlsym,
215+
216+
#[default]
217+
ZstdLogging,
218+
219+
LibclevrbufSys,
220+
221+
LibfakechecksSys,
222+
}
223+
224+
impl From<CrossCheckBackend> for String {
225+
fn from(x: CrossCheckBackend) -> String {
226+
let s = match x {
227+
CrossCheckBackend::DynamicDlsym => "dynamic-dlsym",
228+
CrossCheckBackend::ZstdLogging => "zstd-logging",
229+
CrossCheckBackend::LibclevrbufSys => "libclevrbuf-sys",
230+
CrossCheckBackend::LibfakechecksSys => "libfakechecks-sys",
231+
};
232+
s.to_string()
233+
}
234+
}
235+
203236
#[derive(Debug, PartialEq, Eq, ValueEnum, Clone)]
204237
#[clap(rename_all = "snake_case")]
205238
enum InvalidCodes {
@@ -228,6 +261,9 @@ fn main() {
228261
fail_on_multiple: args.fail_on_multiple,
229262
filter: args.filter,
230263
debug_relooper_labels: args.debug_labels,
264+
cross_checks: args.cross_checks,
265+
cross_check_backend: args.cross_check_backend.into(),
266+
cross_check_configs: args.cross_check_config,
231267
prefix_function_names: args.prefix_function_names,
232268

233269
// We used to guard asm translation with a command-line

cross-checks/README.md

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
This is the top-level directory for all cross-checking components, and contains the following:
2+
3+
* A clang plugin that automatically inserts cross-check instrumentation into C code.
4+
5+
* An equivalent rustc compiler plugin for Rust.
6+
7+
* The `libfakechecks` cross-checking backend library that prints out all cross-checks to standard output.
8+
This library is supported by both the C and Rust compiler plugins.
9+
10+
* Our experimental fork of the `ReMon` MVEE modified for C/Rust side-by-side checking,
11+
along with the `mvee-configs` directory that contains some MVEE configuration examples.

0 commit comments

Comments
 (0)