Skip to content

Latest commit

 

History

History
280 lines (204 loc) · 8.26 KB

File metadata and controls

280 lines (204 loc) · 8.26 KB

Translating your own C project

This guide explains how to apply SmartC2Rust to a C project of your own, rather than one of the bundled benchmarks under benchmark/.

The translation procedure itself (Steps 1–6) is identical to the one in the main README. This document only describes what you need to prepare beforehand and how to adapt the example commands to arbitrary paths.

Prerequisites. You should have already completed the Setup with Docker


1. Overview

To translate your own project, you need to provide three things:

  1. A buildable C source tree.
  2. An entry-point specification (targets.txt).
  3. A test script that exercises the program and reports pass/fail.

Once these are in place, you run the same Steps 1–6 from the main README, substituting the benchmark paths with the paths to your own project.


2. Project layout

Place your project under SmartC2Rust/program/. This is the standard location for user projects. Keeping this convention means you can follow the Step 1–6 commands from the main README with only the project name changed.

/root/SmartC2Rust/
└── program/                
    └── myproj/             # One directory per project
        ├── src/            # Your C source tree (must build as-is)
        │   ├── main.c
        │   └── ...
        ├── targets.txt     # Entry-point specification (you write this)
        ├── run_test.sh     
        └── base_test.sh    # Original test script (you write this)

myproj is just a placeholder — use whatever name you like for your project directory. Throughout the rest of this guide, replace myproj with your actual project name.


3. Setting up the build

3.1 Required compiler flags

SmartC2Rust relies on instrumentation and debug information embedded in the compiled binary (used for capturing golden execution flows in Step 2 and for static analysis in later steps). You must therefore build your project with clang and a specific set of flags.

Required

Flag Why it is needed
clang (as CC) The instrumentation and DWARF handling assume clang.
-finstrument-functions Inserts entry/exit hooks used to record execution flows.
-g -gdwarf-4 Emits DWARF 4 debug info used by the analyzers.

How to integrate them

Edit your project's Makefile (or equivalent build configuration) so that the compiler and flags above are applied to every translation unit.

3.2 Writing c_build.sh

c_build.sh is a build wrapper script that the pipeline invokes to build your project. Its required side-effect is to produce a compile_commands.json at the project root. The macro and C parsers read this file to recover include paths and macro definitions for each translation unit; the pipeline will fail without it.

How you produce compile_commands.json is up to your build system — c_build.sh is just the entry point that runs the build.

Place the script at program/myproj/c_build.sh and make it executable (chmod +x c_build.sh).

Required interface

The script must accept an optional first argument:

  • c_build.sh init — clean any previous build artifacts, then build. Called once before the first translation run.
  • c_build.sh (no argument) — incremental build. Called repeatedly during the repair loops.

After either form returns, compile_commands.json must exist at the project root.

Producing compile_commands.json

Pick the approach that matches your build system:

Make (or any build system without native support): wrap the build with bear, which intercepts compiler invocations and writes them to compile_commands.json.

#!/bin/bash
option=${1:-"build"}

if [ "$option" = "init" ]; then
    make clean
fi

bear -- make

CMake: enable native export with -DCMAKE_EXPORT_COMPILE_COMMANDS=ON. CMake writes compile_commands.json into the build directory, so symlink or copy it to the project root.

#!/bin/bash
option=${1:-"build"}

if [ "$option" = "init" ]; then
    rm -rf build
    cmake -S . -B build -DCMAKE_EXPORT_COMPILE_COMMANDS=ON
fi

cmake --build build
ln -sf build/compile_commands.json compile_commands.json

Other build systems: anything works, as long as the result is a valid compile_commands.json at the project root. Some build tools (e.g. Ninja via ninja -t compdb, Bazel via bazel-compdb) have their own export mechanisms; otherwise fall back to wrapping with bear.


4. Writing targets.txt

targets.txt lists the C functions that will be translated to Rust and called from C via FFI. Each line has the form:

function_name:path/to/file.c:start_line:end_line
  • path/to/file.c is relative to your project root (the directory you pass as <c_source_dir> in Step 1).
  • start_line and end_line are 1-indexed and inclusive, covering the function definition (signature through closing brace).

Choosing entry points

For command-line tools, the simplest choice is a single line pointing at main. This matches what the bundled benchmarks do and lets the minimize FFI strategy translate the program into idiomatic Rust without any FFI boundary inside the translated portion.

main:src/main.c:42:178

For library-style code, list each public function you want translated. In that case you will likely want "ffi_strategy": "preserve" in config.json so the Rust side remains C-callable. See docs/ffi-boundary.md for how the FFI boundary is designed.


5. Writing the test script

See docs/reformat-testcases.md for details.


6. Tuning config.json for your project

Two fields in config.json are worth thinking about for non-benchmark projects:

ffi_strategy

  • "minimize" (default) — translate into idiomatic safe Rust. Best for command-line tools where main is the entry point and there is no external C consumer.
  • "preserve" — keep C-compatible signatures via FFI. Use when the translated functions must be callable from existing C code, e.g. when you are translating a library.

7. Running Steps 1–6 on your project

Below are the same six steps from the main README, with benchmark-specific paths replaced by /root/SmartC2Rust/program/myproj. Adjust the paths to match your setup.

The numeric suffix _0000 is an iteration index used by the pipeline; it will increment automatically across runs. The examples below assume the first run.

Step 1: Prepare the test script

cd /root/SmartC2Rust/macro
python3 pre_process.py \
    /root/SmartC2Rust/program/myproj \
    reformat base \
    /root/SmartC2Rust/program/myproj/base_test.sh

Produces /root/SmartC2Rust/program/myproj/run_test.sh.

Step 2: Get golden flows

cd /root/SmartC2Rust/macro
python3 pre_process.py \
    /root/SmartC2Rust/macro/trans_re_0000/myproj \
    golden

Step 3: Pre-processing for parsing

cd /root/SmartC2Rust/macro
python3 pre_process.py \
    /root/SmartC2Rust/macro/trans_re_0000/myproj \
    macro off \
    /root/SmartC2Rust/macro/trans_re_0000/myproj/run_test.sh \
    /root/SmartC2Rust/program/myproj/targets.txt

Step 4: Pre-processing for segmentation

cd /root/SmartC2Rust/trans
python3 pre_process.py \
    /root/SmartC2Rust/macro/trans_c_0000/myproj \
    meta \
    /root/SmartC2Rust/program/myproj/targets.txt \
    /root/SmartC2Rust/macro/metadata_0000/myproj \
    /root/SmartC2Rust/macro/div_metadata_0000/myproj \
    /root/SmartC2Rust/macro/trans_c_0000/myproj

Step 5: Compilation-repair

cd /root/SmartC2Rust/trans
python3 compile.py \
    /root/SmartC2Rust/trans/c_code_0000/myproj \
    /root/SmartC2Rust/trans/trans_c_0000/myproj \
    /root/SmartC2Rust/program/myproj/targets_actual.txt \
    trans \
    /root/SmartC2Rust/trans/metadata_0000/myproj \
    /root/SmartC2Rust/trans/div_metadata_0000/myproj \
    database_0000/myproj/block_output.txt off

Step 6: Semantics-repair

cd /root/SmartC2Rust/trans
python3 semantics.py \
    /root/SmartC2Rust/trans/workspace_0000_myproj/myproj \
    s_repair

The translated Rust crate is at:

/root/SmartC2Rust/trans/workspace_s_repair_0000_myproj/myproj/trans_rust/