//! This module contains the definitions for a Symbol. use std::collections::HashSet; use uuid::Uuid; use crate::{model::module::ModuleId, symtab::SymbolTable}; /// Tuple struct for type safety. Has methods for fetching symbols by ID. #[derive(Debug, PartialEq, Eq, Hash, Copy, Clone)] pub struct SymbolId(Uuid); impl From for SymbolId { fn from(sym: Symbol) -> Self { sym.id } } impl SymbolId { pub fn new() -> Self { Self(Uuid::new_v4()) } /// Convenience method to get the [`Module`] from a [`ModuleId`]. pub fn to_module<'s>(&self, registry: &'s SymbolTable) -> Option<&'s Symbol> { registry.get(self) } /// Convenience method to get the [`Module`] name from a [`ModuleId`]. pub fn to_module_name<'m>(self, registry: &'m SymbolTable) -> Option<&'m str> { if let Some(module) = self.to_module(®istry) { Some(module.name.as_str()) } else { None } } } /// A symbol is a named reference that may be resolved later to an address by a linker. #[derive(Debug)] pub struct Symbol { /// Stored cheaply instead of the name. Shall be stored in the symbol table under /// this key. pub id: SymbolId, /// The human-readable name for the symbol. pub name: String, pub visibility: Visibility, pub symbol_type: SymbolType, /// The id of the module the symbol is defined in. This will be different for symbols /// in different objects. pub module_id: ModuleId, /// Whether or not the symbol requires relocating. pub needs_relocation: bool, /// A list of the symbol's dependencies. /// /// e.g. /// /// ```dsa /// main: /// call another_func /// /// another_func: /// // Code goes here /// ret /// ``` /// /// Where `main` depends on `another_func`. pub dependencies: HashSet, /// The address of the symbol. pub address: Option, /// The section the symbol is in. /// TODO: Perhaps make this a proper type? pub section: Option, pub size: Option, } impl Symbol { pub fn new( name: String, module_id: ModuleId, visibility: Visibility, symbol_type: SymbolType, ) -> Self { Self { id: SymbolId::new(), name, module_id, address: None, section: None, size: None, visibility, symbol_type, needs_relocation: false, dependencies: HashSet::new(), } } /// Adds a dependency on another [`Symbol`]. pub fn add_dependency(&mut self, dep: SymbolId) { if self.id == dep { return; } // We can resolve a lot of addresses at assembly time, but not really foreign // ones, since we aren't certain of their position. // /* TODO: Handle this for flat binary case i.e. no linker required. This may be * done using a similar method to before, such as just concatenating all * of the files together and handling jumps and halts. * * > Ask Harry or read the initial code. */ if self.dependencies.insert(dep) { self.needs_relocation = true; } } /// Returns whether a [`Symbol`] depends on `symbol_id`. pub fn depends_on(&self, symbol_id: &SymbolId) -> bool { self.dependencies.contains(symbol_id) } /// Removes a [`Symbol`] from the dependency set. pub fn remove_dependency(&mut self, symbol_id: &SymbolId) { self.dependencies.remove(symbol_id); if self.dependencies.is_empty() { self.needs_relocation = false; } } } #[derive(Debug, Copy, Clone)] /// The visibility of the symbol in different object files. pub enum Visibility { /// STB_PUBLIC under the ELF spec. Visible in all other object files. Shall be used /// for labels. Remember labels are namespaced in different files so they won't clash /// with one another. Public, /// Only visible within this object file. STB_LOCAL under ELF spec. Shall be used for /// data definitions unless they are marked public. Local, /// STB_WEAK under the ELF spec. Potentially unused. Weak, } #[derive(Debug)] pub enum SymbolType { LabelOrFunction, Variable, }