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::{SbpfArch, parser::parse},
509 };
510
511 #[test]
512 fn test_program_from_simple_source() {
513 let source = "exit";
514 for arch in [SbpfArch::V0, SbpfArch::V3] {
515 let parse_result = parse(source, arch).unwrap();
516 let program = Program::from_parse_result(parse_result, None);
517
518 assert!(!program.sections.is_empty());
520 assert!(program.sections.len() >= 2);
521 }
522 }
523
524 #[test]
525 fn test_program_without_rodata() {
526 let source = "exit";
527 for arch in [SbpfArch::V0, SbpfArch::V3] {
528 let parse_result = parse(source, arch).unwrap();
529 let program = Program::from_parse_result(parse_result, None);
530
531 assert!(!program.has_rodata());
532 }
533 }
534
535 #[test]
536 fn test_program_emit_bytecode() {
537 let source = "exit";
538 for arch in [SbpfArch::V0, SbpfArch::V3] {
539 let parse_result = parse(source, arch).unwrap();
540 let program = Program::from_parse_result(parse_result, None);
541
542 let bytecode = program.emit_bytecode();
543 assert!(!bytecode.is_empty());
544 assert_eq!(&bytecode[0..4], b"\x7fELF");
546 }
547 }
548
549 #[test]
550 fn test_v0_program_static_no_program_headers() {
551 let source = "exit";
553 let mut parse_result = parse(source, SbpfArch::V0).unwrap();
554 parse_result.prog_is_static = true;
555
556 let program = Program::from_parse_result(parse_result, None);
557 assert!(program.program_headers.is_none());
558 assert_eq!(program.elf_header.e_phnum, 0);
559 }
560
561 #[test]
562 fn test_program_sections_ordering() {
563 let source = r"exit";
564 for arch in [SbpfArch::V0, SbpfArch::V3] {
565 let parse_result = parse(source, arch).unwrap();
566 let program = Program::from_parse_result(parse_result, None);
567
568 assert_eq!(program.sections[0].name(), "");
570 assert_eq!(program.sections[1].name(), ".text");
572 }
573 }
574
575 #[test]
576 fn test_program_sections_debug() {
577 let source = "exit";
578 for arch in [SbpfArch::V0, SbpfArch::V3] {
579 let parse_result = parse(source, arch).unwrap();
580 let debug_data = Some(DebugData {
581 filename: "test.s".to_string(),
582 directory: "/test".to_string(),
583 lines: vec![],
584 labels: vec![],
585 code_start: 0,
586 code_end: 8,
587 });
588 let program = Program::from_parse_result(parse_result, debug_data);
589
590 let debug_section_names: Vec<&str> = program
591 .sections
592 .iter()
593 .map(|s| s.name())
594 .filter(|name| name.starts_with(".debug_"))
595 .collect();
596
597 assert!(debug_section_names.contains(&".debug_abbrev"));
598 assert!(debug_section_names.contains(&".debug_info"));
599 assert!(debug_section_names.contains(&".debug_line"));
600 assert!(debug_section_names.contains(&".debug_line_str"));
601 }
602 }
603
604 #[test]
605 fn test_v3_e_flags() {
606 let source = "exit";
607 let parse_result = parse(source, SbpfArch::V3).unwrap();
608 let program = Program::from_parse_result(parse_result, None);
609 assert_eq!(program.elf_header.e_flags, 3);
610 }
611
612 #[test]
613 fn test_v3_no_rodata_one_header() {
614 let source = "exit";
615 let parse_result = parse(source, SbpfArch::V3).unwrap();
616 let program = Program::from_parse_result(parse_result, None);
617
618 let headers = program.program_headers.as_ref().unwrap();
619 assert_eq!(headers.len(), 1);
620 assert_eq!(headers[0].p_flags, ProgramHeader::PF_X);
621 assert_eq!(headers[0].p_vaddr, ProgramHeader::V3_BYTECODE_VADDR);
622 }
623
624 #[test]
625 fn test_v3_with_rodata_two_headers() {
626 let source = r#"
627.rodata
628msg: .ascii "test"
629.text
630.globl entrypoint
631entrypoint:
632 exit
633 "#;
634 let parse_result = parse(source, SbpfArch::V3).unwrap();
635 let program = Program::from_parse_result(parse_result, None);
636
637 let headers = program.program_headers.as_ref().unwrap();
638 assert_eq!(headers.len(), 2);
639 assert_eq!(headers[0].p_flags, ProgramHeader::PF_R);
641 assert_eq!(headers[0].p_vaddr, ProgramHeader::V3_RODATA_VADDR);
642 assert_eq!(headers[1].p_flags, ProgramHeader::PF_X);
644 assert_eq!(headers[1].p_vaddr, ProgramHeader::V3_BYTECODE_VADDR);
645 }
646
647 #[test]
648 fn test_v3_e_entry() {
649 let source = r#"
650.globl entrypoint
651entrypoint:
652 exit
653 "#;
654 let parse_result = parse(source, SbpfArch::V3).unwrap();
655 let program = Program::from_parse_result(parse_result, None);
656
657 assert!(program.elf_header.e_entry >= ProgramHeader::V3_BYTECODE_VADDR,);
659 }
660
661 #[test]
662 fn test_v3_p_offset() {
663 let source = r#"
664.rodata
665msg: .ascii "test"
666.text
667.globl entrypoint
668entrypoint:
669 exit
670 "#;
671 let parse_result = parse(source, SbpfArch::V3).unwrap();
672 let program = Program::from_parse_result(parse_result, None);
673
674 let headers = program.program_headers.as_ref().unwrap();
675 let expected_first_offset = 64 + (program.elf_header.e_phnum as u64) * 56;
676 assert_eq!(headers[0].p_offset, expected_first_offset);
677 assert_eq!(
678 headers[1].p_offset,
679 headers[0].p_offset + headers[0].p_filesz
680 );
681 }
682
683 #[test]
684 fn test_v3_no_dynamic_sections() {
685 let source = r#"
686.globl entrypoint
687entrypoint:
688 call sol_log_64_
689 exit
690 "#;
691 let parse_result = parse(source, SbpfArch::V3).unwrap();
692 let program = Program::from_parse_result(parse_result, None);
693
694 let section_names: Vec<&str> = program.sections.iter().map(|s| s.name()).collect();
696 assert!(!section_names.contains(&".dynamic"));
697 assert!(!section_names.contains(&".dynsym"));
698 assert!(!section_names.contains(&".dynstr"));
699 assert!(!section_names.contains(&".rel.dyn"));
700 }
701}