parent
863a0c3f8f
commit
1075a73902
|
@ -37,7 +37,6 @@ CC_FLAGS := \
|
||||||
LLC_FLAGS := \
|
LLC_FLAGS := \
|
||||||
-march=bpf \
|
-march=bpf \
|
||||||
-filetype=obj \
|
-filetype=obj \
|
||||||
-function-sections \
|
|
||||||
|
|
||||||
OBJ_DUMP_FLAGS := \
|
OBJ_DUMP_FLAGS := \
|
||||||
-color \
|
-color \
|
||||||
|
|
|
@ -34,36 +34,15 @@ typedef unsigned long int uint64_t;
|
||||||
typedef enum { false = 0, true } bool;
|
typedef enum { false = 0, true } bool;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Built-in helper functions
|
* Helper function that prints a string to stdout
|
||||||
* @{
|
|
||||||
* 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.
|
|
||||||
*/
|
*/
|
||||||
|
extern void sol_log(const char*);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Helper function that prints to stdout
|
* Helper function that prints a 64 bit values represented in hexadecimal
|
||||||
*
|
* to stdout
|
||||||
* Prints the hexadecimal representation of each parameter
|
|
||||||
*/
|
*/
|
||||||
#define BPF_TRACE_PRINTK_IDX 6
|
extern void sol_log_64(uint64_t, uint64_t, uint64_t, uint64_t, uint64_t);
|
||||||
static int (*sol_print)(
|
|
||||||
uint64_t,
|
|
||||||
uint64_t,
|
|
||||||
uint64_t,
|
|
||||||
uint64_t,
|
|
||||||
uint64_t
|
|
||||||
) = (void *)BPF_TRACE_PRINTK_IDX;
|
|
||||||
|
|
||||||
/**@}*/
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Prefix for all BPF functions
|
* 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__)
|
#define sol_panic() _sol_panic(__LINE__)
|
||||||
SOL_FN_PREFIX void _sol_panic(uint64_t 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;
|
uint8_t *pv = (uint8_t *)1;
|
||||||
*pv = 1;
|
*pv = 1;
|
||||||
}
|
}
|
||||||
|
@ -241,9 +220,9 @@ SOL_FN_PREFIX bool sol_deserialize(
|
||||||
*
|
*
|
||||||
* @param key The public key to print
|
* @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++) {
|
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
|
* @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++) {
|
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 A pointer to the instruction data to print
|
||||||
* @param data_len The length in bytes of the instruction data
|
* @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,
|
uint64_t num_ka,
|
||||||
const SolKeyedAccounts *ka,
|
const SolKeyedAccounts *ka,
|
||||||
const uint8_t *data,
|
const uint8_t *data,
|
||||||
uint64_t data_len
|
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++) {
|
for (int i = 0; i < num_ka; i++) {
|
||||||
sol_print_key(ka[i].key);
|
sol_log_key(ka[i].key);
|
||||||
sol_print(0, 0, 0, 0, *ka[i].tokens);
|
sol_log_64(0, 0, 0, 0, *ka[i].tokens);
|
||||||
sol_print_array(ka[i].userdata, ka[i].userdata_len);
|
sol_log_array(ka[i].userdata, ka[i].userdata_len);
|
||||||
sol_print_key(ka[i].program_id);
|
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)) {
|
* if (!sol_deserialize(buf, ka, SOL_ARRAY_SIZE(ka), NULL, &data, &data_len)) {
|
||||||
* return false;
|
* return false;
|
||||||
* }
|
* }
|
||||||
* print_params(1, ka, data, data_len);
|
* sol_log_params(1, ka, data, data_len);
|
||||||
* return true;
|
* return true;
|
||||||
* }
|
* }
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -24,9 +24,9 @@ extern bool entrypoint(const uint8_t *input) {
|
||||||
if (*ka[0].tokens >= tokens) {
|
if (*ka[0].tokens >= tokens) {
|
||||||
*ka[0].tokens -= tokens;
|
*ka[0].tokens -= tokens;
|
||||||
*ka[2].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 {
|
} else {
|
||||||
// sol_print(0, 0, 0xFF, *ka[0].tokens, tokens);
|
// sol_log_64(0, 0, 0xFF, *ka[0].tokens, tokens);
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,10 +16,12 @@ extern bool entrypoint(const uint8_t *input) {
|
||||||
const uint8_t *data;
|
const uint8_t *data;
|
||||||
uint64_t data_len;
|
uint64_t data_len;
|
||||||
|
|
||||||
|
sol_log("noop");
|
||||||
|
|
||||||
if (!sol_deserialize(input, ka, NUM_KA, NULL, &data, &data_len)) {
|
if (!sol_deserialize(input, ka, NUM_KA, NULL, &data, &data_len)) {
|
||||||
return false;
|
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(int8_t) == 1);
|
||||||
sol_assert(sizeof(uint8_t) == 1);
|
sol_assert(sizeof(uint8_t) == 1);
|
||||||
|
|
|
@ -13,7 +13,7 @@ elf = "0.0.10"
|
||||||
env_logger = "0.5.12"
|
env_logger = "0.5.12"
|
||||||
libc = "0.2.43"
|
libc = "0.2.43"
|
||||||
log = "0.4.2"
|
log = "0.4.2"
|
||||||
solana_rbpf = "0.1.2"
|
solana_rbpf = "0.1.3"
|
||||||
serde = "1.0.27"
|
serde = "1.0.27"
|
||||||
serde_derive = "1.0.27"
|
serde_derive = "1.0.27"
|
||||||
solana-sdk = { path = "../../../sdk", version = "0.11.0" }
|
solana-sdk = { path = "../../../sdk", version = "0.11.0" }
|
||||||
|
|
|
@ -5,20 +5,24 @@ extern crate byteorder;
|
||||||
extern crate env_logger;
|
extern crate env_logger;
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
extern crate log;
|
extern crate log;
|
||||||
|
extern crate libc;
|
||||||
extern crate solana_rbpf;
|
extern crate solana_rbpf;
|
||||||
extern crate solana_sdk;
|
extern crate solana_sdk;
|
||||||
|
|
||||||
use bincode::deserialize;
|
use bincode::deserialize;
|
||||||
use byteorder::{ByteOrder, LittleEndian, WriteBytesExt};
|
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::account::KeyedAccount;
|
||||||
use solana_sdk::loader_instruction::LoaderInstruction;
|
use solana_sdk::loader_instruction::LoaderInstruction;
|
||||||
use solana_sdk::pubkey::Pubkey;
|
use solana_sdk::pubkey::Pubkey;
|
||||||
|
use std::ffi::CStr;
|
||||||
use std::io::prelude::*;
|
use std::io::prelude::*;
|
||||||
use std::io::Error;
|
use std::io::{Error, ErrorKind};
|
||||||
use std::mem;
|
use std::mem;
|
||||||
use std::sync::{Once, ONCE_INIT};
|
use std::sync::{Once, ONCE_INIT};
|
||||||
|
|
||||||
|
// TODO use rbpf's disassemble
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
fn dump_program(key: &Pubkey, prog: &[u8]) {
|
fn dump_program(key: &Pubkey, prog: &[u8]) {
|
||||||
let mut eight_bytes: Vec<u8> = Vec::new();
|
let mut eight_bytes: Vec<u8> = 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!(
|
info!(
|
||||||
"bpf_trace_printf: {:#x}, {:#x}, {:#x}, {:#x}, {:#x}",
|
"sol_log_u64: {:#x}, {:#x}, {:#x}, {:#x}, {:#x}",
|
||||||
arg1, arg2, arg3, arg4, arg5
|
arg1, arg2, arg3, arg4, arg5
|
||||||
);
|
);
|
||||||
let size_arg = |x| {
|
0
|
||||||
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)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn create_vm(prog: &[u8]) -> Result<EbpfVmRaw, Error> {
|
fn create_vm(prog: &[u8]) -> Result<EbpfVmRaw, Error> {
|
||||||
let mut vm = EbpfVmRaw::new(None)?;
|
let mut vm = EbpfVmRaw::new(None)?;
|
||||||
vm.set_verifier(bpf_verifier::check)?;
|
vm.set_verifier(bpf_verifier::check)?;
|
||||||
vm.set_max_instruction_count(36000)?; // 36000 is a wag, need to tune
|
vm.set_max_instruction_count(36000)?; // 36000 is a wag, need to tune
|
||||||
vm.set_program(&prog)?;
|
vm.set_elf(&prog)?;
|
||||||
vm.register_helper(helpers::BPF_TRACE_PRINTK_IDX, helper_printf)?;
|
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)
|
Ok(vm)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -165,6 +201,8 @@ pub extern "C" fn process(keyed_accounts: &mut [KeyedAccount], tx_data: &[u8]) -
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
use solana_rbpf::helpers;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
#[should_panic(expected = "Error: Execution exceeded maximum number of instructions")]
|
#[should_panic(expected = "Error: Execution exceeded maximum number of instructions")]
|
||||||
fn test_non_terminating_program() {
|
fn test_non_terminating_program() {
|
||||||
|
@ -182,7 +220,13 @@ mod tests {
|
||||||
0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // exit
|
0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // exit
|
||||||
];
|
];
|
||||||
let input = &mut [0x00];
|
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();
|
vm.execute_program(input).unwrap();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,6 +18,10 @@ use solana_sdk::pubkey::Pubkey;
|
||||||
#[cfg(feature = "bpf_c")]
|
#[cfg(feature = "bpf_c")]
|
||||||
use std::env;
|
use std::env;
|
||||||
#[cfg(feature = "bpf_c")]
|
#[cfg(feature = "bpf_c")]
|
||||||
|
use std::fs::File;
|
||||||
|
#[cfg(feature = "bpf_c")]
|
||||||
|
use std::io::Read;
|
||||||
|
#[cfg(feature = "bpf_c")]
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
|
|
||||||
/// BPF program file extension
|
/// BPF program file extension
|
||||||
|
@ -117,7 +121,7 @@ struct Program {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Program {
|
impl Program {
|
||||||
pub fn new(loader: &Loader, userdata: Vec<u8>) -> Self {
|
pub fn new(loader: &Loader, userdata: &Vec<u8>) -> Self {
|
||||||
let program = Keypair::new();
|
let program = Keypair::new();
|
||||||
|
|
||||||
// allocate, populate, finalize and spawn program
|
// allocate, populate, finalize and spawn program
|
||||||
|
@ -181,7 +185,7 @@ fn test_program_native_noop() {
|
||||||
let loader = Loader::new_native();
|
let loader = Loader::new_native();
|
||||||
let name = String::from("noop");
|
let name = String::from("noop");
|
||||||
let userdata = name.as_bytes().to_vec();
|
let userdata = name.as_bytes().to_vec();
|
||||||
let program = Program::new(&loader, userdata);
|
let program = Program::new(&loader, &userdata);
|
||||||
|
|
||||||
// Call user program
|
// Call user program
|
||||||
let tx = Transaction::new(
|
let tx = Transaction::new(
|
||||||
|
@ -211,7 +215,7 @@ fn test_program_lua_move_funds() {
|
||||||
accounts[2].tokens = accounts[2].tokens + tokens
|
accounts[2].tokens = accounts[2].tokens + tokens
|
||||||
"#.as_bytes()
|
"#.as_bytes()
|
||||||
.to_vec();
|
.to_vec();
|
||||||
let program = Program::new(&loader, userdata);
|
let program = Program::new(&loader, &userdata);
|
||||||
let from = Keypair::new();
|
let from = Keypair::new();
|
||||||
let to = Keypair::new().pubkey();
|
let to = Keypair::new().pubkey();
|
||||||
|
|
||||||
|
@ -269,16 +273,12 @@ fn test_program_lua_move_funds() {
|
||||||
fn test_program_builtin_bpf_noop() {
|
fn test_program_builtin_bpf_noop() {
|
||||||
logger::setup();
|
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 loader = Loader::new_bpf();
|
||||||
let program = Program::new(
|
let program = Program::new(&loader, &elf);
|
||||||
&loader,
|
|
||||||
elf::File::open_path(&create_bpf_path("noop"))
|
|
||||||
.unwrap()
|
|
||||||
.get_section(PLATFORM_SECTION_C)
|
|
||||||
.unwrap()
|
|
||||||
.data
|
|
||||||
.clone(),
|
|
||||||
);
|
|
||||||
|
|
||||||
// Call user program
|
// Call user program
|
||||||
let tx = Transaction::new(
|
let tx = Transaction::new(
|
||||||
|
@ -301,16 +301,12 @@ fn test_program_builtin_bpf_noop() {
|
||||||
fn test_program_bpf_noop_c() {
|
fn test_program_bpf_noop_c() {
|
||||||
logger::setup();
|
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 loader = Loader::new_dynamic("solana_bpf_loader");
|
||||||
let program = Program::new(
|
let program = Program::new(&loader, &elf);
|
||||||
&loader,
|
|
||||||
elf::File::open_path(&create_bpf_path("noop"))
|
|
||||||
.unwrap()
|
|
||||||
.get_section(PLATFORM_SECTION_C)
|
|
||||||
.unwrap()
|
|
||||||
.data
|
|
||||||
.clone(),
|
|
||||||
);
|
|
||||||
|
|
||||||
// Call user program
|
// Call user program
|
||||||
let tx = Transaction::new(
|
let tx = Transaction::new(
|
||||||
|
|
Loading…
Reference in New Issue