1 | use crate::constants; |
2 | use crate::write::{Address, Error, Result, Writer}; |
3 | use crate::SectionId; |
4 | |
5 | /// A relocation to be applied to a section. |
6 | #[derive (Debug, Clone, Copy, PartialEq, Eq)] |
7 | pub struct Relocation { |
8 | /// The offset within the section where the relocation should be applied. |
9 | pub offset: usize, |
10 | /// The size of the value to be relocated. |
11 | pub size: u8, |
12 | /// The target of the relocation. |
13 | pub target: RelocationTarget, |
14 | /// The addend to be applied to the relocated value. |
15 | pub addend: i64, |
16 | /// The pointer encoding for relocations in unwind information. |
17 | pub eh_pe: Option<constants::DwEhPe>, |
18 | } |
19 | |
20 | /// The target of a relocation. |
21 | #[derive (Debug, Clone, Copy, PartialEq, Eq)] |
22 | pub enum RelocationTarget { |
23 | /// The relocation target is a symbol. |
24 | /// |
25 | /// The meaning of this value is decided by the writer, but |
26 | /// will typically be an index into a symbol table. |
27 | Symbol(usize), |
28 | /// The relocation target is a section. |
29 | Section(SectionId), |
30 | } |
31 | |
32 | /// A `Writer` which also records relocations. |
33 | pub trait RelocateWriter { |
34 | /// The type of the writer being used to write the section data. |
35 | type Writer: Writer; |
36 | |
37 | /// Get the writer being used to write the section data. |
38 | fn writer(&self) -> &Self::Writer; |
39 | |
40 | /// Get the writer being used to write the section data. |
41 | fn writer_mut(&mut self) -> &mut Self::Writer; |
42 | |
43 | /// Record a relocation. |
44 | fn relocate(&mut self, relocation: Relocation); |
45 | } |
46 | |
47 | impl<T: RelocateWriter> Writer for T { |
48 | type Endian = <<T as RelocateWriter>::Writer as Writer>::Endian; |
49 | |
50 | fn endian(&self) -> Self::Endian { |
51 | self.writer().endian() |
52 | } |
53 | |
54 | fn len(&self) -> usize { |
55 | self.writer().len() |
56 | } |
57 | |
58 | fn write(&mut self, bytes: &[u8]) -> Result<()> { |
59 | self.writer_mut().write(bytes) |
60 | } |
61 | |
62 | fn write_at(&mut self, offset: usize, bytes: &[u8]) -> Result<()> { |
63 | self.writer_mut().write_at(offset, bytes) |
64 | } |
65 | |
66 | fn write_address(&mut self, address: Address, size: u8) -> Result<()> { |
67 | match address { |
68 | Address::Constant(val) => self.writer_mut().write_udata(val, size), |
69 | Address::Symbol { symbol, addend } => { |
70 | self.relocate(Relocation { |
71 | offset: self.len(), |
72 | size, |
73 | target: RelocationTarget::Symbol(symbol), |
74 | addend, |
75 | eh_pe: None, |
76 | }); |
77 | self.writer_mut().write_udata(0, size) |
78 | } |
79 | } |
80 | } |
81 | |
82 | fn write_offset(&mut self, val: usize, section: SectionId, size: u8) -> Result<()> { |
83 | self.relocate(Relocation { |
84 | offset: self.len(), |
85 | size, |
86 | target: RelocationTarget::Section(section), |
87 | addend: val as i64, |
88 | eh_pe: None, |
89 | }); |
90 | self.writer_mut().write_udata(0, size) |
91 | } |
92 | |
93 | fn write_offset_at( |
94 | &mut self, |
95 | offset: usize, |
96 | val: usize, |
97 | section: SectionId, |
98 | size: u8, |
99 | ) -> Result<()> { |
100 | self.relocate(Relocation { |
101 | offset, |
102 | size, |
103 | target: RelocationTarget::Section(section), |
104 | addend: val as i64, |
105 | eh_pe: None, |
106 | }); |
107 | self.writer_mut().write_udata_at(offset, 0, size) |
108 | } |
109 | |
110 | fn write_eh_pointer( |
111 | &mut self, |
112 | address: Address, |
113 | eh_pe: constants::DwEhPe, |
114 | size: u8, |
115 | ) -> Result<()> { |
116 | match address { |
117 | Address::Constant(_) => self.writer_mut().write_eh_pointer(address, eh_pe, size), |
118 | Address::Symbol { symbol, addend } => { |
119 | let size = match eh_pe.format() { |
120 | constants::DW_EH_PE_absptr => size, |
121 | constants::DW_EH_PE_udata2 => 2, |
122 | constants::DW_EH_PE_udata4 => 4, |
123 | constants::DW_EH_PE_udata8 => 8, |
124 | constants::DW_EH_PE_sdata2 => 2, |
125 | constants::DW_EH_PE_sdata4 => 4, |
126 | constants::DW_EH_PE_sdata8 => 8, |
127 | _ => return Err(Error::UnsupportedPointerEncoding(eh_pe)), |
128 | }; |
129 | self.relocate(Relocation { |
130 | offset: self.len(), |
131 | size, |
132 | target: RelocationTarget::Symbol(symbol), |
133 | addend, |
134 | eh_pe: Some(eh_pe), |
135 | }); |
136 | self.writer_mut().write_udata(0, size) |
137 | } |
138 | } |
139 | } |
140 | } |
141 | |
142 | #[cfg (test)] |
143 | mod tests { |
144 | use super::*; |
145 | use crate::write::EndianVec; |
146 | use crate::{LittleEndian, SectionId}; |
147 | use alloc::vec::Vec; |
148 | |
149 | struct Section { |
150 | writer: EndianVec<LittleEndian>, |
151 | relocations: Vec<Relocation>, |
152 | } |
153 | |
154 | impl RelocateWriter for Section { |
155 | type Writer = EndianVec<LittleEndian>; |
156 | |
157 | fn writer(&self) -> &Self::Writer { |
158 | &self.writer |
159 | } |
160 | |
161 | fn writer_mut(&mut self) -> &mut Self::Writer { |
162 | &mut self.writer |
163 | } |
164 | |
165 | fn relocate(&mut self, relocation: Relocation) { |
166 | self.relocations.push(relocation); |
167 | } |
168 | } |
169 | |
170 | #[test ] |
171 | fn test_relocate_writer() { |
172 | let mut expected_data = Vec::new(); |
173 | let mut expected_relocations = Vec::new(); |
174 | |
175 | let mut section = Section { |
176 | writer: EndianVec::new(LittleEndian), |
177 | relocations: Vec::new(), |
178 | }; |
179 | |
180 | // No relocation for plain data. |
181 | section.write_udata(0x12345678, 4).unwrap(); |
182 | expected_data.extend_from_slice(&0x12345678u32.to_le_bytes()); |
183 | |
184 | // No relocation for a constant address. |
185 | section |
186 | .write_address(Address::Constant(0x87654321), 4) |
187 | .unwrap(); |
188 | expected_data.extend_from_slice(&0x87654321u32.to_le_bytes()); |
189 | |
190 | // Relocation for a symbol address. |
191 | let offset = section.len(); |
192 | section |
193 | .write_address( |
194 | Address::Symbol { |
195 | symbol: 1, |
196 | addend: 0x12345678, |
197 | }, |
198 | 4, |
199 | ) |
200 | .unwrap(); |
201 | expected_data.extend_from_slice(&[0; 4]); |
202 | expected_relocations.push(Relocation { |
203 | offset, |
204 | size: 4, |
205 | target: RelocationTarget::Symbol(1), |
206 | addend: 0x12345678, |
207 | eh_pe: None, |
208 | }); |
209 | |
210 | // Relocation for a section offset. |
211 | let offset = section.len(); |
212 | section |
213 | .write_offset(0x12345678, SectionId::DebugAbbrev, 4) |
214 | .unwrap(); |
215 | expected_data.extend_from_slice(&[0; 4]); |
216 | expected_relocations.push(Relocation { |
217 | offset, |
218 | size: 4, |
219 | target: RelocationTarget::Section(SectionId::DebugAbbrev), |
220 | addend: 0x12345678, |
221 | eh_pe: None, |
222 | }); |
223 | |
224 | // Relocation for a section offset at a specific offset. |
225 | let offset = section.len(); |
226 | section.write_udata(0x12345678, 4).unwrap(); |
227 | section |
228 | .write_offset_at(offset, 0x12345678, SectionId::DebugStr, 4) |
229 | .unwrap(); |
230 | expected_data.extend_from_slice(&[0; 4]); |
231 | expected_relocations.push(Relocation { |
232 | offset, |
233 | size: 4, |
234 | target: RelocationTarget::Section(SectionId::DebugStr), |
235 | addend: 0x12345678, |
236 | eh_pe: None, |
237 | }); |
238 | |
239 | // No relocation for a constant in unwind information. |
240 | section |
241 | .write_eh_pointer(Address::Constant(0x87654321), constants::DW_EH_PE_absptr, 8) |
242 | .unwrap(); |
243 | expected_data.extend_from_slice(&0x87654321u64.to_le_bytes()); |
244 | |
245 | // No relocation for a relative constant in unwind information. |
246 | let offset = section.len(); |
247 | section |
248 | .write_eh_pointer( |
249 | Address::Constant(offset as u64 - 8), |
250 | constants::DW_EH_PE_pcrel | constants::DW_EH_PE_sdata4, |
251 | 8, |
252 | ) |
253 | .unwrap(); |
254 | expected_data.extend_from_slice(&(-8i32).to_le_bytes()); |
255 | |
256 | // Relocation for a symbol in unwind information. |
257 | let offset = section.len(); |
258 | section |
259 | .write_eh_pointer( |
260 | Address::Symbol { |
261 | symbol: 2, |
262 | addend: 0x12345678, |
263 | }, |
264 | constants::DW_EH_PE_pcrel | constants::DW_EH_PE_sdata4, |
265 | 8, |
266 | ) |
267 | .unwrap(); |
268 | expected_data.extend_from_slice(&[0; 4]); |
269 | expected_relocations.push(Relocation { |
270 | offset, |
271 | size: 4, |
272 | target: RelocationTarget::Symbol(2), |
273 | addend: 0x12345678, |
274 | eh_pe: Some(constants::DW_EH_PE_pcrel | constants::DW_EH_PE_sdata4), |
275 | }); |
276 | |
277 | assert_eq!(section.writer.into_vec(), expected_data); |
278 | assert_eq!(section.relocations, expected_relocations); |
279 | } |
280 | } |
281 | |