sbpf-linker 0.1.3

Upstream BPF linker for SBPF V0 programs
use sbpf_assembler::ast::AST;
use sbpf_assembler::astnode::{ASTNode, ROData};
use sbpf_assembler::instruction::Instruction;
use sbpf_assembler::lexer::{ImmediateValue, Token};
use sbpf_assembler::opcode::Opcode;
use sbpf_assembler::parser::ParseResult;

use object::{File, Object, ObjectSection, ObjectSymbol};
use object::RelocationTarget::Symbol;

use anyhow::Result;
use std::collections::HashMap;

pub fn parse_bytecode(bytes: &[u8]) -> Result<ParseResult, String> {
    let mut ast = AST::new();

    let obj = File::parse(bytes).map_err(|e| e.to_string())?;
    let mut rodata_table = HashMap::new();
    if let Some(ro_section) = obj.section_by_name(".rodata") {
        // only handle symbols in the .rodata section for now
        // let ro_section = ro_section.unwrap();
        let mut rodata_offset = 0;
        for symbol in obj.symbols() {
            if symbol.section_index() == Some(ro_section.index()) && symbol.size() > 0 {
                let mut bytes = Vec::new();
                for i in 0..symbol.size() {
                    bytes.push(ImmediateValue::Int(
                        ro_section.data().unwrap()[(symbol.address() + i) as usize] as i64));
                }
                ast.rodata_nodes.push(ASTNode::ROData {
                    rodata: ROData {
                        name: symbol.name().unwrap().to_string(),
                        args: vec![Token::Directive(String::from("byte"), 0..1) //
                                , Token::VectorLiteral(bytes.clone(), 0..1)],
                        span: 0..1,
                    },
                    offset: rodata_offset,
                });
                rodata_table.insert(symbol.address(), symbol.name().unwrap().to_string());
                rodata_offset += symbol.size();
            }
        }
        ast.set_rodata_size(rodata_offset);
    }

    for section in obj.sections() {
        if section.name() == Ok(".text") {
            // parse text section and build instruction nodes
            // lddw takes 16 bytes, other instructions take 8 bytes
            let mut offset = 0;
            while offset < section.data().unwrap().len() {
                let node_len = match Opcode::from_u8(section.data().unwrap()[offset]) {
                    Some(Opcode::Lddw) => 16,
                    _ => 8,
                };
                let node = &section.data().unwrap()[offset..offset + node_len];
                ast.nodes.push(ASTNode::Instruction {
                    instruction: Instruction::from_bytes(node).unwrap(),
                    offset: offset as u64,
                });
                offset += node_len;
            }
            
            if let Some(ro_section) = obj.section_by_name(".rodata") {
                // handle relocations
                for rel in section.relocations() {
                    // only handle relocations for symbols in the .rodata section for now
                    let symbol = match rel.1.target() {
                        Symbol(sym) => Some(obj.symbol_by_index(sym).unwrap()), 
                        _ => None
                    };
                    println!("Symbol: {:?}", symbol);
                
                    if symbol.unwrap().section_index() == Some(ro_section.index()) {
                        println!("Relocation found");
                        // addend is not explicit in the relocation entry, but implicitly encoded
                        // as the immediate value of the instruction
                        let addend = //
                            match ast.get_instruction_at_offset(rel.0 as u64).unwrap()
                                .operands.last().unwrap().clone() {
                                    Token::ImmediateValue(ImmediateValue::Int(val), _) => val,
                                    _ => 0 
                            };

                        // Replace the immediate value with the rodata label
                        let ro_label = rodata_table.get(&(addend as u64)).unwrap();
                        let ro_label_name = ro_label.clone();
                        let node: &mut Instruction = ast.get_instruction_at_offset(rel.0 as u64).unwrap();
                        let last_idx = node.operands.len() - 1;
                        node.operands[last_idx] = Token::Identifier(ro_label_name, 0..1);
                    }
                }
            }
            ast.set_text_size(section.size());
        }
    }

    let parse_result = ast.build_program();
    if let Ok(parse_result) = parse_result {
        Ok(parse_result)
    } else {
        // TODO: handle errors
        Err("Failed to build program".to_string())
    }
}