Skip to content

Commit

Permalink
Fix ELF Symbol Parsing Performance (#176)
Browse files Browse the repository at this point in the history
* Cache parsed and hashed syscall names from ELF dynstrtab.

* Keeps hashed syscall names around for the disassembler and the CLI tool.

* Moves set_syscall_registry() as argument in the constructors of the Executable.

* Moves all syscalls into the file src/syscalls.rs

* Configurable enforcement of syscalls to be resolved when an ELF file is loaded.
  • Loading branch information
Lichtso authored Jul 6, 2021
1 parent 608a756 commit 3639ab8
Show file tree
Hide file tree
Showing 23 changed files with 428 additions and 343 deletions.
6 changes: 4 additions & 2 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ include = [
[dependencies]
byteorder = "1.2"
combine = "3.8.1"
goblin = "0.3.0"
goblin = "0.4.2"
hash32 = "0.1.0"
libc = "0.2"
log = "0.4.2"
Expand Down
20 changes: 13 additions & 7 deletions benches/elf_loader.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,20 @@ extern crate test;
extern crate test_utils;

use solana_rbpf::{
syscalls::BpfSyscallU64,
user_error::UserError,
vm::{Config, DefaultInstructionMeter, Executable, SyscallObject, SyscallRegistry},
};
use std::{fs::File, io::Read};
use test::Bencher;
use test_utils::BpfSyscallU64;

fn syscall_registry() -> SyscallRegistry {
let mut syscall_registry = SyscallRegistry::default();
syscall_registry
.register_syscall_by_name::<UserError, _>(b"log_64", BpfSyscallU64::call)
.unwrap();
syscall_registry
}

#[bench]
fn bench_load_elf(bencher: &mut Bencher) {
Expand All @@ -28,6 +36,7 @@ fn bench_load_elf(bencher: &mut Bencher) {
&elf,
None,
Config::default(),
syscall_registry(),
)
.unwrap()
});
Expand All @@ -43,6 +52,7 @@ fn bench_load_elf_without_syscall(bencher: &mut Bencher) {
&elf,
None,
Config::default(),
syscall_registry(),
)
.unwrap();
executable
Expand All @@ -55,17 +65,13 @@ fn bench_load_elf_with_syscall(bencher: &mut Bencher) {
let mut elf = Vec::new();
file.read_to_end(&mut elf).unwrap();
bencher.iter(|| {
let mut executable = <dyn Executable<UserError, DefaultInstructionMeter>>::from_elf(
let executable = <dyn Executable<UserError, DefaultInstructionMeter>>::from_elf(
&elf,
None,
Config::default(),
syscall_registry(),
)
.unwrap();
let mut syscall_registry = SyscallRegistry::default();
syscall_registry
.register_syscall_by_name::<UserError, _>(b"log_64", BpfSyscallU64::call)
.unwrap();
executable.set_syscall_registry(syscall_registry);
executable
});
}
4 changes: 3 additions & 1 deletion benches/jit_compile.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ extern crate test;

use solana_rbpf::{
user_error::UserError,
vm::{Config, DefaultInstructionMeter, EbpfVm, Executable},
vm::{Config, DefaultInstructionMeter, EbpfVm, Executable, SyscallRegistry},
};
use std::{fs::File, io::Read};
use test::Bencher;
Expand All @@ -25,6 +25,7 @@ fn bench_init_vm(bencher: &mut Bencher) {
&elf,
None,
Config::default(),
SyscallRegistry::default(),
)
.unwrap();
bencher.iter(|| {
Expand All @@ -43,6 +44,7 @@ fn bench_jit_compile(bencher: &mut Bencher) {
&elf,
None,
Config::default(),
SyscallRegistry::default(),
)
.unwrap();
bencher.iter(|| executable.jit_compile().unwrap());
Expand Down
17 changes: 11 additions & 6 deletions benches/vm_execution.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ extern crate test;

use solana_rbpf::{
user_error::UserError,
vm::{Config, DefaultInstructionMeter, EbpfVm, Executable},
vm::{Config, DefaultInstructionMeter, EbpfVm, Executable, SyscallRegistry},
};
use std::{fs::File, io::Read};
use test::Bencher;
Expand All @@ -25,6 +25,7 @@ fn bench_init_interpreter_execution(bencher: &mut Bencher) {
&elf,
None,
Config::default(),
SyscallRegistry::default(),
)
.unwrap();
let mut vm =
Expand All @@ -46,6 +47,7 @@ fn bench_init_jit_execution(bencher: &mut Bencher) {
&elf,
None,
Config::default(),
SyscallRegistry::default(),
)
.unwrap();
executable.jit_compile().unwrap();
Expand All @@ -65,11 +67,14 @@ fn bench_jit_vs_interpreter(
instruction_meter: u64,
mem: &mut [u8],
) {
let mut executable = solana_rbpf::assembler::assemble::<
UserError,
test_utils::TestInstructionMeter,
>(assembly, None, Config::default())
.unwrap();
let mut executable =
solana_rbpf::assembler::assemble::<UserError, test_utils::TestInstructionMeter>(
assembly,
None,
Config::default(),
SyscallRegistry::default(),
)
.unwrap();
executable.jit_compile().unwrap();
let mut vm = EbpfVm::new(executable.as_ref(), mem, &[]).unwrap();
let interpreter_summary = bencher
Expand Down
6 changes: 4 additions & 2 deletions cli/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

26 changes: 15 additions & 11 deletions cli/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,13 @@ use solana_rbpf::{
ebpf,
memory_region::{MemoryMapping, MemoryRegion},
static_analysis::Analysis,
syscalls::Result,
user_error::UserError,
verifier::check,
vm::{Config, DynamicAnalysis, EbpfVm, Executable, SyscallObject, SyscallRegistry},
};
use std::{fs::File, io::Read, path::Path};
use test_utils::{Result, TestInstructionMeter};
use test_utils::TestInstructionMeter;

struct MockSyscall {
name: String,
Expand Down Expand Up @@ -122,6 +123,10 @@ fn main() {
} else {
None
};
let syscall_registry = SyscallRegistry::default();
/*for hash in syscalls.keys() {
let _ = syscall_registry.register_syscall_by_hash(*hash, MockSyscall::call);
}*/
let mut executable = match matches.value_of("assembler") {
Some(asm_file_name) => {
let mut file = File::open(&Path::new(asm_file_name)).unwrap();
Expand All @@ -131,24 +136,23 @@ fn main() {
std::str::from_utf8(source.as_slice()).unwrap(),
verifier,
config,
syscall_registry,
)
}
None => {
let mut file = File::open(&Path::new(matches.value_of("elf").unwrap())).unwrap();
let mut elf = Vec::new();
file.read_to_end(&mut elf).unwrap();
Executable::<UserError, TestInstructionMeter>::from_elf(&elf, verifier, config)
.map_err(|err| format!("Executable constructor failed: {:?}", err))
<dyn Executable<UserError, TestInstructionMeter>>::from_elf(
&elf,
verifier,
config,
syscall_registry,
)
.map_err(|err| format!("Executable constructor failed: {:?}", err))
}
}
.unwrap();

let (syscalls, _functions) = executable.get_symbols();
let mut syscall_registry = SyscallRegistry::default();
for hash in syscalls.keys() {
let _ = syscall_registry.register_syscall_by_hash(*hash, MockSyscall::call);
}
executable.set_syscall_registry(syscall_registry);
executable.jit_compile().unwrap();
let analysis = Analysis::from_executable(executable.as_ref());

Expand Down Expand Up @@ -192,7 +196,7 @@ fn main() {
];
let heap_region = MemoryRegion::new_from_slice(&heap, ebpf::MM_HEAP_START, 0, true);
let mut vm = EbpfVm::new(executable.as_ref(), &mut mem, &[heap_region]).unwrap();
for (hash, name) in &analysis.syscalls {
for (hash, name) in analysis.executable.get_syscall_symbols() {
vm.bind_syscall_context_object(Box::new(MockSyscall { name: name.clone() }), Some(*hash))
.unwrap();
}
Expand Down
5 changes: 3 additions & 2 deletions examples/disassemble.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ extern crate solana_rbpf;
use solana_rbpf::{
static_analysis::Analysis,
user_error::UserError,
vm::{Config, DefaultInstructionMeter, Executable},
vm::{Config, DefaultInstructionMeter, Executable, SyscallRegistry},
};
use std::collections::BTreeMap;

Expand All @@ -32,9 +32,10 @@ fn main() {
];
let executable = <dyn Executable<UserError, DefaultInstructionMeter>>::from_text_bytes(
&program,
BTreeMap::new(),
None,
Config::default(),
SyscallRegistry::default(),
BTreeMap::default(),
)
.unwrap();
let analysis = Analysis::from_executable(executable.as_ref());
Expand Down
5 changes: 3 additions & 2 deletions examples/to_json.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ use solana_rbpf::{
disassembler::disassemble_instruction,
static_analysis::Analysis,
user_error::UserError,
vm::{Config, DefaultInstructionMeter, Executable},
vm::{Config, DefaultInstructionMeter, Executable, SyscallRegistry},
};
use std::collections::BTreeMap;
// Turn a program into a JSON string.
Expand All @@ -30,9 +30,10 @@ use std::collections::BTreeMap;
fn to_json(program: &[u8]) -> String {
let executable = <dyn Executable<UserError, DefaultInstructionMeter>>::from_text_bytes(
&program,
BTreeMap::new(),
None,
Config::default(),
SyscallRegistry::default(),
BTreeMap::default(),
)
.unwrap();
let analysis = Analysis::from_executable(executable.as_ref());
Expand Down
20 changes: 11 additions & 9 deletions examples/uptime.rs
Original file line number Diff line number Diff line change
Expand Up @@ -45,9 +45,10 @@ fn main() {
// Create a VM: this one takes no data. Load prog1 in it.
let executable = <dyn Executable<UserError, DefaultInstructionMeter>>::from_text_bytes(
prog1,
BTreeMap::new(),
None,
Config::default(),
SyscallRegistry::default(),
BTreeMap::default(),
)
.unwrap();
let mut vm =
Expand All @@ -67,21 +68,22 @@ fn main() {
// In the following example we use a syscall to get the elapsed time since boot time: we
// reimplement uptime in eBPF, in Rust. Because why not.

let mut executable = <dyn Executable<UserError, DefaultInstructionMeter>>::from_text_bytes(
prog2,
BTreeMap::new(),
None,
Config::default(),
)
.unwrap();
let mut syscall_registry = SyscallRegistry::default();
syscall_registry
.register_syscall_by_hash::<UserError, _>(
syscalls::BPF_KTIME_GETNS_IDX,
syscalls::BpfTimeGetNs::call,
)
.unwrap();
executable.set_syscall_registry(syscall_registry);
#[allow(unused_mut)]
let mut executable = <dyn Executable<UserError, DefaultInstructionMeter>>::from_text_bytes(
prog2,
None,
Config::default(),
syscall_registry,
BTreeMap::default(),
)
.unwrap();
#[cfg(not(windows))]
{
executable.jit_compile().unwrap();
Expand Down
18 changes: 13 additions & 5 deletions src/assembler.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ use crate::{
ebpf::{self, Insn},
elf::register_bpf_function,
error::UserDefinedError,
vm::{Config, Executable, InstructionMeter, Verifier},
vm::{Config, Executable, InstructionMeter, SyscallRegistry, Verifier},
};
use std::collections::{BTreeMap, HashMap};

Expand Down Expand Up @@ -178,7 +178,7 @@ fn insn(opc: u8, dst: i64, src: i64, off: i64, imm: i64) -> Result<Insn, String>
/// # Examples
///
/// ```
/// use solana_rbpf::{assembler::assemble, user_error::UserError, vm::{Config, DefaultInstructionMeter}};
/// use solana_rbpf::{assembler::assemble, user_error::UserError, vm::{Config, DefaultInstructionMeter, SyscallRegistry}};
/// let executable = assemble::<UserError, DefaultInstructionMeter>(
/// "add64 r1, 0x605
/// mov64 r2, 0x32
Expand All @@ -187,7 +187,8 @@ fn insn(opc: u8, dst: i64, src: i64, off: i64, imm: i64) -> Result<Insn, String>
/// neg64 r2
/// exit",
/// None,
/// Config::default()
/// Config::default(),
/// SyscallRegistry::default(),
/// ).unwrap();
/// let program = executable.get_text_bytes().unwrap().1;
/// println!("{:?}", program);
Expand All @@ -214,6 +215,7 @@ pub fn assemble<E: UserDefinedError, I: 'static + InstructionMeter>(
src: &str,
verifier: Option<Verifier>,
config: Config,
syscall_registry: SyscallRegistry,
) -> Result<Box<dyn Executable<E, I>>, String> {
fn resolve_label(
insn_ptr: usize,
Expand Down Expand Up @@ -358,6 +360,12 @@ pub fn assemble<E: UserDefinedError, I: 'static + InstructionMeter>(
.map(|insn| insn.to_vec())
.flatten()
.collect::<Vec<_>>();
<dyn Executable<E, I>>::from_text_bytes(&program, bpf_functions, verifier, config)
.map_err(|err| format!("Executable constructor {:?}", err))
<dyn Executable<E, I>>::from_text_bytes(
&program,
verifier,
config,
syscall_registry,
bpf_functions,
)
.map_err(|err| format!("Executable constructor {:?}", err))
}
2 changes: 1 addition & 1 deletion src/disassembler.rs
Original file line number Diff line number Diff line change
Expand Up @@ -225,7 +225,7 @@ pub fn disassemble_instruction<E: UserDefinedError, I: InstructionMeter>(insn: &
ebpf::JSLE_IMM => { name = "jsle"; desc = jmp_imm_str(name, insn, analysis); },
ebpf::JSLE_REG => { name = "jsle"; desc = jmp_reg_str(name, insn, analysis); },
ebpf::CALL_IMM => {
desc = if let Some(syscall_name) = analysis.syscalls.get(&(insn.imm as u32)) {
desc = if let Some(syscall_name) = analysis.executable.get_syscall_symbols().get(&(insn.imm as u32)) {
name = "syscall";
format!("{} {}", name, syscall_name)
} else {
Expand Down
Loading

0 comments on commit 3639ab8

Please sign in to comment.