1#[cfg(not(feature = "std"))]
4use alloc::vec::Vec;
5
6use crate::error::FormatError;
7
8#[derive(Debug, Clone, PartialEq)]
10pub struct SymbolTableMessage {
11 pub btree_address: u64,
13 pub local_heap_address: u64,
15}
16
17fn read_offset(data: &[u8], pos: usize, size: u8) -> Result<u64, FormatError> {
18 let s = size as usize;
19 if pos + s > data.len() {
20 return Err(FormatError::UnexpectedEof {
21 expected: pos + s,
22 available: data.len(),
23 });
24 }
25 let slice = &data[pos..pos + s];
26 Ok(match size {
27 2 => u16::from_le_bytes([slice[0], slice[1]]) as u64,
28 4 => u32::from_le_bytes([slice[0], slice[1], slice[2], slice[3]]) as u64,
29 8 => u64::from_le_bytes([
30 slice[0], slice[1], slice[2], slice[3], slice[4], slice[5], slice[6], slice[7],
31 ]),
32 _ => return Err(FormatError::InvalidOffsetSize(size)),
33 })
34}
35
36impl SymbolTableMessage {
37 pub fn parse(data: &[u8], offset_size: u8) -> Result<SymbolTableMessage, FormatError> {
39 let os = offset_size as usize;
40 if data.len() < os * 2 {
41 return Err(FormatError::UnexpectedEof {
42 expected: os * 2,
43 available: data.len(),
44 });
45 }
46 let btree_address = read_offset(data, 0, offset_size)?;
47 let local_heap_address = read_offset(data, os, offset_size)?;
48 Ok(SymbolTableMessage {
49 btree_address,
50 local_heap_address,
51 })
52 }
53}
54
55#[derive(Debug, Clone)]
57pub struct SymbolTableEntry {
58 pub link_name_offset: u64,
60 pub object_header_address: u64,
62 pub cache_type: u32,
64 pub scratch_pad: [u8; 16],
66}
67
68#[derive(Debug, Clone)]
70pub struct SymbolTableNode {
71 pub entries: Vec<SymbolTableEntry>,
73}
74
75impl SymbolTableNode {
76 pub fn parse(
78 file_data: &[u8],
79 offset: usize,
80 offset_size: u8,
81 ) -> Result<SymbolTableNode, FormatError> {
82 if offset + 8 > file_data.len() {
84 return Err(FormatError::UnexpectedEof {
85 expected: offset + 8,
86 available: file_data.len(),
87 });
88 }
89
90 if &file_data[offset..offset + 4] != b"SNOD" {
91 return Err(FormatError::InvalidSymbolTableNodeSignature);
92 }
93
94 let version = file_data[offset + 4];
95 if version != 1 {
96 return Err(FormatError::InvalidSymbolTableNodeVersion(version));
97 }
98
99 let num_symbols =
100 u16::from_le_bytes([file_data[offset + 6], file_data[offset + 7]]) as usize;
101
102 let os = offset_size as usize;
103 let entry_size = os + os + 4 + 4 + 16;
105 let entries_start = offset + 8;
106 let needed = entries_start + num_symbols * entry_size;
107 if needed > file_data.len() {
108 return Err(FormatError::UnexpectedEof {
109 expected: needed,
110 available: file_data.len(),
111 });
112 }
113
114 let mut entries = Vec::with_capacity(num_symbols);
115 let mut pos = entries_start;
116 for _ in 0..num_symbols {
117 let link_name_offset = read_offset(file_data, pos, offset_size)?;
118 pos += os;
119 let object_header_address = read_offset(file_data, pos, offset_size)?;
120 pos += os;
121 let cache_type = u32::from_le_bytes([
122 file_data[pos],
123 file_data[pos + 1],
124 file_data[pos + 2],
125 file_data[pos + 3],
126 ]);
127 pos += 4;
128 pos += 4;
130 let mut scratch_pad = [0u8; 16];
131 scratch_pad.copy_from_slice(&file_data[pos..pos + 16]);
132 pos += 16;
133
134 entries.push(SymbolTableEntry {
135 link_name_offset,
136 object_header_address,
137 cache_type,
138 scratch_pad,
139 });
140 }
141
142 Ok(SymbolTableNode { entries })
143 }
144}
145
146#[cfg(test)]
147mod tests {
148 use super::*;
149
150 #[test]
151 fn parse_symbol_table_message_offset8() {
152 let mut data = Vec::new();
153 data.extend_from_slice(&0x1000u64.to_le_bytes()); data.extend_from_slice(&0x2000u64.to_le_bytes()); let msg = SymbolTableMessage::parse(&data, 8).unwrap();
156 assert_eq!(msg.btree_address, 0x1000);
157 assert_eq!(msg.local_heap_address, 0x2000);
158 }
159
160 #[test]
161 fn parse_symbol_table_message_offset4() {
162 let mut data = Vec::new();
163 data.extend_from_slice(&0x800u32.to_le_bytes());
164 data.extend_from_slice(&0x900u32.to_le_bytes());
165 let msg = SymbolTableMessage::parse(&data, 4).unwrap();
166 assert_eq!(msg.btree_address, 0x800);
167 assert_eq!(msg.local_heap_address, 0x900);
168 }
169
170 fn build_snod(entries: &[(u64, u64, u32)], offset_size: u8) -> Vec<u8> {
171 let mut buf = Vec::new();
172 buf.extend_from_slice(b"SNOD");
174 buf.push(1); buf.push(0); buf.extend_from_slice(&(entries.len() as u16).to_le_bytes());
177 for &(name_off, ohdr_addr, cache_type) in entries {
178 match offset_size {
179 4 => {
180 buf.extend_from_slice(&(name_off as u32).to_le_bytes());
181 buf.extend_from_slice(&(ohdr_addr as u32).to_le_bytes());
182 }
183 8 => {
184 buf.extend_from_slice(&name_off.to_le_bytes());
185 buf.extend_from_slice(&ohdr_addr.to_le_bytes());
186 }
187 _ => panic!("test offset_size"),
188 }
189 buf.extend_from_slice(&cache_type.to_le_bytes());
190 buf.extend_from_slice(&0u32.to_le_bytes()); buf.extend_from_slice(&[0u8; 16]); }
193 buf
194 }
195
196 #[test]
197 fn parse_snod_two_entries() {
198 let data = build_snod(&[(0, 0x100, 0), (8, 0x200, 1)], 8);
199 let snod = SymbolTableNode::parse(&data, 0, 8).unwrap();
200 assert_eq!(snod.entries.len(), 2);
201 assert_eq!(snod.entries[0].link_name_offset, 0);
202 assert_eq!(snod.entries[0].object_header_address, 0x100);
203 assert_eq!(snod.entries[0].cache_type, 0);
204 assert_eq!(snod.entries[1].link_name_offset, 8);
205 assert_eq!(snod.entries[1].object_header_address, 0x200);
206 assert_eq!(snod.entries[1].cache_type, 1);
207 }
208
209 #[test]
210 fn parse_snod_empty() {
211 let data = build_snod(&[], 8);
212 let snod = SymbolTableNode::parse(&data, 0, 8).unwrap();
213 assert_eq!(snod.entries.len(), 0);
214 }
215
216 #[test]
217 fn parse_snod_invalid_signature() {
218 let mut data = build_snod(&[], 8);
219 data[0] = b'X';
220 let err = SymbolTableNode::parse(&data, 0, 8).unwrap_err();
221 assert_eq!(err, FormatError::InvalidSymbolTableNodeSignature);
222 }
223
224 #[test]
225 fn parse_snod_invalid_version() {
226 let mut data = build_snod(&[], 8);
227 data[4] = 2; let err = SymbolTableNode::parse(&data, 0, 8).unwrap_err();
229 assert_eq!(err, FormatError::InvalidSymbolTableNodeVersion(2));
230 }
231}