Pārlūkot izejas kodu

feat: use custom errors

Sonic 1 mēnesi atpakaļ
vecāks
revīzija
9fec311f93
7 mainītis faili ar 117 papildinājumiem un 77 dzēšanām
  1. 0 1
      Cargo.lock
  2. 0 1
      Cargo.toml
  3. 21 0
      LICENSE
  4. 11 11
      src/bin/sbpf-link.rs
  5. 37 29
      src/bin/sbpf-linker.rs
  6. 29 26
      src/byteparser.rs
  7. 19 9
      src/lib.rs

+ 0 - 1
Cargo.lock

@@ -831,7 +831,6 @@ dependencies = [
 name = "sbpf-linker"
 version = "0.1.3"
 dependencies = [
- "anyhow",
  "aya-rustc-llvm-proxy",
  "bpf-linker",
  "clap",

+ 0 - 1
Cargo.toml

@@ -14,7 +14,6 @@ name = "sbpf_linker"
 sbpf-assembler = "0.1.2"
 clap = { version = "4.5.13", features = ["derive"] }
 object = "0.37.3"
-anyhow = "1.0"
 bpf-linker = "0.9.15"
 thiserror = "2.0.12"
 

+ 21 - 0
LICENSE

@@ -0,0 +1,21 @@
+MIT License
+
+Copyright (c) 2025 Claire Fan
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.

+ 11 - 11
src/bin/sbpf-link.rs

@@ -1,14 +1,13 @@
-use std::path::{Path, PathBuf};
-use std::fs;
 use clap::Parser;
-use sbpf_linker::link_program;
+use sbpf_linker::{SbpfLinkerError, link_program};
+use std::fs;
+use std::path::{Path, PathBuf};
 
 /// Links an object file by reading it from the given path and processing its bytecode
-fn link_object_file<P: AsRef<Path>>(path: P) -> Result<Vec<u8>, String> {
+fn link_object_file<P: AsRef<Path>>(path: P) -> Result<Vec<u8>, SbpfLinkerError> {
     // Read the object file into a byte array
-    let bytes = fs::read(path.as_ref())
-        .map_err(|e| format!("Failed to read object file: {}", e))?;
-    
+    let bytes = fs::read(path.as_ref())?;
+
     // Call link_program on the bytes
     link_program(&bytes)
 }
@@ -30,12 +29,14 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
 
     // Link the object file
     println!("Linking: {}", args.input.display());
-    let linked_bytecode = link_object_file(&args.input)
-        .map_err(|e| format!("Failed to link object file: {}", e))?;
+    let linked_bytecode =
+        link_object_file(&args.input).map_err(|e| format!("Failed to link object file: {}", e))?;
 
     // Determine output path in same directory with .so extension
     let parent = args.input.parent().unwrap_or_else(|| Path::new("."));
-    let stem = args.input.file_stem()
+    let stem = args
+        .input
+        .file_stem()
         .and_then(|s| s.to_str())
         .unwrap_or("output");
     let output = parent.join(format!("{}.so", stem));
@@ -47,4 +48,3 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
     println!("Successfully linked {} bytes", linked_bytecode.len());
     Ok(())
 }
-

+ 37 - 29
src/bin/sbpf-linker.rs

@@ -1,9 +1,4 @@
-use std::{
-    env, fs,
-    ffi::CString,
-    path::PathBuf,
-    str::FromStr,
-};
+use std::{env, ffi::CString, fs, path::PathBuf, str::FromStr};
 
 #[cfg(any(
     feature = "rust-llvm-19",
@@ -11,21 +6,24 @@ use std::{
     feature = "rust-llvm-21"
 ))]
 use aya_rustc_llvm_proxy as _;
-use anyhow::Context;
-use thiserror::Error;
 use bpf_linker::{Cpu, Linker, LinkerOptions, OptLevel, OutputType};
-use clap::{
-    error::ErrorKind,
-    Parser,
-};
-use sbpf_linker::link_program;
+use clap::{Parser, error::ErrorKind};
+use sbpf_linker::{SbpfLinkerError, link_program};
 
-#[derive(Debug, Error)]
+#[derive(Debug, thiserror::Error)]
 enum CliError {
     #[error("optimization level needs to be between 0-3, s or z (instead was `{0}`)")]
     InvalidOptimization(String),
-//     #[error("unknown emission type: `{0}` - expected one of: `llvm-bc`, `asm`, `llvm-ir`, `obj`")]
-//     InvalidOutputType(String),
+    #[error("SBPF Linker Error. Error detail: ({0}).")]
+    SbpfLinkerError(#[from] SbpfLinkerError),
+    #[error("Clap Error. Error detail: ({0}).")]
+    ClapError(#[from] clap::error::Error),
+    #[error("Program Read Error. Error detail: ({msg}).")]
+    ProgramReadError { msg: String },
+    #[error("Program Write Error. Error detail: ({msg}).")]
+    ProgramWriteError { msg: String },
+    //     #[error("unknown emission type: `{0}` - expected one of: `llvm-bc`, `asm`, `llvm-ir`, `obj`")]
+    //     InvalidOutputType(String),
 }
 
 #[derive(Copy, Clone, Debug)]
@@ -53,7 +51,7 @@ struct CommandLine {
     /// Target BPF processor. Can be one of `generic`, `probe`, `v1`, `v2`, `v3`
     #[clap(long, default_value = "generic")]
     cpu: Cpu,
-    
+
     /// Write output to <output>
     #[clap(short, long)]
     output: PathBuf,
@@ -122,7 +120,7 @@ struct CommandLine {
     _debug: bool,
 }
 
-fn main() -> anyhow::Result<()> {
+fn main() -> Result<(), CliError> {
     let args = env::args().map(|arg| {
         if arg == "-flavor" {
             "--flavor".to_string()
@@ -160,7 +158,10 @@ fn main() -> anyhow::Result<()> {
         },
     };
 
-    let export_symbols = export_symbols.map(fs::read_to_string).transpose()?;
+    let export_symbols = export_symbols
+        .map(fs::read_to_string)
+        .transpose()
+        .map_err(|e| CliError::SbpfLinkerError(SbpfLinkerError::ObjectFileReadError(e)))?;
 
     // TODO: the data is owned by this call frame; we could make this zero-alloc.
     let export_symbols = export_symbols
@@ -171,12 +172,12 @@ fn main() -> anyhow::Result<()> {
         .chain(export)
         .map(Into::into)
         .collect();
-    
+
     let optimize = match *optimize.as_slice() {
         [] => unreachable!("emit has a default value"),
         [.., CliOptLevel(optimize)] => optimize,
     };
-    
+
     let mut linker = Linker::new(LinkerOptions {
         target: Some("bpf".to_string()),
         cpu,
@@ -190,24 +191,30 @@ fn main() -> anyhow::Result<()> {
         unroll_loops,
         ignore_inline_never,
         dump_module,
-        llvm_args: llvm_args.into_iter().map(|cstring| cstring.into_string().unwrap_or_default()).collect(),
+        llvm_args: llvm_args
+            .into_iter()
+            .map(|cstring| cstring.into_string().unwrap_or_default())
+            .collect(),
         disable_expand_memcpy_in_order,
         disable_memory_builtins,
         btf,
         allow_bpf_trap,
     });
 
-    linker.link()?;
+    linker
+        .link()
+        .map_err(|e| CliError::SbpfLinkerError(SbpfLinkerError::LinkerError(e)))?;
 
     if fatal_errors && linker.has_errors() {
-        return Err(anyhow::anyhow!(
-            "LLVM issued diagnostic with error severity"
+        return Err(CliError::SbpfLinkerError(
+            SbpfLinkerError::LlvmDiagnosticError,
         ));
     }
 
-    let program = std::fs::read(&output).context("Failed to read bytecode")?;
-    let bytecode = link_program(&program)
-        .map_err(|e| anyhow::anyhow!("Link error: {}", e))?;
+    let program =
+        std::fs::read(&output).map_err(|e| CliError::ProgramReadError { msg: e.to_string() })?;
+    let bytecode = link_program(&program).map_err(CliError::SbpfLinkerError)?;
+
     let src_name = std::path::Path::new(&output)
         .file_stem()
         .and_then(|s| s.to_str())
@@ -216,7 +223,8 @@ fn main() -> anyhow::Result<()> {
         .parent()
         .unwrap_or_else(|| std::path::Path::new("."))
         .join(format!("{}.so", src_name));
-    std::fs::write(output_path, bytecode)?;
+    std::fs::write(output_path, bytecode)
+        .map_err(|e| CliError::ProgramWriteError { msg: e.to_string() })?;
 
     Ok(())
 }

+ 29 - 26
src/byteparser.rs

@@ -5,33 +5,36 @@ 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 object::{File, Object, ObjectSection, ObjectSymbol};
 
-use anyhow::Result;
 use std::collections::HashMap;
 
-pub fn parse_bytecode(bytes: &[u8]) -> Result<ParseResult, String> {
+use crate::SbpfLinkerError;
+
+pub fn parse_bytecode(bytes: &[u8]) -> Result<ParseResult, SbpfLinkerError> {
     let mut ast = AST::new();
 
-    let obj = File::parse(bytes).map_err(|e| e.to_string())?;
+    let obj = File::parse(bytes)?;
     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));
+                        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)],
+                        args: vec![
+                            Token::Directive(String::from("byte"), 0..1), //
+                            Token::VectorLiteral(bytes.clone(), 0..1),
+                        ],
                         span: 0..1,
                     },
                     offset: rodata_offset,
@@ -60,32 +63,37 @@ pub fn parse_bytecode(bytes: &[u8]) -> Result<ParseResult, String> {
                 });
                 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
+                        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 
-                            };
+                        let addend = match ast
+                            .get_instruction_at_offset(rel.0)
+                            .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 node: &mut Instruction = ast.get_instruction_at_offset(rel.0).unwrap();
                         let last_idx = node.operands.len() - 1;
                         node.operands[last_idx] = Token::Identifier(ro_label_name, 0..1);
                     }
@@ -95,11 +103,6 @@ pub fn parse_bytecode(bytes: &[u8]) -> Result<ParseResult, String> {
         }
     }
 
-    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())
-    }
+    ast.build_program()
+        .map_err(|errors| SbpfLinkerError::BuildProgramError { errors })
 }

+ 19 - 9
src/lib.rs

@@ -1,19 +1,29 @@
 pub mod byteparser;
+use std::io;
 
+use bpf_linker::LinkerError;
 use byteparser::parse_bytecode;
 
-use sbpf_assembler::Program;
+use sbpf_assembler::{CompileError, Program};
 
-use anyhow::Result;
+#[derive(thiserror::Error, Debug)]
+pub enum SbpfLinkerError {
+    #[error("Error opening object file. Error detail: ({0}).")]
+    ObjectFileOpenError(#[from] object::Error),
+    #[error("Error reading object file. Error detail: ({0}).")]
+    ObjectFileReadError(#[from] io::Error),
+    #[error("Linker Error. Error detail: ({0}).")]
+    LinkerError(#[from] LinkerError),
+    #[error("LLVM issued diagnostic with error severity.")]
+    LlvmDiagnosticError,
+    #[error("Build Program Error. Error details: {errors:?}.")]
+    BuildProgramError { errors: Vec<CompileError> },
+}
 
-pub fn link_program(source: &Vec<u8>) -> Result<Vec<u8>, String> {
-    let parse_result = match parse_bytecode(source) {
-        Ok(program) => program,
-        Err(errors) => {
-            return Err(errors);
-        }
-    };
+pub fn link_program(source: &[u8]) -> Result<Vec<u8>, SbpfLinkerError> {
+    let parse_result = parse_bytecode(source)?;
     let program = Program::from_parse_result(parse_result);
     let bytecode = program.emit_bytecode();
+
     Ok(bytecode)
 }