1#[derive(Clone, Copy, Debug, PartialEq, Eq)]
11pub enum ObjectFormat {
12 Elf,
14 MachO,
16 Coff,
18}
19
20#[derive(Clone, Copy, Debug, PartialEq, Eq)]
22pub enum RelocKind {
23 Pc32,
25 Abs64,
27}
28
29#[derive(Clone, Debug)]
31pub struct Reloc {
32 pub offset: u64,
34 pub symbol: usize,
36 pub kind: RelocKind,
38 pub addend: i64,
40}
41
42#[derive(Clone, Copy, Debug, PartialEq, Eq)]
44pub struct DebugLineRow {
45 pub address: u64,
47 pub line: u32,
49 pub column: u32,
51}
52
53#[derive(Clone, Debug)]
55pub struct Section {
56 pub name: String,
58 pub data: Vec<u8>,
60 pub relocs: Vec<Reloc>,
62 pub debug_rows: Vec<DebugLineRow>,
64}
65
66#[derive(Clone, Debug)]
68pub struct Symbol {
69 pub name: String,
71 pub section: usize,
73 pub offset: u64,
75 pub size: u64,
77 pub global: bool,
79}
80
81#[derive(Clone, Debug)]
83pub struct ObjectFile {
84 pub format: ObjectFormat,
86 pub elf_machine: u16,
89 pub coff_machine: u16,
91 pub sections: Vec<Section>,
93 pub symbols: Vec<Symbol>,
95}
96
97impl ObjectFile {
98 pub fn to_bytes(&self) -> Vec<u8> {
100 match self.format {
101 ObjectFormat::Elf => serialize_elf(self),
102 ObjectFormat::MachO => serialize_macho(self),
103 ObjectFormat::Coff => serialize_coff(self),
104 }
105 }
106}
107
108use crate::isel::{MachineFunction, PReg};
111
112pub trait Emitter {
114 fn emit_function(&mut self, mf: &MachineFunction) -> Section;
116
117 fn object_format(&self) -> ObjectFormat;
119
120 fn elf_machine(&self) -> u16 {
122 62 }
124
125 fn coff_machine(&self) -> u16 {
127 0x8664 }
129}
130
131pub fn emit_object(mf: &MachineFunction, emitter: &mut dyn Emitter) -> ObjectFile {
133 let section = emitter.emit_function(mf);
134 let size = section.data.len() as u64;
135 let sym = Symbol {
136 name: mf.name.clone(),
137 section: 0,
138 offset: 0,
139 size,
140 global: true,
141 };
142 let mut sections = vec![section];
143
144 match emitter.object_format() {
148 ObjectFormat::Elf => {
149 sections.push(Section {
150 name: ".eh_frame".into(),
151 data: build_eh_frame(size, mf.frame_size, &mf.used_callee_saved),
152 relocs: Vec::new(),
153 debug_rows: Vec::new(),
154 });
155 }
156 ObjectFormat::Coff => {
157 let (xdata, pdata) = build_coff_unwind_tables(size, mf.frame_size, &mf.used_callee_saved);
158 sections.push(Section {
159 name: ".xdata".into(),
160 data: xdata,
161 relocs: Vec::new(),
162 debug_rows: Vec::new(),
163 });
164 sections.push(Section {
165 name: ".pdata".into(),
166 data: pdata,
167 relocs: Vec::new(),
168 debug_rows: Vec::new(),
169 });
170 }
171 ObjectFormat::MachO => {}
172 }
173
174 let has_debug = !sections[0].debug_rows.is_empty() || mf.debug_line_start.is_some();
175 if has_debug {
176 let source = mf.debug_source.as_deref().unwrap_or("unknown.c");
177 let rows = if !sections[0].debug_rows.is_empty() {
178 sections[0].debug_rows.clone()
179 } else {
180 vec![DebugLineRow {
181 address: 0,
182 line: mf.debug_line_start.unwrap_or(1),
183 column: 0,
184 }]
185 };
186 match emitter.object_format() {
187 ObjectFormat::Elf => {
188 let line = build_debug_line(source, &rows);
189 let abbrev = build_debug_abbrev();
190 let loclists = build_debug_loclists(size);
191 let info = build_debug_info(source, &mf.name, size, 0, 0, 12);
192 sections.push(Section {
193 name: ".debug_abbrev".into(),
194 data: abbrev,
195 relocs: Vec::new(),
196 debug_rows: Vec::new(),
197 });
198 sections.push(Section {
199 name: ".debug_info".into(),
200 data: info,
201 relocs: Vec::new(),
202 debug_rows: Vec::new(),
203 });
204 sections.push(Section {
205 name: ".debug_line".into(),
206 data: line,
207 relocs: Vec::new(),
208 debug_rows: Vec::new(),
209 });
210 sections.push(Section {
211 name: ".debug_loclists".into(),
212 data: loclists,
213 relocs: Vec::new(),
214 debug_rows: Vec::new(),
215 });
216 }
217 ObjectFormat::Coff => {
218 let cv = build_codeview_debug_s(source, &rows);
219 sections.push(Section {
220 name: ".debug$S".into(),
221 data: cv,
222 relocs: Vec::new(),
223 debug_rows: Vec::new(),
224 });
225 }
226 ObjectFormat::MachO => {}
227 };
228 }
229 ObjectFile {
230 format: emitter.object_format(),
231 elf_machine: emitter.elf_machine(),
232 coff_machine: emitter.coff_machine(),
233 sections,
234 symbols: vec![sym],
235 }
236}
237
238fn serialize_elf(obj: &ObjectFile) -> Vec<u8> {
252 let text_sec = obj.sections.first();
253 let text_data = text_sec.map_or(&[][..], |s| s.data.as_slice());
254 let text_relocs = text_sec.map_or(&[][..], |s| s.relocs.as_slice());
255 let extra_secs = if obj.sections.len() > 1 {
256 &obj.sections[1..]
257 } else {
258 &[][..]
259 };
260 let has_relocs = !text_relocs.is_empty();
261
262 let mut shstrtab: Vec<u8> = vec![0u8];
263 let text_name_off = push_str(&mut shstrtab, b".text");
264 let extra_name_offs: Vec<u32> = extra_secs
265 .iter()
266 .map(|s| push_str(&mut shstrtab, s.name.as_bytes()))
267 .collect();
268 let symtab_name_off = push_str(&mut shstrtab, b".symtab");
269 let strtab_name_off = push_str(&mut shstrtab, b".strtab");
270 let shstrtab_name_off = push_str(&mut shstrtab, b".shstrtab");
271 let relatext_name_off = if has_relocs {
272 push_str(&mut shstrtab, b".rela.text")
273 } else {
274 0
275 };
276
277 let mut strtab: Vec<u8> = vec![0u8];
278 let sym_name_offs: Vec<u32> = obj
279 .symbols
280 .iter()
281 .map(|s| push_str(&mut strtab, s.name.as_bytes()))
282 .collect();
283
284 const ELF_HDR: u64 = 64;
285 const SH_ENT: u64 = 64;
286 const SYM_ENT: u64 = 24;
287 const RELA_ENT: u64 = 24;
288 const SHT_PROGBITS: u32 = 1;
289 const SHT_SYMTAB: u32 = 2;
290 const SHT_STRTAB: u32 = 3;
291 const SHT_RELA: u32 = 4;
292
293 let idx_text = 1u16;
294 let idx_extra_start = idx_text + 1;
295 let idx_symtab = idx_extra_start + extra_secs.len() as u16;
296 let idx_strtab = idx_symtab + 1;
297 let idx_shstrtab = idx_strtab + 1;
298 let idx_rela = idx_shstrtab + 1;
299
300 let num_sections: u16 = if has_relocs {
301 idx_rela + 1
302 } else {
303 idx_shstrtab + 1
304 };
305 let sh_table_size = num_sections as u64 * SH_ENT;
306
307 let mut cursor = ELF_HDR + sh_table_size;
308 let text_off = cursor;
309 let text_size = text_data.len() as u64;
310 cursor += text_size;
311
312 let mut extra_offs = Vec::with_capacity(extra_secs.len());
313 for sec in extra_secs {
314 extra_offs.push(cursor);
315 cursor += sec.data.len() as u64;
316 }
317
318 let sym_count = 1 + obj.symbols.len() as u64;
319 let symtab_off = cursor;
320 let symtab_size = sym_count * SYM_ENT;
321 cursor += symtab_size;
322
323 let strtab_off = cursor;
324 cursor += strtab.len() as u64;
325 let shstrtab_off = cursor;
326 cursor += shstrtab.len() as u64;
327
328 let relatext_off = cursor;
329 let relatext_size = text_relocs.len() as u64 * RELA_ENT;
330
331 let mut buf = Vec::<u8>::new();
332 buf.extend_from_slice(b"\x7fELF");
333 buf.push(2);
334 buf.push(1);
335 buf.push(1);
336 buf.push(0);
337 buf.extend_from_slice(&[0u8; 8]);
338 w16(&mut buf, 1);
339 w16(&mut buf, obj.elf_machine);
340 w32(&mut buf, 1);
341 w64(&mut buf, 0);
342 w64(&mut buf, 0);
343 w64(&mut buf, ELF_HDR);
344 w32(&mut buf, 0);
345 w16(&mut buf, ELF_HDR as u16);
346 w16(&mut buf, 0);
347 w16(&mut buf, 0);
348 w16(&mut buf, SH_ENT as u16);
349 w16(&mut buf, num_sections);
350 w16(&mut buf, idx_shstrtab);
351
352 let write_shdr = |buf: &mut Vec<u8>,
353 name: u32,
354 sh_type: u32,
355 flags: u64,
356 addr: u64,
357 off: u64,
358 size: u64,
359 link: u32,
360 info: u32,
361 align: u64,
362 entsize: u64| {
363 w32(buf, name);
364 w32(buf, sh_type);
365 w64(buf, flags);
366 w64(buf, addr);
367 w64(buf, off);
368 w64(buf, size);
369 w32(buf, link);
370 w32(buf, info);
371 w64(buf, align);
372 w64(buf, entsize);
373 };
374
375 write_shdr(&mut buf, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
376 write_shdr(
377 &mut buf,
378 text_name_off,
379 SHT_PROGBITS,
380 6,
381 0,
382 text_off,
383 text_size,
384 0,
385 0,
386 16,
387 0,
388 );
389
390 for (i, sec) in extra_secs.iter().enumerate() {
391 write_shdr(
392 &mut buf,
393 extra_name_offs[i],
394 SHT_PROGBITS,
395 0,
396 0,
397 extra_offs[i],
398 sec.data.len() as u64,
399 0,
400 0,
401 1,
402 0,
403 );
404 }
405
406 write_shdr(
407 &mut buf,
408 symtab_name_off,
409 SHT_SYMTAB,
410 0,
411 0,
412 symtab_off,
413 symtab_size,
414 idx_strtab as u32,
415 1,
416 8,
417 SYM_ENT,
418 );
419 write_shdr(
420 &mut buf,
421 strtab_name_off,
422 SHT_STRTAB,
423 0,
424 0,
425 strtab_off,
426 strtab.len() as u64,
427 0,
428 0,
429 1,
430 0,
431 );
432 write_shdr(
433 &mut buf,
434 shstrtab_name_off,
435 SHT_STRTAB,
436 0,
437 0,
438 shstrtab_off,
439 shstrtab.len() as u64,
440 0,
441 0,
442 1,
443 0,
444 );
445 if has_relocs {
446 write_shdr(
447 &mut buf,
448 relatext_name_off,
449 SHT_RELA,
450 0,
451 0,
452 relatext_off,
453 relatext_size,
454 idx_symtab as u32,
455 idx_text as u32,
456 8,
457 RELA_ENT,
458 );
459 }
460
461 buf.extend_from_slice(text_data);
462 for sec in extra_secs {
463 buf.extend_from_slice(&sec.data);
464 }
465
466 buf.extend_from_slice(&[0u8; 24]);
467 for (i, sym) in obj.symbols.iter().enumerate() {
468 let st_info: u8 = (1u8 << 4) | 2u8;
469 let st_shndx: u16 = (sym.section + 1) as u16;
470 w32(&mut buf, sym_name_offs[i]);
471 buf.push(st_info);
472 buf.push(0);
473 w16(&mut buf, st_shndx);
474 w64(&mut buf, sym.offset);
475 w64(&mut buf, sym.size);
476 }
477
478 buf.extend_from_slice(&strtab);
479 buf.extend_from_slice(&shstrtab);
480
481 if has_relocs {
482 for reloc in text_relocs {
483 let sym_idx = (reloc.symbol + 1) as u64;
484 let r_type: u64 = match reloc.kind {
485 RelocKind::Pc32 => 2,
486 RelocKind::Abs64 => 1,
487 };
488 let r_info = (sym_idx << 32) | r_type;
489 w64(&mut buf, reloc.offset);
490 w64(&mut buf, r_info);
491 buf.extend_from_slice(&reloc.addend.to_le_bytes());
492 }
493 }
494 buf
495}
496
497fn build_debug_line(source_file: &str, rows: &[DebugLineRow]) -> Vec<u8> {
498 let file = source_file.rsplit('/').next().unwrap_or(source_file);
499
500 let mut header_body = Vec::<u8>::new();
501 header_body.push(1); header_body.push(1); header_body.push((-5i8) as u8); header_body.push(14); header_body.push(13); header_body.extend_from_slice(&[0, 1, 1, 1, 1, 0, 0, 0, 1, 0, 0, 1]); header_body.push(0); header_body.extend_from_slice(file.as_bytes());
509 header_body.push(0); write_uleb128(&mut header_body, 0); write_uleb128(&mut header_body, 0); write_uleb128(&mut header_body, 0); header_body.push(0); let mut program = Vec::<u8>::new();
516 let mut sorted = rows.to_vec();
517 sorted.sort_by_key(|r| r.address);
518 let mut cur_addr = 0u64;
519 let mut cur_line = 1u32;
520 let mut cur_col = 0u32;
521 for row in sorted {
522 if row.address > cur_addr {
523 program.push(2); write_uleb128(&mut program, row.address - cur_addr);
525 cur_addr = row.address;
526 }
527 if row.line != cur_line {
528 program.push(3); write_sleb128(&mut program, row.line as i64 - cur_line as i64);
530 cur_line = row.line;
531 }
532 if row.column != cur_col {
533 program.push(5); write_uleb128(&mut program, row.column as u64);
535 cur_col = row.column;
536 }
537 program.push(1); }
539 program.push(0);
540 program.push(1);
541 program.push(1); let unit_length = (2 + 4 + header_body.len() + program.len()) as u32;
544 let mut out = Vec::<u8>::new();
545 w32(&mut out, unit_length);
546 w16(&mut out, 2); w32(&mut out, header_body.len() as u32);
548 out.extend_from_slice(&header_body);
549 out.extend_from_slice(&program);
550 out
551}
552
553fn build_debug_abbrev() -> Vec<u8> {
554 const DW_TAG_COMPILE_UNIT: u64 = 0x11;
560 const DW_TAG_SUBPROGRAM: u64 = 0x2e;
561 const DW_TAG_VARIABLE: u64 = 0x34;
562 const DW_TAG_BASE_TYPE: u64 = 0x24;
563
564 const DW_CHILDREN_NO: u8 = 0x00;
565 const DW_CHILDREN_YES: u8 = 0x01;
566
567 const DW_AT_NAME: u64 = 0x03;
568 const DW_AT_STMT_LIST: u64 = 0x10;
569 const DW_AT_LOW_PC: u64 = 0x11;
570 const DW_AT_HIGH_PC: u64 = 0x12;
571 const DW_AT_COMP_DIR: u64 = 0x1b;
572 const DW_AT_ENCODING: u64 = 0x3e;
573 const DW_AT_BYTE_SIZE: u64 = 0x0b;
574 const DW_AT_LOCLISTS_BASE: u64 = 0x8c;
575
576 const DW_FORM_ADDR: u64 = 0x01;
577 const DW_FORM_DATA1: u64 = 0x0b;
578 const DW_FORM_DATA8: u64 = 0x07;
579 const DW_FORM_STRING: u64 = 0x08;
580 const DW_FORM_SEC_OFFSET: u64 = 0x17;
581
582 let mut out = Vec::new();
583
584 write_uleb128(&mut out, 1);
586 write_uleb128(&mut out, DW_TAG_COMPILE_UNIT);
587 out.push(DW_CHILDREN_YES);
588 write_uleb128(&mut out, DW_AT_NAME);
589 write_uleb128(&mut out, DW_FORM_STRING);
590 write_uleb128(&mut out, DW_AT_STMT_LIST);
591 write_uleb128(&mut out, DW_FORM_SEC_OFFSET);
592 write_uleb128(&mut out, DW_AT_COMP_DIR);
593 write_uleb128(&mut out, DW_FORM_STRING);
594 write_uleb128(&mut out, DW_AT_LOW_PC);
595 write_uleb128(&mut out, DW_FORM_ADDR);
596 write_uleb128(&mut out, DW_AT_HIGH_PC);
597 write_uleb128(&mut out, DW_FORM_DATA8);
598 write_uleb128(&mut out, DW_AT_LOCLISTS_BASE);
599 write_uleb128(&mut out, DW_FORM_SEC_OFFSET);
600 out.push(0);
601 out.push(0);
602
603 write_uleb128(&mut out, 2);
605 write_uleb128(&mut out, DW_TAG_SUBPROGRAM);
606 out.push(DW_CHILDREN_YES);
607 write_uleb128(&mut out, DW_AT_NAME);
608 write_uleb128(&mut out, DW_FORM_STRING);
609 write_uleb128(&mut out, DW_AT_LOW_PC);
610 write_uleb128(&mut out, DW_FORM_ADDR);
611 write_uleb128(&mut out, DW_AT_HIGH_PC);
612 write_uleb128(&mut out, DW_FORM_DATA8);
613 out.push(0);
614 out.push(0);
615
616 write_uleb128(&mut out, 3);
618 write_uleb128(&mut out, DW_TAG_VARIABLE);
619 out.push(DW_CHILDREN_NO);
620 write_uleb128(&mut out, DW_AT_NAME);
621 write_uleb128(&mut out, DW_FORM_STRING);
622 out.push(0);
623 out.push(0);
624
625 write_uleb128(&mut out, 4);
627 write_uleb128(&mut out, DW_TAG_BASE_TYPE);
628 out.push(DW_CHILDREN_NO);
629 write_uleb128(&mut out, DW_AT_NAME);
630 write_uleb128(&mut out, DW_FORM_STRING);
631 write_uleb128(&mut out, DW_AT_ENCODING);
632 write_uleb128(&mut out, DW_FORM_DATA1);
633 write_uleb128(&mut out, DW_AT_BYTE_SIZE);
634 write_uleb128(&mut out, DW_FORM_DATA1);
635 out.push(0);
636 out.push(0);
637
638 out.push(0);
640 out
641}
642
643fn build_debug_info(
644 source_file: &str,
645 fn_name: &str,
646 text_size: u64,
647 stmt_list_off: u32,
648 _abbrev_off: u32,
649 _loclists_var_off: u32,
650) -> Vec<u8> {
651 const DWARF_VERSION: u16 = 5;
652 const DW_UT_COMPILE: u8 = 0x01;
653 const DW_ATE_SIGNED: u8 = 0x05;
654
655 let file = source_file.rsplit('/').next().unwrap_or(source_file);
656 let comp_dir = source_file
657 .rfind('/')
658 .map(|i| &source_file[..i])
659 .filter(|s| !s.is_empty())
660 .unwrap_or(".");
661
662 let mut body = Vec::new();
663
664 write_uleb128(&mut body, 1);
666 body.extend_from_slice(file.as_bytes());
667 body.push(0);
668 w32(&mut body, stmt_list_off);
669 body.extend_from_slice(comp_dir.as_bytes());
670 body.push(0);
671 w64(&mut body, 0); w64(&mut body, text_size); w32(&mut body, 0); write_uleb128(&mut body, 2);
677 body.extend_from_slice(fn_name.as_bytes());
678 body.push(0);
679 w64(&mut body, 0);
680 w64(&mut body, text_size);
681
682 write_uleb128(&mut body, 3);
684 body.extend_from_slice(b"result");
685 body.push(0);
686
687 body.push(0);
689
690 write_uleb128(&mut body, 4);
692 body.extend_from_slice(b"i64");
693 body.push(0);
694 body.push(DW_ATE_SIGNED);
695 body.push(8);
696
697 body.push(0);
699
700 let mut out = Vec::new();
701 let unit_length = (2 + 1 + 1 + 4 + body.len()) as u32;
702 w32(&mut out, unit_length);
703 w16(&mut out, DWARF_VERSION);
704 out.push(DW_UT_COMPILE);
705 out.push(8); w32(&mut out, 0); out.extend_from_slice(&body);
708 out
709}
710
711fn build_debug_loclists(text_size: u64) -> Vec<u8> {
712 const DW_LLE_END_OF_LIST: u8 = 0x00;
718 const DW_LLE_OFFSET_PAIR: u8 = 0x04;
719 const DW_OP_REG0: u8 = 0x50;
720
721 let mut body = Vec::new();
722 body.push(DW_LLE_OFFSET_PAIR);
723 write_uleb128(&mut body, 0);
724 write_uleb128(&mut body, text_size.max(1));
725 body.push(1); body.push(DW_OP_REG0);
727 body.push(DW_LLE_END_OF_LIST);
728
729 let mut out = Vec::new();
730 let unit_length = (2 + 1 + 1 + 4 + body.len()) as u32;
731 w32(&mut out, unit_length);
732 w16(&mut out, 5); out.push(8); out.push(0); w32(&mut out, 0); out.extend_from_slice(&body);
737 out
738}
739
740fn build_eh_frame(text_size: u64, frame_size: u32, used_callee_saved: &[PReg]) -> Vec<u8> {
741 let mut out = Vec::new();
744
745 let mut cie = Vec::new();
746 cie.push(1); cie.extend_from_slice(b"zR\0"); write_uleb128(&mut cie, 1); write_sleb128(&mut cie, -8); write_uleb128(&mut cie, 16); write_uleb128(&mut cie, 1); cie.push(0x1b); cie.push(0x0c); write_uleb128(&mut cie, 7); write_uleb128(&mut cie, 8);
758 cie.push(0x90); write_uleb128(&mut cie, 1);
760
761 w32(&mut out, cie.len() as u32 + 4);
762 w32(&mut out, 0); out.extend_from_slice(&cie);
764 while out.len() % 8 != 0 {
765 out.push(0);
766 }
767
768 let fde_start = out.len();
769 let mut fde = Vec::new();
770 w32(&mut fde, 0); w32(&mut fde, text_size.max(1) as u32); let mut fde_prog = Vec::new();
775 let mut cfa_off = 8u64;
776
777 let pushes = if frame_size > 0 || !used_callee_saved.is_empty() {
779 1 + used_callee_saved.len() as u64 } else {
781 0
782 };
783
784 if pushes > 0 {
785 cfa_off += pushes * 8;
786 fde_prog.push(0x0e); write_uleb128(&mut fde_prog, cfa_off);
788
789 fde_prog.push(0x86); write_uleb128(&mut fde_prog, 2);
792
793 for (idx, pr) in used_callee_saved.iter().enumerate() {
794 let reg = pr.0 as u8;
795 if reg <= 0x3f {
796 fde_prog.push(0x80 | reg); write_uleb128(&mut fde_prog, 3 + idx as u64); }
799 }
800
801 fde_prog.push(0x0d); write_uleb128(&mut fde_prog, 6); }
805
806 if frame_size > 0 {
807 cfa_off += frame_size as u64;
808 fde_prog.push(0x0e); write_uleb128(&mut fde_prog, cfa_off);
810 }
811
812 write_uleb128(&mut fde, fde_prog.len() as u64); fde.extend_from_slice(&fde_prog);
814
815 w32(&mut out, fde.len() as u32 + 4);
816 let cie_ptr = fde_start as u32;
817 w32(&mut out, cie_ptr); out.extend_from_slice(&fde);
819 while out.len() % 8 != 0 {
820 out.push(0);
821 }
822
823 w32(&mut out, 0); out
825}
826
827fn build_coff_unwind_tables(text_size: u64, frame_size: u32, used_callee_saved: &[PReg]) -> (Vec<u8>, Vec<u8>) {
828 let has_frame = frame_size > 0 || !used_callee_saved.is_empty();
831 let mut codes: Vec<(u8, u8, u16)> = Vec::new();
832
833 if has_frame {
834 codes.push((1, 0, 5)); }
837
838 let alloc_size = if frame_size == 0 { 0 } else { ((frame_size as u32 + 15) / 16) * 16 };
839 if alloc_size > 0 && alloc_size <= 128 {
840 let info = ((alloc_size / 8) - 1) as u16;
842 codes.push((4, 2, info));
843 }
844
845 let count_of_codes = codes.len() as u8;
846 let mut xdata = Vec::new();
847 xdata.push(0x01); xdata.push(if has_frame { 4 } else { 0 }); xdata.push(count_of_codes); xdata.push(if has_frame { 5 } else { 0 }); for (code_off, unwind_op, op_info) in &codes {
853 xdata.push(*code_off);
854 xdata.push(((*op_info as u8) << 4) | (*unwind_op & 0x0f));
855 }
856
857 while xdata.len() % 4 != 0 {
859 xdata.push(0);
860 }
861
862 let mut pdata = Vec::new();
865 w32(&mut pdata, 0);
866 w32(&mut pdata, text_size.max(1) as u32);
867 w32(&mut pdata, 0); (xdata, pdata)
870}
871
872fn serialize_macho(obj: &ObjectFile) -> Vec<u8> {
887 let text_data = obj.sections.first().map_or(&[][..], |s| s.data.as_slice());
888 let text_size = text_data.len() as u32;
889 let text_relocs = obj
890 .sections
891 .first()
892 .map_or(&[][..], |s| s.relocs.as_slice());
893
894 const MH_HDR: u32 = 32;
895 const SEG_CMD: u32 = 72;
896 const SECT_HDR: u32 = 80;
897 const SYMTAB_CMD: u32 = 24;
898 const DYSYMTAB_CMD: u32 = 80;
899 const SYM_ENT: u32 = 16; let header_size = MH_HDR + SEG_CMD + SECT_HDR + SYMTAB_CMD + DYSYMTAB_CMD;
902 let cmds_size = SEG_CMD + SECT_HDR + SYMTAB_CMD + DYSYMTAB_CMD;
903
904 let text_align = 16u32;
906 let text_pad = (text_align - (header_size % text_align)) % text_align;
907 let text_off = header_size + text_pad;
908 let reloc_off = text_off + text_size;
909 let reloc_size = text_relocs.len() as u32 * 8;
910
911 let mut strtab: Vec<u8> = vec![0u8];
913 let sym_name_offs: Vec<u32> = obj
914 .symbols
915 .iter()
916 .map(|s| {
917 let off = strtab.len() as u32;
918 strtab.push(b'_'); strtab.extend_from_slice(s.name.as_bytes());
920 strtab.push(0);
921 off
922 })
923 .collect();
924 while strtab.len() % 4 != 0 {
925 strtab.push(0);
926 } let symtab_off = reloc_off + reloc_size;
929 let symtab_size = obj.symbols.len() as u32 * SYM_ENT;
930 let strtab_off = symtab_off + symtab_size;
931
932 let mut buf = Vec::<u8>::new();
933
934 w32(&mut buf, 0xfeedfacf); w32(&mut buf, 0x01000007); w32(&mut buf, 0x00000003); w32(&mut buf, 1); w32(&mut buf, 3); w32(&mut buf, cmds_size); w32(&mut buf, 0); w32(&mut buf, 0); w32(&mut buf, 0x19); w32(&mut buf, SEG_CMD + SECT_HDR); buf.extend_from_slice(b"__TEXT\0\0\0\0\0\0\0\0\0\0"); w64(&mut buf, 0); w64(&mut buf, text_size as u64); w64(&mut buf, text_off as u64); w64(&mut buf, text_size as u64); w32(&mut buf, 7); w32(&mut buf, 5); w32(&mut buf, 1); w32(&mut buf, 0); buf.extend_from_slice(b"__text\0\0\0\0\0\0\0\0\0\0"); buf.extend_from_slice(b"__TEXT\0\0\0\0\0\0\0\0\0\0"); w64(&mut buf, 0); w64(&mut buf, text_size as u64); w32(&mut buf, text_off); w32(&mut buf, 4); w32(&mut buf, reloc_off); w32(&mut buf, text_relocs.len() as u32); w32(&mut buf, 0x80000400); w32(&mut buf, 0);
968 w32(&mut buf, 0);
969 w32(&mut buf, 0); w32(&mut buf, 2); w32(&mut buf, SYMTAB_CMD);
974 w32(&mut buf, symtab_off);
975 w32(&mut buf, obj.symbols.len() as u32);
976 w32(&mut buf, strtab_off);
977 w32(&mut buf, strtab.len() as u32);
978
979 w32(&mut buf, 0xB); w32(&mut buf, DYSYMTAB_CMD);
982 let n_globals = obj.symbols.iter().filter(|s| s.global).count() as u32;
983 w32(&mut buf, 0);
984 w32(&mut buf, 0); w32(&mut buf, 0);
986 w32(&mut buf, n_globals); w32(&mut buf, n_globals);
988 w32(&mut buf, 0); buf.extend_from_slice(&[0u8; 48]); buf.resize(buf.len() + text_pad as usize, 0);
993
994 buf.extend_from_slice(text_data);
996
997 for reloc in text_relocs {
999 let sym_idx = reloc.symbol as u32;
1000 let (r_type, r_length, r_pcrel): (u32, u32, u32) = match reloc.kind {
1001 RelocKind::Pc32 => (2, 2, 1), RelocKind::Abs64 => (0, 3, 0), };
1004 let r_extern: u32 = 1;
1005 let r_info =
1006 sym_idx | (r_pcrel << 24) | (r_length << 25) | (r_extern << 27) | (r_type << 28);
1007 w32(&mut buf, reloc.offset as u32); w32(&mut buf, r_info);
1009 }
1010
1011 for (i, sym) in obj.symbols.iter().enumerate() {
1013 let n_type: u8 = if sym.global { 0x0F } else { 0x0E }; w32(&mut buf, sym_name_offs[i]); buf.push(n_type); buf.push(1); w16(&mut buf, 0); w64(&mut buf, sym.offset); }
1020
1021 buf.extend_from_slice(&strtab);
1023
1024 buf
1025}
1026
1027fn serialize_coff(obj: &ObjectFile) -> Vec<u8> {
1030 const FILE_HEADER_SIZE: usize = 20;
1031 const SECTION_HEADER_SIZE: usize = 40;
1032 const RELOC_SIZE: usize = 10;
1033 const SYMBOL_SIZE: usize = 18;
1034
1035 let nsec = obj.sections.len();
1036 let sec_headers_size = nsec * SECTION_HEADER_SIZE;
1037 let sec_data_start = FILE_HEADER_SIZE + sec_headers_size;
1038
1039 let mut data_ptr = sec_data_start as u32;
1040 let mut sec_raw_ptrs = Vec::with_capacity(nsec);
1041 let mut sec_reloc_ptrs = Vec::with_capacity(nsec);
1042 for sec in &obj.sections {
1043 let raw_size = sec.data.len() as u32;
1044 let reloc_size = (sec.relocs.len() * RELOC_SIZE) as u32;
1045 sec_raw_ptrs.push(data_ptr);
1046 data_ptr = data_ptr.wrapping_add(raw_size);
1047 sec_reloc_ptrs.push(if reloc_size > 0 { data_ptr } else { 0 });
1048 data_ptr = data_ptr.wrapping_add(reloc_size);
1049 }
1050
1051 let symtab_ptr = data_ptr;
1052 let nsym = obj.symbols.len() as u32;
1053 let mut strtab: Vec<u8> = vec![0, 0, 0, 0];
1055 let mut section_name_offs = Vec::with_capacity(nsec);
1056 let mut symbol_name_offs = Vec::with_capacity(obj.symbols.len());
1057 for sec in &obj.sections {
1058 section_name_offs.push(append_coff_string(&mut strtab, &sec.name));
1059 }
1060 for sym in &obj.symbols {
1061 symbol_name_offs.push(append_coff_string(&mut strtab, &sym.name));
1062 }
1063 let strtab_size = strtab.len() as u32;
1064 strtab[0..4].copy_from_slice(&strtab_size.to_le_bytes());
1065
1066 let total_est = symtab_ptr as usize + nsym as usize * SYMBOL_SIZE + strtab.len();
1067 let mut buf = Vec::with_capacity(total_est);
1068
1069 w16(&mut buf, obj.coff_machine); w16(&mut buf, nsec as u16); w32(&mut buf, 0); w32(&mut buf, symtab_ptr); w32(&mut buf, nsym); w16(&mut buf, 0); w16(&mut buf, 0); for (i, sec) in obj.sections.iter().enumerate() {
1080 write_coff_name_field(&mut buf, &sec.name, section_name_offs[i]);
1081 w32(&mut buf, 0); w32(&mut buf, 0); w32(&mut buf, sec.data.len() as u32); w32(&mut buf, sec_raw_ptrs[i]); w32(&mut buf, sec_reloc_ptrs[i]); w32(&mut buf, 0); w16(&mut buf, sec.relocs.len() as u16); w16(&mut buf, 0); w32(&mut buf, coff_section_characteristics(&sec.name));
1090 }
1091
1092 for sec in &obj.sections {
1094 buf.extend_from_slice(&sec.data);
1095 for reloc in &sec.relocs {
1096 w32(&mut buf, reloc.offset as u32); w32(&mut buf, reloc.symbol as u32); let typ = match reloc.kind {
1099 RelocKind::Pc32 => 0x0004, RelocKind::Abs64 => 0x0001, };
1102 w16(&mut buf, typ);
1103 }
1104 }
1105
1106 for (i, sym) in obj.symbols.iter().enumerate() {
1108 write_coff_name_field(&mut buf, &sym.name, symbol_name_offs[i]);
1109 w32(&mut buf, sym.offset as u32); w16(&mut buf, (sym.section + 1) as u16); w16(&mut buf, 0); buf.push(if sym.global { 2 } else { 3 }); buf.push(0); }
1115
1116 buf.extend_from_slice(&strtab);
1118 buf
1119}
1120
1121fn append_coff_string(strtab: &mut Vec<u8>, s: &str) -> u32 {
1122 let off = strtab.len() as u32;
1123 strtab.extend_from_slice(s.as_bytes());
1124 strtab.push(0);
1125 off
1126}
1127
1128fn write_coff_name_field(buf: &mut Vec<u8>, name: &str, strtab_off: u32) {
1129 if name.len() <= 8 {
1130 let mut raw = [0u8; 8];
1131 raw[..name.len()].copy_from_slice(name.as_bytes());
1132 buf.extend_from_slice(&raw);
1133 } else {
1134 let tag = format!("/{}", strtab_off);
1135 let mut raw = [0u8; 8];
1136 let bytes = tag.as_bytes();
1137 let n = bytes.len().min(8);
1138 raw[..n].copy_from_slice(&bytes[..n]);
1139 buf.extend_from_slice(&raw);
1140 }
1141}
1142
1143fn coff_section_characteristics(name: &str) -> u32 {
1144 if name == ".text" {
1145 0x60000020 } else if name.starts_with(".debug") {
1147 0x42000040 } else {
1149 0x40000040 }
1151}
1152
1153fn build_codeview_debug_s(source_file: &str, rows: &[DebugLineRow]) -> Vec<u8> {
1154 let mut out = Vec::new();
1156 w32(&mut out, 4);
1157
1158 let mut payload = Vec::new();
1161 payload.extend_from_slice(
1162 source_file
1163 .rsplit('/')
1164 .next()
1165 .unwrap_or(source_file)
1166 .as_bytes(),
1167 );
1168 payload.push(0);
1169
1170 let min_line = rows.iter().map(|r| r.line).min().unwrap_or(1);
1171 let max_line = rows.iter().map(|r| r.line).max().unwrap_or(min_line);
1172 w32(&mut payload, min_line);
1173 w32(&mut payload, max_line);
1174
1175 w32(&mut out, 0xF1);
1177 w32(&mut out, payload.len() as u32);
1178 out.extend_from_slice(&payload);
1179 while out.len() % 4 != 0 {
1180 out.push(0);
1181 }
1182 out
1183}
1184
1185#[inline]
1188fn w16(buf: &mut Vec<u8>, v: u16) {
1189 buf.extend_from_slice(&v.to_le_bytes());
1190}
1191#[inline]
1192fn w32(buf: &mut Vec<u8>, v: u32) {
1193 buf.extend_from_slice(&v.to_le_bytes());
1194}
1195#[inline]
1196fn w64(buf: &mut Vec<u8>, v: u64) {
1197 buf.extend_from_slice(&v.to_le_bytes());
1198}
1199
1200fn write_uleb128(buf: &mut Vec<u8>, mut v: u64) {
1201 loop {
1202 let mut byte = (v & 0x7f) as u8;
1203 v >>= 7;
1204 if v != 0 {
1205 byte |= 0x80;
1206 }
1207 buf.push(byte);
1208 if v == 0 {
1209 break;
1210 }
1211 }
1212}
1213
1214fn write_sleb128(buf: &mut Vec<u8>, mut v: i64) {
1215 loop {
1216 let byte = (v as u8) & 0x7f;
1217 let sign = (byte & 0x40) != 0;
1218 v >>= 7;
1219 let done = (v == 0 && !sign) || (v == -1 && sign);
1220 if done {
1221 buf.push(byte);
1222 break;
1223 }
1224 buf.push(byte | 0x80);
1225 }
1226}
1227
1228fn push_str(table: &mut Vec<u8>, s: &[u8]) -> u32 {
1230 let off = table.len() as u32;
1231 table.extend_from_slice(s);
1232 table.push(0);
1233 off
1234}
1235
1236#[cfg(test)]
1239mod tests {
1240 use super::*;
1241
1242 fn make_obj(fmt: ObjectFormat, code: Vec<u8>) -> ObjectFile {
1243 let section_name = match fmt {
1244 ObjectFormat::Elf => ".text",
1245 ObjectFormat::MachO => "__text",
1246 ObjectFormat::Coff => ".text",
1247 };
1248 ObjectFile {
1249 format: fmt,
1250 elf_machine: 62,
1251 coff_machine: 0x8664,
1252 sections: vec![Section {
1253 name: section_name.into(),
1254 data: code,
1255 relocs: vec![],
1256 debug_rows: vec![],
1257 }],
1258 symbols: vec![Symbol {
1259 name: "f".into(),
1260 section: 0,
1261 offset: 0,
1262 size: 1,
1263 global: true,
1264 }],
1265 }
1266 }
1267
1268 #[test]
1269 fn elf_magic_and_class() {
1270 let bytes = make_obj(ObjectFormat::Elf, vec![0x90]).to_bytes();
1271 assert_eq!(&bytes[0..4], b"\x7fELF", "ELF magic");
1272 assert_eq!(bytes[4], 2, "64-bit");
1273 assert_eq!(bytes[5], 1, "little-endian");
1274 }
1275
1276 #[test]
1277 fn elf_machine_x86_64() {
1278 let bytes = make_obj(ObjectFormat::Elf, vec![0x90]).to_bytes();
1279 let e_machine = u16::from_le_bytes([bytes[18], bytes[19]]);
1280 assert_eq!(e_machine, 62, "EM_X86_64 = 62");
1281 }
1282
1283 #[test]
1284 fn elf_relocatable_type() {
1285 let bytes = make_obj(ObjectFormat::Elf, vec![0x90]).to_bytes();
1286 let e_type = u16::from_le_bytes([bytes[16], bytes[17]]);
1287 assert_eq!(e_type, 1, "ET_REL = 1");
1288 }
1289
1290 #[test]
1291 fn macho_magic() {
1292 let bytes = make_obj(ObjectFormat::MachO, vec![0xc3]).to_bytes();
1293 assert_eq!(&bytes[0..4], &[0xcf, 0xfa, 0xed, 0xfe], "MH_MAGIC_64");
1294 }
1295
1296 #[test]
1297 fn macho_filetype_object() {
1298 let bytes = make_obj(ObjectFormat::MachO, vec![0xc3]).to_bytes();
1299 let filetype = u32::from_le_bytes([bytes[12], bytes[13], bytes[14], bytes[15]]);
1300 assert_eq!(filetype, 1, "MH_OBJECT = 1");
1301 }
1302
1303 #[test]
1304 fn macho_strtab_first_byte_is_null() {
1305 let bytes = make_obj(ObjectFormat::MachO, vec![0xc3]).to_bytes();
1310 let strtab_first = bytes[bytes.len() - 4];
1312 assert_eq!(
1313 strtab_first, 0x00,
1314 "Mach-O strtab[0] must be null (\\0), was 0x{:02x}",
1315 strtab_first
1316 );
1317 }
1318
1319 #[test]
1320 fn coff_machine_x86_64() {
1321 let bytes = make_obj(ObjectFormat::Coff, vec![0x90]).to_bytes();
1322 let machine = u16::from_le_bytes([bytes[0], bytes[1]]);
1323 assert_eq!(machine, 0x8664, "IMAGE_FILE_MACHINE_AMD64");
1324 }
1325
1326 #[test]
1327 fn coff_has_text_section_header() {
1328 let bytes = make_obj(ObjectFormat::Coff, vec![0x90]).to_bytes();
1329 let sec_count = u16::from_le_bytes([bytes[2], bytes[3]]) as usize;
1330 assert_eq!(sec_count, 1);
1331 let sec_name = &bytes[20..28];
1332 assert_eq!(sec_name, b".text\0\0\0");
1333 }
1334
1335 #[test]
1336 fn emit_object_roundtrip() {
1337 use crate::isel::{MachineBlock, MachineFunction};
1338
1339 struct NopEmitter;
1340 impl Emitter for NopEmitter {
1341 fn emit_function(&mut self, mf: &MachineFunction) -> Section {
1342 let _ = mf;
1343 Section {
1344 name: ".text".into(),
1345 data: vec![0x90],
1346 relocs: vec![],
1347 debug_rows: vec![],
1348 }
1349 }
1350 fn object_format(&self) -> ObjectFormat {
1351 ObjectFormat::Elf
1352 }
1353 }
1354
1355 let mut mf = MachineFunction::new("test".into());
1356 mf.blocks.push(MachineBlock {
1357 label: "entry".into(),
1358 instrs: vec![],
1359 });
1360 let obj = emit_object(&mf, &mut NopEmitter);
1361 assert_eq!(obj.symbols[0].name, "test");
1362 assert_eq!(obj.sections[0].data, vec![0x90]);
1363 assert_eq!(obj.elf_machine, 62);
1364 assert_eq!(obj.coff_machine, 0x8664);
1365 }
1366
1367 #[test]
1368 fn emit_object_adds_debug_line_section_for_elf() {
1369 use crate::isel::{MachineBlock, MachineFunction};
1370
1371 struct NopEmitter;
1372 impl Emitter for NopEmitter {
1373 fn emit_function(&mut self, _mf: &MachineFunction) -> Section {
1374 Section {
1375 name: ".text".into(),
1376 data: vec![0x90],
1377 relocs: vec![],
1378 debug_rows: vec![],
1379 }
1380 }
1381 fn object_format(&self) -> ObjectFormat {
1382 ObjectFormat::Elf
1383 }
1384 }
1385
1386 let mut mf = MachineFunction::new("dbg".into());
1387 mf.blocks.push(MachineBlock {
1388 label: "entry".into(),
1389 instrs: vec![],
1390 });
1391 mf.debug_source = Some("foo.c".into());
1392 mf.debug_line_start = Some(17);
1393
1394 let obj = emit_object(&mf, &mut NopEmitter);
1395 assert!(obj.sections.iter().any(|s| s.name == ".debug_line"));
1396 assert!(obj.sections.iter().any(|s| s.name == ".debug_info"));
1397 assert!(obj.sections.iter().any(|s| s.name == ".debug_abbrev"));
1398 assert!(obj.sections.iter().any(|s| s.name == ".debug_loclists"));
1399 let bytes = obj.to_bytes();
1400 assert!(bytes.windows(11).any(|w| w == b".debug_line"));
1401 assert!(bytes.windows(11).any(|w| w == b".debug_info"));
1402 assert!(bytes.windows(13).any(|w| w == b".debug_abbrev"));
1403 assert!(bytes.windows(15).any(|w| w == b".debug_loclists"));
1404 }
1405
1406 #[test]
1407 fn emit_object_adds_eh_frame_for_elf() {
1408 use crate::isel::{MachineBlock, MachineFunction};
1409
1410 struct NopEmitter;
1411 impl Emitter for NopEmitter {
1412 fn emit_function(&mut self, _mf: &MachineFunction) -> Section {
1413 Section {
1414 name: ".text".into(),
1415 data: vec![0x90, 0xC3],
1416 relocs: vec![],
1417 debug_rows: vec![],
1418 }
1419 }
1420 fn object_format(&self) -> ObjectFormat {
1421 ObjectFormat::Elf
1422 }
1423 }
1424
1425 let mut mf = MachineFunction::new("eh".into());
1426 mf.blocks.push(MachineBlock {
1427 label: "entry".into(),
1428 instrs: vec![],
1429 });
1430
1431 let obj = emit_object(&mf, &mut NopEmitter);
1432 let eh = obj
1433 .sections
1434 .iter()
1435 .find(|s| s.name == ".eh_frame")
1436 .expect(".eh_frame section");
1437 assert!(!eh.data.is_empty());
1438 let bytes = obj.to_bytes();
1439 assert!(bytes.windows(9).any(|w| w == b".eh_frame"));
1440 }
1441
1442 #[test]
1443 fn emit_object_adds_unwind_tables_for_coff() {
1444 use crate::isel::{MachineBlock, MachineFunction};
1445
1446 struct NopEmitter;
1447 impl Emitter for NopEmitter {
1448 fn emit_function(&mut self, _mf: &MachineFunction) -> Section {
1449 Section {
1450 name: ".text".into(),
1451 data: vec![0x90],
1452 relocs: vec![],
1453 debug_rows: vec![],
1454 }
1455 }
1456 fn object_format(&self) -> ObjectFormat {
1457 ObjectFormat::Coff
1458 }
1459 }
1460
1461 let mut mf = MachineFunction::new("seh".into());
1462 mf.blocks.push(MachineBlock {
1463 label: "entry".into(),
1464 instrs: vec![],
1465 });
1466
1467 let obj = emit_object(&mf, &mut NopEmitter);
1468 assert!(obj.sections.iter().any(|s| s.name == ".xdata"));
1469 assert!(obj.sections.iter().any(|s| s.name == ".pdata"));
1470 let bytes = obj.to_bytes();
1471 assert!(bytes.windows(6).any(|w| w == b".xdata"));
1472 assert!(bytes.windows(6).any(|w| w == b".pdata"));
1473 }
1474
1475 #[test]
1476 fn eh_frame_reflects_frame_facts_when_present() {
1477 use crate::isel::{MachineBlock, MachineFunction};
1478
1479 struct NopEmitter;
1480 impl Emitter for NopEmitter {
1481 fn emit_function(&mut self, _mf: &MachineFunction) -> Section {
1482 Section {
1483 name: ".text".into(),
1484 data: vec![0x90, 0x90, 0xC3],
1485 relocs: vec![],
1486 debug_rows: vec![],
1487 }
1488 }
1489 fn object_format(&self) -> ObjectFormat {
1490 ObjectFormat::Elf
1491 }
1492 }
1493
1494 let mut mf = MachineFunction::new("eh-facts".into());
1495 mf.blocks.push(MachineBlock {
1496 label: "entry".into(),
1497 instrs: vec![],
1498 });
1499 mf.frame_size = 16;
1500 mf.used_callee_saved = vec![PReg(3), PReg(12)]; let obj = emit_object(&mf, &mut NopEmitter);
1503 let eh = obj
1504 .sections
1505 .iter()
1506 .find(|s| s.name == ".eh_frame")
1507 .expect(".eh_frame section")
1508 .data
1509 .clone();
1510
1511 assert!(eh.contains(&0x0e), "expected DW_CFA_def_cfa_offset in frame-aware FDE");
1513 assert!(eh.contains(&0x0d), "expected DW_CFA_def_cfa_register in frame-aware FDE");
1515 }
1516
1517 #[test]
1518 fn eh_frame_has_expected_cie_fde_shape() {
1519 use crate::isel::{MachineBlock, MachineFunction};
1520
1521 struct NopEmitter;
1522 impl Emitter for NopEmitter {
1523 fn emit_function(&mut self, _mf: &MachineFunction) -> Section {
1524 Section {
1525 name: ".text".into(),
1526 data: vec![0x90, 0x90, 0xC3],
1527 relocs: vec![],
1528 debug_rows: vec![],
1529 }
1530 }
1531 fn object_format(&self) -> ObjectFormat {
1532 ObjectFormat::Elf
1533 }
1534 }
1535
1536 let mut mf = MachineFunction::new("eh-shape".into());
1537 mf.blocks.push(MachineBlock {
1538 label: "entry".into(),
1539 instrs: vec![],
1540 });
1541
1542 let obj = emit_object(&mf, &mut NopEmitter);
1543 let eh = obj
1544 .sections
1545 .iter()
1546 .find(|s| s.name == ".eh_frame")
1547 .expect(".eh_frame section")
1548 .data
1549 .clone();
1550
1551 let cie_len = u32::from_le_bytes([eh[0], eh[1], eh[2], eh[3]]) as usize;
1553 assert!(cie_len > 8, "CIE should have payload");
1554 let cie_id = u32::from_le_bytes([eh[4], eh[5], eh[6], eh[7]]);
1555 assert_eq!(cie_id, 0, "CIE id must be zero");
1556 assert_eq!(eh[8], 1, "CIE version");
1557 assert!(eh.windows(3).any(|w| w == b"zR\0"), "CIE augmentation zR");
1558
1559 let fde_off = (4 + cie_len + 7) & !7;
1561 let fde_len = u32::from_le_bytes([eh[fde_off], eh[fde_off + 1], eh[fde_off + 2], eh[fde_off + 3]]) as usize;
1562 assert!(fde_len >= 12, "FDE should contain init loc + range + aug len");
1563
1564 let range_off = fde_off + 4 + 4 + 4;
1566 let range = u32::from_le_bytes([eh[range_off], eh[range_off + 1], eh[range_off + 2], eh[range_off + 3]]);
1567 assert_eq!(range, 3, "FDE range should match text size");
1568
1569 assert_eq!(&eh[eh.len() - 4..], &[0, 0, 0, 0]);
1571 }
1572
1573 #[test]
1574 fn coff_unwind_tables_encode_frame_facts_when_present() {
1575 use crate::isel::{MachineBlock, MachineFunction};
1576
1577 struct NopEmitter;
1578 impl Emitter for NopEmitter {
1579 fn emit_function(&mut self, _mf: &MachineFunction) -> Section {
1580 Section {
1581 name: ".text".into(),
1582 data: vec![0x90, 0x90, 0xC3],
1583 relocs: vec![],
1584 debug_rows: vec![],
1585 }
1586 }
1587 fn object_format(&self) -> ObjectFormat {
1588 ObjectFormat::Coff
1589 }
1590 }
1591
1592 let mut mf = MachineFunction::new("coff-unwind-facts".into());
1593 mf.blocks.push(MachineBlock {
1594 label: "entry".into(),
1595 instrs: vec![],
1596 });
1597 mf.frame_size = 16;
1598 mf.used_callee_saved = vec![PReg(3)]; let obj = emit_object(&mf, &mut NopEmitter);
1601 let xdata = obj
1602 .sections
1603 .iter()
1604 .find(|s| s.name == ".xdata")
1605 .expect(".xdata section")
1606 .data
1607 .clone();
1608 assert!(xdata.len() >= 8, "unwind info should include at least one code slot");
1609 assert_eq!(xdata[0] & 0x7, 1, "UNWIND_INFO version 1");
1610 assert!(xdata[2] >= 1, "count_of_codes should be non-zero when frame facts exist");
1611 assert_eq!(xdata[3] & 0x0f, 5, "frame register should be RBP");
1612 }
1613
1614 #[test]
1615 fn coff_unwind_tables_have_expected_layout() {
1616 use crate::isel::{MachineBlock, MachineFunction};
1617
1618 struct NopEmitter;
1619 impl Emitter for NopEmitter {
1620 fn emit_function(&mut self, _mf: &MachineFunction) -> Section {
1621 Section {
1622 name: ".text".into(),
1623 data: vec![0x90, 0xC3],
1624 relocs: vec![],
1625 debug_rows: vec![],
1626 }
1627 }
1628 fn object_format(&self) -> ObjectFormat {
1629 ObjectFormat::Coff
1630 }
1631 }
1632
1633 let mut mf = MachineFunction::new("coff-unwind-shape".into());
1634 mf.blocks.push(MachineBlock {
1635 label: "entry".into(),
1636 instrs: vec![],
1637 });
1638
1639 let obj = emit_object(&mf, &mut NopEmitter);
1640 let xdata = obj
1641 .sections
1642 .iter()
1643 .find(|s| s.name == ".xdata")
1644 .expect(".xdata section")
1645 .data
1646 .clone();
1647 let pdata = obj
1648 .sections
1649 .iter()
1650 .find(|s| s.name == ".pdata")
1651 .expect(".pdata section")
1652 .data
1653 .clone();
1654
1655 assert_eq!(xdata.len(), 4, "minimal UNWIND_INFO header");
1656 assert_eq!(xdata[0] & 0x7, 1, "UNWIND_INFO version 1");
1657 assert_eq!(pdata.len(), 12, "single RUNTIME_FUNCTION entry");
1658
1659 let begin = u32::from_le_bytes([pdata[0], pdata[1], pdata[2], pdata[3]]);
1660 let end = u32::from_le_bytes([pdata[4], pdata[5], pdata[6], pdata[7]]);
1661 let unwind_info_rva = u32::from_le_bytes([pdata[8], pdata[9], pdata[10], pdata[11]]);
1662
1663 assert_eq!(begin, 0);
1664 assert_eq!(end, 2, "runtime function end should match text size");
1665 assert_eq!(unwind_info_rva, 0, "points at section-local xdata start in this object model");
1666 }
1667
1668 #[test]
1669 fn emit_object_adds_debug_s_section_for_coff() {
1670 use crate::isel::{MachineBlock, MachineFunction};
1671
1672 struct NopEmitter;
1673 impl Emitter for NopEmitter {
1674 fn emit_function(&mut self, _mf: &MachineFunction) -> Section {
1675 Section {
1676 name: ".text".into(),
1677 data: vec![0x90],
1678 relocs: vec![],
1679 debug_rows: vec![],
1680 }
1681 }
1682 fn object_format(&self) -> ObjectFormat {
1683 ObjectFormat::Coff
1684 }
1685 }
1686
1687 let mut mf = MachineFunction::new("dbg".into());
1688 mf.blocks.push(MachineBlock {
1689 label: "entry".into(),
1690 instrs: vec![],
1691 });
1692 mf.debug_source = Some("foo.c".into());
1693 mf.debug_line_start = Some(17);
1694
1695 let obj = emit_object(&mf, &mut NopEmitter);
1696 assert!(obj.sections.iter().any(|s| s.name == ".debug$S"));
1697 let bytes = obj.to_bytes();
1698 assert!(bytes.windows(8).any(|w| w == b".debug$S"));
1699 }
1700}