use colored::Colorize;
use goblin::elf::sym::{
STB_GLOBAL, STB_LOCAL, STB_WEAK, STT_FILE, STT_FUNC, STT_NOTYPE, STT_OBJECT, STT_SECTION,
};
use goblin::elf::Elf;
use crate::display::{
print_field, print_field_with_raw, print_indexed_item, print_section, print_title,
};
pub fn display_symbols(elf: &Elf) {
print_title("Symbol Tables");
let has_syms = !elf.syms.is_empty();
let has_dynsyms = !elf.dynsyms.is_empty();
if !has_syms && !has_dynsyms {
println!(" {}", "No symbol tables".dimmed());
return;
}
if has_syms {
display_symbol_table(".symtab (Static Symbols)", &elf.syms, &elf.strtab);
}
if has_dynsyms {
display_symbol_table(".dynsym (Dynamic Symbols)", &elf.dynsyms, &elf.dynstrtab);
}
}
fn display_symbol_table(
name: &str,
symbols: &goblin::elf::Symtab,
strtab: &goblin::strtab::Strtab,
) {
print_section(name);
println!(" {} symbols\n", symbols.len().to_string().cyan());
for (i, sym) in symbols.iter().enumerate() {
display_symbol(i, &sym, strtab);
}
}
fn display_symbol(index: usize, sym: &goblin::elf::Sym, strtab: &goblin::strtab::Strtab) {
let name = strtab.get_at(sym.st_name).unwrap_or("<no name>");
if sym.st_name == 0 && sym.st_value == 0 && sym.st_size == 0 && index == 0 {
return;
}
let display_name = if name.is_empty() {
match sym.st_type() {
STT_SECTION => "(section)",
STT_FILE => "(file)",
_ => "(unnamed)",
}
} else {
name
};
print_indexed_item(index, display_name);
if !name.is_empty() {
print_field("Name", name);
}
print_field("Value", &format!("{:#018X}", sym.st_value));
if sym.st_size > 0 {
print_field("Size", &format!("{} bytes", sym.st_size));
}
let type_str = symbol_type_to_string(sym.st_type());
print_field_with_raw("Type", type_str, &format!("{}", sym.st_type()));
let bind_str = symbol_binding_to_string(sym.st_bind());
print_field_with_raw("Binding", bind_str, &format!("{}", sym.st_bind()));
let vis_str = symbol_visibility_to_string(sym.st_visibility());
print_field_with_raw("Visibility", vis_str, &format!("{}", sym.st_visibility()));
let shndx_str = section_index_to_string(sym.st_shndx);
print_field_with_raw("Section Index", &shndx_str, &format!("{}", sym.st_shndx));
println!();
}
fn symbol_type_to_string(st_type: u8) -> &'static str {
match st_type {
STT_NOTYPE => "NOTYPE (Unspecified)",
STT_OBJECT => "OBJECT (Data object)",
STT_FUNC => "FUNC (Function)",
3 => "SECTION (Section)",
STT_FILE => "FILE (Source file name)",
5 => "COMMON (Common data object)",
6 => "TLS (Thread-local storage)",
10 => "GNU_IFUNC (Indirect function)",
_ => "Unknown",
}
}
fn symbol_binding_to_string(st_bind: u8) -> &'static str {
match st_bind {
STB_LOCAL => "LOCAL (Not visible outside object)",
STB_GLOBAL => "GLOBAL (Visible to all objects)",
STB_WEAK => "WEAK (Like global, but lower precedence)",
10 => "GNU_UNIQUE (Unique symbol)",
_ => "Unknown",
}
}
fn symbol_visibility_to_string(st_vis: u8) -> &'static str {
match st_vis {
0 => "DEFAULT",
1 => "INTERNAL",
2 => "HIDDEN",
3 => "PROTECTED",
_ => "Unknown",
}
}
fn section_index_to_string(shndx: usize) -> String {
match shndx {
0 => "UNDEF (Undefined)".to_string(),
0xFFF1 => "ABS (Absolute)".to_string(),
0xFFF2 => "COMMON (Common)".to_string(),
0xFFFF => "XINDEX (Extended)".to_string(),
n if (0xFF00..=0xFFFF).contains(&n) => format!("RESERVED ({})", n),
n => n.to_string(),
}
}