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