Next Generation WASM Microkernel Operating System
wasm
os
rust
microkernel
1// Copyright 2025 Jonas Kruckenberg
2//
3// Licensed under the Apache License, Version 2.0, <LICENSE-APACHE or
4// http://apache.org/licenses/LICENSE-2.0> or the MIT license <LICENSE-MIT or
5// http://opensource.org/licenses/MIT>, at your option. This file may not be
6// copied, modified, or distributed except according to those terms.
7
8use core::ffi::c_void;
9use core::{fmt, str};
10
11use fallible_iterator::FallibleIterator;
12use gimli::{EndianSlice, NativeEndian};
13use rustc_demangle::{Demangle, try_demangle};
14use xmas_elf::sections::SectionData;
15use xmas_elf::symbol_table::Entry;
16
17pub enum Symbol<'a> {
18 /// We were able to locate frame information for this symbol, and
19 /// `addr2line`'s frame internally has all the nitty gritty details.
20 Frame {
21 addr: *mut c_void,
22 location: Option<kaddr2line::Location<'a>>,
23 name: Option<&'a str>,
24 },
25 /// Couldn't find debug information, but we found it in the symbol table of
26 /// the elf executable.
27 Symtab { name: &'a str },
28}
29
30impl Symbol<'_> {
31 pub fn name(&self) -> Option<SymbolName<'_>> {
32 match self {
33 Symbol::Frame { name, .. } => {
34 let name = name.as_ref()?;
35 Some(SymbolName::new(name))
36 }
37 Symbol::Symtab { name, .. } => Some(SymbolName::new(name)),
38 }
39 }
40
41 pub fn addr(&self) -> Option<*mut c_void> {
42 match self {
43 Symbol::Frame { addr, .. } => Some(*addr),
44 Symbol::Symtab { .. } => None,
45 }
46 }
47
48 pub fn filename(&self) -> Option<&str> {
49 match self {
50 Symbol::Frame { location, .. } => {
51 let file = location.as_ref()?.file?;
52 Some(file)
53 }
54 Symbol::Symtab { .. } => None,
55 }
56 }
57
58 pub fn lineno(&self) -> Option<u32> {
59 match self {
60 Symbol::Frame { location, .. } => location.as_ref()?.line,
61 Symbol::Symtab { .. } => None,
62 }
63 }
64
65 pub fn colno(&self) -> Option<u32> {
66 match self {
67 Symbol::Frame { location, .. } => location.as_ref()?.column,
68 Symbol::Symtab { .. } => None,
69 }
70 }
71}
72
73impl fmt::Debug for Symbol<'_> {
74 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
75 let mut d = f.debug_struct("Symbol");
76 d.field("name", &self.name());
77 d.field("addr", &self.addr());
78 d.field("filename", &self.filename());
79 d.field("lineno", &self.lineno());
80 d.field("colno", &self.colno());
81 d.finish()
82 }
83}
84
85pub struct SymbolName<'a> {
86 raw: &'a str,
87 demangled: Option<Demangle<'a>>,
88}
89
90impl<'a> SymbolName<'a> {
91 pub fn new(raw: &'a str) -> SymbolName<'a> {
92 let demangled = try_demangle(raw).ok();
93
94 Self { raw, demangled }
95 }
96
97 pub fn as_raw_str(&self) -> &'a str {
98 self.demangled
99 .as_ref()
100 .map(|s| s.as_str())
101 .unwrap_or(self.raw)
102 }
103}
104
105impl fmt::Display for SymbolName<'_> {
106 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
107 if let Some(ref s) = self.demangled {
108 return s.fmt(f);
109 }
110
111 f.write_str(self.raw)
112 }
113}
114
115impl fmt::Debug for SymbolName<'_> {
116 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
117 if let Some(ref s) = self.demangled {
118 return s.fmt(f);
119 }
120
121 f.write_str(self.raw)
122 }
123}
124
125pub struct SymbolsIter<'a, 'ctx> {
126 addr: u64,
127 elf: &'ctx xmas_elf::ElfFile<'a>,
128 symtab: &'ctx [xmas_elf::symbol_table::Entry64],
129 iter: kaddr2line::FrameIter<'ctx, EndianSlice<'a, NativeEndian>>,
130 anything: bool,
131}
132
133impl<'ctx> SymbolsIter<'_, 'ctx> {
134 fn search_symtab(&self) -> Option<&'ctx str> {
135 self.symtab
136 .iter()
137 .find(|sym| sym.value() == self.addr)
138 .map(|sym| sym.get_name(self.elf).unwrap())
139 }
140}
141
142impl<'ctx> FallibleIterator for SymbolsIter<'_, 'ctx> {
143 type Item = Symbol<'ctx>;
144 type Error = gimli::Error;
145
146 fn next(&mut self) -> Result<Option<Self::Item>, Self::Error> {
147 if let Some(frame) = self.iter.next()? {
148 self.anything = true;
149
150 let name = if let Some(func) = frame.function {
151 str::from_utf8(func.name.slice()).ok()
152 } else {
153 self.search_symtab()
154 };
155
156 Ok(Some(Symbol::Frame {
157 addr: self.addr as *mut c_void,
158 location: frame.location,
159 name,
160 }))
161 } else if !self.anything {
162 self.anything = true;
163 // the iterator didn't produce any frames, so let's try the symbol table
164 if let Some(name) = self.search_symtab() {
165 Ok(Some(Symbol::Symtab { name }))
166 } else {
167 Ok(None)
168 }
169 } else {
170 Ok(None)
171 }
172 }
173}
174
175/// Context necessary to resolve an address to its symbol name and source location.
176pub struct SymbolizeContext<'a> {
177 addr2line: kaddr2line::Context<EndianSlice<'a, NativeEndian>>,
178 elf: xmas_elf::ElfFile<'a>,
179 adjust_vma: u64,
180}
181
182impl<'a> SymbolizeContext<'a> {
183 /// # Errors
184 ///
185 /// Returns an error when parsing the DWARF fails.
186 pub fn new(elf: xmas_elf::ElfFile<'a>, adjust_vma: u64) -> gimli::Result<Self> {
187 let dwarf = gimli::Dwarf::load(|section_id| -> gimli::Result<_> {
188 let data = match elf.find_section_by_name(section_id.name()) {
189 Some(section) => section.raw_data(&elf),
190 None => &[],
191 };
192 Ok(EndianSlice::new(data, NativeEndian))
193 })?;
194 let addr2line = kaddr2line::Context::from_dwarf(dwarf)?;
195
196 Ok(Self {
197 addr2line,
198 elf,
199 adjust_vma,
200 })
201 }
202
203 /// # Errors
204 ///
205 /// Returns an error if the given address doesn't correspond to a symbol or parsing the DWARF info
206 /// fails.
207 ///
208 /// # Panics
209 ///
210 /// Panics if the ELF file doesn't contain a symbol table.
211 pub fn resolve_unsynchronized(&self, probe: u64) -> gimli::Result<SymbolsIter<'a, '_>> {
212 let probe = probe - self.adjust_vma;
213 let iter = self.addr2line.find_frames(probe).skip_all_loads()?;
214
215 let symtab = self
216 .elf
217 .section_iter()
218 .find_map(|section| {
219 section
220 .get_data(&self.elf)
221 .ok()
222 .and_then(|data| match data {
223 SectionData::SymbolTable64(symtab) => Some(symtab),
224 _ => None,
225 })
226 })
227 .unwrap();
228
229 Ok(SymbolsIter {
230 addr: probe,
231 elf: &self.elf,
232 symtab,
233 iter,
234 anything: false,
235 })
236 }
237}