1use crate::driver::errors::{LinkError, ProcessingError};
2use crate::tables::{
3 ContextHash, DataTable, Function, MasterSymbolEntry, NameTable, NameTableEntry, ObjectData,
4 SymbolTable, TempInstr, TempOperand,
5};
6use crate::CLIConfig;
7use errors::LinkResult;
8use kerbalobjects::ko::sections::DataIdx;
9use kerbalobjects::ko::symbols::{SymBind, SymType};
10use kerbalobjects::ko::KOFile;
11use kerbalobjects::ksm::sections::{
12 ArgIndex, ArgumentSection, CodeSection, DebugEntry, DebugRange, DebugSection,
13};
14use kerbalobjects::ksm::KSMFile;
15use kerbalobjects::ksm::{Instr, KSMFileBuilder};
16use kerbalobjects::{KOSValue, Opcode};
17use std::collections::hash_map::DefaultHasher;
18use std::collections::HashMap;
19use std::hash::{Hash, Hasher};
20use std::num::NonZeroUsize;
21use std::panic;
22use std::path::PathBuf;
23use std::thread::{self, JoinHandle};
24
25pub mod reader;
26use reader::Reader;
27
28use self::errors::{FileErrorContext, FuncErrorContext};
29
30pub mod errors;
31
32pub struct Driver {
33 config: CLIConfig,
34 thread_handles: Vec<JoinHandle<LinkResult<ObjectData>>>,
35}
36
37impl Driver {
38 pub fn new(config: CLIConfig) -> Self {
39 Driver {
40 config,
41 thread_handles: Vec::with_capacity(16),
42 }
43 }
44
45 pub fn add(&mut self, path: impl Into<PathBuf>) {
46 let path = path.into();
47
48 let handle = thread::spawn(move || {
49 let (file_name, kofile) = Reader::read_file(path)?;
50 Reader::process_file(file_name, kofile)
51 });
52 self.thread_handles.push(handle);
53 }
54
55 pub fn add_file(&mut self, file_name: String, kofile: KOFile) {
56 let handle = thread::spawn(move || Reader::process_file(file_name, kofile));
57 self.thread_handles.push(handle);
58 }
59
60 pub fn link(&mut self) -> LinkResult<KSMFile> {
61 let mut object_data = Vec::with_capacity(self.thread_handles.len());
62
63 for handle in self.thread_handles.drain(..) {
64 let data = match handle.join() {
65 Ok(obj_data) => obj_data?,
66 Err(e) => panic::resume_unwind(e),
67 };
68
69 object_data.push(data);
70 }
71
72 let init_hash = {
73 let mut hasher = DefaultHasher::new();
74
75 hasher.write("_init".as_bytes());
76
77 hasher.finish()
78 };
79
80 let entry_point_hash = {
81 let mut hasher = DefaultHasher::new();
82 hasher.write(self.config.entry_point.as_bytes());
83 hasher.finish()
84 };
85
86 let mut master_data_table = DataTable::new();
87 let mut master_symbol_table = NameTable::<MasterSymbolEntry>::new();
88 let mut master_function_vec = Vec::new();
89 let mut init_function = None;
90 let mut start_function = None;
91 let mut master_function_name_table = NameTable::<NonZeroUsize>::new();
92 let mut file_name_table = NameTable::<()>::new();
93 let mut master_comment: Option<String> = None;
94
95 let mut temporary_function_vec = Vec::new();
96
97 let builder = KSMFileBuilder::new();
98 let mut arg_section = ArgumentSection::new();
99 let mut code_section = CodeSection::new(kerbalobjects::ksm::sections::CodeType::Main);
101
102 let mut data_hash_map = HashMap::<u64, ArgIndex>::new();
104 let mut func_hash_map = HashMap::<u64, usize>::new();
106 let mut func_ref_vec: Vec<u64> = Vec::new();
108 let mut func_offset = 0;
110
111 for (object_data_index, data) in object_data.iter_mut().enumerate() {
113 let mut hasher = DefaultHasher::new();
114 hasher.write(data.input_file_name.as_bytes());
115 let file_name_hash = ContextHash::FileNameHash(hasher.finish());
116 let file_entry = NameTableEntry::from(data.input_file_name.to_owned(), ());
117 let file_name_index = file_name_table.insert(file_entry);
118
119 for mut func_entry in data.function_name_table.drain() {
121 func_entry.set_value(file_name_index);
123 master_function_name_table.insert(func_entry);
124 }
125
126 for func in data.function_table.functions_mut() {
128 func.set_object_data_index(object_data_index);
129 }
130 for func in data.local_function_table.functions_mut() {
131 func.set_object_data_index(object_data_index);
132 }
133
134 Driver::resolve_symbols(
136 &mut master_symbol_table,
137 &mut master_data_table,
138 &master_function_name_table,
139 file_name_hash,
140 data,
141 &mut master_comment,
142 entry_point_hash,
143 )?;
144
145 for value in data.data_table.entries() {
147 master_data_table.add(value.clone());
148 }
149 }
150
151 for symbol_entry in master_symbol_table.entries() {
154 if symbol_entry.value().internal().sym_bind == SymBind::Extern {
155 let name = symbol_entry.name().to_owned();
156 return Err(LinkError::UnresolvedExternalSymbolError(name));
157 }
158 }
159
160 for data in object_data.iter_mut() {
162 for func in data.function_table.drain() {
163 if func.name_hash() == init_hash {
164 init_function = Some(func);
165 } else if func.name_hash() == entry_point_hash {
166 start_function = Some(func);
167 } else {
168 temporary_function_vec.push(func);
169 }
170 }
171 }
172
173 if let Some(init_func) = &init_function {
175 temporary_function_vec.insert(0, init_func.clone());
176 func_ref_vec.push(init_func.name_hash());
177 } else {
178 if self.config.shared {
180 return Err(LinkError::MissingInitFunctionError);
181 }
182 }
183
184 if let Some(start_func) = &start_function {
185 if self.config.shared {
186 return Err(LinkError::EntryInSharedError);
187 }
188
189 if init_function.is_some() {
191 temporary_function_vec.insert(1, start_func.clone());
192 } else {
193 temporary_function_vec.insert(0, start_func.clone());
194 }
195
196 func_ref_vec.push(start_func.name_hash());
197 } else {
198 if !self.config.shared {
200 return Err(LinkError::MissingEntryPointError(
201 self.config.entry_point.to_owned(),
202 ));
203 }
204 }
205
206 if let Some(init_func) = &init_function {
208 Driver::add_func_refs_optimize(
209 init_func.name_hash(),
210 true,
211 &mut func_ref_vec,
212 init_func.object_data_index(),
213 &mut object_data,
214 &master_symbol_table,
215 &temporary_function_vec,
216 );
217 }
218
219 if let Some(start_func) = &start_function {
220 Driver::add_func_refs_optimize(
221 start_func.name_hash(),
222 true,
223 &mut func_ref_vec,
224 start_func.object_data_index(),
225 &mut object_data,
226 &master_symbol_table,
227 &temporary_function_vec,
228 );
229 }
230
231 for data in object_data.iter_mut() {
233 for func in temporary_function_vec.drain(..) {
234 if func_ref_vec.contains(&func.name_hash()) {
236 master_function_vec.push(func);
237 }
238 }
239
240 for func in data.local_function_table.drain() {
241 if data.local_function_ref_vec.contains(&func.name_hash()) {
242 master_function_vec.push(func);
243 }
244 }
245 }
246
247 if let Some(comment) = master_comment {
249 let value = KOSValue::String(comment);
250 arg_section.add(value);
251 }
252
253 let begin_label = KOSValue::String(String::from("@0001"));
257 let begin_index = arg_section.add(begin_label);
258 code_section.add(Instr::OneOp(Opcode::Lbrt, begin_index));
259 func_offset += 1;
260
261 for func in master_function_vec.iter() {
263 func_offset = Driver::calc_func_offset(
264 func,
265 object_data.get_mut(func.object_data_index()).unwrap(),
266 &mut func_hash_map,
267 func_offset,
268 );
269 }
270
271 for mut func in master_function_vec {
273 let object_data_index = func.object_data_index();
274 Driver::add_func_to_code_section(
275 &mut func,
276 &mut arg_section,
277 &mut code_section,
278 &master_symbol_table,
279 &master_data_table,
280 &master_function_name_table,
281 &func_hash_map,
282 &mut data_hash_map,
283 object_data.get(object_data_index).unwrap(),
284 )?;
285 }
286
287 let init_section = CodeSection::new(kerbalobjects::ksm::sections::CodeType::Initialization);
288 let func_section = CodeSection::new(kerbalobjects::ksm::sections::CodeType::Function);
289
290 let builder = builder.with_arg_section(arg_section);
291
292 let builder = builder
293 .with_code_section(func_section)
294 .with_code_section(init_section)
295 .with_code_section(code_section);
296
297 let debug_section = DebugSection::new(DebugEntry::new(1).with_range(DebugRange::new(2, 4)));
298
299 Ok(builder.with_debug_section(debug_section).finish())
300 }
301
302 #[allow(clippy::too_many_arguments)]
303 fn add_func_to_code_section(
304 func: &mut Function,
305 arg_section: &mut ArgumentSection,
306 code_section: &mut CodeSection,
307 master_symbol_table: &NameTable<MasterSymbolEntry>,
308 master_data_table: &DataTable,
309 master_function_name_table: &NameTable<NonZeroUsize>,
310 func_hash_map: &HashMap<u64, usize>,
311 data_hash_map: &mut HashMap<u64, ArgIndex>,
312 object_data: &ObjectData,
313 ) -> LinkResult<()> {
314 for (instr_index, instr) in func.drain().into_iter().enumerate() {
315 let concrete = Driver::concrete_instr(
316 instr,
317 arg_section,
318 master_symbol_table,
319 master_data_table,
320 master_function_name_table,
321 func_hash_map,
322 data_hash_map,
323 object_data,
324 func.name_hash(),
325 instr_index,
326 )?;
327
328 code_section.add(concrete);
329 }
330
331 Ok(())
332 }
333
334 fn func_hash_from_op(
335 op: &TempOperand,
336 master_symbol_table: &NameTable<MasterSymbolEntry>,
337 local_symbol_table: &SymbolTable,
338 ) -> Option<(bool, u64)> {
339 if let TempOperand::SymNameHash(hash) = op {
341 if let Some(sym) = local_symbol_table.get_by_hash(*hash) {
343 if sym.internal().sym_type == SymType::Func {
345 Some((false, *hash))
347 } else {
348 None
349 }
350 } else if let Some(sym) = master_symbol_table.get_by_hash(*hash) {
351 if sym.value().internal().sym_type == SymType::Func {
352 Some((true, *hash))
353 } else {
354 None
355 }
356 } else {
357 None
358 }
359 } else {
360 None
361 }
362 }
363
364 fn add_func_ref_from_op(
365 op: &TempOperand,
366 func_ref_vec: &mut Vec<u64>,
367 parent_object_data_index: usize,
368 object_data: &mut Vec<ObjectData>,
369 master_symbol_table: &NameTable<MasterSymbolEntry>,
370 temporary_function_vec: &Vec<Function>,
371 ) {
372 if let Some((is_global, hash)) = Driver::func_hash_from_op(
373 op,
374 master_symbol_table,
375 &object_data
376 .get(parent_object_data_index)
377 .unwrap()
378 .local_symbol_table,
379 ) {
380 let referenced_func_opt = {
381 if is_global {
382 if !func_ref_vec.contains(&hash) {
383 func_ref_vec.push(hash);
384
385 let referenced_func = temporary_function_vec
386 .iter()
387 .find(|func| func.name_hash() == hash)
388 .unwrap();
389
390 let referenced_func_name_hash = referenced_func.name_hash();
391 let func_object_data_index = referenced_func.object_data_index();
392
393 Some((referenced_func_name_hash, func_object_data_index))
394 } else {
395 None
396 }
397 } else {
398 let parent_object_data = object_data.get_mut(parent_object_data_index).unwrap();
399
400 if !parent_object_data.local_function_ref_vec.contains(&hash) {
401 parent_object_data.local_function_ref_vec.push(hash);
402
403 let referenced_func = object_data
404 .get(parent_object_data_index)
405 .unwrap()
406 .local_function_table
407 .get_by_hash(hash)
408 .unwrap();
409
410 let referenced_func_name_hash = referenced_func.name_hash();
411 let func_object_data_index = referenced_func.object_data_index();
412
413 Some((referenced_func_name_hash, func_object_data_index))
414 } else {
415 None
416 }
417 }
418 };
419
420 if let Some((referenced_name_hash, referenced_object_data_index)) = referenced_func_opt
421 {
422 Driver::add_func_refs_optimize(
424 referenced_name_hash,
425 is_global,
426 func_ref_vec,
427 referenced_object_data_index,
428 object_data,
429 master_symbol_table,
430 temporary_function_vec,
431 );
432 }
433 }
434 }
435
436 fn add_func_refs_optimize(
437 func_name_hash: u64,
438 func_is_global: bool,
439 func_ref_vec: &mut Vec<u64>,
440 object_data_index: usize,
441 object_data: &mut Vec<ObjectData>,
442 master_symbol_table: &NameTable<MasterSymbolEntry>,
443 temporary_function_vec: &Vec<Function>,
444 ) {
445 let mut op_vec = Vec::with_capacity(16);
446 let parent_func = if func_is_global {
447 temporary_function_vec
448 .iter()
449 .find(|func| func.name_hash() == func_name_hash)
450 .unwrap()
451 } else {
452 object_data
453 .get(object_data_index)
454 .unwrap()
455 .local_function_table
456 .get_by_hash(func_name_hash)
457 .unwrap()
458 };
459
460 for instr in parent_func.instructions() {
461 match instr {
462 TempInstr::ZeroOp(_) => {}
463 TempInstr::OneOp(_, op1) => {
464 op_vec.push(*op1);
465 }
466 TempInstr::TwoOp(_, op1, op2) => {
467 op_vec.push(*op1);
468 op_vec.push(*op2);
469 }
470 }
471 }
472
473 for op in op_vec {
474 Driver::add_func_ref_from_op(
475 &op,
476 func_ref_vec,
477 object_data_index,
478 object_data,
479 master_symbol_table,
480 temporary_function_vec,
481 );
482 }
483 }
484
485 fn calc_func_offset(
486 func: &Function,
487 object_data: &mut ObjectData,
488 func_hash_map: &mut HashMap<u64, usize>,
489 current_offset: usize,
490 ) -> usize {
491 let size = func.instruction_count();
492
493 if func.is_global() {
494 func_hash_map.insert(func.name_hash(), current_offset);
495 } else {
496 object_data
497 .local_function_hash_map
498 .insert(func.name_hash(), current_offset);
499 }
500
501 current_offset + size
502 }
503
504 #[allow(clippy::too_many_arguments)]
505 fn concrete_instr(
506 temp: TempInstr,
507 arg_section: &mut ArgumentSection,
508 master_symbol_table: &NameTable<MasterSymbolEntry>,
509 master_data_table: &DataTable,
510 master_function_name_table: &NameTable<NonZeroUsize>,
511 func_hash_map: &HashMap<u64, usize>,
512 data_hash_map: &mut HashMap<u64, ArgIndex>,
513 object_data: &ObjectData,
514 func_name_hash: u64,
515 instr_index: usize,
516 ) -> LinkResult<Instr> {
517 let func_name = match object_data
518 .local_function_name_table
519 .get_by_hash(func_name_hash)
520 {
521 Some(func) => func.name(),
522 None => master_function_name_table
523 .get_by_hash(func_name_hash)
524 .unwrap()
525 .name(),
526 };
527
528 match temp {
529 TempInstr::ZeroOp(opcode) => Ok(Instr::ZeroOp(opcode)),
530 TempInstr::OneOp(opcode, op1) => {
531 let op1_idx = Driver::tempop_to_concrete(
532 op1,
533 arg_section,
534 master_symbol_table,
535 master_data_table,
536 func_hash_map,
537 data_hash_map,
538 object_data,
539 func_name,
540 instr_index,
541 )?;
542
543 Ok(Instr::OneOp(opcode, op1_idx))
544 }
545 TempInstr::TwoOp(opcode, op1, op2) => {
546 let op1_idx = Driver::tempop_to_concrete(
547 op1,
548 arg_section,
549 master_symbol_table,
550 master_data_table,
551 func_hash_map,
552 data_hash_map,
553 object_data,
554 func_name,
555 instr_index,
556 )?;
557 let op2_idx = Driver::tempop_to_concrete(
558 op2,
559 arg_section,
560 master_symbol_table,
561 master_data_table,
562 func_hash_map,
563 data_hash_map,
564 object_data,
565 func_name,
566 instr_index,
567 )?;
568
569 Ok(Instr::TwoOp(opcode, op1_idx, op2_idx))
570 }
571 }
572 }
573
574 #[allow(clippy::too_many_arguments)]
575 fn tempop_to_concrete(
576 op: TempOperand,
577 arg_section: &mut ArgumentSection,
578 master_symbol_table: &NameTable<MasterSymbolEntry>,
579 master_data_table: &DataTable,
580 func_hash_map: &HashMap<u64, usize>,
581 data_hash_map: &mut HashMap<u64, ArgIndex>,
582 object_data: &ObjectData,
583 func_name: &String,
584 instr_index: usize,
585 ) -> LinkResult<ArgIndex> {
586 match op {
587 TempOperand::DataHash(hash) => match data_hash_map.get(&hash) {
588 Some(index) => Ok(*index),
589 None => {
590 let value = master_data_table.get_by_hash(hash).unwrap();
592 let index = arg_section.add(value.clone());
593 data_hash_map.insert(hash, index);
594
595 Ok(index)
596 }
597 },
598 TempOperand::SymNameHash(hash) => {
599 let sym = match object_data.local_symbol_table.get_by_hash(hash) {
600 Some(local_sym) => local_sym.internal(),
601 None => match master_symbol_table.get_by_hash(hash) {
602 Some(entry) => entry.value().internal(),
603 None => {
604 return Err(LinkError::InvalidSymbolRefError(
605 func_name.to_owned(),
606 instr_index,
607 hash,
608 ));
609 }
610 },
611 };
612
613 match sym.sym_type {
614 SymType::Func => {
615 let func_loc = if sym.sym_bind == SymBind::Global {
616 func_hash_map.get(&hash).unwrap()
617 } else {
618 object_data.local_function_hash_map.get(&hash).unwrap()
619 };
620
621 let value = KOSValue::String(format!("@{:0>4}", *func_loc));
623
624 let mut hasher = DefaultHasher::new();
625 value.hash(&mut hasher);
626 let data_hash = hasher.finish();
627
628 match data_hash_map.get(&data_hash) {
629 Some(index) => Ok(*index),
630 None => {
631 let index = arg_section.add(value.clone());
632 data_hash_map.insert(data_hash, index);
633
634 Ok(index)
635 }
636 }
637 }
638 SymType::NoType => {
639 let index =
641 unsafe { NonZeroUsize::new_unchecked(usize::from(sym.value_idx) + 1) };
642
643 let data_hash = master_data_table.hash_at(index).unwrap();
644
645 match data_hash_map.get(data_hash) {
646 Some(index) => Ok(*index),
647 None => {
648 let value = master_data_table.get_at(index).unwrap();
649 let index = arg_section.add(value.clone());
650 data_hash_map.insert(*data_hash, index);
651
652 Ok(index)
653 }
654 }
655 }
656 _ => unreachable!("Symbol type is not of NoType or Func"),
657 }
658 }
659 }
660 }
661
662 fn resolve_symbols(
663 master_symbol_table: &mut NameTable<MasterSymbolEntry>,
664 master_data_table: &mut DataTable,
665 master_function_name_table: &NameTable<NonZeroUsize>,
666 file_name_hash: ContextHash,
667 object_data: &mut ObjectData,
668 comment: &mut Option<String>,
669 entry_point_hash: u64,
670 ) -> LinkResult<()> {
671 for mut symbol in object_data.symbol_table.drain() {
672 let name_entry = object_data
673 .symbol_name_table
674 .get_by_hash(symbol.name_hash())
675 .unwrap();
676
677 if symbol.internal().sym_bind != SymBind::Local {
679 if symbol.internal().sym_type == SymType::Func {
681 symbol.set_context(file_name_hash);
683
684 if entry_point_hash == symbol.name_hash() {
686 *comment = object_data.comment.clone();
687 }
688 }
689
690 match master_symbol_table.get_by_hash(symbol.name_hash()) {
691 Some(other_symbol) => {
692 if other_symbol.value().internal().sym_bind == SymBind::Extern {
694 if symbol.internal().sym_bind != SymBind::Extern {
696 let new_data_idx = if symbol.internal().sym_type == SymType::NoType
697 {
698 let data_index = unsafe {
699 NonZeroUsize::new_unchecked(
700 usize::from(symbol.internal().value_idx) + 1,
701 )
702 };
703 let data = object_data.data_table.get_at(data_index).unwrap();
704
705 let (_, non_zero_idx) = master_data_table.add(data.clone());
706
707 DataIdx::from(non_zero_idx.get() - 1)
708 } else {
709 DataIdx::PLACEHOLDER
711 };
712
713 symbol.internal_mut().value_idx = new_data_idx;
714 let new_symbol = *symbol.internal();
715
716 let new_symbol_entry =
717 MasterSymbolEntry::new(new_symbol, symbol.context());
718
719 master_symbol_table
721 .replace_by_hash(symbol.name_hash(), new_symbol_entry)
722 .map_err(|_| {
723 LinkError::InternalError(String::from(
724 "Symbol name hash invalid.",
725 ))
726 })?;
727 }
728 }
730 else {
732 if symbol.internal().sym_bind != SymBind::Extern {
734 let file_error_context = FileErrorContext {
737 input_file_name: object_data.input_file_name.to_owned(),
738 source_file_name: object_data.source_file_name.to_owned(),
739 };
740
741 let mut func_error_context = FuncErrorContext {
742 file_context: file_error_context.clone(),
743 func_name: String::new(),
744 };
745
746 let mut original_func_name = None;
747
748 if let ContextHash::FuncNameHash(func_name_hash) =
749 other_symbol.value().context()
750 {
751 let original_function_name_entry = master_function_name_table
752 .get_by_hash(func_name_hash)
753 .unwrap();
754 let original_function_name =
755 original_function_name_entry.name();
756
757 original_func_name = Some(original_function_name.to_owned());
758 }
759
760 return Err(match original_func_name {
761 Some(name) => {
762 func_error_context.func_name = name;
763
764 LinkError::FuncContextError(
765 func_error_context,
766 ProcessingError::DuplicateSymbolError(
767 name_entry.name().to_owned(),
768 object_data.source_file_name.to_owned(),
769 ),
770 )
771 }
772 None => LinkError::FileContextError(
773 file_error_context,
774 ProcessingError::DuplicateSymbolError(
775 name_entry.name().to_owned(),
776 object_data.source_file_name.to_owned(),
777 ),
778 ),
779 });
780 }
781 }
783 }
784 None => {
785 let new_symbol = if symbol.internal().sym_type == SymType::NoType {
786 let data_index = unsafe {
787 NonZeroUsize::new_unchecked(
788 usize::from(symbol.internal().value_idx) + 1,
789 )
790 };
791
792 if let Some(data) = object_data.data_table.get_at(data_index) {
793 let (_, non_zero_idx) = master_data_table.add(data.clone());
794
795 let new_data_idx = DataIdx::from(non_zero_idx.get() - 1);
796
797 symbol.internal_mut().value_idx = new_data_idx;
798 }
799
800 *symbol.internal()
801 } else {
802 *symbol.internal()
804 };
805
806 let new_symbol_entry = MasterSymbolEntry::new(new_symbol, symbol.context());
807 let new_name_entry =
808 NameTableEntry::from(name_entry.name().to_owned(), new_symbol_entry);
809
810 master_symbol_table.raw_insert(symbol.name_hash(), new_name_entry);
811 }
812 }
813 }
814 }
815
816 Ok(())
817 }
818}