opentitanlib/debug/
elf_debugger.rs

1// Copyright lowRISC contributors (OpenTitan project).
2// Licensed under the Apache License, Version 2.0, see LICENSE for details.
3// SPDX-License-Identifier: Apache-2.0
4
5use std::collections::HashMap;
6use std::fmt::{Debug, Display};
7use std::fs;
8use std::ops::{Deref, DerefMut, Range};
9use std::path::Path;
10use std::time::Duration;
11
12use anyhow::Result;
13use object::{Object, ObjectSymbol};
14
15use crate::io::jtag::{Jtag, RiscvCsr, RiscvGpr, RiscvReg};
16
17pub struct ElfSymbols {
18    symbols: HashMap<String, u32>,
19}
20
21pub struct ElfDebugger<'a> {
22    symbols: &'a ElfSymbols,
23    jtag: Box<dyn Jtag + 'a>,
24}
25
26impl<'a> Deref for ElfDebugger<'a> {
27    type Target = dyn Jtag + 'a;
28
29    #[inline]
30    fn deref(&self) -> &Self::Target {
31        &*self.jtag
32    }
33}
34
35impl<'a> DerefMut for ElfDebugger<'a> {
36    #[inline]
37    fn deref_mut(&mut self) -> &mut Self::Target {
38        &mut *self.jtag
39    }
40}
41
42#[derive(Clone)]
43pub enum SymbolicAddress {
44    Absolute(u32),
45    SymbolRelative(String, u32),
46}
47
48impl From<String> for SymbolicAddress {
49    #[inline]
50    fn from(s: String) -> Self {
51        Self::SymbolRelative(s, 0)
52    }
53}
54
55impl From<&str> for SymbolicAddress {
56    #[inline]
57    fn from(s: &str) -> Self {
58        Self::SymbolRelative(s.to_owned(), 0)
59    }
60}
61
62impl From<u32> for SymbolicAddress {
63    #[inline]
64    fn from(s: u32) -> Self {
65        Self::Absolute(s)
66    }
67}
68
69impl std::ops::Add<u32> for SymbolicAddress {
70    type Output = Self;
71
72    #[inline]
73    fn add(self, rhs: u32) -> Self::Output {
74        match self {
75            SymbolicAddress::Absolute(addr) => SymbolicAddress::Absolute(addr + rhs),
76            SymbolicAddress::SymbolRelative(symbol, offset) => {
77                SymbolicAddress::SymbolRelative(symbol, offset + rhs)
78            }
79        }
80    }
81}
82
83impl Debug for SymbolicAddress {
84    #[inline]
85    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
86        Display::fmt(self, f)
87    }
88}
89
90impl Display for SymbolicAddress {
91    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
92        match self {
93            SymbolicAddress::Absolute(addr) => write!(f, "{addr:#x}"),
94            SymbolicAddress::SymbolRelative(symbol, offset) => {
95                if *offset == 0 {
96                    write!(f, "{symbol}")
97                } else {
98                    write!(f, "{symbol} + {offset:#x}")
99                }
100            }
101        }
102    }
103}
104
105#[derive(Clone)]
106pub struct ResolvedAddress {
107    pub address: SymbolicAddress,
108    pub resolution: u32,
109}
110
111impl Debug for ResolvedAddress {
112    #[inline]
113    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
114        Display::fmt(self, f)
115    }
116}
117
118impl Display for ResolvedAddress {
119    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
120        Display::fmt(&self.address, f)?;
121
122        // Only output the resolved address if it's not already absolute.
123        if !matches!(self.address, SymbolicAddress::Absolute(_)) {
124            write!(f, " ({:#x})", self.resolution)?;
125        }
126
127        Ok(())
128    }
129}
130
131impl ElfSymbols {
132    pub fn load_elf(path: impl AsRef<Path>) -> Result<Self> {
133        let elf_binary = fs::read(path)?;
134        let elf_file = object::File::parse(&*elf_binary)?;
135        let mut symbols = HashMap::new();
136        for sym in elf_file.symbols() {
137            symbols.insert(sym.name()?.to_owned(), sym.address() as u32);
138        }
139        Ok(Self { symbols })
140    }
141
142    /// Resolve a symbolic address.
143    pub fn resolve(&self, address: impl Into<SymbolicAddress>) -> Result<ResolvedAddress> {
144        let address = address.into();
145        let resolution = match address {
146            SymbolicAddress::Absolute(addr) => addr,
147            SymbolicAddress::SymbolRelative(ref symbol, offset) => {
148                let Some(symbol_addr) = self.symbols.get(symbol).copied() else {
149                    anyhow::bail!("Cannot resolve symbol {symbol}");
150                };
151                symbol_addr + offset
152            }
153        };
154        Ok(ResolvedAddress {
155            address,
156            resolution,
157        })
158    }
159
160    /// Attach to a JTAG interface for debugging.
161    pub fn attach<'a>(&'a self, jtag: Box<dyn Jtag + 'a>) -> ElfDebugger<'a> {
162        ElfDebugger {
163            symbols: self,
164            jtag,
165        }
166    }
167}
168
169impl<'a> ElfDebugger<'a> {
170    pub fn disconnect(self) -> Result<()> {
171        self.jtag.disconnect()
172    }
173
174    pub fn resolve(&self, address: impl Into<SymbolicAddress>) -> Result<ResolvedAddress> {
175        self.symbols.resolve(address)
176    }
177
178    /// Read a RISC-V register.
179    ///
180    /// This is a convience wrapper around `read_riscv_reg` provided by the JTAG trait.
181    pub fn read_reg(&mut self, reg: impl Into<RiscvReg>) -> Result<u32> {
182        self.read_riscv_reg(&reg.into())
183    }
184
185    /// Write a RISC-V register.
186    ///
187    /// This is a convience wrapper around `write_riscv_reg` provided by the JTAG trait.
188    pub fn write_reg(&mut self, reg: impl Into<RiscvReg>, value: u32) -> Result<()> {
189        self.write_riscv_reg(&reg.into(), value)
190    }
191
192    /// Read a 32-bit word from memory.
193    ///
194    /// This is a convience wrapper around `read_memory32` provided by the JTAG trait.
195    pub fn read_u32(&mut self, addr: u32) -> Result<u32> {
196        let mut ret = [0];
197        self.read_memory32(addr, &mut ret)?;
198        Ok(ret[0])
199    }
200
201    /// Write a 32-bit word to memory.
202    ///
203    /// This is a convience wrapper around `write_memory32` provided by the JTAG trait.
204    pub fn write_u32(&mut self, addr: u32, value: u32) -> Result<()> {
205        self.write_memory32(addr, &[value])
206    }
207
208    /// Read the program counter.
209    pub fn get_pc(&mut self) -> Result<u32> {
210        self.read_riscv_reg(&RiscvReg::Csr(RiscvCsr::DPC))
211    }
212
213    /// Set the program counter to the given address.
214    pub fn set_pc(&mut self, address: impl Into<SymbolicAddress>) -> Result<()> {
215        let resolved = self.resolve(address)?;
216        log::info!("Set PC to {}", resolved);
217        self.write_reg(RiscvCsr::DPC, resolved.resolution)?;
218        Ok(())
219    }
220
221    /// Set a breakpoint at the given address.
222    pub fn set_breakpoint(&mut self, address: impl Into<SymbolicAddress>) -> Result<()> {
223        let resolved = self.resolve(address)?;
224        log::info!("Set breakpoint at {}", resolved);
225        self.jtag.set_breakpoint(resolved.resolution, true)?;
226        Ok(())
227    }
228
229    /// Assert the current program counter is at the given address.
230    pub fn expect_pc(&mut self, address: impl Into<SymbolicAddress>) -> Result<()> {
231        let resolved = self.resolve(address)?;
232        let pc = self.get_pc()?;
233        log::info!("PC = {:#x}, expected PC = {}", pc, resolved);
234        if pc != resolved.resolution {
235            anyhow::bail!("unexpected PC");
236        }
237        Ok(())
238    }
239
240    /// Assert the current program counter is within the given range.
241    pub fn expect_pc_range(&mut self, range: Range<impl Into<SymbolicAddress>>) -> Result<()> {
242        let start = self.resolve(range.start)?;
243        let end = self.resolve(range.end)?;
244        let pc = self.get_pc()?;
245        log::info!("PC = {:#x}, expected PC = {}..{}", pc, start, end,);
246        if !(start.resolution..end.resolution).contains(&pc) {
247            anyhow::bail!("unexpected PC");
248        }
249        Ok(())
250    }
251
252    /// Continue execution until the address is hit.
253    ///
254    /// Note that a breakpoint must not already exist for the target address, otherwise
255    /// this function will fail.
256    ///
257    /// This function will also fail if a pre-existing breakpoint is hit before the target
258    /// address is hit.
259    pub fn run_until(
260        &mut self,
261        address: impl Into<SymbolicAddress>,
262        timeout: Duration,
263    ) -> Result<()> {
264        let resolved = self.resolve(address)?;
265        log::info!("Run until {}", resolved);
266        self.jtag.set_breakpoint(resolved.resolution, true)?;
267        self.jtag.resume()?;
268        self.jtag.wait_halt(timeout)?;
269        self.expect_pc(resolved.resolution)?;
270        self.jtag.remove_breakpoint(resolved.resolution)?;
271        Ok(())
272    }
273
274    /// Execute until the current function returns.
275    ///
276    /// This implementation does not use the debugging information from ELF files, and only uses the RA register,
277    /// so it only works when RA has not been overriden, e.g. at the preamble of the function.
278    pub fn finish(&mut self, timeout: Duration) -> Result<()> {
279        let ra = self.read_reg(RiscvGpr::RA)?;
280        self.run_until(ra, timeout)
281    }
282
283    /// Call a function with the given arguments.
284    pub fn call(
285        &mut self,
286        address: impl Into<SymbolicAddress>,
287        args: &[u32],
288        timeout: Duration,
289    ) -> Result<(u32, u32)> {
290        // We're oblivious to the current context, so save all caller-saved registers.
291        // In addition, also save SP, just in case that we have to modify it to align.
292        const REGS_TO_SAVE: &[RiscvGpr] = &[
293            RiscvGpr::RA,
294            RiscvGpr::SP,
295            RiscvGpr::T0,
296            RiscvGpr::T1,
297            RiscvGpr::T2,
298            RiscvGpr::A0,
299            RiscvGpr::A1,
300            RiscvGpr::A2,
301            RiscvGpr::A3,
302            RiscvGpr::A4,
303            RiscvGpr::A5,
304            RiscvGpr::A6,
305            RiscvGpr::A7,
306            RiscvGpr::T3,
307            RiscvGpr::T4,
308            RiscvGpr::T5,
309            RiscvGpr::T6,
310        ];
311        let mut saved = [0; REGS_TO_SAVE.len()];
312        for (idx, gpr) in REGS_TO_SAVE.iter().copied().enumerate() {
313            saved[idx] = self.read_reg(gpr)?;
314        }
315
316        // Align the stack if it's not currently 16-byte aligned.
317        // This rounds down to avoid overwriting anything on stack.
318        if saved[1] % 16 != 0 {
319            self.write_reg(RiscvGpr::SP, saved[1] & !15)?;
320        }
321
322        // Set up arguments
323        const ARG_GPRS: &[RiscvGpr] = &[
324            RiscvGpr::A0,
325            RiscvGpr::A1,
326            RiscvGpr::A2,
327            RiscvGpr::A3,
328            RiscvGpr::A4,
329            RiscvGpr::A5,
330            RiscvGpr::A6,
331        ];
332        assert!(args.len() < ARG_GPRS.len());
333        for (gpr, arg) in ARG_GPRS.iter().copied().zip(args.iter().copied()) {
334            self.write_reg(gpr, arg)?;
335        }
336
337        // Set up call frame, and run until the current PC.
338        // NOTE: This wouldn't guard against recursive function calls;
339        // to support that we also need to check that SP is expected, but
340        // this should be enough for all our current tests.
341        let pc = self.get_pc()?;
342        self.write_reg(RiscvGpr::RA, pc)?;
343        self.set_pc(address)?;
344        self.run_until(pc, timeout)?;
345
346        // A0 and A1 serve as return value, read them.
347        let a0 = self.read_reg(RiscvGpr::A0)?;
348        let a1 = self.read_reg(RiscvGpr::A1)?;
349
350        // Restore all registers
351        for (idx, gpr) in REGS_TO_SAVE.iter().copied().enumerate() {
352            self.write_reg(gpr, saved[idx])?;
353        }
354
355        Ok((a0, a1))
356    }
357}