1use {
2 sbpf_common::errors::SBPFError,
3 std::{ops::Range, string::FromUtf8Error},
4 thiserror::Error,
5};
6
7fn hex_list(values: &[u64]) -> String {
9 values
10 .iter()
11 .map(|v| format!("{v:#x}"))
12 .collect::<Vec<_>>()
13 .join(" or ")
14}
15
16#[derive(Debug, Error, Clone)]
17pub enum DisassemblerError {
18 #[error("Failed to parse ELF file: {source}; first bytes: {first_bytes:02x?}")]
19 InvalidElfFile {
20 first_bytes: Vec<u8>,
21 #[source]
22 source: object::read::Error,
23 },
24 #[error("Non-standard ELF header: {field} is {found:#x}, expected {}", hex_list(.expected))]
25 NonStandardElfHeader {
26 field: &'static str,
27 expected: Vec<u64>,
28 found: u64,
29 },
30 #[error("Invalid Program Type: {0:#x}")]
31 InvalidProgramType(u32),
32 #[error("Invalid Section Header Type: {0:#x}")]
33 InvalidSectionHeaderType(u32),
34 #[error("Invalid Relocation Type: {0:#x}")]
35 InvalidRelocationType(u32),
36 #[error("Failed to read {section} section data: {source}")]
37 SectionDataError {
38 section: &'static str,
39 #[source]
40 source: object::read::Error,
41 },
42 #[error("Invalid data length: {0}")]
43 InvalidDataLength(usize),
44 #[error("Bytecode error at bytes {span:?}: {error}")]
45 BytecodeError { error: String, span: Range<usize> },
46 #[error("Missing text section; sections present: {sections:?}")]
47 MissingTextSection { sections: Vec<String> },
48 #[error("Invalid offset in .dynstr section: offset {offset}, data length {data_len}")]
49 InvalidDynstrOffset { offset: usize, data_len: usize },
50 #[error("Non-UTF8 data in .dynstr section: {0}")]
51 InvalidUtf8InDynstr(FromUtf8Error),
52 #[error("Invalid section header string table index: {shstrndx}, section header count: {shnum}")]
53 InvalidShstrndx { shstrndx: u16, shnum: usize },
54 #[error(
55 "Invalid section name offset: sh_name {sh_name:#x} exceeds string table length \
56 {strtab_len:#x}"
57 )]
58 InvalidSectionName { sh_name: u32, strtab_len: usize },
59 #[error(
60 "Section {section} data out of bounds: offset {offset:#x} + size {size:#x} exceeds file \
61 length {file_len:#x}"
62 )]
63 SectionDataOutOfBounds {
64 section: String,
65 offset: u64,
66 size: u64,
67 file_len: usize,
68 },
69}
70
71impl From<SBPFError> for DisassemblerError {
72 fn from(err: SBPFError) -> Self {
73 match err {
74 SBPFError::BytecodeError { error, span, .. } => {
75 DisassemblerError::BytecodeError { error, span }
76 }
77 }
78 }
79}
80
81#[cfg(test)]
82mod tests {
83 use {super::*, object::read::elf::ElfFile64};
84
85 #[test]
86 fn test_from_sbpf_error() {
87 let sbpf_error = SBPFError::BytecodeError {
88 error: "test error".to_string(),
89 span: 8..16,
90 custom_label: None,
91 };
92 let disasm_error: DisassemblerError = sbpf_error.into();
93 assert!(matches!(
94 disasm_error,
95 DisassemblerError::BytecodeError { error, span }
96 if error == "test error" && span == (8..16)
97 ));
98 }
99
100 #[test]
101 fn test_error_display() {
102 let empty: &[u8] = &[];
103 let parse_error = ElfFile64::<object::Endianness>::parse(empty).unwrap_err();
104 let msg = DisassemblerError::InvalidElfFile {
105 first_bytes: vec![0x7f, 0x45],
106 source: parse_error,
107 }
108 .to_string();
109 assert!(msg.starts_with("Failed to parse ELF file: "));
110 assert!(msg.ends_with("; first bytes: [7f, 45]"));
111
112 assert_eq!(
113 DisassemblerError::NonStandardElfHeader {
114 field: "machine",
115 expected: vec![0xf7, 0x107],
116 found: 0x108,
117 }
118 .to_string(),
119 "Non-standard ELF header: machine is 0x108, expected 0xf7 or 0x107"
120 );
121 assert_eq!(
122 DisassemblerError::InvalidProgramType(0x6474e550).to_string(),
123 "Invalid Program Type: 0x6474e550"
124 );
125 assert_eq!(
126 DisassemblerError::InvalidSectionHeaderType(0x6ffffff6).to_string(),
127 "Invalid Section Header Type: 0x6ffffff6"
128 );
129 assert_eq!(
130 DisassemblerError::InvalidRelocationType(0x2a).to_string(),
131 "Invalid Relocation Type: 0x2a"
132 );
133 let section_error = ElfFile64::<object::Endianness>::parse(empty).unwrap_err();
134 assert!(
135 DisassemblerError::SectionDataError {
136 section: ".rel.dyn",
137 source: section_error,
138 }
139 .to_string()
140 .starts_with("Failed to read .rel.dyn section data: ")
141 );
142 assert_eq!(
143 DisassemblerError::InvalidDataLength(13).to_string(),
144 "Invalid data length: 13"
145 );
146 assert_eq!(
147 DisassemblerError::BytecodeError {
148 error: "custom".to_string(),
149 span: 8..16,
150 }
151 .to_string(),
152 "Bytecode error at bytes 8..16: custom"
153 );
154 assert_eq!(
155 DisassemblerError::MissingTextSection {
156 sections: vec![".rodata".to_string(), ".shstrtab".to_string()],
157 }
158 .to_string(),
159 "Missing text section; sections present: [\".rodata\", \".shstrtab\"]"
160 );
161 assert_eq!(
162 DisassemblerError::InvalidDynstrOffset {
163 offset: 128,
164 data_len: 64,
165 }
166 .to_string(),
167 "Invalid offset in .dynstr section: offset 128, data length 64"
168 );
169
170 let utf8_error = String::from_utf8(vec![0x66, 0xff]).unwrap_err();
171 assert!(
172 DisassemblerError::InvalidUtf8InDynstr(utf8_error)
173 .to_string()
174 .starts_with("Non-UTF8 data in .dynstr section: invalid utf-8")
175 );
176 assert_eq!(
177 DisassemblerError::InvalidShstrndx {
178 shstrndx: 9,
179 shnum: 6,
180 }
181 .to_string(),
182 "Invalid section header string table index: 9, section header count: 6"
183 );
184 assert_eq!(
185 DisassemblerError::InvalidSectionName {
186 sh_name: 0x40,
187 strtab_len: 0x2a,
188 }
189 .to_string(),
190 "Invalid section name offset: sh_name 0x40 exceeds string table length 0x2a"
191 );
192 assert_eq!(
193 DisassemblerError::SectionDataOutOfBounds {
194 section: ".text".to_string(),
195 offset: 0x1000,
196 size: 0x20,
197 file_len: 0x800,
198 }
199 .to_string(),
200 "Section .text data out of bounds: offset 0x1000 + size 0x20 exceeds file length 0x800"
201 );
202 }
203}