]>
Commit | Line | Data |
---|---|---|
f035d41b XL |
1 | //! # Relocation computations |
2 | //! | |
3 | //! The following notation is used to describe relocation computations | |
4 | //! specific to x86_64 ELF. | |
5 | //! | |
6 | //! * A: The addend used to compute the value of the relocatable field. | |
7 | //! * B: The base address at which a shared object is loaded into memory | |
8 | //! during execution. Generally, a shared object file is built with a | |
9 | //! base virtual address of 0. However, the execution address of the | |
10 | //! shared object is different. | |
11 | //! * G: The offset into the global offset table at which the address of | |
12 | //! the relocation entry's symbol resides during execution. | |
13 | //! * GOT: The address of the global offset table. | |
14 | //! * L: The section offset or address of the procedure linkage table entry | |
15 | //! for a symbol. | |
16 | //! * P: The section offset or address of the storage unit being relocated, | |
17 | //! computed using r_offset. | |
18 | //! * S: The value of the symbol whose index resides in the relocation entry. | |
19 | //! * Z: The size of the symbol whose index resides in the relocation entry. | |
20 | //! | |
21 | //! Below are some common x86_64 relocation computations you might find useful: | |
22 | //! | |
23 | //! | Relocation | Value | Size | Formula | | |
24 | //! |:--------------------------|:------|:----------|:------------------| | |
25 | //! | `R_X86_64_NONE` | 0 | NONE | NONE | | |
26 | //! | `R_X86_64_64` | 1 | 64 | S + A | | |
27 | //! | `R_X86_64_PC32` | 2 | 32 | S + A - P | | |
28 | //! | `R_X86_64_GOT32` | 3 | 32 | G + A | | |
29 | //! | `R_X86_64_PLT32` | 4 | 32 | L + A - P | | |
30 | //! | `R_X86_64_COPY` | 5 | NONE | NONE | | |
31 | //! | `R_X86_64_GLOB_DAT` | 6 | 64 | S | | |
32 | //! | `R_X86_64_JUMP_SLOT` | 7 | 64 | S | | |
33 | //! | `R_X86_64_RELATIVE` | 8 | 64 | B + A | | |
34 | //! | `R_X86_64_GOTPCREL` | 9 | 32 | G + GOT + A - P | | |
35 | //! | `R_X86_64_32` | 10 | 32 | S + A | | |
36 | //! | `R_X86_64_32S` | 11 | 32 | S + A | | |
37 | //! | `R_X86_64_16` | 12 | 16 | S + A | | |
38 | //! | `R_X86_64_PC16` | 13 | 16 | S + A - P | | |
39 | //! | `R_X86_64_8` | 14 | 8 | S + A | | |
40 | //! | `R_X86_64_PC8` | 15 | 8 | S + A - P | | |
41 | //! | `R_X86_64_DTPMOD64` | 16 | 64 | | | |
42 | //! | `R_X86_64_DTPOFF64` | 17 | 64 | | | |
43 | //! | `R_X86_64_TPOFF64` | 18 | 64 | | | |
44 | //! | `R_X86_64_TLSGD` | 19 | 32 | | | |
45 | //! | `R_X86_64_TLSLD` | 20 | 32 | | | |
46 | //! | `R_X86_64_DTPOFF32` | 21 | 32 | | | |
47 | //! | `R_X86_64_GOTTPOFF` | 22 | 32 | | | |
48 | //! | `R_X86_64_TPOFF32` | 23 | 32 | | | |
49 | //! | `R_X86_64_PC64` | 24 | 64 | S + A - P | | |
50 | //! | `R_X86_64_GOTOFF64` | 25 | 64 | S + A - GOT | | |
51 | //! | `R_X86_64_GOTPC32` | 26 | 32 | GOT + A - P | | |
52 | //! | `R_X86_64_SIZE32` | 32 | 32 | Z + A | | |
53 | //! | `R_X86_64_SIZE64` | 33 | 64 | Z + A | | |
54 | //! | `R_X86_64_GOTPC32_TLSDESC` 34 | 32 | | | |
55 | //! | `R_X86_64_TLSDESC_CALL` | 35 | NONE | | | |
56 | //! | `R_X86_64_TLSDESC` | 36 | 64 × 2 | | | |
57 | //! | `R_X86_64_IRELATIVE` | 37 | 64 | indirect (B + A) | | |
58 | //! | |
59 | //! TLS information is at http://people.redhat.com/aoliva/writeups/TLS/RFC-TLSDESC-x86.txt | |
60 | //! | |
61 | //! `R_X86_64_IRELATIVE` is similar to `R_X86_64_RELATIVE` except that | |
62 | //! the value used in this relocation is the program address returned by the function, | |
63 | //! which takes no arguments, at the address of the result of the corresponding | |
64 | //! `R_X86_64_RELATIVE` relocation. | |
65 | //! | |
66 | //! Read more https://docs.oracle.com/cd/E23824_01/html/819-0690/chapter6-54839.html | |
67 | ||
68 | include!("constants_relocation.rs"); | |
69 | ||
70 | macro_rules! elf_reloc { | |
71 | ($size:ident, $isize:ty) => { | |
72 | use core::fmt; | |
73 | #[cfg(feature = "alloc")] | |
74 | use scroll::{Pread, Pwrite, SizeWith}; | |
75 | #[repr(C)] | |
76 | #[derive(Clone, Copy, PartialEq, Default)] | |
77 | #[cfg_attr(feature = "alloc", derive(Pread, Pwrite, SizeWith))] | |
78 | /// Relocation with an explicit addend | |
79 | pub struct Rela { | |
80 | /// Address | |
81 | pub r_offset: $size, | |
82 | /// Relocation type and symbol index | |
83 | pub r_info: $size, | |
84 | /// Addend | |
85 | pub r_addend: $isize, | |
86 | } | |
87 | #[repr(C)] | |
88 | #[derive(Clone, PartialEq, Default)] | |
89 | #[cfg_attr(feature = "alloc", derive(Pread, Pwrite, SizeWith))] | |
90 | /// Relocation without an addend | |
91 | pub struct Rel { | |
92 | /// address | |
93 | pub r_offset: $size, | |
94 | /// relocation type and symbol address | |
95 | pub r_info: $size, | |
96 | } | |
97 | use plain; | |
98 | unsafe impl plain::Plain for Rela {} | |
99 | unsafe impl plain::Plain for Rel {} | |
100 | ||
101 | impl fmt::Debug for Rela { | |
102 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { | |
103 | let sym = r_sym(self.r_info); | |
104 | let typ = r_type(self.r_info); | |
105 | f.debug_struct("Rela") | |
106 | .field("r_offset", &format_args!("{:x}", self.r_offset)) | |
107 | .field("r_info", &format_args!("{:x}", self.r_info)) | |
108 | .field("r_addend", &format_args!("{:x}", self.r_addend)) | |
109 | .field("r_typ", &typ) | |
110 | .field("r_sym", &sym) | |
111 | .finish() | |
112 | } | |
113 | } | |
114 | impl fmt::Debug for Rel { | |
115 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { | |
116 | let sym = r_sym(self.r_info); | |
117 | let typ = r_type(self.r_info); | |
118 | f.debug_struct("Rel") | |
119 | .field("r_offset", &format_args!("{:x}", self.r_offset)) | |
120 | .field("r_info", &format_args!("{:x}", self.r_info)) | |
121 | .field("r_typ", &typ) | |
122 | .field("r_sym", &sym) | |
123 | .finish() | |
124 | } | |
125 | } | |
126 | }; | |
127 | } | |
128 | ||
129 | macro_rules! elf_rela_std_impl { | |
130 | ($size:ident, $isize:ty) => { | |
131 | if_alloc! { | |
132 | use crate::elf::reloc::Reloc; | |
133 | ||
134 | use core::slice; | |
135 | ||
136 | if_std! { | |
137 | use crate::error::Result; | |
138 | ||
139 | use std::fs::File; | |
140 | use std::io::{Read, Seek}; | |
141 | use std::io::SeekFrom::Start; | |
142 | } | |
143 | ||
144 | impl From<Rela> for Reloc { | |
145 | fn from(rela: Rela) -> Self { | |
146 | Reloc { | |
147 | r_offset: u64::from(rela.r_offset), | |
148 | r_addend: Some(i64::from(rela.r_addend)), | |
149 | r_sym: r_sym(rela.r_info) as usize, | |
150 | r_type: r_type(rela.r_info), | |
151 | } | |
152 | } | |
153 | } | |
154 | ||
155 | impl From<Rel> for Reloc { | |
156 | fn from(rel: Rel) -> Self { | |
157 | Reloc { | |
158 | r_offset: u64::from(rel.r_offset), | |
159 | r_addend: None, | |
160 | r_sym: r_sym(rel.r_info) as usize, | |
161 | r_type: r_type(rel.r_info), | |
162 | } | |
163 | } | |
164 | } | |
165 | ||
166 | impl From<Reloc> for Rela { | |
167 | fn from(rela: Reloc) -> Self { | |
168 | let r_info = r_info(rela.r_sym as $size, $size::from(rela.r_type)); | |
169 | Rela { | |
170 | r_offset: rela.r_offset as $size, | |
171 | r_info: r_info, | |
172 | r_addend: rela.r_addend.unwrap_or(0) as $isize, | |
173 | } | |
174 | } | |
175 | } | |
176 | ||
177 | impl From<Reloc> for Rel { | |
178 | fn from(rel: Reloc) -> Self { | |
179 | let r_info = r_info(rel.r_sym as $size, $size::from(rel.r_type)); | |
180 | Rel { | |
181 | r_offset: rel.r_offset as $size, | |
182 | r_info: r_info, | |
183 | } | |
184 | } | |
185 | } | |
186 | ||
187 | /// Gets the rela entries given a rela pointer and the _size_ of the rela section in the binary, | |
188 | /// in bytes. | |
189 | /// Assumes the pointer is valid and can safely return a slice of memory pointing to the relas because: | |
190 | /// 1. `ptr` points to memory received from the kernel (i.e., it loaded the executable), _or_ | |
191 | /// 2. The binary has already been mmapped (i.e., it's a `SharedObject`), and hence it's safe to return a slice of that memory. | |
192 | /// 3. Or if you obtained the pointer in some other lawful manner | |
193 | pub unsafe fn from_raw_rela<'a>(ptr: *const Rela, size: usize) -> &'a [Rela] { | |
194 | slice::from_raw_parts(ptr, size / SIZEOF_RELA) | |
195 | } | |
196 | ||
197 | /// Gets the rel entries given a rel pointer and the _size_ of the rel section in the binary, | |
198 | /// in bytes. | |
199 | /// Assumes the pointer is valid and can safely return a slice of memory pointing to the rels because: | |
200 | /// 1. `ptr` points to memory received from the kernel (i.e., it loaded the executable), _or_ | |
201 | /// 2. The binary has already been mmapped (i.e., it's a `SharedObject`), and hence it's safe to return a slice of that memory. | |
202 | /// 3. Or if you obtained the pointer in some other lawful manner | |
203 | pub unsafe fn from_raw_rel<'a>(ptr: *const Rel, size: usize) -> &'a [Rel] { | |
204 | slice::from_raw_parts(ptr, size / SIZEOF_REL) | |
205 | } | |
206 | ||
207 | #[cfg(feature = "std")] | |
208 | pub fn from_fd(fd: &mut File, offset: usize, size: usize) -> Result<Vec<Rela>> { | |
209 | let count = size / SIZEOF_RELA; | |
210 | let mut relocs = vec![Rela::default(); count]; | |
211 | fd.seek(Start(offset as u64))?; | |
212 | unsafe { | |
213 | fd.read_exact(plain::as_mut_bytes(&mut *relocs))?; | |
214 | } | |
215 | Ok(relocs) | |
216 | } | |
217 | } // end if_alloc | |
218 | }; | |
219 | } | |
220 | ||
221 | pub mod reloc32 { | |
222 | ||
223 | pub use crate::elf::reloc::*; | |
224 | ||
225 | elf_reloc!(u32, i32); | |
226 | ||
227 | pub const SIZEOF_RELA: usize = 4 + 4 + 4; | |
228 | pub const SIZEOF_REL: usize = 4 + 4; | |
229 | ||
230 | #[inline(always)] | |
231 | pub fn r_sym(info: u32) -> u32 { | |
232 | info >> 8 | |
233 | } | |
234 | ||
235 | #[inline(always)] | |
236 | pub fn r_type(info: u32) -> u32 { | |
237 | info & 0xff | |
238 | } | |
239 | ||
240 | #[inline(always)] | |
241 | pub fn r_info(sym: u32, typ: u32) -> u32 { | |
242 | (sym << 8) + (typ & 0xff) | |
243 | } | |
244 | ||
245 | elf_rela_std_impl!(u32, i32); | |
246 | } | |
247 | ||
248 | pub mod reloc64 { | |
249 | pub use crate::elf::reloc::*; | |
250 | ||
251 | elf_reloc!(u64, i64); | |
252 | ||
253 | pub const SIZEOF_RELA: usize = 8 + 8 + 8; | |
254 | pub const SIZEOF_REL: usize = 8 + 8; | |
255 | ||
256 | #[inline(always)] | |
257 | pub fn r_sym(info: u64) -> u32 { | |
258 | (info >> 32) as u32 | |
259 | } | |
260 | ||
261 | #[inline(always)] | |
262 | pub fn r_type(info: u64) -> u32 { | |
263 | (info & 0xffff_ffff) as u32 | |
264 | } | |
265 | ||
266 | #[inline(always)] | |
267 | pub fn r_info(sym: u64, typ: u64) -> u64 { | |
268 | (sym << 32) + typ | |
269 | } | |
270 | ||
271 | elf_rela_std_impl!(u64, i64); | |
272 | } | |
273 | ||
274 | ////////////////////////////// | |
275 | // Generic Reloc | |
276 | ///////////////////////////// | |
277 | if_alloc! { | |
278 | use scroll::{ctx, Pread}; | |
279 | use scroll::ctx::SizeWith; | |
280 | use core::fmt; | |
281 | use core::result; | |
282 | use crate::container::{Ctx, Container}; | |
283 | #[cfg(feature = "endian_fd")] | |
284 | use alloc::vec::Vec; | |
285 | ||
286 | #[derive(Clone, Copy, PartialEq, Default)] | |
287 | /// A unified ELF relocation structure | |
288 | pub struct Reloc { | |
289 | /// Address | |
290 | pub r_offset: u64, | |
291 | /// Addend | |
292 | pub r_addend: Option<i64>, | |
293 | /// The index into the corresponding symbol table - either dynamic or regular | |
294 | pub r_sym: usize, | |
295 | /// The relocation type | |
296 | pub r_type: u32, | |
297 | } | |
298 | ||
299 | impl Reloc { | |
300 | pub fn size(is_rela: bool, ctx: Ctx) -> usize { | |
301 | use scroll::ctx::SizeWith; | |
302 | Reloc::size_with(&(is_rela, ctx)) | |
303 | } | |
304 | } | |
305 | ||
306 | type RelocCtx = (bool, Ctx); | |
307 | ||
308 | impl ctx::SizeWith<RelocCtx> for Reloc { | |
309 | fn size_with( &(is_rela, Ctx { container, .. }): &RelocCtx) -> usize { | |
310 | match container { | |
311 | Container::Little => { | |
312 | if is_rela { reloc32::SIZEOF_RELA } else { reloc32::SIZEOF_REL } | |
313 | }, | |
314 | Container::Big => { | |
315 | if is_rela { reloc64::SIZEOF_RELA } else { reloc64::SIZEOF_REL } | |
316 | } | |
317 | } | |
318 | } | |
319 | } | |
320 | ||
321 | impl<'a> ctx::TryFromCtx<'a, RelocCtx> for Reloc { | |
322 | type Error = crate::error::Error; | |
323 | fn try_from_ctx(bytes: &'a [u8], (is_rela, Ctx { container, le }): RelocCtx) -> result::Result<(Self, usize), Self::Error> { | |
324 | use scroll::Pread; | |
325 | let reloc = match container { | |
326 | Container::Little => { | |
327 | if is_rela { | |
328 | (bytes.pread_with::<reloc32::Rela>(0, le)?.into(), reloc32::SIZEOF_RELA) | |
329 | } else { | |
330 | (bytes.pread_with::<reloc32::Rel>(0, le)?.into(), reloc32::SIZEOF_REL) | |
331 | } | |
332 | }, | |
333 | Container::Big => { | |
334 | if is_rela { | |
335 | (bytes.pread_with::<reloc64::Rela>(0, le)?.into(), reloc64::SIZEOF_RELA) | |
336 | } else { | |
337 | (bytes.pread_with::<reloc64::Rel>(0, le)?.into(), reloc64::SIZEOF_REL) | |
338 | } | |
339 | } | |
340 | }; | |
341 | Ok(reloc) | |
342 | } | |
343 | } | |
344 | ||
345 | impl ctx::TryIntoCtx<RelocCtx> for Reloc { | |
346 | type Error = crate::error::Error; | |
347 | /// Writes the relocation into `bytes` | |
348 | fn try_into_ctx(self, bytes: &mut [u8], (is_rela, Ctx {container, le}): RelocCtx) -> result::Result<usize, Self::Error> { | |
349 | use scroll::Pwrite; | |
350 | match container { | |
351 | Container::Little => { | |
352 | if is_rela { | |
353 | let rela: reloc32::Rela = self.into(); | |
354 | Ok(bytes.pwrite_with(rela, 0, le)?) | |
355 | } else { | |
356 | let rel: reloc32::Rel = self.into(); | |
357 | Ok(bytes.pwrite_with(rel, 0, le)?) | |
358 | } | |
359 | }, | |
360 | Container::Big => { | |
361 | if is_rela { | |
362 | let rela: reloc64::Rela = self.into(); | |
363 | Ok(bytes.pwrite_with(rela, 0, le)?) | |
364 | } else { | |
365 | let rel: reloc64::Rel = self.into(); | |
366 | Ok(bytes.pwrite_with(rel, 0, le)?) | |
367 | } | |
368 | }, | |
369 | } | |
370 | } | |
371 | } | |
372 | ||
373 | impl ctx::IntoCtx<(bool, Ctx)> for Reloc { | |
374 | /// Writes the relocation into `bytes` | |
375 | fn into_ctx(self, bytes: &mut [u8], ctx: RelocCtx) { | |
376 | use scroll::Pwrite; | |
377 | bytes.pwrite_with(self, 0, ctx).unwrap(); | |
378 | } | |
379 | } | |
380 | ||
381 | impl fmt::Debug for Reloc { | |
382 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { | |
383 | f.debug_struct("Reloc") | |
384 | .field("r_offset", &format_args!("{:x}", self.r_offset)) | |
385 | .field("r_addend", &format_args!("{:x}", self.r_addend.unwrap_or(0))) | |
386 | .field("r_sym", &self.r_sym) | |
387 | .field("r_type", &self.r_type) | |
388 | .finish() | |
389 | } | |
390 | } | |
391 | ||
392 | #[derive(Default)] | |
393 | /// An ELF section containing relocations, allowing lazy iteration over symbols. | |
394 | pub struct RelocSection<'a> { | |
395 | bytes: &'a [u8], | |
396 | count: usize, | |
397 | ctx: RelocCtx, | |
398 | start: usize, | |
399 | end: usize, | |
400 | } | |
401 | ||
402 | impl<'a> fmt::Debug for RelocSection<'a> { | |
403 | fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { | |
404 | let len = self.bytes.len(); | |
405 | fmt.debug_struct("RelocSection") | |
406 | .field("bytes", &len) | |
407 | .field("range", &format!("{:#x}..{:#x}", self.start, self.end)) | |
408 | .field("count", &self.count) | |
409 | .field("Relocations", &self.to_vec()) | |
410 | .finish() | |
411 | } | |
412 | } | |
413 | ||
414 | impl<'a> RelocSection<'a> { | |
415 | #[cfg(feature = "endian_fd")] | |
416 | /// Parse a REL or RELA section of size `filesz` from `offset`. | |
417 | pub fn parse(bytes: &'a [u8], offset: usize, filesz: usize, is_rela: bool, ctx: Ctx) -> crate::error::Result<RelocSection<'a>> { | |
418 | // TODO: better error message when too large (see symtab implementation) | |
419 | let bytes = bytes.pread_with(offset, filesz)?; | |
420 | ||
421 | Ok(RelocSection { | |
422 | bytes: bytes, | |
423 | count: filesz / Reloc::size(is_rela, ctx), | |
424 | ctx: (is_rela, ctx), | |
425 | start: offset, | |
426 | end: offset + filesz, | |
427 | }) | |
428 | } | |
429 | ||
430 | /// Try to parse a single relocation from the binary, at `index`. | |
431 | #[inline] | |
432 | pub fn get(&self, index: usize) -> Option<Reloc> { | |
433 | if index >= self.count { | |
434 | None | |
435 | } else { | |
436 | Some(self.bytes.pread_with(index * Reloc::size_with(&self.ctx), self.ctx).unwrap()) | |
437 | } | |
438 | } | |
439 | ||
440 | /// The number of relocations in the section. | |
441 | #[inline] | |
442 | pub fn len(&self) -> usize { | |
443 | self.count | |
444 | } | |
445 | ||
446 | /// Returns true if section has no relocations. | |
447 | #[inline] | |
448 | pub fn is_empty(&self) -> bool { | |
449 | self.count == 0 | |
450 | } | |
451 | ||
452 | /// Iterate over all relocations. | |
453 | pub fn iter(&self) -> RelocIterator<'a> { | |
454 | self.into_iter() | |
455 | } | |
456 | ||
457 | /// Parse all relocations into a vector. | |
458 | pub fn to_vec(&self) -> Vec<Reloc> { | |
459 | self.iter().collect() | |
460 | } | |
461 | } | |
462 | ||
463 | impl<'a, 'b> IntoIterator for &'b RelocSection<'a> { | |
464 | type Item = <RelocIterator<'a> as Iterator>::Item; | |
465 | type IntoIter = RelocIterator<'a>; | |
466 | ||
467 | #[inline] | |
468 | fn into_iter(self) -> Self::IntoIter { | |
469 | RelocIterator { | |
470 | bytes: self.bytes, | |
471 | offset: 0, | |
472 | index: 0, | |
473 | count: self.count, | |
474 | ctx: self.ctx, | |
475 | } | |
476 | } | |
477 | } | |
478 | ||
479 | pub struct RelocIterator<'a> { | |
480 | bytes: &'a [u8], | |
481 | offset: usize, | |
482 | index: usize, | |
483 | count: usize, | |
484 | ctx: RelocCtx, | |
485 | } | |
486 | ||
487 | impl<'a> fmt::Debug for RelocIterator<'a> { | |
488 | fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { | |
489 | fmt.debug_struct("RelocIterator") | |
490 | .field("bytes", &"<... redacted ...>") | |
491 | .field("offset", &self.offset) | |
492 | .field("index", &self.index) | |
493 | .field("count", &self.count) | |
494 | .field("ctx", &self.ctx) | |
495 | .finish() | |
496 | } | |
497 | } | |
498 | ||
499 | impl<'a> Iterator for RelocIterator<'a> { | |
500 | type Item = Reloc; | |
501 | ||
502 | #[inline] | |
503 | fn next(&mut self) -> Option<Self::Item> { | |
504 | if self.index >= self.count { | |
505 | None | |
506 | } else { | |
507 | self.index += 1; | |
508 | Some(self.bytes.gread_with(&mut self.offset, self.ctx).unwrap()) | |
509 | } | |
510 | } | |
511 | } | |
512 | ||
513 | impl<'a> ExactSizeIterator for RelocIterator<'a> { | |
514 | #[inline] | |
515 | fn len(&self) -> usize { | |
516 | self.count - self.index | |
517 | } | |
518 | } | |
519 | } // end if_alloc |