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