1//! Apply relocations to addresses and offsets during parsing, instead of requiring the data
2//! to be fully relocated prior to parsing. Necessary to load object files that reference dwarf
3//! objects (not just executables). Implementation derived from Gimli's `dwarfdump` example.
4
5use std::borrow::Cow;
6
7use gimli;
8use hashbrown::HashMap;
9use object::{Object, ObjectSection, ObjectSymbol, RelocationKind, RelocationTarget};
10
11use crate::{Error, Result};
12
13pub(crate) type RelocationMap = HashMap<usize, object::Relocation>;
14
15#[derive(Debug, Clone)]
16pub(crate) struct Relocate<'a, R: gimli::Reader<Offset = usize>> {
17 pub(crate) relocations: &'a RelocationMap,
18 pub(crate) section: R,
19 pub(crate) reader: R,
20}
21
22impl<'a, R: gimli::Reader<Offset = usize>> Relocate<'a, R> {
23 fn relocate(&self, offset: usize, value: u64) -> u64 {
24 if let Some(relocation) = self.relocations.get(&offset) {
25 if matches!(relocation.kind(), RelocationKind::Absolute) {
26 if relocation.has_implicit_addend() {
27 // Use the explicit addend too, because it may have the symbol value.
28 return value.wrapping_add(relocation.addend() as u64);
29 } else {
30 return relocation.addend() as u64;
31 }
32 }
33 };
34
35 value
36 }
37}
38
39impl<'a, R: gimli::Reader<Offset = usize>> gimli::Reader for Relocate<'a, R> {
40 type Endian = R::Endian;
41 type Offset = R::Offset;
42
43 fn read_address(&mut self, address_size: u8) -> gimli::Result<u64> {
44 let offset = self.reader.offset_from(&self.section);
45 let value = self.reader.read_address(address_size)?;
46 Ok(self.relocate(offset, value))
47 }
48
49 fn read_length(&mut self, format: gimli::Format) -> gimli::Result<usize> {
50 let offset = self.reader.offset_from(&self.section);
51 let value = self.reader.read_length(format)?;
52 <usize as gimli::ReaderOffset>::from_u64(self.relocate(offset, value as u64))
53 }
54
55 fn read_offset(&mut self, format: gimli::Format) -> gimli::Result<usize> {
56 let offset = self.reader.offset_from(&self.section);
57 let value = self.reader.read_offset(format)?;
58 <usize as gimli::ReaderOffset>::from_u64(self.relocate(offset, value as u64))
59 }
60
61 fn read_sized_offset(&mut self, size: u8) -> gimli::Result<usize> {
62 let offset = self.reader.offset_from(&self.section);
63 let value = self.reader.read_sized_offset(size)?;
64 <usize as gimli::ReaderOffset>::from_u64(self.relocate(offset, value as u64))
65 }
66
67 #[inline]
68 fn split(&mut self, len: Self::Offset) -> gimli::Result<Self> {
69 let mut other = self.clone();
70 other.reader.truncate(len)?;
71 self.reader.skip(len)?;
72 Ok(other)
73 }
74
75 // All remaining methods simply delegate to `self.reader`.
76
77 #[inline]
78 fn endian(&self) -> Self::Endian {
79 self.reader.endian()
80 }
81
82 #[inline]
83 fn len(&self) -> Self::Offset {
84 self.reader.len()
85 }
86
87 #[inline]
88 fn empty(&mut self) {
89 self.reader.empty()
90 }
91
92 #[inline]
93 fn truncate(&mut self, len: Self::Offset) -> gimli::Result<()> {
94 self.reader.truncate(len)
95 }
96
97 #[inline]
98 fn offset_from(&self, base: &Self) -> Self::Offset {
99 self.reader.offset_from(&base.reader)
100 }
101
102 #[inline]
103 fn offset_id(&self) -> gimli::ReaderOffsetId {
104 self.reader.offset_id()
105 }
106
107 #[inline]
108 fn lookup_offset_id(&self, id: gimli::ReaderOffsetId) -> Option<Self::Offset> {
109 self.reader.lookup_offset_id(id)
110 }
111
112 #[inline]
113 fn find(&self, byte: u8) -> gimli::Result<Self::Offset> {
114 self.reader.find(byte)
115 }
116
117 #[inline]
118 fn skip(&mut self, len: Self::Offset) -> gimli::Result<()> {
119 self.reader.skip(len)
120 }
121
122 #[inline]
123 fn to_slice(&self) -> gimli::Result<Cow<'_, [u8]>> {
124 self.reader.to_slice()
125 }
126
127 #[inline]
128 fn to_string(&self) -> gimli::Result<Cow<'_, str>> {
129 self.reader.to_string()
130 }
131
132 #[inline]
133 fn to_string_lossy(&self) -> gimli::Result<Cow<'_, str>> {
134 self.reader.to_string_lossy()
135 }
136
137 #[inline]
138 fn read_slice(&mut self, buf: &mut [u8]) -> gimli::Result<()> {
139 self.reader.read_slice(buf)
140 }
141}
142
143pub(crate) fn add_relocations(
144 relocations: &mut RelocationMap,
145 file: &object::File<'_>,
146 section: &object::Section<'_, '_>,
147) -> Result<()> {
148 for (offset64, mut relocation) in section.relocations() {
149 let offset = offset64 as usize;
150 if offset as u64 != offset64 {
151 continue;
152 }
153
154 let offset = offset as usize;
155 if matches!(relocation.kind(), RelocationKind::Absolute) {
156 if let RelocationTarget::Symbol(symbol_idx) = relocation.target() {
157 match file.symbol_by_index(symbol_idx) {
158 Ok(symbol) => {
159 let addend = symbol.address().wrapping_add(relocation.addend() as u64);
160 relocation.set_addend(addend as i64);
161 }
162 Err(_) => {
163 return Err(Error::RelocationWithInvalidSymbol(
164 section
165 .name()
166 .map_err(|e| Error::NamelessSection(e, offset))?
167 .to_string(),
168 offset,
169 ));
170 }
171 }
172 }
173
174 if relocations.insert(offset, relocation).is_some() {
175 return Err(Error::MultipleRelocations(
176 section.name().map_err(|e| Error::NamelessSection(e, offset))?.to_string(),
177 offset,
178 ));
179 }
180 } else {
181 return Err(Error::UnsupportedRelocation(
182 section.name().map_err(|e| Error::NamelessSection(e, offset))?.to_string(),
183 offset,
184 ));
185 }
186 }
187
188 Ok(())
189}
190