1use synth_core::Result;
6
7#[derive(Debug, Clone, Copy, PartialEq, Eq)]
9pub enum ElfClass {
10 Elf32 = 1,
12 Elf64 = 2,
14}
15
16#[derive(Debug, Clone, Copy, PartialEq, Eq)]
18pub enum ElfData {
19 LittleEndian = 1,
21 BigEndian = 2,
23}
24
25#[derive(Debug, Clone, Copy, PartialEq, Eq)]
27pub enum ElfType {
28 Rel = 1,
30 Exec = 2,
32 Dyn = 3,
34}
35
36#[derive(Debug, Clone, Copy, PartialEq, Eq)]
38pub enum ElfMachine {
39 Arm = 40,
41 AArch64 = 183,
43}
44
45#[derive(Debug, Clone, Copy, PartialEq, Eq)]
47pub enum SectionType {
48 Null = 0,
50 ProgBits = 1,
52 SymTab = 2,
54 StrTab = 3,
56 Rela = 4,
58 Hash = 5,
60 Dynamic = 6,
62 Note = 7,
64 NoBits = 8,
66 Rel = 9,
68 ArmAttributes = 0x7000_0003,
71}
72
73#[derive(Debug, Clone, Copy)]
75pub struct SectionFlags(pub u32);
76
77impl SectionFlags {
78 pub const WRITE: u32 = 0x1;
80 pub const ALLOC: u32 = 0x2;
82 pub const EXEC: u32 = 0x4;
84 pub const MERGE: u32 = 0x10;
86 pub const STRINGS: u32 = 0x20;
88}
89
90#[derive(Debug, Clone)]
92pub struct Section {
93 pub name: String,
95 pub section_type: SectionType,
97 pub flags: u32,
99 pub addr: u32,
101 pub data: Vec<u8>,
103 pub align: u32,
105 pub explicit_size: Option<u32>,
107}
108
109impl Section {
110 pub fn new(name: &str, section_type: SectionType) -> Self {
112 Self {
113 name: name.to_string(),
114 section_type,
115 flags: 0,
116 addr: 0,
117 data: Vec::new(),
118 align: 1,
119 explicit_size: None,
120 }
121 }
122
123 pub fn with_flags(mut self, flags: u32) -> Self {
125 self.flags = flags;
126 self
127 }
128
129 pub fn with_addr(mut self, addr: u32) -> Self {
131 self.addr = addr;
132 self
133 }
134
135 pub fn with_align(mut self, align: u32) -> Self {
137 self.align = align;
138 self
139 }
140
141 pub fn with_data(mut self, data: Vec<u8>) -> Self {
143 self.data = data;
144 self
145 }
146
147 pub fn with_size(mut self, size: u32) -> Self {
149 self.explicit_size = Some(size);
150 self
151 }
152
153 pub fn size(&self) -> u32 {
155 self.explicit_size.unwrap_or(self.data.len() as u32)
156 }
157}
158
159#[derive(Debug, Clone, Copy, PartialEq, Eq)]
161pub enum SymbolBinding {
162 Local = 0,
164 Global = 1,
166 Weak = 2,
168}
169
170#[derive(Debug, Clone, Copy, PartialEq, Eq)]
172pub enum SymbolType {
173 NoType = 0,
175 Object = 1,
177 Func = 2,
179 Section = 3,
181 File = 4,
183}
184
185#[derive(Debug, Clone)]
187pub struct Symbol {
188 pub name: String,
190 pub value: u32,
192 pub size: u32,
194 pub binding: SymbolBinding,
196 pub symbol_type: SymbolType,
198 pub section: u16,
200}
201
202impl Symbol {
203 pub fn new(name: &str) -> Self {
205 Self {
206 name: name.to_string(),
207 value: 0,
208 size: 0,
209 binding: SymbolBinding::Local,
210 symbol_type: SymbolType::NoType,
211 section: 0,
212 }
213 }
214
215 pub fn with_value(mut self, value: u32) -> Self {
217 self.value = value;
218 self
219 }
220
221 pub fn with_size(mut self, size: u32) -> Self {
223 self.size = size;
224 self
225 }
226
227 pub fn with_binding(mut self, binding: SymbolBinding) -> Self {
229 self.binding = binding;
230 self
231 }
232
233 pub fn with_type(mut self, symbol_type: SymbolType) -> Self {
235 self.symbol_type = symbol_type;
236 self
237 }
238
239 pub fn with_section(mut self, section: u16) -> Self {
241 self.section = section;
242 self
243 }
244}
245
246#[derive(Debug, Clone, Copy, PartialEq, Eq)]
248pub enum ProgramType {
249 Null = 0,
251 Load = 1,
253 Dynamic = 2,
255 Interp = 3,
257 Note = 4,
259}
260
261pub struct ProgramFlags;
263
264impl ProgramFlags {
265 pub const EXEC: u32 = 0x1;
267 pub const WRITE: u32 = 0x2;
269 pub const READ: u32 = 0x4;
271}
272
273#[derive(Debug, Clone)]
275pub struct ProgramHeader {
276 pub p_type: ProgramType,
278 pub offset: u32,
280 pub vaddr: u32,
282 pub paddr: u32,
284 pub filesz: u32,
286 pub memsz: u32,
288 pub flags: u32,
290 pub align: u32,
292}
293
294impl ProgramHeader {
295 pub fn load(vaddr: u32, offset: u32, size: u32, flags: u32) -> Self {
297 Self {
298 p_type: ProgramType::Load,
299 offset,
300 vaddr,
301 paddr: vaddr, filesz: size,
303 memsz: size,
304 flags,
305 align: 4,
306 }
307 }
308
309 pub fn load_nobits(vaddr: u32, memsz: u32, flags: u32) -> Self {
312 Self {
313 p_type: ProgramType::Load,
314 offset: 0, vaddr,
316 paddr: vaddr, filesz: 0, memsz, flags,
320 align: 4,
321 }
322 }
323}
324
325#[derive(Debug, Clone, Copy, PartialEq, Eq)]
327pub enum ArmRelocationType {
328 ThmCall = 10,
332 Call = 28,
334 Jump24 = 29,
336 Abs32 = 2,
338 MovwAbsNc = 43,
340 MovtAbs = 44,
342}
343
344struct ExtraRelSection {
348 name_offset: usize,
349 target_idx: u32,
350 offset: usize,
351 data: Vec<u8>,
352}
353
354#[derive(Debug, Clone)]
356pub struct Relocation {
357 pub offset: u32,
359 pub symbol_index: u32,
361 pub reloc_type: ArmRelocationType,
363}
364
365pub const EF_ARM_EABI_VER5: u32 = 0x05000000;
367pub const EF_ARM_ABI_FLOAT_HARD: u32 = 0x00000400;
369pub const EF_ARM_ABI_FLOAT_SOFT: u32 = 0x00000200;
371
372pub mod aeabi {
377 pub const TAG_CPU_ARCH: u32 = 6;
379 pub const TAG_CPU_ARCH_PROFILE: u32 = 7;
381 pub const TAG_ARM_ISA_USE: u32 = 8;
383 pub const TAG_THUMB_ISA_USE: u32 = 9;
385
386 pub const CPU_ARCH_V7: u32 = 10;
388 pub const CPU_ARCH_V6M: u32 = 11;
390 pub const CPU_ARCH_V7EM: u32 = 13;
392 pub const CPU_ARCH_V8_1M_MAIN: u32 = 21;
394
395 pub const PROFILE_M: u32 = b'M' as u32;
397 pub const PROFILE_R: u32 = b'R' as u32;
399
400 pub const TAG_ABI_VFP_ARGS: u32 = 28;
403 pub const TAG_FP_ARCH: u32 = 10;
407 pub const VFP_ARGS_VFP_REGS: u32 = 1;
409 pub const FP_ARCH_VFPV4_D16: u32 = 6;
414}
415
416fn push_uleb128(out: &mut Vec<u8>, mut v: u32) {
418 loop {
419 let byte = (v & 0x7f) as u8;
420 v >>= 7;
421 if v == 0 {
422 out.push(byte);
423 break;
424 }
425 out.push(byte | 0x80);
426 }
427}
428
429pub fn arm_attributes_section(
436 cpu_arch: u32,
437 cpu_arch_profile: u32,
438 arm_isa_use: u32,
439 thumb_isa_use: u32,
440 fp_arch: u32,
441 vfp_args: u32,
442) -> Section {
443 let mut attrs = Vec::new();
449 for (tag, value) in [
450 (aeabi::TAG_CPU_ARCH, cpu_arch),
451 (aeabi::TAG_CPU_ARCH_PROFILE, cpu_arch_profile),
452 (aeabi::TAG_ARM_ISA_USE, arm_isa_use),
453 (aeabi::TAG_THUMB_ISA_USE, thumb_isa_use),
454 (aeabi::TAG_FP_ARCH, fp_arch),
455 (aeabi::TAG_ABI_VFP_ARGS, vfp_args),
456 ] {
457 if value != 0 {
458 push_uleb128(&mut attrs, tag);
459 push_uleb128(&mut attrs, value);
460 }
461 }
462
463 let file_len = (1 + 4 + attrs.len()) as u32;
465 let mut file_sub = vec![1u8]; file_sub.extend_from_slice(&file_len.to_le_bytes());
467 file_sub.extend_from_slice(&attrs);
468
469 let vendor_name = b"aeabi\0";
471 let vendor_len = (4 + vendor_name.len() + file_sub.len()) as u32;
472 let mut blob = vec![b'A']; blob.extend_from_slice(&vendor_len.to_le_bytes());
474 blob.extend_from_slice(vendor_name);
475 blob.extend_from_slice(&file_sub);
476
477 Section::new(".ARM.attributes", SectionType::ArmAttributes)
478 .with_align(1)
479 .with_data(blob)
480}
481
482pub struct ElfBuilder {
484 class: ElfClass,
486 data: ElfData,
488 elf_type: ElfType,
490 machine: ElfMachine,
492 entry: u32,
494 e_flags: u32,
496 sections: Vec<Section>,
498 symbols: Vec<Symbol>,
500 program_headers: Vec<ProgramHeader>,
502 relocations: Vec<Relocation>,
504 extra_relocations: Vec<(String, Vec<Relocation>)>,
510 thumb_funcs: bool,
516}
517
518impl ElfBuilder {
519 pub fn new_arm32() -> Self {
521 Self {
522 class: ElfClass::Elf32,
523 data: ElfData::LittleEndian,
524 elf_type: ElfType::Exec,
525 machine: ElfMachine::Arm,
526 entry: 0,
527 e_flags: EF_ARM_EABI_VER5,
528 sections: Vec::new(),
529 symbols: Vec::new(),
530 program_headers: Vec::new(),
531 relocations: Vec::new(),
532 extra_relocations: Vec::new(),
533 thumb_funcs: true,
534 }
535 }
536
537 pub fn with_thumb_funcs(mut self, thumb: bool) -> Self {
541 self.thumb_funcs = thumb;
542 self
543 }
544
545 pub fn with_entry(mut self, entry: u32) -> Self {
551 self.entry = if self.machine == ElfMachine::Arm && self.thumb_funcs {
552 entry | 1 } else {
554 entry
555 };
556 self
557 }
558
559 pub fn set_flags(&mut self, flags: u32) {
561 self.e_flags = flags;
562 }
563
564 pub fn with_type(mut self, elf_type: ElfType) -> Self {
566 self.elf_type = elf_type;
567 self
568 }
569
570 pub fn add_section(&mut self, section: Section) {
572 self.sections.push(section);
573 }
574
575 pub fn add_symbol(&mut self, symbol: Symbol) {
577 self.symbols.push(symbol);
578 }
579
580 pub fn add_symbol_indexed(&mut self, symbol: Symbol) -> u32 {
585 let index = self.symbols.len() as u32 + 1;
586 self.symbols.push(symbol);
587 index
588 }
589
590 pub fn add_program_header(&mut self, ph: ProgramHeader) {
592 self.program_headers.push(ph);
593 }
594
595 pub fn add_relocation(&mut self, reloc: Relocation) {
597 self.relocations.push(reloc);
598 }
599
600 pub fn add_section_relocations(&mut self, target_section: &str, relocs: Vec<Relocation>) {
607 if relocs.is_empty() {
608 return;
609 }
610 self.extra_relocations
611 .push((target_section.to_string(), relocs));
612 }
613
614 pub fn add_undefined_symbol(&mut self, name: &str) -> u32 {
617 let index = self.symbols.len() as u32 + 1; self.symbols.push(Symbol {
619 name: name.to_string(),
620 value: 0,
621 size: 0,
622 binding: SymbolBinding::Global,
623 symbol_type: SymbolType::Func,
624 section: 0, });
626 index
627 }
628
629 pub fn build(&self) -> Result<Vec<u8>> {
631 let mut output = Vec::new();
632
633 let header_size = 52;
635 let ph_entry_size = 32;
637 let ph_count = self.program_headers.len();
638 let ph_table_size = ph_entry_size * ph_count;
639
640 output.resize(header_size + ph_table_size, 0);
642
643 let (shstrtab_data, section_name_offsets, extra_rel_name_offsets) =
645 self.build_section_string_table();
646
647 let (strtab_data, symbol_name_offsets) = self.build_symbol_string_table();
649
650 let mut sym_order: Vec<usize> = (0..self.symbols.len()).collect();
660 sym_order.sort_by_key(|&i| self.symbols[i].binding != SymbolBinding::Local);
661 let mut old_to_new = vec![0u32; self.symbols.len() + 1];
663 for (new_pos, &old) in sym_order.iter().enumerate() {
664 old_to_new[old + 1] = new_pos as u32 + 1;
665 }
666 let local_count = self
667 .symbols
668 .iter()
669 .filter(|s| s.binding == SymbolBinding::Local)
670 .count();
671 let symtab_sh_info = local_count as u32 + 1;
673 let remap_relocs = |relocs: &[Relocation]| -> Vec<Relocation> {
674 relocs
675 .iter()
676 .map(|r| Relocation {
677 offset: r.offset,
678 symbol_index: old_to_new[r.symbol_index as usize],
679 reloc_type: r.reloc_type,
680 })
681 .collect()
682 };
683
684 let mut current_offset = header_size + ph_table_size;
686
687 let shstrtab_offset = current_offset;
689 current_offset += shstrtab_data.len();
690
691 let strtab_offset = current_offset;
693 current_offset += strtab_data.len();
694
695 let mut section_offsets = Vec::new();
697 for section in &self.sections {
698 section_offsets.push(current_offset);
699 current_offset += section.data.len();
700 }
701
702 let symtab_offset = current_offset;
704 let symtab_data = self.build_symbol_table(&symbol_name_offsets, &sym_order);
705 current_offset += symtab_data.len();
706
707 let rel_data = Self::encode_rel_entries(&remap_relocs(&self.relocations));
710 let rel_offset = current_offset;
711 current_offset += rel_data.len();
712
713 let mut extra_rel: Vec<ExtraRelSection> = Vec::new();
718 for (i, (target, relocs)) in self.extra_relocations.iter().enumerate() {
719 let Some(target_idx) = self.section_index_by_name(target) else {
720 continue;
721 };
722 let data = Self::encode_rel_entries(&remap_relocs(relocs));
723 let name_offset = extra_rel_name_offsets.get(i).copied().unwrap_or(0);
724 extra_rel.push(ExtraRelSection {
725 name_offset,
726 target_idx,
727 offset: current_offset,
728 data,
729 });
730 current_offset += extra_rel.last().unwrap().data.len();
731 }
732
733 let sh_offset = current_offset;
735
736 output.extend_from_slice(&shstrtab_data);
738 output.extend_from_slice(&strtab_data);
739
740 for section in &self.sections {
741 output.extend_from_slice(§ion.data);
742 }
743
744 output.extend_from_slice(&symtab_data);
745 output.extend_from_slice(&rel_data);
746 for er in &extra_rel {
747 output.extend_from_slice(&er.data);
748 }
749
750 let section_headers = self.build_section_headers_with_rel(
752 §ion_name_offsets,
753 shstrtab_offset,
754 &shstrtab_data,
755 strtab_offset,
756 &strtab_data,
757 symtab_offset,
758 &symtab_data,
759 §ion_offsets,
760 rel_offset,
761 &rel_data,
762 &extra_rel,
763 symtab_sh_info,
764 );
765 output.extend_from_slice(§ion_headers);
766
767 for (i, ph) in self.program_headers.iter().enumerate() {
770 let ph_offset = header_size + i * ph_entry_size;
771 let mut corrected_ph = ph.clone();
772 if corrected_ph.filesz > 0 {
773 for (si, section) in self.sections.iter().enumerate() {
775 if section.addr == corrected_ph.vaddr && si < section_offsets.len() {
776 corrected_ph.offset = section_offsets[si] as u32;
777 break;
778 }
779 }
780 }
781 self.write_program_header(
782 &mut output[ph_offset..ph_offset + ph_entry_size],
783 &corrected_ph,
784 );
785 }
786
787 let has_rel = !self.relocations.is_empty();
789 let num_sections = 4 + self.sections.len() + if has_rel { 1 } else { 0 } + extra_rel.len();
790 let ph_offset = if ph_count > 0 { header_size as u32 } else { 0 };
791 self.write_elf_header_with_phdrs(
792 &mut output[0..header_size],
793 ph_offset,
794 ph_count as u16,
795 sh_offset as u32,
796 num_sections as u16,
797 )?;
798
799 Ok(output)
800 }
801
802 fn write_program_header(&self, output: &mut [u8], ph: &ProgramHeader) {
804 let mut cursor = 0;
805
806 output[cursor..cursor + 4].copy_from_slice(&(ph.p_type as u32).to_le_bytes());
808 cursor += 4;
809
810 output[cursor..cursor + 4].copy_from_slice(&ph.offset.to_le_bytes());
812 cursor += 4;
813
814 output[cursor..cursor + 4].copy_from_slice(&ph.vaddr.to_le_bytes());
816 cursor += 4;
817
818 output[cursor..cursor + 4].copy_from_slice(&ph.paddr.to_le_bytes());
820 cursor += 4;
821
822 output[cursor..cursor + 4].copy_from_slice(&ph.filesz.to_le_bytes());
824 cursor += 4;
825
826 output[cursor..cursor + 4].copy_from_slice(&ph.memsz.to_le_bytes());
828 cursor += 4;
829
830 output[cursor..cursor + 4].copy_from_slice(&ph.flags.to_le_bytes());
832 cursor += 4;
833
834 output[cursor..cursor + 4].copy_from_slice(&ph.align.to_le_bytes());
836 }
837
838 fn write_elf_header_with_phdrs(
840 &self,
841 output: &mut [u8],
842 ph_offset: u32,
843 ph_count: u16,
844 sh_offset: u32,
845 sh_count: u16,
846 ) -> Result<()> {
847 let mut cursor = 0;
848
849 output[cursor..cursor + 4].copy_from_slice(&[0x7f, b'E', b'L', b'F']);
851 cursor += 4;
852
853 output[cursor] = self.class as u8;
855 cursor += 1;
856
857 output[cursor] = self.data as u8;
859 cursor += 1;
860
861 output[cursor] = 1;
863 cursor += 1;
864
865 output[cursor] = 0; cursor += 1;
868
869 output[cursor] = 0;
871 cursor += 1;
872
873 output[cursor..cursor + 7].copy_from_slice(&[0; 7]);
875 cursor += 7;
876
877 let etype = self.elf_type as u16;
879 output[cursor..cursor + 2].copy_from_slice(&etype.to_le_bytes());
880 cursor += 2;
881
882 let machine = self.machine as u16;
884 output[cursor..cursor + 2].copy_from_slice(&machine.to_le_bytes());
885 cursor += 2;
886
887 output[cursor..cursor + 4].copy_from_slice(&1u32.to_le_bytes());
889 cursor += 4;
890
891 output[cursor..cursor + 4].copy_from_slice(&self.entry.to_le_bytes());
893 cursor += 4;
894
895 output[cursor..cursor + 4].copy_from_slice(&ph_offset.to_le_bytes());
897 cursor += 4;
898
899 output[cursor..cursor + 4].copy_from_slice(&sh_offset.to_le_bytes());
901 cursor += 4;
902
903 output[cursor..cursor + 4].copy_from_slice(&self.e_flags.to_le_bytes());
905 cursor += 4;
906
907 output[cursor..cursor + 2].copy_from_slice(&52u16.to_le_bytes());
909 cursor += 2;
910
911 let ph_entry_size: u16 = if ph_count > 0 { 32 } else { 0 };
913 output[cursor..cursor + 2].copy_from_slice(&ph_entry_size.to_le_bytes());
914 cursor += 2;
915
916 output[cursor..cursor + 2].copy_from_slice(&ph_count.to_le_bytes());
918 cursor += 2;
919
920 output[cursor..cursor + 2].copy_from_slice(&40u16.to_le_bytes());
922 cursor += 2;
923
924 output[cursor..cursor + 2].copy_from_slice(&sh_count.to_le_bytes());
926 cursor += 2;
927
928 output[cursor..cursor + 2].copy_from_slice(&1u16.to_le_bytes());
930
931 Ok(())
932 }
933
934 fn build_section_string_table(&self) -> (Vec<u8>, Vec<usize>, Vec<usize>) {
940 let mut strtab = vec![0]; let mut offsets = Vec::new();
942
943 strtab.extend_from_slice(b".shstrtab\0");
945 strtab.extend_from_slice(b".strtab\0");
946 strtab.extend_from_slice(b".symtab\0");
947
948 for section in &self.sections {
950 let offset = strtab.len();
951 offsets.push(offset);
952 strtab.extend_from_slice(section.name.as_bytes());
953 strtab.push(0);
954 }
955
956 if !self.relocations.is_empty() {
958 strtab.extend_from_slice(b".rel.text\0");
959 }
960
961 let mut extra_rel_offsets = Vec::new();
963 for (target, _) in &self.extra_relocations {
964 let offset = strtab.len();
965 extra_rel_offsets.push(offset);
966 strtab.extend_from_slice(format!(".rel{target}\0").as_bytes());
967 }
968
969 (strtab, offsets, extra_rel_offsets)
970 }
971
972 fn build_symbol_string_table(&self) -> (Vec<u8>, Vec<usize>) {
974 let mut strtab = vec![0]; let mut offsets = Vec::new();
976
977 for symbol in &self.symbols {
978 let offset = strtab.len();
979 offsets.push(offset);
980 strtab.extend_from_slice(symbol.name.as_bytes());
981 strtab.push(0);
982 }
983
984 (strtab, offsets)
985 }
986
987 fn encode_rel_entries(relocs: &[Relocation]) -> Vec<u8> {
990 let mut rel_data = Vec::new();
991 for reloc in relocs {
992 rel_data.extend_from_slice(&reloc.offset.to_le_bytes());
994 let r_info = (reloc.symbol_index << 8) | (reloc.reloc_type as u32);
996 rel_data.extend_from_slice(&r_info.to_le_bytes());
997 }
998 rel_data
999 }
1000
1001 fn section_index_by_name(&self, name: &str) -> Option<u32> {
1005 self.sections
1006 .iter()
1007 .position(|s| s.name == name)
1008 .map(|pos| 4 + pos as u32)
1009 }
1010
1011 fn build_symbol_table(&self, name_offsets: &[usize], order: &[usize]) -> Vec<u8> {
1015 let mut symtab = Vec::new();
1016
1017 symtab.extend_from_slice(&[0u8; 16]); for &i in order {
1022 let symbol = &self.symbols[i];
1023 let name_offset = if i < name_offsets.len() {
1024 name_offsets[i] as u32
1025 } else {
1026 0
1027 };
1028
1029 symtab.extend_from_slice(&name_offset.to_le_bytes());
1031
1032 let value = if self.machine == ElfMachine::Arm
1039 && self.thumb_funcs
1040 && symbol.symbol_type == SymbolType::Func
1041 {
1042 symbol.value | 1
1043 } else {
1044 symbol.value
1045 };
1046 symtab.extend_from_slice(&value.to_le_bytes());
1047
1048 symtab.extend_from_slice(&symbol.size.to_le_bytes());
1050
1051 let info = ((symbol.binding as u8) << 4) | (symbol.symbol_type as u8 & 0xf);
1053 symtab.push(info);
1054
1055 symtab.push(0);
1057
1058 symtab.extend_from_slice(&symbol.section.to_le_bytes());
1060 }
1061
1062 symtab
1063 }
1064
1065 #[allow(clippy::too_many_arguments)]
1067 fn build_section_headers_with_rel(
1068 &self,
1069 section_name_offsets: &[usize],
1070 shstrtab_offset: usize,
1071 shstrtab_data: &[u8],
1072 strtab_offset: usize,
1073 strtab_data: &[u8],
1074 symtab_offset: usize,
1075 symtab_data: &[u8],
1076 section_offsets: &[usize],
1077 rel_offset: usize,
1078 rel_data: &[u8],
1079 extra_rel: &[ExtraRelSection],
1080 symtab_sh_info: u32,
1083 ) -> Vec<u8> {
1084 let mut headers = Vec::new();
1085
1086 headers.extend_from_slice(&[0u8; 40]);
1090
1091 self.write_section_header(
1093 &mut headers,
1094 1,
1095 SectionType::StrTab as u32,
1096 0,
1097 0,
1098 shstrtab_offset as u32,
1099 shstrtab_data.len() as u32,
1100 0,
1101 0,
1102 1,
1103 0,
1104 );
1105
1106 let strtab_name_offset = ".shstrtab\0".len();
1108 self.write_section_header(
1109 &mut headers,
1110 strtab_name_offset as u32,
1111 SectionType::StrTab as u32,
1112 0,
1113 0,
1114 strtab_offset as u32,
1115 strtab_data.len() as u32,
1116 0,
1117 0,
1118 1,
1119 0,
1120 );
1121
1122 let symtab_name_offset = ".shstrtab\0.strtab\0".len();
1124 self.write_section_header(
1125 &mut headers,
1126 symtab_name_offset as u32,
1127 SectionType::SymTab as u32,
1128 0,
1129 0,
1130 symtab_offset as u32,
1131 symtab_data.len() as u32,
1132 2,
1133 symtab_sh_info,
1137 4,
1138 16,
1139 );
1140
1141 for (i, section) in self.sections.iter().enumerate() {
1143 let name_offset = if i < section_name_offsets.len() {
1144 section_name_offsets[i] as u32
1145 } else {
1146 0
1147 };
1148 let offset = if i < section_offsets.len() {
1149 section_offsets[i] as u32
1150 } else {
1151 0
1152 };
1153
1154 self.write_section_header(
1155 &mut headers,
1156 name_offset,
1157 section.section_type as u32,
1158 section.flags,
1159 section.addr,
1160 offset,
1161 section.size(),
1162 0,
1163 0,
1164 section.align,
1165 0,
1166 );
1167 }
1168
1169 if !rel_data.is_empty() {
1171 let rel_name_offset = self.rel_text_shstrtab_offset();
1172 let text_section_idx = 4u32; self.write_section_header(
1175 &mut headers,
1176 rel_name_offset as u32,
1177 SectionType::Rel as u32,
1178 0,
1179 0,
1180 rel_offset as u32,
1181 rel_data.len() as u32,
1182 3, text_section_idx, 4,
1185 8, );
1187 }
1188
1189 for er in extra_rel {
1192 self.write_section_header(
1193 &mut headers,
1194 er.name_offset as u32,
1195 SectionType::Rel as u32,
1196 0,
1197 0,
1198 er.offset as u32,
1199 er.data.len() as u32,
1200 3, er.target_idx, 4,
1203 8, );
1205 }
1206
1207 headers
1208 }
1209
1210 fn rel_text_shstrtab_offset(&self) -> usize {
1212 let mut offset = 1 + ".shstrtab\0".len() + ".strtab\0".len() + ".symtab\0".len();
1214 for section in &self.sections {
1215 offset += section.name.len() + 1;
1216 }
1217 offset
1218 }
1219
1220 #[allow(clippy::too_many_arguments)]
1222 fn write_section_header(
1223 &self,
1224 output: &mut Vec<u8>,
1225 name: u32,
1226 sh_type: u32,
1227 flags: u32,
1228 addr: u32,
1229 offset: u32,
1230 size: u32,
1231 link: u32,
1232 info: u32,
1233 align: u32,
1234 entsize: u32,
1235 ) {
1236 output.extend_from_slice(&name.to_le_bytes());
1237 output.extend_from_slice(&sh_type.to_le_bytes());
1238 output.extend_from_slice(&flags.to_le_bytes());
1239 output.extend_from_slice(&addr.to_le_bytes());
1240 output.extend_from_slice(&offset.to_le_bytes());
1241 output.extend_from_slice(&size.to_le_bytes());
1242 output.extend_from_slice(&link.to_le_bytes());
1243 output.extend_from_slice(&info.to_le_bytes());
1244 output.extend_from_slice(&align.to_le_bytes());
1245 output.extend_from_slice(&entsize.to_le_bytes());
1246 }
1247
1248 #[allow(dead_code)]
1250 fn write_elf_header(&self, output: &mut Vec<u8>) -> Result<()> {
1251 output.extend_from_slice(&[0x7f, b'E', b'L', b'F']);
1253
1254 output.push(self.class as u8);
1256
1257 output.push(self.data as u8);
1259
1260 output.push(1);
1262
1263 output.push(0); output.push(0);
1268
1269 output.extend_from_slice(&[0; 7]);
1271
1272 let etype = self.elf_type as u16;
1274 output.extend_from_slice(&etype.to_le_bytes());
1275
1276 let machine = self.machine as u16;
1278 output.extend_from_slice(&machine.to_le_bytes());
1279
1280 output.extend_from_slice(&1u32.to_le_bytes());
1282
1283 output.extend_from_slice(&self.entry.to_le_bytes());
1285
1286 output.extend_from_slice(&0u32.to_le_bytes());
1288
1289 output.extend_from_slice(&0u32.to_le_bytes());
1291
1292 output.extend_from_slice(&0u32.to_le_bytes());
1294
1295 output.extend_from_slice(&52u16.to_le_bytes());
1297
1298 output.extend_from_slice(&0u16.to_le_bytes());
1300
1301 output.extend_from_slice(&0u16.to_le_bytes());
1303
1304 output.extend_from_slice(&40u16.to_le_bytes());
1306
1307 output.extend_from_slice(&0u16.to_le_bytes());
1309
1310 output.extend_from_slice(&0u16.to_le_bytes());
1312
1313 Ok(())
1314 }
1315}
1316
1317#[cfg(test)]
1318mod tests {
1319 use super::*;
1320
1321 #[test]
1322 fn test_elf_builder_creation() {
1323 let builder = ElfBuilder::new_arm32();
1324 assert_eq!(builder.class, ElfClass::Elf32);
1325 assert_eq!(builder.data, ElfData::LittleEndian);
1326 assert_eq!(builder.machine, ElfMachine::Arm);
1327 }
1328
1329 #[test]
1330 fn test_section_creation() {
1331 let section = Section::new(".text", SectionType::ProgBits)
1332 .with_flags(SectionFlags::ALLOC | SectionFlags::EXEC)
1333 .with_addr(0x8000)
1334 .with_align(4);
1335
1336 assert_eq!(section.name, ".text");
1337 assert_eq!(section.section_type, SectionType::ProgBits);
1338 assert_eq!(section.addr, 0x8000);
1339 assert_eq!(section.align, 4);
1340 }
1341
1342 #[test]
1343 fn test_symbol_creation() {
1344 let symbol = Symbol::new("main")
1345 .with_value(0x8000)
1346 .with_size(128)
1347 .with_binding(SymbolBinding::Global)
1348 .with_type(SymbolType::Func)
1349 .with_section(1);
1350
1351 assert_eq!(symbol.name, "main");
1352 assert_eq!(symbol.value, 0x8000);
1353 assert_eq!(symbol.size, 128);
1354 assert_eq!(symbol.binding, SymbolBinding::Global);
1355 assert_eq!(symbol.symbol_type, SymbolType::Func);
1356 }
1357
1358 #[test]
1359 fn test_elf_header_generation() {
1360 let builder = ElfBuilder::new_arm32().with_entry(0x8000);
1361 let elf = builder.build().unwrap();
1362
1363 assert_eq!(&elf[0..4], &[0x7f, b'E', b'L', b'F']);
1365
1366 assert_eq!(elf[4], 1);
1368
1369 assert_eq!(elf[5], 1);
1371
1372 assert_eq!(elf[6], 1);
1374 }
1375
1376 #[test]
1377 fn test_add_sections() {
1378 let mut builder = ElfBuilder::new_arm32();
1379
1380 let text = Section::new(".text", SectionType::ProgBits)
1381 .with_flags(SectionFlags::ALLOC | SectionFlags::EXEC);
1382
1383 let data = Section::new(".data", SectionType::ProgBits)
1384 .with_flags(SectionFlags::ALLOC | SectionFlags::WRITE);
1385
1386 builder.add_section(text);
1387 builder.add_section(data);
1388
1389 assert_eq!(builder.sections.len(), 2);
1390 }
1391
1392 #[test]
1393 fn test_add_symbols() {
1394 let mut builder = ElfBuilder::new_arm32();
1395
1396 let main_sym = Symbol::new("main")
1397 .with_binding(SymbolBinding::Global)
1398 .with_type(SymbolType::Func);
1399
1400 let data_sym = Symbol::new("data")
1401 .with_binding(SymbolBinding::Local)
1402 .with_type(SymbolType::Object);
1403
1404 builder.add_symbol(main_sym);
1405 builder.add_symbol(data_sym);
1406
1407 assert_eq!(builder.symbols.len(), 2);
1408 }
1409
1410 #[test]
1411 fn test_complete_elf_generation() {
1412 let mut builder = ElfBuilder::new_arm32()
1414 .with_entry(0x8000)
1415 .with_type(ElfType::Exec);
1416
1417 let text_code = vec![
1419 0x00, 0x48, 0x2d, 0xe9, 0x04, 0xb0, 0x8d, 0xe2, 0x00, 0x00, 0xa0, 0xe3, 0x00, 0x88, 0xbd, 0xe8, ];
1424 let text = Section::new(".text", SectionType::ProgBits)
1425 .with_flags(SectionFlags::ALLOC | SectionFlags::EXEC)
1426 .with_addr(0x8000)
1427 .with_align(4)
1428 .with_data(text_code);
1429
1430 builder.add_section(text);
1431
1432 let data_content = vec![0x01, 0x02, 0x03, 0x04];
1434 let data = Section::new(".data", SectionType::ProgBits)
1435 .with_flags(SectionFlags::ALLOC | SectionFlags::WRITE)
1436 .with_addr(0x8100)
1437 .with_align(4)
1438 .with_data(data_content);
1439
1440 builder.add_section(data);
1441
1442 let bss = Section::new(".bss", SectionType::NoBits)
1444 .with_flags(SectionFlags::ALLOC | SectionFlags::WRITE)
1445 .with_addr(0x8200)
1446 .with_align(4);
1447
1448 builder.add_section(bss);
1449
1450 let main_sym = Symbol::new("main")
1452 .with_value(0x8000)
1453 .with_size(16)
1454 .with_binding(SymbolBinding::Global)
1455 .with_type(SymbolType::Func)
1456 .with_section(4); builder.add_symbol(main_sym);
1459
1460 let data_var = Symbol::new("global_var")
1461 .with_value(0x8100)
1462 .with_size(4)
1463 .with_binding(SymbolBinding::Global)
1464 .with_type(SymbolType::Object)
1465 .with_section(5); builder.add_symbol(data_var);
1468
1469 let elf = builder.build().unwrap();
1471
1472 assert_eq!(&elf[0..4], &[0x7f, b'E', b'L', b'F']);
1474 assert_eq!(elf[4], 1); assert_eq!(elf[5], 1); assert_eq!(elf[6], 1); assert!(elf.len() > 52); assert!(elf.len() < 10000); let entry_bytes = &elf[24..28];
1484 let entry = u32::from_le_bytes([
1485 entry_bytes[0],
1486 entry_bytes[1],
1487 entry_bytes[2],
1488 entry_bytes[3],
1489 ]);
1490 assert_eq!(entry, 0x8001); let sh_off_bytes = &elf[32..36];
1494 let sh_off = u32::from_le_bytes([
1495 sh_off_bytes[0],
1496 sh_off_bytes[1],
1497 sh_off_bytes[2],
1498 sh_off_bytes[3],
1499 ]);
1500 assert!(sh_off > 0);
1501
1502 let sh_num_bytes = &elf[48..50];
1504 let sh_num = u16::from_le_bytes([sh_num_bytes[0], sh_num_bytes[1]]);
1505 assert_eq!(sh_num, 7);
1506
1507 let shstrndx_bytes = &elf[50..52];
1509 let shstrndx = u16::from_le_bytes([shstrndx_bytes[0], shstrndx_bytes[1]]);
1510 assert_eq!(shstrndx, 1);
1511 }
1512
1513 #[test]
1514 fn test_string_table_generation() {
1515 let mut builder = ElfBuilder::new_arm32();
1516
1517 builder.add_section(Section::new(".text", SectionType::ProgBits));
1518 builder.add_section(Section::new(".data", SectionType::ProgBits));
1519
1520 let (strtab, offsets, _extra_rel_offsets) = builder.build_section_string_table();
1521
1522 assert_eq!(strtab[0], 0);
1524
1525 let strtab_str = String::from_utf8_lossy(&strtab);
1527 assert!(strtab_str.contains(".shstrtab"));
1528 assert!(strtab_str.contains(".strtab"));
1529 assert!(strtab_str.contains(".symtab"));
1530 assert!(strtab_str.contains(".text"));
1531 assert!(strtab_str.contains(".data"));
1532
1533 assert_eq!(offsets.len(), 2);
1535 }
1536
1537 #[test]
1538 fn test_relocation_support() {
1539 let mut builder = ElfBuilder::new_arm32()
1540 .with_entry(0x8000)
1541 .with_type(ElfType::Rel);
1542
1543 let text_code = vec![0x00u8; 16]; let text = Section::new(".text", SectionType::ProgBits)
1546 .with_flags(SectionFlags::ALLOC | SectionFlags::EXEC)
1547 .with_addr(0x8000)
1548 .with_align(4)
1549 .with_data(text_code);
1550 builder.add_section(text);
1551
1552 let sym_idx = builder.add_undefined_symbol("__meld_dispatch_import");
1554 assert!(sym_idx > 0);
1555
1556 builder.add_relocation(Relocation {
1558 offset: 4,
1559 symbol_index: sym_idx,
1560 reloc_type: ArmRelocationType::Call,
1561 });
1562
1563 let elf = builder.build().unwrap();
1564
1565 assert_eq!(&elf[0..4], &[0x7f, b'E', b'L', b'F']);
1567
1568 let sh_num = u16::from_le_bytes([elf[48], elf[49]]);
1571 assert_eq!(sh_num, 6);
1572
1573 let has_undef = elf
1576 .windows(b"__meld_dispatch_import".len())
1577 .any(|w| w == b"__meld_dispatch_import");
1578 assert!(
1579 has_undef,
1580 "ELF should contain __meld_dispatch_import symbol name"
1581 );
1582 }
1583
1584 #[test]
1585 fn test_symbol_table_encoding() {
1586 let mut builder = ElfBuilder::new_arm32();
1587
1588 let sym = Symbol::new("test_func")
1589 .with_value(0x1000)
1590 .with_size(64)
1591 .with_binding(SymbolBinding::Global)
1592 .with_type(SymbolType::Func)
1593 .with_section(1);
1594
1595 builder.add_symbol(sym);
1596
1597 let (_strtab, offsets) = builder.build_symbol_string_table();
1598 let symtab = builder.build_symbol_table(&offsets, &[0]);
1599
1600 assert_eq!(symtab.len(), 32);
1602
1603 assert!(symtab[0..16].iter().all(|&b| b == 0));
1605
1606 let value_bytes = &symtab[20..24];
1610 let value = u32::from_le_bytes([
1611 value_bytes[0],
1612 value_bytes[1],
1613 value_bytes[2],
1614 value_bytes[3],
1615 ]);
1616 assert_eq!(value, 0x1001); let size_bytes = &symtab[24..28];
1620 let size = u32::from_le_bytes([size_bytes[0], size_bytes[1], size_bytes[2], size_bytes[3]]);
1621 assert_eq!(size, 64);
1622
1623 let info = symtab[28];
1625 let binding = info >> 4;
1626 let sym_type = info & 0xf;
1627 assert_eq!(binding, SymbolBinding::Global as u8);
1628 assert_eq!(sym_type, SymbolType::Func as u8);
1629 }
1630
1631 #[test]
1636 fn test_a32_symbols_have_no_thumb_bit_598() {
1637 let mut builder = ElfBuilder::new_arm32().with_thumb_funcs(false);
1638
1639 let func_sym = Symbol::new("a32_func")
1640 .with_value(0x1000)
1641 .with_size(64)
1642 .with_binding(SymbolBinding::Global)
1643 .with_type(SymbolType::Func)
1644 .with_section(1);
1645 builder.add_symbol(func_sym);
1646
1647 let (_strtab, offsets) = builder.build_symbol_string_table();
1648 let symtab = builder.build_symbol_table(&offsets, &[0]);
1649 let value = u32::from_le_bytes(symtab[20..24].try_into().unwrap());
1650 assert_eq!(value, 0x1000, "A32 STT_FUNC st_value must keep bit 0 clear");
1651
1652 let builder = ElfBuilder::new_arm32()
1654 .with_thumb_funcs(false)
1655 .with_entry(0x8000);
1656 assert_eq!(builder.entry, 0x8000, "A32 e_entry must keep bit 0 clear");
1657
1658 let builder = ElfBuilder::new_arm32().with_entry(0x8000);
1660 assert_eq!(builder.entry, 0x8001, "Thumb e_entry keeps the bit");
1661 }
1662
1663 fn read_section_headers(elf: &[u8]) -> Vec<(u32, u32, u32, u32, u32)> {
1666 let e_shoff = u32::from_le_bytes(elf[32..36].try_into().unwrap()) as usize;
1667 let e_shnum = u16::from_le_bytes(elf[48..50].try_into().unwrap()) as usize;
1668 (0..e_shnum)
1669 .map(|i| {
1670 let base = e_shoff + i * 40;
1671 let f = |off: usize| {
1672 u32::from_le_bytes(elf[base + off..base + off + 4].try_into().unwrap())
1673 };
1674 (f(4), f(16), f(20), f(24), f(28))
1675 })
1676 .collect()
1677 }
1678
1679 fn read_symtab(elf: &[u8]) -> (Vec<(u32, u8, u16)>, u32) {
1681 let headers = read_section_headers(elf);
1682 let &(_, off, size, _, sh_info) = headers
1683 .iter()
1684 .find(|h| h.0 == SectionType::SymTab as u32)
1685 .expect("symtab present");
1686 let syms = (0..size as usize / 16)
1687 .map(|i| {
1688 let base = off as usize + i * 16;
1689 (
1690 u32::from_le_bytes(elf[base + 4..base + 8].try_into().unwrap()),
1691 elf[base + 12],
1692 u16::from_le_bytes(elf[base + 14..base + 16].try_into().unwrap()),
1693 )
1694 })
1695 .collect();
1696 (syms, sh_info)
1697 }
1698
1699 #[test]
1703 fn test_locals_sorted_first_sh_info_and_reloc_reindex_656() {
1704 let mut builder = ElfBuilder::new_arm32()
1705 .with_entry(0)
1706 .with_type(ElfType::Rel);
1707 let text = Section::new(".text", SectionType::ProgBits)
1708 .with_flags(SectionFlags::ALLOC | SectionFlags::EXEC)
1709 .with_align(4)
1710 .with_data(vec![0u8; 16]);
1711 builder.add_section(text);
1712
1713 builder.add_symbol(
1716 Symbol::new("exported")
1717 .with_value(0)
1718 .with_binding(SymbolBinding::Global)
1719 .with_type(SymbolType::Func)
1720 .with_section(4),
1721 ); builder.add_symbol(
1723 Symbol::new("func_2")
1724 .with_value(8)
1725 .with_binding(SymbolBinding::Local)
1726 .with_type(SymbolType::Func)
1727 .with_section(4),
1728 ); let undef_idx = builder.add_undefined_symbol("external"); assert_eq!(undef_idx, 3);
1731
1732 builder.add_relocation(Relocation {
1735 offset: 0,
1736 symbol_index: 2,
1737 reloc_type: ArmRelocationType::ThmCall,
1738 });
1739 builder.add_relocation(Relocation {
1740 offset: 4,
1741 symbol_index: undef_idx,
1742 reloc_type: ArmRelocationType::ThmCall,
1743 });
1744
1745 let elf = builder.build().unwrap();
1746 let (syms, sh_info) = read_symtab(&elf);
1747
1748 assert_eq!(syms.len(), 4);
1750 assert_eq!(syms[0], (0, 0, 0), "null symbol first");
1751 let bind = |info: u8| info >> 4;
1752 assert_eq!(bind(syms[1].1), SymbolBinding::Local as u8, "local first");
1753 assert_eq!(syms[1].0, 8 | 1, "func_2 st_value (thumb bit)");
1754 assert_eq!(bind(syms[2].1), SymbolBinding::Global as u8);
1755 assert_eq!(syms[2].0, 1, "exported st_value 0 | thumb bit");
1756 assert_eq!(bind(syms[3].1), SymbolBinding::Global as u8);
1757 assert_eq!(syms[3].2, 0, "external is SHN_UNDEF");
1758 assert_eq!(sh_info, 2, "sh_info = index of first non-local symbol");
1759
1760 let headers = read_section_headers(&elf);
1762 let &(_, rel_off, rel_size, _, rel_info) = headers
1763 .iter()
1764 .find(|h| h.0 == SectionType::Rel as u32)
1765 .expect(".rel.text present");
1766 assert_eq!(rel_info, 4, ".rel.text still targets .text");
1767 assert_eq!(rel_size, 16);
1768 let r_info = |i: usize| {
1769 u32::from_le_bytes(
1770 elf[rel_off as usize + i * 8 + 4..rel_off as usize + i * 8 + 8]
1771 .try_into()
1772 .unwrap(),
1773 )
1774 };
1775 assert_eq!(r_info(0) >> 8, 1, "BL func_2 reloc remapped to new index 1");
1776 assert_eq!(r_info(0) & 0xff, ArmRelocationType::ThmCall as u32);
1777 assert_eq!(r_info(1) >> 8, 3, "BL external reloc keeps index 3");
1778 }
1779
1780 #[test]
1783 fn test_all_global_symtab_unchanged_sh_info_1_656() {
1784 let mut builder = ElfBuilder::new_arm32()
1785 .with_entry(0)
1786 .with_type(ElfType::Rel);
1787 builder.add_section(
1788 Section::new(".text", SectionType::ProgBits)
1789 .with_flags(SectionFlags::ALLOC | SectionFlags::EXEC)
1790 .with_data(vec![0u8; 8]),
1791 );
1792 for (name, val) in [("a", 0u32), ("b", 4u32)] {
1793 builder.add_symbol(
1794 Symbol::new(name)
1795 .with_value(val)
1796 .with_binding(SymbolBinding::Global)
1797 .with_type(SymbolType::Func)
1798 .with_section(4),
1799 );
1800 }
1801 let elf = builder.build().unwrap();
1802 let (syms, sh_info) = read_symtab(&elf);
1803 assert_eq!(sh_info, 1, "no locals ⇒ sh_info stays 1 (pre-#656 layout)");
1804 assert_eq!(syms[1].0, 1, "a first (insertion order preserved)");
1805 assert_eq!(syms[2].0, 5, "b second");
1806 }
1807
1808 #[test]
1812 fn test_arm_attributes_section_bytes_637() {
1813 let sec = arm_attributes_section(aeabi::CPU_ARCH_V7, aeabi::PROFILE_M, 0, 2, 0, 0);
1815 assert_eq!(sec.name, ".ARM.attributes");
1816 assert_eq!(sec.section_type, SectionType::ArmAttributes);
1817 let d = &sec.data;
1818 assert_eq!(d[0], b'A', "format version");
1819 let vendor_len = u32::from_le_bytes(d[1..5].try_into().unwrap()) as usize;
1820 assert_eq!(vendor_len, d.len() - 1, "vendor subsection length");
1821 assert_eq!(&d[5..11], b"aeabi\0");
1822 assert_eq!(d[11], 1, "Tag_File");
1823 let file_len = u32::from_le_bytes(d[12..16].try_into().unwrap()) as usize;
1824 assert_eq!(file_len, d.len() - 11, "Tag_File length");
1825 let attrs = &d[16..];
1827 assert_eq!(
1828 attrs,
1829 &[
1830 6, 10, 7, b'M', 9, 2, ],
1834 );
1835
1836 let sec = arm_attributes_section(aeabi::CPU_ARCH_V7, aeabi::PROFILE_R, 1, 2, 0, 0);
1838 assert_eq!(&sec.data[16..], &[6, 10, 7, b'R', 8, 1, 9, 2]);
1839
1840 let sec = arm_attributes_section(
1843 aeabi::CPU_ARCH_V7EM,
1844 aeabi::PROFILE_M,
1845 0,
1846 2,
1847 aeabi::FP_ARCH_VFPV4_D16,
1848 aeabi::VFP_ARGS_VFP_REGS,
1849 );
1850 assert_eq!(&sec.data[16..], &[6, 13, 7, b'M', 9, 2, 10, 6, 28, 1]);
1851 }
1852}