Skip to content

Commit 014dcc8

Browse files
committed
Apply relocations manually in DWARF parsing.
Hopefully addresses #7480.
1 parent 3f39c3f commit 014dcc8

File tree

3 files changed

+181
-21
lines changed

3 files changed

+181
-21
lines changed

Cargo.lock

Lines changed: 13 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

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: 167 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,26 @@ 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+
let section_name_owned = section.name().to_string_lossy().into_owned();
156+
let is_little =
157+
matches!(view.default_endianness(), Endianness::LittleEndian);
158+
apply_relocations_with_object(
159+
view,
160+
&section_name_owned,
161+
&mut decompressed,
162+
is_little,
163+
);
164+
165+
return Ok(EndianRcSlice::new(
166+
Rc::from(decompressed.into_boxed_slice()),
167+
endian,
168+
));
165169
}
166170
}
167171
}
@@ -172,8 +176,12 @@ pub fn create_section_reader<'a, Endian: 'a + Endianity>(
172176
if len == 0 {
173177
Ok(EndianRcSlice::new(Rc::from([]), endian))
174178
} else {
179+
let mut data = view.read_vec(offset, len);
180+
let section_name_owned = section.name().to_string_lossy().into_owned();
181+
let is_little = matches!(view.default_endianness(), Endianness::LittleEndian);
182+
apply_relocations_with_object(view, &section_name_owned, &mut data, is_little);
175183
Ok(EndianRcSlice::new(
176-
Rc::from(view.read_vec(offset, len).as_slice()),
184+
Rc::from(data.into_boxed_slice()),
177185
endian,
178186
))
179187
}
@@ -183,11 +191,150 @@ pub fn create_section_reader<'a, Endian: 'a + Endianity>(
183191
"__{}",
184192
&section_name[1..section_name.len().min(15)]
185193
)) {
194+
let mut data = view.read_vec(section.start(), section.len());
195+
apply_relocations_with_object(
196+
view,
197+
section.name().to_string_lossy().as_ref(),
198+
&mut data,
199+
matches!(view.default_endianness(), Endianness::LittleEndian),
200+
);
186201
Ok(EndianRcSlice::new(
187-
Rc::from(view.read_vec(section.start(), section.len()).as_slice()),
202+
Rc::from(data.into_boxed_slice()),
188203
endian,
189204
))
190205
} else {
191206
Ok(EndianRcSlice::new(Rc::from([]), endian))
192207
}
193208
}
209+
210+
fn read_int(bytes: &[u8], is_little_endian: bool) -> u64 {
211+
let mut value = 0u64;
212+
if is_little_endian {
213+
for (i, byte) in bytes.iter().enumerate() {
214+
value |= (*byte as u64) << (i * 8);
215+
}
216+
} else {
217+
for (i, byte) in bytes.iter().enumerate() {
218+
value |= (*byte as u64) << (8 * (bytes.len() - 1 - i));
219+
}
220+
}
221+
value
222+
}
223+
224+
fn write_int(bytes: &mut [u8], value: u64, is_little_endian: bool) {
225+
if is_little_endian {
226+
for (i, byte) in bytes.iter_mut().enumerate() {
227+
*byte = (value >> (i * 8)) as u8;
228+
}
229+
} else {
230+
let len = bytes.len();
231+
for (i, byte) in bytes.iter_mut().enumerate() {
232+
let shift = 8 * (len - 1 - i);
233+
*byte = (value >> shift) as u8;
234+
}
235+
}
236+
}
237+
238+
fn apply_relocations_with_object(
239+
view: &BinaryView,
240+
section_name: &str,
241+
data: &mut [u8],
242+
is_little_endian: bool,
243+
) -> bool {
244+
let Some(file_bytes) = read_entire_view(view) else {
245+
return false;
246+
};
247+
248+
let Ok(file) = object::File::parse(&*file_bytes) else {
249+
return false;
250+
};
251+
252+
let Some(obj_section) = file.section_by_name(section_name) else {
253+
return false;
254+
};
255+
256+
let mut applied = false;
257+
258+
for (offset, relocation) in obj_section.relocations() {
259+
let size_bits = relocation.size();
260+
if size_bits == 0 {
261+
continue;
262+
}
263+
let size = (size_bits / 8) as usize;
264+
if size == 0 || size > 8 {
265+
continue;
266+
}
267+
268+
let offset = offset as usize;
269+
if offset + size > data.len() {
270+
continue;
271+
}
272+
273+
let mut base = 0i128;
274+
let mut target_section_name: Option<&str> = None;
275+
276+
match relocation.target() {
277+
object::RelocationTarget::Symbol(symbol_index) => {
278+
let Ok(symbol) = file.symbol_by_index(symbol_index) else {
279+
continue;
280+
};
281+
if let Some(section_index) = symbol.section_index() {
282+
let Ok(target_section) = file.section_by_index(section_index) else {
283+
continue;
284+
};
285+
if let Ok(name) = target_section.name() {
286+
target_section_name = Some(name);
287+
}
288+
base += target_section.address() as i128;
289+
}
290+
base += symbol.address() as i128;
291+
}
292+
object::RelocationTarget::Section(section_index) => {
293+
let Ok(target_section) = file.section_by_index(section_index) else {
294+
continue;
295+
};
296+
if let Ok(name) = target_section.name() {
297+
target_section_name = Some(name);
298+
}
299+
base += target_section.address() as i128;
300+
}
301+
_ => {}
302+
}
303+
304+
if let Some(name) = target_section_name {
305+
if !is_debug_related_section(name) {
306+
continue;
307+
}
308+
} else {
309+
continue;
310+
}
311+
312+
if relocation.kind() != object::RelocationKind::Absolute {
313+
continue;
314+
}
315+
316+
let _existing = read_int(&data[offset..offset + size], is_little_endian) as i128;
317+
let addend = relocation.addend() as i128;
318+
let value = base.wrapping_add(addend) as u64;
319+
write_int(&mut data[offset..offset + size], value, is_little_endian);
320+
applied = true;
321+
}
322+
323+
applied
324+
}
325+
326+
fn read_entire_view(view: &BinaryView) -> Option<Vec<u8>> {
327+
let len = view.len();
328+
if len == 0 || len > usize::MAX as u64 {
329+
return None;
330+
}
331+
let data = view.read_vec(0, len as usize);
332+
if data.len() as u64 != len {
333+
return None;
334+
}
335+
Some(data)
336+
}
337+
338+
fn is_debug_related_section(name: &str) -> bool {
339+
name.starts_with(".debug") || name.starts_with(".zdebug")
340+
}

0 commit comments

Comments
 (0)