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
159pub use synth_core::backend::SymbolBinding;
164
165#[derive(Debug, Clone, Copy, PartialEq, Eq)]
167pub enum SymbolType {
168 NoType = 0,
170 Object = 1,
172 Func = 2,
174 Section = 3,
176 File = 4,
178}
179
180#[derive(Debug, Clone)]
182pub struct Symbol {
183 pub name: String,
185 pub value: u32,
187 pub size: u32,
189 pub binding: SymbolBinding,
191 pub symbol_type: SymbolType,
193 pub section: u16,
195}
196
197impl Symbol {
198 pub fn new(name: &str) -> Self {
200 Self {
201 name: name.to_string(),
202 value: 0,
203 size: 0,
204 binding: SymbolBinding::Local,
205 symbol_type: SymbolType::NoType,
206 section: 0,
207 }
208 }
209
210 pub fn with_value(mut self, value: u32) -> Self {
212 self.value = value;
213 self
214 }
215
216 pub fn with_size(mut self, size: u32) -> Self {
218 self.size = size;
219 self
220 }
221
222 pub fn with_binding(mut self, binding: SymbolBinding) -> Self {
224 self.binding = binding;
225 self
226 }
227
228 pub fn with_type(mut self, symbol_type: SymbolType) -> Self {
230 self.symbol_type = symbol_type;
231 self
232 }
233
234 pub fn with_section(mut self, section: u16) -> Self {
236 self.section = section;
237 self
238 }
239}
240
241#[derive(Debug, Clone, Copy, PartialEq, Eq)]
243pub enum ProgramType {
244 Null = 0,
246 Load = 1,
248 Dynamic = 2,
250 Interp = 3,
252 Note = 4,
254}
255
256pub struct ProgramFlags;
258
259impl ProgramFlags {
260 pub const EXEC: u32 = 0x1;
262 pub const WRITE: u32 = 0x2;
264 pub const READ: u32 = 0x4;
266}
267
268#[derive(Debug, Clone)]
270pub struct ProgramHeader {
271 pub p_type: ProgramType,
273 pub offset: u32,
275 pub vaddr: u32,
277 pub paddr: u32,
279 pub filesz: u32,
281 pub memsz: u32,
283 pub flags: u32,
285 pub align: u32,
287}
288
289impl ProgramHeader {
290 pub fn load(vaddr: u32, offset: u32, size: u32, flags: u32) -> Self {
292 Self {
293 p_type: ProgramType::Load,
294 offset,
295 vaddr,
296 paddr: vaddr, filesz: size,
298 memsz: size,
299 flags,
300 align: 4,
301 }
302 }
303
304 pub fn load_nobits(vaddr: u32, memsz: u32, flags: u32) -> Self {
307 Self {
308 p_type: ProgramType::Load,
309 offset: 0, vaddr,
311 paddr: vaddr, filesz: 0, memsz, flags,
315 align: 4,
316 }
317 }
318}
319
320#[derive(Debug, Clone, Copy, PartialEq, Eq)]
322pub enum ArmRelocationType {
323 ThmCall = 10,
327 Call = 28,
329 Jump24 = 29,
331 Abs32 = 2,
333 MovwAbsNc = 43,
335 MovtAbs = 44,
337}
338
339struct ExtraRelSection {
343 name_offset: usize,
344 target_idx: u32,
345 offset: usize,
346 data: Vec<u8>,
347}
348
349#[derive(Debug, Clone)]
351pub struct Relocation {
352 pub offset: u32,
354 pub symbol_index: u32,
356 pub reloc_type: ArmRelocationType,
358}
359
360pub const EF_ARM_EABI_VER5: u32 = 0x05000000;
362pub const EF_ARM_ABI_FLOAT_HARD: u32 = 0x00000400;
364pub const EF_ARM_ABI_FLOAT_SOFT: u32 = 0x00000200;
366
367pub mod aeabi {
372 pub const TAG_CPU_ARCH: u32 = 6;
374 pub const TAG_CPU_ARCH_PROFILE: u32 = 7;
376 pub const TAG_ARM_ISA_USE: u32 = 8;
378 pub const TAG_THUMB_ISA_USE: u32 = 9;
380
381 pub const CPU_ARCH_V7: u32 = 10;
383 pub const CPU_ARCH_V6M: u32 = 11;
385 pub const CPU_ARCH_V7EM: u32 = 13;
387 pub const CPU_ARCH_V8_1M_MAIN: u32 = 21;
389
390 pub const PROFILE_M: u32 = b'M' as u32;
392 pub const PROFILE_R: u32 = b'R' as u32;
394
395 pub const TAG_ABI_VFP_ARGS: u32 = 28;
398 pub const TAG_FP_ARCH: u32 = 10;
402 pub const VFP_ARGS_VFP_REGS: u32 = 1;
404 pub const FP_ARCH_VFPV4_D16: u32 = 6;
409}
410
411fn push_uleb128(out: &mut Vec<u8>, mut v: u32) {
413 loop {
414 let byte = (v & 0x7f) as u8;
415 v >>= 7;
416 if v == 0 {
417 out.push(byte);
418 break;
419 }
420 out.push(byte | 0x80);
421 }
422}
423
424pub fn arm_attributes_section(
431 cpu_arch: u32,
432 cpu_arch_profile: u32,
433 arm_isa_use: u32,
434 thumb_isa_use: u32,
435 fp_arch: u32,
436 vfp_args: u32,
437) -> Section {
438 let mut attrs = Vec::new();
444 for (tag, value) in [
445 (aeabi::TAG_CPU_ARCH, cpu_arch),
446 (aeabi::TAG_CPU_ARCH_PROFILE, cpu_arch_profile),
447 (aeabi::TAG_ARM_ISA_USE, arm_isa_use),
448 (aeabi::TAG_THUMB_ISA_USE, thumb_isa_use),
449 (aeabi::TAG_FP_ARCH, fp_arch),
450 (aeabi::TAG_ABI_VFP_ARGS, vfp_args),
451 ] {
452 if value != 0 {
453 push_uleb128(&mut attrs, tag);
454 push_uleb128(&mut attrs, value);
455 }
456 }
457
458 let file_len = (1 + 4 + attrs.len()) as u32;
460 let mut file_sub = vec![1u8]; file_sub.extend_from_slice(&file_len.to_le_bytes());
462 file_sub.extend_from_slice(&attrs);
463
464 let vendor_name = b"aeabi\0";
466 let vendor_len = (4 + vendor_name.len() + file_sub.len()) as u32;
467 let mut blob = vec![b'A']; blob.extend_from_slice(&vendor_len.to_le_bytes());
469 blob.extend_from_slice(vendor_name);
470 blob.extend_from_slice(&file_sub);
471
472 Section::new(".ARM.attributes", SectionType::ArmAttributes)
473 .with_align(1)
474 .with_data(blob)
475}
476
477pub struct ElfBuilder {
479 class: ElfClass,
481 data: ElfData,
483 elf_type: ElfType,
485 machine: ElfMachine,
487 entry: u32,
489 e_flags: u32,
491 sections: Vec<Section>,
493 symbols: Vec<Symbol>,
495 program_headers: Vec<ProgramHeader>,
497 relocations: Vec<Relocation>,
499 extra_relocations: Vec<(String, Vec<Relocation>)>,
505 thumb_funcs: bool,
511}
512
513impl ElfBuilder {
514 pub fn new_arm32() -> Self {
516 Self {
517 class: ElfClass::Elf32,
518 data: ElfData::LittleEndian,
519 elf_type: ElfType::Exec,
520 machine: ElfMachine::Arm,
521 entry: 0,
522 e_flags: EF_ARM_EABI_VER5,
523 sections: Vec::new(),
524 symbols: Vec::new(),
525 program_headers: Vec::new(),
526 relocations: Vec::new(),
527 extra_relocations: Vec::new(),
528 thumb_funcs: true,
529 }
530 }
531
532 pub fn with_thumb_funcs(mut self, thumb: bool) -> Self {
536 self.thumb_funcs = thumb;
537 self
538 }
539
540 pub fn with_entry(mut self, entry: u32) -> Self {
546 self.entry = if self.machine == ElfMachine::Arm && self.thumb_funcs {
547 entry | 1 } else {
549 entry
550 };
551 self
552 }
553
554 pub fn set_flags(&mut self, flags: u32) {
556 self.e_flags = flags;
557 }
558
559 pub fn with_type(mut self, elf_type: ElfType) -> Self {
561 self.elf_type = elf_type;
562 self
563 }
564
565 pub fn add_section(&mut self, section: Section) {
567 self.sections.push(section);
568 }
569
570 pub fn add_symbol(&mut self, symbol: Symbol) {
572 self.symbols.push(symbol);
573 }
574
575 pub fn add_symbol_indexed(&mut self, symbol: Symbol) -> u32 {
580 let index = self.symbols.len() as u32 + 1;
581 self.symbols.push(symbol);
582 index
583 }
584
585 pub fn add_program_header(&mut self, ph: ProgramHeader) {
587 self.program_headers.push(ph);
588 }
589
590 pub fn add_relocation(&mut self, reloc: Relocation) {
592 self.relocations.push(reloc);
593 }
594
595 pub fn add_section_relocations(&mut self, target_section: &str, relocs: Vec<Relocation>) {
602 if relocs.is_empty() {
603 return;
604 }
605 self.extra_relocations
606 .push((target_section.to_string(), relocs));
607 }
608
609 pub fn add_undefined_symbol(&mut self, name: &str) -> u32 {
612 let index = self.symbols.len() as u32 + 1; self.symbols.push(Symbol {
614 name: name.to_string(),
615 value: 0,
616 size: 0,
617 binding: SymbolBinding::Global,
618 symbol_type: SymbolType::Func,
619 section: 0, });
621 index
622 }
623
624 pub fn build(&self) -> Result<Vec<u8>> {
626 let mut output = Vec::new();
627
628 let header_size = 52;
630 let ph_entry_size = 32;
632 let ph_count = self.program_headers.len();
633 let ph_table_size = ph_entry_size * ph_count;
634
635 output.resize(header_size + ph_table_size, 0);
637
638 let (shstrtab_data, section_name_offsets, extra_rel_name_offsets) =
640 self.build_section_string_table();
641
642 let (strtab_data, symbol_name_offsets) = self.build_symbol_string_table();
644
645 let locals = synth_core::backend::locals_first(self.symbols.iter().map(|s| s.binding));
659 let sym_order: Vec<usize> = locals.order;
660 let mut old_to_new = vec![0u32; self.symbols.len() + 1];
662 for (old, &new) in locals.old_to_new.iter().enumerate() {
663 old_to_new[old + 1] = new as u32 + 1;
664 }
665 let symtab_sh_info = locals.local_count as u32 + 1;
667 let remap_relocs = |relocs: &[Relocation]| -> Vec<Relocation> {
668 relocs
669 .iter()
670 .map(|r| Relocation {
671 offset: r.offset,
672 symbol_index: old_to_new[r.symbol_index as usize],
673 reloc_type: r.reloc_type,
674 })
675 .collect()
676 };
677
678 let mut current_offset = header_size + ph_table_size;
680
681 let shstrtab_offset = current_offset;
683 current_offset += shstrtab_data.len();
684
685 let strtab_offset = current_offset;
687 current_offset += strtab_data.len();
688
689 let mut section_offsets = Vec::new();
691 for section in &self.sections {
692 section_offsets.push(current_offset);
693 current_offset += section.data.len();
694 }
695
696 let symtab_offset = current_offset;
698 let symtab_data = self.build_symbol_table(&symbol_name_offsets, &sym_order);
699 current_offset += symtab_data.len();
700
701 let rel_data = Self::encode_rel_entries(&remap_relocs(&self.relocations));
704 let rel_offset = current_offset;
705 current_offset += rel_data.len();
706
707 let mut extra_rel: Vec<ExtraRelSection> = Vec::new();
712 for (i, (target, relocs)) in self.extra_relocations.iter().enumerate() {
713 let Some(target_idx) = self.section_index_by_name(target) else {
714 continue;
715 };
716 let data = Self::encode_rel_entries(&remap_relocs(relocs));
717 let name_offset = extra_rel_name_offsets.get(i).copied().unwrap_or(0);
718 extra_rel.push(ExtraRelSection {
719 name_offset,
720 target_idx,
721 offset: current_offset,
722 data,
723 });
724 current_offset += extra_rel.last().unwrap().data.len();
725 }
726
727 let sh_offset = current_offset;
729
730 output.extend_from_slice(&shstrtab_data);
732 output.extend_from_slice(&strtab_data);
733
734 for section in &self.sections {
735 output.extend_from_slice(§ion.data);
736 }
737
738 output.extend_from_slice(&symtab_data);
739 output.extend_from_slice(&rel_data);
740 for er in &extra_rel {
741 output.extend_from_slice(&er.data);
742 }
743
744 let section_headers = self.build_section_headers_with_rel(
746 §ion_name_offsets,
747 shstrtab_offset,
748 &shstrtab_data,
749 strtab_offset,
750 &strtab_data,
751 symtab_offset,
752 &symtab_data,
753 §ion_offsets,
754 rel_offset,
755 &rel_data,
756 &extra_rel,
757 symtab_sh_info,
758 );
759 output.extend_from_slice(§ion_headers);
760
761 for (i, ph) in self.program_headers.iter().enumerate() {
764 let ph_offset = header_size + i * ph_entry_size;
765 let mut corrected_ph = ph.clone();
766 if corrected_ph.filesz > 0 {
767 for (si, section) in self.sections.iter().enumerate() {
769 if section.addr == corrected_ph.vaddr && si < section_offsets.len() {
770 corrected_ph.offset = section_offsets[si] as u32;
771 break;
772 }
773 }
774 }
775 self.write_program_header(
776 &mut output[ph_offset..ph_offset + ph_entry_size],
777 &corrected_ph,
778 );
779 }
780
781 let has_rel = !self.relocations.is_empty();
783 let num_sections = 4 + self.sections.len() + if has_rel { 1 } else { 0 } + extra_rel.len();
784 let ph_offset = if ph_count > 0 { header_size as u32 } else { 0 };
785 self.write_elf_header_with_phdrs(
786 &mut output[0..header_size],
787 ph_offset,
788 ph_count as u16,
789 sh_offset as u32,
790 num_sections as u16,
791 )?;
792
793 Ok(output)
794 }
795
796 fn write_program_header(&self, output: &mut [u8], ph: &ProgramHeader) {
798 let mut cursor = 0;
799
800 output[cursor..cursor + 4].copy_from_slice(&(ph.p_type as u32).to_le_bytes());
802 cursor += 4;
803
804 output[cursor..cursor + 4].copy_from_slice(&ph.offset.to_le_bytes());
806 cursor += 4;
807
808 output[cursor..cursor + 4].copy_from_slice(&ph.vaddr.to_le_bytes());
810 cursor += 4;
811
812 output[cursor..cursor + 4].copy_from_slice(&ph.paddr.to_le_bytes());
814 cursor += 4;
815
816 output[cursor..cursor + 4].copy_from_slice(&ph.filesz.to_le_bytes());
818 cursor += 4;
819
820 output[cursor..cursor + 4].copy_from_slice(&ph.memsz.to_le_bytes());
822 cursor += 4;
823
824 output[cursor..cursor + 4].copy_from_slice(&ph.flags.to_le_bytes());
826 cursor += 4;
827
828 output[cursor..cursor + 4].copy_from_slice(&ph.align.to_le_bytes());
830 }
831
832 fn write_elf_header_with_phdrs(
834 &self,
835 output: &mut [u8],
836 ph_offset: u32,
837 ph_count: u16,
838 sh_offset: u32,
839 sh_count: u16,
840 ) -> Result<()> {
841 let mut cursor = 0;
842
843 output[cursor..cursor + 4].copy_from_slice(&[0x7f, b'E', b'L', b'F']);
845 cursor += 4;
846
847 output[cursor] = self.class as u8;
849 cursor += 1;
850
851 output[cursor] = self.data as u8;
853 cursor += 1;
854
855 output[cursor] = 1;
857 cursor += 1;
858
859 output[cursor] = 0; cursor += 1;
862
863 output[cursor] = 0;
865 cursor += 1;
866
867 output[cursor..cursor + 7].copy_from_slice(&[0; 7]);
869 cursor += 7;
870
871 let etype = self.elf_type as u16;
873 output[cursor..cursor + 2].copy_from_slice(&etype.to_le_bytes());
874 cursor += 2;
875
876 let machine = self.machine as u16;
878 output[cursor..cursor + 2].copy_from_slice(&machine.to_le_bytes());
879 cursor += 2;
880
881 output[cursor..cursor + 4].copy_from_slice(&1u32.to_le_bytes());
883 cursor += 4;
884
885 output[cursor..cursor + 4].copy_from_slice(&self.entry.to_le_bytes());
887 cursor += 4;
888
889 output[cursor..cursor + 4].copy_from_slice(&ph_offset.to_le_bytes());
891 cursor += 4;
892
893 output[cursor..cursor + 4].copy_from_slice(&sh_offset.to_le_bytes());
895 cursor += 4;
896
897 output[cursor..cursor + 4].copy_from_slice(&self.e_flags.to_le_bytes());
899 cursor += 4;
900
901 output[cursor..cursor + 2].copy_from_slice(&52u16.to_le_bytes());
903 cursor += 2;
904
905 let ph_entry_size: u16 = if ph_count > 0 { 32 } else { 0 };
907 output[cursor..cursor + 2].copy_from_slice(&ph_entry_size.to_le_bytes());
908 cursor += 2;
909
910 output[cursor..cursor + 2].copy_from_slice(&ph_count.to_le_bytes());
912 cursor += 2;
913
914 output[cursor..cursor + 2].copy_from_slice(&40u16.to_le_bytes());
916 cursor += 2;
917
918 output[cursor..cursor + 2].copy_from_slice(&sh_count.to_le_bytes());
920 cursor += 2;
921
922 output[cursor..cursor + 2].copy_from_slice(&1u16.to_le_bytes());
924
925 Ok(())
926 }
927
928 fn build_section_string_table(&self) -> (Vec<u8>, Vec<usize>, Vec<usize>) {
934 let mut strtab = vec![0]; let mut offsets = Vec::new();
936
937 strtab.extend_from_slice(b".shstrtab\0");
939 strtab.extend_from_slice(b".strtab\0");
940 strtab.extend_from_slice(b".symtab\0");
941
942 for section in &self.sections {
944 let offset = strtab.len();
945 offsets.push(offset);
946 strtab.extend_from_slice(section.name.as_bytes());
947 strtab.push(0);
948 }
949
950 if !self.relocations.is_empty() {
952 strtab.extend_from_slice(b".rel.text\0");
953 }
954
955 let mut extra_rel_offsets = Vec::new();
957 for (target, _) in &self.extra_relocations {
958 let offset = strtab.len();
959 extra_rel_offsets.push(offset);
960 strtab.extend_from_slice(format!(".rel{target}\0").as_bytes());
961 }
962
963 (strtab, offsets, extra_rel_offsets)
964 }
965
966 fn build_symbol_string_table(&self) -> (Vec<u8>, Vec<usize>) {
968 let mut strtab = vec![0]; let mut offsets = Vec::new();
970
971 for symbol in &self.symbols {
972 let offset = strtab.len();
973 offsets.push(offset);
974 strtab.extend_from_slice(symbol.name.as_bytes());
975 strtab.push(0);
976 }
977
978 (strtab, offsets)
979 }
980
981 fn encode_rel_entries(relocs: &[Relocation]) -> Vec<u8> {
984 let mut rel_data = Vec::new();
985 for reloc in relocs {
986 rel_data.extend_from_slice(&reloc.offset.to_le_bytes());
988 let r_info = (reloc.symbol_index << 8) | (reloc.reloc_type as u32);
990 rel_data.extend_from_slice(&r_info.to_le_bytes());
991 }
992 rel_data
993 }
994
995 fn section_index_by_name(&self, name: &str) -> Option<u32> {
999 self.sections
1000 .iter()
1001 .position(|s| s.name == name)
1002 .map(|pos| 4 + pos as u32)
1003 }
1004
1005 fn build_symbol_table(&self, name_offsets: &[usize], order: &[usize]) -> Vec<u8> {
1009 let mut symtab = Vec::new();
1010
1011 symtab.extend_from_slice(&[0u8; 16]); for &i in order {
1016 let symbol = &self.symbols[i];
1017 let name_offset = if i < name_offsets.len() {
1018 name_offsets[i] as u32
1019 } else {
1020 0
1021 };
1022
1023 symtab.extend_from_slice(&name_offset.to_le_bytes());
1025
1026 let value = if self.machine == ElfMachine::Arm
1033 && self.thumb_funcs
1034 && symbol.symbol_type == SymbolType::Func
1035 {
1036 symbol.value | 1
1037 } else {
1038 symbol.value
1039 };
1040 symtab.extend_from_slice(&value.to_le_bytes());
1041
1042 symtab.extend_from_slice(&symbol.size.to_le_bytes());
1044
1045 let info = ((symbol.binding as u8) << 4) | (symbol.symbol_type as u8 & 0xf);
1047 symtab.push(info);
1048
1049 symtab.push(0);
1051
1052 symtab.extend_from_slice(&symbol.section.to_le_bytes());
1054 }
1055
1056 symtab
1057 }
1058
1059 fn shstrtab_offset_of(shstrtab: &[u8], name: &str) -> u32 {
1075 let needle: Vec<u8> = std::iter::once(0u8)
1076 .chain(name.as_bytes().iter().copied())
1077 .chain(std::iter::once(0u8))
1078 .collect();
1079 let pos = shstrtab
1080 .windows(needle.len())
1081 .position(|w| w == needle)
1082 .unwrap_or_else(|| panic!("#1307: '{name}' is not in the built .shstrtab"));
1083 (pos + 1) as u32
1086 }
1087
1088 #[allow(clippy::too_many_arguments)]
1090 fn build_section_headers_with_rel(
1091 &self,
1092 section_name_offsets: &[usize],
1093 shstrtab_offset: usize,
1094 shstrtab_data: &[u8],
1095 strtab_offset: usize,
1096 strtab_data: &[u8],
1097 symtab_offset: usize,
1098 symtab_data: &[u8],
1099 section_offsets: &[usize],
1100 rel_offset: usize,
1101 rel_data: &[u8],
1102 extra_rel: &[ExtraRelSection],
1103 symtab_sh_info: u32,
1106 ) -> Vec<u8> {
1107 let mut headers = Vec::new();
1108
1109 headers.extend_from_slice(&[0u8; 40]);
1113
1114 self.write_section_header(
1116 &mut headers,
1117 1,
1118 SectionType::StrTab as u32,
1119 0,
1120 0,
1121 shstrtab_offset as u32,
1122 shstrtab_data.len() as u32,
1123 0,
1124 0,
1125 1,
1126 0,
1127 );
1128
1129 let strtab_name_offset = Self::shstrtab_offset_of(shstrtab_data, ".strtab") as usize;
1131 self.write_section_header(
1132 &mut headers,
1133 strtab_name_offset as u32,
1134 SectionType::StrTab as u32,
1135 0,
1136 0,
1137 strtab_offset as u32,
1138 strtab_data.len() as u32,
1139 0,
1140 0,
1141 1,
1142 0,
1143 );
1144
1145 let symtab_name_offset = Self::shstrtab_offset_of(shstrtab_data, ".symtab") as usize;
1147 self.write_section_header(
1148 &mut headers,
1149 symtab_name_offset as u32,
1150 SectionType::SymTab as u32,
1151 0,
1152 0,
1153 symtab_offset as u32,
1154 symtab_data.len() as u32,
1155 2,
1156 symtab_sh_info,
1160 4,
1161 16,
1162 );
1163
1164 for (i, section) in self.sections.iter().enumerate() {
1166 let name_offset = if i < section_name_offsets.len() {
1167 section_name_offsets[i] as u32
1168 } else {
1169 0
1170 };
1171 let offset = if i < section_offsets.len() {
1172 section_offsets[i] as u32
1173 } else {
1174 0
1175 };
1176
1177 self.write_section_header(
1178 &mut headers,
1179 name_offset,
1180 section.section_type as u32,
1181 section.flags,
1182 section.addr,
1183 offset,
1184 section.size(),
1185 0,
1186 0,
1187 section.align,
1188 0,
1189 );
1190 }
1191
1192 if !rel_data.is_empty() {
1194 let rel_name_offset = self.rel_text_shstrtab_offset();
1195 let text_section_idx = 4u32; self.write_section_header(
1198 &mut headers,
1199 rel_name_offset as u32,
1200 SectionType::Rel as u32,
1201 0,
1202 0,
1203 rel_offset as u32,
1204 rel_data.len() as u32,
1205 3, text_section_idx, 4,
1208 8, );
1210 }
1211
1212 for er in extra_rel {
1215 self.write_section_header(
1216 &mut headers,
1217 er.name_offset as u32,
1218 SectionType::Rel as u32,
1219 0,
1220 0,
1221 er.offset as u32,
1222 er.data.len() as u32,
1223 3, er.target_idx, 4,
1226 8, );
1228 }
1229
1230 headers
1231 }
1232
1233 fn rel_text_shstrtab_offset(&self) -> usize {
1235 let mut offset = 1 + ".shstrtab\0".len() + ".strtab\0".len() + ".symtab\0".len();
1237 for section in &self.sections {
1238 offset += section.name.len() + 1;
1239 }
1240 offset
1241 }
1242
1243 #[allow(clippy::too_many_arguments)]
1245 fn write_section_header(
1246 &self,
1247 output: &mut Vec<u8>,
1248 name: u32,
1249 sh_type: u32,
1250 flags: u32,
1251 addr: u32,
1252 offset: u32,
1253 size: u32,
1254 link: u32,
1255 info: u32,
1256 align: u32,
1257 entsize: u32,
1258 ) {
1259 output.extend_from_slice(&name.to_le_bytes());
1260 output.extend_from_slice(&sh_type.to_le_bytes());
1261 output.extend_from_slice(&flags.to_le_bytes());
1262 output.extend_from_slice(&addr.to_le_bytes());
1263 output.extend_from_slice(&offset.to_le_bytes());
1264 output.extend_from_slice(&size.to_le_bytes());
1265 output.extend_from_slice(&link.to_le_bytes());
1266 output.extend_from_slice(&info.to_le_bytes());
1267 output.extend_from_slice(&align.to_le_bytes());
1268 output.extend_from_slice(&entsize.to_le_bytes());
1269 }
1270
1271 #[allow(dead_code)]
1273 fn write_elf_header(&self, output: &mut Vec<u8>) -> Result<()> {
1274 output.extend_from_slice(&[0x7f, b'E', b'L', b'F']);
1276
1277 output.push(self.class as u8);
1279
1280 output.push(self.data as u8);
1282
1283 output.push(1);
1285
1286 output.push(0); output.push(0);
1291
1292 output.extend_from_slice(&[0; 7]);
1294
1295 let etype = self.elf_type as u16;
1297 output.extend_from_slice(&etype.to_le_bytes());
1298
1299 let machine = self.machine as u16;
1301 output.extend_from_slice(&machine.to_le_bytes());
1302
1303 output.extend_from_slice(&1u32.to_le_bytes());
1305
1306 output.extend_from_slice(&self.entry.to_le_bytes());
1308
1309 output.extend_from_slice(&0u32.to_le_bytes());
1311
1312 output.extend_from_slice(&0u32.to_le_bytes());
1314
1315 output.extend_from_slice(&0u32.to_le_bytes());
1317
1318 output.extend_from_slice(&52u16.to_le_bytes());
1320
1321 output.extend_from_slice(&0u16.to_le_bytes());
1323
1324 output.extend_from_slice(&0u16.to_le_bytes());
1326
1327 output.extend_from_slice(&40u16.to_le_bytes());
1329
1330 output.extend_from_slice(&0u16.to_le_bytes());
1332
1333 output.extend_from_slice(&0u16.to_le_bytes());
1335
1336 Ok(())
1337 }
1338}
1339
1340#[cfg(test)]
1341mod tests {
1342 use super::*;
1343
1344 #[test]
1345 fn test_elf_builder_creation() {
1346 let builder = ElfBuilder::new_arm32();
1347 assert_eq!(builder.class, ElfClass::Elf32);
1348 assert_eq!(builder.data, ElfData::LittleEndian);
1349 assert_eq!(builder.machine, ElfMachine::Arm);
1350 }
1351
1352 #[test]
1353 fn test_section_creation() {
1354 let section = Section::new(".text", SectionType::ProgBits)
1355 .with_flags(SectionFlags::ALLOC | SectionFlags::EXEC)
1356 .with_addr(0x8000)
1357 .with_align(4);
1358
1359 assert_eq!(section.name, ".text");
1360 assert_eq!(section.section_type, SectionType::ProgBits);
1361 assert_eq!(section.addr, 0x8000);
1362 assert_eq!(section.align, 4);
1363 }
1364
1365 #[test]
1366 fn test_symbol_creation() {
1367 let symbol = Symbol::new("main")
1368 .with_value(0x8000)
1369 .with_size(128)
1370 .with_binding(SymbolBinding::Global)
1371 .with_type(SymbolType::Func)
1372 .with_section(1);
1373
1374 assert_eq!(symbol.name, "main");
1375 assert_eq!(symbol.value, 0x8000);
1376 assert_eq!(symbol.size, 128);
1377 assert_eq!(symbol.binding, SymbolBinding::Global);
1378 assert_eq!(symbol.symbol_type, SymbolType::Func);
1379 }
1380
1381 #[test]
1382 fn test_elf_header_generation() {
1383 let builder = ElfBuilder::new_arm32().with_entry(0x8000);
1384 let elf = builder.build().unwrap();
1385
1386 assert_eq!(&elf[0..4], &[0x7f, b'E', b'L', b'F']);
1388
1389 assert_eq!(elf[4], 1);
1391
1392 assert_eq!(elf[5], 1);
1394
1395 assert_eq!(elf[6], 1);
1397 }
1398
1399 #[test]
1400 fn test_add_sections() {
1401 let mut builder = ElfBuilder::new_arm32();
1402
1403 let text = Section::new(".text", SectionType::ProgBits)
1404 .with_flags(SectionFlags::ALLOC | SectionFlags::EXEC);
1405
1406 let data = Section::new(".data", SectionType::ProgBits)
1407 .with_flags(SectionFlags::ALLOC | SectionFlags::WRITE);
1408
1409 builder.add_section(text);
1410 builder.add_section(data);
1411
1412 assert_eq!(builder.sections.len(), 2);
1413 }
1414
1415 #[test]
1416 fn test_add_symbols() {
1417 let mut builder = ElfBuilder::new_arm32();
1418
1419 let main_sym = Symbol::new("main")
1420 .with_binding(SymbolBinding::Global)
1421 .with_type(SymbolType::Func);
1422
1423 let data_sym = Symbol::new("data")
1424 .with_binding(SymbolBinding::Local)
1425 .with_type(SymbolType::Object);
1426
1427 builder.add_symbol(main_sym);
1428 builder.add_symbol(data_sym);
1429
1430 assert_eq!(builder.symbols.len(), 2);
1431 }
1432
1433 #[test]
1434 fn test_complete_elf_generation() {
1435 let mut builder = ElfBuilder::new_arm32()
1437 .with_entry(0x8000)
1438 .with_type(ElfType::Exec);
1439
1440 let text_code = vec![
1442 0x00, 0x48, 0x2d, 0xe9, 0x04, 0xb0, 0x8d, 0xe2, 0x00, 0x00, 0xa0, 0xe3, 0x00, 0x88, 0xbd, 0xe8, ];
1447 let text = Section::new(".text", SectionType::ProgBits)
1448 .with_flags(SectionFlags::ALLOC | SectionFlags::EXEC)
1449 .with_addr(0x8000)
1450 .with_align(4)
1451 .with_data(text_code);
1452
1453 builder.add_section(text);
1454
1455 let data_content = vec![0x01, 0x02, 0x03, 0x04];
1457 let data = Section::new(".data", SectionType::ProgBits)
1458 .with_flags(SectionFlags::ALLOC | SectionFlags::WRITE)
1459 .with_addr(0x8100)
1460 .with_align(4)
1461 .with_data(data_content);
1462
1463 builder.add_section(data);
1464
1465 let bss = Section::new(".bss", SectionType::NoBits)
1467 .with_flags(SectionFlags::ALLOC | SectionFlags::WRITE)
1468 .with_addr(0x8200)
1469 .with_align(4);
1470
1471 builder.add_section(bss);
1472
1473 let main_sym = Symbol::new("main")
1475 .with_value(0x8000)
1476 .with_size(16)
1477 .with_binding(SymbolBinding::Global)
1478 .with_type(SymbolType::Func)
1479 .with_section(4); builder.add_symbol(main_sym);
1482
1483 let data_var = Symbol::new("global_var")
1484 .with_value(0x8100)
1485 .with_size(4)
1486 .with_binding(SymbolBinding::Global)
1487 .with_type(SymbolType::Object)
1488 .with_section(5); builder.add_symbol(data_var);
1491
1492 let elf = builder.build().unwrap();
1494
1495 assert_eq!(&elf[0..4], &[0x7f, b'E', b'L', b'F']);
1497 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];
1507 let entry = u32::from_le_bytes([
1508 entry_bytes[0],
1509 entry_bytes[1],
1510 entry_bytes[2],
1511 entry_bytes[3],
1512 ]);
1513 assert_eq!(entry, 0x8001); let sh_off_bytes = &elf[32..36];
1517 let sh_off = u32::from_le_bytes([
1518 sh_off_bytes[0],
1519 sh_off_bytes[1],
1520 sh_off_bytes[2],
1521 sh_off_bytes[3],
1522 ]);
1523 assert!(sh_off > 0);
1524
1525 let sh_num_bytes = &elf[48..50];
1527 let sh_num = u16::from_le_bytes([sh_num_bytes[0], sh_num_bytes[1]]);
1528 assert_eq!(sh_num, 7);
1529
1530 let shstrndx_bytes = &elf[50..52];
1532 let shstrndx = u16::from_le_bytes([shstrndx_bytes[0], shstrndx_bytes[1]]);
1533 assert_eq!(shstrndx, 1);
1534 }
1535
1536 #[test]
1537 fn test_string_table_generation() {
1538 let mut builder = ElfBuilder::new_arm32();
1539
1540 builder.add_section(Section::new(".text", SectionType::ProgBits));
1541 builder.add_section(Section::new(".data", SectionType::ProgBits));
1542
1543 let (strtab, offsets, _extra_rel_offsets) = builder.build_section_string_table();
1544
1545 assert_eq!(strtab[0], 0);
1547
1548 let strtab_str = String::from_utf8_lossy(&strtab);
1550 assert!(strtab_str.contains(".shstrtab"));
1551 assert!(strtab_str.contains(".strtab"));
1552 assert!(strtab_str.contains(".symtab"));
1553 assert!(strtab_str.contains(".text"));
1554 assert!(strtab_str.contains(".data"));
1555
1556 assert_eq!(offsets.len(), 2);
1558 }
1559
1560 #[test]
1561 fn test_relocation_support() {
1562 let mut builder = ElfBuilder::new_arm32()
1563 .with_entry(0x8000)
1564 .with_type(ElfType::Rel);
1565
1566 let text_code = vec![0x00u8; 16]; let text = Section::new(".text", SectionType::ProgBits)
1569 .with_flags(SectionFlags::ALLOC | SectionFlags::EXEC)
1570 .with_addr(0x8000)
1571 .with_align(4)
1572 .with_data(text_code);
1573 builder.add_section(text);
1574
1575 let sym_idx = builder.add_undefined_symbol("__meld_dispatch_import");
1577 assert!(sym_idx > 0);
1578
1579 builder.add_relocation(Relocation {
1581 offset: 4,
1582 symbol_index: sym_idx,
1583 reloc_type: ArmRelocationType::Call,
1584 });
1585
1586 let elf = builder.build().unwrap();
1587
1588 assert_eq!(&elf[0..4], &[0x7f, b'E', b'L', b'F']);
1590
1591 let sh_num = u16::from_le_bytes([elf[48], elf[49]]);
1594 assert_eq!(sh_num, 6);
1595
1596 let has_undef = elf
1599 .windows(b"__meld_dispatch_import".len())
1600 .any(|w| w == b"__meld_dispatch_import");
1601 assert!(
1602 has_undef,
1603 "ELF should contain __meld_dispatch_import symbol name"
1604 );
1605 }
1606
1607 #[test]
1608 fn test_symbol_table_encoding() {
1609 let mut builder = ElfBuilder::new_arm32();
1610
1611 let sym = Symbol::new("test_func")
1612 .with_value(0x1000)
1613 .with_size(64)
1614 .with_binding(SymbolBinding::Global)
1615 .with_type(SymbolType::Func)
1616 .with_section(1);
1617
1618 builder.add_symbol(sym);
1619
1620 let (_strtab, offsets) = builder.build_symbol_string_table();
1621 let symtab = builder.build_symbol_table(&offsets, &[0]);
1622
1623 assert_eq!(symtab.len(), 32);
1625
1626 assert!(symtab[0..16].iter().all(|&b| b == 0));
1628
1629 let value_bytes = &symtab[20..24];
1633 let value = u32::from_le_bytes([
1634 value_bytes[0],
1635 value_bytes[1],
1636 value_bytes[2],
1637 value_bytes[3],
1638 ]);
1639 assert_eq!(value, 0x1001); let size_bytes = &symtab[24..28];
1643 let size = u32::from_le_bytes([size_bytes[0], size_bytes[1], size_bytes[2], size_bytes[3]]);
1644 assert_eq!(size, 64);
1645
1646 let info = symtab[28];
1648 let binding = info >> 4;
1649 let sym_type = info & 0xf;
1650 assert_eq!(binding, SymbolBinding::Global as u8);
1651 assert_eq!(sym_type, SymbolType::Func as u8);
1652 }
1653
1654 #[test]
1659 fn test_a32_symbols_have_no_thumb_bit_598() {
1660 let mut builder = ElfBuilder::new_arm32().with_thumb_funcs(false);
1661
1662 let func_sym = Symbol::new("a32_func")
1663 .with_value(0x1000)
1664 .with_size(64)
1665 .with_binding(SymbolBinding::Global)
1666 .with_type(SymbolType::Func)
1667 .with_section(1);
1668 builder.add_symbol(func_sym);
1669
1670 let (_strtab, offsets) = builder.build_symbol_string_table();
1671 let symtab = builder.build_symbol_table(&offsets, &[0]);
1672 let value = u32::from_le_bytes(symtab[20..24].try_into().unwrap());
1673 assert_eq!(value, 0x1000, "A32 STT_FUNC st_value must keep bit 0 clear");
1674
1675 let builder = ElfBuilder::new_arm32()
1677 .with_thumb_funcs(false)
1678 .with_entry(0x8000);
1679 assert_eq!(builder.entry, 0x8000, "A32 e_entry must keep bit 0 clear");
1680
1681 let builder = ElfBuilder::new_arm32().with_entry(0x8000);
1683 assert_eq!(builder.entry, 0x8001, "Thumb e_entry keeps the bit");
1684 }
1685
1686 fn read_section_headers(elf: &[u8]) -> Vec<(u32, u32, u32, u32, u32)> {
1689 let e_shoff = u32::from_le_bytes(elf[32..36].try_into().unwrap()) as usize;
1690 let e_shnum = u16::from_le_bytes(elf[48..50].try_into().unwrap()) as usize;
1691 (0..e_shnum)
1692 .map(|i| {
1693 let base = e_shoff + i * 40;
1694 let f = |off: usize| {
1695 u32::from_le_bytes(elf[base + off..base + off + 4].try_into().unwrap())
1696 };
1697 (f(4), f(16), f(20), f(24), f(28))
1698 })
1699 .collect()
1700 }
1701
1702 fn read_symtab(elf: &[u8]) -> (Vec<(u32, u8, u16)>, u32) {
1704 let headers = read_section_headers(elf);
1705 let &(_, off, size, _, sh_info) = headers
1706 .iter()
1707 .find(|h| h.0 == SectionType::SymTab as u32)
1708 .expect("symtab present");
1709 let syms = (0..size as usize / 16)
1710 .map(|i| {
1711 let base = off as usize + i * 16;
1712 (
1713 u32::from_le_bytes(elf[base + 4..base + 8].try_into().unwrap()),
1714 elf[base + 12],
1715 u16::from_le_bytes(elf[base + 14..base + 16].try_into().unwrap()),
1716 )
1717 })
1718 .collect();
1719 (syms, sh_info)
1720 }
1721
1722 #[test]
1726 fn test_locals_sorted_first_sh_info_and_reloc_reindex_656() {
1727 let mut builder = ElfBuilder::new_arm32()
1728 .with_entry(0)
1729 .with_type(ElfType::Rel);
1730 let text = Section::new(".text", SectionType::ProgBits)
1731 .with_flags(SectionFlags::ALLOC | SectionFlags::EXEC)
1732 .with_align(4)
1733 .with_data(vec![0u8; 16]);
1734 builder.add_section(text);
1735
1736 builder.add_symbol(
1739 Symbol::new("exported")
1740 .with_value(0)
1741 .with_binding(SymbolBinding::Global)
1742 .with_type(SymbolType::Func)
1743 .with_section(4),
1744 ); builder.add_symbol(
1746 Symbol::new("func_2")
1747 .with_value(8)
1748 .with_binding(SymbolBinding::Local)
1749 .with_type(SymbolType::Func)
1750 .with_section(4),
1751 ); let undef_idx = builder.add_undefined_symbol("external"); assert_eq!(undef_idx, 3);
1754
1755 builder.add_relocation(Relocation {
1758 offset: 0,
1759 symbol_index: 2,
1760 reloc_type: ArmRelocationType::ThmCall,
1761 });
1762 builder.add_relocation(Relocation {
1763 offset: 4,
1764 symbol_index: undef_idx,
1765 reloc_type: ArmRelocationType::ThmCall,
1766 });
1767
1768 let elf = builder.build().unwrap();
1769 let (syms, sh_info) = read_symtab(&elf);
1770
1771 assert_eq!(syms.len(), 4);
1773 assert_eq!(syms[0], (0, 0, 0), "null symbol first");
1774 let bind = |info: u8| info >> 4;
1775 assert_eq!(bind(syms[1].1), SymbolBinding::Local as u8, "local first");
1776 assert_eq!(syms[1].0, 8 | 1, "func_2 st_value (thumb bit)");
1777 assert_eq!(bind(syms[2].1), SymbolBinding::Global as u8);
1778 assert_eq!(syms[2].0, 1, "exported st_value 0 | thumb bit");
1779 assert_eq!(bind(syms[3].1), SymbolBinding::Global as u8);
1780 assert_eq!(syms[3].2, 0, "external is SHN_UNDEF");
1781 assert_eq!(sh_info, 2, "sh_info = index of first non-local symbol");
1782
1783 let headers = read_section_headers(&elf);
1785 let &(_, rel_off, rel_size, _, rel_info) = headers
1786 .iter()
1787 .find(|h| h.0 == SectionType::Rel as u32)
1788 .expect(".rel.text present");
1789 assert_eq!(rel_info, 4, ".rel.text still targets .text");
1790 assert_eq!(rel_size, 16);
1791 let r_info = |i: usize| {
1792 u32::from_le_bytes(
1793 elf[rel_off as usize + i * 8 + 4..rel_off as usize + i * 8 + 8]
1794 .try_into()
1795 .unwrap(),
1796 )
1797 };
1798 assert_eq!(r_info(0) >> 8, 1, "BL func_2 reloc remapped to new index 1");
1799 assert_eq!(r_info(0) & 0xff, ArmRelocationType::ThmCall as u32);
1800 assert_eq!(r_info(1) >> 8, 3, "BL external reloc keeps index 3");
1801 }
1802
1803 #[test]
1806 fn test_all_global_symtab_unchanged_sh_info_1_656() {
1807 let mut builder = ElfBuilder::new_arm32()
1808 .with_entry(0)
1809 .with_type(ElfType::Rel);
1810 builder.add_section(
1811 Section::new(".text", SectionType::ProgBits)
1812 .with_flags(SectionFlags::ALLOC | SectionFlags::EXEC)
1813 .with_data(vec![0u8; 8]),
1814 );
1815 for (name, val) in [("a", 0u32), ("b", 4u32)] {
1816 builder.add_symbol(
1817 Symbol::new(name)
1818 .with_value(val)
1819 .with_binding(SymbolBinding::Global)
1820 .with_type(SymbolType::Func)
1821 .with_section(4),
1822 );
1823 }
1824 let elf = builder.build().unwrap();
1825 let (syms, sh_info) = read_symtab(&elf);
1826 assert_eq!(sh_info, 1, "no locals ⇒ sh_info stays 1 (pre-#656 layout)");
1827 assert_eq!(syms[1].0, 1, "a first (insertion order preserved)");
1828 assert_eq!(syms[2].0, 5, "b second");
1829 }
1830
1831 #[test]
1835 fn test_arm_attributes_section_bytes_637() {
1836 let sec = arm_attributes_section(aeabi::CPU_ARCH_V7, aeabi::PROFILE_M, 0, 2, 0, 0);
1838 assert_eq!(sec.name, ".ARM.attributes");
1839 assert_eq!(sec.section_type, SectionType::ArmAttributes);
1840 let d = &sec.data;
1841 assert_eq!(d[0], b'A', "format version");
1842 let vendor_len = u32::from_le_bytes(d[1..5].try_into().unwrap()) as usize;
1843 assert_eq!(vendor_len, d.len() - 1, "vendor subsection length");
1844 assert_eq!(&d[5..11], b"aeabi\0");
1845 assert_eq!(d[11], 1, "Tag_File");
1846 let file_len = u32::from_le_bytes(d[12..16].try_into().unwrap()) as usize;
1847 assert_eq!(file_len, d.len() - 11, "Tag_File length");
1848 let attrs = &d[16..];
1850 assert_eq!(
1851 attrs,
1852 &[
1853 6, 10, 7, b'M', 9, 2, ],
1857 );
1858
1859 let sec = arm_attributes_section(aeabi::CPU_ARCH_V7, aeabi::PROFILE_R, 1, 2, 0, 0);
1861 assert_eq!(&sec.data[16..], &[6, 10, 7, b'R', 8, 1, 9, 2]);
1862
1863 let sec = arm_attributes_section(
1866 aeabi::CPU_ARCH_V7EM,
1867 aeabi::PROFILE_M,
1868 0,
1869 2,
1870 aeabi::FP_ARCH_VFPV4_D16,
1871 aeabi::VFP_ARGS_VFP_REGS,
1872 );
1873 assert_eq!(&sec.data[16..], &[6, 13, 7, b'M', 9, 2, 10, 6, 28, 1]);
1874 }
1875}