]> Shamusworld >> Repos - rln/blob - rln.c
94e2416ef45250e412ebc9b918b3a6fd0bec0e7a
[rln] / rln.c
1 //
2 // RLN - Reboot's Linker for the Atari Jaguar Console System
3 // RLN.C - Application Code
4 // Copyright (C) 199x, Allan K. Pratt, 2014 Reboot & Friends
5 //
6
7 #include "rln.h"
8 //#include <assert.h>
9
10 unsigned errflag = 0;                           // Error flag, goes TRUE on error
11 unsigned waitflag = 0;                          // Wait for any keypress flag
12 unsigned versflag = 0;                          // Version banner has been shown flag
13 unsigned aflag = 0;                                     // Absolute linking flag
14 unsigned bflag = 0;                                     // Don't remove mulitply def locals flag
15 unsigned cflag = 0;                                     // COF executable
16 unsigned dflag = 0;                                     // Wait for key after link flag
17 unsigned gflag = 0;                                     // Source level debug include flag
18 unsigned lflag = 0;                                     // Add local symbols to output flag
19 unsigned mflag = 0;                                     // Produce symbol load map flag
20 unsigned oflag = 0;                                     // Output filename specified
21 unsigned rflag = 0;                                     // Segment alignment size flag
22 unsigned sflag = 0;                                     // Output only global symbols
23 unsigned vflag = 0;                                     // Verbose flag
24 unsigned wflag = 0;                                     // Show warnings flag
25 unsigned zflag = 0;                                     // Suppress banner flag
26 unsigned pflag = 0, uflag = 0;          // Unimplemented flags
27 unsigned hd = 0;                                        // Index of next file handle to fill
28 unsigned secalign = 7;                          // Section Alignment (8=phrase)
29 unsigned tbase = 0;                                     // TEXT base address
30 unsigned dbase = 0;                                     // DATA base address
31 unsigned bbase = 0;                                     // BSS base address
32 unsigned textoffset = 0;                        // COF TEXT segment offset
33 unsigned dataoffset = 0;                        // COF DATA segment offset
34 unsigned bssoffset = 0;                         // COF BSS segment offset
35 unsigned displaybanner = 1;                     // Display version banner
36 unsigned symoffset = 0;                         // Symbol table offset in output file
37 unsigned dosymi = 0;                            // Dosym() processing iterator
38 unsigned dbgsymbase = 0;                        // Debug symbol base address
39 //unsigned symtrunc = 0;                        // Symbol truncation -i and -ii
40 int noheaderflag = 0;                           // No header flag for ABS files
41 int hflags;                                                     // Value of the arg to -h option
42 int ttype, dtype, btype;                        // Type flag: 0, -1, -2, -3, -4
43 int tval, dval, bval;                           // Values of these abs bases
44 int hflag[NHANDLES];                            // True for include files
45 int handle[NHANDLES];                           // Open file handles
46 int textsize, datasize, bsssize;        // Cumulative segment sizes
47 char libdir[FARGSIZE * 3];                      // Library directory to search
48 char ofile[FARGSIZE];                           // Output file name (.o)
49 char * name[NHANDLES];                          // Associated file names
50 char * cmdlnexec = NULL;                        // Executable name - pointer to ARGV[0]
51 char * hsym1[SYMLEN];                           // First symbol for include files
52 char * hsym2[SYMLEN];                           // Second symbol for include files
53 struct OFILE * plist = NULL;            // Object image list pointer
54 struct OFILE * plast;                           // Last object image list pointer
55 struct OFILE * olist = NULL;            // Pointer to first object file in list
56 struct OFILE * olast;                           // Pointer to last object file in list
57 char obj_fname[16384][FNLEN];           // Object file names
58 unsigned obj_segsize[16384][3];         // Object file seg sizes; TEXT,DATA,BSS
59 unsigned obj_index = 0;                         // Object file index/count
60 char * arPtr[512];
61 uint32_t arIndex = 0;
62 struct HREC * htable[NBUCKETS];         // Hash table
63 struct HREC * unresolved = NULL;        // Pointer to unresolved hash list
64 char * ost;                                                     // Output symbol table
65 char * ost_ptr;                                         // Output symbol table; current pointer
66 char * ost_end;                                         // Output symbol table; end pointer
67 char * oststr;                                          // Output string table
68 char * oststr_ptr;                                      // Output string table; current pointer
69 char * oststr_end;                                      // Output string table; end pointer
70 int ost_index = 0;                                      // Index of next ost addition
71 uint8_t nullStr[1] = "\x00";            // Empty string
72 struct HREC * arSymbol = NULL;          // Pointer to AR symbol table
73
74
75 // Function prototypes
76 struct HREC * LookupHREC(char *);
77 char * PathTail(char *);
78 void ShowHelp(void);
79 void ShowVersion(void);
80
81
82 //
83 // Get a long word from memory
84 //
85 static inline uint32_t GetLong(uint8_t * src)
86 {
87         return (src[0] << 24) | (src[1] << 16) | (src[2] << 8) | src[3];
88 }
89
90
91 //
92 // Put a long word into memory
93 //
94 static inline void PutLong(uint8_t * dest, uint32_t val)
95 {
96         *dest++ = (uint8_t)(val >> 24);
97         *dest++ = (uint8_t)(val >> 16);
98         *dest++ = (uint8_t)(val >> 8);
99         *dest = (uint8_t)val;
100 }
101
102
103 //
104 // Get a word from memory
105 //
106 static inline uint16_t GetWord(uint8_t * src)
107 {
108         return (src[0] << 8) | src[1];
109 }
110
111
112 //
113 // Put a word into memory
114 //
115 static inline void PutWord(uint8_t * dest, uint16_t val)
116 {
117         *dest++ = (uint8_t)(val >> 8);
118         *dest = (uint8_t)val;
119 }
120
121
122 //
123 // Find passed in file's length in bytes
124 // N.B.: This also resets the file's pointer to the start of the file
125 //
126 long FileSize(int fd)
127 {
128         long size = lseek(fd, 0, SEEK_END);
129         lseek(fd, 0, SEEK_SET);
130
131         return size;
132 }
133
134
135 //
136 // Debugging detritus
137 //
138 void DumpOListAndObjSegSizeList(void)
139 {
140         struct OFILE * o;
141         int i;
142
143         printf("Object list order:\n");
144
145         for(o=olist; o!=NULL; o=o->o_next)
146         {
147                 printf("\t%s\n", o->o_name);
148         }
149
150         printf("\nobj_segsize[][] list order:\n");
151
152         for(i=0; i<(int)obj_index; i++)
153         {
154                 printf("\t%s\n", obj_fname[i]);
155         }
156
157         printf("\n");
158 }
159
160
161 //
162 // For this object file, add symbols to the output symbol table after
163 // relocating them. Returns TRUE if OSTLookup returns an error (-1).
164 //
165 int DoSymbols(struct OFILE * ofile)
166 {
167         int type;
168         long value;
169         int index;
170         int j;
171         struct HREC * hptr;
172         unsigned tsoSave, dsoSave, bsoSave;
173
174 //      DumpOListAndObjSegSizeList();
175
176         // Point to first symbol record in the object file
177         char * symptr = (ofile->o_image + 32
178                 + ofile->o_header.tsize
179                 + ofile->o_header.dsize
180                 + ofile->o_header.absrel.reloc.tsize
181                 + ofile->o_header.absrel.reloc.dsize);
182
183         // Point to end of symbol record in the object file
184         char * symend = symptr + ofile->o_header.ssize;
185
186         // Search through object segment size table to accumulated segment sizes to
187         // ensure the correct offsets are used in the resulting COF file.
188         int ssidx = -1;
189         unsigned tsegoffset = 0, dsegoffset = 0, bsegoffset = 0;
190
191         // Search for object file name
192         for(j=0; j<(int)obj_index; j++)
193         {
194                 if (!strcmp(ofile->o_name, obj_fname[j]))
195                 {
196                         // Object file name found
197                         ssidx = j;
198                         break;
199                 }
200
201                 // Accumulate segment sizes
202 // N.B. We can get rid of this now that there is a spot in the OFILE for these.
203 //      Just have to make sure that the order that the linked list is in
204 //      corresponds to the order shown here...
205 //      I've proved to my satisfaction that the orders are the same...!
206                 tsegoffset += obj_segsize[j][0];
207                 dsegoffset += obj_segsize[j][1];
208                 bsegoffset += obj_segsize[j][2];
209         }
210
211         if (ssidx == -1)
212         {
213                 printf("DoSymbols(): Object file missing from obj_fname: %s\n",
214                         ofile->o_name);
215                 return 1;
216         }
217
218         // Save segment vars, so we can restore them if needed
219         tsoSave = tsegoffset, dsoSave = dsegoffset, bsoSave = bsegoffset;
220
221         // Process each record in the object's symbol table
222         for(; symptr!=symend; symptr+=12)
223         {
224                 index = GetLong(symptr + 0);    // Obtain symbol string index
225                 type  = GetLong(symptr + 4);    // Obtain symbol type
226                 value = GetLong(symptr + 8);    // Obtain symbol value
227
228                 // Global/External symbols have a pre-processing stage
229                 // N.B.: This destroys the t/d/bsegoffset discovered above. So if a
230                 //       local symbol follows a global/exported one, it gets wrong
231                 //       info! [Should be FIXED now]
232                 if (type & 0x01000000)
233                 {
234                         // Obtain the string table index for the relocation symbol, look
235                         // for it in the globals hash table to obtain information on that
236                         // symbol.
237                         hptr = LookupHREC(symend + index);
238
239                         if (hptr == NULL)
240                         {
241                                 // Try to find it in the OST
242                                 int ostIndex = OSTLookup(symend + index);
243
244                                 if (ostIndex == -1)
245                                 {
246                                         printf("DoSymbols(): Symbol not found in hash table: '%s' (%s)\n", symend + index, ofile->o_name);
247                                         return 1;
248                                 }
249
250                                 if (vflag > 1)
251                                         printf("DoSymbols(): Skipping symbol '%s' (%s) found in OST...\n", symend + index, ofile->o_name);
252
253                                 // If the symbol is not in any .a or .o units, it must be one
254                                 // of the injected ones (_TEXT_E, _DATA_E, or _BSS_E), so skip
255                                 // it [or maybe not? In verbose mode, we see nothing...!]
256                                 continue;
257                         }
258
259                         // Search through object segment size table to obtain segment sizes
260                         // for the object that has the required external/global as a local
261                         // symbol. As each object is interrogated the segment sizes are
262                         // accumulated to ensure the correct offsets are used in the
263                         // resulting COF file. This is effectively 'done again' only as we
264                         // are working with a different object file.
265                         ssidx = -1;
266                         tsegoffset = dsegoffset = bsegoffset = 0;
267
268                         // Search for object filename
269                         for(j=0; j<(int)obj_index; j++)
270                         {
271                                 if (!strcmp(hptr->h_ofile->o_name, obj_fname[j]))
272                                 {
273                                         ssidx = j;      // Symbol object filename
274                                         break;
275                                 }
276
277                                 // Accumulate segment sizes
278                                 tsegoffset += obj_segsize[j][0];
279                                 dsegoffset += obj_segsize[j][1];
280                                 bsegoffset += obj_segsize[j][2];
281                         }
282
283                         if (ssidx == -1)
284                         {
285                                 printf("DoSymbols(): Object file missing from obj_fname: '%s:%s' symbol: '%s' (%s)\n",
286                                         hptr->h_ofile->o_name, hptr->h_ofile->o_arname,
287                                         symend + index, ofile->o_name);
288                                 return 1;
289                         }
290
291                         // Update type with global type
292                         type = hptr->h_type;
293
294                         // Remove external flag if absolute
295                         if (type == (T_GLBL | T_ABS))
296                                 type = T_ABS;
297
298                         // If the global/external has a value then update that value in
299                         // accordance with the segment sizes of the object file it
300                         // originates from
301                         if (hptr->h_value)
302                         {
303                                 switch (hptr->h_type & 0x0E000000)
304                                 {
305                                 case T_ABS:
306                                 case T_TEXT:
307                                         value = hptr->h_value;
308                                         break;
309                                 case T_DATA:
310                                         value = hptr->h_value - hptr->h_ofile->o_header.tsize;
311                                         break;
312                                 case T_BSS:
313                                         value = hptr->h_value
314                                                 - (hptr->h_ofile->o_header.tsize
315                                                 + hptr->h_ofile->o_header.dsize);
316                                         break;
317                                 default:
318                                         if (vflag > 1)
319                                                 printf("DoSymbols: No adjustment made for symbol: %s (%s) = %X\n", symend + index, ofile->o_name, hptr->h_value);
320                                 }
321                         }
322                 }
323                 // If *not* a global/external, use the info from passed in object
324                 else
325                         tsegoffset = tsoSave, dsegoffset = dsoSave, bsegoffset = bsoSave;
326
327                 // Process and update the value dependent on whether the symbol is a
328                 // debug symbol or not
329                 // N.B.: These are currently not supported
330                 if (type & 0xF0000000)
331                 {
332                         // DEBUG SYMBOL
333                         // Set the correct debug symbol base address (TEXT segment)
334                         dbgsymbase = 0;
335
336                         for(j=0; (unsigned)j<dosymi; j++)
337                                 dbgsymbase += obj_segsize[j][0];
338
339                         switch (type & 0xFF000000)
340                         {
341                         case 0x64000000:
342                                 value = tval + dbgsymbase;
343                                 break;
344                         case 0x44000000:
345                         case 0x46000000:
346                         case 0x48000000:
347                                 value = tval + dbgsymbase + value;
348                         default:
349                                 break;
350                         }
351
352                         PutLong(symptr + 8, value);
353                 }
354                 else
355                 {
356                         // NON-DEBUG SYMBOL
357                         // Now make modifications to the symbol value, local or global,
358                         // based on the segment sizes of the object file currently being
359                         // processed.
360                         switch (type & T_SEG)
361                         {
362                         case T_ABS:
363                                 break;
364                         case T_TEXT:
365                                 value = tbase + tsegoffset + value;
366                                 PutLong(symptr + 8, value);
367                                 break;
368                         case T_DATA:
369                                 if (type & T_GLBL)
370                                         value = dbase + dsegoffset + value;
371                                 else
372                                         value = dbase + dsegoffset + (value
373                                                 - ofile->o_header.tsize);
374
375                                 PutLong(symptr + 8, value);
376                                 break;
377                         case T_BSS:
378                                 if (type & T_GLBL)
379                                         value = bbase + bsegoffset + value;
380                                 else
381                                         value = bbase + bsegoffset
382                                                 + (value - (ofile->o_header.tsize
383                                                 + ofile->o_header.dsize));
384
385                                 PutLong(symptr + 8, value);
386                                 break;
387                         default:
388                                 break;
389                         }
390                 }
391
392                 // Add to output symbol table if global/extern, or local flag is set
393                 if (isglobal(type) || lflag)
394                 {
395                         if (vflag > 1)
396                                 printf("DoSymbols: Adding symbol: %s (%s) to OST...\n", symend + index, ofile->o_name);
397
398                         index = OSTAdd(symend + index, type, value);
399
400                         if (index == -1)
401                         {
402                                 printf("DoSymbols(): Failed to add symbol '%s' to OST!\n", symend + index);
403                                 return 1;
404                         }
405                 }
406         }
407
408         // Increment dosymi processsing
409         dosymi++;
410
411         return 0;
412 }
413
414
415 //
416 // Free up hash memory
417 //
418 void FreeHashes(void)
419 {
420         int i;
421
422         for(i=0; i<NBUCKETS; i++)
423         {
424                 struct HREC * hptr = htable[i];
425
426                 while (hptr)
427                 {
428                         struct HREC * htemp = hptr->h_next;
429                         free(hptr);
430                         hptr = htemp;
431                 }
432         }
433 }
434
435
436 //
437 // Add all global and external symbols to the output symbol table
438 // [This is confusing--is it adding globals or locals? common == local!
439 //  but then again, we see this in the header:
440 //  #define T_COMMON  (T_GLOBAL | T_EXTERN) but that could be just bullshit.]
441 //
442 // Common symbols have a different number in the "value" field of the symbol
443 // table (!0) than purely external symbols do (0). So you have to look at the
444 // type (T_GLBL) *and* the value to determine if it's a common symbol.
445 //
446 long DoCommon(void)
447 {
448         struct HREC * hptr;
449         int i;
450
451         for(i=0; i<NBUCKETS; i++)
452         {
453                 for(hptr=htable[i]; hptr!=NULL; hptr=hptr->h_next)
454                 {
455 //NO!                   if (iscommon(hptr->h_type))
456                         if (isglobal(hptr->h_type))// || isextern(hptr->h_type))
457                         {
458 // Skip if in *.a file... (does nothing)
459 //if (hptr->h_ofile->isArchiveFile)
460 //      continue;
461
462 //Is this true? Couldn't an absolute be exported???
463                                 if (hptr->h_type == (T_GLBL | T_ABS))
464                                         hptr->h_type = T_ABS;   // Absolutes *can't* be externals
465
466                                 if (OSTAdd(hptr->h_sym, hptr->h_type, hptr->h_value) == -1)
467                                         return -1;
468                         }
469                 }
470         }
471
472         return 0;
473 }
474
475
476 //
477 // Add a symbol's name, type, and value to the OST.
478 // Returns the index of the symbol in OST, or -1 for error.
479 //
480 int OSTAdd(char * name, int type, long value)
481 {
482         int ost_offset_p, ost_offset_e = 0;     // OST table offsets for position calcs
483         int ostresult;                                          // OST index result
484         int slen = strlen(name);
485
486         // If the OST or OST string table has not been initialised then do so
487         if (ost_index == 0)
488         {
489                 ost = malloc(OST_BLOCK);
490                 oststr = malloc(OST_BLOCK);
491
492                 if (ost == NULL)
493                 {
494                         printf("OST memory allocation error.\n");
495                         return -1;
496                 }
497
498                 if (oststr == NULL)
499                 {
500                         printf("OSTSTR memory allocation error.\n");
501                         return -1;
502                 }
503
504                 ost_ptr = ost;                                          // Set OST start pointer
505                 ost_end = ost + OST_BLOCK;                      // Set OST end pointer
506
507                 PutLong(oststr, 0x00000004);            // Just null long for now
508                 oststr_ptr = oststr + 4;                        // Skip size of str table long (incl null long)
509                 PutLong(oststr_ptr, 0x00000000);        // Null terminating long
510                 oststr_end = oststr + OST_BLOCK;
511         }
512         else
513         {
514                 // If next symbol record exceeds current allocation then expand symbol
515                 // table and/or symbol string table.
516                 ost_offset_p = (ost_ptr - ost);
517                 ost_offset_e = (ost_end - ost);
518
519                 // 3 x uint32_t (12 bytes)
520                 if ((ost_ptr + 12) > ost_end)
521                 {
522                         // We want to allocate the current size of the OST + another block.
523                         ost = realloc(ost, ost_offset_e + OST_BLOCK);
524
525                         if (ost == NULL)
526                         {
527                                 printf("OST memory reallocation error.\n");
528                                 return -1;
529                         }
530
531                         ost_ptr = ost + ost_offset_p;
532                         ost_end = (ost + ost_offset_e) + OST_BLOCK;
533                 }
534
535                 ost_offset_p = (oststr_ptr - oststr);
536                 ost_offset_e = (oststr_end - oststr);
537
538                 // string length + terminating NULL + uint32_t (terminal long)
539                 if ((oststr_ptr + (slen + 1 + 4)) > oststr_end)
540                 {
541                         oststr = realloc(oststr, ost_offset_e + OST_BLOCK);
542
543                         if (oststr == NULL)
544                         {
545                                 printf("OSTSTR memory reallocation error.\n");
546                                 return -1;
547                         }
548
549                         oststr_ptr = oststr + ost_offset_p;
550                         oststr_end = (oststr + ost_offset_e) + OST_BLOCK;
551                 }
552         }
553
554         // If this is a debug symbol and the include debug symbol flag (-g) is not
555         // set then do nothing
556         if ((type & 0xF0000000) && !gflag)
557         {
558                 // Do nothing
559                 return 0;
560         }
561
562         // Get symbol index in OST, if any (-1 if not found)
563         ostresult = OSTLookup(name);
564
565         // If the symbol is in the output symbol table and the bflag is set
566         // (don't remove multiply defined locals) and this is not an
567         // external/global symbol *** OR *** the symbol is not in the output
568         // symbol table then add it.
569         if (((ostresult != -1) && bflag && !(type & 0x01000000))
570                 || ((ostresult != -1) && gflag && (type & 0xF0000000))
571                 || (ostresult == -1))
572         {
573                 if ((type & 0xF0000000) == 0x40000000)
574                         PutLong(ost_ptr, 0x00000000);   // Zero string table offset for dbg line
575                 else
576                         PutLong(ost_ptr, (oststr_ptr - oststr));        // String table offset of symbol string
577
578                 PutLong(ost_ptr + 4, type);
579                 PutLong(ost_ptr + 8, value);
580                 ost_ptr += 12;
581
582                 // If the symbol type is anything but a debug line information
583                 // symbol then write the symbol string to the string table
584                 if ((type & 0xF0000000) != 0x40000000)
585                 {
586                         strcpy(oststr_ptr, name);               // Put symbol name in string table
587                         *(oststr_ptr + slen) = '\0';    // Add null terminating character
588                         oststr_ptr += (slen + 1);
589                         PutLong(oststr_ptr, 0x00000000);        // Null terminating long
590                         PutLong(oststr, (oststr_ptr - oststr)); // Update size of string table
591                 }
592
593                 if (vflag > 1)
594                         printf("OSTAdd: (%s), type=$%08X, val=$%08lX\n", name, type, value);
595
596 // is ost_index pointing one past?
597 // does this return the same regardless of if its ++n or n++?
598 // no. it returns the value of ost_index *before* it's incremented.
599                 return ++ost_index;
600         }
601
602         return ostresult;
603 }
604
605
606 //
607 // Return the index of a symbol in the output symbol table
608 // N.B.: This is a 1-based index! (though there's no real reason for it to be)
609 //
610 int OSTLookup(char * sym)
611 {
612         int i;
613         int stro = 4;           // Offset in string table
614
615         for(i=0; i<ost_index; i++)
616         {
617                 if (strcmp(oststr + stro, sym) == 0)
618                         return i + 1;
619
620                 stro += strlen(oststr + stro) + 1;
621         }
622
623         return -1;
624 }
625
626
627 //
628 // Add unresolved externs to the output symbol table
629 // N.B.: Only adds unresolved symbols *if* they're not already in the OST
630 //
631 int DoUnresolved(void)
632 {
633         struct HREC * hptr = unresolved;
634
635         // Add to OST while unresolved list is valid
636         while (hptr != NULL)
637         {
638                 if (OSTAdd(hptr->h_sym, T_GLBL, 0L) == -1)
639                         return 1;
640
641                 if (vflag > 1)
642                         printf("DoUnresolved(): '%s' (%s:$%08X) in OST\n", hptr->h_sym, hptr->h_ofile->o_name, hptr->h_type);
643
644                 struct HREC * htemp = hptr->h_next;
645                 free(hptr);
646                 hptr = htemp;
647         }
648
649         unresolved = NULL;
650         return 0;
651 }
652
653
654 //
655 // Update object file TEXT and DATA segments based on relocation records. Take
656 // in an OFILE header and flag (T_TEXT, T_DATA) to process. Return (0) is
657 // successful or non-zero (1) if failed.
658 //
659 int RelocateSegment(struct OFILE * ofile, int flag)
660 {
661         char * symtab;                  // Start of symbol table
662         char * symbols;                 // Start of symbols
663         char * sptr;                    // Start of segment data
664         char * rptr;                    // Start of segment relocation records
665         unsigned symidx;                // Offset to symbol
666         unsigned addr;                  // Relocation address
667         unsigned rflg;                  // Relocation flags
668         unsigned olddata;               // Old segment data at reloc address
669         unsigned newdata = 0;   // New segment data at reloc address
670         unsigned pad;                   // Temporary to calculate phrase padding
671         int i;                                  // Iterator
672         char sym[SYMLEN];               // String for symbol name/hash search
673         int ssidx;                              // Segment size table index
674         unsigned glblreloc;             // Global relocation flag
675         unsigned absreloc;              // Absolute relocation flag
676         unsigned relreloc;              // Relative relocation flag
677         unsigned swcond;                // Switch statement condition
678         unsigned relocsize;             // Relocation record size
679
680         // If there is no TEXT relocation data for the selected object file segment
681         // then update the COF TEXT segment offset allowing for the phrase padding
682         if ((flag == T_TEXT) && !ofile->o_header.absrel.reloc.tsize)
683         {
684                 // SCPCD : we should not increment the textoffset before the end of processing the object file, else data section will point to wrong textoffset
685                 return 0;
686         }
687
688         // If there is no DATA relocation data for the selected object file segment
689         // then update the COF DATA and BSS segment offsets allowing for the phrase
690         // padding
691         if ((flag == T_DATA) && !ofile->o_header.absrel.reloc.dsize)
692         {
693                 // SCPCD : the T_DATA is the last section of the file, we can now increment the textoffset, dataoffset and bssoffset
694
695                 // TEXT segment size plus padding
696                 pad = ((ofile->o_header.tsize + secalign) & ~secalign);
697                 textoffset += (ofile->o_header.tsize + (pad - ofile->o_header.tsize));
698
699                 if (vflag > 1)
700                         printf("RelocateSegment(%s, TEXT) : No relocation data\n", ofile->o_name);
701
702                 // DATA segment size plus padding
703                 pad = ((ofile->o_header.dsize + secalign) & ~secalign);
704                 dataoffset += (ofile->o_header.dsize + (pad - ofile->o_header.dsize));
705
706                 // BSS segment size plus padding
707                 pad = ((ofile->o_header.bsize + secalign) & ~secalign);
708                 bssoffset += (ofile->o_header.bsize + (pad - ofile->o_header.bsize));
709
710                 if (vflag > 1)
711                         printf("RelocateSegment(%s, DATA) : No relocation data\n", ofile->o_name);
712
713                 return 0;
714         }
715
716         if (vflag > 1)
717                 printf("RelocateSegment(%s, %s) : Processing Relocation Data\n",
718                         ofile->o_name, flag == T_DATA ? "DATA" : "TEXT");
719
720         // Obtain pointer to start of symbol table
721         symtab = (ofile->o_image + 32 + ofile->o_header.tsize
722                 + ofile->o_header.dsize
723                 + ofile->o_header.absrel.reloc.tsize
724                 + ofile->o_header.absrel.reloc.dsize);
725
726         // Obtain pointer to start of symbols
727         symbols = symtab + ofile->o_header.ssize;
728
729         // Obtain pointer to start of TEXT segment
730         sptr = ofile->o_image + 32;
731
732         // Obtain pointer to start of TEXT relocation records
733         rptr = sptr + (ofile->o_header.tsize + ofile->o_header.dsize);
734
735         relocsize = ofile->o_header.absrel.reloc.tsize;
736
737     if (vflag)
738         printf("RELOCSIZE :: %d  Records = %d\n", relocsize, relocsize / 8);
739
740         // Update pointers if DATA relocation records are being processed
741         if (flag == T_DATA)
742         {
743                 sptr += ofile->o_header.tsize;              // Start of DATA segment
744                 rptr += ofile->o_header.absrel.reloc.tsize; // Start of DATA relocation records
745                 relocsize = ofile->o_header.absrel.reloc.dsize;
746         }
747
748         // Process each relocation record for the TEXT segment
749         for(i=0; i<(int)relocsize; i+=8)
750         {
751                 // Obtain both the relocation address and the relocation flags from the
752                 // object file image
753                 addr = GetLong(rptr);
754                 rflg = GetLong(rptr + 4);
755                 glblreloc = (rflg & 0x00000010 ? 1 : 0);// Set global relocation flag
756                 absreloc = (rflg & 0x00000040 ? 1 : 0); // Set absolute relocation flag
757                 relreloc = (rflg & 0x000000A0 ? 1 : 0); // Set relative relocation flag
758
759                 // Additional processing required for global relocations
760                 if (glblreloc)
761                 {
762                         // Obtain the string table index for the relocation symbol, look
763                         // for it in the globals hash table to obtain information on that
764                         // symbol. For the hash calculation to work correctly it must be
765                         // placed in a 'clean' string before looking it up.
766                         symidx = GetLong(symtab + ((rflg >> 8) * 12));
767                         memset(sym, 0, SYMLEN);
768                         strcpy(sym, symbols + symidx);
769                         olddata = newdata = 0;   // Initialise old and new segment data
770                         ssidx = OSTLookup(sym);
771                         newdata = GetLong(ost + ((ssidx - 1) * 12) + 8);
772                 }
773
774                 // Obtain the existing long word segment data and flip words if the
775                 // relocation flags indicate it relates to a RISC MOVEI instruction
776                 olddata = GetLong(sptr + addr);
777
778                 if (rflg & 0x01)
779                         olddata = _SWAPWORD(olddata);
780
781                 // Process record dependant on segment it relates to; TEXT, DATA or
782                 // BSS. Construct a new relocated segment long word based on the
783                 // required segment base address, the segment data offset in the
784                 // resulting COF file and the offsets from the incoming object file.
785                 swcond = (rflg & 0xFFFFFF00);
786
787                 if (!glblreloc)
788                 {
789                         switch (swcond)
790                         {
791                         case 0x00000200:          // Absolute Value
792                                 break;
793                         case 0x00000400:          // TEXT segment relocation record
794                                 // SCPCD : the symbol point to a text segment, we should use the textoffset
795                                         newdata = tbase + textoffset + olddata;
796
797                                 break;
798                         case 0x00000600:          // DATA segment relocation record
799                                 newdata = dbase + dataoffset
800                                         + (olddata - ofile->o_header.tsize);
801
802                                 break;
803                         case 0x00000800:          // BSS segment relocation record
804                                 newdata = bbase + bssoffset
805                                         + (olddata - (ofile->o_header.tsize
806                                         + ofile->o_header.dsize));
807
808                                 break;
809                         }
810                 }
811                 else
812                 {
813                         if (!relreloc)
814                                 newdata += olddata;
815                 }
816
817                 // Set absolute (long) or relative (word) address of symbol
818                 if (absreloc)
819                 {
820                         // Flip the new long word segment data if the relocation record
821                         // indicated a RISC MOVEI instruction and place the resulting data
822                         // back in the COF segment
823                         if (rflg & 0x01)
824                                 newdata = _SWAPWORD(newdata);
825
826                         PutLong(sptr + addr, newdata);
827                 }
828                 else if (relreloc)
829                 {
830                         PutWord(sptr + addr, newdata - tbase - addr - ofile->o_tbase);
831                 }
832
833                 // Shamus: Let's output some info to aid in debugging this crap
834                 if (vflag > 1)
835                 {
836                         char ssiString[128];
837                         ssiString[0] = 0;
838
839                         if (glblreloc)
840                                 sprintf(ssiString, " [ssi:%i]", ssidx);
841
842                         printf("RelocateSegment($%08X): %s, $%08X: $%08X => $%08X%s\n", rflg, (glblreloc ? sym : "(LOCAL)"), addr, olddata, GetLong(sptr + addr), ssiString);
843                 }
844
845                 rptr += 8;     // Point to the next relocation record
846         }
847
848         // Update the COF segment offset allowing for the phrase padding.
849         // SCPCD : we should not increment the textoffset before the end of processing the object file, else data section will point to wrong textoffset
850         if (flag == T_DATA)
851         {
852                 // TEXT segment plus padding
853                 pad = ((ofile->o_header.tsize + secalign) & ~secalign);
854                 textoffset += (ofile->o_header.tsize + (pad - ofile->o_header.tsize));
855
856                 // DATA segment plus padding
857                 pad = ((ofile->o_header.dsize + secalign) & ~secalign);
858                 dataoffset += (ofile->o_header.dsize + (pad - ofile->o_header.dsize));
859
860                 // BSS segment plus padding
861                 pad = ((ofile->o_header.bsize + secalign) & ~secalign);
862                 bssoffset += (ofile->o_header.bsize + (pad - ofile->o_header.bsize));
863         }
864
865         // Return value, should always be zero
866         return 0;
867 }
868
869
870 //
871 // Add a path character to the end of string 's' if it doesn't already end with
872 // one. The last occurrance of '/' or '\' in the string is assumed to be the
873 // path character.
874 //
875 // This is fucking shit. We know what the path delimiter is, its FUCKING
876 // DEFINED IN THE FUCKING HEADER. FOR FUCKS SAKE. AND YES, HOPE TO GOD THERE'S
877 // ENOUGH SPACE IN THE PASSED IN BUFFER TO HOLD THE EXTRA CHARACTER!
878 //
879 void AppendPathDelimiter(char * s)
880 {
881 #if 0
882         // And hope to God that there's enough space in the buffer...
883         char pathchar = 0;
884
885         while (*s)
886         {
887                 if (*s == '/' || *s == '\\')
888                         pathchar = *s;
889
890                 s++;
891         }
892
893         s--;
894
895         if (*s == pathchar)
896                 return;
897
898         *++s = pathchar;
899         *++s = 0;
900 #else
901         int length = strlen(s);
902
903         if (s[length - 1] != PATH_DELIMITER)
904         {
905                 s[length] = PATH_DELIMITER;
906                 s[length + 1] = 0;      // BUFFER OVERFLOW!!!! FFFFFFFFUUUUUUUUUUUU
907         }
908 #endif
909 }
910
911
912 //
913 // Try to open "name", "name.o", "${libdir}name", "${libdir}name.o". Return the
914 // handle of the file successfully opened. p_name is updated to point to a
915 // malloc()'ed string which is the name which actually got opened. p_name will
916 // return unchanged if the file can't be found.
917 //
918 int TryOpenFile(char ** p_name)
919 {
920         char * name = *p_name;
921
922         // Note that libdir will be an empty string if there is none specified
923         char * tmpbuf = malloc(strlen(name) + strlen(libdir) + 3);
924
925         if (tmpbuf == NULL)
926         {
927                 printf("TryOpenFile() : out of memory\n");
928                 return -1;
929         }
930
931         strcpy(tmpbuf, name);
932         int hasdot = (strrchr(tmpbuf, '.') > strrchr(tmpbuf, PATH_DELIMITER));
933         // Try to open file as passed first
934         int fd = open(tmpbuf, _OPEN_FLAGS);
935
936         if (fd >= 0)
937                 goto ok;
938
939         if (!hasdot)
940         {
941                 // Try to open file with '.o' added
942                 strcat(tmpbuf, ".o");
943                 fd = open(tmpbuf, _OPEN_FLAGS);
944
945                 if (fd >= 0)
946                         goto ok;
947         }
948
949         // Try the libdir only if the name isn't already anchored
950         // Shamus: WTH, this makes no sense... Why the ':'? Is this a Macintosh??
951 //      if (*name != '/' && *name != '\\' && !strchr(name, ':'))
952         if ((*name != PATH_DELIMITER) && (strchr(name, ':') == NULL))
953         {
954                 strcpy(tmpbuf, libdir);
955                 // Add a trailing path char if there isn't one already
956                 AppendPathDelimiter(tmpbuf);
957                 strcat(tmpbuf, name);
958
959                 if ((fd = open(tmpbuf, _OPEN_FLAGS)) >= 0)
960                         goto ok;
961
962                 if (!hasdot)
963                 {
964                         strcat(tmpbuf, ".o");
965
966                         if ((fd = open(tmpbuf, _OPEN_FLAGS)) >= 0)
967                                 goto ok;
968                 }
969         }
970
971         // Couldn't open file at all
972         return -1;
973
974 // There are worse things... :-P
975 ok:
976         tmpbuf = realloc(tmpbuf, strlen(tmpbuf) + 1);
977
978         if (tmpbuf == NULL)
979         {
980                 printf("TryOpenFile() : out of memory\n");
981                 return -1;
982         }
983
984         *p_name = tmpbuf;
985         return fd;
986 }
987
988
989 //
990 // What it says on the tin
991 //
992 void WriteARName(struct OFILE * p)
993 {
994         int flag = *(p->o_arname);
995         printf("%s%s%s", (flag ? (char *)(p->o_arname) : ""), (flag ? ":" : ""), p->o_name);
996 }
997
998
999 //
1000 // Collect file names and handles in a buffer so there is less disk activity.
1001 // Call DoFile with flag FALSE for normal object files and archives.
1002 // Call it with flag TRUE and a symbol name for include files (-i).
1003 //
1004 int DoFile(char * fname, int incFlag, char * sym)
1005 {
1006         // Verbose information
1007         if (vflag)
1008         {
1009                 printf("DoFile() : `%s' %s", fname, incFlag ? "INCLUDE" : "NORMAL");
1010
1011                 if (incFlag)
1012                         printf(" symbol %s", sym);
1013
1014                 printf("\n");
1015         }
1016
1017         // Reached maximum file handles
1018         if (hd == NHANDLES)
1019         {
1020                 if (ProcessFiles())
1021                         return 1;
1022         }
1023
1024         // Attempt to open input file
1025         int fd = TryOpenFile(&fname);
1026
1027         if (fd < 0)
1028         {
1029                 printf("Cannot find input module %s\n", fname);
1030                 return 1;
1031         }
1032
1033         // The file is open; save its info in the handle and name arrays
1034         handle[hd] = fd;
1035         name[hd] = fname;               // This is the name from TryOpenFile()
1036         hflag[hd] = incFlag;
1037
1038         // Include files
1039         if (incFlag)
1040         {
1041                 int temp = strlen(sym);         // Get symbol length
1042
1043                 // 100 chars is max length of a symbol
1044                 if (temp > 99)
1045                 {
1046                         sym[99] = '\0';
1047                         temp = 99;
1048                 }
1049
1050                 // Malloc enough space for two symbols, then build the second one.
1051                 // Second one may be one character longer than first
1052                 if ((hsym1[hd] = malloc((long)temp + 1)) == NULL
1053                         || (hsym2[hd] = malloc((long)temp + 2)) == NULL)
1054                 {
1055                         printf("DoFile() : out of memory for include-file symbols\n");
1056                         return 1;
1057                 }
1058
1059                 strcpy(hsym1[hd], sym);
1060                 strcpy(hsym2[hd], sym);
1061
1062                 if (temp == 99)
1063                 {
1064                         if (sym[99] == 'x')
1065                         {
1066                                 printf("Last char of %s is already 'x': choose another name\n", sym);
1067                                 return 1;
1068                         }
1069
1070                         hsym2[hd][99] = 'x';
1071                 }
1072                 else
1073                 {
1074                         hsym2[hd][temp] = 'x';
1075                         hsym2[hd][temp+1] = '\0';
1076                 }
1077         }
1078
1079         // Increment next handle index
1080         hd++;
1081         // No problems
1082         return 0;
1083 }
1084
1085
1086 //
1087 // Pad TEXT or DATA segment to the requested boundary
1088 //
1089 int PadSegment(FILE * fd, long segsize, int value)
1090 {
1091         int i;
1092         char padarray[32];
1093         char * padptr;
1094
1095         // Determine the number of padding bytes that are needed
1096         long padsize = (segsize + secalign) & ~secalign;
1097         padsize = padsize - segsize;
1098
1099         // Fill pad array if padding is required
1100         if (padsize)
1101         {
1102                 padptr = padarray;
1103
1104                 for(i=0; i<16; i++)
1105                 {
1106                         PutWord(padptr, value);
1107                         padptr += 2;
1108                 }
1109
1110                 symoffset += padsize;
1111
1112                 // Write padding bytes
1113                 if (fwrite(padarray, padsize, 1, fd) != 1)
1114                         return 1;
1115         }
1116
1117         return 0;
1118 }
1119
1120
1121 //
1122 // Write the output file
1123 //
1124 int WriteOutputFile(struct OHEADER * header)
1125 {
1126         unsigned osize;                                         // Object segment size
1127         struct OFILE * otemp;                           // Object file pointer
1128         int i, j;                                                       // Iterators
1129         char himage[0x168];                                     // Header image (COF = 0xA8)
1130         unsigned tsoff, dsoff, bsoff;           // Segment offset values
1131         unsigned index, type, value;            // Symbol table index, type and value
1132         short abstype;                                          // ABS symbol type
1133         char symbol[14];                                        // Symbol record for ABS files
1134         int slen;                                                       // Symbol string length
1135
1136         symoffset = 0;                                          // Initialise symbol offset
1137
1138         // Add correct output extension if none
1139         if (strchr(ofile, '.') == NULL)
1140         {
1141                 if (aflag && cflag)
1142                         strcat(ofile, ".cof");          // COF files
1143                 else if (aflag && !cflag)
1144                         strcat(ofile, ".abs");          // ABS files
1145                 else
1146                         strcat(ofile, ".o");            // Object files (partial linking etc)
1147         }
1148
1149         FILE * fd = fopen(ofile, "wb");         // Attempt to open output file
1150
1151         if (!fd)
1152         {
1153                 printf("Can't open output file %s\n", ofile);
1154                 return 1;
1155         }
1156
1157         // Build the output file header
1158         // Absolute (COF) header
1159         if (cflag)
1160         {
1161                 tsoff = dsoff = bsoff = 0xA8;   // Initialises segment offsets
1162
1163                 // Process each object file segment size to obtain a cumulative segment
1164                 // size for both the TEXT and DATA segments
1165                 for(i=0; i<(int)obj_index; i++)
1166                 {
1167                         dsoff += obj_segsize[i][0];     // Adding TEXT segment sizes
1168                         bsoff += obj_segsize[i][0] + obj_segsize[i][1]; // Adding TEXT and DATA segment sizes
1169                 }
1170
1171                 // Currently this only builds a COF absolute file. Conditionals and
1172                 // additional code will need to be added for ABS and partial linking.
1173
1174                 // Build the COF_HDR
1175                 PutWord(himage + 0,   0x0150               ); // Magic Number (0x0150)
1176                 PutWord(himage + 2,   0x0003               ); // Sections Number (3)
1177                 PutLong(himage + 4,   0x00000000           ); // Date (0L)
1178                 PutLong(himage + 8,   dsoff + header->dsize); // Offset to Symbols Section
1179                 PutLong(himage + 12,  ost_index);             // Number of Symbols
1180                 PutWord(himage + 16,  0x001C               ); // Size of RUN_HDR (0x1C)
1181                 PutWord(himage + 18,  0x0003               ); // Executable Flags (3)
1182
1183                 // Build the RUN_HDR
1184                 PutLong(himage + 20,  0x00000107           ); // Magic/vstamp
1185                 PutLong(himage + 24,  header->tsize        ); // TEXT size in bytes
1186                 PutLong(himage + 28,  header->dsize        ); // DATA size in bytes
1187                 PutLong(himage + 32,  header->bsize        ); // BSS size in bytes
1188                 PutLong(himage + 36,  tbase                ); // Start of executable, normally @TEXT
1189                 PutLong(himage + 40,  tbase                ); // @TEXT
1190                 PutLong(himage + 44,  dbase                ); // @DATA
1191
1192                 // Build the TEXT SEC_HDR
1193                 PutLong(himage + 48,  0x2E746578           );
1194                 PutLong(himage + 52,  0x74000000           ); // ".text"
1195                 PutLong(himage + 56,  tbase                ); // TEXT START
1196                 PutLong(himage + 60,  tbase                ); // TEXT START
1197                 PutLong(himage + 64,  header->tsize        ); // TEXT size in bytes
1198                 PutLong(himage + 68,  tsoff                ); // Offset to section data in file
1199                 PutLong(himage + 72,  0x00000000           ); // Offset to section reloc in file (0L)
1200                 PutLong(himage + 76,  0x00000000           ); // Offset to debug lines structures (0L)
1201                 PutLong(himage + 80,  0x00000000           ); // Nreloc/nlnno (0L)
1202                 PutLong(himage + 84,  0x00000020           ); // SEC_FLAGS: STYP_TEXT
1203
1204                 // Build the DATA SEC_HDR
1205                 PutLong(himage + 88,  0x2E646174           );
1206                 PutLong(himage + 92,  0x61000000           ); // ".data"
1207                 PutLong(himage + 96,  dbase                ); // DATA START
1208                 PutLong(himage + 100, dbase                ); // DATA START
1209                 PutLong(himage + 104, header->dsize        ); // DATA size in bytes
1210                 PutLong(himage + 108, dsoff                ); // Offset to section data in file
1211                 PutLong(himage + 112, 0x00000000           ); // Offset to section reloc in file (0L)
1212                 PutLong(himage + 116, 0x00000000           ); // Offset to debugging lines structures (0L)
1213                 PutLong(himage + 120, 0x00000000           ); // Nreloc/nlnno (0L)
1214                 PutLong(himage + 124, 0x00000040           ); // SEC_FLAGS: STYP_DATA
1215
1216                 // Build the BSS SEC_HDR
1217                 PutLong(himage + 128, 0x2E627373           );
1218                 PutLong(himage + 132, 0x00000000           ); // ".bss"
1219                 PutLong(himage + 136, bbase                ); // BSS START
1220                 PutLong(himage + 140, bbase                ); // BSS START
1221                 PutLong(himage + 144, header->bsize        ); // BSS size in bytes
1222                 PutLong(himage + 148, bsoff                ); // Offset to section data in file
1223                 PutLong(himage + 152, 0x00000000           ); // Offset to section reloc in file (0L)
1224                 PutLong(himage + 156, 0x00000000           ); // Offset to debugging lines structures (0L)
1225                 PutLong(himage + 160, 0x00000000           ); // Nreloc/nlnno (0L)
1226                 PutLong(himage + 164, 0x00000080           ); // SEC_FLAGS: STYP_BSS
1227
1228                 symoffset = 168;                              // Update symbol offset
1229         }
1230         // Absolute (ABS) header
1231         else
1232         {
1233                 // Build the ABS header
1234                 PutWord(himage + 0,   0x601B               ); // Magic Number (0x601B)
1235                 PutLong(himage + 2,   header->tsize        ); // TEXT segment size
1236                 PutLong(himage + 6,   header->dsize        ); // DATA segment size
1237                 PutLong(himage + 10,  header->bsize        ); // BSS segment size
1238                 PutLong(himage + 14,  ost_index * 14       ); // Symbol table size (?)
1239                 PutLong(himage + 18,  0x00000000           ); //
1240                 PutLong(himage + 22,  tbase                ); // TEXT base address
1241                 PutWord(himage + 26,  0xFFFF               ); // Flags (?)
1242                 PutLong(himage + 28,  dbase                ); // DATA base address
1243                 PutLong(himage + 32,  bbase                ); // BSS base address
1244
1245                 symoffset = 36;                               // Update symbol offset
1246         }
1247
1248         // Write the header, but not if noheaderflag
1249         // Absolute (ABS) header
1250         if (!cflag)
1251         {
1252                 if (!noheaderflag)
1253                         if (fwrite(himage, 36, 1, fd) != 1)
1254                                 goto werror;
1255         }
1256         // Absolute (COF) header
1257         else
1258         {
1259                 if (fwrite(himage, 168, 1, fd) != 1)
1260                         goto werror;
1261         }
1262
1263         // Write the TEXT segment of each object file
1264         for(otemp=olist; otemp!=NULL; otemp=otemp->o_next)
1265         {
1266                 osize = otemp->o_header.tsize;
1267
1268                 // Write only if segment has size
1269                 if (osize)
1270                 {
1271                         if (vflag > 1)
1272                                 printf("Writing TEXT Segment of %s\n", otemp->o_name);
1273
1274                         if (fwrite(otemp->o_image + 32, osize, 1, fd) != 1)
1275                                 goto werror;
1276
1277                         // Pad to required alignment boundary
1278                         if (PadSegment(fd, osize, 0x0000))
1279                                 goto werror;
1280
1281                         symoffset += osize;
1282                 }
1283         }
1284
1285         // Write the DATA segment of each object file
1286         for(otemp=olist; otemp!=NULL; otemp=otemp->o_next)
1287         {
1288                 osize = otemp->o_header.dsize;
1289
1290                 // Write only if the segment has size
1291                 if (osize)
1292                 {
1293                         if (vflag > 1)
1294                                 printf("Writing DATA Segment of %s\n", otemp->o_name);
1295
1296                         if (fwrite((otemp->o_image + 32 + otemp->o_header.tsize), osize, 1, fd) != 1)
1297                                 goto werror;
1298
1299                         // Pad to required alignment boundary
1300                         if (PadSegment(fd, osize, 0))
1301                                 goto werror;
1302
1303                         symoffset += osize;
1304                 }
1305         }
1306
1307         if (!noheaderflag)
1308         {
1309                 // Write the symbols table and string table
1310                 // Absolute (COF) symbol/string table
1311                 if (cflag)
1312                 {
1313                         if (header->ssize)
1314                         {
1315                                 if (fwrite(ost, (ost_ptr - ost), 1, fd) != 1)
1316                                         goto werror;
1317
1318                                 if (fwrite(oststr, (oststr_ptr - oststr), 1, fd) != 1)
1319                                         goto werror;
1320                         }
1321                 }
1322                 // Absolute (ABS) symbol/string table
1323                 else
1324                 {
1325                         // The symbol and string table have been created as part of the
1326                         // DoSymbols() function and the output symbol and string tables are
1327                         // in COF format. For an ABS file we need to process through this
1328                         // to create the 14 character long combined symbol and string
1329                         // table. Format of symbol table in ABS: AAAAAAAATTVVVV, where
1330                         // (A)=STRING, (T)=TYPE & (V)=VALUE
1331
1332                         for(i=0; i<ost_index; i++)
1333                         {
1334                                 memset(symbol, 0, 14);          // Initialise symbol record
1335                                 abstype = 0;                            // Initialise ABS symbol type
1336                                 slen = 0;                                       // Initialise symbol string length
1337                                 index = GetLong(ost + (i * 12));        // Get symbol index
1338                                 type  = GetLong((ost + (i * 12)) + 4);  // Get symbol type
1339
1340                                 // Skip debug symbols
1341                                 if (type & 0xF0000000)
1342                                         continue;
1343
1344                                 // Get symbol value
1345                                 value = GetLong((ost + (i * 12)) + 8);
1346                                 slen = strlen(oststr + index);
1347
1348                                 // Get symbol string (maximum 8 chars)
1349                                 if (slen > 8)
1350                                 {
1351                                         for(j=0; j<8; j++)
1352                                                 *(symbol + j) = *(oststr + index + j);
1353                                 }
1354                                 else
1355                                 {
1356                                         for(j=0; j<slen; j++)
1357                                                 *(symbol + j) = *(oststr + index + j);
1358                                 }
1359
1360                                 // Modify to ABS symbol type
1361                                 switch (type)
1362                                 {
1363                                 case 0x02000000: abstype = (short)ABST_DEFINED;                           break;
1364                                 case 0x04000000: abstype = (short)ABST_DEFINED | ABST_TEXT;               break;
1365                                 case 0x05000000: abstype = (short)ABST_DEFINED | ABST_GLOBAL | ABST_TEXT; break;
1366                                 case 0x06000000: abstype = (short)ABST_DEFINED | ABST_DATA;               break;
1367                                 case 0x07000000: abstype = (short)ABST_DEFINED | ABST_GLOBAL | ABST_DATA; break;
1368                                 case 0x08000000: abstype = (short)ABST_DEFINED | ABST_BSS;                break;
1369                                 case 0x09000000: abstype = (short)ABST_DEFINED | ABST_GLOBAL | ABST_BSS;  break;
1370                                 default:
1371                                         printf("warning (WriteOutputFile): ABS, cannot determine symbol type ($%08X)\n", type);
1372                                         type = 0;
1373                                         break;
1374                                 }
1375
1376                                 PutWord(symbol + 8, abstype);   // Write back new ABS type
1377                                 PutLong(symbol + 10, value);    // Write back value
1378
1379                                 // Write symbol record
1380                                 if (fwrite(symbol, 14, 1, fd) != 1)
1381                                         goto werror;
1382                         }
1383                 }
1384         }
1385
1386         // Close the file
1387         if (fclose(fd))
1388         {
1389                 printf("Close error on output file %s\n",ofile);
1390                 return 1;
1391         }
1392
1393         return 0;
1394
1395 werror:
1396         printf("Write error on output file %s\n", ofile);
1397         fclose(fd);                     // Try to close output file anyway
1398         return 1;
1399 }
1400
1401
1402 //
1403 // Display the symbol load map
1404 //
1405 int ShowSymbolLoadMap(struct OHEADER * header)
1406 {
1407         unsigned i, o;                  // Inner and outer loop iterators
1408         unsigned c;                             // Column number
1409         unsigned index;                 // Symbol string index
1410         unsigned type;                  // Symbol type
1411         unsigned value;                 // Symbol value
1412         char * symbol;                  // Symbol string value
1413
1414         if (ost_index == 0)
1415                 return 0;                       // Return if no symbols to map
1416
1417         printf("LOAD MAP\n\n");
1418
1419         // Outer loop for each of the symbol areas to map out;
1420         // 0 = NON-RELOCATABLE SYMBOLS
1421         // 1 = TEXT-SEGMENT RELOCATABLE SYMBOLS
1422         // 2 = DATA-SEGMENT RELOCATABLE SYMBOLS
1423         // 3 = BSS-SEGMENT RELOCATABLE SYMBOLS
1424         for(o=0; o<4; o++)
1425         {
1426                 // Display the correct map header for the symbols being processed
1427                 switch (o)
1428                 {
1429                 case 0: printf("NON-RELOCATABLE SYMBOLS\n\n");          break;
1430                 case 1: printf("TEXT-SEGMENT RELOCATABLE SYMBOLS\n\n"); break;
1431                 case 2: printf("DATA-SEGMENT RELOCATABLE SYMBOLS\n\n"); break;
1432                 case 3: printf("BSS-SEGMENT RELOCATABLE SYMBOLS\n\n");  break;
1433                 }
1434
1435                 c = 0;                          // Initialise column number
1436
1437                 // Inner loop to process each record in the symbol table
1438                 for(i=0; i<(unsigned)ost_index; i++)
1439                 {
1440                         index  = GetLong(ost + (i * 12));               // Get symbol string index
1441                         type   = GetLong(ost + (i * 12) + 4);   // Get symbol type
1442                         value  = GetLong(ost + (i * 12) + 8);   // Get symbol value
1443                         symbol = oststr + index;                                // Get symbol string
1444
1445                         // Display only three columns
1446                         if (c == 3)
1447                         {
1448                                 printf("\n");
1449                                 c = 0;
1450                         }
1451
1452                         // If local symbols not included and the type is local then go to
1453                         // next symbol record
1454                         if (!lflag & !(type & 0x01000000))
1455                                 continue;
1456
1457                         // Output each symbol to the display, dependant on type
1458                         switch (o)
1459                         {
1460                         case 0:
1461                                 // Non-relocatable symbols
1462                                 if (type == 0x02000000 || type == 0x03000000)
1463                                 {
1464                                         printf("%-8s %c  %08X   ", symbol, (type & 0x01000000) ? 'G' : 'L', value);
1465                                         c++;
1466                                 }
1467
1468                                 break;
1469                         case 1:
1470                                 // TEXT segment relocatable symbols
1471                                 if (type == 0x04000000 || type == 0x05000000)
1472                                 {
1473                                         printf("%-8s %c  %08X   ", symbol, (type & 0x01000000) ? 'G' : 'L', value);
1474                                         c++;
1475                                 }
1476
1477                                 break;
1478                         case 2:
1479                                 // DATA segment relocatble symbols
1480                                 if (type == 0x06000000 || type == 0x07000000)
1481                                 {
1482                                         printf("%-8s %c  %08X   ", symbol, (type & 0x01000000) ? 'G' : 'L', value);
1483                                         c++;
1484                                 }
1485
1486                                 break;
1487                         case 3:
1488                                 // BSS segment relocatable symbols
1489                                 if (type == 0x08000000 || type == 0x09000000)
1490                                 {
1491                                         printf("%-8s %c  %08X   ", symbol, (type & 0x01000000) ? 'G' : 'L', value);
1492                                         c++;
1493                                 }
1494
1495                                 break;
1496                         }
1497                 }
1498
1499                 printf("\n\n");
1500         }
1501
1502         return 0;
1503 }
1504
1505
1506 //
1507 // Stuff the (long) value of a string into the value argument. RETURNS TRUE if
1508 // the string doesn't parse.  Parses only as a hexadecimal string.
1509 //
1510 int GetHexValue(char * string, int * value)
1511 {
1512         *value = 0;
1513
1514         while (isxdigit(*string))
1515         {
1516                 if (isdigit(*string))
1517                 {
1518                         *value = (*value << 4) + (*string++ - '0');
1519                 }
1520                 else
1521                 {
1522                         if (isupper(*string))
1523                                 *string = tolower(*string);
1524
1525                         *value = (*value << 4) + ((*string++ - 'a') + 10);
1526                 }
1527         }
1528
1529         if (*string != '\0')
1530         {
1531                 printf("Invalid hexadecimal value");
1532                 return 1;
1533         }
1534
1535         return 0;
1536 }
1537
1538
1539 //
1540 // Create one big .o file from the images already in memory, returning a
1541 // pointer to an OHEADER. Note that the oheader is just the header for the
1542 // output (plus some other information). The text, data, and fixups are all
1543 // still in the ofile images hanging off the global `olist'.
1544 //
1545 struct OHEADER * MakeOutputFile()
1546 {
1547         unsigned tptr, dptr, bptr;      // Bases in runtime model
1548         int ret = 0;                            // Return value
1549         struct OHEADER * header;        // Output header pointer
1550
1551         // Initialize cumulative segment sizes
1552         textsize = datasize = bsssize = 0;
1553
1554         // For each object file, accumulate the sizes of the segments but remove
1555         // those object files which are unused
1556         struct OFILE * oprev = NULL;    // Init previous obj file list ptr
1557         struct OFILE * otemp = olist;   // Set temp pointer to object file list
1558         int i = 0;
1559
1560         while (otemp != NULL)
1561         {
1562                 // UNUSED !!!!! (it's !O_ARCHIVE, so this code executes.)
1563                 if (!(otemp->o_flags & O_ARCHIVE))
1564                 {
1565                         if ((otemp->o_flags & O_USED) == 0)
1566                         {
1567                                 if (wflag)
1568                                 {
1569                                         printf("Unused object file ");
1570                                         WriteARName(otemp);
1571                                         printf(" discarded.\n");
1572                                 }
1573
1574                                 // Drop the entry from the linked list
1575                                 if (oprev == NULL)
1576                                         olist = otemp->o_next;
1577                                 else
1578                                         oprev->o_next = otemp->o_next;
1579
1580                                 // Free the object entry if it's not an archive file
1581                                 if (!otemp->isArchiveFile)
1582                                         free(otemp->o_image);
1583
1584                                 // Also need to remove them from the obj_* tables too :-P
1585                                 // N.B.: Would probably be worthwhile to remove crap like this
1586                                 //       and stuff it into the OFILE structure...
1587                                 if (wflag)
1588                                         printf("»-> removing %s... (index == %i, len == %i)\n", obj_fname[i], i, obj_index - 1);
1589
1590                                 int k;
1591
1592                                 for(k=i; k<obj_index-1; k++)
1593                                 {
1594                                         memcpy(obj_fname[k], obj_fname[k + 1], FNLEN);
1595                                         obj_segsize[k][0] = obj_segsize[k + 1][0];
1596                                         obj_segsize[k][1] = obj_segsize[k + 1][1];
1597                                         obj_segsize[k][2] = obj_segsize[k + 1][2];
1598                                 }
1599
1600                                 obj_index--;
1601                                 i--;
1602                         }
1603                         else
1604                         {
1605                                 // Increment total of segment sizes ensuring requested
1606                                 // alignment
1607                                 textsize += (otemp->o_header.tsize + secalign) & ~secalign;
1608                                 datasize += (otemp->o_header.dsize + secalign) & ~secalign;
1609                                 bsssize  += (otemp->o_header.bsize + secalign) & ~secalign;
1610                                 oprev = otemp;
1611                         }
1612                 }
1613
1614                 // Go to next object file list pointer
1615                 otemp = otemp->o_next;
1616                 i++;
1617         }
1618
1619         // Update base addresses and inject the symbols _TEXT_E, _DATA_E and _BSS_E
1620         // into the OST
1621         tbase = tval;
1622
1623         if (!dval)
1624         {
1625                 // DATA follows TEXT
1626                 dbase = tval + textsize;
1627
1628                 if (!bval)
1629                         // BSS follows DATA
1630                         bbase = tval + textsize + datasize;
1631                 else
1632                         // BSS is independent of DATA
1633                         bbase = bval;
1634         }
1635         else
1636         {
1637                 // DATA is independant of TEXT
1638                 dbase = dval;
1639
1640                 if (!bval)
1641                         // BSS follows DATA
1642                         bbase = dval + datasize;
1643                 else
1644                         // BSS is independent of DATA
1645                         bbase = bval;
1646         }
1647
1648         OSTAdd("_TEXT_E", 0x05000000, tbase + textsize);
1649         OSTAdd("_DATA_E", 0x07000000, dbase + datasize);
1650         OSTAdd("_BSS_E",  0x09000000, bbase + bsssize);
1651
1652         // Place each unresolved symbol in the output symbol table
1653         // N.B.: It only gets here to do this if user passes in -u flag
1654         //       [Only used here, once]
1655         if (DoUnresolved())
1656                 return NULL;
1657
1658         // Initialise base addresses
1659         tptr = dptr = bptr = 0;
1660
1661         // For each file, relocate its symbols and add them to the output symbol
1662         // table
1663         otemp = olist;
1664         oprev = NULL;
1665
1666         while (otemp != NULL)
1667         {
1668                 otemp->o_tbase = tptr;
1669
1670                 // Do rest only for non-ARCHIVE markers
1671                 if (!(otemp->o_flags & O_ARCHIVE))
1672                 {
1673                         otemp->o_dbase = dptr;
1674                         otemp->o_bbase = bptr;
1675                         tptr += (otemp->o_header.tsize + secalign) & ~secalign;
1676                         dptr += (otemp->o_header.dsize + secalign) & ~secalign;
1677                         bptr += (otemp->o_header.bsize + secalign) & ~secalign;
1678                 }
1679
1680                 // For each symbol, (conditionally) add it to the ost
1681                 // For ARCHIVE markers, this adds the symbol for the file & returns
1682                 // (Shamus: N.B. it does no such thing ATM)
1683                 // [Only used here, once]
1684                 if (DoSymbols(otemp))
1685                         return NULL;
1686
1687                 if (otemp->o_flags & O_ARCHIVE)
1688                 {
1689                         struct OFILE * oNext = otemp->o_next;
1690
1691                         // Now that the archive is marked, remove it from list
1692                         if (oprev == NULL)
1693                                 olist = otemp->o_next;
1694                         else
1695                                 oprev->o_next = otemp->o_next;
1696
1697                         if (otemp->o_image && !otemp->isArchiveFile)
1698                                 free(otemp->o_image);
1699
1700                         free(otemp);
1701                         otemp = oNext;
1702                 }
1703                 else
1704                 {
1705                         oprev = otemp;
1706                         otemp = otemp->o_next;
1707                 }
1708         }
1709
1710         // Places all the externs, globals etc into the output symbol table
1711         if (DoCommon() == -1)
1712                 return NULL;
1713
1714         // Create a new output file header
1715         header = new_oheader();
1716
1717         if (header == NULL)
1718         {
1719                 printf("MakeOutputFile: out of memory!\n");
1720                 return NULL;
1721         }
1722
1723         // Fill in the output header. Does not match the actual output but values
1724         // used as reference
1725         header->magic = 0x0150;                         // COF magic number
1726         header->tsize = textsize;                       // TEXT segment size
1727         header->dsize = datasize;                       // DATA segment size
1728         header->bsize = bsssize;                        // BSS segment size
1729         header->ssize = (ost_ptr - ost);        // Symbol table size
1730         header->ostbase = ost;                          // Output symbol table base address
1731
1732         // For each object file, relocate its TEXT and DATA segments. OR the result
1733         // into ret so all files get moved (and errors reported) before returning
1734         // with the error condition
1735         for(otemp=olist; otemp!=NULL; otemp=otemp->o_next)
1736         {
1737                 if (!(otemp->o_flags & O_ARCHIVE))
1738                 {
1739                         ret |= RelocateSegment(otemp, T_TEXT); // TEXT segment relocations
1740                         ret |= RelocateSegment(otemp, T_DATA); // DATA segment relocations
1741                 }
1742         }
1743
1744         // Done with global symbol hash tables
1745         FreeHashes();
1746
1747         return (ret ? (struct OHEADER *)NULL : header);
1748 }
1749
1750
1751 //
1752 // Add symbol to hash list
1753 //
1754 int AddSymbolToHashList(struct HREC ** hptr, char * sym, struct OFILE * ofile,
1755         long value, int type)
1756 {
1757         struct HREC * htemp = new_hrec();
1758
1759         if (htemp == NULL)
1760         {
1761                 printf("Out of memory\n");
1762                 return 1;
1763         }
1764
1765         // Shamus: Moar testing...
1766         if (vflag > 1)
1767         {
1768                 printf("AddSymbolToHashList(): hptr=$%08X, sym=\"%s\", ofile=$%08X, value=$%X, type=$%X\n", hptr, sym, ofile, value, type);
1769         }
1770
1771         // Populate hash record
1772         memset(htemp->h_sym, 0, SYMLEN);
1773         strcpy(htemp->h_sym, sym);
1774         htemp->h_ofile = ofile;
1775         htemp->h_value = value;
1776         htemp->h_type = type;
1777
1778         // Add new hash to the front of the list (hence the ** for hptr)
1779         htemp->h_next = *hptr;
1780         *hptr = htemp;
1781
1782         return 0;
1783 }
1784
1785
1786 //
1787 // Add symbol to the unresolved symbols hash table (really, it's a linked list)
1788 //
1789 int AddUnresolvedSymbol(char * sym, struct OFILE * ofile)
1790 {
1791         if (vflag > 1)
1792                 printf("AddUnresolvedSymbol(%s, %s)\n", sym, ofile->o_name);
1793
1794         return AddSymbolToHashList(&unresolved, sym, ofile, 0L, 0);
1795 }
1796
1797
1798 //
1799 // Remove the HREC from the unresolved symbol list, and pass back a pointer
1800 // to the spot where the HREC was.
1801 //
1802 struct HREC * RemoveUnresolvedSymbol(struct HREC * hrec)
1803 {
1804         struct HREC * ptr = unresolved;
1805         struct HREC * previous = NULL;
1806
1807         while ((ptr != hrec) && (ptr != NULL))
1808         {
1809                 previous = ptr;
1810                 ptr = ptr->h_next;
1811         }
1812
1813         // Not found...!
1814         if (ptr == NULL)
1815                 return NULL;
1816
1817         struct HREC * next = ptr->h_next;
1818
1819         // Remove the head if nothing previous, otherwise, remove what we found
1820         if (previous == NULL)
1821                 unresolved = next;
1822         else
1823                 previous->h_next = next;
1824
1825         free(ptr);
1826         return next;
1827 }
1828
1829
1830 //
1831 // Add symbol to the unresolved symbols hash table
1832 //
1833 int AddARSymbol(char * sym, struct OFILE * ofile)
1834 {
1835         if (vflag > 1)
1836                 printf("AddARSymbol(%s, %s)\n", sym, ofile->o_name);
1837
1838         return AddSymbolToHashList(&arSymbol, sym, ofile, 0L, 0);
1839 }
1840
1841
1842 //
1843 // Generate hash value from the 1st 15 characters of the symbol modulo the
1844 // number of buckets in the hash.
1845 //
1846 int GetHash(char * s)
1847 {
1848         // For this to be consistent, the symbol MUST be zeroed out beforehand!
1849         // N.B.: strncpy() pads zeroes for us, if the symbol is less than 15 chars.
1850         char c[15];
1851         strncpy(c, s, 15);
1852
1853         int i = (c[0] + c[1] + c[2] + c[3] + c[4] + c[5] + c[6] + c[7] + c[8]
1854                 + c[9] + c[10] + c[11] + c[12] + c[13] + c[14]) % NBUCKETS;
1855         return i;
1856 }
1857
1858
1859 //
1860 // Lookup a symbol in the hash table.
1861 // Returns either a pointer to the HREC or NULL if not found.
1862 //
1863 struct HREC * LookupHREC(char * symbol)
1864 {
1865         struct HREC * hptr = htable[GetHash(symbol)];
1866
1867         while (hptr != NULL)
1868         {
1869 //This is utter failure...
1870 //              if (symcmp(symbol, hptr->h_sym))  <-- left here for giggles :D  - LinkoVitch
1871                 // Return hash record pointer if found
1872                 if (strcmp(symbol, hptr->h_sym) == 0)
1873                         return hptr;
1874
1875                 hptr = hptr->h_next;
1876         }
1877
1878         return NULL;
1879 }
1880
1881
1882 //
1883 // Lookup a symbol in the AR symbol table.
1884 // Returns either a pointer to the HREC or NULL if not found.
1885 //
1886 struct HREC * LookupARHREC(char * symbol)
1887 {
1888         struct HREC * hptr = arSymbol;
1889
1890         while (hptr != NULL)
1891         {
1892                 // Return hash record pointer if found
1893                 if (strcmp(symbol, hptr->h_sym) == 0)
1894                         return hptr;
1895
1896                 hptr = hptr->h_next;
1897         }
1898
1899         return NULL;
1900 }
1901
1902
1903 //
1904 // Find the index in the obj_segsize table for the passed in filename
1905 //
1906 int GetObjSegSizeIndex(char * name)
1907 {
1908         int i;
1909
1910         for(i=0; i<(int)obj_index; i++)
1911         {
1912                 if (strcmp(name, obj_fname[i]) == 0)
1913                         // Object file name was found!
1914                         return i;
1915         }
1916
1917         return -1;
1918 }
1919
1920
1921 //
1922 // Add the imported symbols from this file to unresolved, and the global and
1923 // common (???) symbols to the exported hash table.
1924 //
1925 // Change old-style commons (type == T_EXTERN, value != 0) to new-style ones
1926 // (type == (T_GLOBAL | T_EXTERN)). [??? O_o]
1927 // [N.B.: Whoever wrote the above didn't know what the fuck they were talking
1928 //        about. Commons (globals) are exactly what they are calling 'old
1929 //        style'. Also note, that there is no "T_GLOBAL" or "T_EXTERN" symbols
1930 //        defined anywhere in the code.]
1931 //
1932 int AddSymbols(struct OFILE * Ofile)
1933 {
1934         struct HREC * hptr;                     // Hash record pointer
1935
1936         if (vflag > 1)
1937         {
1938                 printf("AddSymbols: for file %s\n", Ofile->o_name);
1939                 printf("            t_bbase = $%X\n", Ofile->o_tbase);
1940                 printf("            d_bbase = $%X\n", Ofile->o_dbase);
1941                 printf("            o_bbase = $%X\n", Ofile->o_bbase);
1942                 printf("            tsize = $%X\n", Ofile->o_header.tsize);
1943                 printf("            dsize = $%X\n", Ofile->o_header.dsize);
1944                 printf("            bsize = $%X\n", Ofile->o_header.bsize);
1945                 printf("            reloc.tsize = $%X\n", Ofile->o_header.absrel.reloc.tsize);
1946                 printf("            reloc.dsize = $%X\n", Ofile->o_header.absrel.reloc.dsize);
1947         }
1948
1949         // Get base pointer, start of sym fixups
1950         char * ptr = Ofile->o_image + 32
1951                 + Ofile->o_header.tsize
1952                 + Ofile->o_header.dsize
1953                 + Ofile->o_header.absrel.reloc.tsize
1954                 + Ofile->o_header.absrel.reloc.dsize;
1955         char * sfix = ptr;                                                      // Set symbol fixup pointer
1956         char * sstr = sfix + Ofile->o_header.ssize;     // Set symbol table pointer
1957         long nsymbols = Ofile->o_header.ssize / 12;     // Obtain number of symbols
1958
1959         while (nsymbols)
1960         {
1961                 long index = GetLong(sfix);                             // Get symbol string index
1962                 long type  = GetLong(sfix + 4);                 // Get symbol type
1963                 long value = GetLong(sfix + 8);                 // Get symbol value
1964
1965                 if ((Ofile->isArchiveFile) && !(Ofile->o_flags & O_USED))
1966                 {
1967                         if ((type & T_GLBL) && (type & (T_SEG | T_ABS)))
1968                                 if (AddARSymbol(sstr + index, Ofile))
1969                                         return 1;
1970                 }
1971                 else if (type == T_GLBL)
1972                 {
1973                         // External symbol that is *not* in the current unit
1974                         // N.B.: Also, could be common symbol *in* current unit...!
1975                         hptr = LookupHREC(sstr + index);
1976
1977                         if (hptr != NULL)
1978                                 hptr->h_ofile->o_flags |= O_USED;       // Mark .o file as used
1979                         // Otherwise, *maybe* add to unresolved list
1980                         else
1981                         {
1982                                 // Check to see if this is a common symbol; if so, try to add
1983                                 // it to the hash list...
1984                                 if (value != 0)
1985                                 {
1986                                         // Actually, we need to convert this to a BSS symbol,
1987                                         // increase the size of the BSS segment for this object, &
1988                                         // add it to the hash list
1989                                         uint32_t valueSave = value;
1990                                         uint32_t bssLocation = Ofile->o_header.tsize + Ofile->o_header.dsize + Ofile->o_header.bsize;
1991                                         Ofile->o_header.bsize += value;
1992                                         type |= T_BSS;
1993                                         value = bssLocation;
1994                                         PutLong(sfix + 4, type);
1995                                         PutLong(sfix + 8, value);
1996
1997                                         // Also need to reset the size of the object's BSS section:
1998                                         int i = GetObjSegSizeIndex(Ofile->o_name);
1999 //printf("AddSymbols: obj_segsize[%i][BSS] = %d (value = %d)\n", i, obj_segsize[i][BSS], value);
2000                                         obj_segsize[i][BSS] += valueSave; // need to realign the section, but only *after* all the common symbols have been added from this unit... !!! FIX !!!
2001 //                                      obj_segsize[obj_index][2] = (GetLong(ptr + 12) + secalign) & ~secalign;
2002
2003                                         if (vflag > 1)
2004                                                 printf("AddSymbols: Resetting common label to BSS label\n");
2005
2006                                         if (AddSymbolToHashList(&htable[GetHash(sstr + index)],
2007                                                 sstr + index, Ofile, value, type))
2008                                                 return 1;                               // Error if addition failed
2009                                 }
2010                                 // Check for built-in externals...
2011                                 else if ((strcmp(sstr + index, "_TEXT_E") != 0)
2012                                         && (strcmp(sstr + index, "_DATA_E") != 0)
2013                                         && (strcmp(sstr + index, "_BSS_E") != 0))
2014                                 {
2015                                         if (AddUnresolvedSymbol(sstr + index, Ofile))
2016                                                 return 1;                               // Error if addition failed
2017                                 }
2018                         }
2019                 }
2020                 else if ((type & T_GLBL) && (type & (T_SEG | T_ABS)))
2021                 {
2022                         hptr = LookupHREC(sstr + index);
2023
2024                         // Symbol isn't in the table, so try to add it:
2025                         if (hptr == NULL)
2026                         {
2027                                 if (AddSymbolToHashList(&htable[GetHash(sstr + index)],
2028                                         sstr + index, Ofile, value, type))
2029                                         return 1;
2030                         }
2031                         else
2032                         {
2033                                 // Symbol already exists, decide what to do about it
2034                                 // [N.B.: This isn't a check for a common symbol...
2035                                 //        BEWARE OF BAD INTERPRETATIONS!!]
2036                                 if (iscommon(hptr->h_type))
2037                                 {
2038                                         // Mismatch: common came first; warn and keep the global
2039                                         if (wflag)
2040                                         {
2041                                                 printf("Warning: %s: global from ", sstr + index);
2042                                                 WriteARName(Ofile);
2043                                                 printf(" used, common from ");
2044                                                 WriteARName(hptr->h_ofile);
2045                                                 printf(" discarded.\n");
2046                                         }
2047
2048                                         hptr->h_ofile = Ofile;
2049                                         hptr->h_type = type;
2050                                         hptr->h_value = value;
2051                                 }
2052                                 else
2053                                 {
2054                                         // Global exported by another ofile; warn and make this one
2055                                         // extern
2056                                         if (wflag)
2057                                         {
2058                                                 printf("Duplicate symbol %s: ", sstr + index);
2059                                                 WriteARName(hptr->h_ofile);
2060                                                 printf(" used, ");
2061                                                 WriteARName(Ofile);
2062                                                 printf(" discarded\n");
2063                                         }
2064
2065                                         // Set the global in this unit to pure external
2066                                         // (is this a good idea? what if the other one is a ref to
2067                                         // this one???)
2068                                         PutLong(sfix + 4, T_GLBL);
2069                                 }
2070                         }
2071                 }
2072
2073                 sfix += 12;                     // Increment symbol fixup pointer
2074                 nsymbols--;                     // Decrement num of symbols to process
2075         }
2076
2077         // Success loading symbols
2078         return 0;
2079 }
2080
2081
2082 //
2083 // Process object file for symbols
2084 //
2085 int DoItem(struct OFILE * obj)
2086 {
2087         // Allocate memory for object record ptr
2088         struct OFILE * Ofile = new_ofile();
2089
2090         if (Ofile == NULL)
2091         {
2092                 printf("Out of memory while processing %s\n", obj->o_name);
2093                 return 1;
2094         }
2095
2096         // Starting after all pathnames, etc., copy .o file name to Ofile
2097         char * temp = PathTail(obj->o_name);
2098
2099         // Check filename length
2100         if (strlen(temp) > FNLEN - 1)
2101         {
2102                 printf("File name too long: %s\n", temp);
2103                 return 1;
2104         }
2105
2106         // Check archive name length
2107         if (strlen(obj->o_arname) > FNLEN - 1)
2108         {
2109                 printf("Archive name too long: %s\n", obj->o_arname);
2110                 return 1;
2111         }
2112
2113         strcpy(Ofile->o_name, temp);            // Store filename
2114         strcpy(Ofile->o_arname, obj->o_arname); // Store archive name
2115
2116         // Initialise object record information
2117         Ofile->o_next  = NULL;
2118         Ofile->o_tbase = 0;
2119         Ofile->o_dbase = 0;
2120         Ofile->o_bbase = 0;
2121         Ofile->o_flags = obj->o_flags;
2122         Ofile->o_image = obj->o_image;
2123         Ofile->isArchiveFile = obj->isArchiveFile;
2124         char * ptr = obj->o_image;
2125
2126         // Don't do anything if this is just an ARCHIVE marker, just add the file
2127         // to the olist
2128         // (Shamus: N.B.: it does no such ARCHIVE thing ATM)
2129         if (!(obj->o_flags & O_ARCHIVE))
2130         {
2131                 Ofile->o_header.magic = GetLong(ptr);
2132                 Ofile->o_header.tsize = GetLong(ptr + 4);
2133                 Ofile->o_header.dsize = GetLong(ptr + 8);
2134                 Ofile->o_header.bsize = GetLong(ptr + 12);
2135                 Ofile->o_header.ssize = GetLong(ptr + 16);
2136                 Ofile->o_header.absrel.reloc.tsize = GetLong(ptr + 24);
2137                 Ofile->o_header.absrel.reloc.dsize = GetLong(ptr + 28);
2138
2139                 // Round BSS off to alignment boundary
2140                 Ofile->o_header.bsize = (Ofile->o_header.bsize + secalign) & ~secalign;
2141
2142                 if ((Ofile->o_header.dsize & 7) && wflag)
2143                 {
2144                         printf("Warning: data segment size of ");
2145                         WriteARName(Ofile);
2146                         printf(" is not a phrase multiple\n");
2147                 }
2148
2149                 // Check for odd segment sizes
2150                 if ((Ofile->o_header.tsize & 1) || (Ofile->o_header.dsize & 1)
2151                         || (Ofile->o_header.bsize & 1))
2152                 {
2153                         printf("Error: odd-sized segment in ");
2154                         WriteARName(Ofile);
2155                         printf("; link aborted.\n");
2156                         return 1;
2157                 }
2158
2159                 if (AddSymbols(Ofile))
2160                         return 1;
2161         }
2162
2163         // Add this file to the olist
2164         if (olist == NULL)
2165                 olist = Ofile;
2166         else
2167                 olast->o_next = Ofile;
2168
2169         olast = Ofile;
2170         return 0;
2171 }
2172
2173
2174 //
2175 // Handle items in processing list.
2176 //
2177 // After loading all objects, archives & include files, we now go and process
2178 // each item on the processing list (plist). Once this is done, we go through
2179 // any unresolved symbols left and see if they have shown up.
2180 //
2181 int ProcessLists(void)
2182 {
2183         // Process object file list first (adds symbols from each unit & creates
2184         // the olist)
2185         while (plist != NULL)
2186         {
2187                 if (DoItem(plist))
2188                         return 1;
2189
2190                 struct OFILE * ptemp = plist;
2191                 plist = plist->o_next;
2192                 free(ptemp);
2193         }
2194
2195         struct HREC * uptr;
2196
2197         // Process the unresolved symbols list. This may involve pulling in symbols
2198         // from any included .a units. Such units are lazy linked by default; we
2199         // generally don't want everything they provide, just what's referenced.
2200         for(uptr=unresolved; uptr!=NULL; )
2201         {
2202                 if (vflag > 1)
2203                         printf("LookupHREC(%s) => ", uptr->h_sym);
2204
2205                 struct HREC * htemp = LookupHREC(uptr->h_sym);
2206
2207                 if (htemp != NULL)
2208                 {
2209                         // Found it in the symbol table!
2210                         if (vflag > 1)
2211                                 printf("%s in %s (=$%06X)\n", (isglobal(htemp->h_type) ? "global" : "common"), htemp->h_ofile->o_name, htemp->h_value);
2212
2213                         // Mark the .o unit that the symbol is in as seen & remove from the
2214                         // unresolved list
2215                         htemp->h_ofile->o_flags |= O_USED;
2216                         uptr = RemoveUnresolvedSymbol(uptr);
2217                 }
2218                 else
2219                 {
2220                         if (vflag > 1)
2221                                 printf("NULL\n");
2222
2223                         // Check to see if the unresolved symbol is on the AR symbol list.
2224                         htemp = LookupARHREC(uptr->h_sym);
2225
2226                         // If the unresolved symbol is in a .o unit that is unused, we can
2227                         // drop it; same if the unresolved symbol is in the exported AR
2228                         // symbol list. Otherwise, go to the next unresolved symbol.
2229                         if (!(uptr->h_ofile->o_flags & O_USED) || (htemp != NULL))
2230                                 uptr = RemoveUnresolvedSymbol(uptr);
2231                         else
2232                                 uptr = uptr->h_next;
2233
2234                         // Now that we've possibly deleted the symbol from unresolved list
2235                         // that was also in the AR list, we add the symbols from this .o
2236                         // unit to the symbol table, mark the .o unit as used, and restart
2237                         // scanning the unresolved list as there is a good possibility that
2238                         // the symbols in the unit we're adding has unresolved symbols as
2239                         // well.
2240                         if (htemp != NULL)
2241                         {
2242                                 htemp->h_ofile->o_flags |= O_USED;
2243                                 AddSymbols(htemp->h_ofile);
2244                                 uptr = unresolved;
2245                         }
2246                 }
2247         }
2248
2249         // Show files used if the user requests it.
2250         if (vflag > 1)
2251         {
2252                 printf("Files used:\n");
2253                 struct OFILE * filePtr = olist;
2254
2255                 while (filePtr != NULL)
2256                 {
2257                         if (filePtr->o_flags & O_USED)
2258                         {
2259                                 printf("   %s%s%s\n", filePtr->o_name, (filePtr->isArchiveFile ? ":" : ""), (filePtr->isArchiveFile ? filePtr->o_arname : nullStr));
2260                         }
2261
2262                         filePtr = filePtr->o_next;
2263                 }
2264         }
2265
2266         return 0;
2267 }
2268
2269
2270 //
2271 // Extract filename from path
2272 //
2273 char * PathTail(char * name)
2274 {
2275         // Find last occurance of PATH_DELIMETER
2276         char * temp = strrchr(name, PATH_DELIMITER);
2277
2278         // Return what was passed in if path delimiter was not found
2279         if (temp == NULL)
2280                 return name;
2281
2282         return temp + 1;
2283 }
2284
2285
2286 //
2287 // Add input file to processing list
2288 //
2289 int AddToProcessingList(char * ptr, char * fname, char * arname, uint8_t arFile, uint32_t tSize, uint32_t dSize, uint32_t bSize)
2290 {
2291         if (plist == NULL)
2292         {
2293                 // First time object record allocation
2294                 plist = new_ofile();
2295                 plast = plist;
2296         }
2297         else
2298         {
2299                 // Next object record allocation
2300                 plast->o_next = new_ofile();
2301                 plast = plast->o_next;
2302         }
2303
2304         if (plast == NULL)
2305         {
2306                 printf("Out of memory.\n");             // Error if memory allocation fails
2307                 return 1;
2308         }
2309
2310         // Discard paths from filenames...
2311         fname = PathTail(fname);
2312         arname = PathTail(arname);
2313
2314         // Check for filename length errors...
2315         if (strlen(fname) > (FNLEN - 1))
2316         {
2317                 printf("File name too long: %s (sorry!)\n", fname);
2318                 return 1;
2319         }
2320
2321         if (strlen(arname) > (FNLEN - 1))
2322         {
2323                 printf("AR file name too long: %s (sorry!)\n", arname);
2324                 return 1;
2325         }
2326
2327         strcpy(plast->o_name, fname);           // Store filename sans path
2328         strcpy(plast->o_arname, arname);        // Store archive name sans path
2329         plast->o_image = ptr;                           // Store data pointer
2330         plast->o_flags = (arFile ? 0 : O_USED); // File is used if NOT in archive
2331         plast->o_next = NULL;                           // Initialise next record pointer
2332         plast->isArchiveFile = arFile;          // Shamus: Temp until can sort it out
2333         plast->segSize[TEXT] = tSize;
2334         plast->segSize[DATA] = dSize;
2335         plast->segSize[BSS]  = bSize;
2336
2337         return 0;                                                       // Return without errors
2338 }
2339
2340
2341 //
2342 // Process in binary include files and add them to the processing list. This
2343 // routine takes in the binary file and creates an 'object' file in memory.
2344 // Sym1/Sym2 point to the start and end of data.
2345 //
2346 // Image size for include files is:
2347 // Header ....... 32 bytes
2348 // Data ......... dsize
2349 // Sym Fixups ... 2 * 12 bytes
2350 // Symbol Size .. 4 bytes (Value to include symbols and terminating null)
2351 // Symbols ...... (strlen(sym1) + 1) + (strlen(sym2) + 1)
2352 // Terminate .... 4 bytes (0x00000000)
2353 //
2354 int LoadInclude(char * fname, int handle, char * sym1, char * sym2, int segment)
2355 {
2356         char * ptr, * sptr;
2357         int i;
2358         unsigned symtype = 0;
2359
2360         long fsize = FileSize(handle);          // Get size of include file
2361         long dsize = (fsize + secalign) & ~secalign;    // Align size to boundary
2362         int sym1len = strlen(sym1) + 1;         // Get sym1 length + null termination
2363         int sym2len = strlen(sym2) + 1;         // Get sym2 length + null termination
2364         long size = 32 + dsize + 24 + 4 + sym1len + sym2len + 4;
2365
2366         // Use calloc so the header & fixups initialize to zero
2367         // Allocate object image memory
2368         if ((ptr = calloc(size, 1)) == NULL)
2369         {
2370                 printf("Out of memory while including %s\n", fname);
2371                 close(handle);
2372                 return 1;
2373         }
2374
2375         // Read in binary data
2376         if (read(handle, ptr + 32, fsize) != fsize)
2377         {
2378                 printf("File read error on %s\n", fname);
2379                 close(handle);
2380                 free(ptr);
2381                 return 1;
2382         }
2383
2384         close(handle);
2385
2386         strcpy(obj_fname[obj_index], PathTail(fname));
2387
2388         // Build this image's dummy header
2389         PutLong(ptr, 0x00000107);              // Magic number
2390
2391         if (segment)
2392         {
2393                 PutLong(ptr+4, dsize);             // Text size
2394                 PutLong(ptr+8, 0L);                // Data size
2395                 symtype = 0x05000000;
2396                 obj_segsize[obj_index][0] = dsize;
2397                 obj_segsize[obj_index][1] = 0;
2398                 obj_segsize[obj_index][2] = 0;
2399         }
2400         else
2401         {
2402                 PutLong(ptr+4, 0L);                // Text size
2403                 PutLong(ptr+8, dsize);             // Data size
2404                 symtype = 0x07000000;
2405                 obj_segsize[obj_index][0] = 0;
2406                 obj_segsize[obj_index][1] = dsize;
2407                 obj_segsize[obj_index][2] = 0;
2408         }
2409
2410         obj_index++;                           // Increment object count
2411
2412         PutLong(ptr+12, 0L);                   // BSS size
2413         PutLong(ptr+16, 24);                   // Symbol table size
2414         PutLong(ptr+20, 0L);                   // Entry point
2415         PutLong(ptr+24, 0L);                   // TEXT relocation size
2416         PutLong(ptr+28, 0L);                   // DATA relocation size
2417
2418         sptr = ptr + 32 + dsize;               // Set sptr to symbol table location
2419
2420         PutLong(sptr,    4L);                  // String offset of symbol1
2421         PutLong(sptr+4,  symtype);             // Symbol type
2422         PutLong(sptr+8,  0x00000000);          // Symbol has no value (START)
2423         PutLong(sptr+12, 4L + (sym2len - 1));  // String offset of symbol2
2424         PutLong(sptr+16, symtype);             // Symbol type
2425         PutLong(sptr+20, dsize);               // Symbol is data size (END)
2426
2427         sptr = ptr + 32 + dsize + 24;          // Set sptr to symbol table size loc
2428
2429         PutLong(sptr, sym1len + 4L);           // Size of symbol table
2430
2431         sptr = ptr + 32 + dsize + 24 + 4;      // Set sptr to symbol table location
2432
2433         for(i=0; i<(sym1len-1); i++)           // Write symbol1 to string table
2434                 sptr[i] = *sym1++;
2435
2436         sptr += (sym1len - 1);                 // Step past symbol string
2437         *sptr = '\0';                          // Terminate symbol string
2438         sptr += 1;                             // Step past termination
2439
2440         for(i=0; i<(sym2len-1); i++)           // Write symbol2 to string table
2441                 sptr[i] = *sym2++;
2442
2443         sptr += (sym2len - 1);                 // Step past symbol string
2444         *sptr = '\0';                          // Terminate symbol string
2445         sptr += 1;                             // Step past termination
2446
2447         PutLong(sptr, 0L);                     // Terminating long for object file
2448
2449         return AddToProcessingList(ptr, fname, nullStr, 0, obj_segsize[obj_index - 1][0], obj_segsize[obj_index - 1][1], obj_segsize[obj_index - 1][2]);
2450 }
2451
2452
2453 //
2454 // Takes a file name, gets in its image, puts it on plist. The image may
2455 // already be in memory: If so, the ptr arg is non-null.  If so, the file is
2456 // already closed. Note that the file is already open (from DoFile()). RETURNS
2457 // a pointer to the OFILE structure for this file, so you can diddle its flags
2458 // (DoFile sets O_USED for files on the command line).
2459 //
2460 int LoadObject(char * fname, int fd, char * ptr)
2461 {
2462         if (ptr == NULL)
2463         {
2464                 long size = FileSize(fd);
2465
2466                 // Allocate memory for file data
2467                 ptr = malloc(size);
2468
2469                 if (ptr == NULL)
2470                 {
2471                         printf("Out of memory while processing %s\n", fname);
2472                         close(fd);
2473                         return 1;
2474                 }
2475
2476                 // Read in file data
2477                 if (read(fd, ptr, size) != size)
2478                 {
2479                         printf("File read error on %s\n", fname);
2480                         close(fd);
2481                         free(ptr);
2482                         return 1;
2483                 }
2484
2485                 // SCPCD: get the name of the file instead of all pathname
2486                 strcpy(obj_fname[obj_index], PathTail(fname));
2487                 obj_segsize[obj_index][0] = (GetLong(ptr + 4) + secalign) & ~secalign;
2488                 obj_segsize[obj_index][1] = (GetLong(ptr + 8) + secalign) & ~secalign;
2489                 obj_segsize[obj_index][2] = (GetLong(ptr + 12) + secalign) & ~secalign;
2490                 obj_index++;
2491
2492                 close(fd);                                            // Close file
2493         }
2494
2495         // Now add this image to the list of pending ofiles (plist)
2496         // This routine is shared by LoadInclude after it builds the image
2497         return AddToProcessingList(ptr, fname, nullStr, 0, obj_segsize[obj_index - 1][0], obj_segsize[obj_index - 1][1], obj_segsize[obj_index - 1][2]);
2498 }
2499
2500
2501 //
2502 // What it says on the tin: check for a .o suffix on the passed in string
2503 //
2504 uint8_t HasDotOSuffix(char * s)
2505 {
2506         char * temp = strrchr(s, '.');
2507
2508         if ((temp == NULL) || (strncmp(temp, ".o", 2) != 0))
2509                 return 0;
2510
2511         return 1;
2512 }
2513
2514
2515 //
2516 // Process an ar archive file (*.a)
2517 //
2518 int LoadArchive(char * fname, int fd)
2519 {
2520         // Read in the archive file to memory and process
2521         long size = FileSize(fd);
2522         char * ptr = malloc(size);
2523         char * endPtr = ptr + size;
2524         char * longFilenames = NULL;
2525
2526         if (ptr == NULL)
2527         {
2528                 printf("Out of memory while processing %s\n", fname);
2529                 close(fd);
2530                 return 1;
2531         }
2532
2533         if (read(fd, ptr, size) != size)
2534         {
2535                 printf("File read error on %s\n", fname);
2536                 close(fd);
2537                 free(ptr);
2538                 return 1;
2539         }
2540
2541         close(fd);
2542
2543         // Save the pointer for later...
2544         arPtr[arIndex++] = ptr;
2545         char objName[FNLEN];
2546         char objSize[11];
2547         int i;
2548 //printf("\nProcessing AR file \"%s\"...\n", fname);
2549         ptr += 8;
2550
2551         // Loop through all objects in the archive and process them
2552         do
2553         {
2554                 memset(objName, 0, 17);
2555                 objSize[10] = 0;
2556
2557                 for(i=0; i<16; i++)
2558                 {
2559 //                      if ((ptr[i] == '/') || (ptr[i] == ' '))
2560                         if ((ptr[i] == ' ') && (i != 0))
2561                         {
2562                                 objName[i] = 0;
2563                                 break;
2564                         }
2565
2566                         objName[i] = ptr[i];
2567                 }
2568
2569                 for(i=0; i<10; i++)
2570                 {
2571                         if (ptr[48 + i] == ' ')
2572                         {
2573                                 objSize[i] = 0;
2574                                 break;
2575                         }
2576
2577                         objSize[i] = ptr[48 + i];
2578                 }
2579
2580                 // Check to see if a long filename was requested
2581                 if (objName[0] == 0x20)
2582                 {
2583                         uint32_t fnSize = atoi(objName + 1);
2584
2585                         if (longFilenames != NULL)
2586                         {
2587                                 i = 0;
2588                                 char * currentFilename = longFilenames + fnSize;
2589
2590                                 while (*currentFilename != 0x0A)
2591                                         objName[i++] = *currentFilename++;
2592
2593                                 objName[i] = 0;
2594                         }
2595                 }
2596
2597                 if ((strncmp(objName, "ARFILENAMES/", 12) == 0) || (strncmp(objName, "//", 2) == 0))
2598                 {
2599                         longFilenames = ptr + 60;
2600                 }
2601                 else if (HasDotOSuffix(objName))
2602                 {
2603
2604                         // Strip off any trailing forward slash at end of object name
2605                         int lastChar = strlen(objName) - 1;
2606
2607                         if (objName[lastChar] == '/')
2608                                 objName[lastChar] = 0;
2609
2610 //printf("Processing object \"%s\" (size == %i, obj_index == %i)...\n", objName, atoi(objSize), obj_index);
2611                         strcpy(obj_fname[obj_index], objName);
2612                         obj_segsize[obj_index][0] = (GetLong(ptr + 60 + 4) + secalign) & ~secalign;
2613                         obj_segsize[obj_index][1] = (GetLong(ptr + 60  + 8) + secalign) & ~secalign;
2614                         obj_segsize[obj_index][2] = (GetLong(ptr + 60  + 12) + secalign) & ~secalign;
2615                         obj_index++;
2616
2617                         if (AddToProcessingList(ptr + 60, objName, fname, 1, obj_segsize[obj_index - 1][0], obj_segsize[obj_index - 1][1], obj_segsize[obj_index - 1][2]))
2618                                 return 1;
2619                 }
2620
2621                 uint32_t size = atoi(objSize);
2622                 size += (size & 0x01 ? 1 : 0);
2623                 ptr += 60 + size;
2624         }
2625         while (ptr < endPtr);
2626
2627         return 0;
2628 }
2629
2630
2631 //
2632 // Process files (*.o, *.a) passed in on the command line
2633 //
2634 int ProcessFiles(void)
2635 {
2636         int i;
2637         char magic[8];          // Magic header number (4 bytes for *.o, 8 for *.a)
2638
2639         // Process all file handles
2640         for(i=0; i<(int)hd; i++)
2641         {
2642                 // Verbose mode information
2643                 if (vflag == 1)
2644                         printf("Read file %s%s\n", name[i], (hflag[i] ? " (include)" : ""));
2645
2646                 if (!hflag[i])
2647                 {
2648                         // Attempt to read file magic number (OBJECT/ARCHIVE FILES)
2649                         if (read(handle[i], magic, 8) != 8)
2650                         {
2651                                 printf("Error reading file %s\n", name[i]);
2652                                 close(handle[i]);
2653                                 return 1;
2654                         }
2655
2656                         lseek(handle[i], 0L, 0);        // Reset to start of input file
2657
2658                         // Look for RMAC/MAC/GCC (a.out) object files
2659                         if ((GetLong(magic) & 0xFFFF) == 0x0107)
2660                         {
2661                                 // Process input object file
2662                                 if (LoadObject(name[i], handle[i], 0L))
2663                                         return 1;
2664                         }
2665                         // Otherwise, look for an object archive file
2666                         else if (strncmp(magic, "!<arch>\x0A", 8) == 0)
2667                         {
2668                                 if (LoadArchive(name[i], handle[i]))
2669                                         return 1;
2670                         }
2671                         else
2672                         {
2673                                 // Close file and error
2674                                 printf("%s is not a supported object or archive file\n", name[i]);
2675                                 printf("Magic == [%02X][%02X][%02X][%02X]\n", magic[0], magic[1], magic[2], magic[3]);
2676                                 close(handle[i]);
2677                                 return 1;
2678                         }
2679                 }
2680                 else
2681                 {
2682                         // INCLUDE FILES
2683                         // If hflag[i] is 1, include this in the data segment; if 2, put it
2684                         // in text segment
2685                         if (LoadInclude(name[i], handle[i], hsym1[i], hsym2[i], hflag[i] - 1))
2686                                 return 1;
2687                 }
2688         }
2689
2690         // Free include, symbol & object handles
2691         for(i=0; i<(int)hd; i++)
2692         {
2693                 free(name[i]);
2694
2695                 if (hflag[i])
2696                 {
2697                         free(hsym1[i]);
2698                         free(hsym2[i]);
2699                 }
2700         }
2701
2702         // Reset next handle indicator
2703         hd = 0;
2704         return 0;
2705 }
2706
2707
2708 //
2709 // Load newargv with pointers to arguments found in the buffer
2710 //
2711 int parse(char * buf, char * newargv[])
2712 {
2713         int i = 1;
2714
2715         if (vflag)
2716                 printf("begin parsing\n");
2717
2718         while (1)
2719         {
2720                 while (*buf && strchr(",\t\n\r\14 ", *buf))
2721                         buf++;
2722
2723                 /* test for eof */
2724                 if (*buf == '\0' || *buf == 26)
2725                 {
2726                         if (i == 0)
2727                         {
2728                                 printf("No commands in command file\n");
2729                                 return -1;
2730                         }
2731                         else
2732                         {
2733                                 return i;
2734                         }
2735                 }
2736
2737                 /* test for comment */
2738                 if (*buf == '#')
2739                 {
2740                         /* found a comment; skip to next \n and start over */
2741                         while (*buf && *buf != '\n')
2742                                 buf++;
2743
2744                         continue;
2745                 }
2746
2747                 if (i == MAXARGS)
2748                 {
2749                         printf("Too many arguments in command file\n");
2750                         return -1;
2751                 }
2752
2753                 newargv[i] = buf;
2754
2755                 while (!strchr(",\t\n\r\14 ", *buf))
2756                 {
2757                         if (*buf == '\0' || *buf == 26)
2758                         {
2759                                 printf("Finished parsing %d args\n", i);
2760                                 return i;
2761                         }
2762
2763                         buf++;
2764                 }
2765
2766                 *buf++ = '\0';
2767
2768                 if (vflag)
2769                         printf("argv[%d] = \"%s\"\n", i, newargv[i]);
2770
2771                 i++;
2772         }
2773 }
2774
2775
2776 //
2777 // Process in a link command file
2778 //
2779 int docmdfile(char * fname)
2780 {
2781         int fd;                                     // File descriptor
2782         unsigned size;                              // Command file size
2783         char * ptr;                                 // Pointer
2784         int newargc;                                // New argument count
2785         char * (*newargv)[];                        // New argument value array
2786
2787         // Verbose information
2788         if (vflag > 1)
2789                 printf("docmdfile(%s)\n", fname);
2790
2791         // Allocate memory for new argument values
2792         newargv = malloc((long)(sizeof(char *) * MAXARGS));
2793
2794         if (!newargv)
2795         {
2796                 printf("Out of memory.\n");
2797                 return 1;
2798         }
2799
2800         // Attempt to open and read in the command file
2801         if (fname)
2802         {
2803                 if ((fd = open(fname, _OPEN_FLAGS)) < 0)
2804                 {
2805                         printf("Cannot open command file %s.\n", fname);
2806                         return 1;
2807                 }
2808
2809                 size = FileSize(fd);
2810
2811                 if ((ptr = malloc(size + 1)) == NULL)
2812                 {
2813                         printf("Out of memory.\n");
2814                         close(fd);
2815                         return 1;
2816                 }
2817
2818                 if (read(fd, ptr, size) != (int)size)
2819                 {
2820                         printf("Read error on command file %s.\n", fname);
2821                         close(fd);
2822                         return 1;
2823                 }
2824
2825                 *(ptr + size) = 0;                      // Null terminate the buffer
2826                 close(fd);
2827         }
2828         else
2829         {
2830                 printf("No command filename specified\n");
2831                 return 1;
2832         }
2833
2834         // Parse the command file
2835         if ((newargc = parse(ptr, *newargv)) == -1)
2836         {
2837                 return 1;
2838         }
2839
2840         // Process the inputted flags
2841         if (doargs(newargc, *newargv))
2842         {
2843                 printf("docmdfile: doargs returns TRUE\n");
2844                 return 1;
2845         }
2846
2847         free(ptr);
2848         free(newargv);
2849
2850         return 0;
2851 }
2852
2853
2854 //
2855 // Take an argument list and parse the command line
2856 //
2857 int doargs(int argc, char * argv[])
2858 {
2859         int i = 1;                                      // Iterator
2860         int c;                                          // Command line character
2861         char * ifile, * isym;           // File name and symbol name for -i
2862
2863         // Parse through option switches & files
2864         while (i < argc)
2865         {
2866                 // Process command line switches
2867                 if (argv[i][0] == '-')
2868                 {
2869                         if (!argv[i][1])
2870                         {
2871                                 printf("Illegal option argument: %s\n\n", argv[i]);
2872                                 ShowHelp();
2873                                 return 1;
2874                         }
2875
2876                         c = argv[i++][1];                       // Get next character in command line
2877
2878                         // Process command line switch
2879                         switch (c)
2880                         {
2881                         case '?':                                       // Display usage information
2882                         case 'h':
2883                         case 'H':
2884                                 ShowVersion();
2885                                 ShowHelp();
2886                                 return 1;
2887                         case 'a':
2888                         case 'A':                                       // Set absolute linking on
2889                                 if (aflag)
2890                                         warn('a', 1);
2891
2892                                 if (i + 2 >= argc)
2893                                 {
2894                                         printf("Not enough arguments to -a\n");
2895                                         return 1;
2896                                 }
2897
2898                                 aflag = 1;                              // Set abs link flag
2899
2900                                 // Segment order is TEXT, DATA, BSS
2901                                 // Text segment can be 'r' or a value
2902                                 if ((*argv[i] == 'r' || *argv[i] == 'R') && !argv[i][1])
2903                                 {
2904                                         ttype = -1;                     // TEXT segment is relocatable
2905                                 }
2906                                 else if ((*argv[i] == 'x' || *argv[i] == 'X'))
2907                                 {
2908                                         printf("Error in text-segment address: cannot be contiguous\n");
2909                                         return 1;
2910                                 }
2911                                 else if (GetHexValue(argv[i], &tval))
2912                                 {
2913                                         printf("Error in text-segment address: %s is not 'r' or an address.", argv[i]);
2914                                         return 1;
2915                                 }
2916
2917                                 i++;
2918
2919                                 // Data segment can be 'r', 'x' or a value
2920                                 if ((*argv[i] == 'r' || *argv[i] == 'R') && !argv[i][1])
2921                                 {
2922                                         dtype = -1;                     // DATA segment is relocatable
2923                                 }
2924                                 else if ((*argv[i] == 'x' || *argv[i] == 'X'))
2925                                 {
2926                                         dtype = -2;                     // DATA follows TEXT
2927                                 }
2928                                 else if (GetHexValue(argv[i], &dval))
2929                                 {
2930                                         printf("Error in data-segment address: %s is not 'r', 'x' or an address.", argv[i]);
2931                                         return 1;
2932                                 }
2933
2934                                 i++;
2935
2936                                 // BSS segment can be 'r', 'x' or a value
2937                                 if ((*argv[i] == 'r' || *argv[i] == 'R') && !argv[i][1])
2938                                 {
2939                                         btype = -1;                     // BSS segment is relocatable
2940                                 }
2941                                 else if ((*argv[i] == 'x' || *argv[i] == 'X'))
2942                                 {
2943                                         btype = -3;                     // BSS follows DATA
2944                                 }
2945                                 else if (GetHexValue(argv[i], &bval))
2946                                 {
2947                                         printf("Error in bss-segment address: %s is not 'r', 'x[td]', or an address.", argv[i]);
2948                                         return 1;
2949                                 }
2950
2951                                 i++;
2952                                 break;
2953                         case 'b':
2954                         case 'B':                                       // Don't remove muliply defined locals
2955                                 if (bflag)
2956                                         warn('b', 1);
2957
2958                                 bflag = 1;
2959                                 break;
2960                         case 'c':
2961                         case 'C':                                       // Process a command file
2962                                 if (i == argc)
2963                                 {
2964                                         printf("Not enough arguments to -c\n");
2965                                         return 1;
2966                                 }
2967
2968                                 if (docmdfile(argv[i++]))
2969                                 {
2970                                         return 1;
2971                                 }
2972
2973                                 break;
2974                         case 'd':
2975                         case 'D':                                       // Wait for "return" before exiting
2976                                 if (dflag)
2977                                         warn('d', 0);
2978
2979                                 dflag = 1;
2980                                 waitflag = 1;
2981                                 break;
2982                         case 'e':
2983                         case 'E':                                       // Output COFF (absolute only)
2984                                 cflag = 1;
2985                                 break;
2986                         case 'g':
2987                         case 'G':                                       // Output source level debugging
2988                                 printf("\'g\' flag not currently implemented\n");
2989                                 gflag = 0;
2990                                 /*
2991                                 if (gflag) warn('g', 1);
2992                                 gflag = 1;
2993                                 */
2994                                 break;
2995                         case 'i':
2996                         case 'I':                                       // Include binary file
2997                                 if (i + 2 > argc)
2998                                 {
2999                                         printf("Not enough arguments to -i\n");
3000                                         return 1;
3001                                 }
3002
3003                                 ifile = argv[i++];
3004                                 isym = argv[i++];
3005
3006                                 // handle -ii (No truncation)
3007                                 if ((argv[i-3][2] == 'i') || (argv[i-3][2] == 'I'))
3008                                 {
3009                                         if (!cflag)
3010                                                 printf("warning: (-ii) COFF format output not specified\n");
3011                                 }
3012                                 // handle -i (Truncation)
3013                                 else
3014                                 {
3015                                         if (strlen(isym) > 8)
3016                                                 isym[8] = '\0';
3017                                 }
3018
3019                                 // Place include files in the DATA segment only
3020                                 if (DoFile(ifile, DSTSEG_D, isym))
3021                                         return 1;
3022
3023                                 break;
3024                         case 'l':
3025                         case 'L':                                       // Add local symbols
3026                                 if (lflag)
3027                                         warn('l', 1);
3028
3029                                 lflag = 1;
3030                                 break;
3031                         case 'm':
3032                         case 'M':                                       // Produce load symbol map
3033                                 if (mflag)
3034                                         warn('m', 1);
3035
3036                                 mflag = 1;
3037                                 break;
3038                         case 'n':
3039                         case 'N':                                       // Output no header to .abs file
3040                                 if (noheaderflag)
3041                                         warn('n', 1);
3042
3043                                 noheaderflag = 1;
3044                                 break;
3045                         case 'o':
3046                         case 'O':                                       // Specify an output file
3047                                 if (oflag)
3048                                         warn('o', 1);
3049
3050                                 oflag = 1;
3051
3052                                 if (i >= argc)
3053                                 {
3054                                         printf("No output filename following -o switch\n");
3055                                         return 1;
3056                                 }
3057
3058                                 if (strlen(argv[i]) > FARGSIZE - 5)
3059                                 {
3060                                         printf("Output file name too long (sorry!)\n");
3061                                         return 1;
3062                                 }
3063
3064                                 strcpy(ofile, argv[i++]);
3065                                 break;
3066                         case 'r':
3067                         case 'R':                                       // Section alignment size
3068                                 if (rflag)
3069                                         warn('r', 1);
3070
3071                                 rflag = 1;
3072
3073                                 switch (argv[i-1][2])
3074                                 {
3075                                         case 'w': case 'W': secalign = 1;  break; // Word alignment
3076                                         case 'l': case 'L': secalign = 3;  break; // Long alignment
3077                                         case 'p': case 'P': secalign = 7;  break; // Phrase alignment
3078                                         case 'd': case 'D': secalign = 15; break; // Double phrase alignment
3079                                         case 'q': case 'Q': secalign = 31; break; // Quad phrase alignment
3080                                         default:            secalign = 7;  break; // Default phrase alignment
3081                                 }
3082
3083                                 break;
3084                         case 's':
3085                         case 'S':                                       // Output only global symbols
3086                                 if (sflag)
3087                                         warn('s', 1);
3088
3089                                 sflag = 1;
3090                                 break;
3091                         case 'u':
3092                         case 'U':                                       // Undefined symbols
3093                                 uflag++;
3094                                 break;
3095                         case 'v':
3096                         case 'V':                                       // Verbose information
3097                                 if (!vflag && !versflag)
3098                                 {
3099                                         ShowVersion();
3100                                 }
3101
3102                                 vflag++;
3103                                 break;
3104                         case 'w':
3105                         case 'W':                                       // Show warnings flag
3106                                 if (wflag)
3107                                         warn('w', 1);
3108
3109                                 wflag = 1;
3110                                 break;
3111                         case 'z':
3112                         case 'Z':                                       // Suppress banner flag
3113                                 if (zflag)
3114                                         warn('z', 1);
3115
3116                                 zflag = 1;
3117                                 break;
3118                         default:
3119                                 printf("unknown option argument `%c'\n", c);
3120                                 return 1;
3121                         }
3122                 }
3123                 else
3124                 {
3125                         // Not a switch, then process as a file
3126                         if (DoFile(argv[i++], 0, NULL))
3127                                 return 1;
3128                 }
3129         }
3130
3131         if (!oflag && vflag)
3132         {
3133                 strcpy(ofile, "output");
3134                 printf("Output file is %s[.ext]\n", ofile);
3135         }
3136
3137         if (oflag && vflag)
3138                 printf("Output file is %s\n", ofile);
3139
3140         if (sflag)
3141                 lflag = 0;
3142
3143         // No problems encountered
3144         return 0;
3145 }
3146
3147
3148 //
3149 // Display version information
3150 //
3151 void ShowVersion(void)
3152 {
3153         if (displaybanner)// && vflag)
3154         {
3155                 printf(
3156                 "      _\n"
3157                 " _ __| |_ ___\n"
3158                 "| '__| | '_  \\\n"
3159                 "| |  | | | | |\n"
3160                 "|_|  |_|_| |_|\n"
3161                 "\nReboot's Linker for Atari Jaguar\n"
3162                 "Copyright (c) 199x Allan K. Pratt, 2014-2015 Reboot\n"
3163                 "V%i.%i.%i %s (%s)\n\n", MAJOR, MINOR, PATCH, __DATE__, PLATFORM);
3164         }
3165 }
3166
3167
3168 //
3169 // Display command line help
3170 //
3171 void ShowHelp(void)
3172 {
3173         printf("Usage:\n");
3174         printf("    %s [-options] file(s)\n", cmdlnexec);
3175         printf("\n");
3176         printf("Options:\n");
3177         printf("   -? or -h                display usage information\n");
3178         printf("   -a <text> <data> <bss>  output absolute file\n");
3179         printf("                           hex value: segment address\n");
3180         printf("                           r: relocatable segment\n");
3181         printf("                           x: contiguous segment\n");
3182         printf("   -b                      don't remove multiply defined local labels\n");
3183         printf("   -c <fname>              add contents of <fname> to command line\n");
3184         printf("   -d                      wait for key after link\n");
3185         printf("   -e                      output COF absolute file\n");
3186         printf("   -g                      output source-level debugging\n");
3187         printf("   -i <fname> <label>      incbin <fname> and set <label>\n");
3188         printf("   -l                      add local symbols\n");
3189         printf("   -m                      produce load symbols map\n");
3190         printf("   -n                      output no file header to .abs file\n");
3191         printf("   -o <fname>              set output name\n");
3192         printf("   -r<size>                section alignment size\n");
3193         printf("                           w: word (2 bytes)\n");
3194         printf("                           l: long (4 bytes)\n");
3195         printf("                           p: phrase (8 bytes, default alignment)\n");
3196         printf("                           d: double phrase (16 bytes)\n");
3197         printf("                           q: quad phrase (32 bytes)\n");
3198         printf("   -s                      output only global symbols\n");
3199         printf("   -u                      allow unresolved symbols (experimental)\n");
3200         printf("   -v                      set verbose mode\n");
3201         printf("   -w                      show linker warnings\n");
3202         printf("   -z                      suppress banner\n");
3203         printf("\n");
3204 }
3205
3206
3207 //
3208 // Application exit
3209 //
3210 void ExitLinker(void)
3211 {
3212         char tempbuf[128];
3213         char * c;
3214
3215         // Display link status if verbose mode
3216         if (vflag)
3217                 printf("Link %s.\n", errflag ? "aborted" : "complete");
3218
3219         // Wait for return key if requested
3220         if (waitflag)
3221         {
3222                 printf("\nPress the [RETURN] key to continue. ");
3223                 c = fgets(tempbuf, 128, stdin);
3224         }
3225
3226         exit(errflag);
3227 }
3228
3229
3230 int main(int argc, char * argv[])
3231 {
3232         char * s = NULL;                                // String pointer for "getenv"
3233         struct HREC * utemp;                    // Temporary hash record pointer
3234         struct OHEADER * header;                // Pointer to output header structure
3235
3236         cmdlnexec = argv[0];                    // Obtain executable name
3237         s = getenv("RLNPATH");
3238
3239         if (s)                                                  // Attempt to obtain env variable
3240                 strcpy(libdir, s);                      // Store it if found
3241
3242         // Initialize some vars
3243         tval = dval = bval = 0;
3244         ttype = dtype = btype = 0;
3245
3246         // Parse the command line
3247         if (doargs(argc, argv))
3248         {
3249                 errflag = 1;
3250                 ExitLinker();
3251         }
3252
3253         if (!zflag && !vflag)
3254         {
3255                 ShowVersion();                  // Display version information
3256                 versflag = 1;                           // We've dumped the version banner
3257         }
3258
3259         // Load in specified files/objects and add to processing list
3260         if (ProcessFiles())
3261         {
3262                 errflag = 1;
3263                 ExitLinker();
3264         }
3265
3266         // Work in items in processing list & deal with unresolved list
3267         if (ProcessLists())
3268         {
3269                 errflag = 1;
3270                 ExitLinker();
3271         }
3272
3273         // Check that there is something to link
3274         if (olist == NULL)
3275         {
3276                 ShowHelp();
3277                 ExitLinker();
3278         }
3279
3280         // Report unresolved externals
3281         if (unresolved != NULL)
3282         {
3283                 printf("UNRESOLVED SYMBOLS\n");
3284
3285                 // Don't list them if two -u's or more
3286                 if (uflag < 2)
3287                 {
3288                         utemp = unresolved;
3289
3290                         while (utemp != NULL)
3291                         {
3292                                 printf("\t%s (", utemp->h_sym);
3293                                 WriteARName(utemp->h_ofile);
3294                                 printf(")\n");
3295                                 utemp = utemp->h_next;
3296                         }
3297                 }
3298
3299                 if (!uflag)
3300                 {
3301                         errflag = 1;
3302                         ExitLinker();
3303                 }
3304         }
3305
3306         // Make one output file from input objs
3307         header = MakeOutputFile();
3308
3309         if (header == NULL)
3310         {
3311                 errflag = 1;
3312                 ExitLinker();
3313         }
3314
3315         // Partial linking
3316         if (pflag)
3317         {
3318                 printf("TO DO:Partial linking\n");
3319                 errflag = 1;
3320         }
3321         // Relocatable linking
3322         else if (!aflag)
3323         {
3324                 printf("TO DO:Relocatable linking\n");
3325                 errflag = 1;
3326         }
3327         // Absolute linking
3328         else
3329         {
3330                 if (vflag)
3331                 {
3332                         printf("Absolute linking ");
3333
3334                         if (cflag)
3335                                 printf("(COF)\n"); 
3336                         else
3337                                 printf("(ABS)\n");
3338                 }
3339
3340                 if (vflag > 1)
3341                         printf("Header magic is 0x%04X\n", (unsigned int)header->magic);
3342
3343                 if (WriteOutputFile(header))
3344                         errflag = 1;
3345         }
3346
3347         // Display the loaded symbols map
3348         if (mflag)
3349                 if (ShowSymbolLoadMap(header))
3350                         errflag = 1;
3351
3352         // Display segment size summary
3353         if (vflag)
3354         {
3355                 printf("\n");
3356                 printf("+---------+----------+----------+----------+\n");
3357                 printf("| Segment |     TEXT |     DATA |      BSS |\n");
3358                 printf("| Sizes   |----------+----------+----------|\n");
3359                 printf("| (Hex)   | %8X | %8X | %8X |\n", (unsigned int)header->tsize, (unsigned int)header->dsize, (unsigned int)header->bsize);
3360                 printf("+---------+----------+----------+----------+\n\n");
3361         }
3362
3363         free(header);
3364         ExitLinker();
3365 }
3366