2 // RLN - Renamed Linker for the Atari Jaguar console system
3 // Copyright (C) 199x, Allan K. Pratt, 2014-2021 Reboot & Friends
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
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
67 // Function prototypes
68 struct HREC * LookupHREC(char *);
69 char * PathTail(char *);
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[]);
79 // Get a long word from memory
81 static inline uint32_t GetLong(uint8_t * src)
83 return (src[0] << 24) | (src[1] << 16) | (src[2] << 8) | src[3];
88 // Put a long word into memory
90 static inline void PutLong(uint8_t * dest, uint32_t val)
92 *dest++ = (uint8_t)(val >> 24);
93 *dest++ = (uint8_t)(val >> 16);
94 *dest++ = (uint8_t)(val >> 8);
100 // Get a word from memory
102 static inline uint16_t GetWord(uint8_t * src)
104 return (src[0] << 8) | src[1];
109 // Put a word into memory
111 static inline void PutWord(uint8_t * dest, uint16_t val)
113 *dest++ = (uint8_t)(val >> 8);
114 *dest = (uint8_t)val;
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
122 long FileSize(int fd)
124 long size = lseek(fd, 0, SEEK_END);
125 lseek(fd, 0, SEEK_SET);
132 // For this object file, add symbols to the output symbol table after
133 // relocating them. Returns TRUE if OSTLookup returns an error (-1).
135 int DoSymbols(struct OFILE * ofile)
143 uint32_t tsoSave, dsoSave, bsoSave;
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);
152 // Point to end of symbol record in the object file
153 char * symend = symptr + ofile->o_header.ssize;
155 uint32_t tsegoffset = ofile->segBase[TEXT];
156 uint32_t dsegoffset = ofile->segBase[DATA];
157 uint32_t bsegoffset = ofile->segBase[BSS];
159 // Save segment vars, so we can restore them if needed
160 tsoSave = tsegoffset, dsoSave = dsegoffset, bsoSave = bsegoffset;
162 // Process each record in the object's symbol table
163 for(; symptr!=symend; symptr+=12)
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 : "";
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)
176 // Obtain the string table index for the relocation symbol, look
177 // for it in the globals hash table to obtain information on that
179 hptr = LookupHREC(string);
183 // Try to find it in the OST
184 int ostIndex = OSTLookup(string);
188 printf("DoSymbols(): Symbol not found in hash table: '%s' (%s)\n", string, ofile->o_name);
193 printf("DoSymbols(): Skipping symbol '%s' (%s) found in OST...\n", string, ofile->o_name);
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...!]
201 tsegoffset = hptr->h_ofile->segBase[TEXT];
202 dsegoffset = hptr->h_ofile->segBase[DATA];
203 bsegoffset = hptr->h_ofile->segBase[BSS];
205 // Update type with global type
208 // Remove global flag if absolute
209 if (type == (T_GLBL | T_ABS))
212 // If the global/external has a value then update that value in
213 // accordance with the segment sizes of the object file it
217 switch (hptr->h_type & 0x0E000000)
221 value = hptr->h_value;
224 value = hptr->h_value - hptr->h_ofile->o_header.tsize;
227 value = hptr->h_value
228 - (hptr->h_ofile->o_header.tsize
229 + hptr->h_ofile->o_header.dsize);
233 printf("DoSymbols: No adjustment made for symbol: %s (%s) = %X\n", string, ofile->o_name, hptr->h_value);
237 // If *not* a global/external, use the info from passed in object
239 tsegoffset = tsoSave, dsegoffset = dsoSave, bsegoffset = bsoSave;
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)
247 switch (type & 0xFF000000)
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;
254 case 0x46000000: // Data line number (Not used by GCC/rmac)
255 value = dbase + dsegoffset + value;
257 case 0x48000000: // BSS line number (Not used by GCC/rmac)
258 value = bbase + bsegoffset + value;
260 // All other debug symbols don't need to be relocated
261 // XXX Not true, but matches ALN behavior.
265 PutLong(symptr + 8, value);
270 // Now make modifications to the symbol value, local or global,
271 // based on the segment sizes of the object file currently being
273 switch (type & T_SEG)
278 value = tbase + tsegoffset + value;
279 PutLong(symptr + 8, value);
283 value = dbase + dsegoffset + value;
285 value = dbase + dsegoffset + (value
286 - ofile->o_header.tsize);
288 PutLong(symptr + 8, value);
292 value = bbase + bsegoffset + value;
294 value = bbase + bsegoffset
295 + (value - (ofile->o_header.tsize
296 + ofile->o_header.dsize));
298 PutLong(symptr + 8, value);
305 // Add to output symbol table if global/extern, or local flag is set
306 if (isglobal(type) || lflag)
309 printf("DoSymbols: Adding symbol: %s (%s) to OST...\n", string, ofile->o_name);
311 index = OSTAdd(index ? string : NULL, type, value);
315 printf("DoSymbols(): Failed to add symbol '%s' to OST!\n", string);
326 // Free up hash memory
328 void FreeHashes(void)
332 for(i=0; i<NBUCKETS; i++)
334 struct HREC * hptr = htable[i];
338 struct HREC * htemp = hptr->h_next;
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.]
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.
361 for(i=0; i<NBUCKETS; i++)
363 for(hptr=htable[i]; hptr!=NULL; hptr=hptr->h_next)
365 //NO! if (iscommon(hptr->h_type))
366 if (isglobal(hptr->h_type))// || isextern(hptr->h_type))
368 // Skip if in *.a file... (does nothing)
369 //if (hptr->h_ofile->isArchiveFile)
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
376 if (OSTAdd(hptr->h_sym, hptr->h_type, hptr->h_value) == -1)
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.
390 int OSTAdd(char * name, int type, long value)
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
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)
404 if (!name || !name[0])
407 slen = strlen(name) + 1;
409 // Get symbol index in OST, if any (-1 if not found)
410 ostresult = slen ? OSTLookup(name) : -1;
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)))
423 // If the OST has not been initialised, or more space is needed, then
425 if ((ost_index + 1) > ost_size)
428 ost_size = OST_SIZE_INIT;
432 ost = realloc(ost, ost_size * sizeof(ost[0]));
436 printf("OST memory allocation error.\n");
443 ost_offset_p = (oststr_ptr - oststr);
444 ost_offset_e = (oststr_end - oststr);
446 // If the OST data has been exhausted, allocate another chunk.
447 if (((oststr_ptr + slen + 4) > oststr_end))
449 // string length + terminating NULL + uint32_t (terminal long)
450 if ((oststr_ptr + (slen + 1 + 4)) > oststr_end)
452 oststr = realloc(oststr, ost_offset_e + OST_BLOCK);
456 printf("OSTSTR memory reallocation error.\n");
460 oststr_ptr = oststr + ost_offset_p;
461 oststr_end = (oststr + ost_offset_e) + OST_BLOCK;
463 // On the first alloc, reserve space for the string table
465 if (ost_offset_e == 0)
470 strcpy(oststr_ptr, name); // Put symbol name in string table
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
477 ostresult = ost_index++;
479 ost[ostresult].s_idx = ost_offset_p;
480 ost[ostresult].s_type = type;
481 ost[ostresult].s_value = value;
484 printf("OSTAdd: (%s), type=$%08X, val=$%08lX\n",
485 slen ? name : "", type, value);
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)
495 int OSTLookup(char * sym)
499 for(i=0; i<ost_index; i++)
501 if (ost[i].s_idx && (strcmp(oststr + ost[i].s_idx, sym) == 0))
510 // Add unresolved externs to the output symbol table
511 // N.B.: Only adds unresolved symbols *if* they're not already in the OST
513 int DoUnresolved(void)
515 struct HREC * hptr = unresolved;
517 // Add to OST while unresolved list is valid
520 if (OSTAdd(hptr->h_sym, T_GLBL, 0L) == -1)
524 printf("DoUnresolved(): '%s' (%s:$%08X) in OST\n", hptr->h_sym, hptr->h_ofile->o_name, hptr->h_type);
526 struct HREC * htemp = hptr->h_next;
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.
541 int RelocateSegment(struct OFILE * ofile, int flag)
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
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
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)
570 // SCPCD : we should not increment the textoffset before the end of processing the object file, else data section will point to wrong textoffset
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
577 if ((flag == T_DATA) && !ofile->o_header.absrel.reloc.dsize)
579 // SCPCD : the T_DATA is the last section of the file, we can now increment the textoffset, dataoffset and bssoffset
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));
586 printf("RelocateSegment(%s, TEXT) : No relocation data\n", ofile->o_name);
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));
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));
597 printf("RelocateSegment(%s, DATA) : No relocation data\n", ofile->o_name);
603 printf("RelocateSegment(%s, %s) : Processing Relocation Data\n",
604 ofile->o_name, flag == T_DATA ? "DATA" : "TEXT");
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);
612 // Obtain pointer to start of symbols
613 symbols = symtab + ofile->o_header.ssize;
615 // Obtain pointer to start of TEXT segment
616 sptr = ofile->o_image + 32;
618 // Obtain pointer to start of TEXT relocation records
619 rptr = sptr + (ofile->o_header.tsize + ofile->o_header.dsize);
621 relocsize = ofile->o_header.absrel.reloc.tsize;
624 printf("RELOCSIZE :: %d Records = %d\n", relocsize, relocsize / 8);
626 // Update pointers if DATA relocation records are being processed
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;
634 // Process each relocation record for the TEXT segment
635 for(i=0; i<(int)relocsize; i+=8)
637 // Obtain both the relocation address and the relocation flags from the
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);
647 // Additional processing required for global relocations
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;
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
665 olddata = (wordreloc ? GetWord(sptr + addr) : GetLong(sptr + addr));
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).
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
679 if (rflg & BSDREL_MOVEI)
680 olddata = _SWAPWORD(olddata);
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);
694 case BSDREL_SEG_TEXT:
695 // SCPCD : the symbol point to a text segment, we should use the textoffset
696 newdata = tbase + textoffset + olddata;
699 case BSDREL_SEG_DATA:
700 newdata = dbase + dataoffset
701 + (olddata - ofile->o_header.tsize);
705 newdata = bbase + bssoffset
706 + (olddata - (ofile->o_header.tsize
707 + ofile->o_header.dsize));
718 // Set absolute (long) or relative (word) address of symbol
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);
728 PutWord(sptr + addr, newdata);
732 printf("OP reloc: oldata=$%X, newdata=$%X\n", olddata, newdata);
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);
741 PutLong(sptr + addr, newdata);
745 PutWord(sptr + addr, newdata - tbase - addr - ofile->o_tbase);
748 // Shamus: Let's output some info to aid in debugging this crap
755 sprintf(ssiString, " [ssi:%i]", ssidx);
757 printf("RelocateSegment($%08X): %s, $%08X: $%08X => $%08X%s\n", rflg, (glblreloc ? sym : "(LOCAL)"), addr, olddata, GetLong(sptr + addr), ssiString);
760 rptr += 8; // Point to the next relocation record
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
767 // TEXT segment plus padding
768 pad = ((ofile->o_header.tsize + secalign) & ~secalign);
769 textoffset += (ofile->o_header.tsize + (pad - ofile->o_header.tsize));
771 // DATA segment plus padding
772 pad = ((ofile->o_header.dsize + secalign) & ~secalign);
773 dataoffset += (ofile->o_header.dsize + (pad - ofile->o_header.dsize));
775 // BSS segment plus padding
776 pad = ((ofile->o_header.bsize + secalign) & ~secalign);
777 bssoffset += (ofile->o_header.bsize + (pad - ofile->o_header.bsize));
780 // Return value, should always be zero
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
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!
794 void AppendPathDelimiter(char * s)
797 // And hope to God that there's enough space in the buffer...
802 if (*s == '/' || *s == '\\')
816 int length = strlen(s);
818 if (s[length - 1] != PATH_DELIMITER)
820 s[length] = PATH_DELIMITER;
821 s[length + 1] = 0; // BUFFER OVERFLOW!!!! FFFFFFFFUUUUUUUUUUUU
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.
833 int TryOpenFile(char ** p_name)
835 char * name = *p_name;
837 // Note that libdir will be an empty string if there is none specified
838 char * tmpbuf = malloc(strlen(name) + strlen(libdir) + 3);
842 printf("TryOpenFile() : out of memory\n");
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);
856 // Try to open file with '.o' added
857 strcat(tmpbuf, ".o");
858 fd = open(tmpbuf, _OPEN_FLAGS);
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))
869 strcpy(tmpbuf, libdir);
870 // Add a trailing path char if there isn't one already
871 AppendPathDelimiter(tmpbuf);
872 strcat(tmpbuf, name);
874 if ((fd = open(tmpbuf, _OPEN_FLAGS)) >= 0)
879 strcat(tmpbuf, ".o");
881 if ((fd = open(tmpbuf, _OPEN_FLAGS)) >= 0)
886 // Couldn't open file at all
889 // There are worse things... :-P
891 tmpbuf = realloc(tmpbuf, strlen(tmpbuf) + 1);
895 printf("TryOpenFile() : out of memory\n");
905 // What it says on the tin
907 void WriteARName(struct OFILE * p)
909 int flag = *(p->o_arname);
910 printf("%s%s%s", (flag ? (char *)(p->o_arname) : ""), (flag ? ":" : ""), p->o_name);
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).
919 int DoFile(char * fname, int incFlag, char * sym)
921 // Verbose information
924 printf("DoFile() : `%s' %s", fname, incFlag ? "INCLUDE" : "NORMAL");
927 printf(" symbol %s", sym);
932 // Reached maximum file handles
939 // Attempt to open input file
940 int fd = TryOpenFile(&fname);
944 printf("Cannot find input module %s\n", fname);
948 // The file is open; save its info in the handle and name arrays
950 name[hd] = fname; // This is the name from TryOpenFile()
956 int temp = strlen(sym); // Get symbol length
958 // 100 chars is max length of a symbol
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)
970 printf("DoFile() : out of memory for include-file symbols\n");
974 strcpy(hsym1[hd], sym);
975 strcpy(hsym2[hd], sym);
981 printf("Last char of %s is already 'x': choose another name\n", sym);
989 hsym2[hd][temp] = 'x';
990 hsym2[hd][temp+1] = '\0';
994 // Increment next handle index
1002 // Pad TEXT or DATA segment to the requested boundary
1004 int PadSegment(FILE * fd, long segsize, int value)
1010 // Determine the number of padding bytes that are needed
1011 long padsize = (segsize + secalign) & ~secalign;
1012 padsize = padsize - segsize;
1014 // Fill pad array if padding is required
1021 PutWord(padptr, value);
1025 symoffset += padsize;
1027 // Write padding bytes
1028 if (fwrite(padarray, padsize, 1, fd) != 1)
1037 // Write the output file
1039 int WriteOutputFile(struct OHEADER * header)
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
1049 symoffset = 0; // Initialise symbol offset
1051 // Add correct output extension if none
1052 if (strchr(ofile, '.') == NULL)
1055 strcat(ofile, ".cof"); // COF files (a type of abs)
1056 else if (aflag && !cflag)
1057 strcat(ofile, ".abs"); // ABS files
1059 strcat(ofile, ".o"); // Object files (partial linking, etc)
1062 FILE * fd = fopen(ofile, "wb"); // Attempt to open output file
1066 printf("Can't open output file %s\n", ofile);
1070 // Build the output file header
1071 // Absolute (COF) header
1074 tsoff = dsoff = bsoff = 0xA8; // Initialises segment offsets
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)
1080 dsoff += otemp->segSize[TEXT];
1081 bsoff += otemp->segSize[TEXT] + otemp->segSize[DATA];
1084 // Currently this only builds a COF absolute file. Conditionals and
1085 // additional code will need to be added for ABS and partial linking.
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)
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
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
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
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
1141 symoffset = 168; // Update symbol offset
1143 // Absolute (ABS) header
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
1158 symoffset = 36; // Update symbol offset
1161 // Write the header, but not if noheaderflag
1164 // Absolute (ABS) header
1167 if (fwrite(himage, 36, 1, fd) != 1)
1170 // Absolute (COF) header
1173 if (fwrite(himage, 168, 1, fd) != 1)
1178 // Write the TEXT segment of each object file
1179 for(otemp=olist; otemp!=NULL; otemp=otemp->o_next)
1181 osize = otemp->o_header.tsize;
1183 // Write only if segment has size
1187 printf("Writing TEXT Segment of %s\n", otemp->o_name);
1189 if (fwrite(otemp->o_image + 32, osize, 1, fd) != 1)
1192 // Pad to required alignment boundary
1193 if (PadSegment(fd, osize, 0x0000))
1200 // Write the DATA segment of each object file
1201 for(otemp=olist; otemp!=NULL; otemp=otemp->o_next)
1203 osize = otemp->o_header.dsize;
1205 // Write only if the segment has size
1209 printf("Writing DATA Segment of %s\n", otemp->o_name);
1211 if (fwrite((otemp->o_image + 32 + otemp->o_header.tsize), osize, 1, fd) != 1)
1214 // Pad to required alignment boundary
1215 if (PadSegment(fd, osize, 0))
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...)
1229 // Write the symbols table and string table
1230 // Absolute (COF) symbol/string table
1235 for (i = 0; i < ost_index; i++)
1237 PutLong(symbol, ost[i].s_idx);
1238 PutLong(symbol + 4, ost[i].s_type);
1239 PutLong(symbol + 8, ost[i].s_value);
1241 if (fwrite(symbol, 12, 1, fd) != 1)
1245 if (fwrite(oststr, (oststr_ptr - oststr), 1, fd) != 1)
1249 // Absolute (ABS) symbol/string table
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
1259 for(i=0; i<ost_index; i++)
1261 memset(symbol, 0, 14); // Initialise symbol record
1262 abstype = 0; // Initialise ABS symbol type
1264 // Skip debug symbols
1265 if (ost[i].s_type & 0xF0000000)
1268 // Get symbol string (maximum 8 chars)
1269 strncpy(symbol, oststr + ost[i].s_idx, 8);
1271 // Modify to ABS symbol type
1272 switch (ost[i].s_type)
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;
1282 printf("warning (WriteOutputFile): ABS, cannot determine symbol type ($%08X) [%s]\n", ost[i].s_type, symbol);
1287 PutWord(symbol + 8, abstype); // Write back new ABS type
1288 PutLong(symbol + 10, ost[i].s_value); // Write back value
1290 // Write symbol record
1291 if (fwrite(symbol, 14, 1, fd) != 1)
1299 printf("Close error on output file %s\n", ofile);
1306 printf("Write error on output file %s\n", ofile);
1307 fclose(fd); // Try to close output file anyway
1313 // Display the symbol load map
1315 int ShowSymbolLoadMap(struct OHEADER * header)
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
1325 return 0; // Return if no symbols to map
1327 printf("LOAD MAP\n\n");
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
1336 // Display the correct map header for the symbols being processed
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;
1345 c = 0; // Initialise column number
1347 // Inner loop to process each record in the symbol table
1348 for(i=0; i<(unsigned)ost_index; i++)
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
1355 // Display only three columns
1362 // If local symbols not included and the type is local then go to
1363 // next symbol record
1364 if (!lflag & !(type & 0x01000000))
1367 // Output each symbol to the display, dependant on type
1371 // Non-relocatable symbols
1372 if (type == 0x02000000 || type == 0x03000000)
1374 printf("%-8s %c %08X ", symbol, (type & 0x01000000) ? 'G' : 'L', value);
1380 // TEXT segment relocatable symbols
1381 if (type == 0x04000000 || type == 0x05000000)
1383 printf("%-8s %c %08X ", symbol, (type & 0x01000000) ? 'G' : 'L', value);
1389 // DATA segment relocatble symbols
1390 if (type == 0x06000000 || type == 0x07000000)
1392 printf("%-8s %c %08X ", symbol, (type & 0x01000000) ? 'G' : 'L', value);
1398 // BSS segment relocatable symbols
1399 if (type == 0x08000000 || type == 0x09000000)
1401 printf("%-8s %c %08X ", symbol, (type & 0x01000000) ? 'G' : 'L', value);
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.
1420 int GetHexValue(char * string, int * value)
1424 while (isxdigit(*string))
1426 if (isdigit(*string))
1428 *value = (*value << 4) + (*string++ - '0');
1432 if (isupper(*string))
1433 *string = tolower(*string);
1435 *value = (*value << 4) + ((*string++ - 'a') + 10);
1439 if (*string != '\0')
1441 printf("Invalid hexadecimal value");
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'.
1455 struct OHEADER * MakeOutputObject()
1457 unsigned tptr, dptr, bptr; // Bases in runtime model
1458 int ret = 0; // Return value
1459 struct OHEADER * header; // Output header pointer
1461 // Initialize cumulative segment sizes
1462 textsize = datasize = bsssize = 0;
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
1469 while (otemp != NULL)
1471 // If the object is unused, discard it...
1472 if ((otemp->o_flags & O_USED) == 0)
1476 printf("Unused object file ");
1478 printf(" discarded.\n");
1481 // Drop the entry from the linked list
1483 olist = otemp->o_next;
1485 oprev->o_next = otemp->o_next;
1487 // Free the object entry if it's not an archive file
1488 if (!otemp->isArchiveFile)
1489 free(otemp->o_image);
1493 // Save accumulated addresses in the object
1494 otemp->segBase[TEXT] = textsize;
1495 otemp->segBase[DATA] = datasize;
1496 otemp->segBase[BSS] = bsssize;
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;
1505 // Go to next object file list pointer
1506 otemp = otemp->o_next;
1509 // Update base addresses and inject the symbols _TEXT_E, _DATA_E and _BSS_E
1515 // DATA follows TEXT
1516 dbase = tval + textsize;
1520 bbase = tval + textsize + datasize;
1522 // BSS is independent of DATA
1527 // DATA is independent of TEXT
1534 bbase = tval + textsize;
1537 bbase = dval + datasize;
1541 // BSS is independent of DATA
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);
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]
1557 // Initialise base addresses
1558 tptr = dptr = bptr = 0;
1560 // For each file, relocate its symbols and add them to the output symbol
1565 while (otemp != NULL)
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;
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))
1582 otemp = otemp->o_next;
1585 // Places all the externs, globals etc into the output symbol table
1586 if (DoCommon() == -1)
1589 // Create a new output file header
1590 header = new_oheader();
1594 printf("MakeOutputObject: out of memory!\n");
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
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)
1612 ret |= RelocateSegment(otemp, T_TEXT); // TEXT segment relocations
1613 ret |= RelocateSegment(otemp, T_DATA); // DATA segment relocations
1616 // Done with global symbol hash tables
1619 return (ret ? (struct OHEADER *)NULL : header);
1624 // Add symbol to hash list
1626 int AddSymbolToHashList(struct HREC ** hptr, char * sym, struct OFILE * ofile,
1627 long value, int type)
1629 struct HREC * htemp = new_hrec();
1633 printf("Out of memory\n");
1637 // Shamus: Moar testing...
1640 printf("AddSymbolToHashList(): hptr=$%08X, sym=\"%s\", ofile=$%08X, value=$%X, type=$%X\n", hptr, sym, ofile, value, type);
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;
1650 // Add new hash to the front of the list (hence the ** for hptr)
1651 htemp->h_next = *hptr;
1659 // Add symbol to the unresolved symbols hash table (really, it's a linked list)
1661 int AddUnresolvedSymbol(char * sym, struct OFILE * ofile)
1664 printf("AddUnresolvedSymbol(%s, %s)\n", sym, ofile->o_name);
1666 return AddSymbolToHashList(&unresolved, sym, ofile, 0L, 0);
1671 // Remove the HREC from the unresolved symbol list, and pass back a pointer
1672 // to the spot where the HREC was.
1674 struct HREC * RemoveUnresolvedSymbol(struct HREC * hrec)
1676 struct HREC * ptr = unresolved;
1677 struct HREC * previous = NULL;
1679 while ((ptr != hrec) && (ptr != NULL))
1689 struct HREC * next = ptr->h_next;
1691 // Remove the head if nothing previous, otherwise, remove what we found
1692 if (previous == NULL)
1695 previous->h_next = next;
1703 // Add symbol to the unresolved symbols hash table
1705 int AddARSymbol(char * sym, struct OFILE * ofile)
1708 printf("AddARSymbol(%s, %s)\n", sym, ofile->o_name);
1710 return AddSymbolToHashList(&arSymbol, sym, ofile, 0L, 0);
1715 // Generate hash value from the 1st 15 characters of the symbol modulo the
1716 // number of buckets in the hash.
1718 int GetHash(char * s)
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.
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;
1732 // Lookup a symbol in the hash table.
1733 // Returns either a pointer to the HREC or NULL if not found.
1735 struct HREC * LookupHREC(char * symbol)
1737 struct HREC * hptr = htable[GetHash(symbol)];
1739 while (hptr != NULL)
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)
1747 hptr = hptr->h_next;
1755 // Lookup a symbol in the AR symbol table.
1756 // Returns either a pointer to the HREC or NULL if not found.
1758 struct HREC * LookupARHREC(char * symbol)
1760 struct HREC * hptr = arSymbol;
1762 while (hptr != NULL)
1764 // Return hash record pointer if found
1765 if (strcmp(symbol, hptr->h_sym) == 0)
1768 hptr = hptr->h_next;
1776 // Add the imported symbols from this file to unresolved, and the global and
1777 // common (???) symbols to the exported hash table.
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.]
1786 int AddSymbols(struct OFILE * Ofile)
1788 struct HREC * hptr; // Hash record pointer
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);
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
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
1819 if ((Ofile->isArchiveFile) && !(Ofile->o_flags & O_USED))
1821 if ((type & T_GLBL) && (type & (T_SEG | T_ABS)))
1822 if (AddARSymbol(sstr + index, Ofile))
1825 else if (type == T_GLBL)
1827 // Global symbol that may or may not be in the current unit
1828 hptr = LookupHREC(sstr + index);
1831 hptr->h_ofile->o_flags |= O_USED; // Mark .o file as used
1832 // Otherwise, *maybe* add to unresolved list
1835 // Check to see if this is a common symbol; if so, add it to
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;
1846 value = bssLocation;
1847 PutLong(sfix + 4, type);
1848 PutLong(sfix + 8, value);
1851 printf("AddSymbols: Resetting common label to BSS label\n");
1853 if (AddSymbolToHashList(&htable[GetHash(sstr + index)],
1854 sstr + index, Ofile, value, type))
1855 return 1; // Error if addition failed
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))
1862 if (AddUnresolvedSymbol(sstr + index, Ofile))
1863 return 1; // Error if addition failed
1867 else if ((type & T_GLBL) && (type & (T_SEG | T_ABS)))
1869 hptr = LookupHREC(sstr + index);
1871 // Symbol isn't in the table, so try to add it:
1874 if (AddSymbolToHashList(&htable[GetHash(sstr + index)],
1875 sstr + index, Ofile, value, type))
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))
1885 // Mismatch: common came first; warn and keep the global
1888 printf("Warning: %s: global from ", sstr + index);
1890 printf(" used, common from ");
1891 WriteARName(hptr->h_ofile);
1892 printf(" discarded.\n");
1895 hptr->h_ofile = Ofile;
1896 hptr->h_type = type;
1897 hptr->h_value = value;
1901 // Global exported by another ofile; warn and make this one
1905 printf("Duplicate symbol %s: ", sstr + index);
1906 WriteARName(hptr->h_ofile);
1909 printf(" discarded\n");
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
1915 PutLong(sfix + 4, T_GLBL);
1920 sfix += 12; // Increment symbol fixup pointer
1921 nsymbols--; // Decrement num of symbols to process
1924 // Success loading symbols
1930 // Process object file for symbols
1932 int DoItem(struct OFILE * obj)
1934 // Allocate memory for object record ptr
1935 struct OFILE * Ofile = new_ofile();
1939 printf("Out of memory while processing %s\n", obj->o_name);
1943 // Starting after all pathnames, etc., copy .o file name to Ofile
1944 char * temp = PathTail(obj->o_name);
1946 // Check filename length
1947 if (strlen(temp) > FNLEN - 1)
1949 printf("File name too long: %s\n", temp);
1953 // Check archive name length
1954 if (strlen(obj->o_arname) > (FNLEN - 1))
1956 printf("Archive name too long: %s\n", obj->o_arname);
1960 strcpy(Ofile->o_name, temp); // Store filename
1961 strcpy(Ofile->o_arname, obj->o_arname); // Store archive name
1963 // Initialise object record information
1964 Ofile->o_next = NULL;
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;
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);
1984 // Round BSS off to alignment boundary (??? isn't this already done ???)
1985 Ofile->o_header.bsize = (Ofile->o_header.bsize + secalign) & ~secalign;
1987 if ((Ofile->o_header.dsize & 7) && wflag)
1989 printf("Warning: data segment size of ");
1991 printf(" is not a phrase multiple\n");
1994 // Check for odd segment sizes
1995 if ((Ofile->o_header.tsize & 1) || (Ofile->o_header.dsize & 1)
1996 || (Ofile->o_header.bsize & 1))
1998 printf("Error: odd-sized segment in ");
2000 printf("; link aborted.\n");
2004 if (AddSymbols(Ofile))
2007 // Add this file to the olist
2011 olast->o_next = Ofile;
2019 // Handle items in processing list.
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.
2025 int ProcessLists(void)
2027 // Process object file list first (adds symbols from each unit & creates
2029 while (plist != NULL)
2034 struct OFILE * ptemp = plist;
2035 plist = plist->o_next;
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; )
2047 printf("LookupHREC(%s) => ", uptr->h_sym);
2049 struct HREC * htemp = LookupHREC(uptr->h_sym);
2053 // Found it in the symbol table!
2055 printf("%s in %s (=$%06X)\n", (isglobal(htemp->h_type) ? "global" : "common"), htemp->h_ofile->o_name, htemp->h_value);
2057 // Mark the .o unit that the symbol is in as seen & remove from the
2059 htemp->h_ofile->o_flags |= O_USED;
2060 uptr = RemoveUnresolvedSymbol(uptr);
2067 // Check to see if the unresolved symbol is on the AR symbol list.
2068 htemp = LookupARHREC(uptr->h_sym);
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);
2076 uptr = uptr->h_next;
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
2086 htemp->h_ofile->o_flags |= O_USED;
2087 AddSymbols(htemp->h_ofile);
2093 // Show files used if the user requests it.
2096 printf("Files used:\n");
2097 struct OFILE * filePtr = olist;
2099 while (filePtr != NULL)
2101 if (filePtr->o_flags & O_USED)
2103 printf(" %s%s%s\n", filePtr->o_name, (filePtr->isArchiveFile ? ":" : ""), (filePtr->isArchiveFile ? filePtr->o_arname : nullStr));
2106 filePtr = filePtr->o_next;
2115 // Extract filename from path
2117 char * PathTail(char * name)
2119 // Find last occurance of PATH_DELIMETER
2120 char * temp = strrchr(name, PATH_DELIMITER);
2122 // Return what was passed in if path delimiter was not found
2131 // Add input file to processing list
2133 int AddToProcessingList(char * ptr, char * fname, char * arname, uint8_t arFile, uint32_t tSize, uint32_t dSize, uint32_t bSize)
2137 // First time object record allocation
2138 plist = new_ofile();
2143 // Next object record allocation
2144 plast->o_next = new_ofile();
2145 plast = plast->o_next;
2150 printf("Out of memory.\n"); // Error if memory allocation fails
2154 // Discard paths from filenames...
2155 fname = PathTail(fname);
2156 arname = PathTail(arname);
2158 // Check for filename length errors...
2159 if (strlen(fname) > (FNLEN - 1))
2161 printf("File name too long: %s (sorry!)\n", fname);
2165 if (strlen(arname) > (FNLEN - 1))
2167 printf("AR file name too long: %s (sorry!)\n", arname);
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;
2181 return 0; // Return without errors
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.
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)
2198 int LoadInclude(char * fname, int handle, char * sym1, char * sym2, int segment)
2202 unsigned symtype = 0;
2203 uint32_t tSize = 0, dSize = 0, bSize = 0;
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;
2211 // Use calloc so the header & fixups initialize to zero
2212 // Allocate object image memory
2213 if ((ptr = calloc(size, 1)) == NULL)
2215 printf("Out of memory while including %s\n", fname);
2220 // Read in binary data
2221 if (read(handle, ptr + 32, fsize) != fsize)
2223 printf("File read error on %s\n", fname);
2231 // Build this image's dummy header
2232 PutLong(ptr, 0x00000107); // Magic number
2236 PutLong(ptr+4, dsize); // Text size
2237 PutLong(ptr+8, 0L); // Data size
2238 symtype = 0x05000000;
2243 PutLong(ptr+4, 0L); // Text size
2244 PutLong(ptr+8, dsize); // Data size
2245 symtype = 0x07000000;
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
2255 sptr = ptr + 32 + dsize; // Set sptr to symbol table location
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)
2264 sptr = ptr + 32 + dsize + 24; // Set sptr to symbol table size loc
2266 PutLong(sptr, sym1len + 4L); // Size of symbol table
2268 sptr = ptr + 32 + dsize + 24 + 4; // Set sptr to symbol table location
2270 for(i=0; i<(sym1len-1); i++) // Write symbol1 to string table
2273 sptr += (sym1len - 1); // Step past symbol string
2274 *sptr = '\0'; // Terminate symbol string
2275 sptr += 1; // Step past termination
2277 for(i=0; i<(sym2len-1); i++) // Write symbol2 to string table
2280 sptr += (sym2len - 1); // Step past symbol string
2281 *sptr = '\0'; // Terminate symbol string
2282 sptr += 1; // Step past termination
2284 PutLong(sptr, 0L); // Terminating long for object file
2286 return AddToProcessingList(ptr, fname, nullStr, 0, tSize, dSize, bSize);
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).
2297 int LoadObject(char * fname, int fd, char * ptr)
2299 uint32_t tSize = 0, dSize = 0, bSize = 0;
2303 long size = FileSize(fd);
2305 // Allocate memory for file data
2310 printf("Out of memory while processing %s\n", fname);
2315 // Read in file data
2316 if (read(fd, ptr, size) != size)
2318 printf("File read error on %s\n", fname);
2324 tSize = (GetLong(ptr + 4) + secalign) & ~secalign;
2325 dSize = (GetLong(ptr + 8) + secalign) & ~secalign;
2326 bSize = (GetLong(ptr + 12) + secalign) & ~secalign;
2330 // Now add this image to the list of pending ofiles (plist)
2331 return AddToProcessingList(ptr, fname, nullStr, 0, tSize, dSize, bSize);
2334 uint32_t SymTypeAlcToAout(uint32_t alcType)
2336 uint32_t type = T_UNDF;
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.
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)
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))
2360 else if (alcType & ALCSYM_TEXT)
2362 else if (alcType & ALCSYM_DATA)
2364 else if (alcType & ALCSYM_BSS)
2367 if ((alcType & ALCSYM_GLOBAL) ||
2368 (alcType & ALCSYM_EXTERN))
2374 int LoadAlcyon(char * fname, int fd)
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);
2383 uint32_t numSyms, numTRel, numDRel, i, j;
2385 // Validate the file is at least large enough to contain a valid header
2388 printf("Alcyon object file %s too small to contain header\n", fname);
2392 // Allocate memory for file data
2397 printf("Out of memory while processing %s\n", fname);
2402 // Read in file data
2403 if (read(fd, ptr, size) != size)
2405 printf("File read error on %s\n", fname);
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);
2419 // Construct a BSD-style/aout object file in memory from the Alcyon data
2420 numSyms = hdr.ssize / 14;
2422 alcSyms = calloc(numSyms, sizeof(*alcSyms));
2423 if (alcSyms == NULL)
2425 printf("Out of memory while processing %s\n", fname);
2430 sptr = ptr + 0x1c + hdr.tsize + hdr.dsize;
2431 trelptr = sptr + hdr.ssize;
2432 drelptr = trelptr + hdr.tsize;
2433 relend = drelptr + hdr.dsize;
2435 if (relend - ptr > size)
2437 printf("Alcyon object file %s truncated: Header wants %ldB, file is %ldB\n",
2438 fname, relend - ptr, size);
2442 for (i = 0, symStrLen = 0; i < numSyms; i++)
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;
2451 for (i = 0, numTRel = 0; trelptr + i < drelptr; i += 2)
2453 uint16_t rel = GetWord(trelptr + i);
2454 if ((rel != ALCREL_ABS) &&
2455 (rel != ALCREL_LONG))
2459 for (i = 0, numDRel = 0; drelptr + i < relend; i += 2)
2461 uint16_t rel = GetWord(drelptr + i);
2462 if ((rel != ALCREL_ABS) &&
2463 (rel != ALCREL_LONG))
2467 aout = malloc(32 + /* header */
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 */);
2477 printf("Out of memory while processing %s\n", fname);
2483 // Construct the BSD/a.out header.
2484 PutLong(aout, 0x00000107); // Magic number
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
2492 PutLong(aout+24, numTRel * 8); // TEXT relocation size
2493 PutLong(aout+28, numDRel * 8); // DATA relocation size
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);
2499 // Set traout to the start of the relocation tables
2500 traout = aout + 32 + hdr.tsize + hdr.dsize;
2502 // Set saout to symbol table location
2503 saout = traout + numTRel * 8 + numDRel * 8 ;
2505 // Convert the text and data relocations to a.out format
2506 for (i = 0; trelptr + i < relend; i += 2)
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;
2515 if (relFlags == ALCREL_LONG)
2518 rel = GetWord(trelptr + i);
2523 aoutflags |= BSDREL_WORD;
2526 if (relFlags == ALCREL_ABS)
2530 case ALCREL_EXTPCREL:
2531 aoutflags &= ~BSDREL_ABS;
2532 aoutflags |= BSDREL_PCREL;
2535 aoutflags |= BSDREL_GLOBAL;
2536 aoutflags |= (ALCREL_SYMIDX(rel) << BSDREL_SYMIDX_SHIFT);
2539 aoutflags |= BSDREL_SEG_TEXT;
2542 aoutflags |= BSDREL_SEG_DATA;
2543 valoffset = hdr.tsize;
2546 aoutflags |= BSDREL_SEG_BSS;
2547 valoffset = hdr.tsize + hdr.dsize;
2551 printf("Invalid Alcyon relocation flags: 0x%02x\n", relFlags);
2560 if (aoutflags & BSDREL_WORD)
2562 valoffset += GetWord(valaddr);
2563 PutWord(valaddr, (uint16_t)valoffset);
2567 valoffset += GetLong(valaddr);
2568 PutLong(valaddr, valoffset);
2572 PutLong(traout, reladdr);
2573 PutLong(traout+4, aoutflags);
2577 // Done with the Alcyon data.
2582 // Set strPtr to string table location and write string table size
2583 strPtr = saout + numSyms * 12;
2584 PutLong(strPtr, 4 + symStrLen);
2586 for (i = 0, strOff = 4; i < numSyms; i++)
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
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
2600 PutLong(strPtr + strOff, 0L); // Terminating long for object file
2602 // Done with the Alcyon symbol table.
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);
2610 // What it says on the tin: check for a .o suffix on the passed in string
2612 uint8_t HasDotOSuffix(char * s)
2614 char * temp = strrchr(s, '.');
2616 if ((temp == NULL) || (strncmp(temp, ".o", 2) != 0))
2624 // Process an ar archive file (*.a)
2626 int LoadArchive(char * fname, int fd)
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;
2636 printf("Out of memory while processing %s\n", fname);
2641 if (read(fd, ptr, size) != size)
2643 printf("File read error on %s\n", fname);
2651 // Save the pointer for later...
2652 arPtr[arIndex++] = ptr;
2653 char objName[FNLEN];
2656 //printf("\nProcessing AR file \"%s\"...\n", fname);
2659 // Loop through all objects in the archive and process them
2662 memset(objName, 0, 17);
2667 // if ((ptr[i] == '/') || (ptr[i] == ' '))
2668 if ((ptr[i] == ' ') && (i != 0))
2674 objName[i] = ptr[i];
2679 if (ptr[48 + i] == ' ')
2685 objSize[i] = ptr[48 + i];
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] == '/'))
2692 uint32_t fnSize = atoi(objName + 1);
2694 if (longFilenames != NULL)
2697 char * currentFilename = longFilenames + fnSize;
2699 while (*currentFilename != 0x0A)
2700 objName[i++] = *currentFilename++;
2706 if ((strncmp(objName, "ARFILENAMES/", 12) == 0) || (strncmp(objName, "//", 2) == 0))
2708 longFilenames = ptr + 60;
2710 else if (HasDotOSuffix(objName))
2713 // Strip off any trailing forward slash at end of object name
2714 int lastChar = strlen(objName) - 1;
2716 if (objName[lastChar] == '/')
2717 objName[lastChar] = 0;
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;
2724 if (AddToProcessingList(ptr + 60, objName, fname, 1, tSize, dSize, bSize))
2728 uint32_t size = atoi(objSize);
2729 size += (size & 0x01 ? 1 : 0);
2732 while (ptr < endPtr);
2739 // Process files (*.o, *.a) passed in on the command line
2741 int ProcessFiles(void)
2744 char magic[8]; // Magic header number (4 bytes for *.o, 8 for *.a)
2746 // Process all file handles
2747 for(i=0; i<(int)hd; i++)
2749 // Verbose mode information
2751 printf("Read file %s%s\n", name[i], (hflag[i] ? " (include)" : ""));
2755 // Attempt to read file magic number (OBJECT/ARCHIVE FILES)
2756 if (read(handle[i], magic, 8) != 8)
2758 printf("Error reading file %s\n", name[i]);
2763 lseek(handle[i], 0L, 0); // Reset to start of input file
2765 // Look for RMAC/MAC/GCC (a.out) object files
2766 if ((GetLong(magic) & 0xFFFF) == 0x0107)
2768 // Process input object file
2769 if (LoadObject(name[i], handle[i], 0L))
2772 // Look for DRI Alcyon C (and old MAC) object files
2773 else if (GetWord(magic) == 0x601A)
2775 // Process Alcyon object file.
2776 if (LoadAlcyon(name[i], handle[i]))
2779 // Otherwise, look for an object archive file
2780 else if (strncmp(magic, "!<arch>\x0A", 8) == 0)
2782 if (LoadArchive(name[i], handle[i]))
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]);
2797 // If hflag[i] is 1, include this in the data segment; if 2, put it
2799 if (LoadInclude(name[i], handle[i], hsym1[i], hsym2[i], hflag[i] - 1))
2804 // Free include, symbol & object handles
2805 for(i=0; i<(int)hd; i++)
2816 // Reset next handle indicator
2823 // Load newargv with pointers to arguments found in the buffer
2825 int parse(char * buf, char * newargv[])
2830 printf("begin parsing\n");
2834 while (*buf && strchr(",\t\n\r\14 ", *buf))
2838 if (*buf == '\0' || *buf == 26)
2842 printf("No commands in command file\n");
2851 /* test for comment */
2854 /* found a comment; skip to next \n and start over */
2855 while (*buf && *buf != '\n')
2863 printf("Too many arguments in command file\n");
2869 while (!strchr(",\t\n\r\14 ", *buf))
2871 if (*buf == '\0' || *buf == 26)
2873 printf("Finished parsing %d args\n", i);
2883 printf("argv[%d] = \"%s\"\n", i, newargv[i]);
2891 // Process in a link command file
2893 int docmdfile(char * fname)
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
2901 // Verbose information
2903 printf("docmdfile(%s)\n", fname);
2905 // Allocate memory for new argument values
2906 newargv = malloc((long)(sizeof(char *) * MAXARGS));
2910 printf("Out of memory.\n");
2914 // Attempt to open and read in the command file
2917 if ((fd = open(fname, _OPEN_FLAGS)) < 0)
2919 printf("Cannot open command file %s.\n", fname);
2923 size = FileSize(fd);
2925 if ((ptr = malloc(size + 1)) == NULL)
2927 printf("Out of memory.\n");
2932 if (read(fd, ptr, size) != (int)size)
2934 printf("Read error on command file %s.\n", fname);
2939 *(ptr + size) = 0; // Null terminate the buffer
2944 printf("No command filename specified\n");
2948 // Parse the command file
2949 if ((newargc = parse(ptr, *newargv)) == -1)
2954 // Process the inputted flags
2955 if (doargs(newargc, *newargv))
2957 printf("docmdfile: doargs returns TRUE\n");
2969 // Take an argument list and parse the command line
2971 int doargs(int argc, char * argv[])
2973 int i = 1; // Iterator
2974 int c; // Command line character
2975 char * ifile, * isym; // File name and symbol name for -i
2977 // Parse through option switches & files
2980 // Process command line switches
2981 if (argv[i][0] == '-')
2985 printf("Illegal option argument: %s\n\n", argv[i]);
2990 c = argv[i++][1]; // Get next character in command line
2992 // Process command line switch
2995 case '?': // Display usage information
3002 case 'A': // Set absolute linking on
3008 printf("Not enough arguments to -a\n");
3012 aflag = 1; // Set abs link flag
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])
3018 ttype = -1; // TEXT segment is relocatable
3020 else if ((*argv[i] == 'x' || *argv[i] == 'X'))
3022 printf("Error in text-segment address: cannot be contiguous\n");
3025 else if (GetHexValue(argv[i], &tval))
3027 printf("Error in text-segment address: %s is not 'r' or an address.", argv[i]);
3033 // Data segment can be 'r', 'x' or a value
3034 if ((*argv[i] == 'r' || *argv[i] == 'R') && !argv[i][1])
3036 dtype = -1; // DATA segment is relocatable
3038 else if ((*argv[i] == 'x' || *argv[i] == 'X'))
3040 dtype = -2; // DATA follows TEXT
3042 else if (GetHexValue(argv[i], &dval))
3044 printf("Error in data-segment address: %s is not 'r', 'x' or an address.", argv[i]);
3050 // BSS segment can be 'r', 'x' or a value
3051 if ((*argv[i] == 'r' || *argv[i] == 'R') && !argv[i][1])
3053 btype = -1; // BSS segment is relocatable
3055 else if ((*argv[i] == 'x' || *argv[i] == 'X'))
3059 case 'd': case 'D': case '\0':
3060 btype = -3; // BSS follows DATA
3064 btype = -2; // BSS follows TEXT
3067 printf("Error in bss-segment address: data-segment also follows text\n");
3073 btype = -4; // Error
3077 else if (GetHexValue(argv[i], &bval))
3085 printf("Error in bss-segment address: %s is not 'r', 'x[td]', or an address.", argv[i]);
3092 case 'B': // Don't remove muliply defined locals
3099 case 'C': // Process a command file
3102 printf("Not enough arguments to -c\n");
3106 if (docmdfile(argv[i++]))
3113 case 'D': // Wait for "return" before exiting
3121 case 'E': // Output COFF (absolute only)
3125 printf("Warning: -e overridden by -n, output will be headerless\n");
3129 case 'G': // Output source level debugging
3130 if (gflag) warn('g', 1);
3134 case 'I': // Include binary file
3137 printf("Not enough arguments to -i\n");
3144 // handle -ii (No truncation)
3145 if ((argv[i-3][2] == 'i') || (argv[i-3][2] == 'I'))
3148 printf("warning: (-ii) COFF format output not specified\n");
3150 // handle -i (Truncation)
3153 if (strlen(isym) > 8)
3157 // Place include files in the DATA segment only
3158 if (DoFile(ifile, DSTSEG_D, isym))
3163 case 'L': // Add local symbols
3170 case 'M': // Produce load symbol map
3177 case 'N': // Output no header to .abs file
3182 printf("Warning: -e overridden by -n, output will be headerless\n");
3187 case 'O': // Specify an output file
3195 printf("No output filename following -o switch\n");
3199 if (strlen(argv[i]) > FARGSIZE - 5)
3201 printf("Output file name too long (sorry!)\n");
3205 strcpy(ofile, argv[i++]);
3208 case 'R': // Section alignment size
3214 switch (argv[i-1][2])
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
3226 case 'S': // Output only global symbols
3233 case 'U': // Undefined symbols
3237 case 'V': // Verbose information
3238 if (!vflag && !versflag)
3246 case 'W': // Show warnings flag
3256 printf("No directory filename following -y switch\n");
3260 if (strlen(argv[i]) > FARGSIZE * 3)
3262 printf("Directory file name too long (sorry!)\n");
3266 strcpy(libdir, argv[i++]);
3269 case 'Z': // Suppress banner flag
3276 printf("unknown option argument `%c'\n", c);
3282 // Not a switch, then process as a file
3283 if (DoFile(argv[i++], 0, NULL))
3288 if (!oflag && vflag)
3290 strcpy(ofile, "output");
3291 printf("Output file is %s[.ext]\n", ofile);
3295 printf("Output file is %s\n", ofile);
3300 // No problems encountered
3306 // Display version information
3308 void ShowVersion(void)
3310 if (displaybanner)// && vflag)
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);
3326 // Display command line help
3331 printf(" %s [-options] file(s)\n", cmdlnexec);
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");
3372 void ExitLinker(void)
3376 // Display link status if verbose mode
3378 printf("Link %s.\n", errflag ? "aborted" : "complete");
3380 // Wait for return key if requested
3383 printf("\nPress the [RETURN] key to continue. ");
3384 char * c = fgets(tempbuf, 128, stdin);
3391 int main(int argc, char * argv[])
3393 cmdlnexec = argv[0]; // Obtain executable name
3394 char * s = getenv("RLNPATH"); // Attempt to obtain env variable
3397 strcpy(libdir, s); // Store it if found
3399 // Initialize some vars
3400 tval = dval = bval = 0;
3401 ttype = dtype = btype = 0;
3403 // Parse the command line
3404 if (doargs(argc, argv))
3410 // Check to see if include paths actually exist
3411 if (strlen(libdir) > 0)
3413 DIR * test = opendir(libdir);
3417 printf("Invalid include path: %s\n", libdir);
3425 if (!zflag && !vflag)
3427 ShowVersion(); // Display version information
3428 versflag = 1; // We've dumped the version banner
3431 // Load in specified files/objects and add to processing list
3438 // Work in items in processing list & deal with unresolved list
3445 // Check that there is something to link
3452 // Report unresolved externals
3453 if (unresolved != NULL)
3455 printf("UNRESOLVED SYMBOLS\n");
3457 // Don't list them if two -u's or more
3460 struct HREC * utemp = unresolved;
3462 while (utemp != NULL)
3464 printf("\t%s (", utemp->h_sym);
3465 WriteARName(utemp->h_ofile);
3467 utemp = utemp->h_next;
3478 // Make one output object from input objects
3479 struct OHEADER * header = MakeOutputObject();
3490 printf("TO DO: Partial linking\n");
3493 // Relocatable linking
3496 printf("TO DO: Relocatable linking\n");
3503 printf("Absolute linking (%s)\n", (cflag ? "COF" : "ABS"));
3506 printf("Header magic is 0x%04X\n", (unsigned int)header->magic);
3508 if (WriteOutputFile(header))
3512 // Display the loaded symbols map
3514 if (ShowSymbolLoadMap(header))
3517 // Display segment size summary
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");