1use {
2 serde::{Deserialize, Serialize},
3 std::collections::BTreeSet,
4};
5
6#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
7pub enum RodataType {
8 Ascii(String),
9 Byte(Vec<i8>),
10 Word(i16),
11 Long(i32),
12 Quad(i64),
13}
14
15impl RodataType {
16 pub fn to_asm(&self) -> String {
17 match self {
18 RodataType::Ascii(s) => format!(".ascii \"{}\"", s),
19 RodataType::Byte(v) => format!(".byte {}", format_byte_values(v)),
20 RodataType::Word(v) => format!(".word 0x{:04x}", *v as u16),
21 RodataType::Long(v) => format!(".long 0x{:08x}", *v as u32),
22 RodataType::Quad(v) => format!(".quad 0x{:016x}", *v as u64),
23 }
24 }
25}
26
27#[derive(Debug, Clone, Serialize, Deserialize)]
28pub struct RodataItem {
29 pub label: String,
30 pub offset: u64,
31 pub size: u64,
32 pub data_type: RodataType,
33 pub data: Vec<u8>,
34}
35
36impl RodataItem {
37 pub fn new(label: String, offset: u64, data: Vec<u8>, data_type: RodataType) -> Self {
38 Self {
39 label,
40 size: data.len() as u64,
41 offset,
42 data_type,
43 data,
44 }
45 }
46
47 pub fn to_asm(&self) -> String {
48 format!("{}: {}", self.label, self.data_type.to_asm())
49 }
50}
51
52#[derive(Debug, Clone, Serialize, Deserialize)]
53pub struct RodataSection {
54 pub base_address: u64, pub data: Vec<u8>, pub items: Vec<RodataItem>, }
58
59impl RodataSection {
60 pub fn parse(data: Vec<u8>, base_address: u64, references: &BTreeSet<u64>) -> Self {
61 let items = parse_rodata_items(&data, base_address, references);
62 Self {
63 base_address,
64 data,
65 items,
66 }
67 }
68
69 #[inline]
70 pub fn has_items(&self) -> bool {
71 !self.items.is_empty()
72 }
73
74 pub fn to_asm(&self) -> String {
75 if self.items.is_empty() {
76 return String::new();
77 }
78
79 let mut output = String::from(".rodata\n");
80 for item in &self.items {
81 output.push_str(&format!(" {}\n", item.to_asm()));
82 }
83 output
84 }
85
86 pub fn get_label(&self, address: u64) -> Option<&str> {
87 if address < self.base_address {
88 return None;
89 }
90 let offset = address - self.base_address;
91 self.items
92 .iter()
93 .find(|item| item.offset == offset)
94 .map(|item| item.label.as_str())
95 }
96
97 #[inline]
98 pub fn contains_address(&self, address: u64) -> bool {
99 address >= self.base_address && address < self.base_address + self.data.len() as u64
100 }
101}
102
103fn parse_rodata_items(
104 data: &[u8],
105 base_address: u64,
106 references: &BTreeSet<u64>,
107) -> Vec<RodataItem> {
108 if data.is_empty() {
109 return Vec::new();
110 }
111
112 let mut offsets: Vec<u64> = references
114 .iter()
115 .filter_map(|&addr| {
116 if addr >= base_address && addr < base_address + data.len() as u64 {
117 Some(addr - base_address)
118 } else {
119 None
120 }
121 })
122 .collect();
123
124 if offsets.is_empty() {
126 let trimmed = trim_trailing_zeros(data);
127 if trimmed.is_empty() {
128 return Vec::new();
129 }
130 let data_type = infer_type(trimmed);
131 let label = generate_label(0, &data_type);
132 return vec![RodataItem::new(label, 0, trimmed.to_vec(), data_type)];
133 }
134
135 if offsets[0] != 0 {
137 offsets.insert(0, 0);
138 }
139
140 let mut items = Vec::new();
142 for (i, &offset) in offsets.iter().enumerate() {
143 let start = offset as usize;
144 if start >= data.len() {
145 continue;
146 }
147
148 let end = if i + 1 < offsets.len() {
150 (offsets[i + 1] as usize).min(data.len())
151 } else {
152 let remaining = &data[start..];
153 start + trim_trailing_zeros(remaining).len()
154 };
155
156 if start < end {
157 let bytes = data[start..end].to_vec();
158 let data_type = infer_type(&bytes);
159 let label = generate_label(offset, &data_type);
160 items.push(RodataItem::new(label, offset, bytes, data_type));
161 }
162 }
163
164 items
165}
166
167#[inline]
168fn trim_trailing_zeros(data: &[u8]) -> &[u8] {
169 let end = data.iter().rposition(|&b| b != 0).map_or(0, |i| i + 1);
170 &data[..end]
171}
172
173fn infer_type(data: &[u8]) -> RodataType {
174 if let Ok(s) = std::str::from_utf8(data)
175 && is_ascii(s)
176 && !s.is_empty()
177 {
178 return RodataType::Ascii(s.to_string());
179 }
180
181 match data.len() {
182 2 => RodataType::Word(i16::from_le_bytes([data[0], data[1]])),
183 4 => RodataType::Long(i32::from_le_bytes(data[0..4].try_into().unwrap())),
184 8 => RodataType::Quad(i64::from_le_bytes(data[0..8].try_into().unwrap())),
185 _ => RodataType::Byte(data.iter().map(|&b| b as i8).collect()),
186 }
187}
188
189#[inline]
190fn is_ascii(s: &str) -> bool {
191 s.chars()
192 .all(|c| c.is_ascii_graphic() || c == ' ' || c == '\t' || c == '\n' || c == '\r')
193}
194
195fn generate_label(offset: u64, data_type: &RodataType) -> String {
196 match data_type {
197 RodataType::Ascii(_) => format!("str_{:04x}", offset),
198 _ => format!("data_{:04x}", offset),
199 }
200}
201
202fn format_byte_values(vals: &[i8]) -> String {
203 vals.iter()
204 .map(|&v| format!("0x{:02x}", v as u8))
205 .collect::<Vec<_>>()
206 .join(", ")
207}
208
209#[cfg(test)]
210mod tests {
211 use super::*;
212
213 #[test]
214 fn test_infer_type_ascii() {
215 let data = b"Hello, World!";
216 let result = infer_type(data);
217 assert!(matches!(result, RodataType::Ascii(s) if s == "Hello, World!"));
218 }
219
220 #[test]
221 fn test_infer_type_byte() {
222 let data = &[0x01];
223 if let RodataType::Byte(vals) = infer_type(data) {
224 assert_eq!(vals[0], 0x01);
225 } else {
226 panic!("Expected Byte type");
227 }
228 }
229
230 #[test]
231 fn test_infer_type_word() {
232 let data = &[0x34, 0x12];
233 if let RodataType::Word(val) = infer_type(data) {
234 assert_eq!(val, 0x1234);
235 } else {
236 panic!("Expected Word type");
237 }
238 }
239
240 #[test]
241 fn test_infer_type_long() {
242 let data = &[0x78, 0x56, 0x34, 0x12];
243 if let RodataType::Long(val) = infer_type(data) {
244 assert_eq!(val, 0x12345678);
245 } else {
246 panic!("Expected Long type");
247 }
248 }
249
250 #[test]
251 fn test_infer_type_quad() {
252 let data = &[0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08];
253 if let RodataType::Quad(val) = infer_type(data) {
254 assert_eq!(val, 0x0807060504030201i64);
255 } else {
256 panic!("Expected Quad type");
257 }
258 }
259
260 #[test]
261 fn test_infer_type_bytes() {
262 let data = &[0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x0];
263 if let RodataType::Byte(vals) = infer_type(data) {
264 assert_eq!(vals.len(), 9);
265 } else {
266 panic!("Expected Byte array for 9 bytes");
267 }
268 }
269
270 #[test]
271 fn test_generate_label_str() {
272 let t = RodataType::Ascii("test".to_string());
273 assert_eq!(generate_label(0, &t), "str_0000");
274 assert_eq!(generate_label(16, &t), "str_0010");
275 assert_eq!(generate_label(255, &t), "str_00ff");
276 }
277
278 #[test]
279 fn test_generate_label_data() {
280 assert_eq!(generate_label(0, &RodataType::Byte(vec![0])), "data_0000");
281 assert_eq!(generate_label(0, &RodataType::Word(0)), "data_0000");
282 assert_eq!(generate_label(0, &RodataType::Long(0)), "data_0000");
283 assert_eq!(generate_label(0, &RodataType::Quad(0)), "data_0000");
284 }
285
286 #[test]
287 fn test_rodata_type_to_asm() {
288 assert_eq!(
289 RodataType::Ascii("Hello".to_string()).to_asm(),
290 ".ascii \"Hello\""
291 );
292 assert_eq!(
293 RodataType::Byte(vec![0, 1, -1]).to_asm(),
294 ".byte 0x00, 0x01, 0xff"
295 );
296 assert_eq!(RodataType::Word(0x1234).to_asm(), ".word 0x1234");
297 assert_eq!(RodataType::Long(0x12345678).to_asm(), ".long 0x12345678");
298 assert_eq!(
299 RodataType::Quad(0x123456789ABCDEF0u64 as i64).to_asm(),
300 ".quad 0x123456789abcdef0"
301 );
302 }
303
304 #[test]
305 fn test_rodata_item_to_asm() {
306 let item = RodataItem::new(
307 "str_0000".to_string(),
308 0,
309 b"Hello".to_vec(),
310 RodataType::Ascii("Hello".to_string()),
311 );
312 assert_eq!(item.to_asm(), "str_0000: .ascii \"Hello\"");
313 }
314
315 #[test]
316 fn test_rodata_section_empty() {
317 let section = RodataSection::parse(Vec::new(), 0x100, &BTreeSet::new());
318 assert!(section.to_asm().is_empty());
319 }
320
321 #[test]
322 fn test_rodata_section_contains_address() {
323 let section = RodataSection::parse(vec![0x01, 0x02, 0x03, 0x04], 0x100, &BTreeSet::new());
324
325 assert!(section.contains_address(0x100));
326 assert!(section.contains_address(0x103));
327 assert!(!section.contains_address(0x99));
328 assert!(!section.contains_address(0x104));
329 }
330
331 #[test]
332 fn test_rodata_section_has_items() {
333 let section_with_data = RodataSection::parse(vec![0x01], 0x100, &BTreeSet::new());
334 assert!(section_with_data.has_items());
335
336 let section_empty = RodataSection::parse(Vec::new(), 0x100, &BTreeSet::new());
337 assert!(!section_empty.has_items());
338 }
339
340 #[test]
341 fn test_trim_trailing_zeros() {
342 assert_eq!(trim_trailing_zeros(&[1, 2, 3, 0, 0]), &[1, 2, 3]);
343 assert_eq!(trim_trailing_zeros(&[0, 0, 0]), &[] as &[u8]);
344 assert_eq!(trim_trailing_zeros(&[1, 0, 2, 0]), &[1, 0, 2]);
345 assert_eq!(trim_trailing_zeros(&[]), &[] as &[u8]);
346 }
347}