-
Notifications
You must be signed in to change notification settings - Fork 55
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Reproducible buid: introduce a tool to help reproducible build
1. Introduce a reproducible tool, td-reproducible-tool, to help reproducible 2. Add strip = "symbols" to cargo.toml in root workspace to remove symbols Signed-off-by: Longlong Yang <longlong.yang@intel.com>
- Loading branch information
1 parent
0e08c3e
commit d88ad7c
Showing
5 changed files
with
306 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
[package] | ||
name = "td-reproducible-tool" | ||
version = "0.1.0" | ||
description = "A tool modify compiled binary to keep reproducible" | ||
repository = "https://github.com/confidential-containers/td-shim" | ||
homepage = "https://github.com/confidential-containers" | ||
license = "BSD-2-Clause-Patent" | ||
edition = "2018" | ||
|
||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html | ||
|
||
[features] | ||
default = [] | ||
|
||
[dependencies] | ||
argparse = "0.2.2" | ||
zeroize = "1.5.4" | ||
regex = "1" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,61 @@ | ||
# td-reproducible-tool | ||
|
||
## Background | ||
|
||
Elements break reproducible build (x86_64-unknown-uefi + target x86_64-unknown-none) <br> | ||
<br> | ||
``` | ||
1. Debug related sections (PE + ELF) | ||
2. Code base path like \home\USERNAME\rust-td-payload\xxx\xxx.rs (PE + ELF) | ||
3. CARGO_HOME like \home\USERNAME\.cargo\xxx\xxx.rs (PE + ELF) | ||
4. RUSTUP_HOME like \home\USERNAME\.rustup\xxx\xxx.rs (PE + ELF) | ||
5. TimeDateStamp field in COFF file header (PE only) | ||
``` | ||
|
||
Solution: <br> | ||
|
||
``` | ||
1. Use strip = "symbols" option to [profile.release] | ||
2. Use strip = "symbols" option to [profile.release] | ||
3. Use a post-build tool to zero CARGO_HOME string | ||
4. Use a post-build tool to zero RUSTUP_HOME string | ||
5. Use a post-build tool to zero TimeDateStamp for PE | ||
``` | ||
NOTE: 3 and 4 may be resolved directly after rust-lang RFC 3127. This tool provide a temp solution to solve 3 and 4 before RFC is implemented. | ||
|
||
## tool usage | ||
|
||
``` | ||
td-reproducible-tool [OPTIONS] | ||
TD REPRODUCIBLE TOOL | ||
Optional arguments: | ||
-h,--help Show this help message and exit | ||
-w,--workspace WORKSPACE | ||
Where to find the target folder. | ||
-n,--name NAME Name for the compiled binary. | ||
-t,--target TARGET The built target to find. | ||
-p,--profile PROFILE The built profile to find. | ||
-c,--cargo_home CARGO_HOME | ||
The cargo home. If not specify, system variable CARGO_HOME will be searched. | ||
-r,--rustup_home RUSTUP_HOME | ||
The rustup home. If not specify, system variable RUSTUP_HOME will be searched. | ||
-v,--verbose Verbose output. | ||
``` | ||
|
||
example:<br> | ||
Command used under windows: | ||
``` | ||
cargo run -p td-reproducible-tool -- -n rust-td-payload --target x86_64-unknown-uefi -p release -v | ||
``` | ||
is equal to | ||
``` | ||
cargo run -p td-reproducible-tool -- -w "." -n rust-td-payload.efi -t x86_64-unknown-uefi -p release -v | ||
``` | ||
<br> | ||
Command used under linux: | ||
|
||
``` | ||
cargo run -p td-reproducible-tool -- -n rust-td-payload --target x86_64-unknown-none -p release -v | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,200 @@ | ||
// Copyright (c) 2022 Intel Corporation | ||
// | ||
// SPDX-License-Identifier: BSD-2-Clause-Patent | ||
|
||
#![forbid(unsafe_code)] | ||
|
||
use argparse::{ArgumentParser, Store, StoreTrue}; | ||
use regex::bytes::Regex; | ||
use std::convert::TryInto; | ||
use std::fs; | ||
use std::io::Read; | ||
use std::{env, fs::File, path::PathBuf}; | ||
use zeroize::Zeroize; | ||
|
||
// https://docs.microsoft.com/en-us/windows/win32/debug/pe-format | ||
const PE_SIG_PTR_OFF: usize = 0x3C; | ||
const TIMEDATASTAMP_OFF_TO_PE_SIG: usize = 0x8; | ||
|
||
fn main() -> std::io::Result<()> { | ||
// handle args | ||
let mut workspace = "".to_string(); | ||
let mut binary_name = "".to_string(); | ||
let mut target = "".to_string(); | ||
let mut profile = "".to_string(); | ||
let mut verbose = false; | ||
|
||
let mut cargo_home = "".to_string(); | ||
let mut rustup_home = "".to_string(); | ||
|
||
{ | ||
let mut ap = ArgumentParser::new(); | ||
ap.set_description("TD REPRODUCIBLE TOOL"); | ||
ap.refer(&mut workspace).add_option( | ||
&["-w", "--workspace"], | ||
Store, | ||
"Where to find the target folder.", | ||
); | ||
ap.refer(&mut binary_name).add_option( | ||
&["-n", "--name"], | ||
Store, | ||
"Name for the compiled binary.", | ||
); | ||
ap.refer(&mut target) | ||
.add_option(&["-t", "--target"], Store, "The built target to find."); | ||
ap.refer(&mut profile).add_option( | ||
&["-p", "--profile"], | ||
Store, | ||
"The built profile to find.", | ||
); | ||
ap.refer(&mut cargo_home) | ||
.add_option(&["-c", "--cargo_home"], Store, "The cargo home."); | ||
ap.refer(&mut rustup_home) | ||
.add_option(&["-r", "--rustup_home"], Store, "The rustup home."); | ||
ap.refer(&mut verbose) | ||
.add_option(&["-v", "--verbose"], StoreTrue, "Verbose output."); | ||
ap.parse_args_or_exit(); | ||
} | ||
|
||
if target == "" { | ||
target = ".".to_string(); | ||
} else if target == "x86_64-unknown-uefi" { | ||
if !binary_name.ends_with(".efi") { | ||
binary_name = binary_name + ".efi"; | ||
} | ||
} else if target == "x86_64-unknown-none" { | ||
// nothing to check | ||
} | ||
|
||
// solve the path | ||
let binary_path: PathBuf = [ | ||
if workspace == "" { "." } else { &workspace }, | ||
"target", | ||
&target, | ||
if profile == "" { "release" } else { &profile }, | ||
&binary_name, | ||
] | ||
.iter() | ||
.collect(); | ||
|
||
assert!(binary_path.exists()); | ||
println!("INFO: Found the compiled file: {:?}", binary_path); | ||
|
||
// check out CARGO_HOME and RUSTUP_HOME | ||
cargo_home = if cargo_home == "" { | ||
if env::var("CARGO_HOME").is_err() { | ||
panic!("Neither --cargo_home nor system environment for \"CARGO_HOME\" is found! ") | ||
} else { | ||
env::var("CARGO_HOME").unwrap() | ||
} | ||
} else { | ||
cargo_home | ||
}; | ||
|
||
rustup_home = if rustup_home == "" { | ||
if env::var("RUSTUP_HOME").is_err() { | ||
panic!("Neither --rustup_home nor system environment for \"RUSTUP_HOME\" is found! ") | ||
} else { | ||
env::var("RUSTUP_HOME").unwrap() | ||
} | ||
} else { | ||
rustup_home | ||
}; | ||
|
||
// load the binary | ||
let mut binary = | ||
File::open(binary_path.clone()).expect("Failed to open the compiled binary!\n"); | ||
let mut buf = Vec::new(); | ||
binary | ||
.read_to_end(&mut buf) | ||
.expect("Failed to read the compiled binary!\n"); | ||
|
||
// strip time data stamp in PE case | ||
if target == "x86_64-unknown-uefi" | ||
|| binary_name.ends_with("exe") | ||
|| binary_name.ends_with("efi") | ||
{ | ||
let pe_sig_off = | ||
u32::from_le_bytes(buf[PE_SIG_PTR_OFF..PE_SIG_PTR_OFF + 4].try_into().unwrap()); | ||
let pe_sig = u32::from_le_bytes( | ||
buf[pe_sig_off as usize..pe_sig_off as usize + 4] | ||
.try_into() | ||
.unwrap(), | ||
); | ||
|
||
assert_eq!( | ||
pe_sig, | ||
u32::from_le_bytes([b'P', b'E', b'\0', b'\0']), | ||
"Found invalid PE signature!" | ||
); | ||
|
||
let time_stamp = u32::from_le_bytes( | ||
buf[pe_sig_off as usize + TIMEDATASTAMP_OFF_TO_PE_SIG | ||
..pe_sig_off as usize + TIMEDATASTAMP_OFF_TO_PE_SIG + 4] | ||
.try_into() | ||
.unwrap(), | ||
); | ||
|
||
println!("INFO: Detected TimeDateStamp: {:?}", time_stamp); | ||
|
||
buf[pe_sig_off as usize + TIMEDATASTAMP_OFF_TO_PE_SIG | ||
..pe_sig_off as usize + TIMEDATASTAMP_OFF_TO_PE_SIG + 4] | ||
.zeroize(); | ||
|
||
let time_stamp = u32::from_le_bytes( | ||
buf[pe_sig_off as usize + TIMEDATASTAMP_OFF_TO_PE_SIG | ||
..pe_sig_off as usize + TIMEDATASTAMP_OFF_TO_PE_SIG + 4] | ||
.try_into() | ||
.unwrap(), | ||
); | ||
println!("INFO: After removing TimeDateStamp: {:?}", time_stamp); | ||
} | ||
|
||
let mut cargo_like_path_to_strip_regex_pat_str = regex::escape(&cargo_home); | ||
cargo_like_path_to_strip_regex_pat_str.push_str(".*?\\.rs"); | ||
let mut rustup_like_path_to_strip_regex_pat_str = regex::escape(&rustup_home); | ||
rustup_like_path_to_strip_regex_pat_str.push_str(".*?\\.rs"); | ||
|
||
let cargo_like_path_to_strip_regex = | ||
Regex::new(&cargo_like_path_to_strip_regex_pat_str).unwrap(); | ||
let rustup_like_path_to_strip_regex = | ||
Regex::new(&rustup_like_path_to_strip_regex_pat_str).unwrap(); | ||
|
||
let mut stat = 0; | ||
let buf_read_only = buf.clone(); | ||
for mat in cargo_like_path_to_strip_regex.find_iter(&buf_read_only) { | ||
stat += 1; | ||
if verbose { | ||
println!( | ||
"Found an item with start offset:{:?}, end offset:{:?}", | ||
mat.start(), | ||
mat.end() | ||
); | ||
println!("{:?}", std::str::from_utf8(mat.as_bytes())); | ||
println!("Now removing it!"); | ||
} | ||
buf[mat.start()..mat.end()].zeroize(); | ||
} | ||
|
||
for mat in rustup_like_path_to_strip_regex.find_iter(&buf_read_only) { | ||
stat += 1; | ||
if verbose { | ||
println!( | ||
"Found an item with start offset:{:?}, end offset:{:?}", | ||
mat.start(), | ||
mat.end() | ||
); | ||
println!("{:?}", std::str::from_utf8(mat.as_bytes())); | ||
println!("Now removing it!"); | ||
} | ||
buf[mat.start()..mat.end()].zeroize(); | ||
} | ||
|
||
println!("INFO: Removed {:?} items!", stat); | ||
|
||
fs::write(binary_path, buf).expect("Failed to open the compiled binary!\n"); | ||
|
||
println!("INFO: Successful patched the compiled binary!"); | ||
|
||
Ok(()) | ||
} |