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
401fn push_uleb128(out: &mut Vec<u8>, mut v: u32) {
403 loop {
404 let byte = (v & 0x7f) as u8;
405 v >>= 7;
406 if v == 0 {
407 out.push(byte);
408 break;
409 }
410 out.push(byte | 0x80);
411 }
412}
413
414pub fn arm_attributes_section(
421 cpu_arch: u32,
422 cpu_arch_profile: u32,
423 arm_isa_use: u32,
424 thumb_isa_use: u32,
425) -> Section {
426 let mut attrs = Vec::new();
428 for (tag, value) in [
429 (aeabi::TAG_CPU_ARCH, cpu_arch),
430 (aeabi::TAG_CPU_ARCH_PROFILE, cpu_arch_profile),
431 (aeabi::TAG_ARM_ISA_USE, arm_isa_use),
432 (aeabi::TAG_THUMB_ISA_USE, thumb_isa_use),
433 ] {
434 if value != 0 {
435 push_uleb128(&mut attrs, tag);
436 push_uleb128(&mut attrs, value);
437 }
438 }
439
440 let file_len = (1 + 4 + attrs.len()) as u32;
442 let mut file_sub = vec![1u8]; file_sub.extend_from_slice(&file_len.to_le_bytes());
444 file_sub.extend_from_slice(&attrs);
445
446 let vendor_name = b"aeabi\0";
448 let vendor_len = (4 + vendor_name.len() + file_sub.len()) as u32;
449 let mut blob = vec![b'A']; blob.extend_from_slice(&vendor_len.to_le_bytes());
451 blob.extend_from_slice(vendor_name);
452 blob.extend_from_slice(&file_sub);
453
454 Section::new(".ARM.attributes", SectionType::ArmAttributes)
455 .with_align(1)
456 .with_data(blob)
457}
458
459pub struct ElfBuilder {
461 class: ElfClass,
463 data: ElfData,
465 elf_type: ElfType,
467 machine: ElfMachine,
469 entry: u32,
471 e_flags: u32,
473 sections: Vec<Section>,
475 symbols: Vec<Symbol>,
477 program_headers: Vec<ProgramHeader>,
479 relocations: Vec<Relocation>,
481 extra_relocations: Vec<(String, Vec<Relocation>)>,
487 thumb_funcs: bool,
493}
494
495impl ElfBuilder {
496 pub fn new_arm32() -> Self {
498 Self {
499 class: ElfClass::Elf32,
500 data: ElfData::LittleEndian,
501 elf_type: ElfType::Exec,
502 machine: ElfMachine::Arm,
503 entry: 0,
504 e_flags: EF_ARM_EABI_VER5,
505 sections: Vec::new(),
506 symbols: Vec::new(),
507 program_headers: Vec::new(),
508 relocations: Vec::new(),
509 extra_relocations: Vec::new(),
510 thumb_funcs: true,
511 }
512 }
513
514 pub fn with_thumb_funcs(mut self, thumb: bool) -> Self {
518 self.thumb_funcs = thumb;
519 self
520 }
521
522 pub fn with_entry(mut self, entry: u32) -> Self {
528 self.entry = if self.machine == ElfMachine::Arm && self.thumb_funcs {
529 entry | 1 } else {
531 entry
532 };
533 self
534 }
535
536 pub fn set_flags(&mut self, flags: u32) {
538 self.e_flags = flags;
539 }
540
541 pub fn with_type(mut self, elf_type: ElfType) -> Self {
543 self.elf_type = elf_type;
544 self
545 }
546
547 pub fn add_section(&mut self, section: Section) {
549 self.sections.push(section);
550 }
551
552 pub fn add_symbol(&mut self, symbol: Symbol) {
554 self.symbols.push(symbol);
555 }
556
557 pub fn add_symbol_indexed(&mut self, symbol: Symbol) -> u32 {
562 let index = self.symbols.len() as u32 + 1;
563 self.symbols.push(symbol);
564 index
565 }
566
567 pub fn add_program_header(&mut self, ph: ProgramHeader) {
569 self.program_headers.push(ph);
570 }
571
572 pub fn add_relocation(&mut self, reloc: Relocation) {
574 self.relocations.push(reloc);
575 }
576
577 pub fn add_section_relocations(&mut self, target_section: &str, relocs: Vec<Relocation>) {
584 if relocs.is_empty() {
585 return;
586 }
587 self.extra_relocations
588 .push((target_section.to_string(), relocs));
589 }
590
591 pub fn add_undefined_symbol(&mut self, name: &str) -> u32 {
594 let index = self.symbols.len() as u32 + 1; self.symbols.push(Symbol {
596 name: name.to_string(),
597 value: 0,
598 size: 0,
599 binding: SymbolBinding::Global,
600 symbol_type: SymbolType::Func,
601 section: 0, });
603 index
604 }
605
606 pub fn build(&self) -> Result<Vec<u8>> {
608 let mut output = Vec::new();
609
610 let header_size = 52;
612 let ph_entry_size = 32;
614 let ph_count = self.program_headers.len();
615 let ph_table_size = ph_entry_size * ph_count;
616
617 output.resize(header_size + ph_table_size, 0);
619
620 let (shstrtab_data, section_name_offsets, extra_rel_name_offsets) =
622 self.build_section_string_table();
623
624 let (strtab_data, symbol_name_offsets) = self.build_symbol_string_table();
626
627 let mut sym_order: Vec<usize> = (0..self.symbols.len()).collect();
637 sym_order.sort_by_key(|&i| self.symbols[i].binding != SymbolBinding::Local);
638 let mut old_to_new = vec![0u32; self.symbols.len() + 1];
640 for (new_pos, &old) in sym_order.iter().enumerate() {
641 old_to_new[old + 1] = new_pos as u32 + 1;
642 }
643 let local_count = self
644 .symbols
645 .iter()
646 .filter(|s| s.binding == SymbolBinding::Local)
647 .count();
648 let symtab_sh_info = local_count as u32 + 1;
650 let remap_relocs = |relocs: &[Relocation]| -> Vec<Relocation> {
651 relocs
652 .iter()
653 .map(|r| Relocation {
654 offset: r.offset,
655 symbol_index: old_to_new[r.symbol_index as usize],
656 reloc_type: r.reloc_type,
657 })
658 .collect()
659 };
660
661 let mut current_offset = header_size + ph_table_size;
663
664 let shstrtab_offset = current_offset;
666 current_offset += shstrtab_data.len();
667
668 let strtab_offset = current_offset;
670 current_offset += strtab_data.len();
671
672 let mut section_offsets = Vec::new();
674 for section in &self.sections {
675 section_offsets.push(current_offset);
676 current_offset += section.data.len();
677 }
678
679 let symtab_offset = current_offset;
681 let symtab_data = self.build_symbol_table(&symbol_name_offsets, &sym_order);
682 current_offset += symtab_data.len();
683
684 let rel_data = Self::encode_rel_entries(&remap_relocs(&self.relocations));
687 let rel_offset = current_offset;
688 current_offset += rel_data.len();
689
690 let mut extra_rel: Vec<ExtraRelSection> = Vec::new();
695 for (i, (target, relocs)) in self.extra_relocations.iter().enumerate() {
696 let Some(target_idx) = self.section_index_by_name(target) else {
697 continue;
698 };
699 let data = Self::encode_rel_entries(&remap_relocs(relocs));
700 let name_offset = extra_rel_name_offsets.get(i).copied().unwrap_or(0);
701 extra_rel.push(ExtraRelSection {
702 name_offset,
703 target_idx,
704 offset: current_offset,
705 data,
706 });
707 current_offset += extra_rel.last().unwrap().data.len();
708 }
709
710 let sh_offset = current_offset;
712
713 output.extend_from_slice(&shstrtab_data);
715 output.extend_from_slice(&strtab_data);
716
717 for section in &self.sections {
718 output.extend_from_slice(§ion.data);
719 }
720
721 output.extend_from_slice(&symtab_data);
722 output.extend_from_slice(&rel_data);
723 for er in &extra_rel {
724 output.extend_from_slice(&er.data);
725 }
726
727 let section_headers = self.build_section_headers_with_rel(
729 §ion_name_offsets,
730 shstrtab_offset,
731 &shstrtab_data,
732 strtab_offset,
733 &strtab_data,
734 symtab_offset,
735 &symtab_data,
736 §ion_offsets,
737 rel_offset,
738 &rel_data,
739 &extra_rel,
740 symtab_sh_info,
741 );
742 output.extend_from_slice(§ion_headers);
743
744 for (i, ph) in self.program_headers.iter().enumerate() {
747 let ph_offset = header_size + i * ph_entry_size;
748 let mut corrected_ph = ph.clone();
749 if corrected_ph.filesz > 0 {
750 for (si, section) in self.sections.iter().enumerate() {
752 if section.addr == corrected_ph.vaddr && si < section_offsets.len() {
753 corrected_ph.offset = section_offsets[si] as u32;
754 break;
755 }
756 }
757 }
758 self.write_program_header(
759 &mut output[ph_offset..ph_offset + ph_entry_size],
760 &corrected_ph,
761 );
762 }
763
764 let has_rel = !self.relocations.is_empty();
766 let num_sections = 4 + self.sections.len() + if has_rel { 1 } else { 0 } + extra_rel.len();
767 let ph_offset = if ph_count > 0 { header_size as u32 } else { 0 };
768 self.write_elf_header_with_phdrs(
769 &mut output[0..header_size],
770 ph_offset,
771 ph_count as u16,
772 sh_offset as u32,
773 num_sections as u16,
774 )?;
775
776 Ok(output)
777 }
778
779 fn write_program_header(&self, output: &mut [u8], ph: &ProgramHeader) {
781 let mut cursor = 0;
782
783 output[cursor..cursor + 4].copy_from_slice(&(ph.p_type as u32).to_le_bytes());
785 cursor += 4;
786
787 output[cursor..cursor + 4].copy_from_slice(&ph.offset.to_le_bytes());
789 cursor += 4;
790
791 output[cursor..cursor + 4].copy_from_slice(&ph.vaddr.to_le_bytes());
793 cursor += 4;
794
795 output[cursor..cursor + 4].copy_from_slice(&ph.paddr.to_le_bytes());
797 cursor += 4;
798
799 output[cursor..cursor + 4].copy_from_slice(&ph.filesz.to_le_bytes());
801 cursor += 4;
802
803 output[cursor..cursor + 4].copy_from_slice(&ph.memsz.to_le_bytes());
805 cursor += 4;
806
807 output[cursor..cursor + 4].copy_from_slice(&ph.flags.to_le_bytes());
809 cursor += 4;
810
811 output[cursor..cursor + 4].copy_from_slice(&ph.align.to_le_bytes());
813 }
814
815 fn write_elf_header_with_phdrs(
817 &self,
818 output: &mut [u8],
819 ph_offset: u32,
820 ph_count: u16,
821 sh_offset: u32,
822 sh_count: u16,
823 ) -> Result<()> {
824 let mut cursor = 0;
825
826 output[cursor..cursor + 4].copy_from_slice(&[0x7f, b'E', b'L', b'F']);
828 cursor += 4;
829
830 output[cursor] = self.class as u8;
832 cursor += 1;
833
834 output[cursor] = self.data as u8;
836 cursor += 1;
837
838 output[cursor] = 1;
840 cursor += 1;
841
842 output[cursor] = 0; cursor += 1;
845
846 output[cursor] = 0;
848 cursor += 1;
849
850 output[cursor..cursor + 7].copy_from_slice(&[0; 7]);
852 cursor += 7;
853
854 let etype = self.elf_type as u16;
856 output[cursor..cursor + 2].copy_from_slice(&etype.to_le_bytes());
857 cursor += 2;
858
859 let machine = self.machine as u16;
861 output[cursor..cursor + 2].copy_from_slice(&machine.to_le_bytes());
862 cursor += 2;
863
864 output[cursor..cursor + 4].copy_from_slice(&1u32.to_le_bytes());
866 cursor += 4;
867
868 output[cursor..cursor + 4].copy_from_slice(&self.entry.to_le_bytes());
870 cursor += 4;
871
872 output[cursor..cursor + 4].copy_from_slice(&ph_offset.to_le_bytes());
874 cursor += 4;
875
876 output[cursor..cursor + 4].copy_from_slice(&sh_offset.to_le_bytes());
878 cursor += 4;
879
880 output[cursor..cursor + 4].copy_from_slice(&self.e_flags.to_le_bytes());
882 cursor += 4;
883
884 output[cursor..cursor + 2].copy_from_slice(&52u16.to_le_bytes());
886 cursor += 2;
887
888 let ph_entry_size: u16 = if ph_count > 0 { 32 } else { 0 };
890 output[cursor..cursor + 2].copy_from_slice(&ph_entry_size.to_le_bytes());
891 cursor += 2;
892
893 output[cursor..cursor + 2].copy_from_slice(&ph_count.to_le_bytes());
895 cursor += 2;
896
897 output[cursor..cursor + 2].copy_from_slice(&40u16.to_le_bytes());
899 cursor += 2;
900
901 output[cursor..cursor + 2].copy_from_slice(&sh_count.to_le_bytes());
903 cursor += 2;
904
905 output[cursor..cursor + 2].copy_from_slice(&1u16.to_le_bytes());
907
908 Ok(())
909 }
910
911 fn build_section_string_table(&self) -> (Vec<u8>, Vec<usize>, Vec<usize>) {
917 let mut strtab = vec![0]; let mut offsets = Vec::new();
919
920 strtab.extend_from_slice(b".shstrtab\0");
922 strtab.extend_from_slice(b".strtab\0");
923 strtab.extend_from_slice(b".symtab\0");
924
925 for section in &self.sections {
927 let offset = strtab.len();
928 offsets.push(offset);
929 strtab.extend_from_slice(section.name.as_bytes());
930 strtab.push(0);
931 }
932
933 if !self.relocations.is_empty() {
935 strtab.extend_from_slice(b".rel.text\0");
936 }
937
938 let mut extra_rel_offsets = Vec::new();
940 for (target, _) in &self.extra_relocations {
941 let offset = strtab.len();
942 extra_rel_offsets.push(offset);
943 strtab.extend_from_slice(format!(".rel{target}\0").as_bytes());
944 }
945
946 (strtab, offsets, extra_rel_offsets)
947 }
948
949 fn build_symbol_string_table(&self) -> (Vec<u8>, Vec<usize>) {
951 let mut strtab = vec![0]; let mut offsets = Vec::new();
953
954 for symbol in &self.symbols {
955 let offset = strtab.len();
956 offsets.push(offset);
957 strtab.extend_from_slice(symbol.name.as_bytes());
958 strtab.push(0);
959 }
960
961 (strtab, offsets)
962 }
963
964 fn encode_rel_entries(relocs: &[Relocation]) -> Vec<u8> {
967 let mut rel_data = Vec::new();
968 for reloc in relocs {
969 rel_data.extend_from_slice(&reloc.offset.to_le_bytes());
971 let r_info = (reloc.symbol_index << 8) | (reloc.reloc_type as u32);
973 rel_data.extend_from_slice(&r_info.to_le_bytes());
974 }
975 rel_data
976 }
977
978 fn section_index_by_name(&self, name: &str) -> Option<u32> {
982 self.sections
983 .iter()
984 .position(|s| s.name == name)
985 .map(|pos| 4 + pos as u32)
986 }
987
988 fn build_symbol_table(&self, name_offsets: &[usize], order: &[usize]) -> Vec<u8> {
992 let mut symtab = Vec::new();
993
994 symtab.extend_from_slice(&[0u8; 16]); for &i in order {
999 let symbol = &self.symbols[i];
1000 let name_offset = if i < name_offsets.len() {
1001 name_offsets[i] as u32
1002 } else {
1003 0
1004 };
1005
1006 symtab.extend_from_slice(&name_offset.to_le_bytes());
1008
1009 let value = if self.machine == ElfMachine::Arm
1016 && self.thumb_funcs
1017 && symbol.symbol_type == SymbolType::Func
1018 {
1019 symbol.value | 1
1020 } else {
1021 symbol.value
1022 };
1023 symtab.extend_from_slice(&value.to_le_bytes());
1024
1025 symtab.extend_from_slice(&symbol.size.to_le_bytes());
1027
1028 let info = ((symbol.binding as u8) << 4) | (symbol.symbol_type as u8 & 0xf);
1030 symtab.push(info);
1031
1032 symtab.push(0);
1034
1035 symtab.extend_from_slice(&symbol.section.to_le_bytes());
1037 }
1038
1039 symtab
1040 }
1041
1042 #[allow(clippy::too_many_arguments)]
1044 fn build_section_headers_with_rel(
1045 &self,
1046 section_name_offsets: &[usize],
1047 shstrtab_offset: usize,
1048 shstrtab_data: &[u8],
1049 strtab_offset: usize,
1050 strtab_data: &[u8],
1051 symtab_offset: usize,
1052 symtab_data: &[u8],
1053 section_offsets: &[usize],
1054 rel_offset: usize,
1055 rel_data: &[u8],
1056 extra_rel: &[ExtraRelSection],
1057 symtab_sh_info: u32,
1060 ) -> Vec<u8> {
1061 let mut headers = Vec::new();
1062
1063 headers.extend_from_slice(&[0u8; 40]);
1067
1068 self.write_section_header(
1070 &mut headers,
1071 1,
1072 SectionType::StrTab as u32,
1073 0,
1074 0,
1075 shstrtab_offset as u32,
1076 shstrtab_data.len() as u32,
1077 0,
1078 0,
1079 1,
1080 0,
1081 );
1082
1083 let strtab_name_offset = ".shstrtab\0".len();
1085 self.write_section_header(
1086 &mut headers,
1087 strtab_name_offset as u32,
1088 SectionType::StrTab as u32,
1089 0,
1090 0,
1091 strtab_offset as u32,
1092 strtab_data.len() as u32,
1093 0,
1094 0,
1095 1,
1096 0,
1097 );
1098
1099 let symtab_name_offset = ".shstrtab\0.strtab\0".len();
1101 self.write_section_header(
1102 &mut headers,
1103 symtab_name_offset as u32,
1104 SectionType::SymTab as u32,
1105 0,
1106 0,
1107 symtab_offset as u32,
1108 symtab_data.len() as u32,
1109 2,
1110 symtab_sh_info,
1114 4,
1115 16,
1116 );
1117
1118 for (i, section) in self.sections.iter().enumerate() {
1120 let name_offset = if i < section_name_offsets.len() {
1121 section_name_offsets[i] as u32
1122 } else {
1123 0
1124 };
1125 let offset = if i < section_offsets.len() {
1126 section_offsets[i] as u32
1127 } else {
1128 0
1129 };
1130
1131 self.write_section_header(
1132 &mut headers,
1133 name_offset,
1134 section.section_type as u32,
1135 section.flags,
1136 section.addr,
1137 offset,
1138 section.size(),
1139 0,
1140 0,
1141 section.align,
1142 0,
1143 );
1144 }
1145
1146 if !rel_data.is_empty() {
1148 let rel_name_offset = self.rel_text_shstrtab_offset();
1149 let text_section_idx = 4u32; self.write_section_header(
1152 &mut headers,
1153 rel_name_offset as u32,
1154 SectionType::Rel as u32,
1155 0,
1156 0,
1157 rel_offset as u32,
1158 rel_data.len() as u32,
1159 3, text_section_idx, 4,
1162 8, );
1164 }
1165
1166 for er in extra_rel {
1169 self.write_section_header(
1170 &mut headers,
1171 er.name_offset as u32,
1172 SectionType::Rel as u32,
1173 0,
1174 0,
1175 er.offset as u32,
1176 er.data.len() as u32,
1177 3, er.target_idx, 4,
1180 8, );
1182 }
1183
1184 headers
1185 }
1186
1187 fn rel_text_shstrtab_offset(&self) -> usize {
1189 let mut offset = 1 + ".shstrtab\0".len() + ".strtab\0".len() + ".symtab\0".len();
1191 for section in &self.sections {
1192 offset += section.name.len() + 1;
1193 }
1194 offset
1195 }
1196
1197 #[allow(clippy::too_many_arguments)]
1199 fn write_section_header(
1200 &self,
1201 output: &mut Vec<u8>,
1202 name: u32,
1203 sh_type: u32,
1204 flags: u32,
1205 addr: u32,
1206 offset: u32,
1207 size: u32,
1208 link: u32,
1209 info: u32,
1210 align: u32,
1211 entsize: u32,
1212 ) {
1213 output.extend_from_slice(&name.to_le_bytes());
1214 output.extend_from_slice(&sh_type.to_le_bytes());
1215 output.extend_from_slice(&flags.to_le_bytes());
1216 output.extend_from_slice(&addr.to_le_bytes());
1217 output.extend_from_slice(&offset.to_le_bytes());
1218 output.extend_from_slice(&size.to_le_bytes());
1219 output.extend_from_slice(&link.to_le_bytes());
1220 output.extend_from_slice(&info.to_le_bytes());
1221 output.extend_from_slice(&align.to_le_bytes());
1222 output.extend_from_slice(&entsize.to_le_bytes());
1223 }
1224
1225 #[allow(dead_code)]
1227 fn write_elf_header(&self, output: &mut Vec<u8>) -> Result<()> {
1228 output.extend_from_slice(&[0x7f, b'E', b'L', b'F']);
1230
1231 output.push(self.class as u8);
1233
1234 output.push(self.data as u8);
1236
1237 output.push(1);
1239
1240 output.push(0); output.push(0);
1245
1246 output.extend_from_slice(&[0; 7]);
1248
1249 let etype = self.elf_type as u16;
1251 output.extend_from_slice(&etype.to_le_bytes());
1252
1253 let machine = self.machine as u16;
1255 output.extend_from_slice(&machine.to_le_bytes());
1256
1257 output.extend_from_slice(&1u32.to_le_bytes());
1259
1260 output.extend_from_slice(&self.entry.to_le_bytes());
1262
1263 output.extend_from_slice(&0u32.to_le_bytes());
1265
1266 output.extend_from_slice(&0u32.to_le_bytes());
1268
1269 output.extend_from_slice(&0u32.to_le_bytes());
1271
1272 output.extend_from_slice(&52u16.to_le_bytes());
1274
1275 output.extend_from_slice(&0u16.to_le_bytes());
1277
1278 output.extend_from_slice(&0u16.to_le_bytes());
1280
1281 output.extend_from_slice(&40u16.to_le_bytes());
1283
1284 output.extend_from_slice(&0u16.to_le_bytes());
1286
1287 output.extend_from_slice(&0u16.to_le_bytes());
1289
1290 Ok(())
1291 }
1292}
1293
1294#[cfg(test)]
1295mod tests {
1296 use super::*;
1297
1298 #[test]
1299 fn test_elf_builder_creation() {
1300 let builder = ElfBuilder::new_arm32();
1301 assert_eq!(builder.class, ElfClass::Elf32);
1302 assert_eq!(builder.data, ElfData::LittleEndian);
1303 assert_eq!(builder.machine, ElfMachine::Arm);
1304 }
1305
1306 #[test]
1307 fn test_section_creation() {
1308 let section = Section::new(".text", SectionType::ProgBits)
1309 .with_flags(SectionFlags::ALLOC | SectionFlags::EXEC)
1310 .with_addr(0x8000)
1311 .with_align(4);
1312
1313 assert_eq!(section.name, ".text");
1314 assert_eq!(section.section_type, SectionType::ProgBits);
1315 assert_eq!(section.addr, 0x8000);
1316 assert_eq!(section.align, 4);
1317 }
1318
1319 #[test]
1320 fn test_symbol_creation() {
1321 let symbol = Symbol::new("main")
1322 .with_value(0x8000)
1323 .with_size(128)
1324 .with_binding(SymbolBinding::Global)
1325 .with_type(SymbolType::Func)
1326 .with_section(1);
1327
1328 assert_eq!(symbol.name, "main");
1329 assert_eq!(symbol.value, 0x8000);
1330 assert_eq!(symbol.size, 128);
1331 assert_eq!(symbol.binding, SymbolBinding::Global);
1332 assert_eq!(symbol.symbol_type, SymbolType::Func);
1333 }
1334
1335 #[test]
1336 fn test_elf_header_generation() {
1337 let builder = ElfBuilder::new_arm32().with_entry(0x8000);
1338 let elf = builder.build().unwrap();
1339
1340 assert_eq!(&elf[0..4], &[0x7f, b'E', b'L', b'F']);
1342
1343 assert_eq!(elf[4], 1);
1345
1346 assert_eq!(elf[5], 1);
1348
1349 assert_eq!(elf[6], 1);
1351 }
1352
1353 #[test]
1354 fn test_add_sections() {
1355 let mut builder = ElfBuilder::new_arm32();
1356
1357 let text = Section::new(".text", SectionType::ProgBits)
1358 .with_flags(SectionFlags::ALLOC | SectionFlags::EXEC);
1359
1360 let data = Section::new(".data", SectionType::ProgBits)
1361 .with_flags(SectionFlags::ALLOC | SectionFlags::WRITE);
1362
1363 builder.add_section(text);
1364 builder.add_section(data);
1365
1366 assert_eq!(builder.sections.len(), 2);
1367 }
1368
1369 #[test]
1370 fn test_add_symbols() {
1371 let mut builder = ElfBuilder::new_arm32();
1372
1373 let main_sym = Symbol::new("main")
1374 .with_binding(SymbolBinding::Global)
1375 .with_type(SymbolType::Func);
1376
1377 let data_sym = Symbol::new("data")
1378 .with_binding(SymbolBinding::Local)
1379 .with_type(SymbolType::Object);
1380
1381 builder.add_symbol(main_sym);
1382 builder.add_symbol(data_sym);
1383
1384 assert_eq!(builder.symbols.len(), 2);
1385 }
1386
1387 #[test]
1388 fn test_complete_elf_generation() {
1389 let mut builder = ElfBuilder::new_arm32()
1391 .with_entry(0x8000)
1392 .with_type(ElfType::Exec);
1393
1394 let text_code = vec![
1396 0x00, 0x48, 0x2d, 0xe9, 0x04, 0xb0, 0x8d, 0xe2, 0x00, 0x00, 0xa0, 0xe3, 0x00, 0x88, 0xbd, 0xe8, ];
1401 let text = Section::new(".text", SectionType::ProgBits)
1402 .with_flags(SectionFlags::ALLOC | SectionFlags::EXEC)
1403 .with_addr(0x8000)
1404 .with_align(4)
1405 .with_data(text_code);
1406
1407 builder.add_section(text);
1408
1409 let data_content = vec![0x01, 0x02, 0x03, 0x04];
1411 let data = Section::new(".data", SectionType::ProgBits)
1412 .with_flags(SectionFlags::ALLOC | SectionFlags::WRITE)
1413 .with_addr(0x8100)
1414 .with_align(4)
1415 .with_data(data_content);
1416
1417 builder.add_section(data);
1418
1419 let bss = Section::new(".bss", SectionType::NoBits)
1421 .with_flags(SectionFlags::ALLOC | SectionFlags::WRITE)
1422 .with_addr(0x8200)
1423 .with_align(4);
1424
1425 builder.add_section(bss);
1426
1427 let main_sym = Symbol::new("main")
1429 .with_value(0x8000)
1430 .with_size(16)
1431 .with_binding(SymbolBinding::Global)
1432 .with_type(SymbolType::Func)
1433 .with_section(4); builder.add_symbol(main_sym);
1436
1437 let data_var = Symbol::new("global_var")
1438 .with_value(0x8100)
1439 .with_size(4)
1440 .with_binding(SymbolBinding::Global)
1441 .with_type(SymbolType::Object)
1442 .with_section(5); builder.add_symbol(data_var);
1445
1446 let elf = builder.build().unwrap();
1448
1449 assert_eq!(&elf[0..4], &[0x7f, b'E', b'L', b'F']);
1451 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];
1461 let entry = u32::from_le_bytes([
1462 entry_bytes[0],
1463 entry_bytes[1],
1464 entry_bytes[2],
1465 entry_bytes[3],
1466 ]);
1467 assert_eq!(entry, 0x8001); let sh_off_bytes = &elf[32..36];
1471 let sh_off = u32::from_le_bytes([
1472 sh_off_bytes[0],
1473 sh_off_bytes[1],
1474 sh_off_bytes[2],
1475 sh_off_bytes[3],
1476 ]);
1477 assert!(sh_off > 0);
1478
1479 let sh_num_bytes = &elf[48..50];
1481 let sh_num = u16::from_le_bytes([sh_num_bytes[0], sh_num_bytes[1]]);
1482 assert_eq!(sh_num, 7);
1483
1484 let shstrndx_bytes = &elf[50..52];
1486 let shstrndx = u16::from_le_bytes([shstrndx_bytes[0], shstrndx_bytes[1]]);
1487 assert_eq!(shstrndx, 1);
1488 }
1489
1490 #[test]
1491 fn test_string_table_generation() {
1492 let mut builder = ElfBuilder::new_arm32();
1493
1494 builder.add_section(Section::new(".text", SectionType::ProgBits));
1495 builder.add_section(Section::new(".data", SectionType::ProgBits));
1496
1497 let (strtab, offsets, _extra_rel_offsets) = builder.build_section_string_table();
1498
1499 assert_eq!(strtab[0], 0);
1501
1502 let strtab_str = String::from_utf8_lossy(&strtab);
1504 assert!(strtab_str.contains(".shstrtab"));
1505 assert!(strtab_str.contains(".strtab"));
1506 assert!(strtab_str.contains(".symtab"));
1507 assert!(strtab_str.contains(".text"));
1508 assert!(strtab_str.contains(".data"));
1509
1510 assert_eq!(offsets.len(), 2);
1512 }
1513
1514 #[test]
1515 fn test_relocation_support() {
1516 let mut builder = ElfBuilder::new_arm32()
1517 .with_entry(0x8000)
1518 .with_type(ElfType::Rel);
1519
1520 let text_code = vec![0x00u8; 16]; let text = Section::new(".text", SectionType::ProgBits)
1523 .with_flags(SectionFlags::ALLOC | SectionFlags::EXEC)
1524 .with_addr(0x8000)
1525 .with_align(4)
1526 .with_data(text_code);
1527 builder.add_section(text);
1528
1529 let sym_idx = builder.add_undefined_symbol("__meld_dispatch_import");
1531 assert!(sym_idx > 0);
1532
1533 builder.add_relocation(Relocation {
1535 offset: 4,
1536 symbol_index: sym_idx,
1537 reloc_type: ArmRelocationType::Call,
1538 });
1539
1540 let elf = builder.build().unwrap();
1541
1542 assert_eq!(&elf[0..4], &[0x7f, b'E', b'L', b'F']);
1544
1545 let sh_num = u16::from_le_bytes([elf[48], elf[49]]);
1548 assert_eq!(sh_num, 6);
1549
1550 let has_undef = elf
1553 .windows(b"__meld_dispatch_import".len())
1554 .any(|w| w == b"__meld_dispatch_import");
1555 assert!(
1556 has_undef,
1557 "ELF should contain __meld_dispatch_import symbol name"
1558 );
1559 }
1560
1561 #[test]
1562 fn test_symbol_table_encoding() {
1563 let mut builder = ElfBuilder::new_arm32();
1564
1565 let sym = Symbol::new("test_func")
1566 .with_value(0x1000)
1567 .with_size(64)
1568 .with_binding(SymbolBinding::Global)
1569 .with_type(SymbolType::Func)
1570 .with_section(1);
1571
1572 builder.add_symbol(sym);
1573
1574 let (_strtab, offsets) = builder.build_symbol_string_table();
1575 let symtab = builder.build_symbol_table(&offsets, &[0]);
1576
1577 assert_eq!(symtab.len(), 32);
1579
1580 assert!(symtab[0..16].iter().all(|&b| b == 0));
1582
1583 let value_bytes = &symtab[20..24];
1587 let value = u32::from_le_bytes([
1588 value_bytes[0],
1589 value_bytes[1],
1590 value_bytes[2],
1591 value_bytes[3],
1592 ]);
1593 assert_eq!(value, 0x1001); let size_bytes = &symtab[24..28];
1597 let size = u32::from_le_bytes([size_bytes[0], size_bytes[1], size_bytes[2], size_bytes[3]]);
1598 assert_eq!(size, 64);
1599
1600 let info = symtab[28];
1602 let binding = info >> 4;
1603 let sym_type = info & 0xf;
1604 assert_eq!(binding, SymbolBinding::Global as u8);
1605 assert_eq!(sym_type, SymbolType::Func as u8);
1606 }
1607
1608 #[test]
1613 fn test_a32_symbols_have_no_thumb_bit_598() {
1614 let mut builder = ElfBuilder::new_arm32().with_thumb_funcs(false);
1615
1616 let func_sym = Symbol::new("a32_func")
1617 .with_value(0x1000)
1618 .with_size(64)
1619 .with_binding(SymbolBinding::Global)
1620 .with_type(SymbolType::Func)
1621 .with_section(1);
1622 builder.add_symbol(func_sym);
1623
1624 let (_strtab, offsets) = builder.build_symbol_string_table();
1625 let symtab = builder.build_symbol_table(&offsets, &[0]);
1626 let value = u32::from_le_bytes(symtab[20..24].try_into().unwrap());
1627 assert_eq!(value, 0x1000, "A32 STT_FUNC st_value must keep bit 0 clear");
1628
1629 let builder = ElfBuilder::new_arm32()
1631 .with_thumb_funcs(false)
1632 .with_entry(0x8000);
1633 assert_eq!(builder.entry, 0x8000, "A32 e_entry must keep bit 0 clear");
1634
1635 let builder = ElfBuilder::new_arm32().with_entry(0x8000);
1637 assert_eq!(builder.entry, 0x8001, "Thumb e_entry keeps the bit");
1638 }
1639
1640 fn read_section_headers(elf: &[u8]) -> Vec<(u32, u32, u32, u32, u32)> {
1643 let e_shoff = u32::from_le_bytes(elf[32..36].try_into().unwrap()) as usize;
1644 let e_shnum = u16::from_le_bytes(elf[48..50].try_into().unwrap()) as usize;
1645 (0..e_shnum)
1646 .map(|i| {
1647 let base = e_shoff + i * 40;
1648 let f = |off: usize| {
1649 u32::from_le_bytes(elf[base + off..base + off + 4].try_into().unwrap())
1650 };
1651 (f(4), f(16), f(20), f(24), f(28))
1652 })
1653 .collect()
1654 }
1655
1656 fn read_symtab(elf: &[u8]) -> (Vec<(u32, u8, u16)>, u32) {
1658 let headers = read_section_headers(elf);
1659 let &(_, off, size, _, sh_info) = headers
1660 .iter()
1661 .find(|h| h.0 == SectionType::SymTab as u32)
1662 .expect("symtab present");
1663 let syms = (0..size as usize / 16)
1664 .map(|i| {
1665 let base = off as usize + i * 16;
1666 (
1667 u32::from_le_bytes(elf[base + 4..base + 8].try_into().unwrap()),
1668 elf[base + 12],
1669 u16::from_le_bytes(elf[base + 14..base + 16].try_into().unwrap()),
1670 )
1671 })
1672 .collect();
1673 (syms, sh_info)
1674 }
1675
1676 #[test]
1680 fn test_locals_sorted_first_sh_info_and_reloc_reindex_656() {
1681 let mut builder = ElfBuilder::new_arm32()
1682 .with_entry(0)
1683 .with_type(ElfType::Rel);
1684 let text = Section::new(".text", SectionType::ProgBits)
1685 .with_flags(SectionFlags::ALLOC | SectionFlags::EXEC)
1686 .with_align(4)
1687 .with_data(vec![0u8; 16]);
1688 builder.add_section(text);
1689
1690 builder.add_symbol(
1693 Symbol::new("exported")
1694 .with_value(0)
1695 .with_binding(SymbolBinding::Global)
1696 .with_type(SymbolType::Func)
1697 .with_section(4),
1698 ); builder.add_symbol(
1700 Symbol::new("func_2")
1701 .with_value(8)
1702 .with_binding(SymbolBinding::Local)
1703 .with_type(SymbolType::Func)
1704 .with_section(4),
1705 ); let undef_idx = builder.add_undefined_symbol("external"); assert_eq!(undef_idx, 3);
1708
1709 builder.add_relocation(Relocation {
1712 offset: 0,
1713 symbol_index: 2,
1714 reloc_type: ArmRelocationType::ThmCall,
1715 });
1716 builder.add_relocation(Relocation {
1717 offset: 4,
1718 symbol_index: undef_idx,
1719 reloc_type: ArmRelocationType::ThmCall,
1720 });
1721
1722 let elf = builder.build().unwrap();
1723 let (syms, sh_info) = read_symtab(&elf);
1724
1725 assert_eq!(syms.len(), 4);
1727 assert_eq!(syms[0], (0, 0, 0), "null symbol first");
1728 let bind = |info: u8| info >> 4;
1729 assert_eq!(bind(syms[1].1), SymbolBinding::Local as u8, "local first");
1730 assert_eq!(syms[1].0, 8 | 1, "func_2 st_value (thumb bit)");
1731 assert_eq!(bind(syms[2].1), SymbolBinding::Global as u8);
1732 assert_eq!(syms[2].0, 1, "exported st_value 0 | thumb bit");
1733 assert_eq!(bind(syms[3].1), SymbolBinding::Global as u8);
1734 assert_eq!(syms[3].2, 0, "external is SHN_UNDEF");
1735 assert_eq!(sh_info, 2, "sh_info = index of first non-local symbol");
1736
1737 let headers = read_section_headers(&elf);
1739 let &(_, rel_off, rel_size, _, rel_info) = headers
1740 .iter()
1741 .find(|h| h.0 == SectionType::Rel as u32)
1742 .expect(".rel.text present");
1743 assert_eq!(rel_info, 4, ".rel.text still targets .text");
1744 assert_eq!(rel_size, 16);
1745 let r_info = |i: usize| {
1746 u32::from_le_bytes(
1747 elf[rel_off as usize + i * 8 + 4..rel_off as usize + i * 8 + 8]
1748 .try_into()
1749 .unwrap(),
1750 )
1751 };
1752 assert_eq!(r_info(0) >> 8, 1, "BL func_2 reloc remapped to new index 1");
1753 assert_eq!(r_info(0) & 0xff, ArmRelocationType::ThmCall as u32);
1754 assert_eq!(r_info(1) >> 8, 3, "BL external reloc keeps index 3");
1755 }
1756
1757 #[test]
1760 fn test_all_global_symtab_unchanged_sh_info_1_656() {
1761 let mut builder = ElfBuilder::new_arm32()
1762 .with_entry(0)
1763 .with_type(ElfType::Rel);
1764 builder.add_section(
1765 Section::new(".text", SectionType::ProgBits)
1766 .with_flags(SectionFlags::ALLOC | SectionFlags::EXEC)
1767 .with_data(vec![0u8; 8]),
1768 );
1769 for (name, val) in [("a", 0u32), ("b", 4u32)] {
1770 builder.add_symbol(
1771 Symbol::new(name)
1772 .with_value(val)
1773 .with_binding(SymbolBinding::Global)
1774 .with_type(SymbolType::Func)
1775 .with_section(4),
1776 );
1777 }
1778 let elf = builder.build().unwrap();
1779 let (syms, sh_info) = read_symtab(&elf);
1780 assert_eq!(sh_info, 1, "no locals ⇒ sh_info stays 1 (pre-#656 layout)");
1781 assert_eq!(syms[1].0, 1, "a first (insertion order preserved)");
1782 assert_eq!(syms[2].0, 5, "b second");
1783 }
1784
1785 #[test]
1789 fn test_arm_attributes_section_bytes_637() {
1790 let sec = arm_attributes_section(aeabi::CPU_ARCH_V7, aeabi::PROFILE_M, 0, 2);
1792 assert_eq!(sec.name, ".ARM.attributes");
1793 assert_eq!(sec.section_type, SectionType::ArmAttributes);
1794 let d = &sec.data;
1795 assert_eq!(d[0], b'A', "format version");
1796 let vendor_len = u32::from_le_bytes(d[1..5].try_into().unwrap()) as usize;
1797 assert_eq!(vendor_len, d.len() - 1, "vendor subsection length");
1798 assert_eq!(&d[5..11], b"aeabi\0");
1799 assert_eq!(d[11], 1, "Tag_File");
1800 let file_len = u32::from_le_bytes(d[12..16].try_into().unwrap()) as usize;
1801 assert_eq!(file_len, d.len() - 11, "Tag_File length");
1802 let attrs = &d[16..];
1804 assert_eq!(
1805 attrs,
1806 &[
1807 6, 10, 7, b'M', 9, 2, ],
1811 );
1812
1813 let sec = arm_attributes_section(aeabi::CPU_ARCH_V7, aeabi::PROFILE_R, 1, 2);
1815 assert_eq!(&sec.data[16..], &[6, 10, 7, b'R', 8, 1, 9, 2]);
1816 }
1817}