gimli/write/
relocate.rs

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}