Skip to content

Commit 8c6733b

Browse files
committed
Apply relocations manually in DWARF parsing.
Hopefully addresses #7480.
1 parent 06ee9fe commit 8c6733b

File tree

2 files changed

+234
-26
lines changed

2 files changed

+234
-26
lines changed

plugins/dwarf/shared/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,3 +11,4 @@ binaryninjacore-sys.workspace = true
1111
gimli = "0.31"
1212
zstd = "0.13.2"
1313
thiserror = "2.0"
14+
object = "0.36"

plugins/dwarf/shared/src/lib.rs

Lines changed: 233 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
// limitations under the License.
1414

1515
use gimli::{EndianRcSlice, Endianity, RunTimeEndian, SectionId};
16+
use object::{Object, ObjectSection, ObjectSymbol};
1617

1718
use 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

Comments
 (0)