@@ -11,6 +11,25 @@ use crate::config::error::CrackersConfigError;
1111use crate :: config:: object:: load_sleigh;
1212use crate :: config:: sleigh:: SleighConfig ;
1313use crate :: gadget:: library:: GadgetLibrary ;
14+ use tracing:: { Level , event} ;
15+
16+ const LIB_ALIGNMENT : u64 = 0x4000 ; // 16 KiB alignment for loaded libraries
17+ const LIB_GAP : u64 = 0x1000 ; // small gap between libraries when placing
18+
19+ fn align_up ( x : u64 , align : u64 ) -> u64 {
20+ if align == 0 {
21+ return x;
22+ }
23+ x. div_ceil ( align) * align
24+ }
25+
26+ #[ derive( Clone , Debug , Default , Builder , Deserialize , Serialize ) ]
27+ #[ builder( default ) ]
28+ #[ cfg_attr( feature = "pyo3" , pyclass) ]
29+ pub struct LoadedLibraryConfig {
30+ pub path : String ,
31+ pub base_address : Option < u64 > ,
32+ }
1433
1534#[ derive( Clone , Debug , Default , Builder , Deserialize , Serialize ) ]
1635#[ builder( default ) ]
@@ -22,15 +41,104 @@ pub struct GadgetLibraryConfig {
2241 pub path : String ,
2342 pub sample_size : Option < usize > ,
2443 pub base_address : Option < u64 > ,
44+ /// Additional libraries to load alongside the primary one. Each entry may
45+ /// optionally specify a base address. If no base address is provided the
46+ /// builder will attempt to place the library in an address region that does
47+ /// not conflict with the main library or previously placed libraries.
48+ pub loaded_libraries : Option < Vec < LoadedLibraryConfig > > ,
2549}
2650
2751impl GadgetLibraryConfig {
2852 pub fn build ( & self , sleigh : & SleighConfig ) -> Result < GadgetLibrary , CrackersConfigError > {
2953 let mut library_sleigh = load_sleigh ( & self . path , sleigh) ?;
3054 if let Some ( addr) = self . base_address {
31- library_sleigh. set_base_address ( addr)
55+ let aligned = align_up ( addr, LIB_ALIGNMENT ) ;
56+ if aligned != addr {
57+ event ! (
58+ Level :: WARN ,
59+ "Main library base address {:#x} is not {:#x}-aligned; aligning to {:#x}" ,
60+ addr,
61+ LIB_ALIGNMENT ,
62+ aligned
63+ ) ;
64+ }
65+ library_sleigh. set_base_address ( aligned)
3266 }
33- GadgetLibrary :: build_from_image ( library_sleigh, self ) . map_err ( CrackersConfigError :: Sleigh )
67+
68+ // Prepare a vector of sleigh contexts (main + any additional libraries)
69+ // Start with the primary library context.
70+ let mut sleighs = vec ! [ library_sleigh] ;
71+
72+ // If there are additional libraries to load, load them and
73+ // assign base addresses so they do not conflict with the main
74+ // library or each other.
75+ if let Some ( loaded) = & self . loaded_libraries {
76+ // Collect occupied ranges from the main library (first entry in `sleighs`)
77+ let mut occupied: Vec < ( u64 , u64 ) > = sleighs[ 0 ]
78+ . get_sections ( )
79+ . map ( |s| {
80+ let start = s. base_address as u64 ;
81+ let end = start + s. data . len ( ) as u64 ;
82+ ( start, end)
83+ } )
84+ . collect ( ) ;
85+
86+ let mut current_max: u64 = occupied. iter ( ) . map ( |( _, e) | * e) . max ( ) . unwrap_or ( 0 ) ;
87+
88+ for cfg in loaded {
89+ let mut other = load_sleigh ( & cfg. path , sleigh) ?;
90+ // Use module-level alignment constants
91+ if let Some ( addr) = cfg. base_address {
92+ // If user provided a base address, ensure it is aligned to LIB_ALIGNMENT.
93+ let aligned = align_up ( addr, LIB_ALIGNMENT ) ;
94+ if aligned != addr {
95+ event ! (
96+ Level :: WARN ,
97+ "Provided base address {:#x} for '{}' is not {:#x}-aligned; adjusting to {:#x}" ,
98+ addr,
99+ cfg. path,
100+ LIB_ALIGNMENT ,
101+ aligned
102+ ) ;
103+ }
104+ other. set_base_address ( aligned) ;
105+ } else {
106+ // Place the library after the current known max address with a small gap,
107+ // then align up to LIB_ALIGNMENT to guarantee alignment.
108+ let base_hint = if current_max == 0 {
109+ LIB_GAP
110+ } else {
111+ current_max + LIB_GAP
112+ } ;
113+ let candidate = align_up ( base_hint, LIB_ALIGNMENT ) ;
114+ event ! (
115+ Level :: INFO ,
116+ "Auto-placing '{}' at aligned address {:#x} (base hint {:#x})" ,
117+ cfg. path,
118+ candidate,
119+ base_hint
120+ ) ;
121+ other. set_base_address ( candidate) ;
122+ }
123+
124+ // Update occupied ranges and current_max with this library's sections
125+ for s in other. get_sections ( ) {
126+ let start = s. base_address as u64 ;
127+ let end = start + s. data . len ( ) as u64 ;
128+ occupied. push ( ( start, end) ) ;
129+ if end > current_max {
130+ current_max = end;
131+ }
132+ }
133+
134+ // Keep the loaded context so we can pass all contexts to the
135+ // gadget library builder.
136+ sleighs. push ( other) ;
137+ }
138+ }
139+
140+ // Build gadget library from all provided sleigh contexts.
141+ GadgetLibrary :: build_from_image ( sleighs, self ) . map_err ( CrackersConfigError :: Sleigh )
34142 }
35143}
36144
@@ -69,11 +177,29 @@ fn default_blacklist() -> HashSet<OpCode> {
69177 ] )
70178}
71179
72- /**
180+ #[ cfg( feature = "pyo3" ) ]
181+ #[ pymethods]
182+ impl LoadedLibraryConfig {
183+ #[ getter]
184+ pub fn get_path ( & self ) -> & str {
185+ self . path . as_str ( )
186+ }
187+
188+ #[ setter]
189+ pub fn set_path ( & mut self , p : String ) {
190+ self . path = p;
191+ }
73192
74- pub sample_size: Option<usize>,
75- pub base_address: Option<u64>,
76- */
193+ #[ getter]
194+ pub fn get_base_address ( & self ) -> Option < u64 > {
195+ self . base_address
196+ }
197+
198+ #[ setter]
199+ pub fn set_base_address ( & mut self , a : Option < u64 > ) {
200+ self . base_address = a;
201+ }
202+ }
77203
78204#[ cfg( feature = "pyo3" ) ]
79205#[ pymethods]
@@ -117,4 +243,14 @@ impl GadgetLibraryConfig {
117243 pub fn set_base_address ( & mut self , l : Option < u64 > ) {
118244 self . base_address = l;
119245 }
246+
247+ #[ getter]
248+ pub fn get_loaded_libraries ( & self ) -> Option < Vec < LoadedLibraryConfig > > {
249+ self . loaded_libraries . clone ( )
250+ }
251+
252+ #[ setter]
253+ pub fn set_loaded_libraries ( & mut self , l : Option < Vec < LoadedLibraryConfig > > ) {
254+ self . loaded_libraries = l;
255+ }
120256}
0 commit comments