]> git.proxmox.com Git - rustc.git/blame - vendor/goblin/src/elf/reloc.rs
New upstream version 1.48.0+dfsg1
[rustc.git] / vendor / goblin / src / elf / reloc.rs
CommitLineData
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
68include!("constants_relocation.rs");
69
70macro_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
129macro_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
221pub 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
248pub 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/////////////////////////////
277if_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