Update rust example to use BPF enabled infrastructure (#2974)
This commit is contained in:
parent
6b228df3df
commit
b9524217fe
|
@ -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"
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
/out/
|
||||
/target/
|
||||
|
||||
Cargo.lock
|
||||
|
|
|
@ -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"]
|
||||
|
||||
|
||||
|
|
|
@ -0,0 +1,4 @@
|
|||
|
||||
|
||||
[dependencies.compiler_builtins]
|
||||
path = "../../../../sdk/bpf/rust-bpf-sysroot/src/compiler-builtins"
|
|
@ -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; }
|
|
@ -0,0 +1,5 @@
|
|||
#!/usr/bin/env bash
|
||||
|
||||
set -ex
|
||||
|
||||
cargo clean
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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" {
|
||||
|
|
|
@ -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());
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue