goblin_experimental/elf/
sym.rs

1/// === Sym bindings ===
2/// Local symbol.
3pub const STB_LOCAL: u8 = 0;
4/// Global symbol.
5pub const STB_GLOBAL: u8 = 1;
6/// Weak symbol.
7pub const STB_WEAK: u8 = 2;
8/// Number of defined types..
9pub const STB_NUM: u8 = 3;
10/// Start of OS-specific.
11pub const STB_LOOS: u8 = 10;
12/// Unique symbol..
13pub const STB_GNU_UNIQUE: u8 = 10;
14/// End of OS-specific.
15pub const STB_HIOS: u8 = 12;
16/// Start of processor-specific.
17pub const STB_LOPROC: u8 = 13;
18/// End of processor-specific.
19pub const STB_HIPROC: u8 = 15;
20
21/// === Sym types ===
22/// Symbol type is unspecified.
23pub const STT_NOTYPE: u8 = 0;
24/// Symbol is a data object.
25pub const STT_OBJECT: u8 = 1;
26/// Symbol is a code object.
27pub const STT_FUNC: u8 = 2;
28/// Symbol associated with a section.
29pub const STT_SECTION: u8 = 3;
30/// Symbol's name is file name.
31pub const STT_FILE: u8 = 4;
32/// Symbol is a common data object.
33pub const STT_COMMON: u8 = 5;
34/// Symbol is thread-local data object.
35pub const STT_TLS: u8 = 6;
36/// Number of defined types.
37pub const STT_NUM: u8 = 7;
38/// Start of OS-specific.
39pub const STT_LOOS: u8 = 10;
40/// Symbol is indirect code object.
41pub const STT_GNU_IFUNC: u8 = 10;
42/// End of OS-specific.
43pub const STT_HIOS: u8 = 12;
44/// Start of processor-specific.
45pub const STT_LOPROC: u8 = 13;
46/// End of processor-specific.
47pub const STT_HIPROC: u8 = 15;
48
49/// === Sym visibility ===
50/// Default: Visibility is specified by the symbol's binding type
51pub const STV_DEFAULT: u8 = 0;
52/// Internal: use of this attribute is currently reserved.
53pub const STV_INTERNAL: u8 = 1;
54/// Hidden: Not visible to other components, necessarily protected. Binding scope becomes local
55/// when the object is included in an executable or shared object.
56pub const STV_HIDDEN: u8 = 2;
57/// Protected: Symbol defined in current component is visible in other components, but cannot be preempted.
58/// Any reference from within the defining component must be resolved to the definition in that
59/// component.
60pub const STV_PROTECTED: u8 = 3;
61/// Exported: ensures a symbol remains global, cannot be demoted or eliminated by any other symbol
62/// visibility technique.
63pub const STV_EXPORTED: u8 = 4;
64/// Singleton: ensures a symbol remains global, and that a single instance of the definition is
65/// bound to by all references within a process. Cannot be demoted or eliminated.
66pub const STV_SINGLETON: u8 = 5;
67/// Eliminate: extends the hidden attribute. Not written in any symbol table of a dynamic
68/// executable or shared object.
69pub const STV_ELIMINATE: u8 = 6;
70
71/// Get the ST bind.
72///
73/// This is the first four bits of the "info" byte.
74#[inline]
75pub fn st_bind(info: u8) -> u8 {
76    info >> 4
77}
78
79/// Get the ST type.
80///
81/// This is the last four bits of the "info" byte.
82#[inline]
83pub fn st_type(info: u8) -> u8 {
84    info & 0xf
85}
86
87/// Get the ST visibility.
88///
89/// This is the last three bits of the "other" byte.
90#[inline]
91pub fn st_visibility(other: u8) -> u8 {
92    other & 0x7
93}
94
95/// Is this information defining an import?
96#[inline]
97pub fn is_import(info: u8, value: u64) -> bool {
98    let bind = st_bind(info);
99    bind == STB_GLOBAL && value == 0
100}
101
102/// Convenience function to get the &'static str type from the symbols `st_info`.
103#[inline]
104pub fn get_type(info: u8) -> &'static str {
105    type_to_str(st_type(info))
106}
107
108/// Get the string for some bind.
109#[inline]
110pub fn bind_to_str(typ: u8) -> &'static str {
111    match typ {
112        STB_LOCAL => "LOCAL",
113        STB_GLOBAL => "GLOBAL",
114        STB_WEAK => "WEAK",
115        STB_NUM => "NUM",
116        STB_GNU_UNIQUE => "GNU_UNIQUE",
117        _ => "UNKNOWN_STB",
118    }
119}
120
121/// Get the string for some type.
122#[inline]
123pub fn type_to_str(typ: u8) -> &'static str {
124    match typ {
125        STT_NOTYPE => "NOTYPE",
126        STT_OBJECT => "OBJECT",
127        STT_FUNC => "FUNC",
128        STT_SECTION => "SECTION",
129        STT_FILE => "FILE",
130        STT_COMMON => "COMMON",
131        STT_TLS => "TLS",
132        STT_NUM => "NUM",
133        STT_GNU_IFUNC => "GNU_IFUNC",
134        _ => "UNKNOWN_STT",
135    }
136}
137
138/// Get the string for some visibility
139#[inline]
140pub fn visibility_to_str(typ: u8) -> &'static str {
141    match typ {
142        STV_DEFAULT => "DEFAULT",
143        STV_INTERNAL => "INTERNAL",
144        STV_HIDDEN => "HIDDEN",
145        STV_PROTECTED => "PROTECTED",
146        STV_EXPORTED => "EXPORTED",
147        STV_SINGLETON => "SINGLETON",
148        STV_ELIMINATE => "ELIMINATE",
149        _ => "UNKNOWN_STV",
150    }
151}
152
153macro_rules! elf_sym_std_impl {
154    ($size:ty) => {
155        #[cfg(test)]
156        mod tests {
157            use super::*;
158            #[test]
159            fn size_of() {
160                assert_eq!(::std::mem::size_of::<Sym>(), SIZEOF_SYM);
161            }
162        }
163
164        use crate::elf::sym::Sym as ElfSym;
165
166        use core::fmt;
167        use core::slice;
168
169        impl Sym {
170            /// Checks whether this `Sym` has `STB_GLOBAL`/`STB_WEAK` bind and a `st_value` of 0
171            #[inline]
172            pub fn is_import(&self) -> bool {
173                let bind = self.st_info >> 4;
174                (bind == STB_GLOBAL || bind == STB_WEAK) && self.st_value == 0
175            }
176            /// Checks whether this `Sym` has type `STT_FUNC`
177            #[inline]
178            pub fn is_function(&self) -> bool {
179                st_type(self.st_info) == STT_FUNC
180            }
181        }
182
183        impl From<Sym> for ElfSym {
184            #[inline]
185            fn from(sym: Sym) -> Self {
186                ElfSym {
187                    st_name: sym.st_name as usize,
188                    st_info: sym.st_info,
189                    st_other: sym.st_other,
190                    st_shndx: sym.st_shndx as usize,
191                    st_value: u64::from(sym.st_value),
192                    st_size: u64::from(sym.st_size),
193                }
194            }
195        }
196
197        impl From<ElfSym> for Sym {
198            #[inline]
199            fn from(sym: ElfSym) -> Self {
200                Sym {
201                    st_name: sym.st_name as u32,
202                    st_info: sym.st_info,
203                    st_other: sym.st_other,
204                    st_shndx: sym.st_shndx as u16,
205                    st_value: sym.st_value as $size,
206                    st_size: sym.st_size as $size,
207                }
208            }
209        }
210
211        impl fmt::Debug for Sym {
212            fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
213                let bind = st_bind(self.st_info);
214                let typ = st_type(self.st_info);
215                let vis = st_visibility(self.st_other);
216                f.debug_struct("Sym")
217                    .field("st_name", &self.st_name)
218                    .field("st_value", &format_args!("{:x}", self.st_value))
219                    .field("st_size", &self.st_size)
220                    .field(
221                        "st_info",
222                        &format_args!(
223                            "{:x} {} {}",
224                            self.st_info,
225                            bind_to_str(bind),
226                            type_to_str(typ)
227                        ),
228                    )
229                    .field(
230                        "st_other",
231                        &format_args!("{} {}", self.st_other, visibility_to_str(vis)),
232                    )
233                    .field("st_shndx", &self.st_shndx)
234                    .finish()
235            }
236        }
237
238        /// # Safety
239        ///
240        /// This function creates a `Sym` slice directly from a raw pointer
241        #[inline]
242        pub unsafe fn from_raw<'a>(symp: *const Sym, count: usize) -> &'a [Sym] {
243            slice::from_raw_parts(symp, count)
244        }
245
246        if_std! {
247            use crate::error::Result;
248
249            use std::fs::File;
250            use std::io::{Read, Seek};
251            use std::io::SeekFrom::Start;
252
253            pub fn from_fd(fd: &mut File, offset: usize, count: usize) -> Result<Vec<Sym>> {
254                // TODO: AFAIK this shouldn't work, since i pass in a byte size...
255                let mut syms = vec![Sym::default(); count];
256                fd.seek(Start(offset as u64))?;
257                unsafe {
258                    fd.read_exact(plain::as_mut_bytes(&mut *syms))?;
259                }
260                syms.dedup();
261                Ok(syms)
262            }
263        }
264    };
265}
266
267#[cfg(feature = "alloc")]
268use scroll::{Pread, Pwrite, SizeWith};
269
270pub mod sym32 {
271    pub use crate::elf::sym::*;
272
273    #[repr(C)]
274    #[derive(Clone, Copy, PartialEq, Default)]
275    #[cfg_attr(feature = "alloc", derive(Pread, Pwrite, SizeWith))]
276    /// 32-bit Sym - used for both static and dynamic symbol information in a binary
277    pub struct Sym {
278        /// Symbol name (string tbl index)
279        pub st_name: u32,
280        /// Symbol value
281        pub st_value: u32,
282        /// Symbol size
283        pub st_size: u32,
284        /// Symbol type and binding
285        pub st_info: u8,
286        /// Symbol visibility
287        pub st_other: u8,
288        /// Section index
289        pub st_shndx: u16,
290    }
291
292    // Declare that the type is plain.
293    unsafe impl plain::Plain for Sym {}
294
295    pub const SIZEOF_SYM: usize = 4 + 1 + 1 + 2 + 4 + 4;
296
297    elf_sym_std_impl!(u32);
298}
299
300pub mod sym64 {
301    pub use crate::elf::sym::*;
302
303    #[repr(C)]
304    #[derive(Clone, Copy, PartialEq, Default)]
305    #[cfg_attr(feature = "alloc", derive(Pread, Pwrite, SizeWith))]
306    /// 64-bit Sym - used for both static and dynamic symbol information in a binary
307    pub struct Sym {
308        /// Symbol name (string tbl index)
309        pub st_name: u32,
310        /// Symbol type and binding
311        pub st_info: u8,
312        /// Symbol visibility
313        pub st_other: u8,
314        /// Section index
315        pub st_shndx: u16,
316        /// Symbol value
317        pub st_value: u64,
318        /// Symbol size
319        pub st_size: u64,
320    }
321
322    // Declare that the type is plain.
323    unsafe impl plain::Plain for Sym {}
324
325    pub const SIZEOF_SYM: usize = 4 + 1 + 1 + 2 + 8 + 8;
326
327    elf_sym_std_impl!(u64);
328}
329
330use crate::container::{Container, Ctx};
331#[cfg(feature = "alloc")]
332use crate::error::Result;
333#[cfg(feature = "alloc")]
334use alloc::vec::Vec;
335use core::fmt;
336use scroll::ctx;
337use scroll::ctx::SizeWith;
338
339#[derive(Clone, Copy, PartialEq, Default)]
340/// A unified Sym definition - convertible to and from 32-bit and 64-bit variants
341pub struct Sym {
342    pub st_name: usize,
343    pub st_info: u8,
344    pub st_other: u8,
345    pub st_shndx: usize,
346    pub st_value: u64,
347    pub st_size: u64,
348}
349
350impl Sym {
351    #[inline]
352    pub fn size(container: Container) -> usize {
353        Self::size_with(&Ctx::from(container))
354    }
355    /// Checks whether this `Sym` has `STB_GLOBAL`/`STB_WEAK` bind and a `st_value` of 0
356    #[inline]
357    pub fn is_import(&self) -> bool {
358        let bind = self.st_bind();
359        (bind == STB_GLOBAL || bind == STB_WEAK) && self.st_value == 0
360    }
361    /// Checks whether this `Sym` has type `STT_FUNC`
362    #[inline]
363    pub fn is_function(&self) -> bool {
364        st_type(self.st_info) == STT_FUNC
365    }
366    /// Get the ST bind.
367    ///
368    /// This is the first four bits of the "info" byte.
369    #[inline]
370    pub fn st_bind(&self) -> u8 {
371        self.st_info >> 4
372    }
373    /// Get the ST type.
374    ///
375    /// This is the last four bits of the "info" byte.
376    #[inline]
377    pub fn st_type(&self) -> u8 {
378        st_type(self.st_info)
379    }
380    /// Get the ST visibility.
381    ///
382    /// This is the last three bits of the "other" byte.
383    #[inline]
384    pub fn st_visibility(&self) -> u8 {
385        st_visibility(self.st_other)
386    }
387    #[cfg(feature = "endian_fd")]
388    /// Parse `count` vector of ELF symbols from `offset`
389    pub fn parse(bytes: &[u8], mut offset: usize, count: usize, ctx: Ctx) -> Result<Vec<Sym>> {
390        if count > bytes.len() / Sym::size_with(&ctx) {
391            return Err(crate::error::Error::BufferTooShort(count, "symbols"));
392        }
393        let mut syms = Vec::with_capacity(count);
394        for _ in 0..count {
395            let sym = bytes.gread_with(&mut offset, ctx)?;
396            syms.push(sym);
397        }
398        Ok(syms)
399    }
400}
401
402impl fmt::Debug for Sym {
403    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
404        let bind = self.st_bind();
405        let typ = self.st_type();
406        let vis = self.st_visibility();
407        f.debug_struct("Sym")
408            .field("st_name", &self.st_name)
409            .field(
410                "st_info",
411                &format_args!(
412                    "0x{:x} {} {}",
413                    self.st_info,
414                    bind_to_str(bind),
415                    type_to_str(typ)
416                ),
417            )
418            .field(
419                "st_other",
420                &format_args!("{} {}", self.st_other, visibility_to_str(vis)),
421            )
422            .field("st_shndx", &self.st_shndx)
423            .field("st_value", &format_args!("0x{:x}", self.st_value))
424            .field("st_size", &self.st_size)
425            .finish()
426    }
427}
428
429impl ctx::SizeWith<Ctx> for Sym {
430    #[inline]
431    fn size_with(&Ctx { container, .. }: &Ctx) -> usize {
432        match container {
433            Container::Little => sym32::SIZEOF_SYM,
434            Container::Big => sym64::SIZEOF_SYM,
435        }
436    }
437}
438
439if_alloc! {
440    use core::result;
441
442    impl<'a> ctx::TryFromCtx<'a, Ctx> for Sym {
443        type Error = crate::error::Error;
444        #[inline]
445        fn try_from_ctx(bytes: &'a [u8], Ctx { container, le}: Ctx) -> result::Result<(Self, usize), Self::Error> {
446            let sym = match container {
447                Container::Little => {
448                    (bytes.pread_with::<sym32::Sym>(0, le)?.into(), sym32::SIZEOF_SYM)
449                },
450                Container::Big => {
451                    (bytes.pread_with::<sym64::Sym>(0, le)?.into(), sym64::SIZEOF_SYM)
452                }
453            };
454            Ok(sym)
455        }
456    }
457
458    impl ctx::TryIntoCtx<Ctx> for Sym {
459        type Error = crate::error::Error;
460        #[inline]
461        fn try_into_ctx(self, bytes: &mut [u8], Ctx {container, le}: Ctx) -> result::Result<usize, Self::Error> {
462            match container {
463                Container::Little => {
464                    let sym: sym32::Sym = self.into();
465                    Ok(bytes.pwrite_with(sym, 0, le)?)
466                },
467                Container::Big => {
468                    let sym: sym64::Sym = self.into();
469                    Ok(bytes.pwrite_with(sym, 0, le)?)
470                }
471            }
472        }
473    }
474
475    impl ctx::IntoCtx<Ctx> for Sym {
476        #[inline]
477        fn into_ctx(self, bytes: &mut [u8], Ctx {container, le}: Ctx) {
478            match container {
479                Container::Little => {
480                    let sym: sym32::Sym = self.into();
481                    bytes.pwrite_with(sym, 0, le).unwrap();
482                },
483                Container::Big => {
484                    let sym: sym64::Sym = self.into();
485                    bytes.pwrite_with(sym, 0, le).unwrap();
486                }
487            }
488        }
489    }
490}
491
492if_alloc! {
493    #[derive(Default)]
494    /// An ELF symbol table, allowing lazy iteration over symbols
495    pub struct Symtab<'a> {
496        bytes: &'a [u8],
497        count: usize,
498        ctx: Ctx,
499        start: usize,
500        end: usize,
501    }
502
503    impl<'a> fmt::Debug for Symtab<'a> {
504        fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
505            let len = self.bytes.len();
506            fmt.debug_struct("Symtab")
507                .field("bytes", &len)
508                .field("range", &format_args!("{:#x}..{:#x}", self.start, self.end))
509                .field("count", &self.count)
510                .field("Symbols", &self.to_vec())
511                .finish()
512        }
513    }
514
515    impl<'a> Symtab<'a> {
516        /// Parse a table of `count` ELF symbols from `offset`.
517        pub fn parse(bytes: &'a [u8], offset: usize, count: usize, ctx: Ctx) -> Result<Symtab<'a>> {
518            let size = count
519                .checked_mul(Sym::size_with(&ctx))
520                .ok_or_else(|| crate::error::Error::Malformed(
521                    format!("Too many ELF symbols (offset {:#x}, count {})", offset, count)
522                ))?;
523            // TODO: make this a better error message when too large
524            let bytes = bytes.pread_with(offset, size)?;
525            Ok(Symtab { bytes, count, ctx, start: offset, end: offset+size })
526        }
527
528        /// Try to parse a single symbol from the binary, at `index`.
529        #[inline]
530        pub fn get(&self, index: usize) -> Option<Sym> {
531            if index >= self.count {
532                None
533            } else {
534                Some(self.bytes.pread_with(index * Sym::size_with(&self.ctx), self.ctx).unwrap())
535            }
536        }
537
538        /// The number of symbols in the table.
539        #[inline]
540        pub fn len(&self) -> usize {
541            self.count
542        }
543
544        /// The offset of symbol table in elf
545        #[inline]
546        pub fn offset(&self) -> usize {
547            self.start
548        }
549
550        /// The ctx of symbol table
551        #[inline]
552        pub fn ctx(&self) -> &Ctx {
553            &self.ctx
554        }
555
556        /// Returns true if table has no symbols.
557        #[inline]
558        pub fn is_empty(&self) -> bool {
559            self.count == 0
560        }
561
562        /// Iterate over all symbols.
563        #[inline]
564        pub fn iter(&self) -> SymIterator<'a> {
565            self.into_iter()
566        }
567
568        /// Parse all symbols into a vector.
569        pub fn to_vec(&self) -> Vec<Sym> {
570            self.iter().collect()
571        }
572    }
573
574    impl<'a, 'b> IntoIterator for &'b Symtab<'a> {
575        type Item = <SymIterator<'a> as Iterator>::Item;
576        type IntoIter = SymIterator<'a>;
577
578        #[inline]
579        fn into_iter(self) -> Self::IntoIter {
580            SymIterator {
581                bytes: self.bytes,
582                offset: 0,
583                index: 0,
584                count: self.count,
585                ctx: self.ctx,
586            }
587        }
588    }
589
590    /// An iterator over symbols in an ELF symbol table
591    pub struct SymIterator<'a> {
592        bytes: &'a [u8],
593        offset: usize,
594        index: usize,
595        count: usize,
596        ctx: Ctx,
597    }
598
599    impl<'a> Iterator for SymIterator<'a> {
600        type Item = Sym;
601
602        #[inline]
603        fn next(&mut self) -> Option<Self::Item> {
604            if self.index >= self.count {
605                None
606            } else {
607                self.index += 1;
608                Some(self.bytes.gread_with(&mut self.offset, self.ctx).unwrap())
609            }
610        }
611    }
612
613    impl<'a> ExactSizeIterator for SymIterator<'a> {
614        #[inline]
615        fn len(&self) -> usize {
616            self.count - self.index
617        }
618    }
619} // end if_alloc