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,22 +168,233 @@ pub fn create_section_reader<'a, Endian: 'a + Endianity>(
172168 if len == 0 {
173169 Ok ( EndianRcSlice :: new ( Rc :: from ( [ ] ) , endian) )
174170 } else {
175- Ok ( EndianRcSlice :: new (
176- Rc :: from ( view. read_vec ( offset, len) . as_slice ( ) ) ,
177- endian,
178- ) )
171+ let mut data = view. read_vec ( offset, len) ;
172+ apply_relocations ( view, & section, & mut data) ;
173+ Ok ( EndianRcSlice :: new ( Rc :: from ( data. into_boxed_slice ( ) ) , endian) )
179174 }
180175 }
181176 // Truncate Mach-O section names to 16 bytes
182177 else if let Some ( section) = view. section_by_name ( & format ! (
183178 "__{}" ,
184179 & section_name[ 1 ..section_name. len( ) . min( 15 ) ]
185180 ) ) {
186- Ok ( EndianRcSlice :: new (
187- Rc :: from ( view. read_vec ( section. start ( ) , section. len ( ) ) . as_slice ( ) ) ,
188- endian,
189- ) )
181+ let mut data = view. read_vec ( section. start ( ) , section. len ( ) ) ;
182+ apply_relocations ( view, & section, & mut data) ;
183+ Ok ( EndianRcSlice :: new ( Rc :: from ( data. into_boxed_slice ( ) ) , endian) )
190184 } else {
191185 Ok ( EndianRcSlice :: new ( Rc :: from ( [ ] ) , endian) )
192186 }
193187}
188+
189+ fn apply_relocations (
190+ view : & BinaryView ,
191+ section : & binaryninja:: section:: Section ,
192+ data : & mut [ u8 ] ,
193+ ) {
194+ if data. is_empty ( ) {
195+ return ;
196+ }
197+
198+ let section_start = section. start ( ) ;
199+ let section_end = section. end ( ) ;
200+ let relocation_ranges = view. relocation_ranges ( ) ;
201+ let is_little_endian = matches ! ( view. default_endianness( ) , Endianness :: LittleEndian ) ;
202+ let section_name = section. name ( ) . to_string_lossy ( ) . into_owned ( ) ;
203+ let mut applied = false ;
204+
205+ for range in relocation_ranges {
206+ if range. end <= section_start || range. start >= section_end {
207+ continue ;
208+ }
209+
210+ let relocations = view. relocations_at ( range. start ) ;
211+ for relocation in relocations. iter ( ) {
212+ let info = relocation. info ( ) ;
213+
214+ if !info. data_relocation {
215+ continue ;
216+ }
217+
218+ let reloc_addr = info. address ;
219+ if reloc_addr < section_start {
220+ continue ;
221+ }
222+ let offset = ( reloc_addr - section_start) as usize ;
223+ if offset >= data. len ( ) {
224+ continue ;
225+ }
226+ if info. size == 0 || offset + info. size > data. len ( ) {
227+ log:: warn!(
228+ "Skipping relocation at {:#x} (size {}) for section {}" ,
229+ reloc_addr,
230+ info. size,
231+ section_name
232+ ) ;
233+ continue ;
234+ }
235+
236+ let existing = read_int ( & data[ offset..offset + info. size ] , is_little_endian) ;
237+ let mut value = info. target . wrapping_add ( info. addend as u64 ) ;
238+
239+ if info. implicit_addend {
240+ value = value. wrapping_add ( existing) ;
241+ }
242+
243+ if info. base_relative {
244+ value = value. wrapping_sub ( info. base ) ;
245+ }
246+
247+ if info. pc_relative {
248+ value = value. wrapping_sub ( reloc_addr. wrapping_add ( info. size as u64 ) ) ;
249+ }
250+
251+ if info. size > 8 {
252+ log:: warn!(
253+ "Skipping relocation at {:#x} with unsupported size {}" ,
254+ reloc_addr,
255+ info. size
256+ ) ;
257+ continue ;
258+ }
259+
260+ write_int ( & mut data[ offset..offset + info. size ] , value, is_little_endian) ;
261+ applied = true ;
262+ }
263+ }
264+
265+ if !applied {
266+ let _ = apply_relocations_with_object ( view, & section_name, data, is_little_endian) ;
267+ }
268+ }
269+
270+ fn read_int ( bytes : & [ u8 ] , is_little_endian : bool ) -> u64 {
271+ let mut value = 0u64 ;
272+ if is_little_endian {
273+ for ( i, byte) in bytes. iter ( ) . enumerate ( ) {
274+ value |= ( * byte as u64 ) << ( i * 8 ) ;
275+ }
276+ } else {
277+ for ( i, byte) in bytes. iter ( ) . enumerate ( ) {
278+ value |= ( * byte as u64 ) << ( 8 * ( bytes. len ( ) - 1 - i) ) ;
279+ }
280+ }
281+ value
282+ }
283+
284+ fn write_int ( bytes : & mut [ u8 ] , value : u64 , is_little_endian : bool ) {
285+ if is_little_endian {
286+ for ( i, byte) in bytes. iter_mut ( ) . enumerate ( ) {
287+ * byte = ( value >> ( i * 8 ) ) as u8 ;
288+ }
289+ } else {
290+ let len = bytes. len ( ) ;
291+ for ( i, byte) in bytes. iter_mut ( ) . enumerate ( ) {
292+ let shift = 8 * ( len - 1 - i) ;
293+ * byte = ( value >> shift) as u8 ;
294+ }
295+ }
296+ }
297+
298+ fn apply_relocations_with_object (
299+ view : & BinaryView ,
300+ section_name : & str ,
301+ data : & mut [ u8 ] ,
302+ is_little_endian : bool ,
303+ ) -> bool {
304+ let Some ( file_bytes) = read_entire_view ( view) else {
305+ return false ;
306+ } ;
307+
308+ let Ok ( file) = object:: File :: parse ( & * file_bytes) else {
309+ return false ;
310+ } ;
311+
312+ let Some ( obj_section) = file. section_by_name ( section_name) else {
313+ return false ;
314+ } ;
315+
316+ let mut applied = false ;
317+
318+ for ( offset, relocation) in obj_section. relocations ( ) {
319+ let size_bits = relocation. size ( ) ;
320+ if size_bits == 0 {
321+ continue ;
322+ }
323+ let size = ( size_bits / 8 ) as usize ;
324+ if size == 0 || size > 8 {
325+ continue ;
326+ }
327+
328+ let offset = offset as usize ;
329+ if offset + size > data. len ( ) {
330+ continue ;
331+ }
332+
333+ let mut base = 0i128 ;
334+ let mut target_section_name: Option < & str > = None ;
335+
336+ match relocation. target ( ) {
337+ object:: RelocationTarget :: Symbol ( symbol_index) => {
338+ let Ok ( symbol) = file. symbol_by_index ( symbol_index) else {
339+ continue ;
340+ } ;
341+ if let Some ( section_index) = symbol. section_index ( ) {
342+ let Ok ( target_section) = file. section_by_index ( section_index) else {
343+ continue ;
344+ } ;
345+ if let Ok ( name) = target_section. name ( ) {
346+ target_section_name = Some ( name) ;
347+ }
348+ base += target_section. address ( ) as i128 ;
349+ }
350+ base += symbol. address ( ) as i128 ;
351+ }
352+ object:: RelocationTarget :: Section ( section_index) => {
353+ let Ok ( target_section) = file. section_by_index ( section_index) else {
354+ continue ;
355+ } ;
356+ if let Ok ( name) = target_section. name ( ) {
357+ target_section_name = Some ( name) ;
358+ }
359+ base += target_section. address ( ) as i128 ;
360+ }
361+ _ => { }
362+ }
363+
364+ if let Some ( name) = target_section_name {
365+ if !is_debug_related_section ( name) {
366+ continue ;
367+ }
368+ } else {
369+ continue ;
370+ }
371+
372+ if relocation. kind ( ) != object:: RelocationKind :: Absolute {
373+ continue ;
374+ }
375+
376+ let _existing = read_int ( & data[ offset..offset + size] , is_little_endian) as i128 ;
377+ let addend = relocation. addend ( ) as i128 ;
378+ let value = base. wrapping_add ( addend) as u64 ;
379+ write_int ( & mut data[ offset..offset + size] , value, is_little_endian) ;
380+ applied = true ;
381+ }
382+
383+ applied
384+ }
385+
386+ fn read_entire_view ( view : & BinaryView ) -> Option < Vec < u8 > > {
387+ let len = view. len ( ) ;
388+ if len == 0 || len > usize:: MAX as u64 {
389+ return None ;
390+ }
391+ let data = view. read_vec ( 0 , len as usize ) ;
392+ if data. len ( ) as u64 != len {
393+ return None ;
394+ }
395+ Some ( data)
396+ }
397+
398+ fn is_debug_related_section ( name : & str ) -> bool {
399+ name. starts_with ( ".debug" ) || name. starts_with ( ".zdebug" )
400+ }
0 commit comments