@@ -3,6 +3,7 @@ use alloc::{
33 collections:: BTreeMap ,
44 format,
55 string:: { String , ToString } ,
6+ vec,
67 vec:: Vec ,
78} ;
89use core:: { cmp:: Ordering , num:: NonZeroU64 } ;
@@ -35,6 +36,46 @@ fn map_section_kind(section: &object::Section) -> SectionKind {
3536 }
3637}
3738
39+ /// Check if a symbol's name is partially compiler-generated, and if so normalize it for pairing.
40+ /// e.g. symbol$1234 and symbol$2345 will both be replaced with symbol$0000 internally.
41+ fn get_normalized_symbol_name ( name : & str ) -> Option < String > {
42+ const DUMMY_UNIQUE_ID : & str = "0000" ;
43+ if let Some ( ( prefix, suffix) ) = name. split_once ( "@class$" )
44+ && let Some ( idx) = suffix. chars ( ) . position ( |c| !c. is_numeric ( ) )
45+ && idx > 0
46+ {
47+ // Match Metrowerks anonymous class symbol names, ignoring the unique ID.
48+ // e.g. __dt__Q29dCamera_c23@class$3665d_camera_cppFv
49+ // and: __dt__Q29dCamera_c23@class$1727d_camera_cppFv
50+ let suffix = & suffix[ idx..] ;
51+ Some ( format ! ( "{prefix}@class${DUMMY_UNIQUE_ID}{suffix}" ) )
52+ } else if let Some ( ( prefix, suffix) ) = name. split_once ( '$' )
53+ && suffix. chars ( ) . all ( char:: is_numeric)
54+ {
55+ // Match Metrowerks symbol$1234 against symbol$2345
56+ Some ( format ! ( "{prefix}${DUMMY_UNIQUE_ID}" ) )
57+ } else if let Some ( ( prefix, suffix) ) = name. split_once ( '.' )
58+ && suffix. chars ( ) . all ( char:: is_numeric)
59+ {
60+ // Match GCC symbol.1234 against symbol.2345
61+ Some ( format ! ( "{prefix}.{DUMMY_UNIQUE_ID}" ) )
62+ } else {
63+ None
64+ }
65+ }
66+
67+ /// Check if a symbol's name is entirely compiler-generated, such as @1234 or _$E1234.
68+ /// This enables pairing these symbols up by their value instead of their name.
69+ fn is_symbol_name_compiler_generated ( name : & str ) -> bool {
70+ if name. starts_with ( '@' ) && name[ 1 ..] . chars ( ) . all ( char:: is_numeric) {
71+ // Exclude @stringBase0, @GUARD@, etc.
72+ return true ;
73+ } else if name. starts_with ( "_$E" ) && name[ 3 ..] . chars ( ) . all ( char:: is_numeric) {
74+ return true ;
75+ }
76+ false
77+ }
78+
3879fn map_symbol (
3980 arch : & dyn Arch ,
4081 file : & object:: File ,
@@ -97,10 +138,15 @@ fn map_symbol(
97138 . and_then ( |m| m. virtual_addresses . as_ref ( ) )
98139 . and_then ( |v| v. get ( symbol. index ( ) . 0 ) . cloned ( ) ) ;
99140 let section = symbol. section_index ( ) . and_then ( |i| section_indices. get ( i. 0 ) . copied ( ) ) ;
141+ let normalized_name = get_normalized_symbol_name ( & name) ;
142+ if is_symbol_name_compiler_generated ( & name) {
143+ flags |= SymbolFlag :: CompilerGenerated ;
144+ }
100145
101146 Ok ( Symbol {
102147 name,
103148 demangled_name,
149+ normalized_name,
104150 address,
105151 size,
106152 kind,
@@ -119,13 +165,38 @@ fn map_symbols(
119165 split_meta : Option < & SplitMeta > ,
120166 config : & DiffObjConfig ,
121167) -> Result < ( Vec < Symbol > , Vec < usize > ) > {
122- let symbol_count = obj_file. symbols ( ) . count ( ) ;
123- let mut symbols = Vec :: < Symbol > :: with_capacity ( symbol_count + obj_file. sections ( ) . count ( ) ) ;
124- let mut symbol_indices = Vec :: < usize > :: with_capacity ( symbol_count + 1 ) ;
125- for obj_symbol in obj_file. symbols ( ) {
126- if symbol_indices. len ( ) <= obj_symbol. index ( ) . 0 {
127- symbol_indices. resize ( obj_symbol. index ( ) . 0 + 1 , usize:: MAX ) ;
128- }
168+ // symbols() is not guaranteed to be sorted by address.
169+ // We sort it here to fix pairing bugs with diff algorithms that assume the symbols are ordered.
170+ // Sorting everything here once is less expensive than sorting subsets later in expensive loops.
171+ let mut max_index = 0 ;
172+ let mut obj_symbols = obj_file
173+ . symbols ( )
174+ . filter ( |s| s. kind ( ) != object:: SymbolKind :: File )
175+ . inspect ( |sym| max_index = max_index. max ( sym. index ( ) . 0 ) )
176+ . collect :: < Vec < _ > > ( ) ;
177+ obj_symbols. sort_by ( |a, b| {
178+ // Sort symbols by section index, placing absolute symbols last
179+ a. section_index ( )
180+ . map_or ( usize:: MAX , |s| s. 0 )
181+ . cmp ( & b. section_index ( ) . map_or ( usize:: MAX , |s| s. 0 ) )
182+ . then_with ( || {
183+ // Sort section symbols first in a section
184+ if a. kind ( ) == object:: SymbolKind :: Section {
185+ Ordering :: Less
186+ } else if b. kind ( ) == object:: SymbolKind :: Section {
187+ Ordering :: Greater
188+ } else {
189+ Ordering :: Equal
190+ }
191+ } )
192+ // Sort by address within section
193+ . then_with ( || a. address ( ) . cmp ( & b. address ( ) ) )
194+ // If there are multiple symbols with the same address, smaller symbol first
195+ . then_with ( || a. size ( ) . cmp ( & b. size ( ) ) )
196+ } ) ;
197+ let mut symbols = Vec :: < Symbol > :: with_capacity ( obj_symbols. len ( ) + obj_file. sections ( ) . count ( ) ) ;
198+ let mut symbol_indices = vec ! [ usize :: MAX ; max_index + 1 ] ;
199+ for obj_symbol in obj_symbols {
129200 let symbol = map_symbol ( arch, obj_file, & obj_symbol, section_indices, split_meta, config) ?;
130201 symbol_indices[ obj_symbol. index ( ) . 0 ] = symbols. len ( ) ;
131202 symbols. push ( symbol) ;
@@ -172,6 +243,7 @@ fn add_section_symbols(sections: &[Section], symbols: &mut Vec<Symbol>) {
172243 symbols. push ( Symbol {
173244 name,
174245 demangled_name : None ,
246+ normalized_name : None ,
175247 address : 0 ,
176248 size,
177249 kind : SymbolKind :: Section ,
@@ -193,40 +265,18 @@ fn is_local_label(symbol: &Symbol) -> bool {
193265}
194266
195267fn infer_symbol_sizes ( arch : & dyn Arch , symbols : & mut [ Symbol ] , sections : & [ Section ] ) -> Result < ( ) > {
196- // Create a sorted list of symbol indices by section
197- let mut symbols_with_section = Vec :: < usize > :: with_capacity ( symbols. len ( ) ) ;
198- for ( i, symbol) in symbols. iter ( ) . enumerate ( ) {
199- if symbol. section . is_some ( ) {
200- symbols_with_section. push ( i) ;
201- }
202- }
203- symbols_with_section. sort_by ( |a, b| {
204- let a = & symbols[ * a] ;
205- let b = & symbols[ * b] ;
206- a. section
207- . unwrap_or ( usize:: MAX )
208- . cmp ( & b. section . unwrap_or ( usize:: MAX ) )
209- . then_with ( || {
210- // Sort section symbols first
211- if a. kind == SymbolKind :: Section {
212- Ordering :: Less
213- } else if b. kind == SymbolKind :: Section {
214- Ordering :: Greater
215- } else {
216- Ordering :: Equal
217- }
218- } )
219- . then_with ( || a. address . cmp ( & b. address ) )
220- . then_with ( || a. size . cmp ( & b. size ) )
221- } ) ;
268+ // Above, we've sorted the symbols by section and then by address.
222269
223270 // Set symbol sizes based on the next symbol's address
224271 let mut iter_idx = 0 ;
225272 let mut last_end = ( 0 , 0 ) ;
226- while iter_idx < symbols_with_section . len ( ) {
227- let symbol_idx = symbols_with_section [ iter_idx] ;
273+ while iter_idx < symbols . len ( ) {
274+ let symbol_idx = iter_idx;
228275 let symbol = & symbols[ symbol_idx] ;
229- let section_idx = symbol. section . unwrap ( ) ;
276+ let Some ( section_idx) = symbol. section else {
277+ // Start of absolute symbols
278+ break ;
279+ } ;
230280 iter_idx += 1 ;
231281 if symbol. size != 0 {
232282 if symbol. kind != SymbolKind :: Section {
@@ -239,10 +289,9 @@ fn infer_symbol_sizes(arch: &dyn Arch, symbols: &mut [Symbol], sections: &[Secti
239289 continue ;
240290 }
241291 let next_symbol = loop {
242- if iter_idx >= symbols_with_section . len ( ) {
292+ let Some ( next_symbol ) = symbols . get ( iter_idx ) else {
243293 break None ;
244- }
245- let next_symbol = & symbols[ symbols_with_section[ iter_idx] ] ;
294+ } ;
246295 if next_symbol. section != Some ( section_idx) {
247296 break None ;
248297 }
@@ -298,9 +347,11 @@ fn map_sections(
298347 split_meta : Option < & SplitMeta > ,
299348) -> Result < ( Vec < Section > , Vec < usize > ) > {
300349 let mut section_names = BTreeMap :: < String , usize > :: new ( ) ;
301- let section_count = obj_file. sections ( ) . count ( ) ;
350+ let mut max_index = 0 ;
351+ let section_count =
352+ obj_file. sections ( ) . inspect ( |s| max_index = max_index. max ( s. index ( ) . 0 ) ) . count ( ) ;
302353 let mut result = Vec :: < Section > :: with_capacity ( section_count) ;
303- let mut section_indices = Vec :: < usize > :: with_capacity ( section_count + 1 ) ;
354+ let mut section_indices = vec ! [ usize :: MAX ; max_index + 1 ] ;
304355 for section in obj_file. sections ( ) {
305356 let name = section. name ( ) . context ( "Failed to process section name" ) ?;
306357 let kind = map_section_kind ( & section) ;
@@ -325,9 +376,6 @@ fn map_sections(
325376 let id = format ! ( "{name}-{unique_id}" ) ;
326377 * unique_id += 1 ;
327378
328- if section_indices. len ( ) <= section. index ( ) . 0 {
329- section_indices. resize ( section. index ( ) . 0 + 1 , usize:: MAX ) ;
330- }
331379 section_indices[ section. index ( ) . 0 ] = result. len ( ) ;
332380 result. push ( Section {
333381 id,
0 commit comments