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, 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> = 145 MachOSymbolTable<'data, 'file, macho::MachHeader32<Endian>>; 146 /// An iterator over the symbols of a `MachOFile64`. 147 pub type MachOSymbolTable64<'data, 'file, Endian = Endianness> = 148 MachOSymbolTable<'data, 'file, macho::MachHeader64<Endian>>; 149 150 /// A symbol table of a `MachOFile`. 151 #[derive(Debug, Clone, Copy)] 152 pub struct MachOSymbolTable<'data, 'file, Mach: MachHeader> { 153 pub(super) file: &'file MachOFile<'data, Mach>, 154 } 155 156 impl<'data, 'file, Mach: MachHeader> read::private::Sealed 157 for MachOSymbolTable<'data, 'file, Mach> 158 { 159 } 160 161 impl<'data, 'file, Mach: MachHeader> ObjectSymbolTable<'data> 162 for MachOSymbolTable<'data, 'file, Mach> 163 { 164 type Symbol = MachOSymbol<'data, 'file, Mach>; 165 type SymbolIterator = MachOSymbolIterator<'data, 'file, Mach>; 166 symbols(&self) -> Self::SymbolIterator167 fn symbols(&self) -> Self::SymbolIterator { 168 MachOSymbolIterator { 169 file: self.file, 170 index: 0, 171 } 172 } 173 symbol_by_index(&self, index: SymbolIndex) -> Result<Self::Symbol>174 fn symbol_by_index(&self, index: SymbolIndex) -> Result<Self::Symbol> { 175 let nlist = self.file.symbols.symbol(index.0)?; 176 MachOSymbol::new(self.file, index, nlist).read_error("Unsupported Mach-O symbol index") 177 } 178 } 179 180 /// An iterator over the symbols of a `MachOFile32`. 181 pub type MachOSymbolIterator32<'data, 'file, Endian = Endianness> = 182 MachOSymbolIterator<'data, 'file, macho::MachHeader32<Endian>>; 183 /// An iterator over the symbols of a `MachOFile64`. 184 pub type MachOSymbolIterator64<'data, 'file, Endian = Endianness> = 185 MachOSymbolIterator<'data, 'file, macho::MachHeader64<Endian>>; 186 187 /// An iterator over the symbols of a `MachOFile`. 188 pub struct MachOSymbolIterator<'data, 'file, Mach: MachHeader> { 189 pub(super) file: &'file MachOFile<'data, Mach>, 190 pub(super) index: usize, 191 } 192 193 impl<'data, 'file, Mach: MachHeader> fmt::Debug for MachOSymbolIterator<'data, 'file, Mach> { fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result194 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 195 f.debug_struct("MachOSymbolIterator").finish() 196 } 197 } 198 199 impl<'data, 'file, Mach: MachHeader> Iterator for MachOSymbolIterator<'data, 'file, Mach> { 200 type Item = MachOSymbol<'data, 'file, Mach>; 201 next(&mut self) -> Option<Self::Item>202 fn next(&mut self) -> Option<Self::Item> { 203 loop { 204 let index = self.index; 205 let nlist = self.file.symbols.symbols.get(index)?; 206 self.index += 1; 207 if let Some(symbol) = MachOSymbol::new(self.file, SymbolIndex(index), nlist) { 208 return Some(symbol); 209 } 210 } 211 } 212 } 213 214 /// A symbol of a `MachOFile32`. 215 pub type MachOSymbol32<'data, 'file, Endian = Endianness> = 216 MachOSymbol<'data, 'file, macho::MachHeader32<Endian>>; 217 /// A symbol of a `MachOFile64`. 218 pub type MachOSymbol64<'data, 'file, Endian = Endianness> = 219 MachOSymbol<'data, 'file, macho::MachHeader64<Endian>>; 220 221 /// A symbol of a `MachOFile`. 222 #[derive(Debug, Clone, Copy)] 223 pub struct MachOSymbol<'data, 'file, Mach: MachHeader> { 224 file: &'file MachOFile<'data, Mach>, 225 index: SymbolIndex, 226 nlist: &'data Mach::Nlist, 227 } 228 229 impl<'data, 'file, Mach: MachHeader> MachOSymbol<'data, 'file, Mach> { new( file: &'file MachOFile<'data, Mach>, index: SymbolIndex, nlist: &'data Mach::Nlist, ) -> Option<Self>230 pub(super) fn new( 231 file: &'file MachOFile<'data, Mach>, 232 index: SymbolIndex, 233 nlist: &'data Mach::Nlist, 234 ) -> Option<Self> { 235 if nlist.n_type() & macho::N_STAB != 0 { 236 return None; 237 } 238 Some(MachOSymbol { file, index, nlist }) 239 } 240 } 241 242 impl<'data, 'file, Mach: MachHeader> read::private::Sealed for MachOSymbol<'data, 'file, Mach> {} 243 244 impl<'data, 'file, Mach: MachHeader> ObjectSymbol<'data> for MachOSymbol<'data, 'file, Mach> { 245 #[inline] index(&self) -> SymbolIndex246 fn index(&self) -> SymbolIndex { 247 self.index 248 } 249 name(&self) -> Result<&'data str>250 fn name(&self) -> Result<&'data str> { 251 let name = self 252 .nlist 253 .name(self.file.endian, self.file.symbols.strings)?; 254 str::from_utf8(name) 255 .ok() 256 .read_error("Non UTF-8 Mach-O symbol name") 257 } 258 259 #[inline] address(&self) -> u64260 fn address(&self) -> u64 { 261 self.nlist.n_value(self.file.endian).into() 262 } 263 264 #[inline] size(&self) -> u64265 fn size(&self) -> u64 { 266 0 267 } 268 kind(&self) -> SymbolKind269 fn kind(&self) -> SymbolKind { 270 self.section() 271 .index() 272 .and_then(|index| self.file.section_internal(index).ok()) 273 .map(|section| match section.kind { 274 SectionKind::Text => SymbolKind::Text, 275 SectionKind::Data 276 | SectionKind::ReadOnlyData 277 | SectionKind::ReadOnlyString 278 | SectionKind::UninitializedData 279 | SectionKind::Common => SymbolKind::Data, 280 SectionKind::Tls | SectionKind::UninitializedTls | SectionKind::TlsVariables => { 281 SymbolKind::Tls 282 } 283 _ => SymbolKind::Unknown, 284 }) 285 .unwrap_or(SymbolKind::Unknown) 286 } 287 section(&self) -> SymbolSection288 fn section(&self) -> SymbolSection { 289 match self.nlist.n_type() & macho::N_TYPE { 290 macho::N_UNDF => SymbolSection::Undefined, 291 macho::N_ABS => SymbolSection::Absolute, 292 macho::N_SECT => { 293 let n_sect = self.nlist.n_sect(); 294 if n_sect != 0 { 295 SymbolSection::Section(SectionIndex(n_sect as usize)) 296 } else { 297 SymbolSection::Unknown 298 } 299 } 300 _ => SymbolSection::Unknown, 301 } 302 } 303 304 #[inline] is_undefined(&self) -> bool305 fn is_undefined(&self) -> bool { 306 self.nlist.n_type() & macho::N_TYPE == macho::N_UNDF 307 } 308 309 #[inline] is_definition(&self) -> bool310 fn is_definition(&self) -> bool { 311 self.nlist.is_definition() 312 } 313 314 #[inline] is_common(&self) -> bool315 fn is_common(&self) -> bool { 316 // Mach-O common symbols are based on section, not symbol 317 false 318 } 319 320 #[inline] is_weak(&self) -> bool321 fn is_weak(&self) -> bool { 322 self.nlist.n_desc(self.file.endian) & (macho::N_WEAK_REF | macho::N_WEAK_DEF) != 0 323 } 324 scope(&self) -> SymbolScope325 fn scope(&self) -> SymbolScope { 326 let n_type = self.nlist.n_type(); 327 if n_type & macho::N_TYPE == macho::N_UNDF { 328 SymbolScope::Unknown 329 } else if n_type & macho::N_EXT == 0 { 330 SymbolScope::Compilation 331 } else if n_type & macho::N_PEXT != 0 { 332 SymbolScope::Linkage 333 } else { 334 SymbolScope::Dynamic 335 } 336 } 337 338 #[inline] is_global(&self) -> bool339 fn is_global(&self) -> bool { 340 self.scope() != SymbolScope::Compilation 341 } 342 343 #[inline] is_local(&self) -> bool344 fn is_local(&self) -> bool { 345 self.scope() == SymbolScope::Compilation 346 } 347 348 #[inline] flags(&self) -> SymbolFlags<SectionIndex>349 fn flags(&self) -> SymbolFlags<SectionIndex> { 350 let n_desc = self.nlist.n_desc(self.file.endian); 351 SymbolFlags::MachO { n_desc } 352 } 353 } 354 355 /// A trait for generic access to `Nlist32` and `Nlist64`. 356 #[allow(missing_docs)] 357 pub trait Nlist: Debug + Pod { 358 type Word: Into<u64>; 359 type Endian: endian::Endian; 360 n_strx(&self, endian: Self::Endian) -> u32361 fn n_strx(&self, endian: Self::Endian) -> u32; n_type(&self) -> u8362 fn n_type(&self) -> u8; n_sect(&self) -> u8363 fn n_sect(&self) -> u8; n_desc(&self, endian: Self::Endian) -> u16364 fn n_desc(&self, endian: Self::Endian) -> u16; n_value(&self, endian: Self::Endian) -> Self::Word365 fn n_value(&self, endian: Self::Endian) -> Self::Word; 366 name<'data>( &self, endian: Self::Endian, strings: StringTable<'data>, ) -> Result<&'data [u8]>367 fn name<'data>( 368 &self, 369 endian: Self::Endian, 370 strings: StringTable<'data>, 371 ) -> Result<&'data [u8]> { 372 strings 373 .get(self.n_strx(endian)) 374 .read_error("Invalid Mach-O symbol name offset") 375 } 376 377 /// Return true if the symbol is a definition of a function or data object. is_definition(&self) -> bool378 fn is_definition(&self) -> bool { 379 let n_type = self.n_type(); 380 n_type & macho::N_STAB == 0 && n_type & macho::N_TYPE != macho::N_UNDF 381 } 382 383 /// Return the library ordinal. 384 /// 385 /// This is either a 1-based index into the dylib load commands, 386 /// or a special ordinal. 387 #[inline] library_ordinal(&self, endian: Self::Endian) -> u8388 fn library_ordinal(&self, endian: Self::Endian) -> u8 { 389 (self.n_desc(endian) >> 8) as u8 390 } 391 } 392 393 impl<Endian: endian::Endian> Nlist for macho::Nlist32<Endian> { 394 type Word = u32; 395 type Endian = Endian; 396 n_strx(&self, endian: Self::Endian) -> u32397 fn n_strx(&self, endian: Self::Endian) -> u32 { 398 self.n_strx.get(endian) 399 } n_type(&self) -> u8400 fn n_type(&self) -> u8 { 401 self.n_type 402 } n_sect(&self) -> u8403 fn n_sect(&self) -> u8 { 404 self.n_sect 405 } n_desc(&self, endian: Self::Endian) -> u16406 fn n_desc(&self, endian: Self::Endian) -> u16 { 407 self.n_desc.get(endian) 408 } n_value(&self, endian: Self::Endian) -> Self::Word409 fn n_value(&self, endian: Self::Endian) -> Self::Word { 410 self.n_value.get(endian) 411 } 412 } 413 414 impl<Endian: endian::Endian> Nlist for macho::Nlist64<Endian> { 415 type Word = u64; 416 type Endian = Endian; 417 n_strx(&self, endian: Self::Endian) -> u32418 fn n_strx(&self, endian: Self::Endian) -> u32 { 419 self.n_strx.get(endian) 420 } n_type(&self) -> u8421 fn n_type(&self) -> u8 { 422 self.n_type 423 } n_sect(&self) -> u8424 fn n_sect(&self) -> u8 { 425 self.n_sect 426 } n_desc(&self, endian: Self::Endian) -> u16427 fn n_desc(&self, endian: Self::Endian) -> u16 { 428 self.n_desc.get(endian) 429 } n_value(&self, endian: Self::Endian) -> Self::Word430 fn n_value(&self, endian: Self::Endian) -> Self::Word { 431 self.n_value.get(endian) 432 } 433 } 434