Skip to content

Commit 7afc198

Browse files
committed
WIP: Apply relocations when parsing DWARF
1 parent 09ce2f6 commit 7afc198

File tree

4 files changed

+88
-96
lines changed

4 files changed

+88
-96
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/dwarf_import/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,4 +16,4 @@ log = "0.4"
1616
iset = "0.2.2"
1717
cpp_demangle = "0.4.3"
1818
regex = "1"
19-
indexmap = "2.5.0"
19+
indexmap = "2.5.0"

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: 73 additions & 94 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};
1617

1718
use binaryninja::{
1819
binary_view::{BinaryView, BinaryViewBase, BinaryViewExt},
@@ -35,6 +36,9 @@ pub enum Error {
3536

3637
#[error("{0}")]
3738
IoError(#[from] std::io::Error),
39+
40+
#[error("{0}")]
41+
ObjectError(#[from] object::Error),
3842
}
3943

4044
pub fn is_non_dwo_dwarf(view: &BinaryView) -> bool {
@@ -89,105 +93,80 @@ pub fn get_endian(view: &BinaryView) -> RunTimeEndian {
8993
}
9094
}
9195

92-
pub fn create_section_reader<'a, Endian: 'a + Endianity>(
96+
pub fn create_section_reader<Endian: Endianity>(
9397
section_id: SectionId,
94-
view: &'a BinaryView,
98+
view: &BinaryView,
9599
endian: Endian,
96-
dwo_file: bool,
97-
) -> Result<EndianRcSlice<Endian>, Error> {
98-
let section_name = if dwo_file && section_id.dwo_name().is_some() {
99-
section_id.dwo_name().unwrap()
100-
} else {
101-
section_id.name()
102-
};
100+
is_dwo: bool,
101+
) -> Result<RelocateOwned<gimli::EndianRcSlice<Endian>>, Error> {
102+
let view_data = view.read_vec(0, view.len() as usize);
103+
let file = object::File::parse(&*view_data)?;
104+
load_file_section(section_id, &file, endian, is_dwo)
105+
}
106+
107+
#[derive(Debug, Clone)]
108+
pub struct RelocationMap(Rc<object::read::RelocationMap>);
103109

104-
if let Some(section) = view.section_by_name(section_name) {
105-
// TODO : This is kinda broke....should add rust wrappers for some of this
106-
if let Some(symbol) = view
107-
.symbols()
108-
.iter()
109-
.find(|symbol| symbol.full_name().to_string_lossy() == "__elf_section_headers")
110-
{
111-
if let Some(data_var) = view
112-
.data_variables()
113-
.iter()
114-
.find(|var| var.address == symbol.address())
115-
{
116-
// TODO : This should eventually be wrapped by some DataView sorta thingy thing, like how python does it
117-
let data_type = &data_var.ty.contents;
118-
let data = view.read_vec(data_var.address, data_type.width() as usize);
119-
let element_type = data_type.element_type().unwrap().contents;
120-
121-
if let Some(current_section_header) = data
122-
.chunks(element_type.width() as usize)
123-
.find(|section_header| {
124-
if view.address_size() == 4 {
125-
endian.read_u32(&section_header[16..20]) as u64 == section.start()
126-
} else {
127-
endian.read_u64(&section_header[24..32]) == section.start()
128-
}
129-
})
130-
{
131-
let section_flags = if view.address_size() == 4 {
132-
endian.read_u32(&current_section_header[8..12]) as u64
133-
} else {
134-
endian.read_u64(&current_section_header[8..16])
135-
};
136-
// If the section has the compressed bit set
137-
if (section_flags & 2048) != 0 {
138-
// Get section, trim header, decompress, return
139-
let compressed_header_size = view.address_size() * 3;
140-
141-
let offset = section.start() + compressed_header_size as u64;
142-
let len = section.len() - compressed_header_size;
143-
144-
let ch_type_vec = view.read_vec(section.start(), 4);
145-
let ch_type = endian.read_u32(&ch_type_vec);
146-
147-
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-
}
165-
}
166-
}
167-
}
110+
impl Default for RelocationMap {
111+
fn default() -> Self {
112+
Self(Rc::new(object::read::RelocationMap::default()))
113+
}
114+
}
115+
116+
impl RelocationMap {
117+
fn add(&mut self, file: &object::File, section: &object::Section) {
118+
let map = Rc::get_mut(&mut self.0)
119+
.expect("RelocationMap should have exclusive access during construction");
120+
for (offset, relocation) in section.relocations() {
121+
if let Err(e) = map.add(file, offset, relocation) {
122+
eprintln!(
123+
"Relocation error for section {} at offset 0x{:08x}: {}",
124+
section.name().unwrap(),
125+
offset,
126+
e
127+
);
168128
}
169129
}
170-
let offset = section.start();
171-
let len = section.len();
172-
if len == 0 {
173-
Ok(EndianRcSlice::new(Rc::from([]), endian))
174-
} else {
175-
Ok(EndianRcSlice::new(
176-
Rc::from(view.read_vec(offset, len).as_slice()),
177-
endian,
178-
))
179-
}
180130
}
181-
// Truncate Mach-O section names to 16 bytes
182-
else if let Some(section) = view.section_by_name(&format!(
183-
"__{}",
184-
&section_name[1..section_name.len().min(15)]
185-
)) {
186-
Ok(EndianRcSlice::new(
187-
Rc::from(view.read_vec(section.start(), section.len()).as_slice()),
188-
endian,
189-
))
190-
} else {
191-
Ok(EndianRcSlice::new(Rc::from([]), endian))
131+
}
132+
133+
impl gimli::read::Relocate for RelocationMap {
134+
fn relocate_address(&self, offset: usize, value: u64) -> gimli::Result<u64> {
135+
Ok(self.0.relocate(offset as u64, value))
136+
}
137+
138+
fn relocate_offset(&self, offset: usize, value: usize) -> gimli::Result<usize> {
139+
<usize as gimli::ReaderOffset>::from_u64(self.0.relocate(offset as u64, value as u64))
192140
}
193141
}
142+
143+
type RelocateOwned<R> = gimli::RelocateReader<R, RelocationMap>;
144+
145+
pub fn load_file_section<Endian: gimli::Endianity>(
146+
id: gimli::SectionId,
147+
file: &object::File,
148+
endian: Endian,
149+
is_dwo: bool,
150+
) -> Result<RelocateOwned<gimli::EndianRcSlice<Endian>>, Error> {
151+
let mut relocations = RelocationMap::default();
152+
let name = if is_dwo {
153+
id.dwo_name()
154+
} else if file.format() == object::BinaryFormat::Xcoff {
155+
id.xcoff_name()
156+
} else {
157+
Some(id.name())
158+
};
159+
let data = match name.and_then(|name| file.section_by_name(name)) {
160+
Some(ref section) => {
161+
// DWO sections never have relocations, so don't bother.
162+
if !is_dwo {
163+
relocations.add(file, section);
164+
}
165+
section.uncompressed_data()?.into_owned()
166+
}
167+
// Use a non-zero capacity so that `ReaderOffsetId`s are unique.
168+
None => Vec::with_capacity(1),
169+
};
170+
let section = EndianRcSlice::new(Rc::from(data.into_boxed_slice()), endian);
171+
Ok(gimli::RelocateReader::new(section, relocations))
172+
}

0 commit comments

Comments
 (0)