From b9524217feeea6a377127c1bbdeab683977f070c Mon Sep 17 00:00:00 2001 From: Jack May Date: Thu, 28 Feb 2019 22:05:11 -0800 Subject: [PATCH] Update rust example to use BPF enabled infrastructure (#2974) --- ci/test-stable.sh | 4 +- programs/bpf/rust/noop/.gitignore | 1 - programs/bpf/rust/noop/Cargo.toml | 12 +- programs/bpf/rust/noop/Xargo.toml | 4 + programs/bpf/rust/noop/build.sh | 25 ++++ programs/bpf/rust/noop/clean.sh | 5 + programs/bpf/rust/noop/dump.sh | 12 ++ programs/bpf/rust/noop/makefile | 146 ----------------------- programs/bpf/rust/noop/src/lib.rs | 33 +++++ programs/bpf/rust/noop/src/solana_sdk.rs | 75 +++++++++--- programs/native/bpf_loader/build.rs | 39 +++--- 11 files changed, 173 insertions(+), 183 deletions(-) create mode 100644 programs/bpf/rust/noop/Xargo.toml create mode 100755 programs/bpf/rust/noop/build.sh create mode 100755 programs/bpf/rust/noop/clean.sh create mode 100755 programs/bpf/rust/noop/dump.sh delete mode 100755 programs/bpf/rust/noop/makefile diff --git a/ci/test-stable.sh b/ci/test-stable.sh index 6707688e5..1ac076643 100755 --- a/ci/test-stable.sh +++ b/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" diff --git a/programs/bpf/rust/noop/.gitignore b/programs/bpf/rust/noop/.gitignore index a50bd949c..e13de17f6 100644 --- a/programs/bpf/rust/noop/.gitignore +++ b/programs/bpf/rust/noop/.gitignore @@ -1,4 +1,3 @@ -/out/ /target/ Cargo.lock diff --git a/programs/bpf/rust/noop/Cargo.toml b/programs/bpf/rust/noop/Cargo.toml index 7e464b084..bd1e30efe 100644 --- a/programs/bpf/rust/noop/Cargo.toml +++ b/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"] + + diff --git a/programs/bpf/rust/noop/Xargo.toml b/programs/bpf/rust/noop/Xargo.toml new file mode 100644 index 000000000..b5d189bcc --- /dev/null +++ b/programs/bpf/rust/noop/Xargo.toml @@ -0,0 +1,4 @@ + + +[dependencies.compiler_builtins] +path = "../../../../sdk/bpf/rust-bpf-sysroot/src/compiler-builtins" \ No newline at end of file diff --git a/programs/bpf/rust/noop/build.sh b/programs/bpf/rust/noop/build.sh new file mode 100755 index 000000000..b02726ca2 --- /dev/null +++ b/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; } \ No newline at end of file diff --git a/programs/bpf/rust/noop/clean.sh b/programs/bpf/rust/noop/clean.sh new file mode 100755 index 000000000..ea95016e3 --- /dev/null +++ b/programs/bpf/rust/noop/clean.sh @@ -0,0 +1,5 @@ +#!/usr/bin/env bash + +set -ex + +cargo clean diff --git a/programs/bpf/rust/noop/dump.sh b/programs/bpf/rust/noop/dump.sh new file mode 100755 index 000000000..8108047d5 --- /dev/null +++ b/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 diff --git a/programs/bpf/rust/noop/makefile b/programs/bpf/rust/noop/makefile deleted file mode 100755 index 73522977b..000000000 --- a/programs/bpf/rust/noop/makefile +++ /dev/null @@ -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 diff --git a/programs/bpf/rust/noop/src/lib.rs b/programs/bpf/rust/noop/src/lib.rs index bff0cd8f7..4fa2a549c 100644 --- a/programs/bpf/rust/noop/src/lib.rs +++ b/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 } diff --git a/programs/bpf/rust/noop/src/solana_sdk.rs b/programs/bpf/rust/noop/src/solana_sdk.rs index 69e0fa225..82fac64ab 100644 --- a/programs/bpf/rust/noop/src/solana_sdk.rs +++ b/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 = 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 = 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" { diff --git a/programs/native/bpf_loader/build.rs b/programs/native/bpf_loader/build.rs index 3e071d2ea..b9f85cd34 100644 --- a/programs/native/bpf_loader/build.rs +++ b/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()); } }