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