1use {
2 crate::{
3 debug::{self, DebugData, reuse_debug_sections},
4 dynsym::{DynamicSymbol, RelDyn, RelocationType},
5 header::{ElfHeader, ProgramHeader},
6 parser::ProgramLayout,
7 section::{
8 DebugSection, DynStrSection, DynSymSection, DynamicSection, NullSection, RelDynSection,
9 Section, SectionType, ShStrTabSection,
10 },
11 },
12 std::{fs::File, io::Write, path::Path},
13};
14
15#[derive(Debug)]
16pub struct Program {
17 pub elf_header: ElfHeader,
18 pub program_headers: Option<Vec<ProgramHeader>>,
19 pub sections: Vec<SectionType>,
20}
21
22impl Program {
23 pub fn from_parse_result(
24 ProgramLayout {
25 code_section,
26 data_section,
27 dynamic_symbols,
28 relocation_data,
29 prog_is_static,
30 arch,
31 debug_sections,
32 }: ProgramLayout,
33 debug_data: Option<DebugData>,
34 ) -> Self {
35 let mut elf_header = ElfHeader::new();
36 let mut program_headers = None;
37
38 let bytecode_size = code_section.size();
39 let rodata_size = data_section.size();
40
41 let has_rodata = rodata_size > 0;
42 let ph_count = if arch.is_v3() {
43 if has_rodata { 2 } else { 1 }
44 } else if prog_is_static {
45 0
46 } else {
47 3
48 };
49
50 elf_header.e_flags = arch.e_flags();
51 elf_header.e_phnum = ph_count;
52
53 let text_size = bytecode_size + rodata_size;
56
57 let base_offset = 64 + (ph_count as u64 * 56); let mut current_offset = base_offset;
60
61 let text_offset = if arch.is_v3() && has_rodata {
62 rodata_size + base_offset
63 } else {
64 base_offset
65 };
66
67 let entry_point_offset = dynamic_symbols
69 .get_entry_points()
70 .first()
71 .map(|(_, offset)| *offset)
72 .unwrap_or(0);
73
74 elf_header.e_entry = if arch.is_v3() {
75 ProgramHeader::V3_BYTECODE_VADDR + entry_point_offset
76 } else {
77 text_offset + entry_point_offset
78 };
79
80 let mut sections = Vec::new();
82 sections.push(SectionType::Default(NullSection::new()));
83
84 let mut section_names = Vec::new();
85 let has_debug_sections = debug_data.is_some() || !debug_sections.is_empty();
86
87 section_names.push(".text".to_string());
89 if has_rodata {
90 section_names.push(".rodata".to_string());
91 }
92
93 if arch.is_v3() && has_rodata {
94 let mut rodata_section = SectionType::Data(data_section);
96 rodata_section.set_offset(current_offset);
97 rodata_section.set_vaddr(ProgramHeader::V3_RODATA_VADDR);
98 current_offset += rodata_section.size();
99 sections.push(rodata_section);
100
101 let mut text_section = SectionType::Code(code_section);
103 text_section.set_offset(current_offset);
104 text_section.set_vaddr(ProgramHeader::V3_BYTECODE_VADDR);
105 current_offset += text_section.size();
106 sections.push(text_section);
107 } else {
108 let mut text_section = SectionType::Code(code_section);
110 text_section.set_offset(current_offset);
111 if arch.is_v3() {
112 text_section.set_vaddr(ProgramHeader::V3_BYTECODE_VADDR);
113 }
114 current_offset += text_section.size();
115 sections.push(text_section);
116
117 if has_rodata {
119 let mut rodata_section = SectionType::Data(data_section);
120 rodata_section.set_offset(current_offset);
121 if arch.is_v3() {
122 rodata_section.set_vaddr(ProgramHeader::V3_RODATA_VADDR);
123 }
124 current_offset += rodata_section.size();
125 sections.push(rodata_section);
126 }
127 }
128
129 let padding = (8 - (current_offset % 8)) % 8;
130 current_offset += padding;
131
132 if arch.is_v3() {
133 if has_rodata {
139 let rodata_offset = base_offset;
141 let bytecode_offset = base_offset + rodata_size;
142 program_headers = Some(vec![
143 ProgramHeader::new_load(rodata_offset, rodata_size, false, arch),
144 ProgramHeader::new_load(bytecode_offset, bytecode_size, true, arch),
145 ]);
146 } else {
147 program_headers = Some(vec![ProgramHeader::new_load(
149 base_offset,
150 bytecode_size,
151 true,
152 arch,
153 )]);
154 }
155
156 if has_debug_sections {
157 let debug_sections = Self::generate_debug_sections(
159 debug_sections,
160 &debug_data,
161 ProgramHeader::V3_BYTECODE_VADDR,
162 &mut section_names,
163 &mut current_offset,
164 );
165
166 for debug_section in debug_sections {
167 sections.push(debug_section);
168 }
169
170 let mut shstrtab_section = SectionType::ShStrTab(ShStrTabSection::new(
171 (section_names
172 .iter()
173 .map(|name| name.len() + 1)
174 .sum::<usize>()
175 + 1) as u32,
176 section_names,
177 ));
178 shstrtab_section.set_offset(current_offset);
179 current_offset += shstrtab_section.size();
180 sections.push(shstrtab_section);
181 }
182 } else if !prog_is_static {
183 let mut symbol_names = Vec::new();
184 let mut dyn_syms = Vec::new();
185 let mut dyn_str_offset = 1;
186
187 dyn_syms.push(DynamicSymbol::new(0, 0, 0, 0, 0, 0));
188
189 for (name, _) in dynamic_symbols.get_entry_points() {
191 symbol_names.push(name.clone());
192 dyn_syms.push(DynamicSymbol::new(
193 dyn_str_offset as u32,
194 0x10,
195 0,
196 1,
197 elf_header.e_entry,
198 0,
199 ));
200 dyn_str_offset += name.len() + 1;
201 }
202
203 for (name, _) in dynamic_symbols.get_call_targets() {
204 symbol_names.push(name.clone());
205 dyn_syms.push(DynamicSymbol::new(dyn_str_offset as u32, 0x10, 0, 0, 0, 0));
206 dyn_str_offset += name.len() + 1;
207 }
208
209 let mut rel_count = 0;
210 let mut rel_dyns = Vec::new();
211 for (offset, rel_type, name) in relocation_data.get_rel_dyns() {
212 if rel_type == RelocationType::RSbfSyscall {
213 if let Some(index) = symbol_names.iter().position(|n| *n == name) {
214 rel_dyns.push(RelDyn::new(
215 offset + text_offset,
216 rel_type as u64,
217 index as u64 + 1,
218 ));
219 } else {
220 panic!("Symbol {} not found in symbol_names", name);
221 }
222 } else if rel_type == RelocationType::RSbf64Relative {
223 rel_count += 1;
224 rel_dyns.push(RelDyn::new(offset + text_offset, rel_type as u64, 0));
225 }
226 }
227 let mut dynamic_section = SectionType::Dynamic(DynamicSection::new(
229 (section_names
230 .iter()
231 .map(|name| name.len() + 1)
232 .sum::<usize>()
233 + 1) as u32,
234 ));
235 section_names.push(dynamic_section.name().to_string());
236
237 let mut dynsym_section = SectionType::DynSym(DynSymSection::new(
238 (section_names
239 .iter()
240 .map(|name| name.len() + 1)
241 .sum::<usize>()
242 + 1) as u32,
243 dyn_syms,
244 ));
245 section_names.push(dynsym_section.name().to_string());
246
247 let mut dynstr_section = SectionType::DynStr(DynStrSection::new(
248 (section_names
249 .iter()
250 .map(|name| name.len() + 1)
251 .sum::<usize>()
252 + 1) as u32,
253 symbol_names,
254 ));
255 section_names.push(dynstr_section.name().to_string());
256
257 let mut rel_dyn_section = SectionType::RelDyn(RelDynSection::new(
258 (section_names
259 .iter()
260 .map(|name| name.len() + 1)
261 .sum::<usize>()
262 + 1) as u32,
263 rel_dyns,
264 ));
265 section_names.push(rel_dyn_section.name().to_string());
266
267 dynamic_section.set_offset(current_offset);
268 if let SectionType::Dynamic(ref mut dynamic_section) = dynamic_section {
269 dynamic_section.set_link(
271 section_names
272 .iter()
273 .position(|name| name == ".dynstr")
274 .expect("missing .dynstr section") as u32
275 + 1,
276 );
277 dynamic_section.set_rel_count(rel_count);
278 }
279 current_offset += dynamic_section.size();
280
281 dynsym_section.set_offset(current_offset);
282 if let SectionType::DynSym(ref mut dynsym_section) = dynsym_section {
283 dynsym_section.set_link(
285 section_names
286 .iter()
287 .position(|name| name == ".dynstr")
288 .expect("missing .dynstr section") as u32
289 + 1,
290 );
291 }
292 current_offset += dynsym_section.size();
293
294 dynstr_section.set_offset(current_offset);
295 current_offset += dynstr_section.size();
296
297 rel_dyn_section.set_offset(current_offset);
298 if let SectionType::RelDyn(ref mut rel_dyn_section) = rel_dyn_section {
299 rel_dyn_section.set_link(
301 section_names
302 .iter()
303 .position(|name| name == ".dynsym")
304 .expect("missing .dynsym section") as u32
305 + 1,
306 );
307 }
308 current_offset += rel_dyn_section.size();
309
310 if let SectionType::Dynamic(ref mut dynamic_section) = dynamic_section {
311 dynamic_section.set_rel_offset(rel_dyn_section.offset());
312 dynamic_section.set_rel_size(rel_dyn_section.size());
313 dynamic_section.set_dynsym_offset(dynsym_section.offset());
314 dynamic_section.set_dynstr_offset(dynstr_section.offset());
315 dynamic_section.set_dynstr_size(dynstr_section.size());
316 }
317
318 let debug_sections = Self::generate_debug_sections(
320 debug_sections,
321 &debug_data,
322 text_offset,
323 &mut section_names,
324 &mut current_offset,
325 );
326
327 let mut shstrtab_section = SectionType::ShStrTab(ShStrTabSection::new(
328 (section_names
329 .iter()
330 .map(|name| name.len() + 1)
331 .sum::<usize>()
332 + 1) as u32,
333 section_names,
334 ));
335 shstrtab_section.set_offset(current_offset);
336 current_offset += shstrtab_section.size();
337
338 program_headers = Some(vec![
339 ProgramHeader::new_load(
340 text_offset,
341 text_size,
342 true, arch,
344 ),
345 ProgramHeader::new_load(
346 dynsym_section.offset(),
347 dynsym_section.size() + dynstr_section.size() + rel_dyn_section.size(),
348 false,
349 arch,
350 ),
351 ProgramHeader::new_dynamic(dynamic_section.offset(), dynamic_section.size()),
352 ]);
353
354 sections.push(dynamic_section);
355 sections.push(dynsym_section);
356 sections.push(dynstr_section);
357 sections.push(rel_dyn_section);
358
359 for debug_section in debug_sections {
360 sections.push(debug_section);
361 }
362
363 sections.push(shstrtab_section);
364 } else {
365 let mut section_names = Vec::new();
367 for section in §ions {
368 section_names.push(section.name().to_string());
369 }
370
371 let debug_sections = Self::generate_debug_sections(
373 debug_sections,
374 &debug_data,
375 text_offset,
376 &mut section_names,
377 &mut current_offset,
378 );
379
380 for debug_section in debug_sections {
381 sections.push(debug_section);
382 }
383
384 let mut shstrtab_section = ShStrTabSection::new(
385 section_names
386 .iter()
387 .map(|name| name.len() + 1)
388 .sum::<usize>() as u32,
389 section_names,
390 );
391 shstrtab_section.set_offset(current_offset);
392 current_offset += shstrtab_section.size();
393 sections.push(SectionType::ShStrTab(shstrtab_section));
394 }
395
396 if !arch.is_v3() || has_debug_sections {
399 let padding = (8 - (current_offset % 8)) % 8;
400 elf_header.e_shoff = current_offset + padding;
401 elf_header.e_shnum = sections.len() as u16;
402 elf_header.e_shstrndx = sections.len() as u16 - 1;
403 }
404
405 Self {
406 elf_header,
407 program_headers,
408 sections,
409 }
410 }
411
412 pub fn emit_bytecode(&self) -> Vec<u8> {
413 let mut bytes = Vec::new();
414
415 bytes.extend(self.elf_header.bytecode());
417
418 if let Some(program_headers) = &self.program_headers {
420 for ph in program_headers {
421 bytes.extend(ph.bytecode());
422 }
423 }
424
425 for section in &self.sections {
427 bytes.extend(section.bytecode());
428 }
429
430 if self.elf_header.e_shoff != 0 {
433 for section in &self.sections {
434 bytes.extend(section.section_header_bytecode());
435 }
436 }
437
438 bytes
439 }
440
441 fn generate_debug_sections(
442 parsed_debug_sections: Vec<DebugSection>,
443 debug_data: &Option<DebugData>,
444 text_offset: u64,
445 section_names: &mut Vec<String>,
446 current_offset: &mut u64,
447 ) -> Vec<SectionType> {
448 if let Some(data) = debug_data {
449 debug::generate_debug_sections(data, text_offset, section_names, current_offset)
450 .into_iter()
451 .enumerate()
452 .map(|(i, s)| match i {
453 0 => SectionType::DebugAbbrev(s),
454 1 => SectionType::DebugInfo(s),
455 2 => SectionType::DebugLine(s),
456 3 => SectionType::DebugLineStr(s),
457 _ => unreachable!(),
458 })
459 .collect()
460 } else {
461 reuse_debug_sections(parsed_debug_sections, section_names, current_offset)
462 }
463 }
464
465 pub fn has_rodata(&self) -> bool {
466 self.sections.iter().any(|s| s.name() == ".rodata")
467 }
468
469 pub fn parse_rodata(&self) -> Vec<(String, usize, String)> {
470 let rodata = self
471 .sections
472 .iter()
473 .find(|s| s.name() == ".rodata")
474 .unwrap();
475 if let SectionType::Data(data_section) = rodata {
476 data_section.rodata()
477 } else {
478 panic!("ROData section not found");
479 }
480 }
481
482 pub fn save_to_file(&self, input_path: &str) -> std::io::Result<()> {
483 let path = Path::new(input_path);
485 let file_stem = path
486 .file_stem()
487 .and_then(|s| s.to_str())
488 .unwrap_or("output");
489
490 let output_path = format!("{}.so", file_stem);
492
493 let bytes = self.emit_bytecode();
495
496 let mut file = File::create(output_path)?;
498 file.write_all(&bytes)?;
499
500 Ok(())
501 }
502}
503
504#[cfg(test)]
505mod tests {
506 use {
507 super::*,
508 crate::{
509 SbpfArch,
510 astnode::ASTNode,
511 parser::{Token, parse},
512 },
513 sbpf_common::inst_param::Number,
514 };
515
516 #[test]
517 fn test_program_from_simple_source() {
518 let source = "exit";
519 for arch in [SbpfArch::V0, SbpfArch::V3] {
520 let parse_result = parse(source, arch).unwrap();
521 let program = Program::from_parse_result(parse_result, None);
522
523 assert!(!program.sections.is_empty());
525 assert!(program.sections.len() >= 2);
526 }
527 }
528
529 #[test]
530 fn test_program_without_rodata() {
531 let source = "exit";
532 for arch in [SbpfArch::V0, SbpfArch::V3] {
533 let parse_result = parse(source, arch).unwrap();
534 let program = Program::from_parse_result(parse_result, None);
535
536 assert!(!program.has_rodata());
537 }
538 }
539
540 #[test]
541 fn test_program_emit_bytecode() {
542 let source = "exit";
543 for arch in [SbpfArch::V0, SbpfArch::V3] {
544 let parse_result = parse(source, arch).unwrap();
545 let program = Program::from_parse_result(parse_result, None);
546
547 let bytecode = program.emit_bytecode();
548 assert!(!bytecode.is_empty());
549 assert_eq!(&bytecode[0..4], b"\x7fELF");
551 }
552 }
553
554 #[test]
555 fn test_v0_program_static_no_program_headers() {
556 let source = "exit";
558 let mut parse_result = parse(source, SbpfArch::V0).unwrap();
559 parse_result.prog_is_static = true;
560
561 let program = Program::from_parse_result(parse_result, None);
562 assert!(program.program_headers.is_none());
563 assert_eq!(program.elf_header.e_phnum, 0);
564 }
565
566 #[test]
567 fn test_program_sections_ordering() {
568 let source = r"exit";
569 for arch in [SbpfArch::V0, SbpfArch::V3] {
570 let parse_result = parse(source, arch).unwrap();
571 let program = Program::from_parse_result(parse_result, None);
572
573 assert_eq!(program.sections[0].name(), "");
575 assert_eq!(program.sections[1].name(), ".text");
577 }
578 }
579
580 #[test]
581 fn test_program_sections_debug() {
582 let source = "exit";
583 for arch in [SbpfArch::V0, SbpfArch::V3] {
584 let parse_result = parse(source, arch).unwrap();
585 let debug_data = Some(DebugData {
586 filename: "test.s".to_string(),
587 directory: "/test".to_string(),
588 lines: vec![],
589 labels: vec![],
590 code_start: 0,
591 code_end: 8,
592 });
593 let program = Program::from_parse_result(parse_result, debug_data);
594
595 let debug_section_names: Vec<&str> = program
596 .sections
597 .iter()
598 .map(|s| s.name())
599 .filter(|name| name.starts_with(".debug_"))
600 .collect();
601
602 assert!(debug_section_names.contains(&".debug_abbrev"));
603 assert!(debug_section_names.contains(&".debug_info"));
604 assert!(debug_section_names.contains(&".debug_line"));
605 assert!(debug_section_names.contains(&".debug_line_str"));
606 }
607 }
608
609 #[test]
610 fn test_v3_e_flags() {
611 let source = "exit";
612 let parse_result = parse(source, SbpfArch::V3).unwrap();
613 let program = Program::from_parse_result(parse_result, None);
614 assert_eq!(program.elf_header.e_flags, 3);
615 }
616
617 #[test]
618 fn test_v3_no_rodata_one_header() {
619 let source = "exit";
620 let parse_result = parse(source, SbpfArch::V3).unwrap();
621 let program = Program::from_parse_result(parse_result, None);
622
623 let headers = program.program_headers.as_ref().unwrap();
624 assert_eq!(headers.len(), 1);
625 assert_eq!(headers[0].p_flags, ProgramHeader::PF_X);
626 assert_eq!(headers[0].p_vaddr, ProgramHeader::V3_BYTECODE_VADDR);
627 }
628
629 #[test]
630 fn test_v3_with_rodata_two_headers() {
631 let source = r#"
632.rodata
633msg: .ascii "test"
634.text
635.globl entrypoint
636entrypoint:
637 exit
638 "#;
639 let parse_result = parse(source, SbpfArch::V3).unwrap();
640 let program = Program::from_parse_result(parse_result, None);
641
642 let headers = program.program_headers.as_ref().unwrap();
643 assert_eq!(headers.len(), 2);
644 assert_eq!(headers[0].p_flags, ProgramHeader::PF_R);
646 assert_eq!(headers[0].p_vaddr, ProgramHeader::V3_RODATA_VADDR);
647 assert_eq!(headers[1].p_flags, ProgramHeader::PF_X);
649 assert_eq!(headers[1].p_vaddr, ProgramHeader::V3_BYTECODE_VADDR);
650 }
651
652 #[test]
653 fn test_v0_with_rodata_relocations() {
654 let source = r#"
655 .rodata
656 data_0000: .byte 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11
657 data_0008: .quad data_0000
658 data_0010: .quad target_fn
659 .text
660 .globl entrypoint
661 entrypoint:
662 exit
663 target_fn:
664 exit
665 "#;
666
667 let v0 = parse(source, SbpfArch::V0).unwrap();
669 let ASTNode::ROData { rodata, offset } = &v0.data_section.get_nodes()[1] else {
670 panic!("expected rodata node");
671 };
672 assert_eq!(rodata.name, "data_0008");
673 assert_eq!(*offset, 8);
674 assert!(matches!(
675 (&rodata.args[0], &rodata.args[1]),
676 (Token::Directive(directive, _), Token::VectorLiteral(values, _))
677 if directive == "quad"
678 && matches!(values.as_slice(), [Number::Int(value)]
679 if *value as u64 == (64 + 3 * 56 + 16) << 32)
680 ));
681 let ASTNode::ROData { rodata, offset } = &v0.data_section.get_nodes()[2] else {
682 panic!("expected rodata node");
683 };
684 assert_eq!(rodata.name, "data_0010");
685 assert_eq!(*offset, 16);
686 assert!(matches!(
687 (&rodata.args[0], &rodata.args[1]),
688 (Token::Directive(directive, _), Token::VectorLiteral(values, _))
689 if directive == "quad"
690 && matches!(values.as_slice(), [Number::Int(value)]
691 if *value as u64 == (64 + 3 * 56 + 8) << 32)
692 ));
693
694 assert_eq!(
696 v0.relocation_data.get_rel_dyns(),
697 vec![
698 (24, RelocationType::RSbf64Relative, String::new()),
699 (32, RelocationType::RSbf64Relative, String::new()),
700 ]
701 );
702
703 let v3 = parse(source, SbpfArch::V3).unwrap();
705 let ASTNode::ROData { rodata, offset } = &v3.data_section.get_nodes()[1] else {
706 panic!("expected rodata node");
707 };
708 assert_eq!(rodata.name, "data_0008");
709 assert_eq!(*offset, 8);
710 assert!(matches!(
711 (&rodata.args[0], &rodata.args[1]),
712 (Token::Directive(directive, _), Token::VectorLiteral(values, _))
713 if directive == "quad"
714 && matches!(values.as_slice(), [Number::Int(0)])
715 ));
716 let ASTNode::ROData { rodata, offset } = &v3.data_section.get_nodes()[2] else {
717 panic!("expected rodata node");
718 };
719 assert_eq!(rodata.name, "data_0010");
720 assert_eq!(*offset, 16);
721 assert!(matches!(
722 (&rodata.args[0], &rodata.args[1]),
723 (Token::Directive(directive, _), Token::VectorLiteral(values, _))
724 if directive == "quad"
725 && matches!(values.as_slice(), [Number::Int(value)]
726 if *value as u64 == ProgramHeader::V3_BYTECODE_VADDR + 8)
727 ));
728
729 assert!(v3.relocation_data.get_rel_dyns().is_empty());
731 }
732
733 #[test]
734 fn test_v3_e_entry() {
735 let source = r#"
736.globl entrypoint
737entrypoint:
738 exit
739 "#;
740 let parse_result = parse(source, SbpfArch::V3).unwrap();
741 let program = Program::from_parse_result(parse_result, None);
742
743 assert!(program.elf_header.e_entry >= ProgramHeader::V3_BYTECODE_VADDR,);
745 }
746
747 #[test]
748 fn test_v3_p_offset() {
749 let source = r#"
750.rodata
751msg: .ascii "test"
752.text
753.globl entrypoint
754entrypoint:
755 exit
756 "#;
757 let parse_result = parse(source, SbpfArch::V3).unwrap();
758 let program = Program::from_parse_result(parse_result, None);
759
760 let headers = program.program_headers.as_ref().unwrap();
761 let expected_first_offset = 64 + (program.elf_header.e_phnum as u64) * 56;
762 assert_eq!(headers[0].p_offset, expected_first_offset);
763 assert_eq!(
764 headers[1].p_offset,
765 headers[0].p_offset + headers[0].p_filesz
766 );
767 }
768
769 #[test]
770 fn test_v3_no_dynamic_sections() {
771 let source = r#"
772.globl entrypoint
773entrypoint:
774 call sol_log_64_
775 exit
776 "#;
777 let parse_result = parse(source, SbpfArch::V3).unwrap();
778 let program = Program::from_parse_result(parse_result, None);
779
780 let section_names: Vec<&str> = program.sections.iter().map(|s| s.name()).collect();
782 assert!(!section_names.contains(&".dynamic"));
783 assert!(!section_names.contains(&".dynsym"));
784 assert!(!section_names.contains(&".dynstr"));
785 assert!(!section_names.contains(&".rel.dyn"));
786 }
787}