Skip to main content

strat9_kernel/boot/
symbols.rs

1//! Kernel symbol table for panic backtrace resolution.
2//!
3//! Parses the kernel's own ELF `.symtab`/`.strtab` sections at boot time
4//! to provide function name resolution during panics.
5
6use core::sync::atomic::{AtomicBool, Ordering};
7
8/// A single symbol entry: address, size, name offset in string table.
9#[derive(Clone, Copy)]
10struct KernelSymbol {
11    addr: u64,
12    size: u64,
13    name_offset: u32,
14}
15
16/// Global sorted symbol table and string table.
17static INITIALIZED: AtomicBool = AtomicBool::new(false);
18static mut SYMBOLS: [KernelSymbol; 8192] = [KernelSymbol {
19    addr: 0,
20    size: 0,
21    name_offset: 0,
22}; 8192];
23static mut SYMBOL_COUNT: usize = 0;
24static mut STRTAB: [u8; 65536] = [0; 65536];
25static mut STRTAB_LEN: usize = 0;
26
27/// Initialize the symbol table from the kernel ELF. Called once during early boot.
28pub fn init() {
29    if INITIALIZED.load(Ordering::Relaxed) {
30        return;
31    }
32
33    let elf_bytes = match super::dtb_boot::kernel_elf_bytes() {
34        Some(b) => b,
35        None => return,
36    };
37    #[cfg(not(target_arch = "x86_64"))]
38    let elf_bytes: &[u8] = &[];
39
40    let elf = match xmas_elf::ElfFile::new(elf_bytes) {
41        Ok(e) => e,
42        Err(_) => return,
43    };
44
45    // Find .strtab section
46    let strtab_data = find_section_data(&elf, ".strtab");
47    if strtab_data.is_empty() {
48        return;
49    }
50    let copy_len = strtab_data.len().min(65536);
51    unsafe {
52        STRTAB[..copy_len].copy_from_slice(&strtab_data[..copy_len]);
53        STRTAB_LEN = copy_len;
54    }
55
56    // Find .symtab section
57    let symtab_data = find_section_data(&elf, ".symtab");
58    if symtab_data.is_empty() {
59        return;
60    }
61
62    // Parse symbol entries (each is 24 bytes for ELF64: st_name(4) + st_info(1) +
63    // st_other(1) + st_shndx(2) + st_value(8) + st_size(8))
64    let entry_size = 24;
65    let count = symtab_data.len() / entry_size;
66    let max = 8192;
67    let mut n = 0usize;
68
69    for i in 0..count.min(max) {
70        let off = i * entry_size;
71        if off + entry_size > symtab_data.len() {
72            break;
73        }
74        let st_name = u32::from_le_bytes([
75            symtab_data[off],
76            symtab_data[off + 1],
77            symtab_data[off + 2],
78            symtab_data[off + 3],
79        ]);
80        let st_value = u64::from_le_bytes([
81            symtab_data[off + 8],
82            symtab_data[off + 9],
83            symtab_data[off + 10],
84            symtab_data[off + 11],
85            symtab_data[off + 12],
86            symtab_data[off + 13],
87            symtab_data[off + 14],
88            symtab_data[off + 15],
89        ]);
90        let st_size = u64::from_le_bytes([
91            symtab_data[off + 16],
92            symtab_data[off + 17],
93            symtab_data[off + 18],
94            symtab_data[off + 19],
95            symtab_data[off + 20],
96            symtab_data[off + 21],
97            symtab_data[off + 22],
98            symtab_data[off + 23],
99        ]);
100
101        // Skip undefined symbols (st_value == 0)
102        if st_value == 0 {
103            continue;
104        }
105
106        unsafe {
107            SYMBOLS[n] = KernelSymbol {
108                addr: st_value,
109                size: st_size,
110                name_offset: st_name,
111            };
112        }
113        n += 1;
114    }
115
116    // Sort by address for binary search
117    unsafe {
118        SYMBOL_COUNT = n;
119        let syms = &mut SYMBOLS[..n];
120        syms.sort_unstable_by_key(|s| s.addr);
121    }
122
123    INITIALIZED.store(true, Ordering::Relaxed);
124}
125
126/// Find a section's data by name in the ELF.
127fn find_section_data<'a>(elf: &xmas_elf::ElfFile<'a>, name: &str) -> &'a [u8] {
128    match elf.find_section_by_name(name) {
129        Some(section) => section.raw_data(elf),
130        None => &[],
131    }
132}
133
134/// Look up an address in the symbol table. Returns (name, offset_from_symbol).
135/// Uses binary search for O(log n) lookup.
136pub fn lookup(addr: u64) -> Option<(&'static str, u64)> {
137    if !INITIALIZED.load(Ordering::Relaxed) {
138        return None;
139    }
140
141    let syms = unsafe { &SYMBOLS[..SYMBOL_COUNT] };
142
143    // Binary search: find the last symbol with addr <= target
144    let idx = match syms.binary_search_by_key(&addr, |s| s.addr) {
145        Ok(i) => i,
146        Err(i) => {
147            if i == 0 {
148                return None;
149            }
150            i - 1
151        }
152    };
153
154    let sym = &syms[idx];
155    let offset = addr.saturating_sub(sym.addr);
156
157    // Check if addr is within the symbol's size (if size > 0)
158    if sym.size > 0 && offset >= sym.size {
159        return None;
160    }
161
162    let name = get_symbol_name(sym.name_offset);
163    Some((name, offset))
164}
165
166/// Get a symbol name from the string table.
167fn get_symbol_name(offset: u32) -> &'static str {
168    let off = offset as usize;
169    let tab = unsafe { &STRTAB[..STRTAB_LEN] };
170    if off >= tab.len() {
171        return "<unknown>";
172    }
173    let end = tab[off..]
174        .iter()
175        .position(|&b| b == 0)
176        .map(|p| off + p)
177        .unwrap_or(tab.len());
178    core::str::from_utf8(&tab[off..end]).unwrap_or("<unknown>")
179}