Переглянути джерело

chore: improve docs, Cargo.manifest, lint and format project

Sonic 1 місяць тому
батько
коміт
986bfa2a83
6 змінених файлів з 113 додано та 59 видалено
  1. 5 0
      Cargo.toml
  2. 40 6
      Readme.md
  3. 3 0
      rustfmt.toml
  4. 14 15
      src/bin/sbpf-link.rs
  5. 20 21
      src/bin/sbpf-linker.rs
  6. 31 17
      src/byteparser.rs

+ 5 - 0
Cargo.toml

@@ -5,6 +5,11 @@ version = "0.1.3"
 authors = ["Claire Fan <claire@blueshift.gg>"]
 authors = ["Claire Fan <claire@blueshift.gg>"]
 description = "Upstream BPF linker for SBPF V0 programs"
 description = "Upstream BPF linker for SBPF V0 programs"
 license = "MIT"
 license = "MIT"
+readme = "README.md"
+homepage = "https://github.com/blueshift-gg/sbpf-linker"
+repository = "https://github.com/blueshift-gg/sbpf-linker"
+keywords = ["cli", "sbpf", "linker", "solana"]
+categories = ["command-line-utilities"]
 
 
 [lib]
 [lib]
 crate-type = ["cdylib", "lib"]
 crate-type = ["cdylib", "lib"]

+ 40 - 6
Readme.md

@@ -1,19 +1,53 @@
-# SBPF Linker
+<h1 align="center">
+  SBPF Linker
+</h1>
+<p align="center">
+  An upstream BPF linker to relink upstream BPF binaries into an SBPF V0 compatible binary format.
+</p>
 
 
-An upstream BPF linker to relink upstream BPF binaries into an SBPF V0 compatible binary format.
+### Install
 
 
-### Usage
-Install with:
 ```sh
 ```sh
 cargo install sbpf-linker
 cargo install sbpf-linker
 ```
 ```
 
 
-Create a new program template with
+### Usage
+
+```
+Usage: sbpf-linker [OPTIONS] --output <OUTPUT> <INPUTS>...
+
+Arguments:
+  <INPUTS>...  Input files. Can be object files or static libraries
+
+Options:
+      --cpu <CPU>                       Target BPF processor. Can be one of `generic`, `probe`, `v1`, `v2`, `v3` [default: generic]
+  -o, --output <OUTPUT>                 Write output to <output>
+      --btf                             Emit BTF information
+      --allow-bpf-trap                  Permit automatic insertion of __`bpf_trap` calls. See: <https://github.com/llvm/llvm-project/commit/ab391beb11f733b526b86f9df23734a34657d876>
+  -L <LIBS>                             Add a directory to the library search path
+  -O <OPTIMIZE>                         Optimization level. 0-3, s, or z [default: 2]
+      --export-symbols <path>           Export the symbols specified in the file `path`. The symbols must be separated by new lines
+      --unroll-loops                    Try hard to unroll loops. Useful when targeting kernels that don't support loops
+      --ignore-inline-never             Ignore `noinline`/`#[inline(never)]`. Useful when targeting kernels that don't support function calls
+      --dump-module <path>              Dump the final IR module to the given `path` before generating the code
+      --llvm-args <args>                Extra command line arguments to pass to LLVM
+      --disable-expand-memcpy-in-order  Disable passing --bpf-expand-memcpy-in-order to LLVM
+      --disable-memory-builtins         Disable exporting `memcpy`, `memmove`, `memset`, `memcmp` and `bcmp`. Exporting those is commonly needed when LLVM does not manage to expand memory intrinsics to a sequence of loads and stores
+      --export <symbols>                Comma separated list of symbols to export. See also `--export-symbols`
+      --fatal-errors <FATAL_ERRORS>     Whether to treat LLVM errors as fatal [default: true] [possible values: true, false]
+  -h, --help                            Print help
+  -V, --version                         Print version
+```
+
+
+### Generate a Program
+
 ```sh
 ```sh
 cargo generate --git https://github.com/blueshift-gg/solana-upstream-bpf-template
 cargo generate --git https://github.com/blueshift-gg/solana-upstream-bpf-template
 ```
 ```
 
 
-Build program with:
+### Build
+
 ```sh
 ```sh
 cargo build-bpf
 cargo build-bpf
 ```
 ```

+ 3 - 0
rustfmt.toml

@@ -0,0 +1,3 @@
+max_width = 79
+use_small_heuristics = "Max"
+edition = "2024"

+ 14 - 15
src/bin/sbpf-link.rs

@@ -3,15 +3,6 @@ use sbpf_linker::{SbpfLinkerError, link_program};
 use std::fs;
 use std::fs;
 use std::path::{Path, PathBuf};
 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>, SbpfLinkerError> {
-    // Read the object file into a byte array
-    let bytes = fs::read(path.as_ref())?;
-
-    // Call link_program on the bytes
-    link_program(&bytes)
-}
-
 #[derive(Debug, Parser)]
 #[derive(Debug, Parser)]
 #[command(
 #[command(
     name = "sbpf-link",
     name = "sbpf-link",
@@ -33,12 +24,9 @@ fn main() -> Result<(), SbpfLinkerError> {
 
 
     // Determine output path in same directory with .so extension
     // Determine output path in same directory with .so extension
     let parent = args.input.parent().unwrap_or_else(|| Path::new("."));
     let parent = args.input.parent().unwrap_or_else(|| Path::new("."));
-    let stem = args
-        .input
-        .file_stem()
-        .and_then(|s| s.to_str())
-        .unwrap_or("output");
-    let output = parent.join(format!("{}.so", stem));
+    let stem =
+        args.input.file_stem().and_then(|s| s.to_str()).unwrap_or("output");
+    let output = parent.join(format!("{stem}.so"));
 
 
     // Write the output
     // Write the output
     println!("Writing output to: {}", output.display());
     println!("Writing output to: {}", output.display());
@@ -47,3 +35,14 @@ fn main() -> Result<(), SbpfLinkerError> {
     println!("Successfully linked {} bytes", linked_bytecode.len());
     println!("Successfully linked {} bytes", linked_bytecode.len());
     Ok(())
     Ok(())
 }
 }
+
+/// 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>, SbpfLinkerError> {
+    // Read the object file into a byte array
+    let bytes = fs::read(path.as_ref())?;
+
+    // Call link_program on the bytes
+    link_program(&bytes)
+}

+ 20 - 21
src/bin/sbpf-linker.rs

@@ -12,7 +12,9 @@ use sbpf_linker::{SbpfLinkerError, link_program};
 
 
 #[derive(Debug, thiserror::Error)]
 #[derive(Debug, thiserror::Error)]
 enum CliError {
 enum CliError {
-    #[error("optimization level needs to be between 0-3, s or z (instead was `{0}`)")]
+    #[error(
+        "optimization level needs to be between 0-3, s or z (instead was `{0}`)"
+    )]
     InvalidOptimization(String),
     InvalidOptimization(String),
     #[error("SBPF Linker Error. Error detail: ({0}).")]
     #[error("SBPF Linker Error. Error detail: ({0}).")]
     SbpfLinkerError(#[from] SbpfLinkerError),
     SbpfLinkerError(#[from] SbpfLinkerError),
@@ -60,8 +62,8 @@ struct CommandLine {
     #[clap(long)]
     #[clap(long)]
     btf: bool,
     btf: bool,
 
 
-    /// Permit automatic insertion of __bpf_trap calls.
-    /// See: https://github.com/llvm/llvm-project/commit/ab391beb11f733b526b86f9df23734a34657d876
+    /// Permit automatic insertion of __`bpf_trap` calls.
+    /// See: <https://github.com/llvm/llvm-project/commit/ab391beb11f733b526b86f9df23734a34657d876>
     #[clap(long)]
     #[clap(long)]
     allow_bpf_trap: bool,
     allow_bpf_trap: bool,
 
 
@@ -97,7 +99,7 @@ struct CommandLine {
     #[clap(long)]
     #[clap(long)]
     disable_expand_memcpy_in_order: bool,
     disable_expand_memcpy_in_order: bool,
 
 
-    /// Disable exporting memcpy, memmove, memset, memcmp and bcmp. Exporting
+    /// Disable exporting `memcpy`, `memmove`, `memset`, `memcmp` and `bcmp`. Exporting
     /// those is commonly needed when LLVM does not manage to expand memory
     /// those is commonly needed when LLVM does not manage to expand memory
     /// intrinsics to a sequence of loads and stores.
     /// intrinsics to a sequence of loads and stores.
     #[clap(long)]
     #[clap(long)]
@@ -122,11 +124,7 @@ struct CommandLine {
 
 
 fn main() -> Result<(), CliError> {
 fn main() -> Result<(), CliError> {
     let args = env::args().map(|arg| {
     let args = env::args().map(|arg| {
-        if arg == "-flavor" {
-            "--flavor".to_string()
-        } else {
-            arg
-        }
+        if arg == "-flavor" { "--flavor".to_string() } else { arg }
     });
     });
 
 
     let CommandLine {
     let CommandLine {
@@ -158,10 +156,10 @@ fn main() -> Result<(), CliError> {
         },
         },
     };
     };
 
 
-    let export_symbols = export_symbols
-        .map(fs::read_to_string)
-        .transpose()
-        .map_err(|e| CliError::SbpfLinkerError(SbpfLinkerError::ObjectFileReadError(e)))?;
+    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.
     // TODO: the data is owned by this call frame; we could make this zero-alloc.
     let export_symbols = export_symbols
     let export_symbols = export_symbols
@@ -181,7 +179,7 @@ fn main() -> Result<(), CliError> {
     let mut linker = Linker::new(LinkerOptions {
     let mut linker = Linker::new(LinkerOptions {
         target: Some("bpf".to_string()),
         target: Some("bpf".to_string()),
         cpu,
         cpu,
-        cpu_features: "".to_string(),
+        cpu_features: String::new(),
         inputs,
         inputs,
         output: output.clone(),
         output: output.clone(),
         output_type: OutputType::Object,
         output_type: OutputType::Object,
@@ -201,9 +199,9 @@ fn main() -> Result<(), CliError> {
         allow_bpf_trap,
         allow_bpf_trap,
     });
     });
 
 
-    linker
-        .link()
-        .map_err(|e| CliError::SbpfLinkerError(SbpfLinkerError::LinkerError(e)))?;
+    linker.link().map_err(|e| {
+        CliError::SbpfLinkerError(SbpfLinkerError::LinkerError(e))
+    })?;
 
 
     if fatal_errors && linker.has_errors() {
     if fatal_errors && linker.has_errors() {
         return Err(CliError::SbpfLinkerError(
         return Err(CliError::SbpfLinkerError(
@@ -211,9 +209,10 @@ fn main() -> Result<(), CliError> {
         ));
         ));
     }
     }
 
 
-    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 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)
     let src_name = std::path::Path::new(&output)
         .file_stem()
         .file_stem()
@@ -222,7 +221,7 @@ fn main() -> Result<(), CliError> {
     let output_path = std::path::Path::new(&output)
     let output_path = std::path::Path::new(&output)
         .parent()
         .parent()
         .unwrap_or_else(|| std::path::Path::new("."))
         .unwrap_or_else(|| std::path::Path::new("."))
-        .join(format!("{}.so", src_name));
+        .join(format!("{src_name}.so"));
     std::fs::write(output_path, bytecode)
     std::fs::write(output_path, bytecode)
         .map_err(|e| CliError::ProgramWriteError { msg: e.to_string() })?;
         .map_err(|e| CliError::ProgramWriteError { msg: e.to_string() })?;
 
 

+ 31 - 17
src/byteparser.rs

@@ -6,7 +6,7 @@ use sbpf_assembler::opcode::Opcode;
 use sbpf_assembler::parser::ParseResult;
 use sbpf_assembler::parser::ParseResult;
 
 
 use object::RelocationTarget::Symbol;
 use object::RelocationTarget::Symbol;
-use object::{File, Object, ObjectSection, ObjectSymbol};
+use object::{File, Object as _, ObjectSection as _, ObjectSymbol as _};
 
 
 use std::collections::HashMap;
 use std::collections::HashMap;
 
 
@@ -21,16 +21,19 @@ pub fn parse_bytecode(bytes: &[u8]) -> Result<ParseResult, SbpfLinkerError> {
         // only handle symbols in the .rodata section for now
         // only handle symbols in the .rodata section for now
         let mut rodata_offset = 0;
         let mut rodata_offset = 0;
         for symbol in obj.symbols() {
         for symbol in obj.symbols() {
-            if symbol.section_index() == Some(ro_section.index()) && symbol.size() > 0 {
+            if symbol.section_index() == Some(ro_section.index())
+                && symbol.size() > 0
+            {
                 let mut bytes = Vec::new();
                 let mut bytes = Vec::new();
                 for i in 0..symbol.size() {
                 for i in 0..symbol.size() {
-                    bytes.push(ImmediateValue::Int(
-                        ro_section.data().unwrap()[(symbol.address() + i) as usize] as i64,
-                    ));
+                    bytes.push(ImmediateValue::Int(i64::from(
+                        ro_section.data().unwrap()
+                            [(symbol.address() + i) as usize],
+                    )));
                 }
                 }
                 ast.rodata_nodes.push(ASTNode::ROData {
                 ast.rodata_nodes.push(ASTNode::ROData {
                     rodata: ROData {
                     rodata: ROData {
-                        name: symbol.name().unwrap().to_string(),
+                        name: symbol.name().unwrap().to_owned(),
                         args: vec![
                         args: vec![
                             Token::Directive(String::from("byte"), 0..1), //
                             Token::Directive(String::from("byte"), 0..1), //
                             Token::VectorLiteral(bytes.clone(), 0..1),
                             Token::VectorLiteral(bytes.clone(), 0..1),
@@ -39,7 +42,10 @@ pub fn parse_bytecode(bytes: &[u8]) -> Result<ParseResult, SbpfLinkerError> {
                     },
                     },
                     offset: rodata_offset,
                     offset: rodata_offset,
                 });
                 });
-                rodata_table.insert(symbol.address(), symbol.name().unwrap().to_string());
+                rodata_table.insert(
+                    symbol.address(),
+                    symbol.name().unwrap().to_owned(),
+                );
                 rodata_offset += symbol.size();
                 rodata_offset += symbol.size();
             }
             }
         }
         }
@@ -52,10 +58,11 @@ pub fn parse_bytecode(bytes: &[u8]) -> Result<ParseResult, SbpfLinkerError> {
             // lddw takes 16 bytes, other instructions take 8 bytes
             // lddw takes 16 bytes, other instructions take 8 bytes
             let mut offset = 0;
             let mut offset = 0;
             while offset < section.data().unwrap().len() {
             while offset < section.data().unwrap().len() {
-                let node_len = match Opcode::from_u8(section.data().unwrap()[offset]) {
-                    Some(Opcode::Lddw) => 16,
-                    _ => 8,
-                };
+                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];
                 let node = &section.data().unwrap()[offset..offset + node_len];
                 ast.nodes.push(ASTNode::Instruction {
                 ast.nodes.push(ASTNode::Instruction {
                     instruction: Instruction::from_bytes(node).unwrap(),
                     instruction: Instruction::from_bytes(node).unwrap(),
@@ -72,9 +79,11 @@ pub fn parse_bytecode(bytes: &[u8]) -> Result<ParseResult, SbpfLinkerError> {
                         Symbol(sym) => Some(obj.symbol_by_index(sym).unwrap()),
                         Symbol(sym) => Some(obj.symbol_by_index(sym).unwrap()),
                         _ => None,
                         _ => None,
                     };
                     };
-                    println!("Symbol: {:?}", symbol);
+                    println!("Symbol: {symbol:?}");
 
 
-                    if symbol.unwrap().section_index() == Some(ro_section.index()) {
+                    if symbol.unwrap().section_index()
+                        == Some(ro_section.index())
+                    {
                         println!("Relocation found");
                         println!("Relocation found");
                         // addend is not explicit in the relocation entry, but implicitly encoded
                         // addend is not explicit in the relocation entry, but implicitly encoded
                         // as the immediate value of the instruction
                         // as the immediate value of the instruction
@@ -86,16 +95,21 @@ pub fn parse_bytecode(bytes: &[u8]) -> Result<ParseResult, SbpfLinkerError> {
                             .unwrap()
                             .unwrap()
                             .clone()
                             .clone()
                         {
                         {
-                            Token::ImmediateValue(ImmediateValue::Int(val), _) => val,
+                            Token::ImmediateValue(
+                                ImmediateValue::Int(val),
+                                _,
+                            ) => val,
                             _ => 0,
                             _ => 0,
                         };
                         };
 
 
                         // Replace the immediate value with the rodata label
                         // Replace the immediate value with the rodata label
-                        let ro_label = rodata_table.get(&(addend as u64)).unwrap();
+                        let ro_label = &rodata_table[&(addend as u64)];
                         let ro_label_name = ro_label.clone();
                         let ro_label_name = ro_label.clone();
-                        let node: &mut Instruction = ast.get_instruction_at_offset(rel.0).unwrap();
+                        let node: &mut Instruction =
+                            ast.get_instruction_at_offset(rel.0).unwrap();
                         let last_idx = node.operands.len() - 1;
                         let last_idx = node.operands.len() - 1;
-                        node.operands[last_idx] = Token::Identifier(ro_label_name, 0..1);
+                        node.operands[last_idx] =
+                            Token::Identifier(ro_label_name, 0..1);
                     }
                     }
                 }
                 }
             }
             }