Kaynağa Gözat

Update rust example to use BPF enabled infrastructure (#2974)

Jack May 6 yıl önce
ebeveyn
işleme
b9524217fe

+ 3 - 1
ci/test-stable.sh

@@ -51,7 +51,9 @@ test-stable-perf)
   _ make -C programs/bpf/c tests
 
   # Must be built out of band
-  _ make -C programs/bpf/rust/noop/ all
+  _ pushd programs/bpf/rust/noop
+  ./build.sh
+  popd
 
   _ cargo test --manifest-path programs/Cargo.toml --no-default-features --features="$PROGRAM_FEATURES"
   _ cargo test --manifest-path programs/native/bpf_loader/Cargo.toml --no-default-features --features="$PROGRAM_FEATURES"

+ 0 - 1
programs/bpf/rust/noop/.gitignore

@@ -1,4 +1,3 @@
-/out/
 /target/
 
 Cargo.lock

+ 10 - 2
programs/bpf/rust/noop/Cargo.toml

@@ -1,5 +1,5 @@
 
-# Note: This crate must be built using the makefile, try `make help` instead of `cargo build`
+# Note: This crate must be built using build.sh
 
 [package]
 name = "solana-bpf-rust-noop"
@@ -11,7 +11,15 @@ license = "Apache-2.0"
 homepage = "https://solana.com/"
 
 [dependencies]
-heapless = { version = "0.4.0", default-features = false }
+# byteorder = { version = "1.3.1", default-features = false }
+# heapless = { version = "0.4.0", default-features = false }
+# byte = { version = "0.2", default-features = false }
 
 [workspace]
 members = []
+
+[lib]
+name = "solana_bpf_rust_noop"
+crate-type = ["cdylib"]
+
+

+ 4 - 0
programs/bpf/rust/noop/Xargo.toml

@@ -0,0 +1,4 @@
+
+
+[dependencies.compiler_builtins]
+path = "../../../../sdk/bpf/rust-bpf-sysroot/src/compiler-builtins"

+ 25 - 0
programs/bpf/rust/noop/build.sh

@@ -0,0 +1,25 @@
+#!/usr/bin/env bash
+
+cargo install xargo
+
+set -e
+
+# Ensure the sdk is installed
+../../../../sdk/bpf/scripts/install.sh
+rustup override set bpf
+
+export RUSTFLAGS="$RUSTFLAGS \
+    -C lto=no \
+    -C opt-level=2 \
+    -C link-arg=-Tbpf.ld \
+    -C link-arg=-z -C link-arg=notext \
+    -C link-arg=--Bdynamic \
+    -C link-arg=-shared \
+    -C link-arg=--entry=entrypoint \
+    -C linker=../../../../sdk/bpf/llvm-native/bin/ld.lld"
+export XARGO_HOME="$PWD/target/xargo"
+export XARGO_RUST_SRC="../../../../sdk/bpf/rust-bpf-sysroot/src"
+# export XARGO_RUST_SRC="../../../../../rust-bpf-sysroot/src"
+xargo build --target bpfel-unknown-unknown --release -v
+
+{ { set +x; } 2>/dev/null; echo Success; }

+ 5 - 0
programs/bpf/rust/noop/clean.sh

@@ -0,0 +1,5 @@
+#!/usr/bin/env bash
+
+set -ex
+
+cargo clean

+ 12 - 0
programs/bpf/rust/noop/dump.sh

@@ -0,0 +1,12 @@
+#!/usr/bin/env bash
+
+cp dump.txt dump_last.txt 2>/dev/null
+
+set -x
+set -e
+
+./clean.sh
+./build.sh
+ls -la ./target/bpfel_unknown_unknown/release/solana_bpf_rust_noop.so > dump.txt
+greadelf -aW ./target/bpfel_unknown_unknown/release/solana_bpf_rust_noop.so | rustfilt >> dump.txt
+llvm-objdump -print-imm-hex --source --disassemble ./target/bpfel_unknown_unknown/release/solana_bpf_rust_noop.so >> dump.txt

+ 0 - 146
programs/bpf/rust/noop/makefile

@@ -1,146 +0,0 @@
-LOCAL_PATH := $(dir $(lastword $(MAKEFILE_LIST)))
-SDK_PATH := $(abspath $(LOCAL_PATH)/../../../../sdk/bpf)
-INSTALL_SH := $(abspath $(SDK_PATH)/scripts/install.sh)
-
-all:
-.PHONY: help all install dump clean
-
-ifneq ($(V),1)
-_@ :=@
-endif
-
-TARGET_NAME := solana_bpf_rust_noop
-SRC_DIR ?= ./src
-OUT_DIR ?= ./out
-INSTALL_DIR ?= ./out
-CARGO_OUT_DIR ?=$(LOCAL_PATH)target/release
-
-ifeq ($(DOCKER),1)
-$(warning DOCKER=1 is experimential and may not work as advertised)
-LLVM_DIR = $(SDK_PATH)/llvm-docker
-LLVM_SYSTEM_INC_DIRS := /usr/local/lib/clang/8.0.0/include
-else
-LLVM_DIR = $(SDK_PATH)/llvm-native
-LLVM_SYSTEM_INC_DIRS := $(LLVM_DIR)/lib/clang/8.0.0/include
-endif
-
-CARGO := cargo
-ifdef LLVM_DIR
-LLC := $(LLVM_DIR)/bin/llc
-LLD := $(LLVM_DIR)/bin/ld.lld
-OBJ_COPY := $(LLVM_DIR)/bin/llvm-objcopy
-OBJ_DUMP := $(LLVM_DIR)/bin/llvm-objdump
-endif
-
-CARGO_FLAGS := \
-  +nightly \
-  -vv rustc \
-  -vv \
-  --release \
-  -- \
-  --emit=llvm-ir \
-  -C panic=abort \
-
-LLC_FLAGS := \
-  -march=bpf \
-  -filetype=obj \
-
-LLD_FLAGS := \
-  -z notext \
-  -shared \
-  --Bdynamic \
-  $(LOCAL_PATH)bpf.ld \
-  --entry entrypoint \
-
-OBJ_COPY_FLAGS := \
-  --remove-section .eh_frame \
-
-OBJ_DUMP_FLAGS := \
-  -color \
-  -source \
-  -disassemble \
-
-help:
-	@echo ''
-	@echo 'solana-bpf-rust-noop makefile'
-	@echo ''
-	@echo 'This makefile will build the solana-bpf-rust-noop crate into a BPF shared object'
-	@echo ''
-	@echo 'This makefile is not run as part of the Solana workspace.  Doing so'
-	@echo 'would result in a cargo deadlock since this makefile also calls cargo with parameters'
-	@echo 'required to build the BPF program from Rust.'  
-	@echo ''
-	@echo 'Note: Rust BPF programs are tested as part of the Solana integration tests when'
-	@echo '      feature "bpf_rust" is enabled.  The solana-bpf-rust-noop crate must be built'
-	@echo '      with this makefile first before bulding Solana.'
-	@echo ''
-	@echo '      Here is a sample command that will run this BPF program:'
-	@echo ''
-	@echo '      export RUST_LOG=solana_bpf_loader=info; cargo test --features="bpf_rust" -- --nocapture test_program_bpf_rust'
-	@echo ''
-	@echo 'User settings'
-	@echo '  - The following setting are overridable on the command line, default values shown:'
-	@echo '    - Show commands while building: V=1'
-	@echo '      V=$(V)'
-	@echo '    - Location to place output files:'
-	@echo '      OUT_DIR=$(OUT_DIR)'
-	@echo '    - Location to install the final shared object:'
-	@echo '      INSTALL_DIR=$(INSTALL_DIR)'
-	@echo '    - Location of LLVM:'
-	@echo '      LLVM_DIR=$(LLVM_DIR)'
-	@echo ''
-	@echo 'Usage:'
-	@echo '  - make help - This help message'
-	@echo '  - make all  - Build $(OUT_DIR)/$(TARGET_NAME).so'
-	@echo '  - make dump - Dumps the contents of $(OUT_DIR)/$(TARGET_NAME).so to stdout, requires greadelf and rustfilt'
-	@echo ''
-
-.PHONY: $(INSTALL_SH)
-$(INSTALL_SH):
-	$(_@)$(INSTALL_SH)
-
-.PRECIOUS: $(OUT_DIR)/%.ll
-$(OUT_DIR)/%.ll: $(SRC_DIR)/*
-	@echo "[cargo] $@ ($<)"
-	$(_@)mkdir -p $(OUT_DIR)
-	$(_@)rm -f $(CARGO_OUT_DIR)/deps/$(TARGET_NAME)-*.ll
-	$(_@)export CARGO_INCREMENTAL=0; $(CARGO) $(CARGO_FLAGS)
-	$(_@)cp $(CARGO_OUT_DIR)/deps/$(TARGET_NAME)-*.ll $(OUT_DIR)/$(TARGET_NAME).ll
-
-.PRECIOUS: $(OUT_DIR)/%.o
-$(OUT_DIR)/%.o: $(OUT_DIR)/%.ll $(INSTALL_SH)
-	@echo "[llc] $@ ($<)"
-	$(_@)$(LLC) $(LLC_FLAGS) -o $@ $<
-	$(_@)$(OBJ_COPY) $(OBJ_COPY_FLAGS) $@
-
-.PRECIOUS: $(OUT_DIR)/%.so
-$(OUT_DIR)/%.so: $(OUT_DIR)/%.o $(INSTALL_SH)
-	@echo "[lld] $@ ($<)"
-	$(_@)$(LLD) $(LLD_FLAGS) -o $@ $<
-
--include $(wildcard $(OUT_DIR)/$(TARGET_NAME).d)
-
-define \n
-
-
-endef
-
-all: $(OUT_DIR)/$(TARGET_NAME).so
-
-# Warning: Do not build as part of install (e.g. install must not depend
-# on $(TARGET_NAME).so) doing so will deadlock cargo due to recrusive
-# calls to cargo
-install:
-	$(_@)mkdir -p $(INSTALL_DIR)
-	$(_@)cp $(OUT_DIR)/$(TARGET_NAME).so $(INSTALL_DIR)
-
-dump: $(OUT_DIR)/$(TARGET_NAME).so
-	$(_@)greadelf -aW $(OUT_DIR)/$(TARGET_NAME).so | rustfilt
-	$(_@)$(OBJ_DUMP) -disassemble -source $(OUT_DIR)/$(TARGET_NAME).so | rustfilt
-
-test:
-	cargo test -- --test-threads 1
-
-clean:
-	$(_@)rm -rf $(OUT_DIR)
-	cargo clean

+ 33 - 0
programs/bpf/rust/noop/src/lib.rs

@@ -7,6 +7,17 @@ mod solana_sdk;
 
 use solana_sdk::*;
 
+struct SStruct {
+    x: u64,
+    y: u64,
+    z: u64,
+}
+
+#[inline(never)]
+fn return_sstruct() -> SStruct {
+    SStruct { x: 1, y: 2, z: 3 }
+}
+
 fn process(ka: &mut [SolKeyedAccount], data: &[u8], info: &SolClusterInfo) -> bool {
     sol_log("Tick height:");
     sol_log_64(info.tick_height, 0, 0, 0, 0);
@@ -18,5 +29,27 @@ fn process(ka: &mut [SolKeyedAccount], data: &[u8], info: &SolClusterInfo) -> bo
     // programs will have specific requirements so they can do their work.
     sol_log("Account keys and instruction input data:");
     sol_log_params(ka, data);
+
+    {
+        // Test - use core methods, unwrap
+
+        // valid bytes, in a stack-allocated array
+        let sparkle_heart = [240, 159, 146, 150];
+
+        let result_str = core::str::from_utf8(&sparkle_heart).unwrap();
+
+        sol_log_64(0, 0, 0, 0, result_str.len() as u64);
+        sol_log(result_str);
+        assert_eq!("💖", result_str);
+    }
+
+    {
+        // Test - struct return
+        let s = return_sstruct();
+        sol_log_64(0, 0, s.x, s.y, s.z);
+        assert_eq!(s.x + s.y + s.z, 6);
+    }
+
+    sol_log("Success");
     true
 }

+ 58 - 17
programs/bpf/rust/noop/src/solana_sdk.rs

@@ -1,39 +1,80 @@
 //! @brief Solana Rust-based BPF program utility functions and types
 
-extern crate heapless;
+// extern crate heapless;
 
-use self::heapless::consts::*;
-use self::heapless::String; // fixed capacity `std::Vec` // type level integer used to specify capacity
+// use self::heapless::consts::*;
+// use self::heapless::String; // fixed capacity `std::Vec` // type level integer used to specify capacity
 #[cfg(test)]
 use self::tests::process;
 use core::mem::size_of;
+use core::panic::PanicInfo;
 use core::slice::from_raw_parts;
+
 #[cfg(not(test))]
 use process;
 
+// Panic handling
+extern "C" {
+    pub fn sol_panic_() -> !;
+}
+#[panic_handler]
+fn panic(_info: &PanicInfo) -> ! {
+    sol_log("Panic!");
+    // TODO rashes! sol_log(_info.payload().downcast_ref::<&str>().unwrap());
+    if let Some(location) = _info.location() {
+        if !location.file().is_empty() {
+            // TODO location.file() returns empty str, if we get here its been fixed
+            sol_log(location.file());
+            sol_log("location.file() is fixed!!");
+            unsafe {
+                sol_panic_();
+            }
+        }
+        sol_log_64(0, 0, 0, location.line() as u64, location.column() as u64);
+    } else {
+        sol_log("Panic! but could not get location information");
+    }
+    unsafe {
+        sol_panic_();
+    }
+}
+
 extern "C" {
     fn sol_log_(message: *const u8);
 }
 /// Helper function that prints a string to stdout
+#[inline(never)] // stack intensive, block inline so everyone does not incur
 pub fn sol_log(message: &str) {
-    let mut c_string: String<U256> = String::new();
-    if message.len() < 256 {
-        if c_string.push_str(message).is_err() {
-            c_string
-                .push_str("Attempted to log a malformed string\0")
-                .is_ok();
+    // TODO This is extremely slow, do something better
+    let mut buf: [u8; 128] = [0; 128];
+    for (i, b) in message.as_bytes().iter().enumerate() {
+        if i >= 126 {
+            break;
         }
-        if c_string.push('\0').is_err() {
-            c_string.push_str("Failed to log string\0").is_ok();
-        };
-    } else {
-        c_string
-            .push_str("Attempted to log a string that is too long\0")
-            .is_ok();
+        buf[i] = *b;
     }
     unsafe {
-        sol_log_(c_string.as_bytes().as_ptr());
+        sol_log_(buf.as_ptr());
     }
+
+    // let mut c_string: String<U256> = String::new();
+    // if message.len() < 256 {
+    //     if c_string.push_str(message).is_err() {
+    //         c_string
+    //             .push_str("Attempted to log a malformed string\0")
+    //             .is_ok();
+    //     }
+    //     if c_string.push('\0').is_err() {
+    //         c_string.push_str("Failed to log string\0").is_ok();
+    //     };
+    // } else {
+    //     c_string
+    //         .push_str("Attempted to log a string that is too long\0")
+    //         .is_ok();
+    // }
+    // unsafe {
+    //     sol_log_(message.as_ptr());
+    // }
 }
 
 extern "C" {

+ 23 - 16
programs/native/bpf_loader/build.rs

@@ -60,43 +60,50 @@ fn main() {
 
     let bpf_rust = !env::var("CARGO_FEATURE_BPF_RUST").is_err();
     if bpf_rust {
-        let install_dir = "INSTALL_DIR=../../../../target/".to_string()
-            + &env::var("PROFILE").unwrap()
-            + &"/bpf".to_string();
+        let install_dir =
+            "../../../../target/".to_string() + &env::var("PROFILE").unwrap() + &"/bpf".to_string();
 
-        if !Path::new("../../bpf/rust/noop/out/solana_bpf_rust_noop.so").is_file() {
+        if !Path::new(
+            "../../bpf/rust/noop/target/bpfel-unknown-unknown/release/solana_bpf_rust_noop.so",
+        )
+        .is_file()
+        {
             // Cannot build Rust BPF programs as part of main build because
             // to build it requires calling Cargo with different parameters which
             // would deadlock due to recursive cargo calls
             panic!(
                 "solana_bpf_rust_noop.so not found, you must manually run \
-                 `make all` in programs/bpf/rust/noop to build it"
+                 `build.sh` in programs/bpf/rust/noop to build it"
             );
         }
 
         rerun_if_changed(
             &[
                 "../../bpf/rust/noop/bpf.ld",
-                "../../bpf/rust/noop/makefile",
-                "../../bpf/rust/noop/out/solana_bpf_rust_noop.so",
+                "../../bpf/rust/noop/build.sh",
+                "../../bpf/rust/noop/Cargo.toml",
+                "../../bpf/rust/noop/target/bpfel-unknown-unknown/release/solana_bpf_rust_noop.so",
             ],
-            &[],
+            &["../../bpf/rust/noop/src"],
         );
 
         println!(
             "cargo:warning=(not a warning) Installing Rust-based BPF program: solana_bpf_rust_noop"
         );
-        let status = Command::new("make")
+        let status = Command::new("mkdir")
             .current_dir("../../bpf/rust/noop")
-            .arg("install")
-            .arg("V=1")
-            .arg("OUT_DIR=out")
+            .arg("-p")
             .arg(&install_dir)
             .status()
-            .expect(
-                "solana_bpf_rust_noop.so not found, you must manually run \
-                 `make all` in its program directory",
-            );
+            .expect("Unable to create BPF install directory");
+        assert!(status.success());
+
+        let status = Command::new("cp")
+            .current_dir("../../bpf/rust/noop")
+            .arg("target/bpfel-unknown-unknown/release/solana_bpf_rust_noop.so")
+            .arg(&install_dir)
+            .status()
+            .expect("Failed to copy solana_rust_bpf_noop.so to install directory");
         assert!(status.success());
     }
 }