RetroLinker
Linker for several 8-bit, 16-bit and 32-bit formats
Loading...
Searching...
No Matches
pefexe.h
1#ifndef PEFEXE_H
2#define PEFEXE_H
3
4#include "../common.h"
5#include "../dumper/dumper.h"
6#include "../linker/buffer.h"
7#include "../linker/reader.h"
8#include "../linker/segment_manager.h"
9#include "../linker/writer.h"
10
11namespace Linker
12{
13 class Position;
14}
15
16namespace Apple
17{
21 class PEFFormat : public virtual Linker::SegmentManager
22 {
23 public:
24 // TODO: untested
30 {
31 public:
32 enum opcode_type
33 {
34 Zero = 0,
35 BlockCopy = 1,
36 RepeatedBlock = 2,
37 InterleaveRepeatBlockWithBlockCopy = 3,
38 InterleaveRepeatBlockWithZero = 4,
39 };
41 offset_t file_offset;
43 opcode_type opcode;
45 uint32_t count;
47 std::vector<uint8_t> common_data;
49 std::vector<std::vector<uint8_t>> custom_data;
50
52 static uint32_t ReadValue(Linker::Reader& rd);
54 static size_t GetValueSize(uint32_t value, size_t size_hint = 0);
56 static void WriteValue(Linker::Writer& wr, uint32_t value, size_t size_hint = 0);
57
59 void ReadFile(Linker::Reader& rd);
61 void WriteFile(Linker::Writer& wr) const;
63 offset_t CodeSize() const;
65 offset_t DataSize() const;
67 void ExpandData(Linker::Buffer& buffer) const;
68 };
69
70 class Section;
71 class ImportedSymbol;
72 class ImportedLibrary;
73
79 {
80 public:
81 // add 32-bit section start to offset
82 enum target_type
83 {
84 Section,
85 Symbol,
86 };
87 target_type type = Section;
88 bool compiled = true;
89 // section: section number, symbol: symbol number
90 uint32_t number = 0;
91 // only for section targets
92 std::weak_ptr<PEFFormat::Section> section;
93 // only for symbol targets
94 std::weak_ptr<ImportedSymbol> symbol;
95
96 static inline Relocation ToSection(uint32_t section_number)
97 {
98 Relocation relocation;
99 relocation.type = Section;
100 relocation.number = section_number;
101 return relocation;
102 }
103
104 static inline Relocation ToSection(std::weak_ptr<PEFFormat::Section> section)
105 {
106 Relocation relocation;
107 relocation.type = Section;
108 relocation.compiled = false;
109 relocation.section = section;
110 return relocation;
111 }
112
113 static inline Relocation ToSymbol(uint32_t symbol_number)
114 {
115 Relocation relocation;
116 relocation.type = Symbol;
117 relocation.number = symbol_number;
118 return relocation;
119 }
120
121 static inline Relocation ToSymbol(std::weak_ptr<ImportedSymbol> symbol)
122 {
123 Relocation relocation;
124 relocation.type = Symbol;
125 relocation.compiled = false;
126 relocation.symbol = symbol;
127 return relocation;
128 }
129 };
130
131 class RelocOpcode;
132
141 {
142 public:
143 const PEFFormat& pef_format;
144
145 size_t reloc_instr_ptr = 0;
146 uint32_t reloc_address = 0;
147 uint32_t import_index = 0;
148 uint32_t section_c = 0;
149 uint32_t section_d = 0;
150 uint32_t current_repeat_count = 0;
151
152 std::vector<RelocOpcode>& reloc_opcodes;
153 std::map<uint32_t, Relocation>& relocations;
154
155 void Initialize();
156
157 RelocationProcessor(const PEFFormat& pef_format, std::vector<RelocOpcode>& reloc_opcodes, std::map<uint32_t, Relocation>& relocations)
158 : pef_format(pef_format), reloc_opcodes(reloc_opcodes), relocations(relocations)
159 {
160 Initialize();
161 }
162
163 void Advance(uint32_t offset)
164 {
165 reloc_address += offset;
166 }
167
168 void AddRelocation(Relocation relocation)
169 {
170 relocations[reloc_address] = relocation;
171 Advance(4);
172 }
173
174 void AddSection(uint32_t section_number)
175 {
176 AddRelocation(Relocation::ToSection(section_number));
177 }
178
179 void AddSectionC()
180 {
181 AddSection(section_c);
182 }
183
184 void AddSectionD()
185 {
186 AddSection(section_d);
187 }
188
189 void AddSymbol()
190 {
191 AddRelocation(Relocation::ToSymbol(import_index));
192 import_index ++;
193 }
194
196 void Regress(uint32_t block_count);
197
208 void Repeat(uint32_t block_count, uint32_t repeat_count)
209 {
210 // unless this is the last iteration of this block
211 if(current_repeat_count != 1)
212 {
213 // jump over this (already processed) instruction and the block
214 reloc_instr_ptr --;
215 Regress(block_count);
216 }
217
218 if(current_repeat_count == 0)
219 {
220 // iteration starts
221 current_repeat_count = repeat_count;
222 }
223 else
224 {
225 current_repeat_count --;
226 }
227 }
228
229 void GenerateRelocations();
230 };
231
234 {
235 public:
236 /* the values are chosen so that they can be bitwise or'd to the opcode word */
237 enum opcode_type
238 {
239 SmInvalid = -1,
240 //LgInvalid = -2,
241 BySectDWithSkip = 0x0000,
242 BySectC = 0x4000,
243 BySectD = 0x4200,
244 TVector12 = 0x4400,
245 TVector8 = 0x4600,
246 VTable8 = 0x4800,
247 ImportRun = 0x4A00,
248 SmByImport = 0x6000,
249 SmSetSectC = 0x6200,
250 SmSetSectD = 0x6400,
251 SmBySection = 0x6600,
252 IncrPosition = 0x8000,
253 SmRepeat = 0x9000,
254 SetPosition = 0xA000,
255 LgByImport = 0xA400,
256 LgRepeat = 0xB000,
257 LgBySection = 0xB400,
258 LgSetSectC = 0xB440,
259 LgSetSectD = 0xB480,
260 };
262 uint32_t offset = 0; // within file
264 opcode_type opcode = SmInvalid;
265
271 uint32_t value = 0;
272
273 /*union
274 {
275 uint32_t run_length; // BySectDWithSkip, BySect*, TVector*, VTable8, ImportRun
276 uint32_t repeat_count; // *Repeat
277 };*/
282 uint32_t repeat = 0;
283
284 RelocOpcode(opcode_type opcode = SmInvalid)
285 : opcode(opcode)
286 {
287 }
288
289 RelocOpcode(opcode_type opcode, uint32_t value, uint32_t repeat)
291 {
292 }
293
295 void ReadFile(Linker::Reader& rd);
300 uint32_t GetWord() const;
302 offset_t CodeSize() const;
305 {
306 wr.WriteWord(CodeSize(), GetWord());
307 }
309 void GenerateRelocations(RelocationProcessor& processor) const;
310
311 void Dump(Dumper::Dumper& dump, const PEFFormat& pef_format, uint32_t opcode_index, int display_options = 0) const;
312 };
313
315 {
316 public:
317 enum section_type
318 {
319 Code = 0,
320 UnpackedData = 1,
321 PatternInitializedData = 2,
322 Constant = 3,
323 Loader = 4,
324 Debug = 5,
325 ExecutableData = 6,
326 Exception = 7,
327 Traceback = 8,
328 };
329 enum share_type
330 {
331 ProcessShare = 1,
332 GlobalShare = 4,
333 ProtectedShare = 5,
334 };
335 static constexpr uint32_t NoNameOffset = uint32_t(-1);
336 uint32_t name_offset = NoNameOffset;
337 std::string name = "";
338 uint32_t default_address = 0;
339 uint32_t total_size = 0;
340 uint32_t unpacked_size = 0;
341 uint32_t packed_size = 0;
342 uint32_t container_offset = 0;
343 section_type section_kind = Code;
344 share_type share_kind = ProcessShare;
345 uint8_t alignment = 0;
346 uint8_t reserved = 0;
347 // convenience field to determine index of symbol in table
348 uint32_t section_number = uint32_t(-1);
349
350 std::shared_ptr<Linker::Contents> image;
351 std::vector<PatternInitialization> patterns;
352 // only appearing in sections with relocations
353 std::map<uint32_t, Relocation> relocations;
354 std::vector<RelocOpcode> reloc_opcodes;
355 bool contains_relocations = false;
356 uint16_t reserved_a = 0;
357 uint32_t reloc_instr_size = 0;
358 uint32_t first_reloc_offset = 0;
359
360 Section() = default;
361 Section(section_type section_kind, share_type share_kind, std::shared_ptr<Linker::Contents> image)
362 : section_kind(section_kind), share_kind(share_kind), image(image)
363 {
364 alignment = ExpectedAlignment();
365 }
366
367 bool IsInstantiated() const
368 {
369 switch(section_kind)
370 {
371 case Code:
372 case UnpackedData:
373 case PatternInitializedData:
374 case Constant:
375 case ExecutableData:
376 return true;
377 default:
378 return false;
379 }
380 }
381
382 offset_t ExpectedAlignment() const
383 {
384 switch(section_kind)
385 {
386 case Code:
387 case UnpackedData:
388 case Constant:
389 case ExecutableData:
390 return 16;
391 case PatternInitializedData:
392 case Loader:
393 return 4;
394 default:
395 return 1;
396 }
397 }
398
399 void ReadHeader(Linker::Reader& rd);
400 void ReadFile(PEFFormat& pef_format, Linker::Reader& rd);
401 size_t GetImageSize(PEFFormat& pef_format);
402 void CalculateValues(PEFFormat& pef_format);
403 void WriteHeader(Linker::Writer& wr) const;
404 void WriteFile(const PEFFormat& pef_format, Linker::Writer& wr) const;
405 };
406
407 // container header information
408
409 // values are stored as the bigendian 32-bit word
410 enum cpu_type
411 {
412 M68K = 0x6D36386B, // 'm68k'
413 PPC = 0x70777063, // 'pwpc'
414 };
415 cpu_type architecture = PPC;
416 uint32_t format_version = 1;
417 uint32_t date_time_stamp = 0;
418 uint32_t old_def_version = 0;
419 uint32_t old_imp_version = 0;
420 uint32_t current_version = 0;
421 uint32_t reserved = 0;
422 uint16_t inst_section_count = 0;
423 std::vector<std::shared_ptr<Section>> sections;
424 std::vector<std::string> section_name_table;
425 uint32_t section_name_table_end = 0;
426
427 static constexpr uint32_t ContainerHeaderSize = 40;
428 static constexpr uint32_t SectionHeaderSize = 28;
429
430 uint32_t GetSectionNameTableOffset() const
431 {
432 return ContainerHeaderSize + SectionHeaderSize * sections.size();
433 }
434
435 // loader section information
436 uint32_t loader_section_offset = 0; // duplicate value of sections[*]->container_offset for sections[*]->section_kind == Section::Loader
437
438 static constexpr uint32_t NoSection = uint32_t(-1);
439 static constexpr uint32_t Absolute = uint32_t(-2);
440 static constexpr uint32_t Reexported = uint32_t(-3);
441
444 {
445 std::weak_ptr<Section> section_pointer;
446 uint32_t section = NoSection;
447 uint32_t offset = 0;
448
449 void SetPosition(PEFFormat& pef_format, const Linker::Position& position);
450
451 bool IsPresent() const { return section != NoSection; }
452 void StoreSectionIndex()
453 {
454 auto actual_section = section_pointer.lock();
455 section = actual_section ? actual_section->section_number : NoSection;
456 }
457 };
458 Reference main_symbol, init_symbol, term_symbol;
459
461 class Name
462 {
463 public:
464 uint32_t name_offset = 0;
465 std::string name = "";
466 std::string LoadNameString(const PEFFormat& pef_format, Linker::Reader& rd);
467 std::string LoadNameString(const PEFFormat& pef_format, Linker::Reader& rd, uint16_t length);
468 void StoreNameString(PEFFormat& pef_format);
469 void StoreNameStringNoNull(PEFFormat& pef_format);
470
471 Name() = default;
472
473 Name(std::string name)
474 : name(name)
475 {
476 }
477 };
478
479 class ImportedLibrary;
480
481 enum symbol_class_type
482 {
483 Code = 0,
484 Data = 1,
485 TVect = 2,
486 TOC = 3,
487 Glue = 4,
488 };
489 static inline constexpr bool IsValidSymbolClass(int value)
490 {
491 switch(value)
492 {
493 case Code:
494 case Data:
495 case TVect:
496 case TOC:
497 case Glue:
498 return true;
499 default:
500 return false;
501 }
502 }
503
504 class ImportedSymbol : public Name
505 {
506 public:
507 symbol_class_type symbol_class = TVect;
508 uint8_t flags = 0;
509 // convenience field to determine index of symbol in table
510 uint32_t symbol_number = uint32_t(-1);
511
512 ImportedSymbol() = default;
513 ImportedSymbol(std::string name) : Name(name) { }
514
515 std::weak_ptr<ImportedLibrary> library; // back link to library that includes it
516 };
517
518 class ImportedLibrary : public Name
519 {
520 public:
521 uint32_t old_imp_version = 0;
522 uint32_t current_version = 0;
523 uint32_t imported_symbol_count = 0;
524 uint32_t first_imported_symbol = 0;
525 std::vector<std::shared_ptr<ImportedSymbol>> imported_symbols;
526 // only used for generation
527 std::map<std::string, std::shared_ptr<ImportedSymbol>> named_imported_symbols;
528 uint8_t options = 0;
529 uint8_t reserved_a = 0;
530 uint16_t reserved_b = 0;
531
532 ImportedLibrary() = default;
533 ImportedLibrary(std::string name) : Name(name) { }
534
535 std::shared_ptr<ImportedSymbol> GetImportByName(std::string name)
536 {
537 auto symbol_iter = named_imported_symbols.find(name);
538 if(symbol_iter == named_imported_symbols.end())
539 {
540 auto symbol = std::make_shared<ImportedSymbol>(name);
541 imported_symbols.push_back(symbol);
542 named_imported_symbols[name] = symbol;
543 return symbol;
544 }
545 else
546 {
547 return symbol_iter->second;
548 }
549 }
550 };
551 std::vector<std::shared_ptr<ImportedLibrary>> imported_libraries;
552 // only used for generation
553 std::map<std::string, std::shared_ptr<ImportedLibrary>> named_imported_libraries;
554 std::vector<std::shared_ptr<ImportedSymbol>> imported_symbols;
555 std::vector<uint32_t> reloc_section_indexes;
556 uint32_t reloc_instr_offset = 0;
557 uint32_t loader_strings_offset = 0;
558 uint32_t export_hash_offset = 0;
559 std::vector<RelocOpcode> relocs_area;
560 uint32_t relocs_area_size = 0;
561 std::vector<std::string> loader_string_table;
562 uint32_t loader_string_table_size = 0;
563
564 struct ExportedSymbol;
565
567 {
568 uint16_t chain_count = 0;
569 uint32_t first_index = 0;
570
571 // used only for generation
572 std::vector<std::shared_ptr<ExportedSymbol>> chain;
573 };
574 std::vector<HashTableEntry> hash_table;
575
576 struct ExportedSymbol : public Name, public Reference
577 {
578 uint16_t symbol_length = 0;
579 uint16_t hash_value = 0;
580 symbol_class_type symbol_class = Data;
581
582 ExportedSymbol() = default;
583
584 ExportedSymbol(std::string name)
585 : Name(name)
586 {
587 }
588
589 ExportedSymbol(std::string name, symbol_class_type symbol_class)
590 : Name(name), symbol_class(symbol_class)
591 {
592 }
593
594 using Name::LoadNameString;
595 std::string LoadNameString(const PEFFormat& pef_format, Linker::Reader& rd);
596 };
597 std::vector<std::shared_ptr<ExportedSymbol>> exported_symbols;
598
599 static uint32_t ComputeHashWord(std::string name);
600 static uint32_t HashTableIndex(uint32_t hash_word, uint32_t export_hash_table_power);
601 static uint8_t ComputeHashTableExponent(uint32_t hash_table_size);
602
603 std::shared_ptr<ImportedLibrary> FetchImportLibrary(std::string name)
604 {
605 auto library_iter = named_imported_libraries.find(name);
606 if(library_iter == named_imported_libraries.end())
607 {
608 auto library = std::make_shared<ImportedLibrary>(name);
609 imported_libraries.push_back(library);
610 named_imported_libraries[name] = library;
611 return library;
612 }
613 else
614 {
615 return library_iter->second;
616 }
617 }
618
619 static constexpr uint32_t LoaderHeaderSize = 56;
620 static constexpr uint32_t LibraryDescriptionSize = 28;
621 uint32_t GetLibraryDescriptionsSize() const
622 {
623 return LibraryDescriptionSize * imported_libraries.size();
624 }
625 uint32_t GetSymbolTablesSize() const
626 {
627 uint32_t size = 0;
628 for(auto library : imported_libraries)
629 {
630 size += 4 * library->imported_symbols.size();
631 }
632 return size;
633 }
634 uint32_t GetRelocationHeadersSize() const
635 {
636 return 12 * reloc_section_indexes.size();
637 }
638 uint32_t GetRelocationAreaSize() const
639 {
640 return relocs_area_size;
641 }
642 uint32_t GetLoaderStringAreaSize() const
643 {
644 return loader_string_table_size;
645 }
646 uint32_t GetExportHashTableSize() const
647 {
648 return 4 * hash_table.size();
649 }
650 uint32_t GetExportKeyTableSize() const
651 {
652 return 4 * exported_symbols.size();
653 }
654 uint32_t GetExportSymbolTableSize() const
655 {
656 return 10 * exported_symbols.size();
657 }
658
659 uint32_t GetMinimumRelocInstrOffset() const
660 {
661 return LoaderHeaderSize
662 + GetLibraryDescriptionsSize()
663 + GetSymbolTablesSize()
664 + GetRelocationHeadersSize();
665 }
666
667 uint32_t GetLoaderSectionSize() const
668 {
669 return
670 std::max({LoaderHeaderSize
671 + GetLibraryDescriptionsSize()
672 + GetSymbolTablesSize()
673 + GetRelocationHeadersSize(),
674 reloc_instr_offset + GetRelocationAreaSize(),
675 loader_strings_offset + GetLoaderStringAreaSize(),
676 export_hash_offset
677 + GetExportHashTableSize()
678 + GetExportKeyTableSize()
679 + GetExportSymbolTableSize()});
680 }
681
682 // temporary structure to map linker segments to PEF sections
683 std::map<std::shared_ptr<Linker::Segment>, std::shared_ptr<Section>> segment_to_section_map;
684
685 bool FormatSupportsLibraries() const override;
686 bool FormatSupportsResources() const override;
687
688 void ReadLoaderSection(Linker::Reader& rd);
689 void WriteLoaderSection(Linker::Writer& wr) const;
690
691 void ReadFile(Linker::Reader& rd) override;
692 void CalculateValues() override;
694 offset_t WriteFile(Linker::Writer& wr) const override;
695 void Dump(Dumper::Dumper& dump) const override;
696
697 void OnNewSegment(std::shared_ptr<Linker::Segment> segment) override;
698 std::unique_ptr<Script::List> GetScript(Linker::Module& module);
699 void Link(Linker::Module& module);
700 void SortImports();
701 void ProcessRelocations(Linker::Module& module);
702 void ProcessModule(Linker::Module& module) override;
703 void GenerateFile(std::string filename, Linker::Module& module) override;
704 };
705}
706
707#endif /* PEFEXE_H */
Definition pefexe.h:519
Definition pefexe.h:505
Represents a string stored in the loader section string table.
Definition pefexe.h:462
Pattern initialization data.
Definition pefexe.h:30
void ExpandData(Linker::Buffer &buffer) const
Expand the pattern into unpacked data and append it to the buffer.
Definition pefexe.cc:281
std::vector< uint8_t > common_data
Sequence of data to be used, possibly repeatedly.
Definition pefexe.h:47
void WriteFile(Linker::Writer &wr) const
Writes the initialization pattern to a stream.
Definition pefexe.cc:109
void ReadFile(Linker::Reader &rd)
Reads an initialization pattern and initializes this structure.
Definition pefexe.cc:58
static size_t GetValueSize(uint32_t value, size_t size_hint=0)
Determines the required number of bytes to store this value, with an optional minimum size.
Definition pefexe.cc:24
uint32_t count
Generic count parameter, used for repeating or zero filling data.
Definition pefexe.h:45
offset_t CodeSize() const
Size of the packed data, as stored in the file.
Definition pefexe.cc:178
static uint32_t ReadValue(Linker::Reader &rd)
Reads a variable length value as used by some patterns.
Definition pefexe.cc:12
opcode_type opcode
The pattern type.
Definition pefexe.h:43
std::vector< std::vector< uint8_t > > custom_data
Equal sized sequences of data to be inserted between repeated common data.
Definition pefexe.h:49
offset_t file_offset
Offset at which this pattern is stored in the file (only relevant for dumping)
Definition pefexe.h:41
offset_t DataSize() const
Size of the unpacked data, as loaded into memory.
Definition pefexe.cc:244
static void WriteValue(Linker::Writer &wr, uint32_t value, size_t size_hint=0)
Writes a variable length value, with an optional minimum size.
Definition pefexe.cc:35
Represents a single 16-bit or 32-bit opcode that encodes relocations.
Definition pefexe.h:234
uint32_t offset
Offset of opcode within file (only used for dumping)
Definition pefexe.h:262
void GenerateRelocations(RelocationProcessor &processor) const
Executes the opcode to possibly produce some relocation information and/or alter the state of the pse...
Definition pefexe.cc:532
opcode_type opcode
Opcode type.
Definition pefexe.h:264
uint32_t value
A value parameter.
Definition pefexe.h:271
offset_t CodeSize() const
Returns the number of bytes required to encode this opcode.
Definition pefexe.cc:499
uint32_t GetWord() const
Returns the bit sequence that this opcode is stored as in the file.
Definition pefexe.cc:459
void ReadFile(Linker::Reader &rd)
Reads and initializes a single relocation opcode record.
Definition pefexe.cc:348
void WriteFile(Linker::Writer &wr)
Outputs a single relocation opcode to the file.
Definition pefexe.h:304
uint32_t repeat
A repetition parameter.
Definition pefexe.h:282
A representation of a state machine that generates the actual relocations using the relocation data i...
Definition pefexe.h:141
void Regress(uint32_t block_count)
Step back this amount of 16-bit halfwords in the instruction stream.
Definition pefexe.cc:329
void Repeat(uint32_t block_count, uint32_t repeat_count)
Repeat a previous sequence of instructions.
Definition pefexe.h:208
Represents a single relocated 32-bit word in memory.
Definition pefexe.h:79
Definition pefexe.h:315
PowerPC Classic Mac OS "PEF" file format.
Definition pefexe.h:22
void OnNewSegment(std::shared_ptr< Linker::Segment > segment) override
Callback function when allocating a new segment When the linker script runs, it creates segments cons...
Definition pefexe.cc:1989
offset_t WriteFile(Linker::Writer &wr) const override
Stores data in memory to file.
Definition pefexe.cc:1345
void Dump(Dumper::Dumper &dump) const override
Display file contents in a nice manner.
Definition pefexe.cc:1580
void ReadFile(Linker::Reader &rd) override
Loads file into memory.
Definition pefexe.cc:1307
void GenerateFile(std::string filename, Linker::Module &module) override
The main function that handles processing, calculating and generating the final image.
Definition pefexe.cc:2227
void ProcessModule(Linker::Module &module) override
Processes the module object and initializes format fields.
Definition pefexe.cc:2141
void CalculateValues() override
Intermediate step between processing module and generating output file to set up headers and manageme...
Definition pefexe.cc:1378
bool FormatSupportsLibraries() const override
Whether the format supports libraries.
Definition pefexe.cc:961
bool FormatSupportsResources() const override
Whether the format supports resources.
Definition pefexe.cc:966
An abstract interface that separates structure and presentation of the data inside a file.
Definition dumper.h:828
A buffer that can be used to read and store data from a file.
Definition buffer.h:22
offset_t WriteFile(Writer &wr) const override=0
Stores data in memory to file.
Encodes an object module file as a collection of sections, symbols and relocations.
Definition module.h:24
Stores an absolute address along with the containing segment or address space.
Definition position.h:17
A helper class, encapsulating functionality needed to import binary data.
Definition reader.h:20
A helper class to collect sections into segments.
Definition segment_manager.h:32
A helper class, encapsulating functionality needed to export binary data.
Definition writer.h:15
void WriteWord(size_t bytes, uint64_t value, EndianType endiantype)
Read a word.
Definition writer.cc:67
Definition pefexe.h:577
Definition pefexe.h:567
Represents a reference to some data, stored as an offset into a section pair.
Definition pefexe.h:444