1#![doc(hidden)]
9
10use diags::Diags;
11use ir::IRKind;
12use ir::ParameterValue;
13use irdb::IRDb;
14use locationdb::LocationDb;
15use mapdb::{ConstEntry, LabelEntry, MapDb, SectionEntry};
16
17#[allow(unused_imports)]
18use tracing::{debug, error, info, trace, warn};
19
20fn name_col_width<'a>(names: impl Iterator<Item = &'a str>) -> usize {
25 names.map(str::len).max().unwrap_or(0).max(16)
26}
27
28pub fn fmt_const_value(pv: &ParameterValue) -> String {
34 match pv {
35 ParameterValue::U64(v) => format!("0x{v:016x}"),
36 ParameterValue::I64(v) => format!("{v}"),
37 ParameterValue::Integer(v) => format!("{v}"),
38 ParameterValue::QuotedString(s) => format!("\"{s}\""),
39 ParameterValue::Identifier(s) | ParameterValue::DeferredRef(s) => s.clone(),
40 ParameterValue::Extension => "(extension)".to_string(),
41 ParameterValue::Unknown => "(unknown)".to_string(),
42 }
43}
44
45pub fn format_csv(map: &MapDb) -> String {
69 use std::fmt::Write;
70 let mut out = String::new();
71
72 writeln!(out, "Output File, {}", map.output_file).unwrap();
74 writeln!(out, "Base Address, 0x{:016x}", map.base_addr).unwrap();
75 writeln!(out, "Total Size (hex), 0x{:016x}", map.total_size).unwrap();
76 writeln!(out, "Total Size (decimal), {}", map.total_size).unwrap();
77
78 writeln!(out).unwrap();
80 writeln!(out, "Constants").unwrap();
81 if map.consts.is_empty() {
82 writeln!(out, " (none)").unwrap();
83 } else {
84 let name_w = name_col_width(map.consts.iter().map(|c| c.name.as_str()));
85 writeln!(out, "{:<name_w$}, {:<20}, Used,", "Name,", "Value,").unwrap();
86 for c in &map.consts {
87 writeln!(
88 out,
89 "{:<name_w$}, {:<20}, {},",
90 c.name,
91 fmt_const_value(&c.value),
92 if c.used { "yes" } else { "no" }
93 )
94 .unwrap();
95 }
96 }
97
98 writeln!(out).unwrap();
100 writeln!(out, "Sections").unwrap();
101 if map.sections.is_empty() {
102 writeln!(out, " (none)").unwrap();
103 } else {
104 let name_w = name_col_width(map.sections.iter().map(|s| s.name.as_str()));
105 writeln!(
106 out,
107 "{:<name_w$}, {:<18}, {:<18}, {:<18}, Size (bytes),",
108 "Name", "Address", "Offset", "File Offset"
109 )
110 .unwrap();
111 for s in &map.sections {
112 writeln!(
113 out,
114 "{:<name_w$}, 0x{:016x}, 0x{:016x}, 0x{:016x}, {},",
115 s.name, s.abs_start, s.off, s.file_offset, s.size
116 )
117 .unwrap();
118 }
119 }
120
121 writeln!(out).unwrap();
123 writeln!(out, "Labels").unwrap();
124 if map.labels.is_empty() {
125 writeln!(out, " (none)").unwrap();
126 } else {
127 let name_w = name_col_width(map.labels.iter().map(|l| l.name.as_str()));
128 writeln!(
129 out,
130 "{:<name_w$}, {:<18}, {:<18}, File Offset,",
131 "Name,", "Address,", "Offset,"
132 )
133 .unwrap();
134 for l in &map.labels {
135 writeln!(
136 out,
137 "{:<name_w$}, 0x{:016x}, 0x{:016x}, 0x{:016x},",
138 l.name, l.abs_addr, l.off, l.file_offset
139 )
140 .unwrap();
141 }
142 }
143
144 out
145}
146
147pub fn format_c99(map: &MapDb) -> String {
149 use std::fmt::Write;
150 let mut out = String::new();
151
152 let stem = std::path::Path::new(&map.output_file)
153 .file_stem()
154 .and_then(|s| s.to_str())
155 .unwrap_or("OUTPUT")
156 .to_uppercase()
157 .replace(|c: char| !c.is_ascii_alphanumeric(), "_");
158
159 writeln!(out, "// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!").unwrap();
160 writeln!(out, "// Automatically generated file! Do not edit!").unwrap();
161 writeln!(out, "// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!").unwrap();
162 writeln!(out, "#ifndef {}_MAP_H", stem).unwrap();
163 writeln!(out, "#define {}_MAP_H\n", stem).unwrap();
164
165 writeln!(
166 out,
167 "#define {}_MAP_BASE_ADDR 0x{:016x}ULL",
168 stem, map.base_addr
169 )
170 .unwrap();
171 writeln!(out, "#define {}_MAP_TOTAL_SIZE {}ULL", stem, map.total_size).unwrap();
172
173 if !map.sections.is_empty() {
174 writeln!(out, "\n// Sections").unwrap();
175 for sec in &map.sections {
176 let sec_name = sec.name.replace(|c: char| !c.is_ascii_alphanumeric(), "_");
177 writeln!(
178 out,
179 "#define {}_MAP_{}_ADDR 0x{:016x}ULL",
180 stem, sec_name, sec.abs_start
181 )
182 .unwrap();
183 writeln!(
184 out,
185 "#define {}_MAP_{}_OFFSET 0x{:016x}ULL",
186 stem, sec_name, sec.off
187 )
188 .unwrap();
189 writeln!(
190 out,
191 "#define {}_MAP_{}_FILE_OFFSET 0x{:016x}ULL",
192 stem, sec_name, sec.file_offset
193 )
194 .unwrap();
195 writeln!(
196 out,
197 "#define {}_MAP_{}_SIZE {}ULL",
198 stem, sec_name, sec.size
199 )
200 .unwrap();
201 writeln!(out).unwrap();
202 }
203 }
204
205 if !map.labels.is_empty() {
206 writeln!(out, "// Labels").unwrap();
207 for lab in &map.labels {
208 let lab_name = lab.name.replace(|c: char| !c.is_ascii_alphanumeric(), "_");
209 writeln!(
210 out,
211 "#define {}_MAP_{}_ADDR 0x{:016x}ULL",
212 stem, lab_name, lab.abs_addr
213 )
214 .unwrap();
215 writeln!(
216 out,
217 "#define {}_MAP_{}_OFFSET 0x{:016x}ULL",
218 stem, lab_name, lab.off
219 )
220 .unwrap();
221 writeln!(
222 out,
223 "#define {}_MAP_{}_FILE_OFFSET 0x{:016x}ULL",
224 stem, lab_name, lab.file_offset
225 )
226 .unwrap();
227 writeln!(out).unwrap();
228 }
229 }
230
231 writeln!(out, "\n#endif").unwrap();
232 out
233}
234
235pub fn format_json(map: &MapDb) -> String {
264 use serde_json::{Value, json};
265
266 let constants: Vec<Value> = map
267 .consts
268 .iter()
269 .map(|c| json!({ "name": c.name, "value": fmt_const_value(&c.value), "used": c.used }))
270 .collect();
271
272 let sections: Vec<Value> = map
273 .sections
274 .iter()
275 .map(|s| {
276 json!({
277 "name": s.name,
278 "address": format!("0x{:016x}", s.abs_start),
279 "offset": format!("0x{:016x}", s.off),
280 "file_offset": format!("0x{:016x}", s.file_offset),
281 "size": s.size,
282 })
283 })
284 .collect();
285
286 let labels: Vec<Value> = map
287 .labels
288 .iter()
289 .map(|l| {
290 json!({
291 "name": l.name,
292 "address": format!("0x{:016x}", l.abs_addr),
293 "offset": format!("0x{:016x}", l.off),
294 "file_offset": format!("0x{:016x}", l.file_offset),
295 })
296 })
297 .collect();
298
299 let root = json!({
300 "output_file": map.output_file,
301 "base_addr": format!("0x{:016x}", map.base_addr),
302 "total_size": map.total_size,
303 "constants": constants,
304 "sections": sections,
305 "labels": labels,
306 });
307
308 serde_json::to_string_pretty(&root).expect("JSON serialization failed")
309}
310
311pub fn format_rs(map: &MapDb) -> String {
315 use std::fmt::Write;
316 let mut out = String::new();
317
318 let stem = std::path::Path::new(&map.output_file)
319 .file_stem()
320 .and_then(|s| s.to_str())
321 .unwrap_or("OUTPUT")
322 .replace(|c: char| !c.is_ascii_alphanumeric(), "_");
323
324 writeln!(out, "// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!").unwrap();
325 writeln!(out, "// Automatically generated file! Do not edit!").unwrap();
326 writeln!(out, "// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!").unwrap();
327 writeln!(out, "pub mod {}_map {{", stem).unwrap();
328 writeln!(out, " #![allow(dead_code)]\n").unwrap(); writeln!(
331 out,
332 " pub const BASE_ADDR: u64 = 0x{:016x};",
333 map.base_addr
334 )
335 .unwrap();
336 writeln!(out, " pub const TOTAL_SIZE: u64 = {};", map.total_size).unwrap();
337
338 if !map.consts.is_empty() {
339 writeln!(out, "\n // Constants").unwrap();
340 for c in &map.consts {
341 let rs_val = match &c.value {
342 ParameterValue::U64(v) => format!("0x{:016x}", v),
343 ParameterValue::I64(v) => format!("{}", v),
344 ParameterValue::Integer(v) => format!("{}", v),
345 ParameterValue::QuotedString(s) => format!("\"{}\"", s),
346 _ => continue,
347 };
348
349 let rs_type = match &c.value {
350 ParameterValue::U64(_) => "u64",
351 ParameterValue::I64(_) => "i64",
352 ParameterValue::Integer(_) => "i64", ParameterValue::QuotedString(_) => "&str",
354 _ => continue,
355 };
356
357 let name = c
358 .name
359 .replace(|c: char| !c.is_ascii_alphanumeric(), "_")
360 .to_uppercase();
361 writeln!(out, " pub const {}: {} = {};", name, rs_type, rs_val).unwrap();
362 }
363 }
364
365 if !map.sections.is_empty() {
366 writeln!(out, "\n // Sections").unwrap();
367 for sec in &map.sections {
368 let sec_name = sec
369 .name
370 .replace(|c: char| !c.is_ascii_alphanumeric(), "_")
371 .to_uppercase();
372 writeln!(
373 out,
374 " pub const {}_ADDR: u64 = 0x{:016x};",
375 sec_name, sec.abs_start
376 )
377 .unwrap();
378 writeln!(
379 out,
380 " pub const {}_OFFSET: u64 = 0x{:016x};",
381 sec_name, sec.off
382 )
383 .unwrap();
384 writeln!(
385 out,
386 " pub const {}_FILE_OFFSET: u64 = 0x{:016x};",
387 sec_name, sec.file_offset
388 )
389 .unwrap();
390 writeln!(out, " pub const {}_SIZE: u64 = {};", sec_name, sec.size).unwrap();
391 writeln!(out).unwrap();
392 }
393 }
394
395 if !map.labels.is_empty() {
396 writeln!(out, " // Labels").unwrap();
397 for lab in &map.labels {
398 let lab_name = lab
399 .name
400 .replace(|c: char| !c.is_ascii_alphanumeric(), "_")
401 .to_uppercase();
402 writeln!(
403 out,
404 " pub const {}_ADDR: u64 = 0x{:016x};",
405 lab_name, lab.abs_addr
406 )
407 .unwrap();
408 writeln!(
409 out,
410 " pub const {}_OFFSET: u64 = 0x{:016x};",
411 lab_name, lab.off
412 )
413 .unwrap();
414 writeln!(
415 out,
416 " pub const {}_FILE_OFFSET: u64 = 0x{:016x};",
417 lab_name, lab.file_offset
418 )
419 .unwrap();
420 writeln!(out).unwrap();
421 }
422 }
423
424 writeln!(out, "}}").unwrap();
425 out
426}
427
428pub fn build(
431 location_db: &LocationDb,
432 irdb: &IRDb,
433 output_file: &str,
434 _diags: &mut Diags,
435) -> MapDb {
436 let mut sections: Vec<SectionEntry> = Vec::new();
437 let mut labels: Vec<LabelEntry> = Vec::new();
438
439 let mut stack: Vec<(String, usize)> = Vec::new();
440 for (ir_index, ir) in irdb.ir_vec.iter().enumerate() {
441 match ir.kind {
442 IRKind::SectionStart => {
443 let name = irdb.get_opnd_as_identifier(ir, 0).to_string();
444 stack.push((name, ir_index));
445 }
446 IRKind::SectionEnd => {
447 let (name, start_ir) = stack.pop().unwrap();
448 let file_start = location_db.ir_locs[start_ir].file_offset;
449 let file_end = location_db.ir_locs[ir_index].file_offset;
450
451 let content_idx = ((start_ir + 1)..ir_index)
452 .find(|&j| irdb.ir_vec[j].kind != IRKind::SetAddr)
453 .unwrap_or(ir_index);
454 let content_loc = &location_db.ir_locs[content_idx];
455
456 sections.push(SectionEntry {
457 name,
458 file_offset: file_start,
459 off: content_loc.addr.addr_offset,
460 abs_start: content_loc.addr.addr_base + content_loc.addr.addr_offset,
461 size: file_end - file_start,
462 });
463 }
464 IRKind::Label => {
465 let name = irdb.get_opnd_as_identifier(ir, 0).to_string();
466 let loc = &location_db.ir_locs[ir_index];
467 labels.push(LabelEntry {
468 name,
469 file_offset: loc.file_offset,
470 off: loc.addr.addr_offset,
471 abs_addr: loc.addr.addr_base + loc.addr.addr_offset,
472 });
473 }
474 _ => {}
475 }
476 }
477
478 let mut consts: Vec<ConstEntry> = irdb
479 .symbol_table
480 .iter_defined_with_used()
481 .map(|(name, pv, used)| ConstEntry {
482 name: name.to_string(),
483 value: pv.clone(),
484 used,
485 })
486 .collect();
487 consts.sort_by(|a, b| a.name.cmp(&b.name));
488
489 let base_addr = sections.first().map(|s| s.abs_start).unwrap_or(0);
490 let total_size = sections.last().map(|s| s.file_offset + s.size).unwrap_or(0);
491
492 MapDb {
493 output_file: output_file.to_string(),
494 base_addr,
495 total_size,
496 sections,
497 labels,
498 consts,
499 }
500}
501#[cfg(test)]
504mod tests {
505 use super::*;
506 use ir::ParameterValue;
507
508 fn make_map() -> MapDb {
509 MapDb {
510 output_file: "out.bin".to_string(),
511 base_addr: 0x1000,
512 total_size: 0x80,
513 sections: vec![
514 SectionEntry {
515 name: "text".to_string(),
516 file_offset: 0x00,
517 off: 0x00,
518 abs_start: 0x1000,
519 size: 0x40,
520 },
521 SectionEntry {
522 name: "data".to_string(),
523 file_offset: 0x40,
524 off: 0x40,
525 abs_start: 0x1040,
526 size: 0x40,
527 },
528 ],
529 labels: vec![
530 LabelEntry {
531 name: "start".to_string(),
532 file_offset: 0x00,
533 off: 0x00,
534 abs_addr: 0x1000,
535 },
536 LabelEntry {
537 name: "end_marker".to_string(),
538 file_offset: 0x7f,
539 off: 0x7f,
540 abs_addr: 0x107f,
541 },
542 ],
543 consts: vec![
544 ConstEntry {
545 name: "BASE".to_string(),
546 value: ParameterValue::U64(0x1000),
547 used: true,
548 },
549 ConstEntry {
550 name: "COUNT".to_string(),
551 value: ParameterValue::Integer(42),
552 used: true,
553 },
554 ConstEntry {
555 name: "VERSION".to_string(),
556 value: ParameterValue::QuotedString("v1.0".to_string()),
557 used: true,
558 },
559 ConstEntry {
560 name: "OFFSET".to_string(),
561 value: ParameterValue::I64(-10),
562 used: true,
563 },
564 ],
565 }
566 }
567
568 #[test]
569 fn header_contains_output_file_and_base_addr() {
570 let out = format_csv(&make_map());
571 assert!(out.contains("out.bin"), "output file name missing");
572 assert!(out.contains("0x0000000000001000"), "base addr missing");
573 }
574
575 #[test]
576 fn header_contains_total_size() {
577 let out = format_csv(&make_map());
578 assert!(out.contains("128"), "total size in bytes missing");
579 }
580
581 #[test]
582 fn sections_contain_names_and_addresses() {
583 let out = format_csv(&make_map());
584 assert!(out.contains("text"), "section name 'text' missing");
585 assert!(out.contains("data"), "section name 'data' missing");
586 assert!(
588 out.contains("0x0000000000001000"),
589 "'text' abs_start missing"
590 );
591 assert!(
593 out.contains("0x0000000000001040"),
594 "'data' abs_start missing"
595 );
596 }
597
598 #[test]
599 fn sections_contain_sizes() {
600 let out = format_csv(&make_map());
601 assert!(out.contains("64,"), "section size '64' missing");
602 }
603
604 #[test]
605 fn labels_contain_names_and_addresses() {
606 let out = format_csv(&make_map());
607 assert!(out.contains("start"), "label 'start' missing");
608 assert!(out.contains("end_marker"), "label 'end_marker' missing");
609 assert!(
610 out.contains("0x000000000000107f"),
611 "label 'end_marker' abs_addr missing"
612 );
613 }
614
615 #[test]
616 fn consts_appear_before_sections_in_output() {
617 let out = format_csv(&make_map());
618 let const_pos = out.find("Constants").expect("Constants section missing");
619 let section_pos = out.find("Sections").expect("Sections section missing");
620 assert!(
621 const_pos < section_pos,
622 "Constants must appear before Sections"
623 );
624 }
625
626 #[test]
627 fn consts_contain_names_and_values() {
628 let out = format_csv(&make_map());
629 assert!(out.contains("BASE"), "const name 'BASE' missing");
630 assert!(out.contains("COUNT"), "const name 'COUNT' missing");
631 assert!(out.contains("OFFSET"), "const name 'OFFSET' missing");
632 assert!(out.contains("VERSION"), "const name 'VERSION' missing");
633 assert!(
635 out.contains("0x0000000000001000"),
636 "const U64 hex value missing"
637 );
638 assert!(out.contains("42"), "const Integer decimal value missing");
640 assert!(out.contains("-10"), "const I64 neg value missing");
642 assert!(out.contains("\"v1.0\""), "const QuotedString value missing");
644 assert!(out.contains("yes"), "used column 'yes' missing");
646 }
647
648 #[test]
649 fn consts_unused_shows_no() {
650 let map = MapDb {
651 output_file: "x.bin".to_string(),
652 base_addr: 0,
653 total_size: 0,
654 sections: vec![],
655 labels: vec![],
656 consts: vec![ConstEntry {
657 name: "UNUSED".to_string(),
658 value: ParameterValue::U64(0),
659 used: false,
660 }],
661 };
662 let out = format_csv(&map);
663 assert!(
664 out.contains("no"),
665 "used column 'no' missing for unused const"
666 );
667 }
668
669 #[test]
670 fn empty_sections_shows_none() {
671 let map = MapDb {
672 output_file: "x.bin".to_string(),
673 base_addr: 0,
674 total_size: 0,
675 sections: vec![],
676 labels: vec![],
677 consts: vec![],
678 };
679 let out = format_csv(&map);
680 assert_eq!(
682 out.matches("(none)").count(),
683 3,
684 "expected (none) for each empty table"
685 );
686 }
687
688 #[test]
689 fn repeated_section_name_appears_multiple_times() {
690 let map = MapDb {
691 output_file: "y.bin".to_string(),
692 base_addr: 0,
693 total_size: 0x20,
694 sections: vec![
695 SectionEntry {
696 name: "foo".to_string(),
697 file_offset: 0x00,
698 off: 0x00,
699 abs_start: 0x00,
700 size: 0x10,
701 },
702 SectionEntry {
703 name: "foo".to_string(),
704 file_offset: 0x10,
705 off: 0x10,
706 abs_start: 0x10,
707 size: 0x10,
708 },
709 ],
710 labels: vec![],
711 consts: vec![],
712 };
713 let out = format_csv(&map);
714 assert_eq!(
715 out.matches("foo").count(),
716 2,
717 "repeated section 'foo' should appear twice"
718 );
719 }
720
721 #[test]
722 fn fmt_const_value_variants() {
723 assert_eq!(
724 fmt_const_value(&ParameterValue::U64(0x10)),
725 "0x0000000000000010"
726 );
727 assert_eq!(fmt_const_value(&ParameterValue::I64(-7)), "-7");
728 assert_eq!(fmt_const_value(&ParameterValue::Integer(99)), "99");
729 assert_eq!(
730 fmt_const_value(&ParameterValue::QuotedString("hi".to_string())),
731 "\"hi\""
732 );
733 }
734
735 #[test]
738 fn json_is_valid_and_contains_output_file() {
739 let out = format_json(&make_map());
740 let v: serde_json::Value = serde_json::from_str(&out).expect("output is not valid JSON");
741 assert_eq!(v["output_file"], "out.bin");
742 }
743
744 #[test]
745 fn json_header_fields() {
746 let out = format_json(&make_map());
747 let v: serde_json::Value = serde_json::from_str(&out).unwrap();
748 assert_eq!(v["base_addr"], "0x0000000000001000");
749 assert_eq!(v["total_size"], 0x80u64);
750 }
751
752 #[test]
753 fn json_sections_contain_names_and_addresses() {
754 let out = format_json(&make_map());
755 let v: serde_json::Value = serde_json::from_str(&out).unwrap();
756 let sections = v["sections"].as_array().unwrap();
757 let names: Vec<&str> = sections
758 .iter()
759 .map(|s| s["name"].as_str().unwrap())
760 .collect();
761 assert!(names.contains(&"text"), "section 'text' missing");
762 assert!(names.contains(&"data"), "section 'data' missing");
763 let text = sections.iter().find(|s| s["name"] == "text").unwrap();
765 assert_eq!(text["address"], "0x0000000000001000");
766 assert_eq!(text["size"], 0x40u64);
767 }
768
769 #[test]
770 fn json_labels_contain_names_and_addresses() {
771 let out = format_json(&make_map());
772 let v: serde_json::Value = serde_json::from_str(&out).unwrap();
773 let labels = v["labels"].as_array().unwrap();
774 let start = labels.iter().find(|l| l["name"] == "start").unwrap();
775 assert_eq!(start["address"], "0x0000000000001000");
776 let end_marker = labels.iter().find(|l| l["name"] == "end_marker").unwrap();
777 assert_eq!(end_marker["address"], "0x000000000000107f");
778 }
779
780 #[test]
781 fn json_consts_contain_names_and_values() {
782 let out = format_json(&make_map());
783 let v: serde_json::Value = serde_json::from_str(&out).unwrap();
784 let consts = v["constants"].as_array().unwrap();
785 let base = consts.iter().find(|c| c["name"] == "BASE").unwrap();
786 assert_eq!(base["value"], "0x0000000000001000");
787 assert_eq!(base["used"], true);
788 let count = consts.iter().find(|c| c["name"] == "COUNT").unwrap();
789 assert_eq!(count["value"], "42");
790 assert_eq!(count["used"], true);
791 let offset = consts.iter().find(|c| c["name"] == "OFFSET").unwrap();
792 assert_eq!(offset["value"], "-10");
793 let version = consts.iter().find(|c| c["name"] == "VERSION").unwrap();
794 assert_eq!(version["value"], "\"v1.0\"");
795 }
796
797 #[test]
798 fn json_empty_tables_are_empty_arrays() {
799 let map = MapDb {
800 output_file: "x.bin".to_string(),
801 base_addr: 0,
802 total_size: 0,
803 sections: vec![],
804 labels: vec![],
805 consts: vec![],
806 };
807 let out = format_json(&map);
808 let v: serde_json::Value = serde_json::from_str(&out).unwrap();
809 assert_eq!(v["sections"].as_array().unwrap().len(), 0);
810 assert_eq!(v["labels"].as_array().unwrap().len(), 0);
811 assert_eq!(v["constants"].as_array().unwrap().len(), 0);
812 }
813
814 #[test]
815 fn json_repeated_section_produces_multiple_entries() {
816 let map = MapDb {
817 output_file: "y.bin".to_string(),
818 base_addr: 0,
819 total_size: 0x20,
820 sections: vec![
821 SectionEntry {
822 name: "foo".to_string(),
823 file_offset: 0x00,
824 off: 0x00,
825 abs_start: 0x00,
826 size: 0x10,
827 },
828 SectionEntry {
829 name: "foo".to_string(),
830 file_offset: 0x10,
831 off: 0x10,
832 abs_start: 0x10,
833 size: 0x10,
834 },
835 ],
836 labels: vec![],
837 consts: vec![],
838 };
839 let out = format_json(&map);
840 let v: serde_json::Value = serde_json::from_str(&out).unwrap();
841 assert_eq!(v["sections"].as_array().unwrap().len(), 2);
842 }
843
844 #[test]
847 fn rs_is_valid_and_contains_output_file() {
848 let out = format_rs(&make_map());
849 assert!(out.contains("pub mod out_map {"));
850 assert!(out.contains("pub const BASE_ADDR: u64 = 0x0000000000001000;"));
851 }
852
853 #[test]
854 fn rs_sections_contain_names_and_addresses() {
855 let out = format_rs(&make_map());
856 assert!(out.contains("pub const TEXT_ADDR: u64 = 0x0000000000001000;"));
857 assert!(out.contains("pub const TEXT_SIZE: u64 = 64;"));
858 assert!(out.contains("pub const DATA_ADDR: u64 = 0x0000000000001040;"));
859 assert!(out.contains("pub const DATA_SIZE: u64 = 64;"));
860 }
861
862 #[test]
863 fn rs_labels_contain_names_and_addresses() {
864 let out = format_rs(&make_map());
865 assert!(out.contains("pub const START_ADDR: u64 = 0x0000000000001000;"));
866 assert!(out.contains("pub const END_MARKER_ADDR: u64 = 0x000000000000107f;"));
867 }
868
869 #[test]
870 fn rs_consts_contain_names_and_values() {
871 let out = format_rs(&make_map());
872 assert!(out.contains("pub const BASE: u64 = 0x0000000000001000;"));
873 assert!(out.contains("pub const COUNT: i64 = 42;"));
874 assert!(out.contains("pub const OFFSET: i64 = -10;"));
875 assert!(out.contains("pub const VERSION: &str = \"v1.0\";"));
876 }
877}