@@ -8,11 +8,17 @@ use std::{
88 process:: Command ,
99} ;
1010
11+ /// One compiler invocation of a side, its objects merge before the rename.
12+ struct Unit < ' a > {
13+ files : & ' a [ PathBuf ] ,
14+ includes : & ' a [ PathBuf ] ,
15+ defines : & ' a [ ( & ' a str , Option < & ' a str > ) ] ,
16+ forced_include : Option < & ' a str > ,
17+ }
18+
1119struct Side < ' a > {
1220 prefix : & ' a str ,
13- source : PathBuf ,
14- include : & ' a Path ,
15- defines : & ' a [ ( & ' a str , Option < & ' a str > ) ] ,
21+ units : & ' a [ Unit < ' a > ] ,
1622}
1723
1824fn main ( ) {
@@ -22,29 +28,60 @@ fn main() {
2228 "the symbol renaming needs ELF objects and GNU-compatible nm and objcopy, so the fuzz crate builds on Linux only"
2329 ) ;
2430 let out = PathBuf :: from ( env:: var_os ( "OUT_DIR" ) . expect ( "cargo sets OUT_DIR" ) ) ;
25- let mc_dir = sqlite3mc_src:: source_dir ( ) ;
26- let sc_dir = sqlcipher_src:: source_dir ( ) ;
31+ let crate_dir =
32+ PathBuf :: from ( env:: var_os ( "CARGO_MANIFEST_DIR" ) . expect ( "cargo sets CARGO_MANIFEST_DIR" ) ) ;
33+ let mc_dir = sqlite3mc_src:: source_dir ( ) . to_path_buf ( ) ;
34+ let sc_dir = sqlcipher_src:: source_dir ( ) . to_path_buf ( ) ;
35+ let ltc_dir = sc_dir. join ( sqlcipher_src:: LIBTOMCRYPT_INCLUDE_DIR ) ;
2736 let common = [
2837 ( "SQLITE_TEMP_STORE" , Some ( "2" ) ) ,
2938 ( "SQLITE_ENABLE_DBPAGE_VTAB" , None ) ,
3039 ( "SQLITE_DEFAULT_MEMSTATUS" , Some ( "0" ) ) ,
3140 ] ;
32- let mc_defines = [ common. as_slice ( ) , & [ ( "SQLITE_CORE" , None ) ] ] . concat ( ) ;
41+ let mc_defines: Vec < ( & str , Option < & str > ) > =
42+ [ common. as_slice ( ) , & [ ( "SQLITE_CORE" , None ) ] ] . concat ( ) ;
43+ let ltc_files: Vec < PathBuf > = std:: iter:: once ( "sqlcipher-entropy.c" )
44+ . map ( |name| crate_dir. join ( name) )
45+ . chain (
46+ sqlcipher_src:: LIBTOMCRYPT_SOURCES
47+ . iter ( )
48+ . map ( |source| sc_dir. join ( source) ) ,
49+ )
50+ . collect ( ) ;
51+ let mc_files = [ mc_dir. join ( sqlite3mc_src:: SOURCE_FILE ) ] ;
52+ let mc_includes = [ mc_dir] ;
53+ let wrapper_files = [ crate_dir. join ( "sqlcipher-wrapper.c" ) ] ;
54+ let wrapper_includes = [ sc_dir, ltc_dir. clone ( ) ] ;
55+ let ltc_includes = [ ltc_dir, crate_dir] ;
3356 build (
3457 & Side {
3558 prefix : "mc" ,
36- source : mc_dir. join ( sqlite3mc_src:: SOURCE_FILE ) ,
37- include : mc_dir,
38- defines : & mc_defines,
59+ units : & [ Unit {
60+ files : & mc_files,
61+ includes : & mc_includes,
62+ defines : & mc_defines,
63+ forced_include : None ,
64+ } ] ,
3965 } ,
4066 & out,
4167 ) ;
4268 build (
4369 & Side {
4470 prefix : "sc" ,
45- source : sc_dir. join ( sqlcipher_src:: WASM_SOURCE_FILE ) ,
46- include : sc_dir,
47- defines : & common,
71+ units : & [
72+ Unit {
73+ files : & wrapper_files,
74+ includes : & wrapper_includes,
75+ defines : & common,
76+ forced_include : None ,
77+ } ,
78+ Unit {
79+ files : & ltc_files,
80+ includes : & ltc_includes,
81+ defines : & [ ( "LTC_SOURCE" , None ) ] ,
82+ forced_include : Some ( "sqlcipher-ltc.h" ) ,
83+ } ,
84+ ] ,
4885 } ,
4986 & out,
5087 ) ;
@@ -53,38 +90,80 @@ fn main() {
5390 println ! ( "cargo:rustc-link-lib={lib}" ) ;
5491 }
5592 println ! ( "cargo:rerun-if-changed=build.rs" ) ;
93+ println ! ( "cargo:rerun-if-changed=sqlcipher-wrapper.c" ) ;
94+ println ! ( "cargo:rerun-if-changed=sqlcipher-entropy.c" ) ;
95+ println ! ( "cargo:rerun-if-changed=sqlcipher-ltc.h" ) ;
5696 println ! ( "cargo:rerun-if-env-changed=NM" ) ;
5797 println ! ( "cargo:rerun-if-env-changed=OBJCOPY" ) ;
98+ println ! ( "cargo:rerun-if-env-changed=LD" ) ;
99+ println ! ( "cargo:rerun-if-env-changed=AR" ) ;
58100}
59101
60102fn build ( side : & Side < ' _ > , out : & Path ) {
61- println ! ( "cargo:rerun-if-changed={}" , side. include. display( ) ) ;
62- let mut cc = cc:: Build :: new ( ) ;
63- cc. file ( & side. source ) . include ( side. include ) . warnings ( false ) ;
64- for ( name, value) in side. defines {
65- cc. define ( name, * value) ;
66- }
67- instrument ( & mut cc) ;
68- let objects = cc. compile_intermediates ( ) ;
69- let [ object] = objects. as_slice ( ) else {
70- panic ! (
103+ let mut objects = Vec :: new ( ) ;
104+ for unit in side. units {
105+ for file in unit. files {
106+ println ! ( "cargo:rerun-if-changed={}" , file. display( ) ) ;
107+ }
108+ let mut cc = cc:: Build :: new ( ) ;
109+ for file in unit. files {
110+ cc. file ( file) ;
111+ }
112+ for dir in unit. includes {
113+ cc. include ( dir) ;
114+ }
115+ cc. warnings ( false ) ;
116+ for ( name, value) in unit. defines {
117+ cc. define ( name, * value) ;
118+ }
119+ if let Some ( header) = unit. forced_include {
120+ cc. flag ( "-include" ) . flag ( header) ;
121+ }
122+ instrument ( & mut cc) ;
123+ let unit_objects = cc. compile_intermediates ( ) ;
124+ assert_eq ! (
125+ unit_objects. len( ) ,
126+ unit. files. len( ) ,
71127 "{} compiled to {} objects" ,
72- side . source . display( ) ,
73- objects . len( )
128+ unit . files [ 0 ] . display( ) ,
129+ unit_objects . len( )
74130 ) ;
131+ objects. extend ( unit_objects) ;
132+ }
133+
134+ let combined = if objects. len ( ) == 1 {
135+ objects. remove ( 0 )
136+ } else {
137+ // Clang's .group sections break the final link once a partial link merges them.
138+ let linked: Vec < PathBuf > = objects
139+ . iter ( )
140+ . map ( |object| strip_group_sections ( object) )
141+ . collect ( ) ;
142+ let combined = out. join ( format ! ( "{}.combined.o" , side. prefix) ) ;
143+ // The per-global asan sections survive a bfd partial link, while a gold one
144+ // drops most of them and ASan then never registers the side's globals.
145+ run ( Command :: new ( tool ( "LD" , "ld" ) )
146+ . arg ( "-r" )
147+ . arg ( "-o" )
148+ . arg ( & combined)
149+ . args ( & linked) ) ;
150+ combined
75151 } ;
76152
77153 let map = out. join ( format ! ( "{}.redefine" , side. prefix) ) ;
78- fs:: write ( & map, redefinitions ( object , side. prefix ) ) . expect ( "OUT_DIR is writable" ) ;
154+ fs:: write ( & map, redefinitions ( & combined , side. prefix ) ) . expect ( "OUT_DIR is writable" ) ;
79155 let renamed = out. join ( format ! ( "{}.o" , side. prefix) ) ;
80156 run ( Command :: new ( tool ( "OBJCOPY" , "objcopy" ) )
81157 . arg ( format ! ( "--redefine-syms={}" , map. display( ) ) )
82- . arg ( object )
158+ . arg ( & combined )
83159 . arg ( & renamed) ) ;
84160
85161 let archive = out. join ( format ! ( "lib{}.a" , side. prefix) ) ;
86162 let _ = fs:: remove_file ( & archive) ;
87- run ( cc. get_archiver ( ) . arg ( "crs" ) . arg ( & archive) . arg ( & renamed) ) ;
163+ run ( Command :: new ( tool ( "AR" , "ar" ) )
164+ . arg ( "crs" )
165+ . arg ( & archive)
166+ . arg ( & renamed) ) ;
88167 println ! ( "cargo:rustc-link-lib=static={}" , side. prefix) ;
89168}
90169
@@ -118,6 +197,17 @@ fn redefinitions(object: &Path, prefix: &str) -> String {
118197 } )
119198}
120199
200+ /// Returns a copy of `object` without its `.group` section.
201+ fn strip_group_sections ( object : & Path ) -> PathBuf {
202+ let stem = object. file_stem ( ) . expect ( "object has a stem" ) ;
203+ let stripped = object. with_file_name ( format ! ( "{}.ng.o" , stem. to_string_lossy( ) ) ) ;
204+ run ( Command :: new ( tool ( "OBJCOPY" , "objcopy" ) )
205+ . arg ( "--remove-section=.group" )
206+ . arg ( object)
207+ . arg ( & stripped) ) ;
208+ stripped
209+ }
210+
121211fn tool ( var : & str , default : & str ) -> String {
122212 env:: var ( var) . unwrap_or_else ( |_| default. to_owned ( ) )
123213}
0 commit comments