ckb_vm_debug_utils/
elf_dumper.rs

1use byteorder::{ByteOrder, LittleEndian};
2use bytes::{BufMut, Bytes, BytesMut};
3use ckb_vm::{
4    Error, Memory, RISCV_PAGES, RISCV_PAGESIZE, Register, SupportMachine, Syscalls,
5    memory::{FLAG_EXECUTABLE, FLAG_WXORX_BIT},
6    registers::A7,
7};
8use std::fs::File;
9use std::io::Write;
10
11pub struct ElfDumper {
12    dump_file_name: String,
13    syscall_number: u64,
14    maximum_zero_gap: u64,
15}
16
17impl Default for ElfDumper {
18    fn default() -> ElfDumper {
19        ElfDumper { dump_file_name: "dump.bin".to_string(), syscall_number: 4097, maximum_zero_gap: 64 }
20    }
21}
22
23impl ElfDumper {
24    pub fn new(dump_file_name: String, syscall_number: u64, maximum_zero_gap: u64) -> Self {
25        ElfDumper { dump_file_name, syscall_number, maximum_zero_gap }
26    }
27}
28
29#[derive(Clone)]
30struct Segment {
31    start: u64,
32    data: Bytes,
33    executable: bool,
34}
35
36impl Segment {
37    fn first_page(&self) -> u64 {
38        self.start / RISCV_PAGESIZE as u64
39    }
40
41    fn first_page_address(&self) -> u64 {
42        self.first_page() * RISCV_PAGESIZE as u64
43    }
44
45    fn last_page(&self) -> u64 {
46        (self.start + self.data.len() as u64 - 1) / RISCV_PAGESIZE as u64
47    }
48}
49
50impl<Mac: SupportMachine> Syscalls<Mac> for ElfDumper {
51    fn initialize(&mut self, _machine: &mut Mac) -> Result<(), Error> {
52        Ok(())
53    }
54
55    fn ecall(&mut self, machine: &mut Mac) -> Result<bool, Error> {
56        if machine.registers()[A7].to_u64() != self.syscall_number {
57            return Ok(false);
58        }
59        let mut segments: Vec<Segment> = vec![];
60        let mut page = 0;
61        // Extract all non-empty data from memory
62        while page < RISCV_PAGES as u64 {
63            let mut start = page * RISCV_PAGESIZE as u64;
64            let end = (page + 1) * RISCV_PAGESIZE as u64;
65
66            while start < end {
67                // First, loop for the start of non-zero values
68                while start < end {
69                    if machine.memory_mut().load64(&Mac::REG::from_u64(start))?.to_u64() != 0 {
70                        break;
71                    }
72                    start += 8;
73                }
74
75                if start < end {
76                    // See if we can append to last segment
77                    let executable = machine.memory_mut().fetch_flag(page)? & FLAG_WXORX_BIT == FLAG_EXECUTABLE;
78                    let (bytes_start, mut bytes_mut) = if segments.is_empty() {
79                        (start, BytesMut::new())
80                    } else {
81                        let last_segment = &segments[segments.len() - 1];
82                        let same_page = page == last_segment.last_page();
83                        let gap = start - (last_segment.start + last_segment.data.len() as u64);
84                        if last_segment.executable == executable && ((gap <= self.maximum_zero_gap) || same_page) {
85                            let Segment { start: segment_start, data: segment_data, .. } =
86                                segments.remove(segments.len() - 1);
87                            let mut segment_data = BytesMut::from(segment_data.as_ref());
88                            // Fill in gap first
89                            let mut zeros = vec![];
90                            zeros.resize(gap as usize, 0);
91                            segment_data.extend_from_slice(&zeros);
92                            (segment_start, segment_data)
93                        } else {
94                            (start, BytesMut::new())
95                        }
96                    };
97
98                    // Append non-zero data
99                    while start < end {
100                        let value = machine.memory_mut().load64(&Mac::REG::from_u64(start))?.to_u64();
101                        if value == 0 {
102                            break;
103                        }
104
105                        bytes_mut.put_u64_le(value);
106                        start += 8;
107                    }
108
109                    segments.push(Segment { start: bytes_start, data: bytes_mut.freeze(), executable });
110                }
111            }
112            page += 1;
113        }
114        // There must be one page at least before the first segment, so we can
115        // allocate code we need.
116        if segments.is_empty() || segments[0].start <= RISCV_PAGESIZE as u64 {
117            return Err(Error::Unexpected("Unexpected segments".into()));
118        }
119
120        // Build instructions that restore register values
121        let mut register_buffer = BytesMut::new();
122        for register_value in &machine.registers()[1..] {
123            register_buffer.put_u64_le(register_value.to_u64());
124        }
125        let register_entrypoint = register_buffer.len() as u64;
126        register_buffer.put_u32_le(0x00000517); // auipc a0, 0x0
127        register_buffer.put_u32_le(0xf0050513); // addi a0, a0, -256
128        register_buffer.put_u32_le(0x00853083); // ld ra,8(a0)
129        register_buffer.put_u32_le(0x01053103); // ld sp,16(a0)
130        register_buffer.put_u32_le(0x01853183); // ld gp,24(a0)
131        register_buffer.put_u32_le(0x02053203); // ld tp,32(a0)
132        register_buffer.put_u32_le(0x02853283); // ld t0,40(a0)
133        register_buffer.put_u32_le(0x03053303); // ld t1,48(a0)
134        register_buffer.put_u32_le(0x03853383); // ld t2,56(a0)
135        register_buffer.put_u32_le(0x04053403); // ld s0,64(a0)
136        register_buffer.put_u32_le(0x04853483); // ld s1,72(a0)
137        register_buffer.put_u32_le(0x05853583); // ld a1,88(a0)
138        register_buffer.put_u32_le(0x06053603); // ld a2,96(a0)
139        register_buffer.put_u32_le(0x06853683); // ld a3,104(a0)
140        register_buffer.put_u32_le(0x07053703); // ld a4,112(a0)
141        register_buffer.put_u32_le(0x07853783); // ld a5,120(a0)
142        register_buffer.put_u32_le(0x08053803); // ld a6,128(a0)
143        register_buffer.put_u32_le(0x08853883); // ld a7,136(a0)
144        register_buffer.put_u32_le(0x09053903); // ld s2,144(a0)
145        register_buffer.put_u32_le(0x09853983); // ld s3,152(a0)
146        register_buffer.put_u32_le(0x0a053a03); // ld s4,160(a0)
147        register_buffer.put_u32_le(0x0a853a83); // ld s5,168(a0)
148        register_buffer.put_u32_le(0x0b053b03); // ld s6,176(a0)
149        register_buffer.put_u32_le(0x0b853b83); // ld s7,184(a0)
150        register_buffer.put_u32_le(0x0c053c03); // ld s8,192(a0)
151        register_buffer.put_u32_le(0x0c853c83); // ld s9,200(a0)
152        register_buffer.put_u32_le(0x0d053d03); // ld s10,208(a0)
153        register_buffer.put_u32_le(0x0d853d83); // ld s11,216(a0)
154        register_buffer.put_u32_le(0x0e053e03); // ld t3,224(a0)
155        register_buffer.put_u32_le(0x0e853e83); // ld t4,232(a0)
156        register_buffer.put_u32_le(0x0f053f03); // ld t5,240(a0)
157        register_buffer.put_u32_le(0x0f853f83); // ld t6,248(a0)
158        register_buffer.put_u32_le(0x05053503); // ld a0,80(a0)
159
160        let register_buffer_start = segments[0].first_page_address() - RISCV_PAGESIZE as u64;
161        let jump_instruction_pc = register_buffer_start + register_buffer.len() as u64;
162        let jump_offset = machine.pc().to_u64() - jump_instruction_pc;
163        let masked = jump_offset & 0xFFFFFFFFFFE00001;
164        if masked != 0 && masked != 0xFFFFFFFFFFE00000 {
165            return Err(Error::Unexpected("Unexpected masked".into()));
166        }
167        let jump_instruction = 0b1101111
168            | ((((jump_offset >> 12) & 0b_1111_1111) as u32) << 12)
169            | ((((jump_offset >> 11) & 1) as u32) << 20)
170            | ((((jump_offset >> 1) & 0b_1111_1111_11) as u32) << 21)
171            | ((((jump_offset >> 20) & 1) as u32) << 31);
172        register_buffer.put_u32_le(jump_instruction);
173        assert!(register_buffer.len() < RISCV_PAGESIZE);
174
175        segments.push(Segment { start: register_buffer_start, data: register_buffer.freeze(), executable: true });
176
177        // Piece everything together into an ELF binary
178        let mut elf = BytesMut::new();
179        // ELF Magic
180        elf.extend_from_slice(&[
181            0x7f, 0x45, 0x4c, 0x46, 0x02, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
182        ]);
183        // ELF Type
184        elf.put_u16_le(2);
185        // ELF Machine; RISC-V
186        elf.put_u16_le(243);
187        // ELF Version
188        elf.put_u32_le(1);
189        // ELF Entry
190        elf.put_u64_le(register_buffer_start + register_entrypoint);
191        let program_header_offset = elf.len();
192        // Program Header Offset, placeholder for now
193        elf.put_u64_le(0);
194        let section_header_offset = elf.len();
195        // Section Header Offset, placeholder for now
196        elf.put_u64_le(0);
197        // ELF Flags: 0x1, RVC, soft-float ABI
198        elf.put_u32_le(1);
199        // ELF Header Size: 64
200        elf.put_u16_le(64);
201        // Program Header Entry Size: 56
202        elf.put_u16_le(56);
203        let program_header_number_offset = elf.len();
204        // Program Header Number, placeholder for now
205        elf.put_u16_le(0);
206        // Section Header Entry Size: 64
207        elf.put_u16_le(64);
208        let section_header_number_offset = elf.len();
209        // Section Header Number, placeholder for now
210        elf.put_u16_le(0);
211        // Section header string table index: 0
212        elf.put_u16_le(0);
213        assert!(elf.len() == 64);
214
215        let string_table_offset = elf.len() as u64;
216        elf.put_u32_le(0);
217
218        let mut section_headers = vec![];
219        let mut string_table_section_header = BytesMut::new();
220        // Name
221        string_table_section_header.put_u32_le(0);
222        // Type: STRTAB
223        string_table_section_header.put_u32_le(3);
224        // Flags
225        string_table_section_header.put_u64_le(0);
226        // Address
227        string_table_section_header.put_u64_le(0);
228        // Offset
229        string_table_section_header.put_u64_le(string_table_offset);
230        // Size
231        string_table_section_header.put_u64_le(4);
232        // Link
233        string_table_section_header.put_u32_le(0);
234        // Info
235        string_table_section_header.put_u32_le(0);
236        // Align
237        string_table_section_header.put_u64_le(1);
238        // Entry size
239        string_table_section_header.put_u64_le(0);
240        assert!(string_table_section_header.len() == 64);
241        section_headers.push(string_table_section_header.freeze());
242
243        let mut program_headers = vec![];
244
245        for segment in segments {
246            let current_offset = elf.len() as u64;
247            elf.extend_from_slice(segment.data.as_ref());
248
249            let mut program_header = BytesMut::new();
250            // Type: LOAD
251            program_header.put_u32_le(1);
252            // Flags
253            program_header.put_u32_le(if segment.executable { 5 } else { 6 });
254            // Offset
255            program_header.put_u64_le(current_offset);
256            // Vaddr
257            program_header.put_u64_le(segment.start);
258            // Paddr
259            program_header.put_u64_le(segment.start);
260            // File size
261            program_header.put_u64_le(segment.data.len() as u64);
262            // Memory size
263            program_header.put_u64_le(segment.data.len() as u64);
264            // Align
265            program_header.put_u64_le(0x1000);
266            assert!(program_header.len() == 56);
267            program_headers.push(program_header.freeze());
268
269            // TODO: add an option to provide a binary, and use the binary's
270            // section headers to replace the inferred ones here. Since inferred
271            // sections here can contain non-code data section as well.
272            if segment.executable {
273                let mut section_header = BytesMut::new();
274                // Name
275                section_header.put_u32_le(0);
276                // Type: PROGBITS
277                section_header.put_u32_le(1);
278                // Flags: AX
279                section_header.put_u64_le(6);
280                // Address
281                section_header.put_u64_le(segment.start);
282                // Offset
283                section_header.put_u64_le(current_offset);
284                // Size
285                section_header.put_u64_le(segment.data.len() as u64);
286                // Link
287                section_header.put_u32_le(0);
288                // Info
289                section_header.put_u32_le(0);
290                // Align
291                section_header.put_u64_le(2);
292                // Entry size
293                section_header.put_u64_le(0);
294                assert!(section_header.len() == 64);
295                section_headers.push(section_header.freeze());
296            }
297        }
298
299        while elf.len() % 4 != 0 {
300            elf.put_u8(0);
301        }
302        let current_offset = elf.len() as u64;
303        LittleEndian::write_u64(&mut elf[program_header_offset..program_header_offset + 8], current_offset);
304        LittleEndian::write_u16(
305            &mut elf[program_header_number_offset..program_header_number_offset + 8],
306            program_headers.len() as u16,
307        );
308        for program_header in program_headers {
309            elf.extend_from_slice(program_header.as_ref());
310        }
311
312        while elf.len() % 4 != 0 {
313            elf.put_u8(0);
314        }
315        let current_offset = elf.len() as u64;
316        LittleEndian::write_u64(&mut elf[section_header_offset..section_header_offset + 8], current_offset);
317        LittleEndian::write_u16(
318            &mut elf[section_header_number_offset..section_header_number_offset + 8],
319            section_headers.len() as u16,
320        );
321        for section_header in section_headers {
322            elf.extend_from_slice(section_header.as_ref());
323        }
324
325        let mut file = File::create(&self.dump_file_name)?;
326        file.write_all(&elf)?;
327
328        Ok(true)
329    }
330}