1313// limitations under the License.
1414
1515use gimli:: { EndianRcSlice , Endianity , RunTimeEndian , SectionId } ;
16+ use object:: { Object , ObjectSection , ObjectSymbol } ;
1617
1718use binaryninja:: {
1819 binary_view:: { BinaryView , BinaryViewBase , BinaryViewExt } ,
@@ -135,7 +136,7 @@ pub fn create_section_reader<'a, Endian: 'a + Endianity>(
135136 } ;
136137 // If the section has the compressed bit set
137138 if ( section_flags & 2048 ) != 0 {
138- // Get section, trim header, decompress, return
139+ // Get section, trim header, decompress, and apply relocations before returning
139140 let compressed_header_size = view. address_size ( ) * 3 ;
140141
141142 let offset = section. start ( ) + compressed_header_size as u64 ;
@@ -145,23 +146,18 @@ pub fn create_section_reader<'a, Endian: 'a + Endianity>(
145146 let ch_type = endian. read_u32 ( & ch_type_vec) ;
146147
147148 if let Ok ( buffer) = view. read_buffer ( offset, len) {
148- match ch_type {
149- 1 => {
150- return Ok ( EndianRcSlice :: new (
151- buffer. zlib_decompress ( ) . get_data ( ) . into ( ) ,
152- endian,
153- ) ) ;
154- }
155- 2 => {
156- return Ok ( EndianRcSlice :: new (
157- zstd:: decode_all ( buffer. get_data ( ) ) ?. as_slice ( ) . into ( ) ,
158- endian,
159- ) ) ;
160- }
161- x => {
162- return Err ( Error :: UnknownCompressionMethod ( x) ) ;
163- }
164- }
149+ let mut decompressed = match ch_type {
150+ 1 => buffer. zlib_decompress ( ) . get_data ( ) . to_vec ( ) ,
151+ 2 => zstd:: decode_all ( buffer. get_data ( ) ) ?,
152+ x => return Err ( Error :: UnknownCompressionMethod ( x) ) ,
153+ } ;
154+
155+ apply_relocations ( view, & section, & mut decompressed) ;
156+
157+ return Ok ( EndianRcSlice :: new (
158+ Rc :: from ( decompressed. into_boxed_slice ( ) ) ,
159+ endian,
160+ ) ) ;
165161 }
166162 }
167163 }
@@ -172,8 +168,10 @@ pub fn create_section_reader<'a, Endian: 'a + Endianity>(
172168 if len == 0 {
173169 Ok ( EndianRcSlice :: new ( Rc :: from ( [ ] ) , endian) )
174170 } else {
171+ let mut data = view. read_vec ( offset, len) ;
172+ apply_relocations ( view, & section, & mut data) ;
175173 Ok ( EndianRcSlice :: new (
176- Rc :: from ( view . read_vec ( offset , len ) . as_slice ( ) ) ,
174+ Rc :: from ( data . into_boxed_slice ( ) ) ,
177175 endian,
178176 ) )
179177 }
@@ -183,11 +181,226 @@ pub fn create_section_reader<'a, Endian: 'a + Endianity>(
183181 "__{}" ,
184182 & section_name[ 1 ..section_name. len( ) . min( 15 ) ]
185183 ) ) {
184+ let mut data = view. read_vec ( section. start ( ) , section. len ( ) ) ;
185+ apply_relocations ( view, & section, & mut data) ;
186186 Ok ( EndianRcSlice :: new (
187- Rc :: from ( view . read_vec ( section . start ( ) , section . len ( ) ) . as_slice ( ) ) ,
187+ Rc :: from ( data . into_boxed_slice ( ) ) ,
188188 endian,
189189 ) )
190190 } else {
191191 Ok ( EndianRcSlice :: new ( Rc :: from ( [ ] ) , endian) )
192192 }
193193}
194+
195+ fn apply_relocations ( view : & BinaryView , section : & binaryninja:: section:: Section , data : & mut [ u8 ] ) {
196+ if data. is_empty ( ) {
197+ return ;
198+ }
199+
200+ let section_start = section. start ( ) ;
201+ let section_end = section. end ( ) ;
202+ let relocation_ranges = view. relocation_ranges ( ) ;
203+ let is_little_endian = matches ! ( view. default_endianness( ) , Endianness :: LittleEndian ) ;
204+ let section_name = section. name ( ) . to_string_lossy ( ) . into_owned ( ) ;
205+ let mut applied = false ;
206+
207+ for range in relocation_ranges {
208+ if range. end <= section_start || range. start >= section_end {
209+ continue ;
210+ }
211+
212+ let relocations = view. relocations_at ( range. start ) ;
213+ for relocation in relocations. iter ( ) {
214+ let info = relocation. info ( ) ;
215+
216+ if !info. data_relocation {
217+ continue ;
218+ }
219+
220+ let reloc_addr = info. address ;
221+ if reloc_addr < section_start {
222+ continue ;
223+ }
224+ let offset = ( reloc_addr - section_start) as usize ;
225+ if offset >= data. len ( ) {
226+ continue ;
227+ }
228+ if info. size == 0 || offset + info. size > data. len ( ) {
229+ log:: warn!(
230+ "Skipping relocation at {:#x} (size {}) for section {}" ,
231+ reloc_addr,
232+ info. size,
233+ section_name
234+ ) ;
235+ continue ;
236+ }
237+
238+ let existing = read_int ( & data[ offset..offset + info. size ] , is_little_endian) ;
239+ let mut value = info. target . wrapping_add ( info. addend as u64 ) ;
240+
241+ if info. implicit_addend {
242+ value = value. wrapping_add ( existing) ;
243+ }
244+
245+ if info. base_relative {
246+ value = value. wrapping_sub ( info. base ) ;
247+ }
248+
249+ if info. pc_relative {
250+ value = value. wrapping_sub ( reloc_addr. wrapping_add ( info. size as u64 ) ) ;
251+ }
252+
253+ if info. size > 8 {
254+ log:: warn!(
255+ "Skipping relocation at {:#x} with unsupported size {}" ,
256+ reloc_addr,
257+ info. size
258+ ) ;
259+ continue ;
260+ }
261+
262+ write_int (
263+ & mut data[ offset..offset + info. size ] ,
264+ value,
265+ is_little_endian,
266+ ) ;
267+ applied = true ;
268+ }
269+ }
270+
271+ if !applied {
272+ let _ = apply_relocations_with_object ( view, & section_name, data, is_little_endian) ;
273+ }
274+ }
275+
276+ fn read_int ( bytes : & [ u8 ] , is_little_endian : bool ) -> u64 {
277+ let mut value = 0u64 ;
278+ if is_little_endian {
279+ for ( i, byte) in bytes. iter ( ) . enumerate ( ) {
280+ value |= ( * byte as u64 ) << ( i * 8 ) ;
281+ }
282+ } else {
283+ for ( i, byte) in bytes. iter ( ) . enumerate ( ) {
284+ value |= ( * byte as u64 ) << ( 8 * ( bytes. len ( ) - 1 - i) ) ;
285+ }
286+ }
287+ value
288+ }
289+
290+ fn write_int ( bytes : & mut [ u8 ] , value : u64 , is_little_endian : bool ) {
291+ if is_little_endian {
292+ for ( i, byte) in bytes. iter_mut ( ) . enumerate ( ) {
293+ * byte = ( value >> ( i * 8 ) ) as u8 ;
294+ }
295+ } else {
296+ let len = bytes. len ( ) ;
297+ for ( i, byte) in bytes. iter_mut ( ) . enumerate ( ) {
298+ let shift = 8 * ( len - 1 - i) ;
299+ * byte = ( value >> shift) as u8 ;
300+ }
301+ }
302+ }
303+
304+ fn apply_relocations_with_object (
305+ view : & BinaryView ,
306+ section_name : & str ,
307+ data : & mut [ u8 ] ,
308+ is_little_endian : bool ,
309+ ) -> bool {
310+ let Some ( file_bytes) = read_entire_view ( view) else {
311+ return false ;
312+ } ;
313+
314+ let Ok ( file) = object:: File :: parse ( & * file_bytes) else {
315+ return false ;
316+ } ;
317+
318+ let Some ( obj_section) = file. section_by_name ( section_name) else {
319+ return false ;
320+ } ;
321+
322+ let mut applied = false ;
323+
324+ for ( offset, relocation) in obj_section. relocations ( ) {
325+ let size_bits = relocation. size ( ) ;
326+ if size_bits == 0 {
327+ continue ;
328+ }
329+ let size = ( size_bits / 8 ) as usize ;
330+ if size == 0 || size > 8 {
331+ continue ;
332+ }
333+
334+ let offset = offset as usize ;
335+ if offset + size > data. len ( ) {
336+ continue ;
337+ }
338+
339+ let mut base = 0i128 ;
340+ let mut target_section_name: Option < & str > = None ;
341+
342+ match relocation. target ( ) {
343+ object:: RelocationTarget :: Symbol ( symbol_index) => {
344+ let Ok ( symbol) = file. symbol_by_index ( symbol_index) else {
345+ continue ;
346+ } ;
347+ if let Some ( section_index) = symbol. section_index ( ) {
348+ let Ok ( target_section) = file. section_by_index ( section_index) else {
349+ continue ;
350+ } ;
351+ if let Ok ( name) = target_section. name ( ) {
352+ target_section_name = Some ( name) ;
353+ }
354+ base += target_section. address ( ) as i128 ;
355+ }
356+ base += symbol. address ( ) as i128 ;
357+ }
358+ object:: RelocationTarget :: Section ( section_index) => {
359+ let Ok ( target_section) = file. section_by_index ( section_index) else {
360+ continue ;
361+ } ;
362+ if let Ok ( name) = target_section. name ( ) {
363+ target_section_name = Some ( name) ;
364+ }
365+ base += target_section. address ( ) as i128 ;
366+ }
367+ _ => { }
368+ }
369+
370+ if let Some ( name) = target_section_name {
371+ if !is_debug_related_section ( name) {
372+ continue ;
373+ }
374+ } else {
375+ continue ;
376+ }
377+
378+ if relocation. kind ( ) != object:: RelocationKind :: Absolute {
379+ continue ;
380+ }
381+
382+ let _existing = read_int ( & data[ offset..offset + size] , is_little_endian) as i128 ;
383+ let addend = relocation. addend ( ) as i128 ;
384+ let value = base. wrapping_add ( addend) as u64 ;
385+ write_int ( & mut data[ offset..offset + size] , value, is_little_endian) ;
386+ applied = true ;
387+ }
388+
389+ applied
390+ }
391+
392+ fn read_entire_view ( view : & BinaryView ) -> Option < Vec < u8 > > {
393+ let len = view. len ( ) ;
394+ if len == 0 || len > usize:: MAX as u64 {
395+ return None ;
396+ }
397+ let data = view. read_vec ( 0 , len as usize ) ;
398+ if data. len ( ) as u64 != len {
399+ return None ;
400+ }
401+ Some ( data)
402+ }
403+
404+ fn is_debug_related_section ( name : & str ) -> bool {
405+ name. starts_with ( ".debug" ) || name. starts_with ( ".zdebug" )
406+ }
0 commit comments