Skip to content

Instantly share code, notes, and snippets.

@xobs
Last active August 16, 2022 14:47
Show Gist options
  • Save xobs/9cb7156147de68b0767c43dabafcbab9 to your computer and use it in GitHub Desktop.
Save xobs/9cb7156147de68b0767c43dabafcbab9 to your computer and use it in GitHub Desktop.
Sample program to convert C to Rust
#
# 1. Install c2rust dependencies:
# sudo apt install build-essential llvm clang libclang-dev cmake libssl-dev pkg-config python3
# 2. Install c2rust:
# cargo install c2rust:
# 3. Run this program inside the target directory:
# python ring-transpile-c2rust.py
#
# This creates a bunch of `.rs` files with `#[no_mangle] extern "C"` declarations,
# which allow other Rust code to link against it. This program also tries hard to
# fixup the types so that they work without libc.
#
# The resulting code may even compile! But you may need to add more fixes under the
# `massage_line()` function in order to get things working.
#
# The idea behind this program is that you run it once on a project and then begin
# gradually rewriting parts of it.
import subprocess
import os
RING_C_FILES = [
"crypto/fipsmodule/aes/aes_nohw.c",
"crypto/fipsmodule/bn/montgomery.c",
"crypto/fipsmodule/bn/montgomery_inv.c",
"crypto/limbs/limbs.c",
"crypto/mem.c",
"crypto/poly1305/poly1305.c",
# Other libraries
"crypto/crypto.c",
"crypto/curve25519/curve25519.c",
"crypto/fipsmodule/ec/ecp_nistz.c",
"crypto/fipsmodule/ec/ecp_nistz256.c",
"crypto/fipsmodule/ec/gfp_p256.c",
"crypto/fipsmodule/ec/gfp_p384.c",
]
COMMANDS_FILE = "compile_commands.json"
def massage_line(line):
line = line.strip()
# Remove various compile-time directives
if line == "#![register_tool(c2rust)]":
return ""
if line == "use core::arch::asm;":
return ""
if line.startswith("#![feature("):
return ""
if line.startswith("#![allow("):
return ""
# Convert types
line = line.replace("libc::c_int", "i32")
line = line.replace("libc::c_ulong", "u64")
line = line.replace("libc::c_long", "i64")
line = line.replace("libc::c_uint", "u32")
line = line.replace("libc::c_char", "u8")
line = line.replace("libc::c_uchar", "u8")
line = line.replace("libc::c_schar", "i8")
line = line.replace("libc::c_void", "u8")
# Fix program-specific oddities
line = line.replace("::std::vec::", "std::vec::")
line = line.replace(": Vec::", ": std::vec::Vec::")
line = line.replace(") = limbs_mul_add_limb(", ") = GFp_limbs_mul_add_limb(")
# Replace this ASM weirdness with a barrier
compiler_fence = (
"core::sync::atomic::compiler_fence(core::sync::atomic::Ordering::SeqCst);"
)
line = line.replace(
'asm!("", inlateout(reg) a, options(preserves_flags, pure, readonly, att_syntax));',
compiler_fence,
)
return line
def run():
# Generate the `compile_commands.json` file that c2rust uses
cwd = os.getcwd()
with open(COMMANDS_FILE, "w") as cmd_file:
print("[", file=cmd_file)
first_line = False
for file in RING_C_FILES:
rs_file = file.replace(".c", ".rs")
if os.path.exists(rs_file):
os.unlink(rs_file)
if first_line is not False:
print(",", file=cmd_file)
first_line = True
print(" {", file=cmd_file)
print(
f""" "arguments": [
"cc",
"-c",
"-m32",
"-o",
"build/tmp.o",
"-Iinclude",
"-DOPENSSL_32_BIT=1",
"{file}"
],
"directory": "{cwd}",
"file": "{file}"
}}""",
file=cmd_file,
end="",
)
print("", file=cmd_file)
print("]", file=cmd_file)
subprocess.run(["c2rust", "transpile", COMMANDS_FILE])
if not os.path.exists("src/c2rust"):
os.mkdir("src/c2rust")
print("Add this to the end of `src/lib.rs`:")
print("mod c2rust {")
for file in RING_C_FILES:
mod_name = file.split("/")[-1].split(".")[0]
rs_file = file.replace(".c", ".rs")
# print(f" #[path = \"../{rs_file}\"]")
print(f" mod {mod_name};")
with open(rs_file, "r") as src_file:
with open(f"src/c2rust/{mod_name}.rs", "w") as dest_file:
print("extern crate std;", file=dest_file)
for line in src_file:
print(massage_line(line), file=dest_file)
print("}")
if __name__ == "__main__":
run()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment