use colored::Colorize;
use goblin::elf::section_header::{
SHF_ALLOC, SHF_COMPRESSED, SHF_EXECINSTR, SHF_GROUP, SHF_INFO_LINK, SHF_LINK_ORDER, SHF_MERGE,
SHF_OS_NONCONFORMING, SHF_STRINGS, SHF_TLS, SHF_WRITE,
};
use goblin::elf::Elf;
use crate::display::{print_field, print_field_with_raw, print_indexed_item, print_title};
pub fn display_section_headers(elf: &Elf) {
print_title("Section Headers");
if elf.section_headers.is_empty() {
println!(" {}", "No section headers".dimmed());
return;
}
println!(
" {} section headers at offset {:#X}\n",
elf.section_headers.len().to_string().cyan(),
elf.header.e_shoff
);
for (i, sh) in elf.section_headers.iter().enumerate() {
display_section_header(i, sh, elf);
}
}
fn display_section_header(index: usize, sh: &goblin::elf::SectionHeader, elf: &Elf) {
let name = elf.shdr_strtab.get_at(sh.sh_name).unwrap_or("<no name>");
let type_str = section_type_to_string(sh.sh_type);
print_indexed_item(index, if name.is_empty() { "(empty)" } else { name });
print_field("Name", if name.is_empty() { "(empty)" } else { name });
print_field_with_raw("Type", type_str, &format!("0x{:08X}", sh.sh_type));
let flags_str = format_section_flags(sh.sh_flags);
print_field_with_raw("Flags", &flags_str, &format!("0x{:X}", sh.sh_flags));
print_field("Address", &format!("{:#018X}", sh.sh_addr));
print_field("Offset", &format!("{:#018X}", sh.sh_offset));
print_field("Size", &format!("{:#X} ({} bytes)", sh.sh_size, sh.sh_size));
if sh.sh_link != 0 || sh.sh_info != 0 {
print_field("Link", &format!("{}", sh.sh_link));
print_field("Info", &format!("{}", sh.sh_info));
}
if sh.sh_addralign != 0 {
print_field("Alignment", &format!("{:#X}", sh.sh_addralign));
}
if sh.sh_entsize != 0 {
print_field("Entry Size", &format!("{} bytes", sh.sh_entsize));
}
println!();
}
fn section_type_to_string(sh_type: u32) -> &'static str {
match sh_type {
0 => "SHT_NULL (Inactive)",
1 => "SHT_PROGBITS (Program data)",
2 => "SHT_SYMTAB (Symbol table)",
3 => "SHT_STRTAB (String table)",
4 => "SHT_RELA (Relocation entries with addends)",
5 => "SHT_HASH (Symbol hash table)",
6 => "SHT_DYNAMIC (Dynamic linking info)",
7 => "SHT_NOTE (Notes)",
8 => "SHT_NOBITS (BSS, no file space)",
9 => "SHT_REL (Relocation entries)",
10 => "SHT_SHLIB (Reserved)",
11 => "SHT_DYNSYM (Dynamic linker symbol table)",
14 => "SHT_INIT_ARRAY (Constructors)",
15 => "SHT_FINI_ARRAY (Destructors)",
16 => "SHT_PREINIT_ARRAY (Pre-constructors)",
17 => "SHT_GROUP (Section group)",
18 => "SHT_SYMTAB_SHNDX (Extended section indices)",
0x6FFFFFF5 => "SHT_GNU_ATTRIBUTES (GNU attributes)",
0x6FFFFFF6 => "SHT_GNU_HASH (GNU hash table)",
0x6FFFFFF7 => "SHT_GNU_LIBLIST (Prelink library list)",
0x6FFFFFF8 => "SHT_CHECKSUM (Checksum for DSO)",
0x6FFFFFFD => "SHT_GNU_VERDEF (Version definition)",
0x6FFFFFFE => "SHT_GNU_VERNEED (Version requirements)",
0x6FFFFFFF => "SHT_GNU_VERSYM (Version symbol table)",
st if (0x60000000..=0x6FFFFFFF).contains(&st) => "SHT_LOOS..SHT_HIOS (OS-specific)",
st if (0x70000000..=0x7FFFFFFF).contains(&st) => "SHT_LOPROC..SHT_HIPROC (Processor-specific)",
st if st >= 0x80000000 => "SHT_LOUSER..SHT_HIUSER (User-specific)",
_ => "Unknown",
}
}
fn format_section_flags(flags: u64) -> String {
if flags == 0 {
return String::from("none");
}
let mut parts = Vec::new();
if flags & (SHF_WRITE as u64) != 0 {
parts.push("WRITE");
}
if flags & (SHF_ALLOC as u64) != 0 {
parts.push("ALLOC");
}
if flags & (SHF_EXECINSTR as u64) != 0 {
parts.push("EXECINSTR");
}
if flags & (SHF_MERGE as u64) != 0 {
parts.push("MERGE");
}
if flags & (SHF_STRINGS as u64) != 0 {
parts.push("STRINGS");
}
if flags & (SHF_INFO_LINK as u64) != 0 {
parts.push("INFO_LINK");
}
if flags & (SHF_LINK_ORDER as u64) != 0 {
parts.push("LINK_ORDER");
}
if flags & (SHF_OS_NONCONFORMING as u64) != 0 {
parts.push("OS_NONCONFORMING");
}
if flags & (SHF_GROUP as u64) != 0 {
parts.push("GROUP");
}
if flags & (SHF_TLS as u64) != 0 {
parts.push("TLS");
}
if flags & (SHF_COMPRESSED as u64) != 0 {
parts.push("COMPRESSED");
}
if parts.is_empty() {
format!("0x{:X}", flags)
} else {
parts.join(" | ")
}
}
pub fn section_type_short(sh_type: u32) -> &'static str {
match sh_type {
0 => "NULL",
1 => "PROGBITS",
2 => "SYMTAB",
3 => "STRTAB",
4 => "RELA",
5 => "HASH",
6 => "DYNAMIC",
7 => "NOTE",
8 => "NOBITS",
9 => "REL",
10 => "SHLIB",
11 => "DYNSYM",
14 => "INIT_ARRAY",
15 => "FINI_ARRAY",
16 => "PREINIT_ARRAY",
17 => "GROUP",
18 => "SYMTAB_SHNDX",
0x6FFFFFF6 => "GNU_HASH",
0x6FFFFFFD => "VERDEF",
0x6FFFFFFE => "VERNEED",
0x6FFFFFFF => "VERSYM",
_ => "UNKNOWN",
}
}