Skip to content

Commit 16b7ef3

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

File tree

2 files changed

+234
-20
lines changed

2 files changed

+234
-20
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 & 20 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,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

Comments
 (0)