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 { ($size:ident, $isize:ty) => { 130 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 222 pub mod reloc32 { 223 224 pub use crate::elf::reloc::*; 225 226 elf_reloc!(u32, i32); 227 228 pub const SIZEOF_RELA: usize = 4 + 4 + 4; 229 pub const SIZEOF_REL: usize = 4 + 4; 230 231 #[inline(always)] r_sym(info: u32) -> u32232 pub fn r_sym(info: u32) -> u32 { 233 info >> 8 234 } 235 236 #[inline(always)] r_type(info: u32) -> u32237 pub fn r_type(info: u32) -> u32 { 238 info & 0xff 239 } 240 241 #[inline(always)] r_info(sym: u32, typ: u32) -> u32242 pub fn r_info(sym: u32, typ: u32) -> u32 { 243 (sym << 8) + (typ & 0xff) 244 } 245 246 elf_rela_std_impl!(u32, i32); 247 } 248 249 250 pub mod reloc64 { 251 pub use crate::elf::reloc::*; 252 253 elf_reloc!(u64, i64); 254 255 pub const SIZEOF_RELA: usize = 8 + 8 + 8; 256 pub const SIZEOF_REL: usize = 8 + 8; 257 258 #[inline(always)] r_sym(info: u64) -> u32259 pub fn r_sym(info: u64) -> u32 { 260 (info >> 32) as u32 261 } 262 263 #[inline(always)] r_type(info: u64) -> u32264 pub fn r_type(info: u64) -> u32 { 265 (info & 0xffff_ffff) as u32 266 } 267 268 #[inline(always)] r_info(sym: u64, typ: u64) -> u64269 pub fn r_info(sym: u64, typ: u64) -> u64 { 270 (sym << 32) + typ 271 } 272 273 elf_rela_std_impl!(u64, i64); 274 } 275 276 ////////////////////////////// 277 // Generic Reloc 278 ///////////////////////////// 279 if_alloc! { 280 use scroll::{ctx, Pread}; 281 use scroll::ctx::SizeWith; 282 use core::fmt; 283 use core::result; 284 use crate::container::{Ctx, Container}; 285 #[cfg(feature = "endian_fd")] 286 use crate::alloc::vec::Vec; 287 288 #[derive(Clone, Copy, PartialEq, Default)] 289 /// A unified ELF relocation structure 290 pub struct Reloc { 291 /// Address 292 pub r_offset: u64, 293 /// Addend 294 pub r_addend: Option<i64>, 295 /// The index into the corresponding symbol table - either dynamic or regular 296 pub r_sym: usize, 297 /// The relocation type 298 pub r_type: u32, 299 } 300 301 impl Reloc { 302 pub fn size(is_rela: bool, ctx: Ctx) -> usize { 303 use scroll::ctx::SizeWith; 304 Reloc::size_with(&(is_rela, ctx)) 305 } 306 } 307 308 type RelocCtx = (bool, Ctx); 309 310 impl ctx::SizeWith<RelocCtx> for Reloc { 311 type Units = usize; 312 fn size_with( &(is_rela, Ctx { container, .. }): &RelocCtx) -> Self::Units { 313 match container { 314 Container::Little => { 315 if is_rela { reloc32::SIZEOF_RELA } else { reloc32::SIZEOF_REL } 316 }, 317 Container::Big => { 318 if is_rela { reloc64::SIZEOF_RELA } else { reloc64::SIZEOF_REL } 319 } 320 } 321 } 322 } 323 324 impl<'a> ctx::TryFromCtx<'a, RelocCtx> for Reloc { 325 type Error = crate::error::Error; 326 type Size = usize; 327 fn try_from_ctx(bytes: &'a [u8], (is_rela, Ctx { container, le }): RelocCtx) -> result::Result<(Self, Self::Size), Self::Error> { 328 use scroll::Pread; 329 let reloc = match container { 330 Container::Little => { 331 if is_rela { 332 (bytes.pread_with::<reloc32::Rela>(0, le)?.into(), reloc32::SIZEOF_RELA) 333 } else { 334 (bytes.pread_with::<reloc32::Rel>(0, le)?.into(), reloc32::SIZEOF_REL) 335 } 336 }, 337 Container::Big => { 338 if is_rela { 339 (bytes.pread_with::<reloc64::Rela>(0, le)?.into(), reloc64::SIZEOF_RELA) 340 } else { 341 (bytes.pread_with::<reloc64::Rel>(0, le)?.into(), reloc64::SIZEOF_REL) 342 } 343 } 344 }; 345 Ok(reloc) 346 } 347 } 348 349 impl ctx::TryIntoCtx<RelocCtx> for Reloc { 350 type Error = crate::error::Error; 351 type Size = usize; 352 // TODO: I think this is a bad idea 353 /// Writes the relocation into `bytes` 354 fn try_into_ctx(self, bytes: &mut [u8], (is_rela, Ctx {container, le}): RelocCtx) -> result::Result<Self::Size, Self::Error> { 355 use scroll::Pwrite; 356 match container { 357 Container::Little => { 358 if is_rela { 359 let rela: reloc32::Rela = self.into(); 360 Ok(bytes.pwrite_with(rela, 0, le)?) 361 } else { 362 let rel: reloc32::Rel = self.into(); 363 Ok(bytes.pwrite_with(rel, 0, le)?) 364 } 365 }, 366 Container::Big => { 367 if is_rela { 368 let rela: reloc64::Rela = self.into(); 369 Ok(bytes.pwrite_with(rela, 0, le)?) 370 } else { 371 let rel: reloc64::Rel = self.into(); 372 Ok(bytes.pwrite_with(rel, 0, le)?) 373 } 374 }, 375 } 376 } 377 } 378 379 impl ctx::IntoCtx<(bool, Ctx)> for Reloc { 380 /// Writes the relocation into `bytes` 381 fn into_ctx(self, bytes: &mut [u8], ctx: RelocCtx) { 382 use scroll::Pwrite; 383 bytes.pwrite_with(self, 0, ctx).unwrap(); 384 } 385 } 386 387 impl fmt::Debug for Reloc { 388 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 389 f.debug_struct("Reloc") 390 .field("r_offset", &format_args!("{:x}", self.r_offset)) 391 .field("r_addend", &format_args!("{:x}", self.r_addend.unwrap_or(0))) 392 .field("r_sym", &self.r_sym) 393 .field("r_type", &self.r_type) 394 .finish() 395 } 396 } 397 398 #[derive(Default)] 399 /// An ELF section containing relocations, allowing lazy iteration over symbols. 400 pub struct RelocSection<'a> { 401 bytes: &'a [u8], 402 count: usize, 403 ctx: RelocCtx, 404 start: usize, 405 end: usize, 406 } 407 408 impl<'a> fmt::Debug for RelocSection<'a> { 409 fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { 410 let len = self.bytes.len(); 411 fmt.debug_struct("RelocSection") 412 .field("bytes", &len) 413 .field("range", &format!("{:#x}..{:#x}", self.start, self.end)) 414 .field("count", &self.count) 415 .field("Relocations", &self.to_vec()) 416 .finish() 417 } 418 } 419 420 impl<'a> RelocSection<'a> { 421 #[cfg(feature = "endian_fd")] 422 /// Parse a REL or RELA section of size `filesz` from `offset`. 423 pub fn parse(bytes: &'a [u8], offset: usize, filesz: usize, is_rela: bool, ctx: Ctx) -> crate::error::Result<RelocSection<'a>> { 424 // TODO: better error message when too large (see symtab implementation) 425 let bytes = bytes.pread_with(offset, filesz)?; 426 427 Ok(RelocSection { 428 bytes: bytes, 429 count: filesz / Reloc::size(is_rela, ctx), 430 ctx: (is_rela, ctx), 431 start: offset, 432 end: offset + filesz, 433 }) 434 } 435 436 /// Try to parse a single relocation from the binary, at `index`. 437 #[inline] 438 pub fn get(&self, index: usize) -> Option<Reloc> { 439 if index >= self.count { 440 None 441 } else { 442 Some(self.bytes.pread_with(index * Reloc::size_with(&self.ctx), self.ctx).unwrap()) 443 } 444 } 445 446 /// The number of relocations in the section. 447 #[inline] 448 pub fn len(&self) -> usize { 449 self.count 450 } 451 452 /// Returns true if section has no relocations. 453 #[inline] 454 pub fn is_empty(&self) -> bool { 455 self.count == 0 456 } 457 458 /// Iterate over all relocations. 459 pub fn iter(&self) -> RelocIterator<'a> { 460 self.into_iter() 461 } 462 463 /// Parse all relocations into a vector. 464 pub fn to_vec(&self) -> Vec<Reloc> { 465 self.iter().collect() 466 } 467 } 468 469 impl<'a, 'b> IntoIterator for &'b RelocSection<'a> { 470 type Item = <RelocIterator<'a> as Iterator>::Item; 471 type IntoIter = RelocIterator<'a>; 472 473 #[inline] 474 fn into_iter(self) -> Self::IntoIter { 475 RelocIterator { 476 bytes: self.bytes, 477 offset: 0, 478 index: 0, 479 count: self.count, 480 ctx: self.ctx, 481 } 482 } 483 } 484 485 pub struct RelocIterator<'a> { 486 bytes: &'a [u8], 487 offset: usize, 488 index: usize, 489 count: usize, 490 ctx: RelocCtx, 491 } 492 493 impl<'a> fmt::Debug for RelocIterator<'a> { 494 fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { 495 fmt.debug_struct("RelocIterator") 496 .field("bytes", &"<... redacted ...>") 497 .field("offset", &self.offset) 498 .field("index", &self.index) 499 .field("count", &self.count) 500 .field("ctx", &self.ctx) 501 .finish() 502 } 503 } 504 505 impl<'a> Iterator for RelocIterator<'a> { 506 type Item = Reloc; 507 508 #[inline] 509 fn next(&mut self) -> Option<Self::Item> { 510 if self.index >= self.count { 511 None 512 } else { 513 self.index += 1; 514 Some(self.bytes.gread_with(&mut self.offset, self.ctx).unwrap()) 515 } 516 } 517 } 518 519 impl<'a> ExactSizeIterator for RelocIterator<'a> { 520 #[inline] 521 fn len(&self) -> usize { 522 self.count - self.index 523 } 524 } 525 } // end if_alloc 526