use super::elf_types;
use elf_types::{
st_info, st_other, SHN_ABS, SHN_UNDEF, STB_GLOBAL, STB_LOCAL, STB_WEAK, STT_FILE, STT_FUNC,
STT_NOTYPE, STT_OBJECT, STT_SECTION, STV_DEFAULT,
};
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct ElfSymbolVersion {
pub name: String,
pub is_hidden: bool,
}
impl ElfSymbolVersion {
pub fn new(name: impl Into<String>, is_hidden: bool) -> Self {
ElfSymbolVersion {
name: name.into(),
is_hidden,
}
}
pub fn default_version(name: impl Into<String>) -> Self {
Self::new(name, false) }
pub fn hidden_version(name: impl Into<String>) -> Self {
Self::new(name, true)
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct ElfSymbolEntry {
pub name: String,
pub value: u64,
pub size: u64,
pub binding: u8,
pub sym_type: u8,
pub visibility: u8,
pub section_index: u16,
pub version: Option<ElfSymbolVersion>,
}
impl ElfSymbolEntry {
pub fn new(
name: impl Into<String>,
value: u64,
size: u64,
binding: u8,
sym_type: u8,
visibility: u8,
section_index: u16,
) -> Self {
ElfSymbolEntry {
name: name.into(),
value,
size,
binding,
sym_type,
visibility,
section_index,
version: None,
}
}
pub fn local(
name: impl Into<String>,
value: u64,
size: u64,
sym_type: u8,
section_index: u16,
) -> Self {
Self::new(
name,
value,
size,
STB_LOCAL,
sym_type,
STV_DEFAULT,
section_index,
)
}
pub fn global(
name: impl Into<String>,
value: u64,
size: u64,
sym_type: u8,
section_index: u16,
) -> Self {
Self::new(
name,
value,
size,
STB_GLOBAL,
sym_type,
STV_DEFAULT,
section_index,
)
}
pub fn weak(
name: impl Into<String>,
value: u64,
size: u64,
sym_type: u8,
section_index: u16,
) -> Self {
Self::new(
name,
value,
size,
STB_WEAK,
sym_type,
STV_DEFAULT,
section_index,
)
}
pub fn file_symbol(filename: impl Into<String>) -> Self {
Self::new(filename, 0, 0, STB_LOCAL, STT_FILE, STV_DEFAULT, SHN_ABS)
}
pub fn section_symbol(section_index: u16) -> Self {
Self::new("", 0, 0, STB_LOCAL, STT_SECTION, STV_DEFAULT, section_index)
}
#[inline]
pub fn info_byte(&self) -> u8 {
st_info(self.binding, self.sym_type)
}
#[inline]
pub fn other_byte(&self) -> u8 {
st_other(self.visibility)
}
#[inline]
pub fn is_local(&self) -> bool {
self.binding == STB_LOCAL
}
#[inline]
pub fn is_global(&self) -> bool {
self.binding == STB_GLOBAL
}
#[inline]
pub fn is_weak(&self) -> bool {
self.binding == STB_WEAK
}
#[inline]
pub fn is_undefined(&self) -> bool {
self.section_index == SHN_UNDEF
}
pub fn with_version(mut self, version: ElfSymbolVersion) -> Self {
self.version = Some(version);
self
}
pub fn with_visibility(mut self, visibility: u8) -> Self {
self.visibility = visibility;
self
}
}
#[derive(Debug, Clone)]
pub struct ElfSymbolTable {
pub symbols: Vec<ElfSymbolEntry>,
pub is_dynamic: bool,
}
impl ElfSymbolTable {
pub fn new() -> Self {
let null_entry = ElfSymbolEntry {
name: String::new(),
value: 0,
size: 0,
binding: STB_LOCAL,
sym_type: STT_NOTYPE,
visibility: STV_DEFAULT,
section_index: SHN_UNDEF,
version: None,
};
ElfSymbolTable {
symbols: vec![null_entry],
is_dynamic: false,
}
}
pub fn new_dynamic() -> Self {
let mut t = Self::new();
t.is_dynamic = true;
t
}
pub fn add_symbol(&mut self, entry: ElfSymbolEntry) -> u32 {
let index = self.symbols.len() as u32;
self.symbols.push(entry);
index
}
pub fn add_local_symbol(
&mut self,
name: impl Into<String>,
value: u64,
size: u64,
sym_type: u8,
section_index: u16,
) -> u32 {
self.add_symbol(ElfSymbolEntry::local(
name,
value,
size,
sym_type,
section_index,
))
}
pub fn add_global_symbol(
&mut self,
name: impl Into<String>,
value: u64,
size: u64,
sym_type: u8,
section_index: u16,
) -> u32 {
self.add_symbol(ElfSymbolEntry::global(
name,
value,
size,
sym_type,
section_index,
))
}
pub fn add_weak_symbol(
&mut self,
name: impl Into<String>,
value: u64,
size: u64,
sym_type: u8,
section_index: u16,
) -> u32 {
self.add_symbol(ElfSymbolEntry::weak(
name,
value,
size,
sym_type,
section_index,
))
}
pub fn add_file_symbol(&mut self, filename: impl Into<String>) -> u32 {
self.add_symbol(ElfSymbolEntry::file_symbol(filename))
}
pub fn add_section_symbol(&mut self, section_index: u16) -> u32 {
self.add_symbol(ElfSymbolEntry::section_symbol(section_index))
}
pub fn add_versioned_symbol(
&mut self,
mut entry: ElfSymbolEntry,
version_name: impl Into<String>,
is_default: bool,
) -> u32 {
entry.version = Some(ElfSymbolVersion::new(version_name, !is_default));
self.add_symbol(entry)
}
pub fn get_symbol(&self, index: u32) -> Option<&ElfSymbolEntry> {
self.symbols.get(index as usize)
}
pub fn get_symbol_mut(&mut self, index: u32) -> Option<&mut ElfSymbolEntry> {
self.symbols.get_mut(index as usize)
}
pub fn get_symbol_index(&self, name: &str) -> Option<u32> {
self.symbols
.iter()
.position(|s| s.name == name)
.map(|i| i as u32)
}
pub fn len(&self) -> usize {
self.symbols.len()
}
pub fn is_empty(&self) -> bool {
self.symbols.len() <= 1
}
pub fn num_locals(&self) -> u32 {
self.symbols
.iter()
.take_while(|s| s.binding == STB_LOCAL)
.count() as u32
}
pub fn first_global(&self) -> u32 {
self.num_locals()
}
pub fn iter(&self) -> impl Iterator<Item = &ElfSymbolEntry> {
self.symbols.iter()
}
pub fn globals(&self) -> impl Iterator<Item = &ElfSymbolEntry> {
self.symbols
.iter()
.filter(|s| s.binding == STB_GLOBAL || s.binding == STB_WEAK)
}
pub fn locals(&self) -> impl Iterator<Item = &ElfSymbolEntry> {
self.symbols.iter().filter(|s| s.binding == STB_LOCAL)
}
pub fn build_strtab(&self) -> Vec<u8> {
let mut strtab: Vec<u8> = vec![0u8]; let mut offsets: std::collections::HashMap<String, u32> = std::collections::HashMap::new();
for sym in self.symbols.iter() {
if sym.name.is_empty() {
continue;
}
if offsets.contains_key(&sym.name) {
continue;
}
let offset = strtab.len() as u32;
offsets.insert(sym.name.clone(), offset);
strtab.extend_from_slice(sym.name.as_bytes());
strtab.push(0u8); }
strtab
}
pub fn build_stroff_map(&self) -> Vec<u32> {
let strtab = self.build_strtab();
let mut offsets: Vec<u32> = Vec::with_capacity(self.symbols.len());
for sym in self.symbols.iter() {
if sym.name.is_empty() {
offsets.push(0);
} else {
let name_bytes = sym.name.as_bytes();
if let Some(pos) = strtab
.windows(name_bytes.len() + 1)
.position(|w| w[..name_bytes.len()] == *name_bytes && w[name_bytes.len()] == 0)
{
offsets.push(pos as u32);
} else {
offsets.push(0);
}
}
}
offsets
}
pub fn build_symtab_entries(&self, is_64bit: bool) -> Vec<u8> {
let stroff = self.build_stroff_map();
if is_64bit {
self.build_symtab_entries_64(&stroff)
} else {
self.build_symtab_entries_32(&stroff)
}
}
fn build_symtab_entries_64(&self, stroff: &[u32]) -> Vec<u8> {
let entry_size = 24;
let mut buf: Vec<u8> = Vec::with_capacity(self.symbols.len() * entry_size);
for (i, sym) in self.symbols.iter().enumerate() {
let name_off = stroff.get(i).copied().unwrap_or(0);
buf.extend_from_slice(&name_off.to_le_bytes());
buf.push(sym.info_byte());
buf.push(sym.other_byte());
buf.extend_from_slice(&sym.section_index.to_le_bytes());
buf.extend_from_slice(&sym.value.to_le_bytes());
buf.extend_from_slice(&sym.size.to_le_bytes());
}
buf
}
fn build_symtab_entries_32(&self, stroff: &[u32]) -> Vec<u8> {
let entry_size = 16;
let mut buf: Vec<u8> = Vec::with_capacity(self.symbols.len() * entry_size);
for (i, sym) in self.symbols.iter().enumerate() {
let name_off = stroff.get(i).copied().unwrap_or(0);
buf.extend_from_slice(&name_off.to_le_bytes());
buf.extend_from_slice(&(sym.value as u32).to_le_bytes());
buf.extend_from_slice(&(sym.size as u32).to_le_bytes());
buf.push(sym.info_byte());
buf.push(sym.other_byte());
buf.extend_from_slice(&sym.section_index.to_le_bytes());
}
buf
}
pub fn entry_size(is_64bit: bool) -> usize {
if is_64bit {
24
} else {
16
}
}
pub fn total_size(&self, is_64bit: bool) -> usize {
self.symbols.len() * Self::entry_size(is_64bit)
}
}
impl Default for ElfSymbolTable {
fn default() -> Self {
Self::new()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_new_table_has_null_entry() {
let t = ElfSymbolTable::new();
assert_eq!(t.len(), 1);
let null = t.get_symbol(0).unwrap();
assert!(null.name.is_empty());
assert_eq!(null.value, 0);
assert_eq!(null.size, 0);
assert_eq!(null.binding, STB_LOCAL);
assert_eq!(null.sym_type, STT_NOTYPE);
assert_eq!(null.section_index, SHN_UNDEF);
}
#[test]
fn test_new_dynamic_table() {
let t = ElfSymbolTable::new_dynamic();
assert!(t.is_dynamic);
assert_eq!(t.len(), 1);
}
#[test]
fn test_add_local_symbol() {
let mut t = ElfSymbolTable::new();
let idx = t.add_local_symbol("local_func", 0x1000, 32, STT_FUNC, 1);
assert_eq!(idx, 1);
assert_eq!(t.len(), 2);
let sym = t.get_symbol(1).unwrap();
assert_eq!(sym.name, "local_func");
assert_eq!(sym.value, 0x1000);
assert_eq!(sym.size, 32);
assert_eq!(sym.binding, STB_LOCAL);
assert_eq!(sym.sym_type, STT_FUNC);
assert_eq!(sym.section_index, 1);
}
#[test]
fn test_add_global_symbol() {
let mut t = ElfSymbolTable::new();
let idx = t.add_global_symbol("main", 0x2000, 128, STT_FUNC, 2);
assert_eq!(idx, 1);
let sym = t.get_symbol(1).unwrap();
assert_eq!(sym.name, "main");
assert_eq!(sym.binding, STB_GLOBAL);
assert_eq!(sym.sym_type, STT_FUNC);
}
#[test]
fn test_add_weak_symbol() {
let mut t = ElfSymbolTable::new();
let idx = t.add_weak_symbol("weak_handler", 0x3000, 16, STT_FUNC, 3);
let sym = t.get_symbol(idx).unwrap();
assert_eq!(sym.binding, STB_WEAK);
assert!(sym.is_weak());
assert!(!sym.is_global());
assert!(!sym.is_local());
}
#[test]
fn test_add_file_symbol() {
let mut t = ElfSymbolTable::new();
let idx = t.add_file_symbol("example.c");
let sym = t.get_symbol(idx).unwrap();
assert_eq!(sym.name, "example.c");
assert_eq!(sym.sym_type, STT_FILE);
assert_eq!(sym.binding, STB_LOCAL);
assert_eq!(sym.section_index, SHN_ABS);
}
#[test]
fn test_add_section_symbol() {
let mut t = ElfSymbolTable::new();
let idx = t.add_section_symbol(5);
let sym = t.get_symbol(idx).unwrap();
assert!(sym.name.is_empty());
assert_eq!(sym.sym_type, STT_SECTION);
assert_eq!(sym.binding, STB_LOCAL);
assert_eq!(sym.section_index, 5);
}
#[test]
fn test_multiple_symbols_indexes() {
let mut t = ElfSymbolTable::new();
let a = t.add_local_symbol("a", 0, 0, STT_NOTYPE, SHN_UNDEF);
let b = t.add_global_symbol("b", 0, 0, STT_NOTYPE, SHN_UNDEF);
let c = t.add_global_symbol("c", 0, 0, STT_NOTYPE, SHN_UNDEF);
assert_eq!(a, 1);
assert_eq!(b, 2);
assert_eq!(c, 3);
assert_eq!(t.len(), 4); }
#[test]
fn test_get_symbol_by_index() {
let mut t = ElfSymbolTable::new();
t.add_global_symbol("foo", 0x100, 8, STT_OBJECT, 1);
t.add_global_symbol("bar", 0x200, 8, STT_OBJECT, 2);
assert!(t.get_symbol(0).is_some());
assert!(t.get_symbol(1).is_some());
assert!(t.get_symbol(2).is_some());
assert!(t.get_symbol(3).is_none());
assert!(t.get_symbol(999).is_none());
}
#[test]
fn test_get_symbol_index_by_name() {
let mut t = ElfSymbolTable::new();
t.add_global_symbol("alpha", 0, 0, STT_NOTYPE, SHN_UNDEF);
t.add_global_symbol("beta", 0, 0, STT_NOTYPE, SHN_UNDEF);
t.add_global_symbol("gamma", 0, 0, STT_NOTYPE, SHN_UNDEF);
assert_eq!(t.get_symbol_index("alpha"), Some(1));
assert_eq!(t.get_symbol_index("beta"), Some(2));
assert_eq!(t.get_symbol_index("gamma"), Some(3));
assert_eq!(t.get_symbol_index("delta"), None);
assert_eq!(t.get_symbol_index(""), Some(0)); }
#[test]
fn test_get_symbol_mut() {
let mut t = ElfSymbolTable::new();
t.add_global_symbol("mut_test", 0, 0, STT_NOTYPE, SHN_UNDEF);
{
let sym = t.get_symbol_mut(1).unwrap();
sym.value = 0xDEADBEEF;
}
assert_eq!(t.get_symbol(1).unwrap().value, 0xDEADBEEF);
}
#[test]
fn test_is_empty() {
let t = ElfSymbolTable::new();
assert!(t.is_empty()); let mut t2 = ElfSymbolTable::new();
t2.add_global_symbol("x", 0, 0, STT_NOTYPE, SHN_UNDEF);
assert!(!t2.is_empty());
}
#[test]
fn test_num_locals_only_null() {
let t = ElfSymbolTable::new();
assert_eq!(t.num_locals(), 1);
assert_eq!(t.first_global(), 1);
}
#[test]
fn test_num_locals_mixed() {
let mut t = ElfSymbolTable::new();
t.add_file_symbol("src.c"); t.add_section_symbol(1); t.add_local_symbol("helper", 0, 0, STT_FUNC, 1); t.add_global_symbol("main", 0, 0, STT_FUNC, 1); t.add_global_symbol("other", 0, 0, STT_FUNC, 1); t.add_weak_symbol("weak_sym", 0, 0, STT_FUNC, 1);
assert_eq!(t.num_locals(), 4); assert_eq!(t.first_global(), 4);
}
#[test]
fn test_num_locals_all_local() {
let mut t = ElfSymbolTable::new();
t.add_file_symbol("a.c");
t.add_section_symbol(1);
t.add_local_symbol("f", 0, 0, STT_FUNC, 1);
assert_eq!(t.num_locals(), 4);
}
#[test]
fn test_num_locals_all_global() {
let mut t = ElfSymbolTable::new();
t.add_global_symbol("g", 0, 0, STT_FUNC, 1);
t.add_global_symbol("h", 0, 0, STT_FUNC, 1);
assert_eq!(t.num_locals(), 1); }
#[test]
fn test_iter_all() {
let mut t = ElfSymbolTable::new();
t.add_file_symbol("x.c");
t.add_global_symbol("g", 0, 0, STT_FUNC, 1);
let all: Vec<&str> = t.iter().map(|s| s.name.as_str()).collect();
assert_eq!(all, vec!["", "x.c", "g"]);
}
#[test]
fn test_globals_iterator() {
let mut t = ElfSymbolTable::new();
t.add_file_symbol("f.c");
t.add_global_symbol("g1", 0, 0, STT_FUNC, 1);
t.add_weak_symbol("w1", 0, 0, STT_FUNC, 1);
t.add_local_symbol("l1", 0, 0, STT_FUNC, 1);
t.add_global_symbol("g2", 0, 0, STT_FUNC, 1);
let globals: Vec<&str> = t.globals().map(|s| s.name.as_str()).collect();
assert_eq!(globals, vec!["g1", "w1", "g2"]);
}
#[test]
fn test_locals_iterator() {
let mut t = ElfSymbolTable::new();
t.add_file_symbol("a.c");
t.add_section_symbol(1);
t.add_global_symbol("g", 0, 0, STT_FUNC, 1);
let locals: Vec<&str> = t.locals().map(|s| s.name.as_str()).collect();
assert_eq!(locals.len(), 3);
assert_eq!(locals[0], "");
assert_eq!(locals[1], "a.c");
assert_eq!(locals[2], "");
}
#[test]
fn test_build_strtab_empty() {
let t = ElfSymbolTable::new();
let strtab = t.build_strtab();
assert_eq!(strtab, vec![0u8]);
}
#[test]
fn test_build_strtab_single() {
let mut t = ElfSymbolTable::new();
t.add_global_symbol("main", 0, 0, STT_FUNC, 1);
let strtab = t.build_strtab();
let expected: Vec<u8> = {
let mut v = vec![0u8];
v.extend_from_slice(b"main");
v.push(0u8);
v
};
assert_eq!(strtab, expected);
}
#[test]
fn test_build_strtab_multiple() {
let mut t = ElfSymbolTable::new();
t.add_global_symbol("main", 0, 0, STT_FUNC, 1);
t.add_global_symbol("printf", 0, 0, STT_FUNC, SHN_UNDEF);
t.add_local_symbol("helper", 0, 0, STT_FUNC, 1);
let strtab = t.build_strtab();
assert_eq!(strtab[0], 0u8);
let s = String::from_utf8_lossy(&strtab);
assert!(s.contains("main"));
assert!(s.contains("printf"));
assert!(s.contains("helper"));
assert!(strtab.windows(5).any(|w| w == b"main\0"));
assert!(strtab.windows(7).any(|w| w == b"printf\0"));
assert!(strtab.windows(7).any(|w| w == b"helper\0"));
}
#[test]
fn test_build_strtab_deduplicates() {
let mut t = ElfSymbolTable::new();
t.add_global_symbol("dup", 0x100, 0, STT_OBJECT, 1);
t.add_global_symbol("dup", 0x200, 0, STT_OBJECT, 2); t.add_global_symbol("uniq", 0x300, 0, STT_OBJECT, 3);
let strtab = t.build_strtab();
let dup_count = strtab.windows(4).filter(|w| *w == b"dup\0").count();
assert_eq!(dup_count, 1);
}
#[test]
fn test_build_stroff_map() {
let mut t = ElfSymbolTable::new();
t.add_global_symbol("alpha", 0, 0, STT_FUNC, 1);
t.add_global_symbol("beta", 0, 0, STT_FUNC, 1);
let map = t.build_stroff_map();
assert_eq!(map.len(), 3); assert_eq!(map[0], 0);
let strtab = t.build_strtab();
for (i, &off) in map.iter().enumerate() {
if i == 0 {
assert_eq!(off, 0);
assert_eq!(strtab[off as usize], 0u8);
} else {
let sym = t.get_symbol(i as u32).unwrap();
let name = sym.name.as_bytes();
assert_eq!(&strtab[off as usize..off as usize + name.len()], name);
assert_eq!(strtab[off as usize + name.len()], 0u8); }
}
}
#[test]
fn test_build_symtab_entries_64_empty() {
let t = ElfSymbolTable::new();
let bytes = t.build_symtab_entries(true);
assert_eq!(bytes.len(), 24);
assert!(bytes.iter().all(|&b| b == 0));
}
#[test]
fn test_build_symtab_entries_64_single() {
let mut t = ElfSymbolTable::new();
t.add_global_symbol("main", 0x1000, 0x50, STT_FUNC, 3);
let bytes = t.build_symtab_entries(true);
assert_eq!(bytes.len(), 48);
assert_eq!(&bytes[0..24], &[0u8; 24]);
let entry = &bytes[24..48];
let st_name = u32::from_le_bytes([entry[0], entry[1], entry[2], entry[3]]);
assert_eq!(st_name, 1);
assert_eq!(entry[4], 0x12);
assert_eq!(entry[5], 0);
assert_eq!(u16::from_le_bytes([entry[6], entry[7]]), 3);
assert_eq!(
u64::from_le_bytes([
entry[8], entry[9], entry[10], entry[11], entry[12], entry[13], entry[14],
entry[15]
]),
0x1000
);
assert_eq!(
u64::from_le_bytes([
entry[16], entry[17], entry[18], entry[19], entry[20], entry[21], entry[22],
entry[23]
]),
0x50
);
}
#[test]
fn test_build_symtab_entries_64_with_versioned_symbol() {
let mut t = ElfSymbolTable::new();
let mut sym = ElfSymbolEntry::global("malloc", 0, 0, STT_FUNC, SHN_UNDEF);
sym.version = Some(ElfSymbolVersion::default_version("GLIBC_2.2.5"));
t.add_symbol(sym);
let bytes = t.build_symtab_entries(true);
assert_eq!(bytes.len(), 48);
}
#[test]
fn test_build_symtab_entries_32_empty() {
let t = ElfSymbolTable::new();
let bytes = t.build_symtab_entries(false);
assert_eq!(bytes.len(), 16);
assert!(bytes.iter().all(|&b| b == 0));
}
#[test]
fn test_build_symtab_entries_32_single() {
let mut t = ElfSymbolTable::new();
t.add_global_symbol("fn32", 0xDEAD, 0x20, STT_FUNC, 4);
let bytes = t.build_symtab_entries(false);
assert_eq!(bytes.len(), 32);
assert_eq!(&bytes[0..16], &[0u8; 16]);
let entry = &bytes[16..32];
let st_name = u32::from_le_bytes([entry[0], entry[1], entry[2], entry[3]]);
assert_eq!(st_name, 1);
assert_eq!(
u32::from_le_bytes([entry[4], entry[5], entry[6], entry[7]]),
0xDEAD
);
assert_eq!(
u32::from_le_bytes([entry[8], entry[9], entry[10], entry[11]]),
0x20
);
assert_eq!(entry[12], 0x12);
assert_eq!(entry[13], 0);
assert_eq!(u16::from_le_bytes([entry[14], entry[15]]), 4);
}
#[test]
fn test_entry_size() {
assert_eq!(ElfSymbolTable::entry_size(true), 24);
assert_eq!(ElfSymbolTable::entry_size(false), 16);
}
#[test]
fn test_total_size() {
let mut t = ElfSymbolTable::new();
t.add_global_symbol("a", 0, 0, STT_NOTYPE, SHN_UNDEF);
t.add_global_symbol("b", 0, 0, STT_NOTYPE, SHN_UNDEF);
assert_eq!(t.total_size(true), 3 * 24);
assert_eq!(t.total_size(false), 3 * 16);
}
#[test]
fn test_entry_info_other_byte() {
let entry = ElfSymbolEntry::global("g", 0, 0, STT_FUNC, 1);
assert_eq!(entry.info_byte(), 0x12); assert_eq!(entry.other_byte(), 0x00); }
#[test]
fn test_entry_with_visibility() {
let e =
ElfSymbolEntry::global("g", 0, 0, STT_FUNC, 1).with_visibility(elf_types::STV_HIDDEN);
assert_eq!(e.visibility, elf_types::STV_HIDDEN);
assert_eq!(e.other_byte(), 0x02);
}
#[test]
fn test_entry_with_version() {
let e = ElfSymbolEntry::global("g", 0, 0, STT_FUNC, SHN_UNDEF)
.with_version(ElfSymbolVersion::hidden_version("VER_1"));
assert!(e.version.is_some());
let v = e.version.as_ref().unwrap();
assert_eq!(v.name, "VER_1");
assert!(v.is_hidden);
}
#[test]
fn test_entry_is_undefined() {
let undef = ElfSymbolEntry::global("ext", 0, 0, STT_NOTYPE, SHN_UNDEF);
assert!(undef.is_undefined());
let defined = ElfSymbolEntry::global("local", 0x1000, 4, STT_OBJECT, 3);
assert!(!defined.is_undefined());
}
#[test]
fn test_version_default() {
let v = ElfSymbolVersion::default_version("GLIBC_2.2.5");
assert_eq!(v.name, "GLIBC_2.2.5");
assert!(!v.is_hidden); }
#[test]
fn test_version_hidden() {
let v = ElfSymbolVersion::hidden_version("VER_2");
assert_eq!(v.name, "VER_2");
assert!(v.is_hidden); }
#[test]
fn test_add_versioned_symbol() {
let mut t = ElfSymbolTable::new();
let entry = ElfSymbolEntry::global("foo", 0, 0, STT_FUNC, SHN_UNDEF);
let idx = t.add_versioned_symbol(entry, "GLIBC_2.17", true); let sym = t.get_symbol(idx).unwrap();
assert!(sym.version.is_some());
assert!(!sym.version.as_ref().unwrap().is_hidden);
}
#[test]
fn test_default_creates_valid_table() {
let t: ElfSymbolTable = Default::default();
assert_eq!(t.len(), 1);
assert!(!t.is_dynamic);
}
#[test]
fn test_large_table() {
let mut t = ElfSymbolTable::new();
t.add_file_symbol("large.c");
for i in 0..500 {
t.add_local_symbol(
format!("local_{i}"),
(i * 16) as u64,
8,
STT_OBJECT,
(i % 10) as u16 + 1,
);
}
for i in 0..200 {
t.add_global_symbol(
format!("global_{i}"),
(i * 32) as u64,
16,
STT_FUNC,
(i % 10) as u16 + 1,
);
}
assert_eq!(t.len(), 702);
assert_eq!(t.num_locals(), 502);
assert_eq!(t.first_global(), 502);
let bytes64 = t.build_symtab_entries(true);
assert_eq!(bytes64.len(), 702 * 24);
let bytes32 = t.build_symtab_entries(false);
assert_eq!(bytes32.len(), 702 * 16);
let strtab = t.build_strtab();
assert!(strtab.len() > 1);
assert_eq!(strtab[0], 0u8);
}
}