1 use alloc::vec::Vec; 2 use core::fmt::Debug; 3 use core::{fmt, slice, str}; 4 5 use crate::endian::{self, Endianness}; 6 use crate::macho; 7 use crate::pod::Pod; 8 use crate::read::util::StringTable; 9 use crate::read::{ 10 self, ObjectMap, ObjectMapEntry, ObjectSymbol, ObjectSymbolTable, ReadError, ReadRef, Result, 11 SectionIndex, SectionKind, SymbolFlags, SymbolIndex, SymbolKind, SymbolMap, SymbolMapEntry, 12 SymbolScope, SymbolSection, 13 }; 14 15 use super::{MachHeader, MachOFile}; 16 17 /// A table of symbol entries in a Mach-O file. 18 /// 19 /// Also includes the string table used for the symbol names. 20 #[derive(Debug, Clone, Copy)] 21 pub struct SymbolTable<'data, Mach: MachHeader, R = &'data [u8]> 22 where 23 R: ReadRef<'data>, 24 { 25 symbols: &'data [Mach::Nlist], 26 strings: StringTable<'data, R>, 27 } 28 29 impl<'data, Mach: MachHeader, R: ReadRef<'data>> Default for SymbolTable<'data, Mach, R> { default() -> Self30 fn default() -> Self { 31 SymbolTable { 32 symbols: &[], 33 strings: Default::default(), 34 } 35 } 36 } 37 38 impl<'data, Mach: MachHeader, R: ReadRef<'data>> SymbolTable<'data, Mach, R> { 39 #[inline] new(symbols: &'data [Mach::Nlist], strings: StringTable<'data, R>) -> Self40 pub(super) fn new(symbols: &'data [Mach::Nlist], strings: StringTable<'data, R>) -> Self { 41 SymbolTable { symbols, strings } 42 } 43 44 /// Return the string table used for the symbol names. 45 #[inline] strings(&self) -> StringTable<'data, R>46 pub fn strings(&self) -> StringTable<'data, R> { 47 self.strings 48 } 49 50 /// Iterate over the symbols. 51 #[inline] iter(&self) -> slice::Iter<'data, Mach::Nlist>52 pub fn iter(&self) -> slice::Iter<'data, Mach::Nlist> { 53 self.symbols.iter() 54 } 55 56 /// Return true if the symbol table is empty. 57 #[inline] is_empty(&self) -> bool58 pub fn is_empty(&self) -> bool { 59 self.symbols.is_empty() 60 } 61 62 /// The number of symbols. 63 #[inline] len(&self) -> usize64 pub fn len(&self) -> usize { 65 self.symbols.len() 66 } 67 68 /// Return the symbol at the given index. symbol(&self, index: usize) -> Result<&'data Mach::Nlist>69 pub fn symbol(&self, index: usize) -> Result<&'data Mach::Nlist> { 70 self.symbols 71 .get(index) 72 .read_error("Invalid Mach-O symbol index") 73 } 74 75 /// Construct a map from addresses to a user-defined map entry. map<Entry: SymbolMapEntry, F: Fn(&'data Mach::Nlist) -> Option<Entry>>( &self, f: F, ) -> SymbolMap<Entry>76 pub fn map<Entry: SymbolMapEntry, F: Fn(&'data Mach::Nlist) -> Option<Entry>>( 77 &self, 78 f: F, 79 ) -> SymbolMap<Entry> { 80 let mut symbols = Vec::new(); 81 for nlist in self.symbols { 82 if !nlist.is_definition() { 83 continue; 84 } 85 if let Some(entry) = f(nlist) { 86 symbols.push(entry); 87 } 88 } 89 SymbolMap::new(symbols) 90 } 91 92 /// Construct a map from addresses to symbol names and object file names. object_map(&self, endian: Mach::Endian) -> ObjectMap<'data>93 pub fn object_map(&self, endian: Mach::Endian) -> ObjectMap<'data> { 94 let mut symbols = Vec::new(); 95 let mut objects = Vec::new(); 96 let mut object = None; 97 let mut current_function = None; 98 // Each module starts with one or two N_SO symbols (path, or directory + filename) 99 // and one N_OSO symbol. The module is terminated by an empty N_SO symbol. 100 for nlist in self.symbols { 101 let n_type = nlist.n_type(); 102 if n_type & macho::N_STAB == 0 { 103 continue; 104 } 105 // TODO: includes variables too (N_GSYM, N_STSYM). These may need to get their 106 // address from regular symbols though. 107 match n_type { 108 macho::N_SO => { 109 object = None; 110 } 111 macho::N_OSO => { 112 object = None; 113 if let Ok(name) = nlist.name(endian, self.strings) { 114 if !name.is_empty() { 115 object = Some(objects.len()); 116 objects.push(name); 117 } 118 } 119 } 120 macho::N_FUN => { 121 if let Ok(name) = nlist.name(endian, self.strings) { 122 if !name.is_empty() { 123 current_function = Some((name, nlist.n_value(endian).into())) 124 } else if let Some((name, address)) = current_function.take() { 125 if let Some(object) = object { 126 symbols.push(ObjectMapEntry { 127 address, 128 size: nlist.n_value(endian).into(), 129 name, 130 object, 131 }); 132 } 133 } 134 } 135 } 136 _ => {} 137 } 138 } 139 ObjectMap { 140 symbols: SymbolMap::new(symbols), 141 objects, 142 } 143 } 144 } 145 146 /// An iterator over the symbols of a `MachOFile32`. 147 pub type MachOSymbolTable32<'data, 'file, Endian = Endianness, R = &'data [u8]> = 148 MachOSymbolTable<'data, 'file, macho::MachHeader32<Endian>, R>; 149 /// An iterator over the symbols of a `MachOFile64`. 150 pub type MachOSymbolTable64<'data, 'file, Endian = Endianness, R = &'data [u8]> = 151 MachOSymbolTable<'data, 'file, macho::MachHeader64<Endian>, R>; 152 153 /// A symbol table of a `MachOFile`. 154 #[derive(Debug, Clone, Copy)] 155 pub struct MachOSymbolTable<'data, 'file, Mach, R = &'data [u8]> 156 where 157 Mach: MachHeader, 158 R: ReadRef<'data>, 159 { 160 pub(super) file: &'file MachOFile<'data, Mach, R>, 161 } 162 163 impl<'data, 'file, Mach, R> read::private::Sealed for MachOSymbolTable<'data, 'file, Mach, R> 164 where 165 Mach: MachHeader, 166 R: ReadRef<'data>, 167 { 168 } 169 170 impl<'data, 'file, Mach, R> ObjectSymbolTable<'data> for MachOSymbolTable<'data, 'file, Mach, R> 171 where 172 Mach: MachHeader, 173 R: ReadRef<'data>, 174 { 175 type Symbol = MachOSymbol<'data, 'file, Mach, R>; 176 type SymbolIterator = MachOSymbolIterator<'data, 'file, Mach, R>; 177 symbols(&self) -> Self::SymbolIterator178 fn symbols(&self) -> Self::SymbolIterator { 179 MachOSymbolIterator { 180 file: self.file, 181 index: 0, 182 } 183 } 184 symbol_by_index(&self, index: SymbolIndex) -> Result<Self::Symbol>185 fn symbol_by_index(&self, index: SymbolIndex) -> Result<Self::Symbol> { 186 let nlist = self.file.symbols.symbol(index.0)?; 187 MachOSymbol::new(self.file, index, nlist).read_error("Unsupported Mach-O symbol index") 188 } 189 } 190 191 /// An iterator over the symbols of a `MachOFile32`. 192 pub type MachOSymbolIterator32<'data, 'file, Endian = Endianness, R = &'data [u8]> = 193 MachOSymbolIterator<'data, 'file, macho::MachHeader32<Endian>, R>; 194 /// An iterator over the symbols of a `MachOFile64`. 195 pub type MachOSymbolIterator64<'data, 'file, Endian = Endianness, R = &'data [u8]> = 196 MachOSymbolIterator<'data, 'file, macho::MachHeader64<Endian>, R>; 197 198 /// An iterator over the symbols of a `MachOFile`. 199 pub struct MachOSymbolIterator<'data, 'file, Mach, R = &'data [u8]> 200 where 201 Mach: MachHeader, 202 R: ReadRef<'data>, 203 { 204 pub(super) file: &'file MachOFile<'data, Mach, R>, 205 pub(super) index: usize, 206 } 207 208 impl<'data, 'file, Mach, R> fmt::Debug for MachOSymbolIterator<'data, 'file, Mach, R> 209 where 210 Mach: MachHeader, 211 R: ReadRef<'data>, 212 { fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result213 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 214 f.debug_struct("MachOSymbolIterator").finish() 215 } 216 } 217 218 impl<'data, 'file, Mach, R> Iterator for MachOSymbolIterator<'data, 'file, Mach, R> 219 where 220 Mach: MachHeader, 221 R: ReadRef<'data>, 222 { 223 type Item = MachOSymbol<'data, 'file, Mach, R>; 224 next(&mut self) -> Option<Self::Item>225 fn next(&mut self) -> Option<Self::Item> { 226 loop { 227 let index = self.index; 228 let nlist = self.file.symbols.symbols.get(index)?; 229 self.index += 1; 230 if let Some(symbol) = MachOSymbol::new(self.file, SymbolIndex(index), nlist) { 231 return Some(symbol); 232 } 233 } 234 } 235 } 236 237 /// A symbol of a `MachOFile32`. 238 pub type MachOSymbol32<'data, 'file, Endian = Endianness, R = &'data [u8]> = 239 MachOSymbol<'data, 'file, macho::MachHeader32<Endian>, R>; 240 /// A symbol of a `MachOFile64`. 241 pub type MachOSymbol64<'data, 'file, Endian = Endianness, R = &'data [u8]> = 242 MachOSymbol<'data, 'file, macho::MachHeader64<Endian>, R>; 243 244 /// A symbol of a `MachOFile`. 245 #[derive(Debug, Clone, Copy)] 246 pub struct MachOSymbol<'data, 'file, Mach, R = &'data [u8]> 247 where 248 Mach: MachHeader, 249 R: ReadRef<'data>, 250 { 251 file: &'file MachOFile<'data, Mach, R>, 252 index: SymbolIndex, 253 nlist: &'data Mach::Nlist, 254 } 255 256 impl<'data, 'file, Mach, R> MachOSymbol<'data, 'file, Mach, R> 257 where 258 Mach: MachHeader, 259 R: ReadRef<'data>, 260 { new( file: &'file MachOFile<'data, Mach, R>, index: SymbolIndex, nlist: &'data Mach::Nlist, ) -> Option<Self>261 pub(super) fn new( 262 file: &'file MachOFile<'data, Mach, R>, 263 index: SymbolIndex, 264 nlist: &'data Mach::Nlist, 265 ) -> Option<Self> { 266 if nlist.n_type() & macho::N_STAB != 0 { 267 return None; 268 } 269 Some(MachOSymbol { file, index, nlist }) 270 } 271 } 272 273 impl<'data, 'file, Mach, R> read::private::Sealed for MachOSymbol<'data, 'file, Mach, R> 274 where 275 Mach: MachHeader, 276 R: ReadRef<'data>, 277 { 278 } 279 280 impl<'data, 'file, Mach, R> ObjectSymbol<'data> for MachOSymbol<'data, 'file, Mach, R> 281 where 282 Mach: MachHeader, 283 R: ReadRef<'data>, 284 { 285 #[inline] index(&self) -> SymbolIndex286 fn index(&self) -> SymbolIndex { 287 self.index 288 } 289 name_bytes(&self) -> Result<&'data [u8]>290 fn name_bytes(&self) -> Result<&'data [u8]> { 291 self.nlist.name(self.file.endian, self.file.symbols.strings) 292 } 293 name(&self) -> Result<&'data str>294 fn name(&self) -> Result<&'data str> { 295 let name = self.name_bytes()?; 296 str::from_utf8(name) 297 .ok() 298 .read_error("Non UTF-8 Mach-O symbol name") 299 } 300 301 #[inline] address(&self) -> u64302 fn address(&self) -> u64 { 303 self.nlist.n_value(self.file.endian).into() 304 } 305 306 #[inline] size(&self) -> u64307 fn size(&self) -> u64 { 308 0 309 } 310 kind(&self) -> SymbolKind311 fn kind(&self) -> SymbolKind { 312 self.section() 313 .index() 314 .and_then(|index| self.file.section_internal(index).ok()) 315 .map(|section| match section.kind { 316 SectionKind::Text => SymbolKind::Text, 317 SectionKind::Data 318 | SectionKind::ReadOnlyData 319 | SectionKind::ReadOnlyString 320 | SectionKind::UninitializedData 321 | SectionKind::Common => SymbolKind::Data, 322 SectionKind::Tls | SectionKind::UninitializedTls | SectionKind::TlsVariables => { 323 SymbolKind::Tls 324 } 325 _ => SymbolKind::Unknown, 326 }) 327 .unwrap_or(SymbolKind::Unknown) 328 } 329 section(&self) -> SymbolSection330 fn section(&self) -> SymbolSection { 331 match self.nlist.n_type() & macho::N_TYPE { 332 macho::N_UNDF => SymbolSection::Undefined, 333 macho::N_ABS => SymbolSection::Absolute, 334 macho::N_SECT => { 335 let n_sect = self.nlist.n_sect(); 336 if n_sect != 0 { 337 SymbolSection::Section(SectionIndex(n_sect as usize)) 338 } else { 339 SymbolSection::Unknown 340 } 341 } 342 _ => SymbolSection::Unknown, 343 } 344 } 345 346 #[inline] is_undefined(&self) -> bool347 fn is_undefined(&self) -> bool { 348 self.nlist.n_type() & macho::N_TYPE == macho::N_UNDF 349 } 350 351 #[inline] is_definition(&self) -> bool352 fn is_definition(&self) -> bool { 353 self.nlist.is_definition() 354 } 355 356 #[inline] is_common(&self) -> bool357 fn is_common(&self) -> bool { 358 // Mach-O common symbols are based on section, not symbol 359 false 360 } 361 362 #[inline] is_weak(&self) -> bool363 fn is_weak(&self) -> bool { 364 self.nlist.n_desc(self.file.endian) & (macho::N_WEAK_REF | macho::N_WEAK_DEF) != 0 365 } 366 scope(&self) -> SymbolScope367 fn scope(&self) -> SymbolScope { 368 let n_type = self.nlist.n_type(); 369 if n_type & macho::N_TYPE == macho::N_UNDF { 370 SymbolScope::Unknown 371 } else if n_type & macho::N_EXT == 0 { 372 SymbolScope::Compilation 373 } else if n_type & macho::N_PEXT != 0 { 374 SymbolScope::Linkage 375 } else { 376 SymbolScope::Dynamic 377 } 378 } 379 380 #[inline] is_global(&self) -> bool381 fn is_global(&self) -> bool { 382 self.scope() != SymbolScope::Compilation 383 } 384 385 #[inline] is_local(&self) -> bool386 fn is_local(&self) -> bool { 387 self.scope() == SymbolScope::Compilation 388 } 389 390 #[inline] flags(&self) -> SymbolFlags<SectionIndex>391 fn flags(&self) -> SymbolFlags<SectionIndex> { 392 let n_desc = self.nlist.n_desc(self.file.endian); 393 SymbolFlags::MachO { n_desc } 394 } 395 } 396 397 /// A trait for generic access to `Nlist32` and `Nlist64`. 398 #[allow(missing_docs)] 399 pub trait Nlist: Debug + Pod { 400 type Word: Into<u64>; 401 type Endian: endian::Endian; 402 n_strx(&self, endian: Self::Endian) -> u32403 fn n_strx(&self, endian: Self::Endian) -> u32; n_type(&self) -> u8404 fn n_type(&self) -> u8; n_sect(&self) -> u8405 fn n_sect(&self) -> u8; n_desc(&self, endian: Self::Endian) -> u16406 fn n_desc(&self, endian: Self::Endian) -> u16; n_value(&self, endian: Self::Endian) -> Self::Word407 fn n_value(&self, endian: Self::Endian) -> Self::Word; 408 name<'data, R: ReadRef<'data>>( &self, endian: Self::Endian, strings: StringTable<'data, R>, ) -> Result<&'data [u8]>409 fn name<'data, R: ReadRef<'data>>( 410 &self, 411 endian: Self::Endian, 412 strings: StringTable<'data, R>, 413 ) -> Result<&'data [u8]> { 414 strings 415 .get(self.n_strx(endian)) 416 .read_error("Invalid Mach-O symbol name offset") 417 } 418 419 /// Return true if this is a STAB symbol. 420 /// 421 /// This determines the meaning of the `n_type` field. is_stab(&self) -> bool422 fn is_stab(&self) -> bool { 423 self.n_type() & macho::N_STAB != 0 424 } 425 426 /// Return true if this is an undefined symbol. is_undefined(&self) -> bool427 fn is_undefined(&self) -> bool { 428 let n_type = self.n_type(); 429 n_type & macho::N_STAB == 0 && n_type & macho::N_TYPE == macho::N_UNDF 430 } 431 432 /// Return true if the symbol is a definition of a function or data object. is_definition(&self) -> bool433 fn is_definition(&self) -> bool { 434 let n_type = self.n_type(); 435 n_type & macho::N_STAB == 0 && n_type & macho::N_TYPE != macho::N_UNDF 436 } 437 438 /// Return the library ordinal. 439 /// 440 /// This is either a 1-based index into the dylib load commands, 441 /// or a special ordinal. 442 #[inline] library_ordinal(&self, endian: Self::Endian) -> u8443 fn library_ordinal(&self, endian: Self::Endian) -> u8 { 444 (self.n_desc(endian) >> 8) as u8 445 } 446 } 447 448 impl<Endian: endian::Endian> Nlist for macho::Nlist32<Endian> { 449 type Word = u32; 450 type Endian = Endian; 451 n_strx(&self, endian: Self::Endian) -> u32452 fn n_strx(&self, endian: Self::Endian) -> u32 { 453 self.n_strx.get(endian) 454 } n_type(&self) -> u8455 fn n_type(&self) -> u8 { 456 self.n_type 457 } n_sect(&self) -> u8458 fn n_sect(&self) -> u8 { 459 self.n_sect 460 } n_desc(&self, endian: Self::Endian) -> u16461 fn n_desc(&self, endian: Self::Endian) -> u16 { 462 self.n_desc.get(endian) 463 } n_value(&self, endian: Self::Endian) -> Self::Word464 fn n_value(&self, endian: Self::Endian) -> Self::Word { 465 self.n_value.get(endian) 466 } 467 } 468 469 impl<Endian: endian::Endian> Nlist for macho::Nlist64<Endian> { 470 type Word = u64; 471 type Endian = Endian; 472 n_strx(&self, endian: Self::Endian) -> u32473 fn n_strx(&self, endian: Self::Endian) -> u32 { 474 self.n_strx.get(endian) 475 } n_type(&self) -> u8476 fn n_type(&self) -> u8 { 477 self.n_type 478 } n_sect(&self) -> u8479 fn n_sect(&self) -> u8 { 480 self.n_sect 481 } n_desc(&self, endian: Self::Endian) -> u16482 fn n_desc(&self, endian: Self::Endian) -> u16 { 483 self.n_desc.get(endian) 484 } n_value(&self, endian: Self::Endian) -> Self::Word485 fn n_value(&self, endian: Self::Endian) -> Self::Word { 486 self.n_value.get(endian) 487 } 488 } 489