Skip to content

Commit 738ca71

Browse files
authored
Demangle symbol names in GotoC (-> .demangled.c) (rust-lang#1303)
1 parent 92e5889 commit 738ca71

File tree

8 files changed

+192
-13
lines changed

8 files changed

+192
-13
lines changed

kani-driver/src/call_goto_instrument.rs

Lines changed: 54 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@
33

44
use anyhow::Result;
55
use std::ffi::OsString;
6+
use std::fs::File;
7+
use std::io::BufReader;
68
use std::path::Path;
79
use std::process::Command;
810

@@ -12,7 +14,13 @@ use crate::util::alter_extension;
1214

1315
impl KaniSession {
1416
/// Postprocess a goto binary (before cbmc, after linking) in-place by calling goto-instrument
15-
pub fn run_goto_instrument(&self, input: &Path, output: &Path, function: &str) -> Result<()> {
17+
pub fn run_goto_instrument(
18+
&self,
19+
input: &Path,
20+
output: &Path,
21+
symtabs: &[impl AsRef<Path>],
22+
function: &str,
23+
) -> Result<()> {
1624
// We actually start by calling goto-cc to start the specialization:
1725
self.specialize_to_proof_harness(input, output, function)?;
1826

@@ -26,13 +34,20 @@ impl KaniSession {
2634
self.rewrite_back_edges(output)?;
2735

2836
if self.args.gen_c {
37+
let c_outfile = alter_extension(output, "c");
38+
// We don't put the C file into temporaries to be deleted.
39+
40+
self.gen_c(output, &c_outfile)?;
41+
42+
if !self.args.quiet {
43+
println!("Generated C code written to {}", c_outfile.to_string_lossy());
44+
}
45+
46+
let c_demangled = alter_extension(output, "demangled.c");
47+
self.demangle_c(symtabs, &c_outfile, &c_demangled)?;
2948
if !self.args.quiet {
30-
println!(
31-
"Generated C code written to {}",
32-
alter_extension(output, "c").to_string_lossy()
33-
);
49+
println!("Demangled GotoC code written to {}", c_demangled.to_string_lossy())
3450
}
35-
self.gen_c(output)?;
3651
}
3752

3853
Ok(())
@@ -116,19 +131,47 @@ impl KaniSession {
116131
}
117132

118133
/// Generate a .c file from a goto binary (i.e. --gen-c)
119-
pub fn gen_c(&self, file: &Path) -> Result<()> {
120-
let output_filename = alter_extension(file, "c");
121-
// We don't put the C file into temporaries to be deleted.
122-
134+
pub fn gen_c(&self, file: &Path, output_file: &Path) -> Result<()> {
123135
let args: Vec<OsString> = vec![
124136
"--dump-c".into(),
125137
file.to_owned().into_os_string(),
126-
output_filename.into_os_string(),
138+
output_file.to_owned().into_os_string(),
127139
];
128140

129141
self.call_goto_instrument(args)
130142
}
131143

144+
/// Generate a .demangled.c file from the .c file using the `prettyName`s from the symbol tables
145+
///
146+
/// Currently, only top-level function names and (most) type names are demangled.
147+
/// For local variables, it would be more complicated than a simple search and replace to obtain the demangled name.
148+
pub fn demangle_c(
149+
&self,
150+
symtab_files: &[impl AsRef<Path>],
151+
c_file: &Path,
152+
demangled_file: &Path,
153+
) -> Result<()> {
154+
let mut c_code = std::fs::read_to_string(c_file)?;
155+
for symtab_file in symtab_files {
156+
let reader = BufReader::new(File::open(symtab_file.as_ref())?);
157+
let symtab: serde_json::Value = serde_json::from_reader(reader)?;
158+
for (_, symbol) in symtab["symbolTable"].as_object().unwrap() {
159+
if let Some(serde_json::Value::String(name)) = symbol.get("name") {
160+
if let Some(serde_json::Value::String(pretty)) = symbol.get("prettyName") {
161+
// Struct names start with "tag-", but this prefix is not used in the GotoC files, so we strip it.
162+
// If there is no such prefix, we leave the name unchanged.
163+
let name = name.strip_prefix("tag-").unwrap_or(name);
164+
if !pretty.is_empty() && pretty != name {
165+
c_code = c_code.replace(name, pretty);
166+
}
167+
}
168+
}
169+
}
170+
}
171+
std::fs::write(demangled_file, c_code)?;
172+
Ok(())
173+
}
174+
132175
/// Non-public helper function to actually do the run of goto-instrument
133176
fn call_goto_instrument(&self, args: Vec<OsString>) -> Result<()> {
134177
// TODO get goto-instrument path from self

kani-driver/src/main.rs

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,12 @@ fn cargokani_main(input_args: Vec<OsString>) -> Result<()> {
6363
let harness_filename = harness.pretty_name.replace("::", "-");
6464
let report_dir = report_base.join(format!("report-{}", harness_filename));
6565
let specialized_obj = outputs.outdir.join(format!("cbmc-for-{}.out", harness_filename));
66-
ctx.run_goto_instrument(&linked_obj, &specialized_obj, &harness.mangled_name)?;
66+
ctx.run_goto_instrument(
67+
&linked_obj,
68+
&specialized_obj,
69+
&outputs.symtabs,
70+
&harness.mangled_name,
71+
)?;
6772

6873
let result = ctx.check_harness(&specialized_obj, &report_dir, harness)?;
6974
if result == VerificationStatus::Failure {
@@ -109,7 +114,12 @@ fn standalone_main() -> Result<()> {
109114
let mut temps = ctx.temporaries.borrow_mut();
110115
temps.push(specialized_obj.to_owned());
111116
}
112-
ctx.run_goto_instrument(&linked_obj, &specialized_obj, &harness.mangled_name)?;
117+
ctx.run_goto_instrument(
118+
&linked_obj,
119+
&specialized_obj,
120+
&[&outputs.symtab],
121+
&harness.mangled_name,
122+
)?;
113123

114124
let result = ctx.check_harness(&specialized_obj, &report_dir, harness)?;
115125
if result == VerificationStatus::Failure {

scripts/kani-regression.sh

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,10 @@ cargo test -p cprover_bindings
3636
cargo test -p kani-compiler
3737
cargo test -p kani-driver
3838

39+
# Check output files (--gen-c option)
40+
echo "Check GotoC output file generation"
41+
time "$KANI_DIR"/tests/output-files/check-output.sh
42+
3943
# Declare testing suite information (suite and mode)
4044
TESTS=(
4145
"kani kani"

tests/output-files/.gitignore

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
target/
2+
Cargo.lock
3+
*.c
4+
build/

tests/output-files/check-output.sh

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
#!/usr/bin/env bash
2+
# Copyright Kani Contributors
3+
# SPDX-License-Identifier: Apache-2.0 OR MIT
4+
5+
set -eu
6+
7+
echo
8+
echo "Starting output file check..."
9+
echo
10+
11+
# Test for platform
12+
PLATFORM=$(uname -sp)
13+
if [[ $PLATFORM == "Linux x86_64" ]]
14+
then
15+
TARGET="x86_64-unknown-linux-gnu"
16+
elif [[ $PLATFORM == "Darwin i386" ]]
17+
then
18+
TARGET="x86_64-apple-darwin"
19+
elif [[ $PLATFORM == "Darwin arm" ]]
20+
then
21+
TARGET="aarch64-apple-darwin"
22+
else
23+
echo
24+
echo "Test only works on Linux or OSX platforms, skipping..."
25+
echo
26+
exit 0
27+
fi
28+
29+
cd $(dirname $0)
30+
31+
echo "Running single-file check..."
32+
rm -rf *.c
33+
RUST_BACKTRACE=1 kani --gen-c --enable-unstable singlefile.rs --quiet
34+
if ! [ -e singlefile.out.c ]
35+
then
36+
echo "Error: no GotoC file generated. Expected: singlefile.out.c"
37+
exit 1
38+
fi
39+
40+
if ! [ -e singlefile.out.demangled.c ]
41+
then
42+
echo "Error: no demangled GotoC file generated. Expected singlefile.out.demangled.c."
43+
exit 1
44+
fi
45+
46+
if ! grep -Fq "struct PrettyStruct pretty_function(struct PrettyStruct" singlefile.out.demangled.c;
47+
then
48+
echo "Error: demangled file singlefile.out.demangled.c did not contain expected demangled struct and function name."
49+
exit 1
50+
fi
51+
echo "Finished single-file check successfully..."
52+
echo
53+
54+
(cd multifile
55+
echo "Running multi-file check..."
56+
rm -rf build
57+
RUST_BACKTRACE=1 cargo kani --target-dir build --gen-c --enable-unstable --quiet
58+
cd build/${TARGET}/debug/deps/
59+
60+
if ! [ -e cbmc-for-main.c ]
61+
then
62+
echo "Error: no GotoC file generated. Expected: build/${TARGET}/debug/deps/cbmc-for-main.c"
63+
exit 1
64+
fi
65+
66+
if ! [ -e cbmc-for-main.demangled.c ]
67+
then
68+
echo "Error: no demangled GotoC file generated. Expected build/${TARGET}/debug/deps/cbmc-for-main.demangled.c."
69+
exit 1
70+
fi
71+
72+
if ! grep -Fq "struct PrettyStruct pretty_function(struct PrettyStruct" cbmc-for-main.demangled.c;
73+
then
74+
echo "Error: demangled file build/${TARGET}/debug/deps/cbmc-for-main.demangled.c did not contain expected demangled struct and function name."
75+
exit 1
76+
fi
77+
echo "Finished multi-file check successfully..."
78+
)
79+
80+
echo "Finished output file check successfully."
81+
echo
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
# Copyright Kani Contributors
2+
# SPDX-License-Identifier: Apache-2.0 OR MIT
3+
4+
[package]
5+
name = "multifile"
6+
version = "0.1.0"
7+
edition = "2021"
8+
9+
[workspace]
10+
11+
[dependencies]
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
// Copyright Kani Contributors
2+
// SPDX-License-Identifier: Apache-2.0 OR MIT
3+
4+
pub struct PrettyStruct;
5+
6+
#[kani::proof]
7+
fn main() {
8+
pretty_function(PrettyStruct);
9+
}
10+
11+
pub fn pretty_function(argument: PrettyStruct) -> PrettyStruct {
12+
argument
13+
}

tests/output-files/singlefile.rs

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
// Copyright Kani Contributors
2+
// SPDX-License-Identifier: Apache-2.0 OR MIT
3+
4+
pub struct PrettyStruct;
5+
6+
#[kani::proof]
7+
pub fn main() {
8+
pretty_function(PrettyStruct);
9+
}
10+
11+
pub fn pretty_function(argument: PrettyStruct) -> PrettyStruct {
12+
argument
13+
}

0 commit comments

Comments
 (0)