1use crate::constants;
2use crate::write::{Address, Error, Result, Writer};
3use crate::SectionId;
4
5/// A relocation to be applied to a section.
6#[derive(Debug, Clone, Copy, PartialEq, Eq)]
7pub 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)]
22pub 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.
33pub 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
47impl<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)]
143mod 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