use byteorder::{ByteOrder, LittleEndian};
use bytes::{BufMut, Bytes, BytesMut};
use ckb_vm::{
memory::{FLAG_EXECUTABLE, FLAG_WXORX_BIT},
registers::A7,
Error, Memory, Register, SupportMachine, Syscalls, RISCV_PAGES, RISCV_PAGESIZE,
};
use std::fs::File;
use std::io::Write;
pub struct ElfDumper {
dump_file_name: String,
syscall_number: u64,
maximum_zero_gap: u64,
}
impl Default for ElfDumper {
fn default() -> ElfDumper {
ElfDumper { dump_file_name: "dump.bin".to_string(), syscall_number: 4097, maximum_zero_gap: 64 }
}
}
impl ElfDumper {
pub fn new(dump_file_name: String, syscall_number: u64, maximum_zero_gap: u64) -> Self {
ElfDumper { dump_file_name, syscall_number, maximum_zero_gap }
}
}
#[derive(Clone)]
struct Segment {
start: u64,
data: Bytes,
executable: bool,
}
impl Segment {
fn first_page(&self) -> u64 {
self.start / RISCV_PAGESIZE as u64
}
fn first_page_address(&self) -> u64 {
self.first_page() * RISCV_PAGESIZE as u64
}
fn last_page(&self) -> u64 {
(self.start + self.data.len() as u64 - 1) / RISCV_PAGESIZE as u64
}
}
impl<Mac: SupportMachine> Syscalls<Mac> for ElfDumper {
fn initialize(&mut self, _machine: &mut Mac) -> Result<(), Error> {
Ok(())
}
fn ecall(&mut self, machine: &mut Mac) -> Result<bool, Error> {
if machine.registers()[A7].to_u64() != self.syscall_number {
return Ok(false);
}
let mut segments: Vec<Segment> = vec![];
let mut page = 0;
while page < RISCV_PAGES as u64 {
let mut start = page * RISCV_PAGESIZE as u64;
let end = (page + 1) * RISCV_PAGESIZE as u64;
while start < end {
while start < end {
if machine.memory_mut().load64(&Mac::REG::from_u64(start))?.to_u64() != 0 {
break;
}
start += 8;
}
if start < end {
let executable = machine.memory_mut().fetch_flag(page)? & FLAG_WXORX_BIT == FLAG_EXECUTABLE;
let (bytes_start, mut bytes_mut) = if segments.is_empty() {
(start, BytesMut::new())
} else {
let last_segment = &segments[segments.len() - 1];
let same_page = page == last_segment.last_page();
let gap = start - (last_segment.start + last_segment.data.len() as u64);
if last_segment.executable == executable && ((gap <= self.maximum_zero_gap) || same_page) {
let Segment { start: segment_start, data: segment_data, .. } =
segments.remove(segments.len() - 1);
let mut segment_data = BytesMut::from(segment_data.as_ref());
let mut zeros = vec![];
zeros.resize(gap as usize, 0);
segment_data.extend_from_slice(&zeros);
(segment_start, segment_data)
} else {
(start, BytesMut::new())
}
};
while start < end {
let value = machine.memory_mut().load64(&Mac::REG::from_u64(start))?.to_u64();
if value == 0 {
break;
}
bytes_mut.put_u64_le(value);
start += 8;
}
segments.push(Segment { start: bytes_start, data: bytes_mut.freeze(), executable });
}
}
page += 1;
}
if segments.is_empty() || segments[0].start <= RISCV_PAGESIZE as u64 {
return Err(Error::Unexpected("Unexpected segments".into()));
}
let mut register_buffer = BytesMut::new();
for register_value in &machine.registers()[1..] {
register_buffer.put_u64_le(register_value.to_u64());
}
let register_entrypoint = register_buffer.len() as u64;
register_buffer.put_u32_le(0x00000517); register_buffer.put_u32_le(0xf0050513); register_buffer.put_u32_le(0x00853083); register_buffer.put_u32_le(0x01053103); register_buffer.put_u32_le(0x01853183); register_buffer.put_u32_le(0x02053203); register_buffer.put_u32_le(0x02853283); register_buffer.put_u32_le(0x03053303); register_buffer.put_u32_le(0x03853383); register_buffer.put_u32_le(0x04053403); register_buffer.put_u32_le(0x04853483); register_buffer.put_u32_le(0x05853583); register_buffer.put_u32_le(0x06053603); register_buffer.put_u32_le(0x06853683); register_buffer.put_u32_le(0x07053703); register_buffer.put_u32_le(0x07853783); register_buffer.put_u32_le(0x08053803); register_buffer.put_u32_le(0x08853883); register_buffer.put_u32_le(0x09053903); register_buffer.put_u32_le(0x09853983); register_buffer.put_u32_le(0x0a053a03); register_buffer.put_u32_le(0x0a853a83); register_buffer.put_u32_le(0x0b053b03); register_buffer.put_u32_le(0x0b853b83); register_buffer.put_u32_le(0x0c053c03); register_buffer.put_u32_le(0x0c853c83); register_buffer.put_u32_le(0x0d053d03); register_buffer.put_u32_le(0x0d853d83); register_buffer.put_u32_le(0x0e053e03); register_buffer.put_u32_le(0x0e853e83); register_buffer.put_u32_le(0x0f053f03); register_buffer.put_u32_le(0x0f853f83); register_buffer.put_u32_le(0x05053503); let register_buffer_start = segments[0].first_page_address() - RISCV_PAGESIZE as u64;
let jump_instruction_pc = register_buffer_start + register_buffer.len() as u64;
let jump_offset = machine.pc().to_u64() - jump_instruction_pc;
let masked = jump_offset & 0xFFFFFFFFFFE00001;
if masked != 0 && masked != 0xFFFFFFFFFFE00000 {
return Err(Error::Unexpected("Unexpected masked".into()));
}
let jump_instruction = 0b1101111
| ((((jump_offset >> 12) & 0b_1111_1111) as u32) << 12)
| ((((jump_offset >> 11) & 1) as u32) << 20)
| ((((jump_offset >> 1) & 0b_1111_1111_11) as u32) << 21)
| ((((jump_offset >> 20) & 1) as u32) << 31);
register_buffer.put_u32_le(jump_instruction);
assert!(register_buffer.len() < RISCV_PAGESIZE);
segments.push(Segment { start: register_buffer_start, data: register_buffer.freeze(), executable: true });
let mut elf = BytesMut::new();
elf.extend_from_slice(&[
0x7f, 0x45, 0x4c, 0x46, 0x02, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
]);
elf.put_u16_le(2);
elf.put_u16_le(243);
elf.put_u32_le(1);
elf.put_u64_le(register_buffer_start + register_entrypoint);
let program_header_offset = elf.len();
elf.put_u64_le(0);
let section_header_offset = elf.len();
elf.put_u64_le(0);
elf.put_u32_le(1);
elf.put_u16_le(64);
elf.put_u16_le(56);
let program_header_number_offset = elf.len();
elf.put_u16_le(0);
elf.put_u16_le(64);
let section_header_number_offset = elf.len();
elf.put_u16_le(0);
elf.put_u16_le(0);
assert!(elf.len() == 64);
let string_table_offset = elf.len() as u64;
elf.put_u32_le(0);
let mut section_headers = vec![];
let mut string_table_section_header = BytesMut::new();
string_table_section_header.put_u32_le(0);
string_table_section_header.put_u32_le(3);
string_table_section_header.put_u64_le(0);
string_table_section_header.put_u64_le(0);
string_table_section_header.put_u64_le(string_table_offset);
string_table_section_header.put_u64_le(4);
string_table_section_header.put_u32_le(0);
string_table_section_header.put_u32_le(0);
string_table_section_header.put_u64_le(1);
string_table_section_header.put_u64_le(0);
assert!(string_table_section_header.len() == 64);
section_headers.push(string_table_section_header.freeze());
let mut program_headers = vec![];
for segment in segments {
let current_offset = elf.len() as u64;
elf.extend_from_slice(segment.data.as_ref());
let mut program_header = BytesMut::new();
program_header.put_u32_le(1);
program_header.put_u32_le(if segment.executable { 5 } else { 6 });
program_header.put_u64_le(current_offset);
program_header.put_u64_le(segment.start);
program_header.put_u64_le(segment.start);
program_header.put_u64_le(segment.data.len() as u64);
program_header.put_u64_le(segment.data.len() as u64);
program_header.put_u64_le(0x1000);
assert!(program_header.len() == 56);
program_headers.push(program_header.freeze());
if segment.executable {
let mut section_header = BytesMut::new();
section_header.put_u32_le(0);
section_header.put_u32_le(1);
section_header.put_u64_le(6);
section_header.put_u64_le(segment.start);
section_header.put_u64_le(current_offset);
section_header.put_u64_le(segment.data.len() as u64);
section_header.put_u32_le(0);
section_header.put_u32_le(0);
section_header.put_u64_le(2);
section_header.put_u64_le(0);
assert!(section_header.len() == 64);
section_headers.push(section_header.freeze());
}
}
while elf.len() % 4 != 0 {
elf.put_u8(0);
}
let current_offset = elf.len() as u64;
LittleEndian::write_u64(&mut elf[program_header_offset..program_header_offset + 8], current_offset);
LittleEndian::write_u16(
&mut elf[program_header_number_offset..program_header_number_offset + 8],
program_headers.len() as u16,
);
for program_header in program_headers {
elf.extend_from_slice(program_header.as_ref());
}
while elf.len() % 4 != 0 {
elf.put_u8(0);
}
let current_offset = elf.len() as u64;
LittleEndian::write_u64(&mut elf[section_header_offset..section_header_offset + 8], current_offset);
LittleEndian::write_u16(
&mut elf[section_header_number_offset..section_header_number_offset + 8],
section_headers.len() as u16,
);
for section_header in section_headers {
elf.extend_from_slice(section_header.as_ref());
}
let mut file = File::create(&self.dump_file_name)?;
file.write_all(&elf)?;
Ok(true)
}
}