extern crate crc_any;
extern crate object;
use crc_any::CRCu64;
use object::read::{File, Object};
use object::{ObjectSection, ObjectSymbol};
use std::borrow::Cow;
use std::error::Error;
use std::fs;
use std::path::PathBuf;
use std::process;
use std::{env, iter};
fn main() {
match run() {
Ok(()) => {}
Err(e) => {
eprintln!("Failed to add CRC: {}", e);
process::exit(-1);
}
}
}
fn run() -> Result<(), Box<dyn Error>> {
let input_path = get_input_path();
let mut binary: Vec<u8> = fs::read(&input_path)?;
let offsets = find_file_offsets(&binary)?;
clear_crc(&mut binary, offsets.crc);
write_crc_valid(&mut binary, offsets.crc_valid);
let crc = crc_64_we(&binary);
println!("CRC = {:#x}", crc);
write_crc(&mut binary, offsets.crc, crc);
let temporary_path = input_path.with_extension("tmp");
fs::write(&temporary_path, binary)?;
fs::rename(temporary_path, input_path)?;
Ok(())
}
fn clear_crc(binary: &mut [u8], offset: u64) {
binary[offset as usize..][..8].copy_from_slice(&[0u8; 8]);
}
fn write_crc(binary: &mut [u8], offset: u64, crc: u64) {
let crc_in_binary = &mut binary[offset as usize..][..8];
let crc_bytes = crc.to_le_bytes();
crc_in_binary.copy_from_slice(&crc_bytes);
}
fn write_crc_valid(binary: &mut [u8], offset: u64) {
binary[offset as usize] = 1;
}
struct FileOffsets {
pub crc: u64,
pub crc_valid: u64,
}
fn find_file_offsets(binary: &[u8]) -> Result<FileOffsets, Box<dyn Error>> {
let in_object = File::parse(binary)?;
Ok(FileOffsets {
crc: find_symbol_file_offset(&in_object, "CANADENSIS_CRC")?,
crc_valid: find_symbol_file_offset(&in_object, "CANADENSIS_CRC_VALID")?,
})
}
fn find_symbol_file_offset(in_object: &File, name: &str) -> Result<u64, StringError> {
for symbol in in_object.symbols() {
if symbol.name() == Ok(name) {
let section_index = match symbol.section_index() {
Some(section_index) => section_index,
None => return Err(StringError(format!("Symbol {} is not in a section", name))),
};
let section = match in_object.section_by_index(section_index) {
Ok(section) => section,
Err(e) => {
return Err(StringError(format!(
"Couldn't find section at index {} containing symbol {}: {}",
section_index.0, name, e
)));
}
};
let file_range_start = match section.file_range() {
Some((start, _end)) => start,
None => {
return Err(StringError(format!(
"Section {} is not in the binary file",
section.name().unwrap_or("<unknown>")
)));
}
};
return Ok(file_range_start + (symbol.address() - section.address()));
}
}
Err(StringError(format!("Symbol {} not found", name)))
}
#[derive(Debug)]
struct StringError(String);
impl std::fmt::Display for StringError {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
self.0.fmt(f)
}
}
impl Error for StringError {}
fn get_input_path() -> PathBuf {
match env::args_os().nth(1) {
Some(path) => PathBuf::from(path),
None => {
eprintln!("Usage: canadensis_write_crc binary-path");
process::exit(-1);
}
}
}
fn crc_64_we(data: &[u8]) -> u64 {
let data = &*pad_to_8_bytes(data);
let mut crc = CRCu64::crc64we();
crc.digest(data);
crc.get_crc()
}
fn pad_to_8_bytes(data: &[u8]) -> Cow<'_, [u8]> {
let extra = data.len() % 8;
if extra == 0 {
Cow::Borrowed(data)
} else {
let to_add = 8 - extra;
let mut data = data.to_vec();
data.extend(iter::repeat_n(0u8, to_add));
debug_assert!(data.len() % 8 == 0);
Cow::Owned(data)
}
}