@@ -8,6 +8,7 @@ use std::{
8
8
9
9
use anyhow:: { anyhow, bail, Context , Result } ;
10
10
use argp:: FromArgs ;
11
+ use serde:: { Deserialize , Serialize } ;
11
12
12
13
use crate :: {
13
14
analysis:: {
@@ -24,6 +25,7 @@ use crate::{
24
25
util:: {
25
26
asm:: write_asm,
26
27
config:: { apply_splits, parse_symbol_line, write_splits, write_symbols} ,
28
+ dep:: DepFile ,
27
29
dol:: process_dol,
28
30
elf:: { process_elf, write_elf} ,
29
31
file:: { map_file, map_reader, touch} ,
@@ -60,25 +62,45 @@ pub struct InfoArgs {
60
62
#[ argp( subcommand, name = "split" ) ]
61
63
pub struct SplitArgs {
62
64
#[ argp( positional) ]
63
- /// input file
64
- in_file : PathBuf ,
65
+ /// input configuration file
66
+ config : PathBuf ,
65
67
#[ argp( positional) ]
66
68
/// output directory
67
69
out_dir : PathBuf ,
68
- #[ argp( option, short = 's' ) ]
69
- /// path to symbols file
70
- symbols_file : Option < PathBuf > ,
71
- #[ argp( option, short = 'p' ) ]
72
- /// path to splits file
73
- splits_file : Option < PathBuf > ,
74
- #[ argp( option, short = 'e' ) ]
75
- /// ELF file to validate against (debugging only)
76
- elf_file : Option < PathBuf > ,
77
70
#[ argp( switch) ]
78
71
/// skip updating splits & symbol files (for build systems)
79
72
no_update : bool ,
80
73
}
81
74
75
+ #[ inline]
76
+ fn bool_true ( ) -> bool { true }
77
+
78
+ #[ derive( Serialize , Deserialize , Debug , Clone ) ]
79
+ pub struct ProjectConfig {
80
+ pub object : PathBuf ,
81
+ pub splits : Option < PathBuf > ,
82
+ pub symbols : Option < PathBuf > ,
83
+ // Analysis options
84
+ #[ serde( default = "bool_true" ) ]
85
+ pub detect_objects : bool ,
86
+ #[ serde( default = "bool_true" ) ]
87
+ pub detect_strings : bool ,
88
+ #[ serde( default = "bool_true" ) ]
89
+ pub write_asm : bool ,
90
+ }
91
+
92
+ #[ derive( Serialize , Deserialize , Debug , Clone ) ]
93
+ pub struct OutputUnit {
94
+ pub object : PathBuf ,
95
+ pub name : String ,
96
+ pub autogenerated : bool ,
97
+ }
98
+
99
+ #[ derive( Serialize , Deserialize , Debug , Clone , Default ) ]
100
+ pub struct OutputConfig {
101
+ pub units : Vec < OutputUnit > ,
102
+ }
103
+
82
104
pub fn run ( args : Args ) -> Result < ( ) > {
83
105
match args. command {
84
106
SubCommand :: Info ( c_args) => info ( c_args) ,
@@ -128,10 +150,20 @@ fn info(args: InfoArgs) -> Result<()> {
128
150
}
129
151
130
152
fn split ( args : SplitArgs ) -> Result < ( ) > {
131
- log:: info!( "Loading {}" , args. in_file. display( ) ) ;
132
- let mut obj = process_dol ( & args. in_file ) ?;
153
+ log:: info!( "Loading {}" , args. config. display( ) ) ;
154
+ let mut config_file = File :: open ( & args. config )
155
+ . with_context ( || format ! ( "Failed to open config file '{}'" , args. config. display( ) ) ) ?;
156
+ let config: ProjectConfig = serde_yaml:: from_reader ( & mut config_file) ?;
133
157
134
- if let Some ( splits_path) = & args. splits_file {
158
+ let out_config_path = args. out_dir . join ( "config.json" ) ;
159
+ let mut dep = DepFile :: new ( out_config_path. clone ( ) ) ;
160
+
161
+ log:: info!( "Loading {}" , config. object. display( ) ) ;
162
+ let mut obj = process_dol ( & config. object ) ?;
163
+ dep. push ( config. object . clone ( ) ) ;
164
+
165
+ if let Some ( splits_path) = & config. splits {
166
+ dep. push ( splits_path. clone ( ) ) ;
135
167
if splits_path. is_file ( ) {
136
168
let map = map_file ( splits_path) ?;
137
169
apply_splits ( map_reader ( & map) , & mut obj) ?;
@@ -140,7 +172,8 @@ fn split(args: SplitArgs) -> Result<()> {
140
172
141
173
let mut state = AnalyzerState :: default ( ) ;
142
174
143
- if let Some ( symbols_path) = & args. symbols_file {
175
+ if let Some ( symbols_path) = & config. symbols {
176
+ dep. push ( symbols_path. clone ( ) ) ;
144
177
if symbols_path. is_file ( ) {
145
178
let map = map_file ( symbols_path) ?;
146
179
for result in map_reader ( & map) . lines ( ) {
@@ -176,22 +209,29 @@ fn split(args: SplitArgs) -> Result<()> {
176
209
log:: info!( "Applying relocations" ) ;
177
210
tracker. apply ( & mut obj, false ) ?;
178
211
179
- log:: info!( "Detecting object boundaries" ) ;
180
- detect_object_boundaries ( & mut obj) ?;
212
+ if config. detect_objects {
213
+ log:: info!( "Detecting object boundaries" ) ;
214
+ detect_object_boundaries ( & mut obj) ?;
215
+ }
181
216
182
- log:: info!( "Detecting strings" ) ;
183
- detect_strings ( & mut obj) ?;
217
+ if config. detect_strings {
218
+ log:: info!( "Detecting strings" ) ;
219
+ detect_strings ( & mut obj) ?;
220
+ }
221
+
222
+ log:: info!( "Adjusting splits" ) ;
223
+ update_splits ( & mut obj) ?;
184
224
185
225
if !args. no_update {
186
- if let Some ( symbols_path) = & args . symbols_file {
226
+ if let Some ( symbols_path) = & config . symbols {
187
227
let mut symbols_writer = BufWriter :: new (
188
228
File :: create ( symbols_path)
189
229
. with_context ( || format ! ( "Failed to create '{}'" , symbols_path. display( ) ) ) ?,
190
230
) ;
191
231
write_symbols ( & mut symbols_writer, & obj) ?;
192
232
}
193
233
194
- if let Some ( splits_path) = & args . splits_file {
234
+ if let Some ( splits_path) = & config . splits {
195
235
let mut splits_writer = BufWriter :: new (
196
236
File :: create ( splits_path)
197
237
. with_context ( || format ! ( "Failed to create '{}'" , splits_path. display( ) ) ) ?,
@@ -200,9 +240,6 @@ fn split(args: SplitArgs) -> Result<()> {
200
240
}
201
241
}
202
242
203
- log:: info!( "Adjusting splits" ) ;
204
- update_splits ( & mut obj) ?;
205
-
206
243
log:: info!( "Splitting {} objects" , obj. link_order. len( ) ) ;
207
244
let split_objs = split_obj ( & obj) ?;
208
245
@@ -224,13 +261,17 @@ fn split(args: SplitArgs) -> Result<()> {
224
261
} ;
225
262
}
226
263
227
- let mut units_file = BufWriter :: new ( File :: create ( args . out_dir . join ( "units.txt" ) ) ? ) ;
264
+ let mut out_config = OutputConfig :: default ( ) ;
228
265
for unit in & obj. link_order {
229
266
let object = file_map
230
267
. get ( unit)
231
268
. ok_or_else ( || anyhow ! ( "Failed to find object file for unit '{unit}'" ) ) ?;
232
269
let out_path = obj_dir. join ( obj_path_for_unit ( unit) ) ;
233
- writeln ! ( units_file, "{}:{}" , out_path. display( ) , unit) ?;
270
+ out_config. units . push ( OutputUnit {
271
+ object : out_path. clone ( ) ,
272
+ name : unit. clone ( ) ,
273
+ autogenerated : false ,
274
+ } ) ;
234
275
if let Some ( parent) = out_path. parent ( ) {
235
276
DirBuilder :: new ( ) . recursive ( true ) . create ( parent) ?;
236
277
}
@@ -239,7 +280,11 @@ fn split(args: SplitArgs) -> Result<()> {
239
280
file. write_all ( object) ?;
240
281
file. flush ( ) ?;
241
282
}
242
- units_file. flush ( ) ?;
283
+ {
284
+ let mut out_file = BufWriter :: new ( File :: create ( & out_config_path) ?) ;
285
+ serde_json:: to_writer_pretty ( & mut out_file, & out_config) ?;
286
+ out_file. flush ( ) ?;
287
+ }
243
288
244
289
// Generate ldscript.lcf
245
290
fs:: write ( args. out_dir . join ( "ldscript.lcf" ) , generate_ldscript ( & obj) ?) ?;
@@ -256,14 +301,26 @@ fn split(args: SplitArgs) -> Result<()> {
256
301
w. flush ( ) ?;
257
302
}
258
303
259
- // (debugging) validate against ELF
260
- if let Some ( file) = & args. elf_file {
261
- validate ( & obj, file, & state) ?;
304
+ // Write dep file
305
+ {
306
+ let dep_path = args. out_dir . join ( "dep" ) ;
307
+ let mut dep_file = BufWriter :: new (
308
+ File :: create ( & dep_path)
309
+ . with_context ( || format ! ( "Failed to create dep file '{}'" , dep_path. display( ) ) ) ?,
310
+ ) ;
311
+ dep. write ( & mut dep_file) ?;
312
+ dep_file. flush ( ) ?;
262
313
}
263
314
315
+ // (debugging) validate against ELF
316
+ // if let Some(file) = &args.elf_file {
317
+ // validate(&obj, file, &state)?;
318
+ // }
319
+
264
320
Ok ( ( ) )
265
321
}
266
322
323
+ #[ allow( dead_code) ]
267
324
fn validate < P : AsRef < Path > > ( obj : & ObjInfo , elf_file : P , state : & AnalyzerState ) -> Result < ( ) > {
268
325
let real_obj = process_elf ( elf_file) ?;
269
326
for real_section in & real_obj. sections {
0 commit comments