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