sbpf_assembler/
dynsym.rs

1use {
2    either::Either,
3    sbpf_common::{instruction::Instruction, opcode::Opcode},
4    std::collections::BTreeMap,
5};
6
7#[derive(Debug)]
8pub struct DynamicSymbol {
9    name: u32,  // index into .dynstr section
10    info: u8,   // symbol binding and type
11    other: u8,  // symbol visibility
12    shndx: u16, // section index
13    value: u64, // symbol value
14    size: u64,  // symbol size
15}
16
17impl DynamicSymbol {
18    pub fn new(name: u32, info: u8, other: u8, shndx: u16, value: u64, size: u64) -> Self {
19        Self {
20            name,
21            info,
22            other,
23            shndx,
24            value,
25            size,
26        }
27    }
28
29    pub fn bytecode(&self) -> Vec<u8> {
30        let mut bytes = Vec::new();
31        bytes.extend(self.name.to_le_bytes());
32        bytes.push(self.info);
33        bytes.push(self.other);
34        bytes.extend(self.shndx.to_le_bytes());
35        bytes.extend(self.value.to_le_bytes());
36        bytes.extend(self.size.to_le_bytes());
37        bytes
38    }
39
40    pub fn get_name(&self) -> u32 {
41        self.name
42    }
43}
44
45#[derive(Debug, Clone, PartialEq)]
46pub enum SymbolKind {
47    EntryPoint,
48    CallTarget,
49}
50
51#[derive(Debug, Default)]
52pub struct DynamicSymbolMap {
53    symbols: BTreeMap<String, Vec<(SymbolKind, u64)>>,
54}
55
56impl DynamicSymbolMap {
57    pub fn new() -> Self {
58        Self::default()
59    }
60
61    pub fn copy(&self) -> Self {
62        Self {
63            symbols: self.symbols.clone(),
64        }
65    }
66
67    pub fn add_symbol(&mut self, name: String, kind: SymbolKind, offset: u64) {
68        self.symbols.entry(name).or_default().push((kind, offset));
69    }
70
71    pub fn add_entry_point(&mut self, name: String, offset: u64) {
72        self.add_symbol(name, SymbolKind::EntryPoint, offset);
73    }
74
75    pub fn add_call_target(&mut self, name: String, offset: u64) {
76        self.add_symbol(name, SymbolKind::CallTarget, offset);
77    }
78
79    pub fn get_entry_points(&self) -> Vec<(String, u64)> {
80        self.get_symbols_by_kind(SymbolKind::EntryPoint)
81    }
82
83    pub fn get_call_targets(&self) -> Vec<(String, u64)> {
84        self.get_symbols_by_kind(SymbolKind::CallTarget)
85    }
86
87    fn get_symbols_by_kind(&self, kind: SymbolKind) -> Vec<(String, u64)> {
88        self.symbols
89            .iter()
90            .filter(|(_, symbols)| symbols.iter().any(|(k, _)| *k == kind))
91            .map(|(name, symbols)| {
92                (
93                    name.clone(),
94                    symbols.iter().find(|(k, _)| *k == kind).unwrap().1,
95                )
96            })
97            .collect()
98    }
99
100    pub fn get_symbol(&self, name: &str) -> Option<&Vec<(SymbolKind, u64)>> {
101        self.symbols.get(name)
102    }
103
104    pub fn get_symbols(&self) -> &BTreeMap<String, Vec<(SymbolKind, u64)>> {
105        &self.symbols
106    }
107}
108
109#[derive(Debug, Clone, Copy, PartialEq)]
110#[repr(u64)]
111pub enum RelocationType {
112    RSbf64Relative = 0x08,
113    RSbfSyscall = 0x0a,
114}
115
116pub fn get_relocation_info(inst: &Instruction) -> (RelocationType, String) {
117    match inst.opcode {
118        Opcode::Lddw => match &inst.imm {
119            Some(Either::Left(identifier)) => (RelocationType::RSbf64Relative, identifier.clone()),
120            _ => panic!("Expected label operand"),
121        },
122        _ => {
123            if let Some(Either::Left(identifier)) = &inst.imm {
124                (RelocationType::RSbfSyscall, identifier.clone())
125            } else {
126                panic!("Expected label operand")
127            }
128        }
129    }
130}
131
132#[derive(Debug, Clone, PartialEq)]
133pub struct RelDyn {
134    offset: u64,
135    rel_type: u64,
136    dynstr_offset: u64,
137}
138
139impl RelDyn {
140    pub fn new(offset: u64, rel_type: u64, dynstr_offset: u64) -> Self {
141        Self {
142            offset,
143            rel_type,
144            dynstr_offset,
145        }
146    }
147
148    pub fn bytecode(&self) -> Vec<u8> {
149        let mut bytes = Vec::new();
150        bytes.extend(self.offset.to_le_bytes());
151
152        if self.rel_type == 0x08 {
153            // 8 bytes rel_type
154            bytes.extend(self.rel_type.to_le_bytes());
155        } else if self.rel_type == 0x0a {
156            // 4 bytes rel_type
157            bytes.extend((self.rel_type as u32).to_le_bytes());
158            // 4 bytes dynstr_offset
159            bytes.extend((self.dynstr_offset as u32).to_le_bytes());
160        }
161
162        bytes
163    }
164}
165
166#[derive(Debug, Default)]
167pub struct RelDynMap {
168    rel_dyns: BTreeMap<u64, Vec<(RelocationType, String)>>,
169}
170
171impl RelDynMap {
172    pub fn new() -> Self {
173        Self::default()
174    }
175
176    pub fn add_rel_dyn(&mut self, offset: u64, rel_type: RelocationType, name: String) {
177        self.rel_dyns
178            .entry(offset)
179            .or_default()
180            .push((rel_type, name));
181    }
182
183    pub fn get_rel_dyns(&self) -> Vec<(u64, RelocationType, String)> {
184        self.rel_dyns
185            .iter()
186            .flat_map(|(offset, rel_types)| {
187                rel_types
188                    .iter()
189                    .map(move |(rel_type, name)| (*offset, *rel_type, name.clone()))
190            })
191            .collect()
192    }
193
194    pub fn copy(&self) -> Self {
195        Self {
196            rel_dyns: self.rel_dyns.clone(),
197        }
198    }
199}
200
201#[cfg(test)]
202mod tests {
203    use {super::*, sbpf_common::inst_param::Register};
204
205    #[test]
206    fn test_dynamic_symbol_get_name() {
207        let sym = DynamicSymbol::new(42, 0x12, 0, 1, 0, 0);
208        assert_eq!(sym.get_name(), 42);
209    }
210
211    #[test]
212    fn test_dynamic_symbol_map_new() {
213        let map = DynamicSymbolMap::new();
214        assert!(map.symbols.is_empty());
215    }
216
217    #[test]
218    fn test_dynamic_symbol_map_add_entry_point() {
219        let mut map = DynamicSymbolMap::new();
220        map.add_entry_point("entrypoint".to_string(), 0x120);
221
222        let entry_points = map.get_entry_points();
223        assert_eq!(entry_points.len(), 1);
224        assert_eq!(entry_points[0].0, "entrypoint");
225        assert_eq!(entry_points[0].1, 0x120);
226    }
227
228    #[test]
229    fn test_dynamic_symbol_map_add_call_target() {
230        let mut map = DynamicSymbolMap::new();
231        map.add_call_target("function".to_string(), 0x200);
232
233        let call_targets = map.get_call_targets();
234        assert_eq!(call_targets.len(), 1);
235        assert_eq!(call_targets[0].0, "function");
236        assert_eq!(call_targets[0].1, 0x200);
237    }
238
239    #[test]
240    fn test_dynamic_symbol_map_get_symbol() {
241        let mut map = DynamicSymbolMap::new();
242        map.add_symbol("test".to_string(), SymbolKind::CallTarget, 100);
243
244        let sym = map.get_symbol("test");
245        assert!(sym.is_some());
246        assert_eq!(sym.unwrap().len(), 1);
247
248        let not_found = map.get_symbol("nonexistent");
249        assert!(not_found.is_none());
250    }
251
252    #[test]
253    fn test_dynamic_symbol_map_get_symbols() {
254        let mut map = DynamicSymbolMap::new();
255        map.add_symbol("func1".to_string(), SymbolKind::CallTarget, 100);
256        map.add_symbol("func2".to_string(), SymbolKind::EntryPoint, 200);
257
258        let symbols = map.get_symbols();
259        assert_eq!(symbols.len(), 2);
260        assert!(symbols.contains_key("func1"));
261        assert!(symbols.contains_key("func2"));
262    }
263
264    #[test]
265    fn test_dynamic_symbol_map_copy() {
266        let mut map = DynamicSymbolMap::new();
267        map.add_entry_point("main".to_string(), 0);
268
269        let copy = map.copy();
270        assert_eq!(copy.symbols.len(), 1);
271        assert!(copy.get_symbol("main").is_some());
272    }
273
274    #[test]
275    fn test_get_relocation_info_lddw() {
276        let inst = Instruction {
277            opcode: Opcode::Lddw,
278            dst: Some(Register { n: 1 }),
279            src: None,
280            off: None,
281            imm: Some(Either::Left("my_data".to_string())),
282            span: 0..10,
283        };
284
285        let (rel_type, name) = get_relocation_info(&inst);
286        assert_eq!(rel_type, RelocationType::RSbf64Relative);
287        assert_eq!(name, "my_data");
288    }
289
290    #[test]
291    fn test_get_relocation_info_call() {
292        let inst = Instruction {
293            opcode: Opcode::Call,
294            dst: None,
295            src: Some(Register { n: 1 }),
296            off: None,
297            imm: Some(Either::Left("my_function".to_string())),
298            span: 0..10,
299        };
300
301        let (rel_type, name) = get_relocation_info(&inst);
302        assert_eq!(rel_type, RelocationType::RSbfSyscall);
303        assert_eq!(name, "my_function");
304    }
305
306    #[test]
307    fn test_rel_dyn_map_add_and_get() {
308        let mut map = RelDynMap::new();
309        map.add_rel_dyn(0x100, RelocationType::RSbf64Relative, "data".to_string());
310        map.add_rel_dyn(0x200, RelocationType::RSbfSyscall, "func".to_string());
311
312        let rel_dyns = map.get_rel_dyns();
313        assert_eq!(rel_dyns.len(), 2);
314    }
315
316    #[test]
317    fn test_rel_dyn_map_copy() {
318        let mut map = RelDynMap::new();
319        map.add_rel_dyn(0x100, RelocationType::RSbf64Relative, "test".to_string());
320
321        let copy = map.copy();
322        assert_eq!(copy.rel_dyns.len(), 1);
323    }
324}