From 1075a73902e0a004f49f4a42e962b91c316b5cd7 Mon Sep 17 00:00:00 2001 From: jackcmay Date: Tue, 6 Nov 2018 14:28:46 -0800 Subject: [PATCH] Elf relocations (#1724) Use relocatable BPF ELFs --- programs/bpf/c/sdk/bpf.mk | 1 - programs/bpf/c/sdk/inc/solana_sdk.h | 57 ++++++------------ programs/bpf/c/src/move_funds.c | 4 +- programs/bpf/c/src/noop.c | 4 +- programs/native/bpf_loader/Cargo.toml | 2 +- programs/native/bpf_loader/src/lib.rs | 84 ++++++++++++++++++++------- tests/programs.rs | 38 ++++++------ 7 files changed, 105 insertions(+), 85 deletions(-) diff --git a/programs/bpf/c/sdk/bpf.mk b/programs/bpf/c/sdk/bpf.mk index 76cf19e494..1ff171ee21 100644 --- a/programs/bpf/c/sdk/bpf.mk +++ b/programs/bpf/c/sdk/bpf.mk @@ -37,7 +37,6 @@ CC_FLAGS := \ LLC_FLAGS := \ -march=bpf \ -filetype=obj \ - -function-sections \ OBJ_DUMP_FLAGS := \ -color \ diff --git a/programs/bpf/c/sdk/inc/solana_sdk.h b/programs/bpf/c/sdk/inc/solana_sdk.h index 7a2892cbe5..ac12a68c43 100644 --- a/programs/bpf/c/sdk/inc/solana_sdk.h +++ b/programs/bpf/c/sdk/inc/solana_sdk.h @@ -34,36 +34,15 @@ typedef unsigned long int uint64_t; typedef enum { false = 0, true } bool; /** - * Built-in helper functions - * @{ - * The BPF VM makes a limited number of helper functions available to BPF - * programs. They are resolved at run-time and identified by a function index. - * Calling any of these functions results in `Call` instruction out of the - * user's BPF program. - * - * The helper functions all follow the same signature: - * - * int helper(uint64_t, uint64_t, uint64_t, uint64_t, uint64_t) - * - * The meaning of each argument and return value is dependent on the particular - * helper function being called. + * Helper function that prints a string to stdout */ +extern void sol_log(const char*); /** - * Helper function that prints to stdout - * - * Prints the hexadecimal representation of each parameter + * Helper function that prints a 64 bit values represented in hexadecimal + * to stdout */ -#define BPF_TRACE_PRINTK_IDX 6 -static int (*sol_print)( - uint64_t, - uint64_t, - uint64_t, - uint64_t, - uint64_t -) = (void *)BPF_TRACE_PRINTK_IDX; - -/**@}*/ +extern void sol_log_64(uint64_t, uint64_t, uint64_t, uint64_t, uint64_t); /** * Prefix for all BPF functions @@ -147,7 +126,7 @@ SOL_FN_PREFIX int sol_memcmp(const void *s1, const void *s2, int n) { */ #define sol_panic() _sol_panic(__LINE__) SOL_FN_PREFIX void _sol_panic(uint64_t line) { - sol_print(0xFF, 0xFF, 0xFF, 0xFF, line); + sol_log_64(0xFF, 0xFF, 0xFF, 0xFF, line); uint8_t *pv = (uint8_t *)1; *pv = 1; } @@ -241,9 +220,9 @@ SOL_FN_PREFIX bool sol_deserialize( * * @param key The public key to print */ -SOL_FN_PREFIX void sol_print_key(const SolPubkey *key) { +SOL_FN_PREFIX void sol_log_key(const SolPubkey *key) { for (int j = 0; j < sizeof(*key); j++) { - sol_print(0, 0, 0, j, key->x[j]); + sol_log_64(0, 0, 0, j, key->x[j]); } } @@ -252,9 +231,9 @@ SOL_FN_PREFIX void sol_print_key(const SolPubkey *key) { * * @param array The array to print */ -SOL_FN_PREFIX void sol_print_array(const uint8_t *array, int len) { +SOL_FN_PREFIX void sol_log_array(const uint8_t *array, int len) { for (int j = 0; j < len; j++) { - sol_print(0, 0, 0, j, array[j]); + sol_log_64(0, 0, 0, j, array[j]); } } @@ -266,20 +245,20 @@ SOL_FN_PREFIX void sol_print_array(const uint8_t *array, int len) { * @param data A pointer to the instruction data to print * @param data_len The length in bytes of the instruction data */ -SOL_FN_PREFIX void sol_print_params( +SOL_FN_PREFIX void sol_log_params( uint64_t num_ka, const SolKeyedAccounts *ka, const uint8_t *data, uint64_t data_len ) { - sol_print(0, 0, 0, 0, num_ka); + sol_log_64(0, 0, 0, 0, num_ka); for (int i = 0; i < num_ka; i++) { - sol_print_key(ka[i].key); - sol_print(0, 0, 0, 0, *ka[i].tokens); - sol_print_array(ka[i].userdata, ka[i].userdata_len); - sol_print_key(ka[i].program_id); + sol_log_key(ka[i].key); + sol_log_64(0, 0, 0, 0, *ka[i].tokens); + sol_log_array(ka[i].userdata, ka[i].userdata_len); + sol_log_key(ka[i].program_id); } - sol_print_array(data, data_len); + sol_log_array(data, data_len); } /**@}*/ @@ -299,7 +278,7 @@ SOL_FN_PREFIX void sol_print_params( * if (!sol_deserialize(buf, ka, SOL_ARRAY_SIZE(ka), NULL, &data, &data_len)) { * return false; * } - * print_params(1, ka, data, data_len); + * sol_log_params(1, ka, data, data_len); * return true; * } */ diff --git a/programs/bpf/c/src/move_funds.c b/programs/bpf/c/src/move_funds.c index d796797d6d..6c4d354c0d 100644 --- a/programs/bpf/c/src/move_funds.c +++ b/programs/bpf/c/src/move_funds.c @@ -24,9 +24,9 @@ extern bool entrypoint(const uint8_t *input) { if (*ka[0].tokens >= tokens) { *ka[0].tokens -= tokens; *ka[2].tokens += tokens; - // sol_print(0, 0, *ka[0].tokens, *ka[2].tokens, tokens); + // sol_log_64(0, 0, *ka[0].tokens, *ka[2].tokens, tokens); } else { - // sol_print(0, 0, 0xFF, *ka[0].tokens, tokens); + // sol_log_64(0, 0, 0xFF, *ka[0].tokens, tokens); } return true; } diff --git a/programs/bpf/c/src/noop.c b/programs/bpf/c/src/noop.c index 6955ef31fb..c126424ca8 100644 --- a/programs/bpf/c/src/noop.c +++ b/programs/bpf/c/src/noop.c @@ -15,11 +15,13 @@ extern bool entrypoint(const uint8_t *input) { SolKeyedAccounts ka[NUM_KA]; const uint8_t *data; uint64_t data_len; + + sol_log("noop"); if (!sol_deserialize(input, ka, NUM_KA, NULL, &data, &data_len)) { return false; } - sol_print_params(NUM_KA, ka, data, data_len); + sol_log_params(NUM_KA, ka, data, data_len); sol_assert(sizeof(int8_t) == 1); sol_assert(sizeof(uint8_t) == 1); diff --git a/programs/native/bpf_loader/Cargo.toml b/programs/native/bpf_loader/Cargo.toml index 2f07efd88f..36a56ab33d 100644 --- a/programs/native/bpf_loader/Cargo.toml +++ b/programs/native/bpf_loader/Cargo.toml @@ -13,7 +13,7 @@ elf = "0.0.10" env_logger = "0.5.12" libc = "0.2.43" log = "0.4.2" -solana_rbpf = "0.1.2" +solana_rbpf = "0.1.3" serde = "1.0.27" serde_derive = "1.0.27" solana-sdk = { path = "../../../sdk", version = "0.11.0" } diff --git a/programs/native/bpf_loader/src/lib.rs b/programs/native/bpf_loader/src/lib.rs index e08876fb89..c54e330939 100644 --- a/programs/native/bpf_loader/src/lib.rs +++ b/programs/native/bpf_loader/src/lib.rs @@ -5,20 +5,24 @@ extern crate byteorder; extern crate env_logger; #[macro_use] extern crate log; +extern crate libc; extern crate solana_rbpf; extern crate solana_sdk; use bincode::deserialize; use byteorder::{ByteOrder, LittleEndian, WriteBytesExt}; -use solana_rbpf::{helpers, EbpfVmRaw}; +use libc::c_char; +use solana_rbpf::EbpfVmRaw; use solana_sdk::account::KeyedAccount; use solana_sdk::loader_instruction::LoaderInstruction; use solana_sdk::pubkey::Pubkey; +use std::ffi::CStr; use std::io::prelude::*; -use std::io::Error; +use std::io::{Error, ErrorKind}; use std::mem; use std::sync::{Once, ONCE_INIT}; +// TODO use rbpf's disassemble #[allow(dead_code)] fn dump_program(key: &Pubkey, prog: &[u8]) { let mut eight_bytes: Vec = Vec::new(); @@ -33,32 +37,64 @@ fn dump_program(key: &Pubkey, prog: &[u8]) { } } -pub fn helper_printf(arg1: u64, arg2: u64, arg3: u64, arg4: u64, arg5: u64) -> u64 { +#[allow(unused_variables)] +pub fn helper_sol_log_verify( + addr: u64, + unused2: u64, + unused3: u64, + unused4: u64, + unused5: u64, + ro_regions: &[&[u8]], + unused7: &[&[u8]], +) -> Result<(()), Error> { + for region in ro_regions.iter() { + if region.as_ptr() as u64 <= addr + && addr as u64 <= region.as_ptr() as u64 + region.len() as u64 + { + let c_buf: *const c_char = addr as *const c_char; + let max_size = (region.as_ptr() as u64 + region.len() as u64) - addr; + unsafe { + for i in 0..max_size { + if std::ptr::read(c_buf.offset(i as isize)) == 0 { + return Ok(()); + } + } + } + return Err(Error::new(ErrorKind::Other, "Error, Unterminated string")); + } + } + Err(Error::new( + ErrorKind::Other, + "Error: Load segfault, bad string pointer", + )) +} + +#[allow(unused_variables)] +pub fn helper_sol_log(addr: u64, unused2: u64, unused3: u64, unused4: u64, unused5: u64) -> u64 { + let c_buf: *const c_char = addr as *const c_char; + let c_str: &CStr = unsafe { CStr::from_ptr(c_buf) }; + match c_str.to_str() { + Ok(slice) => info!("sol_log: {:?}", slice), + Err(e) => warn!("Error: Cannot print invalid string"), + }; + 0 +} + +pub fn helper_sol_log_u64(arg1: u64, arg2: u64, arg3: u64, arg4: u64, arg5: u64) -> u64 { info!( - "bpf_trace_printf: {:#x}, {:#x}, {:#x}, {:#x}, {:#x}", + "sol_log_u64: {:#x}, {:#x}, {:#x}, {:#x}, {:#x}", arg1, arg2, arg3, arg4, arg5 ); - let size_arg = |x| { - if x == 0 { - 1 - } else { - (x as f64).log(16.0).floor() as u64 + 1 - } - }; - "bpf_trace_printf: 0x, 0x, 0x, 0x, 0x\n".len() as u64 - + size_arg(arg1) - + size_arg(arg2) - + size_arg(arg3) - + size_arg(arg4) - + size_arg(arg5) + 0 } fn create_vm(prog: &[u8]) -> Result { let mut vm = EbpfVmRaw::new(None)?; vm.set_verifier(bpf_verifier::check)?; vm.set_max_instruction_count(36000)?; // 36000 is a wag, need to tune - vm.set_program(&prog)?; - vm.register_helper(helpers::BPF_TRACE_PRINTK_IDX, helper_printf)?; + vm.set_elf(&prog)?; + vm.register_helper_ex("sol_log", Some(helper_sol_log_verify), helper_sol_log)?; + vm.register_helper_ex("sol_log_64", None, helper_sol_log_u64)?; Ok(vm) } @@ -165,6 +201,8 @@ pub extern "C" fn process(keyed_accounts: &mut [KeyedAccount], tx_data: &[u8]) - #[cfg(test)] mod tests { use super::*; + use solana_rbpf::helpers; + #[test] #[should_panic(expected = "Error: Execution exceeded maximum number of instructions")] fn test_non_terminating_program() { @@ -182,7 +220,13 @@ mod tests { 0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // exit ]; let input = &mut [0x00]; - let mut vm = create_vm(prog).unwrap(); + + let mut vm = EbpfVmRaw::new(None).unwrap(); + vm.set_verifier(bpf_verifier::check).unwrap(); + vm.set_max_instruction_count(36000).unwrap(); // 36000 is a wag, need to tune + vm.set_program(prog).unwrap(); + vm.register_helper(helpers::BPF_TRACE_PRINTK_IDX, helpers::bpf_trace_printf) + .unwrap(); vm.execute_program(input).unwrap(); } } diff --git a/tests/programs.rs b/tests/programs.rs index 246b596e33..5f94e89a85 100644 --- a/tests/programs.rs +++ b/tests/programs.rs @@ -18,6 +18,10 @@ use solana_sdk::pubkey::Pubkey; #[cfg(feature = "bpf_c")] use std::env; #[cfg(feature = "bpf_c")] +use std::fs::File; +#[cfg(feature = "bpf_c")] +use std::io::Read; +#[cfg(feature = "bpf_c")] use std::path::PathBuf; /// BPF program file extension @@ -117,7 +121,7 @@ struct Program { } impl Program { - pub fn new(loader: &Loader, userdata: Vec) -> Self { + pub fn new(loader: &Loader, userdata: &Vec) -> Self { let program = Keypair::new(); // allocate, populate, finalize and spawn program @@ -181,7 +185,7 @@ fn test_program_native_noop() { let loader = Loader::new_native(); let name = String::from("noop"); let userdata = name.as_bytes().to_vec(); - let program = Program::new(&loader, userdata); + let program = Program::new(&loader, &userdata); // Call user program let tx = Transaction::new( @@ -211,7 +215,7 @@ fn test_program_lua_move_funds() { accounts[2].tokens = accounts[2].tokens + tokens "#.as_bytes() .to_vec(); - let program = Program::new(&loader, userdata); + let program = Program::new(&loader, &userdata); let from = Keypair::new(); let to = Keypair::new().pubkey(); @@ -269,16 +273,12 @@ fn test_program_lua_move_funds() { fn test_program_builtin_bpf_noop() { logger::setup(); + let mut file = File::open(create_bpf_path("noop")).expect("file open failed"); + let mut elf = Vec::new(); + file.read_to_end(&mut elf).unwrap(); + let loader = Loader::new_bpf(); - let program = Program::new( - &loader, - elf::File::open_path(&create_bpf_path("noop")) - .unwrap() - .get_section(PLATFORM_SECTION_C) - .unwrap() - .data - .clone(), - ); + let program = Program::new(&loader, &elf); // Call user program let tx = Transaction::new( @@ -301,16 +301,12 @@ fn test_program_builtin_bpf_noop() { fn test_program_bpf_noop_c() { logger::setup(); + let mut file = File::open(create_bpf_path("noop")).expect("file open failed"); + let mut elf = Vec::new(); + file.read_to_end(&mut elf).unwrap(); + let loader = Loader::new_dynamic("solana_bpf_loader"); - let program = Program::new( - &loader, - elf::File::open_path(&create_bpf_path("noop")) - .unwrap() - .get_section(PLATFORM_SECTION_C) - .unwrap() - .data - .clone(), - ); + let program = Program::new(&loader, &elf); // Call user program let tx = Transaction::new(