1use {
2 crate::{
3 debugger::Debugger,
4 error::{DebuggerError, DebuggerResult},
5 execution_cost::ExecutionCost,
6 parser::{LineMap, rodata_from_section},
7 syscalls::DebuggerSyscallHandler,
8 },
9 either::Either,
10 sbpf_assembler::{Assembler, AssemblerOption, DebugMode, SbpfArch},
11 sbpf_common::{inst_param::Number, opcode::Opcode},
12 sbpf_disassembler::program::Program,
13 sbpf_vm::{
14 compute::ComputeMeter,
15 memory::Memory,
16 vm::{SbpfVm, SbpfVmConfig},
17 },
18 solana_address::Address,
19 std::{
20 fs::File,
21 io::Read,
22 path::{Path, PathBuf},
23 },
24};
25
26pub struct DebuggerSession {
27 pub debugger: Debugger<DebuggerSyscallHandler>,
28 pub line_map: Option<LineMap>,
29 pub elf_bytes: Vec<u8>,
30 pub elf_path: PathBuf,
31}
32
33impl DebuggerSession {
34 pub fn build_vm(
35 instructions: Vec<sbpf_common::instruction::Instruction>,
36 input: Vec<u8>,
37 rodata_bytes: Vec<u8>,
38 config: SbpfVmConfig,
39 program_id: Address,
40 ) -> SbpfVm<DebuggerSyscallHandler> {
41 let compute_meter = ComputeMeter::new(config.compute_unit_limit);
42 let handler = DebuggerSyscallHandler::new(ExecutionCost::default(), program_id);
43
44 let mut vm = SbpfVm::new_with_config(instructions, input, rodata_bytes, handler, config);
45 vm.compute_meter = compute_meter;
46 vm
47 }
48}
49
50pub fn load_session_from_asm(
51 asm_path: &str,
52 input: Vec<u8>,
53 config: SbpfVmConfig,
54 program_id: Address,
55) -> DebuggerResult<DebuggerSession> {
56 let asm_path = Path::new(asm_path);
57 if !asm_path.exists() {
58 return Err(DebuggerError::InvalidInput(format!(
59 "Assembly file not found: {}",
60 asm_path.display()
61 )));
62 }
63
64 let source_code = std::fs::read_to_string(asm_path)?;
65 let filename = asm_path
66 .file_name()
67 .and_then(|n| n.to_str())
68 .unwrap_or("unknown.s")
69 .to_string();
70 let directory = asm_path
71 .parent()
72 .and_then(|p| p.canonicalize().ok())
73 .map(|p| p.to_string_lossy().to_string())
74 .unwrap_or_else(|| ".".to_string());
75
76 let options = AssemblerOption {
77 arch: SbpfArch::V0,
78 debug_mode: Some(DebugMode {
79 filename,
80 directory,
81 }),
82 };
83 let assembler = Assembler::new(options);
84 let bytecode = assembler
85 .assemble(&source_code)
86 .map_err(|errors| DebuggerError::Assembler(format!("{:?}", errors)))?;
87
88 load_session_from_bytes(bytecode, input, config, None, program_id)
89}
90
91pub fn load_session_from_elf(
92 elf_path: &str,
93 input: Vec<u8>,
94 config: SbpfVmConfig,
95 program_id: Address,
96) -> DebuggerResult<DebuggerSession> {
97 let mut file = File::open(elf_path)?;
98 let mut elf_bytes = Vec::new();
99 file.read_to_end(&mut elf_bytes)?;
100 load_session_from_bytes(elf_bytes, input, config, Some(elf_path.into()), program_id)
101}
102
103pub fn load_session_from_bytes(
104 elf_bytes: Vec<u8>,
105 input: Vec<u8>,
106 config: SbpfVmConfig,
107 elf_path: Option<PathBuf>,
108 program_id: Address,
109) -> DebuggerResult<DebuggerSession> {
110 let program = Program::from_bytes(&elf_bytes)?;
111 let entrypoint = program.get_entrypoint_offset().unwrap_or(0);
112 let (mut instructions, rodata_section) = program.to_ixs()?;
113 let rodata_bytes = rodata_section
114 .as_ref()
115 .map(|section| section.data.clone())
116 .unwrap_or_default();
117
118 if let Some(ref section) = rodata_section {
120 let elf_rodata_base = section.base_address;
121 let elf_rodata_end = elf_rodata_base + section.data.len() as u64;
122
123 for ix in &mut instructions {
124 if ix.opcode == Opcode::Lddw
125 && let Some(Either::Right(Number::Int(imm))) = &ix.imm
126 {
127 let addr = *imm as u64;
128 if addr >= elf_rodata_base && addr < elf_rodata_end {
129 let offset = addr - elf_rodata_base;
130 let vm_addr = Memory::RODATA_START + offset;
131 ix.imm = Some(Either::Right(Number::Int(vm_addr as i64)));
132 }
133 }
134 }
135 }
136
137 let mut vm = DebuggerSession::build_vm(instructions, input, rodata_bytes, config, program_id);
138 vm.set_entrypoint(entrypoint as usize);
139
140 let mut debugger = Debugger::new(vm);
141 if let Ok(line_map) = LineMap::from_elf_data(&elf_bytes) {
142 debugger.set_dwarf_line_map(line_map.clone());
143 }
144
145 if let Some(ref section) = rodata_section {
147 let mut rodata_symbols = rodata_from_section(section);
148 if let Some(ref line_map) = debugger.dwarf_line_map {
150 let text_offset = line_map.get_text_offset();
151 for sym in &mut rodata_symbols {
152 let rodata_offset = sym.address - Memory::RODATA_START;
153 let addr = rodata_offset + text_offset;
154 if let Some(name) = line_map.get_label_for_address(addr) {
155 sym.name = name.to_string();
156 }
157 }
158 }
159 if !rodata_symbols.is_empty() {
160 debugger.set_rodata(rodata_symbols);
161 }
162 }
163
164 Ok(DebuggerSession {
165 line_map: debugger.dwarf_line_map.clone(),
166 debugger,
167 elf_bytes,
168 elf_path: elf_path.unwrap_or_else(|| PathBuf::from("<memory>")),
169 })
170}