Aursen преди 1 месец
родител
ревизия
4df3a91d8d
променени са 37 файла, в които са добавени 461 реда и са изтрити 348 реда
  1. 42 0
      .github/workflows/ci.yml
  2. 14 6
      Cargo.toml
  3. 4 3
      crates/assembler/Cargo.toml
  4. 54 55
      crates/assembler/src/ast.rs
  5. 10 7
      crates/assembler/src/astnode.rs
  6. 1 2
      crates/assembler/src/errors.rs
  7. 69 20
      crates/assembler/src/instruction.rs
  8. 5 3
      crates/assembler/src/lexer.rs
  9. 30 32
      crates/assembler/src/parser.rs
  10. 12 12
      crates/assembler/src/program.rs
  11. 12 11
      crates/assembler/src/section.rs
  12. 1 2
      crates/assembler/src/syscall.rs
  13. 7 7
      crates/assembler/src/wasm.rs
  14. 4 6
      crates/assembler/tests/regression.rs
  15. 4 3
      crates/common/Cargo.toml
  16. 1 2
      crates/common/src/errors.rs
  17. 26 18
      crates/common/src/instruction.rs
  18. 5 5
      crates/common/src/opcode.rs
  19. 1 2
      crates/common/src/syscall.rs
  20. 4 3
      crates/disassembler/Cargo.toml
  21. 15 13
      crates/disassembler/src/elf_header.rs
  22. 1 2
      crates/disassembler/src/errors.rs
  23. 8 10
      crates/disassembler/src/program.rs
  24. 7 10
      crates/disassembler/src/program_header.rs
  25. 7 11
      crates/disassembler/src/section_header.rs
  26. 14 11
      crates/disassembler/src/section_header_entry.rs
  27. 6 4
      crates/disassembler/src/wasm.rs
  28. 3 0
      rustfmt.toml
  29. 31 31
      src/commands/build.rs
  30. 9 10
      src/commands/clean.rs
  31. 10 13
      src/commands/deploy.rs
  32. 5 5
      src/commands/disassemble.rs
  33. 12 8
      src/commands/init.rs
  34. 6 10
      src/commands/test.rs
  35. 5 3
      src/main.rs
  36. 2 2
      tests/test_memo.rs
  37. 14 6
      tests/utils.rs

+ 42 - 0
.github/workflows/ci.yml

@@ -0,0 +1,42 @@
+name: CI
+
+on:
+  push:
+    branches: [ master ]
+  pull_request:
+
+env:
+  CARGO_TERM_COLOR: always
+
+jobs:
+  fmt:
+    name: rustfmt (nightly)
+    runs-on: ubuntu-latest
+    steps:
+      - uses: actions/checkout@v5
+      - uses: dtolnay/rust-toolchain@master
+        with:
+          toolchain: nightly
+          components: rustfmt
+      - uses: Swatinem/rust-cache@v2
+        with:
+          cache-on-failure: true
+      - name: rustfmt (check)
+        run: cargo +nightly fmt --all -- --check
+
+  lint-test:
+    name: clippy & tests (stable)
+    runs-on: ubuntu-latest
+    steps:
+      - uses: actions/checkout@v5
+      - uses: dtolnay/rust-toolchain@master
+        with:
+          toolchain: stable
+          components: clippy
+      - uses: Swatinem/rust-cache@v2
+        with:
+          cache-on-failure: true
+      - name: clippy (deny warnings)
+        run: cargo clippy --all-targets --all-features -- -D warnings
+      - name: tests
+        run: cargo test --all-features --all-targets --no-fail-fast

+ 14 - 6
Cargo.toml

@@ -1,14 +1,18 @@
 [package]
 name = "sbpf"
 version = "0.1.5"
-authors = ["Dean Little <dean@blueshift.gg>", "Claire Fan <claire@blueshift.gg>"]
-edition = "2021"
+authors = [
+    "Dean Little <dean@blueshift.gg>",
+    "Claire Fan <claire@blueshift.gg>",
+]
+edition.workspace = true
 description = "A complete toolchain for building and deploying Solana BPF programs"
-license = "MIT OR Apache-2.0"
-repository = "https://github.com/blueshift-gg/sbpf"
+license.workspace = true
+repository.workspace = true
 readme = "README.md"
 keywords = ["solana", "bpf", "blockchain", "assembler"]
 categories = ["development-tools", "command-line-utilities"]
+rust-version.workspace = true
 
 # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
 [dependencies]
@@ -30,10 +34,14 @@ exclude = ["examples"]
 
 [workspace.package]
 version = "0.1.5"
+edition = "2024"
+license = "MIT OR Apache-2.0"
+repository = "https://github.com/blueshift-gg/sbpf"
+rust-version = "1.90"
 
 [workspace.dependencies]
-num-derive    = "0.4"
-num-traits    = "0.2"
+num-derive = "0.4"
+num-traits = "0.2"
 thiserror = "2.0.12"
 anyhow = "1.0.86"
 object = "0.37.3"

+ 4 - 3
crates/assembler/Cargo.toml

@@ -2,11 +2,12 @@
 name = "sbpf-assembler"
 version.workspace = true
 description = "Assembler for SBPF (Solana BPF) assembly language"
-edition = "2024"
-license = "MIT OR Apache-2.0"
-repository = "https://github.com/blueshift-gg/sbpf"
+edition.workspace = true
+license.workspace = true
+repository.workspace = true
 keywords = ["solana", "bpf", "assembler", "blockchain"]
 categories = ["development-tools", "compilers"]
+rust-version.workspace = true
 
 [lib]
 crate-type = ["cdylib", "lib"]

+ 54 - 55
crates/assembler/src/ast.rs

@@ -1,13 +1,16 @@
-use crate::CompileError;
-use crate::astnode::{ASTNode, ROData};
-use crate::dynsym::{DynamicSymbolMap, RelDynMap, RelocationType};
-use crate::instruction::Instruction;
-use crate::lexer::{ImmediateValue, Token};
-use crate::parser::ParseResult;
-use crate::section::{CodeSection, DataSection};
-use sbpf_common::opcode::Opcode;
-
-use std::collections::HashMap;
+use {
+    crate::{
+        CompileError,
+        astnode::{ASTNode, ROData},
+        dynsym::{DynamicSymbolMap, RelDynMap, RelocationType},
+        instruction::Instruction,
+        lexer::{ImmediateValue, Token},
+        parser::ParseResult,
+        section::{CodeSection, DataSection},
+    },
+    sbpf_common::opcode::Opcode,
+    std::collections::HashMap,
+};
 
 #[derive(Default)]
 pub struct AST {
@@ -105,25 +108,23 @@ impl AST {
             } = node
             {
                 // For jump/call instructions, replace label with relative offsets
-                if inst.is_jump() || inst.opcode == Opcode::Call {
-                    if let Some(Token::Identifier(label, span)) = inst.operands.last() {
-                        let label = label.clone();
-                        if let Some(target_offset) = label_offset_map.get(&label) {
-                            let rel_offset = (*target_offset as i64 - *offset as i64) / 8 - 1;
-                            let last_idx = inst.operands.len() - 1;
-                            inst.operands[last_idx] = Token::ImmediateValue(
-                                ImmediateValue::Int(rel_offset),
-                                span.clone(),
-                            );
-                        } else if inst.is_jump() {
-                            // only error out unresolved jump labels, since call
-                            // labels could exist externally
-                            errors.push(CompileError::UndefinedLabel {
-                                label: label.clone(),
-                                span: span.clone(),
-                                custom_label: None,
-                            });
-                        }
+                if (inst.is_jump() || inst.opcode == Opcode::Call)
+                    && let Some(Token::Identifier(label, span)) = inst.operands.last()
+                {
+                    let label = label.clone();
+                    if let Some(target_offset) = label_offset_map.get(&label) {
+                        let rel_offset = (*target_offset as i64 - *offset as i64) / 8 - 1;
+                        let last_idx = inst.operands.len() - 1;
+                        inst.operands[last_idx] =
+                            Token::ImmediateValue(ImmediateValue::Int(rel_offset), span.clone());
+                    } else if inst.is_jump() {
+                        // only error out unresolved jump labels, since call
+                        // labels could exist externally
+                        errors.push(CompileError::UndefinedLabel {
+                            label: label.clone(),
+                            span: span.clone(),
+                            custom_label: None,
+                        });
                     }
                 }
                 // This has to be done before resolving lddw labels since lddw
@@ -136,38 +137,36 @@ impl AST {
                         dynamic_symbols.add_call_target(label.clone(), *offset);
                     }
                 }
-                if inst.opcode == Opcode::Lddw {
-                    if let Some(Token::Identifier(name, span)) = inst.operands.last() {
-                        let label = name.clone();
-                        if let Some(target_offset) = label_offset_map.get(&label) {
-                            // actually lddw with label makes a program dynamic, so
-                            // we should be able to hard code ph_offset
-                            let ph_count = if program_is_static { 1 } else { 3 };
-                            let ph_offset = 64 + (ph_count as u64 * 56) as i64;
-                            let abs_offset = *target_offset as i64 + ph_offset;
-                            // Replace label with immediate value
-                            let last_idx = inst.operands.len() - 1;
-                            inst.operands[last_idx] = Token::ImmediateValue(
-                                ImmediateValue::Addr(abs_offset),
-                                span.clone(),
-                            );
-                        } else {
-                            errors.push(CompileError::UndefinedLabel {
-                                label: name.clone(),
-                                span: span.clone(),
-                                custom_label: None,
-                            });
-                        }
+                if inst.opcode == Opcode::Lddw
+                    && let Some(Token::Identifier(name, span)) = inst.operands.last()
+                {
+                    let label = name.clone();
+                    if let Some(target_offset) = label_offset_map.get(&label) {
+                        // actually lddw with label makes a program dynamic, so
+                        // we should be able to hard code ph_offset
+                        let ph_count = if program_is_static { 1 } else { 3 };
+                        let ph_offset = 64 + (ph_count as u64 * 56) as i64;
+                        let abs_offset = *target_offset as i64 + ph_offset;
+                        // Replace label with immediate value
+                        let last_idx = inst.operands.len() - 1;
+                        inst.operands[last_idx] =
+                            Token::ImmediateValue(ImmediateValue::Addr(abs_offset), span.clone());
+                    } else {
+                        errors.push(CompileError::UndefinedLabel {
+                            label: name.clone(),
+                            span: span.clone(),
+                            custom_label: None,
+                        });
                     }
                 }
             }
         }
 
         // Set entry point offset if an entry label was specified
-        if let Some(entry_label) = &self.entry_label {
-            if let Some(offset) = label_offset_map.get(entry_label) {
-                dynamic_symbols.add_entry_point(entry_label.clone(), *offset);
-            }
+        if let Some(entry_label) = &self.entry_label
+            && let Some(offset) = label_offset_map.get(entry_label)
+        {
+            dynamic_symbols.add_entry_point(entry_label.clone(), *offset);
         }
 
         if !errors.is_empty() {

+ 10 - 7
crates/assembler/src/astnode.rs

@@ -1,10 +1,13 @@
-use crate::debuginfo::{DebugInfo, RegisterHint, RegisterType};
-use crate::errors::CompileError;
-use crate::instruction::Instruction;
-use crate::lexer::{ImmediateValue, Token};
-use sbpf_common::opcode::Opcode;
-use std::collections::HashMap;
-use std::ops::Range;
+use {
+    crate::{
+        debuginfo::{DebugInfo, RegisterHint, RegisterType},
+        errors::CompileError,
+        instruction::Instruction,
+        lexer::{ImmediateValue, Token},
+    },
+    sbpf_common::opcode::Opcode,
+    std::{collections::HashMap, ops::Range},
+};
 
 #[derive(Debug, Clone)]
 pub enum ASTNode {

+ 1 - 2
crates/assembler/src/errors.rs

@@ -1,5 +1,4 @@
-use crate::define_compile_errors;
-use std::ops::Range;
+use {crate::define_compile_errors, std::ops::Range};
 
 // labels could be overridden by passing a valid custom_label in the error variant
 // if not provided, the label will use default messages from below

+ 69 - 20
crates/assembler/src/instruction.rs

@@ -1,10 +1,13 @@
-use crate::dynsym::RelocationType;
-use crate::lexer::{ImmediateValue, Token};
-use crate::syscall::SYSCALLS;
-use crate::errors::CompileError;
-use sbpf_common::opcode::Opcode;
-
-use std::ops::Range;
+use {
+    crate::{
+        dynsym::RelocationType,
+        errors::CompileError,
+        lexer::{ImmediateValue, Token},
+        syscall::SYSCALLS,
+    },
+    sbpf_common::opcode::Opcode,
+    std::ops::Range,
+};
 
 #[derive(Debug, Clone)]
 pub struct Instruction {
@@ -100,7 +103,11 @@ impl Instruction {
             Opcode::Lddw => {
                 if src != 0 || off != 0 {
                     return Err(CompileError::BytecodeError {
-                        error: format!("Lddw instruction expects src and off to be 0, but got src: {}, off: {}", src, off),
+                        error: format!(
+                            "Lddw instruction expects src and off to be 0, but got src: {}, off: \
+                             {}",
+                            src, off
+                        ),
                         span: span.clone(),
                         custom_label: None,
                     });
@@ -113,7 +120,11 @@ impl Instruction {
                 if let Some(name) = SYSCALLS.get(&(imm as u32)) {
                     if reg != 0 || off != 0 {
                         return Err(CompileError::BytecodeError {
-                            error: format!("Call instruction with syscall expects reg and off to be 0, but got reg: {}, off: {}", reg, off),
+                            error: format!(
+                                "Call instruction with syscall expects reg and off to be 0, but \
+                                 got reg: {}, off: {}",
+                                reg, off
+                            ),
                             span: span.clone(),
                             custom_label: None,
                         });
@@ -122,7 +133,11 @@ impl Instruction {
                 } else {
                     if reg != 16 || off != 0 {
                         return Err(CompileError::BytecodeError {
-                            error: format!("Call instruction with immediate expects reg to be 16 and off to be 0, but got reg: {}, off: {}", reg, off),
+                            error: format!(
+                                "Call instruction with immediate expects reg to be 16 and off to \
+                                 be 0, but got reg: {}, off: {}",
+                                reg, off
+                            ),
                             span: span.clone(),
                             custom_label: None,
                         });
@@ -134,7 +149,11 @@ impl Instruction {
             Opcode::Callx => {
                 if src != 0 || off != 0 || imm != 0 {
                     return Err(CompileError::BytecodeError {
-                        error: format!("Callx instruction expects src, off, and imm to be 0, but got src: {}, off: {}, imm: {}", src, off, imm),
+                        error: format!(
+                            "Callx instruction expects src, off, and imm to be 0, but got src: \
+                             {}, off: {}, imm: {}",
+                            src, off, imm
+                        ),
                         span: span.clone(),
                         custom_label: None,
                     });
@@ -146,7 +165,10 @@ impl Instruction {
             Opcode::Ja => {
                 if reg != 0 || imm != 0 {
                     return Err(CompileError::BytecodeError {
-                        error: format!("Ja instruction expects reg and imm to be 0, but got reg: {}, imm: {}", reg, imm),
+                        error: format!(
+                            "Ja instruction expects reg and imm to be 0, but got reg: {}, imm: {}",
+                            reg, imm
+                        ),
                         span: span.clone(),
                         custom_label: None,
                     });
@@ -167,7 +189,10 @@ impl Instruction {
             | Opcode::JsleImm => {
                 if src != 0 {
                     return Err(CompileError::BytecodeError {
-                        error: format!("Jump instruction with immediate expects src to be 0, but got src: {}", src),
+                        error: format!(
+                            "Jump instruction with immediate expects src to be 0, but got src: {}",
+                            src
+                        ),
                         span: span.clone(),
                         custom_label: None,
                     });
@@ -190,7 +215,10 @@ impl Instruction {
             | Opcode::JsleReg => {
                 if imm != 0 {
                     return Err(CompileError::BytecodeError {
-                        error: format!("Jump instruction with register expects imm to be 0, but got imm: {}", imm),
+                        error: format!(
+                            "Jump instruction with register expects imm to be 0, but got imm: {}",
+                            imm
+                        ),
                         span: span.clone(),
                         custom_label: None,
                     });
@@ -242,7 +270,11 @@ impl Instruction {
             | Opcode::Le => {
                 if src != 0 || off != 0 {
                     return Err(CompileError::BytecodeError {
-                        error: format!("Arithmetic instruction with immediate expects src and off to be 0, but got src: {}, off: {}", src, off),
+                        error: format!(
+                            "Arithmetic instruction with immediate expects src and off to be 0, \
+                             but got src: {}, off: {}",
+                            src, off
+                        ),
                         span: span.clone(),
                         custom_label: None,
                     });
@@ -290,7 +322,11 @@ impl Instruction {
             | Opcode::Srem64Reg => {
                 if off != 0 || imm != 0 {
                     return Err(CompileError::BytecodeError {
-                        error: format!("Arithmetic instruction with register expects off and imm to be 0, but got off: {}, imm: {}", off, imm),
+                        error: format!(
+                            "Arithmetic instruction with register expects off and imm to be 0, \
+                             but got off: {}, imm: {}",
+                            off, imm
+                        ),
                         span: span.clone(),
                         custom_label: None,
                     });
@@ -302,7 +338,10 @@ impl Instruction {
             Opcode::Ldxw | Opcode::Ldxh | Opcode::Ldxb | Opcode::Ldxdw => {
                 if imm != 0 {
                     return Err(CompileError::BytecodeError {
-                        error: format!("Load instruction expects imm to be 0, but got imm: {}", imm),
+                        error: format!(
+                            "Load instruction expects imm to be 0, but got imm: {}",
+                            imm
+                        ),
                         span: span.clone(),
                         custom_label: None,
                     });
@@ -315,7 +354,10 @@ impl Instruction {
             Opcode::Stw | Opcode::Sth | Opcode::Stb | Opcode::Stdw => {
                 if src != 0 {
                     return Err(CompileError::BytecodeError {
-                        error: format!("Store instruction expects src to be 0, but got src: {}", src),
+                        error: format!(
+                            "Store instruction expects src to be 0, but got src: {}",
+                            src
+                        ),
                         span: span.clone(),
                         custom_label: None,
                     });
@@ -328,7 +370,10 @@ impl Instruction {
             Opcode::Stxb | Opcode::Stxh | Opcode::Stxw | Opcode::Stxdw => {
                 if imm != 0 {
                     return Err(CompileError::BytecodeError {
-                        error: format!("Store instruction with register expects imm to be 0, but got imm: {}", imm),
+                        error: format!(
+                            "Store instruction with register expects imm to be 0, but got imm: {}",
+                            imm
+                        ),
                         span: span.clone(),
                         custom_label: None,
                     });
@@ -342,7 +387,11 @@ impl Instruction {
             Opcode::Neg32 | Opcode::Neg64 | Opcode::Exit => {
                 if src != 0 || off != 0 || imm != 0 {
                     return Err(CompileError::BytecodeError {
-                        error: format!("Unary operation expects src, off, and imm to be 0, but got src: {}, off: {}, imm: {}", src, off, imm),
+                        error: format!(
+                            "Unary operation expects src, off, and imm to be 0, but got src: {}, \
+                             off: {}, imm: {}",
+                            src, off, imm
+                        ),
                         span: span.clone(),
                         custom_label: None,
                     });

+ 5 - 3
crates/assembler/src/lexer.rs

@@ -1,6 +1,8 @@
-use crate::errors::CompileError;
-use sbpf_common::opcode::Opcode;
-use std::{ops::Range, str::FromStr as _};
+use {
+    crate::errors::CompileError,
+    sbpf_common::opcode::Opcode,
+    std::{ops::Range, str::FromStr as _},
+};
 
 #[derive(Debug, Clone)]
 pub enum Op {

+ 30 - 32
crates/assembler/src/parser.rs

@@ -1,18 +1,19 @@
-use crate::ast::AST;
-use crate::astnode::{
-    ASTNode, Directive, EquDecl, ExternDecl, GlobalDecl, Label, ROData, RodataDecl,
+use {
+    crate::{
+        ast::AST,
+        astnode::{ASTNode, Directive, EquDecl, ExternDecl, GlobalDecl, Label, ROData, RodataDecl},
+        bug,
+        dynsym::{DynamicSymbolMap, RelDynMap},
+        errors::CompileError,
+        instruction::Instruction,
+        lexer::{ImmediateValue, Op, Token},
+        messages::*,
+        section::{CodeSection, DataSection},
+    },
+    num_traits::FromPrimitive,
+    sbpf_common::opcode::Opcode,
+    std::collections::HashMap,
 };
-use crate::bug;
-use crate::dynsym::{DynamicSymbolMap, RelDynMap};
-use crate::errors::CompileError;
-use crate::instruction::Instruction;
-use crate::lexer::Op;
-use crate::lexer::{ImmediateValue, Token};
-use crate::messages::*;
-use crate::section::{CodeSection, DataSection};
-use num_traits::FromPrimitive;
-use sbpf_common::opcode::Opcode;
-use std::collections::HashMap;
 
 pub struct ParseResult {
     // TODO: parse result is basically 1. static part 2. dynamic part of the program
@@ -944,12 +945,11 @@ fn inline_and_fold_constant_with_map(
                 expect_number = false;
 
                 // Immediately fold * / if top
-                if stack.len() > 2 {
-                    if let Token::BinaryOp(op, _) = &stack[stack.len() - 2] {
-                        if matches!(op, Op::Mul | Op::Div) {
-                            fold_top(&mut stack);
-                        }
-                    }
+                if stack.len() > 2
+                    && let Token::BinaryOp(op, _) = &stack[stack.len() - 2]
+                    && matches!(op, Op::Mul | Op::Div)
+                {
+                    fold_top(&mut stack);
                 }
             }
 
@@ -959,12 +959,11 @@ fn inline_and_fold_constant_with_map(
                         stack.push(Token::ImmediateValue(val.clone(), span.clone()));
                         expect_number = false;
 
-                        if stack.len() > 2 {
-                            if let Token::BinaryOp(op, _) = &stack[stack.len() - 2] {
-                                if matches!(op, Op::Mul | Op::Div) {
-                                    fold_top(&mut stack);
-                                }
-                            }
+                        if stack.len() > 2
+                            && let Token::BinaryOp(op, _) = &stack[stack.len() - 2]
+                            && matches!(op, Op::Mul | Op::Div)
+                        {
+                            fold_top(&mut stack);
                         }
                     } else {
                         return (None, idx);
@@ -998,12 +997,11 @@ fn inline_and_fold_constant_with_map(
                     stack.push(Token::ImmediateValue(v, span.clone()));
                     expect_number = false;
 
-                    if stack.len() > 2 {
-                        if let Token::BinaryOp(op, _) = &stack[stack.len() - 2] {
-                            if matches!(op, Op::Mul | Op::Div) {
-                                fold_top(&mut stack);
-                            }
-                        }
+                    if stack.len() > 2
+                        && let Token::BinaryOp(op, _) = &stack[stack.len() - 2]
+                        && matches!(op, Op::Mul | Op::Div)
+                    {
+                        fold_top(&mut stack);
                     }
                 } else {
                     return (None, idx);

+ 12 - 12
crates/assembler/src/program.rs

@@ -1,16 +1,16 @@
-use crate::debuginfo::DebugInfo;
-use crate::dynsym::{DynamicSymbol, RelDyn, RelocationType};
-use crate::header::ElfHeader;
-use crate::header::ProgramHeader;
-use crate::parser::ParseResult;
-use crate::section::{
-    DynStrSection, DynSymSection, DynamicSection, NullSection, RelDynSection, Section, SectionType,
-    ShStrTabSection,
+use {
+    crate::{
+        debuginfo::DebugInfo,
+        dynsym::{DynamicSymbol, RelDyn, RelocationType},
+        header::{ElfHeader, ProgramHeader},
+        parser::ParseResult,
+        section::{
+            DynStrSection, DynSymSection, DynamicSection, NullSection, RelDynSection, Section,
+            SectionType, ShStrTabSection,
+        },
+    },
+    std::{collections::HashMap, fs::File, io::Write, path::Path},
 };
-use std::collections::HashMap;
-use std::fs::File;
-use std::io::Write;
-use std::path::Path;
 
 #[derive(Debug)]
 pub struct Program {

+ 12 - 11
crates/assembler/src/section.rs

@@ -1,11 +1,13 @@
-use crate::astnode::ASTNode;
-use crate::astnode::ROData;
-use crate::debuginfo::DebugInfo;
-use crate::dynsym::DynamicSymbol;
-use crate::dynsym::RelDyn;
-use crate::header::SectionHeader;
-use crate::lexer::Token;
-use std::collections::HashMap;
+use {
+    crate::{
+        astnode::{ASTNode, ROData},
+        debuginfo::DebugInfo,
+        dynsym::{DynamicSymbol, RelDyn},
+        header::SectionHeader,
+        lexer::Token,
+    },
+    std::collections::HashMap,
+};
 
 // Base Section trait
 pub trait Section {
@@ -146,10 +148,9 @@ impl DataSection {
                 rodata: ROData { name, args, .. },
                 offset,
             } = node
+                && let Some(Token::StringLiteral(str_literal, _)) = args.get(1)
             {
-                if let Some(Token::StringLiteral(str_literal, _)) = args.get(1) {
-                    ro_data_labels.push((name.clone(), *offset as usize, str_literal.clone()));
-                }
+                ro_data_labels.push((name.clone(), *offset as usize, str_literal.clone()));
             }
         }
         ro_data_labels

+ 1 - 2
crates/assembler/src/syscall.rs

@@ -1,5 +1,4 @@
-use phf::Map;
-use phf_macros::phf_map;
+use {phf::Map, phf_macros::phf_map};
 
 pub static SYSCALLS: Map<u32, &'static str> = phf_map! {
     0xb6fc1a11u32 => "abort",

+ 7 - 7
crates/assembler/src/wasm.rs

@@ -1,10 +1,10 @@
-use crate::lexer::tokenize;
-use crate::parser::parse_tokens;
-use crate::program::Program;
-use serde::Serialize;
-use serde_wasm_bindgen::to_value;
-use std::ops::Range;
-use wasm_bindgen::prelude::*;
+use {
+    crate::{lexer::tokenize, parser::parse_tokens, program::Program},
+    serde::Serialize,
+    serde_wasm_bindgen::to_value,
+    std::ops::Range,
+    wasm_bindgen::prelude::*,
+};
 
 #[derive(Serialize)]
 struct CompileErrorInfo {

+ 4 - 6
crates/assembler/tests/regression.rs

@@ -1,9 +1,7 @@
-use std::collections::BTreeMap;
-use std::env;
-use std::fs;
-use std::path::PathBuf;
-
-use serde::{Deserialize, Serialize};
+use {
+    serde::{Deserialize, Serialize},
+    std::{collections::BTreeMap, env, fs, path::PathBuf},
+};
 
 #[derive(Debug, Deserialize, Serialize)]
 struct Manifest {

+ 4 - 3
crates/common/Cargo.toml

@@ -2,11 +2,12 @@
 name = "sbpf-common"
 version.workspace = true
 description = "Common types and utilities for SBPF (Solana BPF)"
-edition = "2024"
-license = "MIT OR Apache-2.0"
-repository = "https://github.com/blueshift-gg/sbpf"
+edition.workspace = true
+license.workspace = true
+repository.workspace = true
 keywords = ["solana", "bpf", "blockchain"]
 categories = ["development-tools"]
+rust-version.workspace = true
 
 [lib]
 crate-type = ["cdylib", "lib"]

+ 1 - 2
crates/common/src/errors.rs

@@ -1,5 +1,4 @@
-use std::ops::Range;
-use thiserror::Error;
+use {std::ops::Range, thiserror::Error};
 
 #[derive(Debug, Error)]
 pub enum SBPFError {

+ 26 - 18
crates/common/src/instruction.rs

@@ -1,10 +1,8 @@
-use crate::errors::SBPFError;
-use crate::opcode::Opcode;
-use crate::syscall::SYSCALLS;
-
-use core::fmt;
-use core::ops::Range;
-use serde::{Deserialize, Serialize};
+use {
+    crate::{errors::SBPFError, opcode::Opcode, syscall::SYSCALLS},
+    core::{fmt, ops::Range},
+    serde::{Deserialize, Serialize},
+};
 
 #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
 pub struct Register {
@@ -161,7 +159,8 @@ impl Instruction {
                 if src != 0 || off != 0 {
                     return Err(SBPFError::BytecodeError {
                         error: format!(
-                            "Lddw instruction expects src and off to be 0, but got src: {}, off: {}",
+                            "Lddw instruction expects src and off to be 0, but got src: {}, off: \
+                             {}",
                             src, off
                         ),
                         span: span.clone(),
@@ -177,7 +176,8 @@ impl Instruction {
                     if reg != 0 || off != 0 {
                         return Err(SBPFError::BytecodeError {
                             error: format!(
-                                "Call instruction with syscall expects reg and off to be 0, but got reg: {}, off: {}",
+                                "Call instruction with syscall expects reg and off to be 0, but \
+                                 got reg: {}, off: {}",
                                 reg, off
                             ),
                             span: span.clone(),
@@ -189,7 +189,8 @@ impl Instruction {
                     if reg != 16 || off != 0 {
                         return Err(SBPFError::BytecodeError {
                             error: format!(
-                                "Call instruction with immediate expects reg to be 16 and off to be 0, but got reg: {}, off: {}",
+                                "Call instruction with immediate expects reg to be 16 and off to \
+                                 be 0, but got reg: {}, off: {}",
                                 reg, off
                             ),
                             span: span.clone(),
@@ -204,7 +205,8 @@ impl Instruction {
                 if src != 0 || off != 0 || imm != 0 {
                     return Err(SBPFError::BytecodeError {
                         error: format!(
-                            "Callx instruction expects src, off, and imm to be 0, but got src: {}, off: {}, imm: {}",
+                            "Callx instruction expects src, off, and imm to be 0, but got src: \
+                             {}, off: {}, imm: {}",
                             src, off, imm
                         ),
                         span: span.clone(),
@@ -324,7 +326,8 @@ impl Instruction {
                 if src != 0 || off != 0 {
                     return Err(SBPFError::BytecodeError {
                         error: format!(
-                            "Arithmetic instruction with immediate expects src and off to be 0, but got src: {}, off: {}",
+                            "Arithmetic instruction with immediate expects src and off to be 0, \
+                             but got src: {}, off: {}",
                             src, off
                         ),
                         span: span.clone(),
@@ -375,7 +378,8 @@ impl Instruction {
                 if off != 0 || imm != 0 {
                     return Err(SBPFError::BytecodeError {
                         error: format!(
-                            "Arithmetic instruction with register expects off and imm to be 0, but got off: {}, imm: {}",
+                            "Arithmetic instruction with register expects off and imm to be 0, \
+                             but got off: {}, imm: {}",
                             off, imm
                         ),
                         span: span.clone(),
@@ -439,7 +443,8 @@ impl Instruction {
                 if src != 0 || off != 0 || imm != 0 {
                     return Err(SBPFError::BytecodeError {
                         error: format!(
-                            "Unary operation expects src, off, and imm to be 0, but got src: {}, off: {}, imm: {}",
+                            "Unary operation expects src, off, and imm to be 0, but got src: {}, \
+                             off: {}, imm: {}",
                             src, off, imm
                         ),
                         span: span.clone(),
@@ -749,10 +754,13 @@ impl Instruction {
 
 #[cfg(test)]
 mod test {
-    use hex_literal::hex;
-
-    use crate::instruction::{Instruction, Register};
-    use crate::opcode::Opcode;
+    use {
+        crate::{
+            instruction::{Instruction, Register},
+            opcode::Opcode,
+        },
+        hex_literal::hex,
+    };
 
     #[test]
     fn serialize_e2e() {

+ 5 - 5
crates/common/src/opcode.rs

@@ -1,8 +1,8 @@
-use core::fmt;
-use core::str::FromStr;
-
-use num_derive::FromPrimitive;
-use serde::{Deserialize, Serialize};
+use {
+    core::{fmt, str::FromStr},
+    num_derive::FromPrimitive,
+    serde::{Deserialize, Serialize},
+};
 
 #[derive(Debug, Clone, Copy, PartialEq, FromPrimitive, Serialize, Deserialize)]
 #[repr(u8)]

+ 1 - 2
crates/common/src/syscall.rs

@@ -1,5 +1,4 @@
-use phf::Map;
-use phf_macros::phf_map;
+use {phf::Map, phf_macros::phf_map};
 
 pub static SYSCALLS: Map<u32, &'static str> = phf_map! {
     0xb6fc1a11u32 => "abort",

+ 4 - 3
crates/disassembler/Cargo.toml

@@ -2,11 +2,12 @@
 name = "sbpf-disassembler"
 version.workspace = true
 description = "Disassembler for SBPF (Solana BPF) bytecode"
-edition = "2024"
-license = "MIT OR Apache-2.0"
-repository = "https://github.com/blueshift-gg/sbpf"
+edition.workspace = true
+license.workspace = true
+repository.workspace = true
 keywords = ["solana", "bpf", "disassembler", "blockchain"]
 categories = ["development-tools"]
+rust-version.workspace = true
 
 [lib]
 crate-type = ["cdylib", "lib"]

+ 15 - 13
crates/disassembler/src/elf_header.rs

@@ -1,10 +1,9 @@
-use std::str;
-
-use object::Endianness;
-use object::read::elf::ElfFile64;
-use serde::{Deserialize, Serialize, Serializer};
-
-use crate::errors::DisassemblerError;
+use {
+    crate::errors::DisassemblerError,
+    object::{Endianness, read::elf::ElfFile64},
+    serde::{Deserialize, Serialize, Serializer},
+    std::str,
+};
 
 pub const EI_MAGIC: [u8; 4] = *b"\x7fELF"; // ELF magic
 pub const EI_CLASS: u8 = 0x02; // 64-bit
@@ -139,13 +138,16 @@ impl ELFHeader {
 
 #[cfg(test)]
 mod tests {
-    use hex_literal::hex;
-
-    use crate::elf_header::{
-        E_MACHINE, E_MACHINE_SBPF, E_TYPE, E_VERSION, EI_ABIVERSION, EI_CLASS, EI_DATA, EI_MAGIC,
-        EI_OSABI, EI_PAD, EI_VERSION,
+    use {
+        crate::{
+            elf_header::{
+                E_MACHINE, E_MACHINE_SBPF, E_TYPE, E_VERSION, EI_ABIVERSION, EI_CLASS, EI_DATA,
+                EI_MAGIC, EI_OSABI, EI_PAD, EI_VERSION,
+            },
+            program::Program,
+        },
+        hex_literal::hex,
     };
-    use crate::program::Program;
 
     #[test]
     fn test_elf_header() {

+ 1 - 2
crates/disassembler/src/errors.rs

@@ -1,5 +1,4 @@
-use sbpf_common::errors::SBPFError;
-use thiserror::Error;
+use {sbpf_common::errors::SBPFError, thiserror::Error};
 
 #[derive(Debug, Error)]
 pub enum DisassemblerError {

+ 8 - 10
crates/disassembler/src/program.rs

@@ -1,10 +1,10 @@
-use object::Endianness;
-use object::read::elf::ElfFile64;
-use serde::{Deserialize, Serialize};
-
-use crate::{
-    elf_header::ELFHeader, errors::DisassemblerError, program_header::ProgramHeader,
-    section_header::SectionHeader, section_header_entry::SectionHeaderEntry,
+use {
+    crate::{
+        elf_header::ELFHeader, errors::DisassemblerError, program_header::ProgramHeader,
+        section_header::SectionHeader, section_header_entry::SectionHeaderEntry,
+    },
+    object::{Endianness, read::elf::ElfFile64},
+    serde::{Deserialize, Serialize},
 };
 
 #[derive(Debug, Serialize, Deserialize)]
@@ -40,9 +40,7 @@ impl Program {
 
 #[cfg(test)]
 mod tests {
-    use hex_literal::hex;
-
-    use crate::program::Program;
+    use {crate::program::Program, hex_literal::hex};
 
     #[test]
     fn try_deserialize_program() {

+ 7 - 10
crates/disassembler/src/program_header.rs

@@ -1,10 +1,9 @@
-use std::fmt::Debug;
-
-use object::Endianness;
-use object::read::elf::ElfFile64;
-use serde::{Deserialize, Serialize};
-
-use crate::errors::DisassemblerError;
+use {
+    crate::errors::DisassemblerError,
+    object::{Endianness, read::elf::ElfFile64},
+    serde::{Deserialize, Serialize},
+    std::fmt::Debug,
+};
 
 // Program Segment Flags
 pub const PF_X: u8 = 0x01;
@@ -160,9 +159,7 @@ impl ProgramHeader {
 
 #[cfg(test)]
 mod tests {
-    use hex_literal::hex;
-
-    use crate::program::Program;
+    use {crate::program::Program, hex_literal::hex};
 
     #[test]
     fn test_program_headers() {

+ 7 - 11
crates/disassembler/src/section_header.rs

@@ -1,11 +1,9 @@
-use std::{fmt::Debug, fmt::Display};
-
-use object::Endianness;
-use object::read::elf::ElfFile64;
-use serde::{Deserialize, Serialize};
-
-use crate::errors::DisassemblerError;
-use crate::section_header_entry::SectionHeaderEntry;
+use {
+    crate::{errors::DisassemblerError, section_header_entry::SectionHeaderEntry},
+    object::{Endianness, read::elf::ElfFile64},
+    serde::{Deserialize, Serialize},
+    std::fmt::{Debug, Display},
+};
 
 #[allow(non_camel_case_types)]
 #[derive(Debug, Clone, Serialize, Deserialize)]
@@ -190,9 +188,7 @@ impl SectionHeader {
 
 #[cfg(test)]
 mod tests {
-    use hex_literal::hex;
-
-    use crate::program::Program;
+    use {crate::program::Program, hex_literal::hex};
 
     #[test]
     fn test_section_headers() {

+ 14 - 11
crates/disassembler/src/section_header_entry.rs

@@ -1,10 +1,9 @@
-use std::fmt::Debug;
-
-use serde::{Deserialize, Serialize};
-
-use crate::errors::DisassemblerError;
-use sbpf_common::instruction::Instruction;
-use sbpf_common::opcode::Opcode;
+use {
+    crate::errors::DisassemblerError,
+    sbpf_common::{instruction::Instruction, opcode::Opcode},
+    serde::{Deserialize, Serialize},
+    std::fmt::Debug,
+};
 
 #[derive(Debug, Clone, Serialize, Deserialize)]
 pub struct SectionHeaderEntry {
@@ -42,7 +41,7 @@ impl SectionHeaderEntry {
     }
 
     pub fn to_ixs(&self) -> Result<Vec<Instruction>, DisassemblerError> {
-        if self.data.len() % 8 != 0 {
+        if !self.data.len().is_multiple_of(8) {
             return Err(DisassemblerError::InvalidDataLength);
         }
         let mut ixs: Vec<Instruction> = vec![];
@@ -74,9 +73,13 @@ impl SectionHeaderEntry {
 
 #[cfg(test)]
 mod test {
-    use crate::section_header_entry::SectionHeaderEntry;
-    use sbpf_common::instruction::{Instruction, Number, Register};
-    use sbpf_common::opcode::Opcode;
+    use {
+        crate::section_header_entry::SectionHeaderEntry,
+        sbpf_common::{
+            instruction::{Instruction, Number, Register},
+            opcode::Opcode,
+        },
+    };
 
     #[test]
     fn serialize_e2e() {

+ 6 - 4
crates/disassembler/src/wasm.rs

@@ -1,7 +1,9 @@
-use crate::program::Program as EBPFProgram;
-use serde::{Deserialize, Serialize};
-use serde_wasm_bindgen::to_value;
-use wasm_bindgen::prelude::*;
+use {
+    crate::program::Program as EBPFProgram,
+    serde::{Deserialize, Serialize},
+    serde_wasm_bindgen::to_value,
+    wasm_bindgen::prelude::*,
+};
 
 #[wasm_bindgen]
 #[derive(Debug, Serialize, Deserialize)]

+ 3 - 0
rustfmt.toml

@@ -0,0 +1,3 @@
+imports_granularity = "One"
+format_strings = true
+group_imports = "One"

+ 31 - 31
src/commands/build.rs

@@ -1,18 +1,16 @@
-use sbpf_assembler::assemble;
-use sbpf_assembler::errors::CompileError;
-
-use ed25519_dalek::SigningKey;
-use rand::rngs::OsRng;
-use std::fs;
-
-use anyhow::{Error, Result};
-use codespan_reporting::diagnostic::{Diagnostic, Label};
-use codespan_reporting::files::SimpleFile;
-use codespan_reporting::term;
-use std::fs::create_dir_all;
-use std::path::Path;
-use std::time::Instant;
-use termcolor::{ColorChoice, StandardStream};
+use {
+    anyhow::{Error, Result},
+    codespan_reporting::{
+        diagnostic::{Diagnostic, Label},
+        files::SimpleFile,
+        term,
+    },
+    ed25519_dalek::SigningKey,
+    rand::rngs::OsRng,
+    sbpf_assembler::{assemble, errors::CompileError},
+    std::{fs, fs::create_dir_all, path::Path, time::Instant},
+    termcolor::{ColorChoice, StandardStream},
+};
 
 pub trait AsDiagnostic {
     // currently only support single source file reporting
@@ -36,8 +34,10 @@ impl AsDiagnostic for CompileError {
                 ]),
             _ => Diagnostic::error()
                 .with_message(self.to_string())
-                .with_labels(vec![Label::primary((), self.span().start..self.span().end)
-                    .with_message(self.label())]),
+                .with_labels(vec![
+                    Label::primary((), self.span().start..self.span().end)
+                        .with_message(self.label()),
+                ]),
         }
     }
 }
@@ -122,20 +122,20 @@ pub fn build() -> Result<()> {
     for entry in src_path.read_dir()? {
         let entry = entry?;
         let path = entry.path();
-        if path.is_dir() {
-            if let Some(subdir) = path.file_name().and_then(|name| name.to_str()) {
-                let asm_file = format!("{}/{}/{}.s", src, subdir, subdir);
-                if Path::new(&asm_file).exists() {
-                    println!("⚡️ Building \"{}\"", subdir);
-                    let start = Instant::now();
-                    compile_assembly(&asm_file, deploy)?;
-                    let duration = start.elapsed();
-                    println!(
-                        "✅ \"{}\" built successfully in {}ms!",
-                        subdir,
-                        duration.as_micros() as f64 / 1000.0
-                    );
-                }
+        if path.is_dir()
+            && let Some(subdir) = path.file_name().and_then(|name| name.to_str())
+        {
+            let asm_file = format!("{}/{}/{}.s", src, subdir, subdir);
+            if Path::new(&asm_file).exists() {
+                println!("⚡️ Building \"{}\"", subdir);
+                let start = Instant::now();
+                compile_assembly(&asm_file, deploy)?;
+                let duration = start.elapsed();
+                println!(
+                    "✅ \"{}\" built successfully in {}ms!",
+                    subdir,
+                    duration.as_micros() as f64 / 1000.0
+                );
             }
         }
     }

+ 9 - 10
src/commands/clean.rs

@@ -1,7 +1,7 @@
-use std::fs;
-use std::path::Path;
-
-use anyhow::{Error, Result};
+use {
+    anyhow::{Error, Result},
+    std::{fs, path::Path},
+};
 
 pub fn clean() -> Result<(), Error> {
     fs::remove_dir_all(".sbpf")?;
@@ -14,12 +14,11 @@ fn clean_directory(directory: &str, extension: &str) -> Result<(), Error> {
     for entry in path.read_dir()? {
         let entry = entry?;
         let path = entry.path();
-        if path.is_file() {
-            if let Some(ext) = path.extension().and_then(|ext| ext.to_str()) {
-                if extension.is_empty() || ext == extension {
-                    fs::remove_file(&path)?;
-                }
-            }
+        if path.is_file()
+            && let Some(ext) = path.extension().and_then(|ext| ext.to_str())
+            && (extension.is_empty() || ext == extension)
+        {
+            fs::remove_file(&path)?;
         }
     }
     Ok(())

+ 10 - 13
src/commands/deploy.rs

@@ -1,8 +1,7 @@
-use std::io;
-use std::path::Path;
-use std::process::Command;
-
-use anyhow::{Error, Result};
+use {
+    anyhow::{Error, Result},
+    std::{io, path::Path, process::Command},
+};
 
 fn deploy_program(program_name: &str, url: &str) -> Result<(), Error> {
     let program_id_file = format!("./deploy/{}-keypair.json", program_name);
@@ -23,10 +22,7 @@ fn deploy_program(program_name: &str, url: &str) -> Result<(), Error> {
 
         if !status.success() {
             eprintln!("Failed to deploy program for {}", program_name);
-            return Err(Error::new(io::Error::new(
-                io::ErrorKind::Other,
-                "❌ Deployment failed",
-            )));
+            return Err(Error::new(io::Error::other("❌ Deployment failed")));
         }
 
         println!("✅ \"{}\" deployed successfully!", program_name);
@@ -47,10 +43,11 @@ fn deploy_all_programs(url: &str) -> Result<(), Error> {
     for entry in deploy_path.read_dir()? {
         let entry = entry?;
         let path = entry.path();
-        if path.is_file() && path.extension().and_then(|ext| ext.to_str()) == Some("so") {
-            if let Some(filename) = path.file_stem().and_then(|name| name.to_str()) {
-                deploy_program(filename, url)?;
-            }
+        if path.is_file()
+            && path.extension().and_then(|ext| ext.to_str()) == Some("so")
+            && let Some(filename) = path.file_stem().and_then(|name| name.to_str())
+        {
+            deploy_program(filename, url)?;
         }
     }
 

+ 5 - 5
src/commands/disassemble.rs

@@ -1,8 +1,8 @@
-use std::fs::File;
-use std::io::Read;
-
-use anyhow::{Error, Result};
-use sbpf_disassembler::program::Program;
+use {
+    anyhow::{Error, Result},
+    sbpf_disassembler::program::Program,
+    std::{fs::File, io::Read},
+};
 
 pub fn disassemble(filename: String, asm: bool) -> Result<(), Error> {
     let mut file = File::open(filename)?;

+ 12 - 8
src/commands/init.rs

@@ -1,12 +1,16 @@
-use super::common::{
-    CARGO_TOML, GITIGNORE, PACKAGE_JSON, PROGRAM, README, RUST_TESTS, TSCONFIG, TS_TESTS,
+use {
+    super::common::{
+        CARGO_TOML, GITIGNORE, PACKAGE_JSON, PROGRAM, README, RUST_TESTS, TS_TESTS, TSCONFIG,
+    },
+    anyhow::{Error, Result},
+    ed25519_dalek::SigningKey,
+    rand::rngs::OsRng,
+    std::{
+        fs,
+        io::{self, Write},
+        process::Command,
+    },
 };
-use anyhow::{Error, Result};
-use ed25519_dalek::SigningKey;
-use rand::rngs::OsRng;
-use std::fs;
-use std::io::{self, Write};
-use std::process::Command;
 
 pub fn init(name: Option<String>, ts_tests: bool) -> Result<(), Error> {
     let project_name = match name {

+ 6 - 10
src/commands/test.rs

@@ -1,5 +1,7 @@
-use anyhow::{Error, Result};
-use std::{fs, io, path::Path, process::Command};
+use {
+    anyhow::{Error, Result},
+    std::{fs, io, path::Path, process::Command},
+};
 
 pub fn test() -> Result<(), Error> {
     println!("🧪 Running tests");
@@ -43,10 +45,7 @@ pub fn test() -> Result<(), Error> {
 
             if !output.success() {
                 eprintln!("Failed to run Rust tests");
-                return Err(Error::new(io::Error::new(
-                    io::ErrorKind::Other,
-                    "❌ Rust tests failed",
-                )));
+                return Err(Error::new(io::Error::other("❌ Rust tests failed")));
             }
         }
         (false, true) => {
@@ -56,10 +55,7 @@ pub fn test() -> Result<(), Error> {
 
             if !status.success() {
                 eprintln!("Failed to run tests");
-                return Err(Error::new(io::Error::new(
-                    io::ErrorKind::Other,
-                    "❌ Test failed",
-                )));
+                return Err(Error::new(io::Error::other("❌ Test failed")));
             }
         }
         (false, false) => {

+ 5 - 3
src/main.rs

@@ -1,7 +1,9 @@
 pub mod commands;
-use anyhow::Error;
-use clap::{Args, Parser, Subcommand};
-use commands::{build, clean, deploy, disassemble, init, test};
+use {
+    anyhow::Error,
+    clap::{Args, Parser, Subcommand},
+    commands::{build, clean, deploy, disassemble, init, test},
+};
 
 #[derive(Parser)]
 #[command(version, about, long_about = None)]

+ 2 - 2
tests/test_memo.rs

@@ -1,8 +1,8 @@
 mod utils;
 
 use utils::{
-    init_project, run_build, update_assembly_file, verify_project_structure, verify_so_files,
-    TestEnv,
+    TestEnv, init_project, run_build, update_assembly_file, verify_project_structure,
+    verify_so_files,
 };
 
 #[test]

+ 14 - 6
tests/utils.rs

@@ -8,7 +8,9 @@ impl EnvVarGuard {
     pub fn new<K: Into<String>, V: Into<String>>(key: K, value: V) -> Self {
         let key = key.into();
         let original = std::env::var(&key).ok();
-        std::env::set_var(&key, value.into());
+        unsafe {
+            std::env::set_var(&key, value.into());
+        }
         Self { key, original }
     }
 }
@@ -16,16 +18,22 @@ impl EnvVarGuard {
 impl Drop for EnvVarGuard {
     fn drop(&mut self) {
         if let Some(ref val) = self.original {
-            std::env::set_var(&self.key, val);
+            unsafe {
+                std::env::set_var(&self.key, val);
+            }
         } else {
-            std::env::remove_var(&self.key);
+            unsafe {
+                std::env::remove_var(&self.key);
+            }
         }
     }
 }
 
-use std::fs;
-use std::path::PathBuf;
-use std::process::{Command, Output};
+use std::{
+    fs,
+    path::PathBuf,
+    process::{Command, Output},
+};
 
 /// Test environment setup for SBPF tests
 pub struct TestEnv {