-
Notifications
You must be signed in to change notification settings - Fork 1
/
brownout.cpp
1966 lines (1722 loc) · 64.9 KB
/
brownout.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
/*
brownout - A humble .elf to ST .prg binary converter
Written by George Nakos and Douglas Little.
Thanks to Troed Sångberg for the Macintosh bits.
Uses the elfio library by Serge Lamikhov-Center to do the
heavy lifting that is ELF parsing. Also used elfdump.cpp
from the examples folder as the basis for this source.
See elfio.hpp for its license.
Command line parsing uses simpleopt by Brodie Thiesfield.
See SimpleOpt.h for its license.
Everything else is released under the WTFPL. Probably.
*/
#define VERSION fawn
#define STRINGIFY2(X) #X
#define STRINGIFY(X) STRINGIFY2(X)
#ifdef _MSC_VER
#define _SCL_SECURE_NO_WARNINGS
#define ELFIO_NO_INTTYPES
// Yes yes dear, fopen is insecure, blah blah. We know.
// Don't bug us about it.
#define _CRT_SECURE_NO_WARNINGS
// Let's make sure struct members are aligned to 2 bytes.
// We wouldn't have put this here if we didn't get bit by this nonsense.
// Note: When building under gcc, turning this on globally does some
// "creative" optimisations so it's turned on for selective structs.
// And it's too early to start drinking...
#pragma pack(2)
// Oooh look, VS 2019 thinks it's a bad idea to use #pragma pack because
// it would lead to corruption. Like, we don't know why we want to pack structs
// because "who packs structs? We have memory and all. Are you some kind of
// weirdo?". And, wow, they had to take the extra mile and make this a
// fatal compilation error too. They really don't want people doing this!
/// Pffffft, anyway, defining this magic switch makes bad error go away.
#define WINDOWS_IGNORE_PACKING_MISMATCH
#endif
// M68k defines lifted from bintools 2.27.
// Added here instead of elftypes.hpp so the
// elfio lib won't need any modifying should
// we ever need to update to a newer version.
// note: elfio_relocation.hpp contained x86 elf reloc handling only - has been modified to use these instead
#define R_68K_NONE 0
#define R_68K_32 1
#define R_68K_16 2
#define R_68K_8 3
#define R_68K_PC32 4
#define R_68K_PC16 5
#define R_68K_PC8 6
#define R_68K_GOT32 7
#define R_68K_GOT16 8
#define R_68K_GOT8 9
#define R_68K_GOT32O 10
#define R_68K_GOT16O 11
#define R_68K_GOT8O 12
#define R_68K_PLT32 13
#define R_68K_PLT16 14
#define R_68K_PLT8 15
#define R_68K_PLT32O 16
#define R_68K_PLT16O 17
#define R_68K_PLT8O 18
#define R_68K_COPY 19
#define R_68K_GLOB_DAT 20
#define R_68K_JMP_SLOT 21
#define R_68K_RELATIVE 22
#define R_68K_GNU_VTINHERIT 23
#define R_68K_GNU_VTENTRY 24
#define R_68K_TLS_GD32 25
#define R_68K_TLS_GD16 26
#define R_68K_TLS_GD8 27
#define R_68K_TLS_LDM32 28
#define R_68K_TLS_LDM16 29
#define R_68K_TLS_LDM8 30
#define R_68K_TLS_LDO32 31
#define R_68K_TLS_LDO16 32
#define R_68K_TLS_LDO8 33
#define R_68K_TLS_IE32 34
#define R_68K_TLS_IE16 35
#define R_68K_TLS_IE8 36
#define R_68K_TLS_LE32 37
#define R_68K_TLS_LE16 38
#define R_68K_TLS_LE8 39
#define R_68K_TLS_DTPMOD32 40
#define R_68K_TLS_DTPREL32 41
#define R_68K_TLS_TPREL32 42
#include <assert.h>
#include <iostream>
#include <elfio/elfio_dump.hpp>
#include <elfio/elfio.hpp>
#include <SimpleOpt.h>
#include <map>
#include <cstdio>
#include <iostream>
#include <memory>
// default c++filt, can also be set on commandline
std::string brownfilter = "m68k-atarimegabrown-elf-c++filt";
/* some reference code for GOT/PLT relocation handling - although we will probably never want to have
these present in the first place since they can change code generation at the referring site to something
unusable at runtime without the section being populated.
// copied from Atom OS
R_386_GOTOFF (== 0x9)
This relocation type computes the difference between a symbol's value and the address of the global offset table. It also instructs the link-editor to create the global offset table.
R_386_GOTPC (== 0xA)
This relocation type resembles R_386_PC32, except it uses the address of the global offset table in its calculation
uint GOT = Heap.kmalloc(1024 * 128); // 128 KB
...
private static void Relocate(Elf_Header* aHeader, Elf_Shdr* aShdr, uint GOT)
{
uint BaseAddress = (uint)aHeader;
Elf32_Rel* Reloc = (Elf32_Rel*)aShdr->sh_addr;
Elf_Shdr* TargetSection = (Elf_Shdr*)(BaseAddress + aHeader->e_shoff) + aShdr->sh_info;
uint RelocCount = aShdr->sh_size / aShdr->sh_entsize;
uint SymIdx, SymVal, RelocType;
for (int i = 0; i < RelocCount; i++, Reloc++)
{
SymVal = 0;
SymIdx = (Reloc->r_info >> 8);
RelocType = Reloc->r_info & 0xFF;
if (SymIdx != SHN_UNDEF)
{
if (RelocType == R_386_GOTPC)
SymVal = GOT;
else
SymVal = GetSymValue(aHeader, TargetSection->sh_link, SymIdx);
}
uint* add_ref = (uint*)(TargetSection->sh_addr + Reloc->r_offset);
switch(RelocType)
{
case R_386_32:
*add_ref = SymVal + *add_ref; // S + A
break;
case R_386_GOTOFF:
*add_ref = SymVal + *add_ref - GOT; // S + A - GOT
break;
case R_386_PLT32: // L + A - P
case R_386_PC32: // S + A - P
case R_386_GOTPC: // GOT + A - P
*add_ref = SymVal + *add_ref - (uint)add_ref;
break;
default:
throw new Exception("[ELF]: Unsupported Relocation type");
}
}
}
*/
// better at catching things early inside VS debugger
#ifdef _MSC_VER
#define assert(_x_) { if (!(_x_)) { __debugbreak(); }; }
//#define assert(_x_) { if (!(_x_)) { __asm int 3 }; }
#endif
void demangle(std::string &name, std::string &demangled);
// Little endian to big endian conversion depending on platform
#if defined(__linux__) || defined(__CYGWIN__)
#include <endian.h>
#define BYTESWAP32 htobe32
#define BYTESWAP16 htobe16
#endif
#if defined(__MINGW32__)
static __inline unsigned short
__bswap_16 (unsigned short __x)
{
return (__x >> 8) | (__x << 8);
}
static __inline unsigned int
__bswap_32 (unsigned int __x)
{
return (__bswap_16 (__x & 0xffff) << 16) | (__bswap_16 (__x >> 16));
}
#define BYTESWAP32 __bswap_32
#define BYTESWAP16 __bswap_16
#endif
#if defined(__APPLE__)
#include "mac_endian.h"
#define BYTESWAP32 htobe32
#define BYTESWAP16 htobe16
#endif
#ifdef _MSC_VER
#include <stdlib.h>
#define BYTESWAP16 _byteswap_ushort
#define BYTESWAP32 _byteswap_ulong
#endif
#if defined(_MSC_VER)
# include <windows.h>
# include <tchar.h>
#else
# define TCHAR char
# define _T(x) x
# define _tprintf printf
# define _tmain main
#endif
enum
{
OPT_INFILE,
OPT_OUTFILE,
OPT_PRGFLAGS,
OPT_SYMTABLE,
OPT_EXTEND,
OPT_DEMANGLE,
OPT_CXXFILT,
//
// OPT_ELF_HEADER,
// OPT_ELF_SECTION_HEADERS,
// OPT_ELF_SEGMENT_HEADERS,
// OPT_ELF_SYMBOL_TABLES,
// OPT_ELF_NOTES,
// OPT_ELF_DYNAMIC_TAGS,
// OPT_ELF_SECTION_DATAS,
// OPT_ELF_SEGMENT_DATAS,
//
//
OPT_VERBOSE,
OPT_DEBUG,
OPT_HELP,
//
OPT_END__
};
CSimpleOpt::SOption g_rgOptions[] =
{
{ OPT_INFILE, _T("-i"), SO_REQ_SEP },
{ OPT_OUTFILE, _T("-o"), SO_REQ_SEP },
{ OPT_PRGFLAGS, _T("-p"), SO_REQ_SEP },
{ OPT_SYMTABLE, _T("-s"), SO_NONE },
{ OPT_EXTEND, _T("-x"), SO_NONE },
{ OPT_DEMANGLE, _T("-f"), SO_NONE },
{ OPT_CXXFILT, _T("-cxxf"), SO_REQ_SEP },
//
{ OPT_VERBOSE, _T("-v"), SO_NONE },
{ OPT_DEBUG, _T("-d"), SO_NONE },
{ OPT_HELP, _T("-h"), SO_NONE },
SO_END_OF_OPTIONS // END
};
void printhelp()
{
printf("brownout version \"" STRINGIFY(VERSION) "\"\n"
"Usage: brownout -i <input_elf_file_name> -o <output_tos_file_name> [-p PRGFLAGS] [-s] [-d] [-x]\n"
"-i input ELF file.\n"
"-o output TOS file.\n"
"-p to specify TOS PRGFLAGS.\n"
"-s will create a symbol table.\n"
"-x will create an extended symbol table.\n"
"-f will turn on C++ symbol demangling via GCC's c++filt tool (i.e. you don't get ugly symbol names).\n"
"-cxxf to specify c++filt demangling tool for any gcc version (if different from default: [%s]).\n"
"-v will turn on verbose mode (less spammy than debugging)\n"
"-d will turn on verbose debugging.\n"
"-h shows help\n",
//
brownfilter.c_str() // c++filt default name
);
}
#if defined(__linux__) || defined(__CYGWIN__) || defined(__MINGW32__) || defined(__APPLE__)
#pragma pack(push,2)
#endif
typedef struct
{
uint16_t PRG_magic; // This WORD contains the magic value (0x601A).
uint32_t PRG_tsize; // This LONG contains the size of the TEXT segment in bytes.
uint32_t PRG_dsize; // This LONG contains the size of the DATA segment in bytes.
uint32_t PRG_bsize; // This LONG contains the size of the BSS segment in bytes.
uint32_t PRG_ssize; // This LONG contains the size of the symbol table in bytes.
uint32_t PRG_res1; // This LONG is unused and is currently reserved.
uint32_t PRGFLAGS; // This LONG contains flags which define certain process characteristics (as defined below).
uint16_t ABSFLAG; // This WORD flag should be non-zero to indicate that the program has no fixups or 0 to indicate it does.Since some versions of TOS handle files with this value being non-zero incorrectly, it is better to represent a program having no fixups with 0 here and placing a 0 longword as the fixup offset.
} PRG_HEADER;
#if defined(__linux__) || defined(__CYGWIN__) || defined(__MINGW32__) || defined(__APPLE__)
#pragma pack(pop)
#endif
using namespace ELFIO;
static const int TOS_FILE_HEADERSIZE = 28;
typedef struct
{
uint32_t offset_fixup; // Offset inside the section
uint32_t elfcalcvalue;
short elfsection; // Which section we're on
short tossection;
std::string elfsymname;
bool absolute;
short type;
} TOS_RELOC;
#define MAX_TOS_RELOCS (256*1024)
TOS_RELOC tos_relocs[MAX_TOS_RELOCS]; // Enough? Who knows!
#if defined(__linux__) || defined(__CYGWIN__) || defined(__MINGW32__) || defined(__APPLE__)
#pragma pack(push,2)
#endif
typedef struct
{
char name[8];
uint16_t type;
uint32_t value;
} GST_SYMBOL;
#if defined(__linux__) || defined(__CYGWIN__) || defined(__MINGW32__) || defined(__APPLE__)
#pragma pack(pop)
#endif
GST_SYMBOL symtab[100 * 1024]; // Enough? Who knows!
typedef struct
{
int type; // Type of section (see enum below)
std::string name;
uint32_t offset; // Offset of section inside the TOS PRG
uint32_t size; // Original size of section
uint32_t padded_size; // Size of section with even padding
const char *data; // Points to the start of the actual section data
uint32_t sect_start; // address of section start inside the elf
uint32_t sect_end; // address of section end inside the elf
} ST_SECTION;
enum
{
SECT_TEXT,
SECT_DATA,
SECT_BSS
};
enum
{
SYM_NONE,
SYM_DRI,
SYM_EXTEND
};
uint32_t relo_data[1];
bool DEMANGLE = false;
Elf_Half sec_num;
typedef std::map<uint32_t, std::pair<uint32_t, int> > elfsectionboundsmap_t;
elfio reader;
static elfsectionboundsmap_t::iterator get_section_for_va(elfsectionboundsmap_t &smap, uint32_t _va)
{
// find ELF section this reference belongs to
// 1) search for first section with (VA < section_end)
elfsectionboundsmap_t::iterator reference_bound = smap.upper_bound(_va);
// 2) no result could mean we're pointing at the very end of the last section, which is still valid (or beyond - which would be a fail)
if (reference_bound == smap.end())
{
// check end of last section
auto last = std::prev(reference_bound);
if (last->first == _va)
{
// yup - pointing at the end of last section
return last;
}
// pointing somewhere unmapped, beyond last section.
// It might have been a section we filtered out during the gathering of sections,
// so let's re-read all sections and try to find which section this relocation is mapped.
// At least then the user will have more info to look into the problem
printf("error: reference 0x%08x points at an area not mapped by existing sections\n", _va);
for (int i = 0; i < sec_num; i++)
{
uint32_t sect_start, sect_end;
ELFIO::section *psec = reader.sections[i];
sect_start = psec->get_address();
sect_end = sect_start + psec->get_size();
if (sect_start <= _va && sect_end >= _va)
{
printf("This reference is located at section %s\n", psec->get_name().c_str());
break;
}
}
exit(-1);
}
// 3) if this returns a section which doesn't contain the VA (VA >= section_start)
// then it must be a reference to the end of a section (but not the last section, handled above),
// so we use an end-inclusive search to make sure it refers to a section we actually kept
//if (_va < reference_bound->second.first)
// reference_bound = smap.lower_bound(_va);
// make sure the reference is actually inside the nearest section (i.e. not < section startaddr) pair<[startaddr],index>
assert(_va >= reference_bound->second.first);
return reference_bound;
}
int _tmain(int argc, TCHAR * argv[])
{
PRG_HEADER toshead = { 0x601a, 0, 0, 0, 0, 0, 0, 0 }; // Set up TOS header
// declare our options parser, pass in the arguments from main
// as well as our array of valid options.
CSimpleOpt args(argc, argv, g_rgOptions);
char infile[1024];
char outfile[1024];
bool DEBUG = false;
bool FPIC = false;
int SYMTABLE = SYM_NONE;
bool VERBOSE = false;
bool gotinput = false, gotoutput = false;
// while there are arguments left to process
while (args.Next())
{
if (args.LastError() == SO_SUCCESS)
{
if (args.OptionId() == OPT_HELP)
{
printhelp();
return 0;
}
else if (args.OptionId() == OPT_INFILE)
{
std::string s_arg = (std::string)args.OptionArg();
strcpy(infile, s_arg.c_str());
gotinput = true;
}
else if (args.OptionId() == OPT_OUTFILE)
{
std::string s_arg = (std::string)args.OptionArg();
strcpy(outfile, s_arg.c_str());
gotoutput = true;
}
else if (args.OptionId() == OPT_PRGFLAGS)
{
std::string s_arg = (std::string)args.OptionArg();
toshead.PRGFLAGS = atoi(s_arg.c_str());
}
else if (args.OptionId() == OPT_DEBUG)
{
DEBUG = true;
VERBOSE = true;
}
else if (args.OptionId() == OPT_SYMTABLE)
{
SYMTABLE = SYM_DRI;
}
else if (args.OptionId() == OPT_EXTEND)
{
SYMTABLE = SYM_EXTEND;
}
else if (args.OptionId() == OPT_DEMANGLE)
{
DEMANGLE = true;
}
else if (args.OptionId() == OPT_CXXFILT)
{
brownfilter = (std::string)args.OptionArg();
}
else if (args.OptionId() == OPT_VERBOSE)
{
VERBOSE = true;
}
}
else
{
_tprintf(_T("Invalid argument: %s\n"), args.OptionText());
return 1;
}
}
if ((gotinput & gotoutput) == false)
{
printhelp();
return 1;
}
if (!reader.load(infile))
{
printf("File %s is not found or it is not an ELF file\n", infile);
return 1;
}
if (DEBUG)
{
dump::header(std::cout, reader);
dump::section_headers(std::cout, reader);
dump::segment_headers(std::cout, reader);
dump::symbol_tables(std::cout, reader);
dump::notes(std::cout, reader);
dump::dynamic_tags(std::cout, reader);
dump::section_datas(std::cout, reader);
dump::segment_datas(std::cout, reader);
}
if (reader.get_type() != ET_EXEC)
{
printf("error: file %s is not an ELF executable (possibly an object file)\n", infile);
exit(1);
}
// From here on starts the browness - helmets on!
sec_num = reader.sections.size();
elfsectionboundsmap_t elfsectionboundsmap;
elfsectionboundsmap_t elfsectionstartmap;
int no_relocs = 0;
uint32_t file_offset = TOS_FILE_HEADERSIZE; // Mostly used to calculate the offset of the BSS section inside the .prg
ST_SECTION prg_sect[256]; // Enough? Who knows!
int section_map[256]; // This keeps track of which elf section is mapped in which prg_sect index (i.e. a reverse look-up)
int no_sect = 0;
bool claimed_sections[256];
for (int i = 0; i < 256; i++)
{
section_map[i] = -1;
claimed_sections[i] = false;
}
ELFIO::section *psec;
if (VERBOSE)
{
std::cout << "performing section layout..." << std::endl;
}
// inject link section in front of everything, which links to real entrypoint
// since ELF has its entrypoint in the header
char injected_link_section[] =
{
0x00, 0x00,
0x00, 0x00,
0x00, 0x00,
0x4e, 0x71
};
int linksize = 0;
if (FPIC)
{
// 32k relative branch
injected_link_section[0] = 0x60;
injected_link_section[1] = 0x00;
linksize = 4;
}
else
{
// abs jmp
injected_link_section[0] = 0x4e;
injected_link_section[1] = 0xf9;
linksize = 8;
}
{
prg_sect[no_sect].type = SECT_TEXT;
prg_sect[no_sect].name = "TEXT";
prg_sect[no_sect].offset = file_offset;
prg_sect[no_sect].size = linksize;
prg_sect[no_sect].padded_size = linksize;
prg_sect[no_sect].data = (const char *)injected_link_section;
prg_sect[no_sect].sect_start = 0;
prg_sect[no_sect].sect_end = 0;
file_offset += linksize;
toshead.PRG_tsize += linksize;
if (VERBOSE)
{
printf("record: section[.tos_entrypoint] tsi:%d fbeg:0x%x fend:0x%x\n",
no_sect, prg_sect[no_sect].offset, file_offset);
}
no_sect++;
}
for (int i = 0; i < sec_num; i++)
{
if (claimed_sections[i]) continue;
psec = reader.sections[i];
if ((psec->get_type() == SHT_PROGBITS) &&
(psec->get_name() == ".boot") &&
(psec->get_size() > 0))
{
claimed_sections[i] = true;
prg_sect[no_sect].type = SECT_TEXT;
prg_sect[no_sect].name = "TEXT";
prg_sect[no_sect].offset = file_offset; // Mark start offset of section inside .prg
prg_sect[no_sect].size = (uint32_t)psec->get_size(); // Mark section's size
prg_sect[no_sect].padded_size = (prg_sect[no_sect].size + 1) & 0xfffffffe; // Pad section size so it will be even if needed
prg_sect[no_sect].data = (const char *)psec->get_data(); // Mark section's start of data
prg_sect[no_sect].sect_start = (uint32_t)psec->get_address(); // Mark elf section's start (for symbol outputting)
prg_sect[no_sect].sect_end = (uint32_t)psec->get_address() + (uint32_t)psec->get_size(); // Mark elf section's end (for symbol outputting)
// record section bounds (and index) in a map of <startaddr/index> pairs using endaddr as the sort key,
// for identity queries using reloc addresses. this way we can identify & bounds-check the owning
// section and identify it from a single query address.
if (elfsectionstartmap.find(psec->get_address()) != elfsectionstartmap.end())
{
printf("error: section[%s] elfaddr:0x%08x esi:%d tsi:%d fbeg:0x%x fend:0x%x collides with an existing section\n",
psec->get_name().c_str(), uint32_t(psec->get_address()), i, no_sect, file_offset, file_offset + prg_sect[no_sect].padded_size);
exit(1);
}
elfsectionstartmap[prg_sect[no_sect].sect_start] = std::pair<uint32_t, int>(prg_sect[no_sect].sect_end, i);
elfsectionboundsmap[prg_sect[no_sect].sect_end] = std::pair<uint32_t, int>(prg_sect[no_sect].sect_start, i);
file_offset += prg_sect[no_sect].padded_size; // Update prg BSS offset
toshead.PRG_tsize += prg_sect[no_sect].padded_size; // Update prg text size
section_map[i] = no_sect; // Mark where in prg_sect this section will lie
if (VERBOSE)
{
printf("record: section[%s] elfaddr:0x%08x esi:%d tsi:%d fbeg:0x%x fend:0x%x\n",
psec->get_name().c_str(), uint32_t(psec->get_address()), i, no_sect, prg_sect[no_sect].offset, file_offset);
}
no_sect++;
}
}
// Group text segments and determine their position inside the output file
for (int i = 0; i < sec_num; i++)
{
if (claimed_sections[i]) continue;
psec = reader.sections[i];
if
(
(
(psec->get_flags() & SHF_EXECINSTR) ||
(
(psec->get_type() == SHT_INIT_ARRAY) ||
(psec->get_type() == SHT_PREINIT_ARRAY) ||
(psec->get_type() == SHT_FINI_ARRAY)
)
)
&& (psec->get_size() > 0)
&& (psec->get_name().find(".debug_") == std::string::npos)
//&& (psec->get_name().find(".init") == std::string::npos)
)
{
claimed_sections[i] = true;
prg_sect[no_sect].type = SECT_TEXT;
prg_sect[no_sect].name = "TEXT";
prg_sect[no_sect].offset = file_offset; // Mark start offset of section inside .prg
prg_sect[no_sect].size = (uint32_t)psec->get_size(); // Mark section's size
prg_sect[no_sect].padded_size = (prg_sect[no_sect].size + 1) & 0xfffffffe; // Pad section size so it will be even if needed
prg_sect[no_sect].data = (const char *)psec->get_data(); // Mark section's start of data
prg_sect[no_sect].sect_start = (uint32_t)psec->get_address(); // Mark elf section's start (for symbol outputting)
prg_sect[no_sect].sect_end = (uint32_t)psec->get_address() + (uint32_t)psec->get_size(); // Mark elf section's end (for symbol outputting)
// record section bounds (and index) in a map of <startaddr/index> pairs using endaddr as the sort key,
// for identity queries using reloc addresses. this way we can identify & bounds-check the owning
// section and identify it from a single query address.
if (elfsectionstartmap.find(psec->get_address()) != elfsectionstartmap.end())
{
printf("error: section[%s] elfaddr:0x%08x esi:%d tsi:%d fbeg:0x%x fend:0x%x collides with an existing section\n",
psec->get_name().c_str(), uint32_t(psec->get_address()), i, no_sect, file_offset, file_offset + prg_sect[no_sect].padded_size);
exit(1);
}
elfsectionstartmap[prg_sect[no_sect].sect_start] = std::pair<uint32_t, int>(prg_sect[no_sect].sect_end, i);
elfsectionboundsmap[prg_sect[no_sect].sect_end] = std::pair<uint32_t, int>(prg_sect[no_sect].sect_start, i);
file_offset += prg_sect[no_sect].padded_size; // Update prg BSS offset
toshead.PRG_tsize += prg_sect[no_sect].padded_size; // Update prg text size
section_map[i] = no_sect; // Mark where in prg_sect this section will lie
if (VERBOSE)
{
printf("record: section[%s] elfaddr:0x%08x esi:%d tsi:%d fbeg:0x%x fend:0x%x\n",
psec->get_name().c_str(), uint32_t(psec->get_address()), i, no_sect, prg_sect[no_sect].offset, file_offset);
}
no_sect++;
}
}
// Group data segments and determine their position inside the output file
for (int i = 0; i < sec_num; i++)
{
if (claimed_sections[i]) continue;
psec = reader.sections[i];
if ((psec->get_type() == SHT_PROGBITS) &&
(psec->get_flags() & SHF_ALLOC) &&
(psec->get_size() > 0) &&
(psec->get_name().find(".debug_") == std::string::npos) &&
(psec->get_name().find(".init") == std::string::npos)
)
{
claimed_sections[i] = true;
prg_sect[no_sect].type = SECT_DATA;
prg_sect[no_sect].name = "DATA";
prg_sect[no_sect].offset = file_offset; // Mark start offset of section inside .prg
prg_sect[no_sect].size = (uint32_t)psec->get_size(); // Mark section's size
prg_sect[no_sect].padded_size = (prg_sect[no_sect].size + 1) & 0xfffffffe; // Pad section size so it will be even if needed
prg_sect[no_sect].data = (const char *)psec->get_data(); // Mark section's start of data
prg_sect[no_sect].sect_start = (uint32_t)psec->get_address(); // Mark elf section's start (for symbol outputting)
prg_sect[no_sect].sect_end = (uint32_t)psec->get_address() + (uint32_t)psec->get_size(); // Mark elf section's end (for symbol outputting)
//if (elfsectionboundsmap.find(psec->get_address() + psec->get_size()) != elfsectionboundsmap.end())
//{
// std::cerr << "error: section [" << psec->get_name() << "] esi:" << i << " tsi:" << no_sect << " fbeg:" << (prg_sect[no_sect].offset) << " fend:" << file_offset << " collides with an existing section" << std::endl;
//}
// record section bounds (and index) in a map of <startaddr/index> pairs using endaddr as the sort key,
// for identity queries using reloc addresses. this way we can identify & bounds-check the owning
// section and identify it from a single query address.
if (elfsectionstartmap.find(psec->get_address()) != elfsectionstartmap.end())
{
printf("error: section[%s] elfaddr:0x%08x esi:%d tsi:%d fbeg:0x%x fend:0x%x collides with an existing section\n",
psec->get_name().c_str(), uint32_t(psec->get_address()), i, no_sect, file_offset, file_offset + prg_sect[no_sect].padded_size);
exit(1);
}
elfsectionstartmap[prg_sect[no_sect].sect_start] = std::pair<uint32_t, int>(prg_sect[no_sect].sect_end, i);
elfsectionboundsmap[prg_sect[no_sect].sect_end] = std::pair<uint32_t, int>(prg_sect[no_sect].sect_start, i);
file_offset += prg_sect[no_sect].padded_size; // Update prg BSS offset
toshead.PRG_dsize += prg_sect[no_sect].padded_size; // Update prg data size
section_map[i] = no_sect; // Mark where in prg_sect this section will lie
if (VERBOSE)
{
printf("record: section[%s] elfaddr:0x%08x esi:%d tsi:%d fbeg:0x%x fend:0x%x\n",
psec->get_name().c_str(), uint32_t(psec->get_address()), i, no_sect, prg_sect[no_sect].offset, file_offset);
}
no_sect++;
}
}
// Group BSS segments and determine their position inside the output file
for (int i = 0; i < sec_num; i++)
{
if (claimed_sections[i]) continue;
psec = reader.sections[i];
if ((psec->get_type() == SHT_NOBITS) &&
(psec->get_size() > 0) &&
(psec->get_name().find(".debug_") == std::string::npos) &&
(psec->get_name().find(".init") == std::string::npos)
)
{
claimed_sections[i] = true;
prg_sect[no_sect].type = SECT_BSS;
prg_sect[no_sect].name = "BSS";
prg_sect[no_sect].offset = file_offset; // Mark start offset of section inside .prg
prg_sect[no_sect].size = (uint32_t)psec->get_size(); // Mark section's size
prg_sect[no_sect].padded_size = (prg_sect[no_sect].size + 1) & 0xfffffffe; // Pad section size so it will be even if needed
prg_sect[no_sect].sect_start = (uint32_t)psec->get_address(); // Mark elf section's start (for symbol outputting)
prg_sect[no_sect].sect_end = (uint32_t)psec->get_address() + (uint32_t)psec->get_size(); // Mark elf section's end (for symbol outputting)
// record section bounds (and index) in a map of <startaddr/index> pairs using endaddr as the sort key,
// for identity queries using reloc addresses. this way we can identify & bounds-check the owning
// section and identify it from a single query address.
if (elfsectionstartmap.find(psec->get_address()) != elfsectionstartmap.end())
{
printf("error: section[%s] elfaddr:0x%08x esi:%d tsi:%d fbeg:0x%x fend:0x%x collides with an existing section\n",
psec->get_name().c_str(), uint32_t(psec->get_address()), i, no_sect, file_offset, file_offset + prg_sect[no_sect].padded_size);
exit(1);
}
elfsectionstartmap[prg_sect[no_sect].sect_start] = std::pair<uint32_t, int>(prg_sect[no_sect].sect_end, i);
elfsectionboundsmap[prg_sect[no_sect].sect_end] = std::pair<uint32_t, int>(prg_sect[no_sect].sect_start, i);
//file_offset += (uint32_t)psec->get_size(); // Update prg offset
file_offset += prg_sect[no_sect].padded_size; // Update prg offset
toshead.PRG_bsize += prg_sect[no_sect].padded_size; // Update prg bss size
section_map[i] = no_sect; // Mark where in prg_sect this section will lie
if (VERBOSE)
{
printf("record: section[%s] elfaddr:0x%08x esi:%d tsi:%d fbeg:0x%x fend:0x%x\n",
psec->get_name().c_str(), uint32_t(psec->get_address()), i, no_sect, prg_sect[no_sect].offset, file_offset);
}
no_sect++;
}
}
// Print some basic info
if (VERBOSE)
{
std::cout <<
"TEXT size: " << toshead.PRG_tsize << std::endl <<
"DATA size:" << toshead.PRG_dsize << std::endl <<
"BSS size:" << toshead.PRG_bsize << std::endl;
}
if (VERBOSE)
{
std::cout << "processing relocation entries..." << std::endl;
}
uint32_t elf_entrypoint = 1;// reader.get_entry();
{
if (1)
{
if (VERBOSE)
{
std::cout << "hunting entrypoint symbol..." << std::endl;
}
for (int si = 0; si < sec_num; si++)
{
psec = reader.sections[si];
if (psec->get_type() == SHT_SYMTAB)
{
symbol_section_accessor symbols(reader, psec);
Elf_Xword sym_no = symbols.get_symbols_num();
if (sym_no > 0)
{
int remaining = 1;
for (Elf_Half i = 0; (i < sym_no) && (remaining > 0); ++i)
{
std::string name;
Elf64_Addr value = 0;
Elf_Xword size = 0;
unsigned char bind = 0;
unsigned char type = 0;
Elf_Half section = 0;
unsigned char other = 0;
symbols.get_symbol(i, name, value, size, bind, type, section, other);
// if (name.length() > 0)
// __asm int 3;
// Check binding type
switch (bind)
{
case STB_LOCAL:
case STB_WEAK:
case STB_GLOBAL:
{
// Section 65521 is (probably) the section for absolute labels
//if (section == 65521)
//{
//}
//else
{
if ((name.compare("__start") == 0) ||
(name.compare("_start") == 0))
{
elf_entrypoint = value;
remaining--;
break;
}
}
break;
}
default:
{
// Do nothing?
break;
}
}
}
}
}
}
}
if (elf_entrypoint == 1)
{
std::cerr << "error: entrypoint (_start symbol) could not be found. can't link!" << std::endl;
exit(1);
}
// find ELF section this reference belongs to (section with lower bound <= query address)
elfsectionboundsmap_t::iterator reference_bound = get_section_for_va(elfsectionboundsmap, elf_entrypoint);
// get ELF section index pair<endaddr,[index]>
int reference_elfidx = reference_bound->second.second;
int reference_tosidx = section_map[reference_elfidx];
assert(reference_tosidx >= 0);
// base address of original elf section bounding the reference
uint32_t reference_esa = reader.sections[reference_elfidx]->get_address();
// base address of new tos section bounding the reference
uint32_t reference_tsa = prg_sect[reference_tosidx].offset - TOS_FILE_HEADERSIZE;
uint32_t tos_entrypoint = elf_entrypoint - reference_esa + reference_tsa;
// if (VERBOSE)
{
printf("entrypoint located at eVA:$%06x (tVA:$%06x)\n", elf_entrypoint, tos_entrypoint);
}
if (FPIC)
{
uint32_t branch_offset = tos_entrypoint - 2;
if (branch_offset >= 32768)
{
std::cerr << "error: entrypoint (_start symbol) is >= 32k into program image in -fpic mode. can't link!" << std::endl;
exit(1);
}
// word branch - no relocation required
injected_link_section[2] = (branch_offset >> 8) & 0xFF;
injected_link_section[3] = (branch_offset >> 0) & 0xFF;
}
else
{
// abs jmp - must emit relocation for thi
injected_link_section[2] = (tos_entrypoint >> 24) & 0xFF;
injected_link_section[3] = (tos_entrypoint >> 16) & 0xFF;
injected_link_section[4] = (tos_entrypoint >> 8) & 0xFF;
injected_link_section[5] = (tos_entrypoint >> 0) & 0xFF;
// record the relocation for processing
tos_relocs[no_relocs].elfsection = -1;
tos_relocs[no_relocs].tossection = 0;
tos_relocs[no_relocs].offset_fixup = (uint32_t)2;
tos_relocs[no_relocs].elfcalcvalue = tos_entrypoint;
tos_relocs[no_relocs].elfsymname = "toslink";
// we need to handle more than one type of reloc, so we query again later after filtering and sorting
// todo: can remove some of these stored fields not required for the sorting pass
// and defer them to the final stage. they eat a lot of memory anyway.
tos_relocs[no_relocs].type = R_68K_32;
// only absolute relocations make it into the TOS reloc table.
// the pc-relative ones are just baked into the text/data after section rearrangement.
tos_relocs[no_relocs].absolute = true;
no_relocs++;
if (no_relocs > MAX_TOS_RELOCS)
{
std::cerr << "error: tos_relocs overflow! increase MAX_TOS_RELOCS and rebuild!" << std::endl;
exit(1);
}
}
}
// Perform any relocations that may be needed
//section *psec_reloc;
for (int i = 0; i < sec_num; i++)
{
psec = reader.sections[i];
std::string sectname = psec->get_name(); // Debug
if (
(psec->get_type() == SHT_RELA) &&
(psec->get_name().find(".debug_") == std::string::npos)
)
{
Elf64_Addr offset;
Elf64_Addr symbolValue;
std::string symbolName;
Elf_Word type;
Elf_Sxword addend;
Elf_Sxword calcValue;
relocation_section_accessor relocs(reader, psec);
int sec_size = (int)relocs.get_entries_num(); //Number of entries in the table
for (Elf_Xword j = 0; j < sec_size; j++)
{
// So, before we forget what variable does what,
// let's add an example to illustrate them.
// Assume there's this code here:
// 2c98: 47f9 0005 6c56 lea 56c56 <_s_entity_links+0x4>,a3
// Obviously the address _s_entity_links+0x4 needs to be
// relocated. For this we need:
// a) The offset into the section that contains the longword
// to be patched. This is provided by "offset".
// b) The address of _s_entity_links. This is provided by "symbolValue".
// c) The value to patch into the offset. This is provided by "calcValue.
relocs.get_entry(j, offset, symbolValue, symbolName, type, addend, calcValue);
switch (type)
{
case R_68K_32:
case R_68K_PC32:
case R_68K_PC16:
case R_68K_PC8:
{
if (DEBUG)
{
// get reloc type string for printing
std::string typestr;
switch (type)
{
case R_68K_32: typestr = "32"; break;