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 unsigned dbgsymbase = 0; // Debug symbol base address
37 int noheaderflag = 0; // No header flag for ABS files
38 int hflags; // Value of the arg to -h option
39 int ttype, dtype, btype; // Type flag: 0, -1, -2, -3, -4
40 int tval, dval, bval; // Values of these abs bases
41 int hflag[NHANDLES]; // True for include files
42 int handle[NHANDLES]; // Open file handles
43 int textsize, datasize, bsssize; // Cumulative segment sizes
44 char libdir[FARGSIZE * 3]; // Library directory to search
45 char ofile[FARGSIZE]; // Output file name (.o)
46 char * name[NHANDLES]; // Associated file names
47 char * cmdlnexec = NULL; // Executable name - pointer to ARGV[0]
48 char * hsym1[SYMLEN]; // First symbol for include files
49 char * hsym2[SYMLEN]; // Second symbol for include files
50 struct OFILE * plist = NULL; // Object image list pointer
51 struct OFILE * plast; // Last object image list pointer
52 struct OFILE * olist = NULL; // Pointer to first object file in list
53 struct OFILE * olast; // Pointer to last object file in list
56 struct HREC * htable[NBUCKETS]; // Hash table
57 struct HREC * unresolved = NULL; // Pointer to unresolved hash list
58 struct SYMREC * ost; // Output symbol table
59 char * oststr = NULL; // Output string table
60 char * oststr_ptr = NULL; // Output string table; current pointer
61 char * oststr_end = NULL; // Output string table; end pointer
62 int ost_index = 0; // Index of next free ost entry
63 int ost_size = 0; // Size of ost
64 uint8_t nullStr[1] = "\x00"; // Empty string
65 struct HREC * arSymbol = NULL; // Pointer to AR symbol table
68 // Function prototypes
69 struct HREC * LookupHREC(char *);
70 char * PathTail(char *);
72 void ShowVersion(void);
73 int OSTLookup(char * sym);
74 int OSTAdd(char * name, int type, long value);
75 int ProcessFiles(void);
76 int doargs(int argc, char * argv[]);
80 // Get a long word from memory
82 static inline uint32_t GetLong(uint8_t * src)
84 return (src[0] << 24) | (src[1] << 16) | (src[2] << 8) | src[3];
89 // Put a long word into memory
91 static inline void PutLong(uint8_t * dest, uint32_t val)
93 *dest++ = (uint8_t)(val >> 24);
94 *dest++ = (uint8_t)(val >> 16);
95 *dest++ = (uint8_t)(val >> 8);
101 // Get a word from memory
103 static inline uint16_t GetWord(uint8_t * src)
105 return (src[0] << 8) | src[1];
110 // Put a word into memory
112 static inline void PutWord(uint8_t * dest, uint16_t val)
114 *dest++ = (uint8_t)(val >> 8);
115 *dest = (uint8_t)val;
120 // Find passed in file's length in bytes
121 // N.B.: This also resets the file's pointer to the start of the file
123 long FileSize(int fd)
125 long size = lseek(fd, 0, SEEK_END);
126 lseek(fd, 0, SEEK_SET);
133 // For this object file, add symbols to the output symbol table after
134 // relocating them. Returns TRUE if OSTLookup returns an error (-1).
136 int DoSymbols(struct OFILE * ofile)
144 uint32_t tsoSave, dsoSave, bsoSave;
146 // Point to first symbol record in the object file
147 char * symptr = (ofile->o_image + 32
148 + ofile->o_header.tsize
149 + ofile->o_header.dsize
150 + ofile->o_header.absrel.reloc.tsize
151 + ofile->o_header.absrel.reloc.dsize);
153 // Point to end of symbol record in the object file
154 char * symend = symptr + ofile->o_header.ssize;
156 uint32_t tsegoffset = ofile->segBase[TEXT];
157 uint32_t dsegoffset = ofile->segBase[DATA];
158 uint32_t bsegoffset = ofile->segBase[BSS];
160 // Save segment vars, so we can restore them if needed
161 tsoSave = tsegoffset, dsoSave = dsegoffset, bsoSave = bsegoffset;
163 // Process each record in the object's symbol table
164 for(; symptr!=symend; symptr+=12)
166 index = GetLong(symptr + 0); // Obtain symbol string index
167 type = GetLong(symptr + 4); // Obtain symbol type
168 value = GetLong(symptr + 8); // Obtain symbol value
169 string = index ? symend + index : "";
171 // Global/External symbols have a pre-processing stage
172 // N.B.: This destroys the t/d/bsegoffset discovered above. So if a
173 // local symbol follows a global/exported one, it gets wrong
174 // info! [Should be FIXED now]
175 if (type & 0x01000000)
177 // Obtain the string table index for the relocation symbol, look
178 // for it in the globals hash table to obtain information on that
180 hptr = LookupHREC(string);
184 // Try to find it in the OST
185 int ostIndex = OSTLookup(string);
189 printf("DoSymbols(): Symbol not found in hash table: '%s' (%s)\n", string, ofile->o_name);
194 printf("DoSymbols(): Skipping symbol '%s' (%s) found in OST...\n", string, ofile->o_name);
196 // If the symbol is not in any .a or .o units, it must be one
197 // of the injected ones (_TEXT_E, _DATA_E, or _BSS_E), so skip
198 // it [or maybe not? In verbose mode, we see nothing...!]
202 tsegoffset = hptr->h_ofile->segBase[TEXT];
203 dsegoffset = hptr->h_ofile->segBase[DATA];
204 bsegoffset = hptr->h_ofile->segBase[BSS];
206 // Update type with global type
209 // Remove global flag if absolute
210 if (type == (T_GLBL | T_ABS))
213 // If the global/external has a value then update that value in
214 // accordance with the segment sizes of the object file it
218 switch (hptr->h_type & 0x0E000000)
222 value = hptr->h_value;
225 value = hptr->h_value - hptr->h_ofile->o_header.tsize;
228 value = hptr->h_value
229 - (hptr->h_ofile->o_header.tsize
230 + hptr->h_ofile->o_header.dsize);
234 printf("DoSymbols: No adjustment made for symbol: %s (%s) = %X\n", string, ofile->o_name, hptr->h_value);
238 // If *not* a global/external, use the info from passed in object
240 tsegoffset = tsoSave, dsegoffset = dsoSave, bsegoffset = bsoSave;
242 // Process and update the value dependent on whether the symbol is a
243 // debug symbol or not
244 // N.B.: Debug symbols are currently not supported
245 if (type & 0xF0000000)
248 // Set the correct debug symbol base address (TEXT segment)
252 for(j=0; (unsigned)j<dosymi; j++)
253 dbgsymbase += obj_segsize[j][0];
255 dbgsymbase = ofile->segBase[TEXT];
258 switch (type & 0xFF000000)
261 value = tval + dbgsymbase;
266 value = tval + dbgsymbase + value;
271 PutLong(symptr + 8, value);
276 // Now make modifications to the symbol value, local or global,
277 // based on the segment sizes of the object file currently being
279 switch (type & T_SEG)
284 value = tbase + tsegoffset + value;
285 PutLong(symptr + 8, value);
289 value = dbase + dsegoffset + value;
291 value = dbase + dsegoffset + (value
292 - ofile->o_header.tsize);
294 PutLong(symptr + 8, value);
298 value = bbase + bsegoffset + value;
300 value = bbase + bsegoffset
301 + (value - (ofile->o_header.tsize
302 + ofile->o_header.dsize));
304 PutLong(symptr + 8, value);
311 // Add to output symbol table if global/extern, or local flag is set
312 if (isglobal(type) || lflag)
315 printf("DoSymbols: Adding symbol: %s (%s) to OST...\n", string, ofile->o_name);
317 index = OSTAdd(index ? string : NULL, type, value);
321 printf("DoSymbols(): Failed to add symbol '%s' to OST!\n", string);
332 // Free up hash memory
334 void FreeHashes(void)
338 for(i=0; i<NBUCKETS; i++)
340 struct HREC * hptr = htable[i];
344 struct HREC * htemp = hptr->h_next;
353 // Add all global and external symbols to the output symbol table
354 // [This is confusing--is it adding globals or locals? common == local!
355 // but then again, we see this in the header:
356 // #define T_COMMON (T_GLOBAL | T_EXTERN) but that could be just bullshit.]
358 // Common symbols have a different number in the "value" field of the symbol
359 // table (!0) than purely external symbols do (0). So you have to look at the
360 // type (T_GLBL) *and* the value to determine if it's a common symbol.
367 for(i=0; i<NBUCKETS; i++)
369 for(hptr=htable[i]; hptr!=NULL; hptr=hptr->h_next)
371 //NO! if (iscommon(hptr->h_type))
372 if (isglobal(hptr->h_type))// || isextern(hptr->h_type))
374 // Skip if in *.a file... (does nothing)
375 //if (hptr->h_ofile->isArchiveFile)
378 //Is this true? Couldn't an absolute be exported???
379 if (hptr->h_type == (T_GLBL | T_ABS))
380 hptr->h_type = T_ABS; // Absolutes *can't* be externals
382 if (OSTAdd(hptr->h_sym, hptr->h_type, hptr->h_value) == -1)
393 // Add a symbol's name, type, and value to the OST.
394 // Returns the index of the symbol in OST, or -1 for error.
396 int OSTAdd(char * name, int type, long value)
398 int ost_offset_p = 0, ost_offset_e; // OST table offsets for position calcs
399 int ostresult; // OST index result
400 int slen; // String length, including terminator
402 // If this is a debug symbol and the include debug symbol flag (-g) is not
403 // set then do nothing
404 if ((type & 0xF0000000) && !gflag)
410 if (!name || !name[0])
413 slen = strlen(name) + 1;
415 // Get symbol index in OST, if any (-1 if not found)
416 ostresult = slen ? OSTLookup(name) : -1;
418 // If the symbol is in the output symbol table and the bflag is set
419 // (don't remove multiply defined locals) and this is not an
420 // external/global symbol, or the gflag (output debug symbols) is
421 // set and this a debug symbol, *** OR *** the symbol is not in the
422 // output symbol table then add it.
423 if ((ostresult != -1) && !(bflag && !(type & 0x01000000))
424 && !(gflag && (type & 0xF0000000)))
429 // If the OST has not been initialised, or more space is needed, then
431 if ((ost_index + 1) > ost_size)
434 ost_size = OST_SIZE_INIT;
438 ost = realloc(ost, ost_size * sizeof(ost[0]));
442 printf("OST memory allocation error.\n");
449 ost_offset_p = (oststr_ptr - oststr);
450 ost_offset_e = (oststr_end - oststr);
452 // If the OST data has been exhausted, allocate another chunk.
453 if (((oststr_ptr + slen + 4) > oststr_end))
455 // string length + terminating NULL + uint32_t (terminal long)
456 if ((oststr_ptr + (slen + 1 + 4)) > oststr_end)
458 oststr = realloc(oststr, ost_offset_e + OST_BLOCK);
462 printf("OSTSTR memory reallocation error.\n");
466 oststr_ptr = oststr + ost_offset_p;
467 oststr_end = (oststr + ost_offset_e) + OST_BLOCK;
469 // On the first alloc, reserve space for the string table
471 if (ost_offset_e == 0)
476 strcpy(oststr_ptr, name); // Put symbol name in string table
478 oststr_ptr[-1] = '\0'; // Add null terminating character
479 PutLong(oststr_ptr, 0x00000000); // Null terminating long
480 PutLong(oststr, (oststr_ptr - oststr)); // Update size of string table
483 ostresult = ost_index++;
485 ost[ostresult].s_idx = ost_offset_p;
486 ost[ostresult].s_type = type;
487 ost[ostresult].s_value = value;
490 printf("OSTAdd: (%s), type=$%08X, val=$%08lX\n",
491 slen ? name : "", type, value);
498 // Return the index of a symbol in the output symbol table
499 // N.B.: This is a 1-based index! (though there's no real reason for it to be)
501 int OSTLookup(char * sym)
505 for(i=0; i<ost_index; i++)
507 if (ost[i].s_idx && (strcmp(oststr + ost[i].s_idx, sym) == 0))
516 // Add unresolved externs to the output symbol table
517 // N.B.: Only adds unresolved symbols *if* they're not already in the OST
519 int DoUnresolved(void)
521 struct HREC * hptr = unresolved;
523 // Add to OST while unresolved list is valid
526 if (OSTAdd(hptr->h_sym, T_GLBL, 0L) == -1)
530 printf("DoUnresolved(): '%s' (%s:$%08X) in OST\n", hptr->h_sym, hptr->h_ofile->o_name, hptr->h_type);
532 struct HREC * htemp = hptr->h_next;
543 // Update object file TEXT and DATA segments based on relocation records. Take
544 // in an OFILE header and flag (T_TEXT, T_DATA) to process. Return (0) is
545 // successful or non-zero (1) if failed.
547 int RelocateSegment(struct OFILE * ofile, int flag)
549 char * symtab; // Start of symbol table
550 char * symbols; // Start of symbols
551 char * sptr; // Start of segment data
552 char * rptr; // Start of segment relocation records
553 unsigned symidx; // Offset to symbol
554 unsigned addr; // Relocation address
555 unsigned rflg; // Relocation flags
556 unsigned olddata; // Old segment data at reloc address
557 unsigned newdata = 0; // New segment data at reloc address
558 unsigned pad; // Temporary to calculate phrase padding
560 char sym[SYMLEN]; // String for symbol name/hash search
561 int ssidx; // Segment size table index
562 unsigned glblreloc; // Global relocation flag
563 unsigned absreloc; // Absolute relocation flag
564 unsigned relreloc; // Relative relocation flag
565 unsigned wordreloc; // Relocate word only flag
566 unsigned opreloc; // Relocate OP data address only flag
567 unsigned swcond; // Switch statement condition
568 unsigned relocsize; // Relocation record size
569 unsigned saveBits; // OP data leftover bits save
572 // If there is no TEXT relocation data for the selected object file segment
573 // then update the COF TEXT segment offset allowing for the phrase padding
574 if ((flag == T_TEXT) && !ofile->o_header.absrel.reloc.tsize)
576 // SCPCD : we should not increment the textoffset before the end of processing the object file, else data section will point to wrong textoffset
580 // If there is no DATA relocation data for the selected object file segment
581 // then update the COF DATA and BSS segment offsets allowing for the phrase
583 if ((flag == T_DATA) && !ofile->o_header.absrel.reloc.dsize)
585 // SCPCD : the T_DATA is the last section of the file, we can now increment the textoffset, dataoffset and bssoffset
587 // TEXT segment size plus padding
588 pad = ((ofile->o_header.tsize + secalign) & ~secalign);
589 textoffset += (ofile->o_header.tsize + (pad - ofile->o_header.tsize));
592 printf("RelocateSegment(%s, TEXT) : No relocation data\n", ofile->o_name);
594 // DATA segment size plus padding
595 pad = ((ofile->o_header.dsize + secalign) & ~secalign);
596 dataoffset += (ofile->o_header.dsize + (pad - ofile->o_header.dsize));
598 // BSS segment size plus padding
599 pad = ((ofile->o_header.bsize + secalign) & ~secalign);
600 bssoffset += (ofile->o_header.bsize + (pad - ofile->o_header.bsize));
603 printf("RelocateSegment(%s, DATA) : No relocation data\n", ofile->o_name);
609 printf("RelocateSegment(%s, %s) : Processing Relocation Data\n",
610 ofile->o_name, flag == T_DATA ? "DATA" : "TEXT");
612 // Obtain pointer to start of symbol table
613 symtab = (ofile->o_image + 32 + ofile->o_header.tsize
614 + ofile->o_header.dsize
615 + ofile->o_header.absrel.reloc.tsize
616 + ofile->o_header.absrel.reloc.dsize);
618 // Obtain pointer to start of symbols
619 symbols = symtab + ofile->o_header.ssize;
621 // Obtain pointer to start of TEXT segment
622 sptr = ofile->o_image + 32;
624 // Obtain pointer to start of TEXT relocation records
625 rptr = sptr + (ofile->o_header.tsize + ofile->o_header.dsize);
627 relocsize = ofile->o_header.absrel.reloc.tsize;
630 printf("RELOCSIZE :: %d Records = %d\n", relocsize, relocsize / 8);
632 // Update pointers if DATA relocation records are being processed
635 sptr += ofile->o_header.tsize; // Start of DATA segment
636 rptr += ofile->o_header.absrel.reloc.tsize; // Start of DATA relocation records
637 relocsize = ofile->o_header.absrel.reloc.dsize;
640 // Process each relocation record for the TEXT segment
641 for(i=0; i<(int)relocsize; i+=8)
643 // Obtain both the relocation address and the relocation flags from the
645 addr = GetLong(rptr);
646 rflg = GetLong(rptr + 4);
647 glblreloc = (rflg & BSDREL_GLOBAL ? 1 : 0);
648 absreloc = (rflg & BSDREL_ABS ? 1 : 0);
649 relreloc = (rflg & BSDREL_PCREL ? 1 : 0);
650 wordreloc = (rflg & BSDREL_WORD ? 1 : 0);
651 opreloc = (rflg & BSDREL_OP ? 1 : 0);
653 // Additional processing required for global relocations
656 // Obtain the string table index for the relocation symbol, look
657 // for it in the globals hash table to obtain information on that
658 // symbol. For the hash calculation to work correctly it must be
659 // placed in a 'clean' string before looking it up.
660 symidx = GetLong(symtab + (BSDREL_SYMIDX(rflg) * 12));
661 memset(sym, 0, SYMLEN);
662 strcpy(sym, symbols + symidx);
663 olddata = newdata = 0; // Initialise old and new segment data
664 ssidx = OSTLookup(sym);
665 newdata = ost[ssidx - 1].s_value;
668 // Obtain the existing long word (or word) segment data and flip words
669 // if the relocation flags indicate it relates to a RISC MOVEI
671 olddata = (wordreloc ? GetWord(sptr + addr) : GetLong(sptr + addr));
673 // If it's a OP QUAD relocation, get the data out of the DATA bits.
674 // Note that because we can't afford to lose the bottom 3 bits of the
675 // relocation record, we lose 3 off the top--which means the maximum
676 // this can store is $1FFFF8 (vs. $FFFFF8).
679 saveBits2 = (GetLong(sptr + addr + 8) & 0xE0000000) >> 8; // Upper 3 of data addr
680 saveBits = olddata & 0x7FF;
681 olddata = (olddata & 0xFFFFF800) >> 11;
682 olddata |= saveBits2; // Restore upper 3 bits of data addr
685 if (rflg & BSDREL_MOVEI)
686 olddata = _SWAPWORD(olddata);
688 // Process record dependant on segment it relates to; TEXT, DATA or
689 // BSS. Construct a new relocated segment long word based on the
690 // required segment base address, the segment data offset in the
691 // resulting COF file and the offsets from the incoming object file.
692 swcond = (rflg & BSDREL_SEGMASK);
700 case BSDREL_SEG_TEXT:
701 // SCPCD : the symbol point to a text segment, we should use the textoffset
702 newdata = tbase + textoffset + olddata;
705 case BSDREL_SEG_DATA:
706 newdata = dbase + dataoffset
707 + (olddata - ofile->o_header.tsize);
711 newdata = bbase + bssoffset
712 + (olddata - (ofile->o_header.tsize
713 + ofile->o_header.dsize));
724 // Set absolute (long) or relative (word) address of symbol
727 // Flip the new long word segment data if the relocation record
728 // indicated a RISC MOVEI instruction and place the resulting data
729 // back in the COF segment
730 if (rflg & BSDREL_MOVEI)
731 newdata = _SWAPWORD(newdata);
734 PutWord(sptr + addr, newdata);
738 printf("OP reloc: oldata=$%X, newdata=$%X\n", olddata, newdata);
740 newdata = ((newdata & 0x00FFFFF8) << 8) | saveBits;
741 PutLong(sptr + addr, newdata);
742 // Strip out extraneous data hitchhikers from 2nd phrase...
743 newdata = GetLong(sptr + addr + 8) & 0x007FFFFF;
744 PutLong(sptr + addr + 8, newdata);
747 PutLong(sptr + addr, newdata);
751 PutWord(sptr + addr, newdata - tbase - addr - ofile->o_tbase);
754 // Shamus: Let's output some info to aid in debugging this crap
761 sprintf(ssiString, " [ssi:%i]", ssidx);
763 printf("RelocateSegment($%08X): %s, $%08X: $%08X => $%08X%s\n", rflg, (glblreloc ? sym : "(LOCAL)"), addr, olddata, GetLong(sptr + addr), ssiString);
766 rptr += 8; // Point to the next relocation record
769 // Update the COF segment offset allowing for the phrase padding.
770 // SCPCD : we should not increment the textoffset before the end of processing the object file, else data section will point to wrong textoffset
773 // TEXT segment plus padding
774 pad = ((ofile->o_header.tsize + secalign) & ~secalign);
775 textoffset += (ofile->o_header.tsize + (pad - ofile->o_header.tsize));
777 // DATA segment plus padding
778 pad = ((ofile->o_header.dsize + secalign) & ~secalign);
779 dataoffset += (ofile->o_header.dsize + (pad - ofile->o_header.dsize));
781 // BSS segment plus padding
782 pad = ((ofile->o_header.bsize + secalign) & ~secalign);
783 bssoffset += (ofile->o_header.bsize + (pad - ofile->o_header.bsize));
786 // Return value, should always be zero
792 // Add a path character to the end of string 's' if it doesn't already end with
793 // one. The last occurrance of '/' or '\' in the string is assumed to be the
796 // This is fucking shit. We know what the path delimiter is, its FUCKING
797 // DEFINED IN THE FUCKING HEADER. FOR FUCKS SAKE. AND YES, HOPE TO GOD THERE'S
798 // ENOUGH SPACE IN THE PASSED IN BUFFER TO HOLD THE EXTRA CHARACTER!
800 void AppendPathDelimiter(char * s)
803 // And hope to God that there's enough space in the buffer...
808 if (*s == '/' || *s == '\\')
822 int length = strlen(s);
824 if (s[length - 1] != PATH_DELIMITER)
826 s[length] = PATH_DELIMITER;
827 s[length + 1] = 0; // BUFFER OVERFLOW!!!! FFFFFFFFUUUUUUUUUUUU
834 // Try to open "name", "name.o", "${libdir}name", "${libdir}name.o". Return the
835 // handle of the file successfully opened. p_name is updated to point to a
836 // malloc()'ed string which is the name which actually got opened. p_name will
837 // return unchanged if the file can't be found.
839 int TryOpenFile(char ** p_name)
841 char * name = *p_name;
843 // Note that libdir will be an empty string if there is none specified
844 char * tmpbuf = malloc(strlen(name) + strlen(libdir) + 3);
848 printf("TryOpenFile() : out of memory\n");
852 strcpy(tmpbuf, name);
853 int hasdot = (strrchr(tmpbuf, '.') > strrchr(tmpbuf, PATH_DELIMITER));
854 // Try to open file as passed first
855 int fd = open(tmpbuf, _OPEN_FLAGS);
862 // Try to open file with '.o' added
863 strcat(tmpbuf, ".o");
864 fd = open(tmpbuf, _OPEN_FLAGS);
870 // Try the libdir only if the name isn't already anchored
871 // Shamus: WTH, this makes no sense... Why the ':'? Is this a Macintosh??
872 // if (*name != '/' && *name != '\\' && !strchr(name, ':'))
873 if ((*name != PATH_DELIMITER) && (strchr(name, ':') == NULL))
875 strcpy(tmpbuf, libdir);
876 // Add a trailing path char if there isn't one already
877 AppendPathDelimiter(tmpbuf);
878 strcat(tmpbuf, name);
880 if ((fd = open(tmpbuf, _OPEN_FLAGS)) >= 0)
885 strcat(tmpbuf, ".o");
887 if ((fd = open(tmpbuf, _OPEN_FLAGS)) >= 0)
892 // Couldn't open file at all
895 // There are worse things... :-P
897 tmpbuf = realloc(tmpbuf, strlen(tmpbuf) + 1);
901 printf("TryOpenFile() : out of memory\n");
911 // What it says on the tin
913 void WriteARName(struct OFILE * p)
915 int flag = *(p->o_arname);
916 printf("%s%s%s", (flag ? (char *)(p->o_arname) : ""), (flag ? ":" : ""), p->o_name);
921 // Collect file names and handles in a buffer so there is less disk activity.
922 // Call DoFile with flag FALSE for normal object files and archives.
923 // Call it with flag TRUE and a symbol name for include files (-i).
925 int DoFile(char * fname, int incFlag, char * sym)
927 // Verbose information
930 printf("DoFile() : `%s' %s", fname, incFlag ? "INCLUDE" : "NORMAL");
933 printf(" symbol %s", sym);
938 // Reached maximum file handles
945 // Attempt to open input file
946 int fd = TryOpenFile(&fname);
950 printf("Cannot find input module %s\n", fname);
954 // The file is open; save its info in the handle and name arrays
956 name[hd] = fname; // This is the name from TryOpenFile()
962 int temp = strlen(sym); // Get symbol length
964 // 100 chars is max length of a symbol
971 // Malloc enough space for two symbols, then build the second one.
972 // Second one may be one character longer than first
973 if ((hsym1[hd] = malloc((long)temp + 1)) == NULL
974 || (hsym2[hd] = malloc((long)temp + 2)) == NULL)
976 printf("DoFile() : out of memory for include-file symbols\n");
980 strcpy(hsym1[hd], sym);
981 strcpy(hsym2[hd], sym);
987 printf("Last char of %s is already 'x': choose another name\n", sym);
995 hsym2[hd][temp] = 'x';
996 hsym2[hd][temp+1] = '\0';
1000 // Increment next handle index
1008 // Pad TEXT or DATA segment to the requested boundary
1010 int PadSegment(FILE * fd, long segsize, int value)
1016 // Determine the number of padding bytes that are needed
1017 long padsize = (segsize + secalign) & ~secalign;
1018 padsize = padsize - segsize;
1020 // Fill pad array if padding is required
1027 PutWord(padptr, value);
1031 symoffset += padsize;
1033 // Write padding bytes
1034 if (fwrite(padarray, padsize, 1, fd) != 1)
1043 // Write the output file
1045 int WriteOutputFile(struct OHEADER * header)
1047 unsigned osize; // Object segment size
1048 struct OFILE * otemp; // Object file pointer
1049 int i, j; // Iterators
1050 char himage[0x168]; // Header image (COF = 0xA8)
1051 uint32_t tsoff, dsoff, bsoff; // Segment offset values
1052 short abstype; // ABS symbol type
1053 char symbol[14]; // raw symbol record
1055 symoffset = 0; // Initialise symbol offset
1057 // Add correct output extension if none
1058 if (strchr(ofile, '.') == NULL)
1061 strcat(ofile, ".cof"); // COF files (a type of abs)
1062 else if (aflag && !cflag)
1063 strcat(ofile, ".abs"); // ABS files
1065 strcat(ofile, ".o"); // Object files (partial linking, etc)
1068 FILE * fd = fopen(ofile, "wb"); // Attempt to open output file
1072 printf("Can't open output file %s\n", ofile);
1076 // Build the output file header
1077 // Absolute (COF) header
1080 tsoff = dsoff = bsoff = 0xA8; // Initialises segment offsets
1082 // Process each object file segment size to obtain a cumulative segment
1083 // size for both the TEXT and DATA segments
1084 for(otemp=olist; otemp!=NULL; otemp=otemp->o_next)
1086 dsoff += otemp->segSize[TEXT];
1087 bsoff += otemp->segSize[TEXT] + otemp->segSize[DATA];
1090 // Currently this only builds a COF absolute file. Conditionals and
1091 // additional code will need to be added for ABS and partial linking.
1093 // Build the COF_HDR
1094 PutWord(himage + 0, 0x0150 ); // Magic Number (0x0150)
1095 PutWord(himage + 2, 0x0003 ); // Sections Number (3)
1096 PutLong(himage + 4, 0x00000000 ); // Date (0L)
1097 PutLong(himage + 8, dsoff + header->dsize); // Offset to Symbols Section
1098 PutLong(himage + 12, ost_index); // Number of Symbols
1099 PutWord(himage + 16, 0x001C ); // Size of RUN_HDR (0x1C)
1100 PutWord(himage + 18, 0x0003 ); // Executable Flags (3)
1102 // Build the RUN_HDR
1103 PutLong(himage + 20, 0x00000107 ); // Magic/vstamp
1104 PutLong(himage + 24, header->tsize ); // TEXT size in bytes
1105 PutLong(himage + 28, header->dsize ); // DATA size in bytes
1106 PutLong(himage + 32, header->bsize ); // BSS size in bytes
1107 PutLong(himage + 36, tbase ); // Start of executable, normally @TEXT
1108 PutLong(himage + 40, tbase ); // @TEXT
1109 PutLong(himage + 44, dbase ); // @DATA
1111 // Build the TEXT SEC_HDR
1112 PutLong(himage + 48, 0x2E746578 );
1113 PutLong(himage + 52, 0x74000000 ); // ".text"
1114 PutLong(himage + 56, tbase ); // TEXT START
1115 PutLong(himage + 60, tbase ); // TEXT START
1116 PutLong(himage + 64, header->tsize ); // TEXT size in bytes
1117 PutLong(himage + 68, tsoff ); // Offset to section data in file
1118 PutLong(himage + 72, 0x00000000 ); // Offset to section reloc in file (0L)
1119 PutLong(himage + 76, 0x00000000 ); // Offset to debug lines structures (0L)
1120 PutLong(himage + 80, 0x00000000 ); // Nreloc/nlnno (0L)
1121 PutLong(himage + 84, 0x00000020 ); // SEC_FLAGS: STYP_TEXT
1123 // Build the DATA SEC_HDR
1124 PutLong(himage + 88, 0x2E646174 );
1125 PutLong(himage + 92, 0x61000000 ); // ".data"
1126 PutLong(himage + 96, dbase ); // DATA START
1127 PutLong(himage + 100, dbase ); // DATA START
1128 PutLong(himage + 104, header->dsize ); // DATA size in bytes
1129 PutLong(himage + 108, dsoff ); // Offset to section data in file
1130 PutLong(himage + 112, 0x00000000 ); // Offset to section reloc in file (0L)
1131 PutLong(himage + 116, 0x00000000 ); // Offset to debugging lines structures (0L)
1132 PutLong(himage + 120, 0x00000000 ); // Nreloc/nlnno (0L)
1133 PutLong(himage + 124, 0x00000040 ); // SEC_FLAGS: STYP_DATA
1135 // Build the BSS SEC_HDR
1136 PutLong(himage + 128, 0x2E627373 );
1137 PutLong(himage + 132, 0x00000000 ); // ".bss"
1138 PutLong(himage + 136, bbase ); // BSS START
1139 PutLong(himage + 140, bbase ); // BSS START
1140 PutLong(himage + 144, header->bsize ); // BSS size in bytes
1141 PutLong(himage + 148, bsoff ); // Offset to section data in file
1142 PutLong(himage + 152, 0x00000000 ); // Offset to section reloc in file (0L)
1143 PutLong(himage + 156, 0x00000000 ); // Offset to debugging lines structures (0L)
1144 PutLong(himage + 160, 0x00000000 ); // Nreloc/nlnno (0L)
1145 PutLong(himage + 164, 0x00000080 ); // SEC_FLAGS: STYP_BSS
1147 symoffset = 168; // Update symbol offset
1149 // Absolute (ABS) header
1152 // Build the ABS header
1153 PutWord(himage + 0, 0x601B ); // Magic Number (0x601B)
1154 PutLong(himage + 2, header->tsize ); // TEXT segment size
1155 PutLong(himage + 6, header->dsize ); // DATA segment size
1156 PutLong(himage + 10, header->bsize ); // BSS segment size
1157 PutLong(himage + 14, ost_index * 14 ); // Symbol table size (?)
1158 PutLong(himage + 18, 0x00000000 ); //
1159 PutLong(himage + 22, tbase ); // TEXT base address
1160 PutWord(himage + 26, 0xFFFF ); // Flags (?)
1161 PutLong(himage + 28, dbase ); // DATA base address
1162 PutLong(himage + 32, bbase ); // BSS base address
1164 symoffset = 36; // Update symbol offset
1167 // Write the header, but not if noheaderflag
1170 // Absolute (ABS) header
1173 if (fwrite(himage, 36, 1, fd) != 1)
1176 // Absolute (COF) header
1179 if (fwrite(himage, 168, 1, fd) != 1)
1184 // Write the TEXT segment of each object file
1185 for(otemp=olist; otemp!=NULL; otemp=otemp->o_next)
1187 osize = otemp->o_header.tsize;
1189 // Write only if segment has size
1193 printf("Writing TEXT Segment of %s\n", otemp->o_name);
1195 if (fwrite(otemp->o_image + 32, osize, 1, fd) != 1)
1198 // Pad to required alignment boundary
1199 if (PadSegment(fd, osize, 0x0000))
1206 // Write the DATA segment of each object file
1207 for(otemp=olist; otemp!=NULL; otemp=otemp->o_next)
1209 osize = otemp->o_header.dsize;
1211 // Write only if the segment has size
1215 printf("Writing DATA Segment of %s\n", otemp->o_name);
1217 if (fwrite((otemp->o_image + 32 + otemp->o_header.tsize), osize, 1, fd) != 1)
1220 // Pad to required alignment boundary
1221 if (PadSegment(fd, osize, 0))
1228 //wha? if (!noheaderflag)
1229 // This isn't quite right, but it's closer...
1230 // (the l and s flags tell the linker to output local & global symbols
1231 // in the symbol table, so it seems there's some other thing that's a
1232 // default set somewhere. Not 100% sure. Setting -s kills -l, BTW...)
1235 // Write the symbols table and string table
1236 // Absolute (COF) symbol/string table
1241 for (i = 0; i < ost_index; i++)
1243 PutLong(symbol, ost[i].s_idx);
1244 PutLong(symbol + 4, ost[i].s_type);
1245 PutLong(symbol + 8, ost[i].s_value);
1247 if (fwrite(symbol, 12, 1, fd) != 1)
1251 if (fwrite(oststr, (oststr_ptr - oststr), 1, fd) != 1)
1255 // Absolute (ABS) symbol/string table
1258 // The symbol and string table have been created as part of the
1259 // DoSymbols() function and the output symbol and string tables are
1260 // in COF format. For an ABS file we need to process through this
1261 // to create the 14 character long combined symbol and string
1262 // table. Format of symbol table in ABS: AAAAAAAATTVVVV, where
1263 // (A)=STRING, (T)=TYPE & (V)=VALUE
1265 for(i=0; i<ost_index; i++)
1267 memset(symbol, 0, 14); // Initialise symbol record
1268 abstype = 0; // Initialise ABS symbol type
1270 // Skip debug symbols
1271 if (ost[i].s_type & 0xF0000000)
1274 // Get symbol string (maximum 8 chars)
1275 strncpy(symbol, oststr + ost[i].s_idx, 8);
1277 // Modify to ABS symbol type
1278 switch (ost[i].s_type)
1280 case 0x02000000: abstype = (short)ABST_DEFINED; break;
1281 case 0x04000000: abstype = (short)ABST_DEFINED | ABST_TEXT; break;
1282 case 0x05000000: abstype = (short)ABST_DEFINED | ABST_GLOBAL | ABST_TEXT; break;
1283 case 0x06000000: abstype = (short)ABST_DEFINED | ABST_DATA; break;
1284 case 0x07000000: abstype = (short)ABST_DEFINED | ABST_GLOBAL | ABST_DATA; break;
1285 case 0x08000000: abstype = (short)ABST_DEFINED | ABST_BSS; break;
1286 case 0x09000000: abstype = (short)ABST_DEFINED | ABST_GLOBAL | ABST_BSS; break;
1288 printf("warning (WriteOutputFile): ABS, cannot determine symbol type ($%08X) [%s]\n", ost[i].s_type, symbol);
1293 PutWord(symbol + 8, abstype); // Write back new ABS type
1294 PutLong(symbol + 10, ost[i].s_value); // Write back value
1296 // Write symbol record
1297 if (fwrite(symbol, 14, 1, fd) != 1)
1305 printf("Close error on output file %s\n", ofile);
1312 printf("Write error on output file %s\n", ofile);
1313 fclose(fd); // Try to close output file anyway
1319 // Display the symbol load map
1321 int ShowSymbolLoadMap(struct OHEADER * header)
1323 unsigned i, o; // Inner and outer loop iterators
1324 unsigned c; // Column number
1325 unsigned index; // Symbol string index
1326 unsigned type; // Symbol type
1327 unsigned value; // Symbol value
1328 char * symbol; // Symbol string value
1331 return 0; // Return if no symbols to map
1333 printf("LOAD MAP\n\n");
1335 // Outer loop for each of the symbol areas to map out;
1336 // 0 = NON-RELOCATABLE SYMBOLS
1337 // 1 = TEXT-SEGMENT RELOCATABLE SYMBOLS
1338 // 2 = DATA-SEGMENT RELOCATABLE SYMBOLS
1339 // 3 = BSS-SEGMENT RELOCATABLE SYMBOLS
1342 // Display the correct map header for the symbols being processed
1345 case 0: printf("NON-RELOCATABLE SYMBOLS\n\n"); break;
1346 case 1: printf("TEXT-SEGMENT RELOCATABLE SYMBOLS\n\n"); break;
1347 case 2: printf("DATA-SEGMENT RELOCATABLE SYMBOLS\n\n"); break;
1348 case 3: printf("BSS-SEGMENT RELOCATABLE SYMBOLS\n\n"); break;
1351 c = 0; // Initialise column number
1353 // Inner loop to process each record in the symbol table
1354 for(i=0; i<(unsigned)ost_index; i++)
1356 index = ost[i].s_idx; // Get symbol string index
1357 type = ost[i].s_type; // Get symbol type
1358 value = ost[i].s_value; // Get symbol value
1359 symbol = index ? oststr + index : ""; // Get symbol string
1361 // Display only three columns
1368 // If local symbols not included and the type is local then go to
1369 // next symbol record
1370 if (!lflag & !(type & 0x01000000))
1373 // Output each symbol to the display, dependant on type
1377 // Non-relocatable symbols
1378 if (type == 0x02000000 || type == 0x03000000)
1380 printf("%-8s %c %08X ", symbol, (type & 0x01000000) ? 'G' : 'L', value);
1386 // TEXT segment relocatable symbols
1387 if (type == 0x04000000 || type == 0x05000000)
1389 printf("%-8s %c %08X ", symbol, (type & 0x01000000) ? 'G' : 'L', value);
1395 // DATA segment relocatble symbols
1396 if (type == 0x06000000 || type == 0x07000000)
1398 printf("%-8s %c %08X ", symbol, (type & 0x01000000) ? 'G' : 'L', value);
1404 // BSS segment relocatable symbols
1405 if (type == 0x08000000 || type == 0x09000000)
1407 printf("%-8s %c %08X ", symbol, (type & 0x01000000) ? 'G' : 'L', value);
1423 // Stuff the (long) value of a string into the value argument. RETURNS TRUE if
1424 // the string doesn't parse. Parses only as a hexadecimal string.
1426 int GetHexValue(char * string, int * value)
1430 while (isxdigit(*string))
1432 if (isdigit(*string))
1434 *value = (*value << 4) + (*string++ - '0');
1438 if (isupper(*string))
1439 *string = tolower(*string);
1441 *value = (*value << 4) + ((*string++ - 'a') + 10);
1445 if (*string != '\0')
1447 printf("Invalid hexadecimal value");
1456 // Create one big .o file from the images already in memory, returning a
1457 // pointer to an OHEADER. Note that the oheader is just the header for the
1458 // output (plus some other information). The text, data, and fixups are all
1459 // still in the ofile images hanging off the global 'olist'.
1461 struct OHEADER * MakeOutputObject()
1463 unsigned tptr, dptr, bptr; // Bases in runtime model
1464 int ret = 0; // Return value
1465 struct OHEADER * header; // Output header pointer
1467 // Initialize cumulative segment sizes
1468 textsize = datasize = bsssize = 0;
1470 // For each object file, accumulate the sizes of the segments but remove
1471 // those object files which are unused
1472 struct OFILE * oprev = NULL; // Init previous obj file list ptr
1473 struct OFILE * otemp = olist; // Set temp pointer to object file list
1475 while (otemp != NULL)
1477 // If the object is unused, discard it...
1478 if ((otemp->o_flags & O_USED) == 0)
1482 printf("Unused object file ");
1484 printf(" discarded.\n");
1487 // Drop the entry from the linked list
1489 olist = otemp->o_next;
1491 oprev->o_next = otemp->o_next;
1493 // Free the object entry if it's not an archive file
1494 if (!otemp->isArchiveFile)
1495 free(otemp->o_image);
1499 // Save accumulated addresses in the object
1500 otemp->segBase[TEXT] = textsize;
1501 otemp->segBase[DATA] = datasize;
1502 otemp->segBase[BSS] = bsssize;
1504 // Increment total of segment sizes ensuring requested alignment
1505 textsize += (otemp->o_header.tsize + secalign) & ~secalign;
1506 datasize += (otemp->o_header.dsize + secalign) & ~secalign;
1507 bsssize += (otemp->o_header.bsize + secalign) & ~secalign;
1511 // Go to next object file list pointer
1512 otemp = otemp->o_next;
1515 // Update base addresses and inject the symbols _TEXT_E, _DATA_E and _BSS_E
1521 // DATA follows TEXT
1522 dbase = tval + textsize;
1526 bbase = tval + textsize + datasize;
1528 // BSS is independent of DATA
1533 // DATA is independent of TEXT
1540 bbase = tval + textsize;
1543 bbase = dval + datasize;
1547 // BSS is independent of DATA
1552 // Inject segment end labels, for C compilers that expect this shite
1553 OSTAdd("_TEXT_E", 0x05000000, tbase + textsize);
1554 OSTAdd("_DATA_E", 0x07000000, dbase + datasize);
1555 OSTAdd("_BSS_E", 0x09000000, bbase + bsssize);
1557 // Place each unresolved symbol in the output symbol table
1558 // N.B.: It only gets here to do this if user passes in -u flag
1559 // [Only used here, once]
1563 // Initialise base addresses
1564 tptr = dptr = bptr = 0;
1566 // For each file, relocate its symbols and add them to the output symbol
1571 while (otemp != NULL)
1573 otemp->o_tbase = tptr;
1574 otemp->o_dbase = dptr;
1575 otemp->o_bbase = bptr;
1576 tptr += (otemp->o_header.tsize + secalign) & ~secalign;
1577 dptr += (otemp->o_header.dsize + secalign) & ~secalign;
1578 bptr += (otemp->o_header.bsize + secalign) & ~secalign;
1580 // For each symbol, (conditionally) add it to the ost
1581 // For ARCHIVE markers, this adds the symbol for the file & returns
1582 // (Shamus: N.B. it does no such thing ATM)
1583 // [Only used here, once]
1584 if (DoSymbols(otemp))
1588 otemp = otemp->o_next;
1591 // Places all the externs, globals etc into the output symbol table
1592 if (DoCommon() == -1)
1595 // Create a new output file header
1596 header = new_oheader();
1600 printf("MakeOutputObject: out of memory!\n");
1604 // Fill in the output header. Does not match the actual output but values
1605 // used as reference
1606 header->magic = 0x0150; // COF magic number
1607 header->tsize = textsize; // TEXT segment size
1608 header->dsize = datasize; // DATA segment size
1609 header->bsize = bsssize; // BSS segment size
1610 header->ssize = ost_index * 12; // Symbol table size
1611 header->ostbase = NULL; // Output symbol table base address
1613 // For each object file, relocate its TEXT and DATA segments. OR the result
1614 // into ret so all files get moved (and errors reported) before returning
1615 // with the error condition
1616 for(otemp=olist; otemp!=NULL; otemp=otemp->o_next)
1618 ret |= RelocateSegment(otemp, T_TEXT); // TEXT segment relocations
1619 ret |= RelocateSegment(otemp, T_DATA); // DATA segment relocations
1622 // Done with global symbol hash tables
1625 return (ret ? (struct OHEADER *)NULL : header);
1630 // Add symbol to hash list
1632 int AddSymbolToHashList(struct HREC ** hptr, char * sym, struct OFILE * ofile,
1633 long value, int type)
1635 struct HREC * htemp = new_hrec();
1639 printf("Out of memory\n");
1643 // Shamus: Moar testing...
1646 printf("AddSymbolToHashList(): hptr=$%08X, sym=\"%s\", ofile=$%08X, value=$%X, type=$%X\n", hptr, sym, ofile, value, type);
1649 // Populate hash record
1650 memset(htemp->h_sym, 0, SYMLEN);
1651 strcpy(htemp->h_sym, sym);
1652 htemp->h_ofile = ofile;
1653 htemp->h_value = value;
1654 htemp->h_type = type;
1656 // Add new hash to the front of the list (hence the ** for hptr)
1657 htemp->h_next = *hptr;
1665 // Add symbol to the unresolved symbols hash table (really, it's a linked list)
1667 int AddUnresolvedSymbol(char * sym, struct OFILE * ofile)
1670 printf("AddUnresolvedSymbol(%s, %s)\n", sym, ofile->o_name);
1672 return AddSymbolToHashList(&unresolved, sym, ofile, 0L, 0);
1677 // Remove the HREC from the unresolved symbol list, and pass back a pointer
1678 // to the spot where the HREC was.
1680 struct HREC * RemoveUnresolvedSymbol(struct HREC * hrec)
1682 struct HREC * ptr = unresolved;
1683 struct HREC * previous = NULL;
1685 while ((ptr != hrec) && (ptr != NULL))
1695 struct HREC * next = ptr->h_next;
1697 // Remove the head if nothing previous, otherwise, remove what we found
1698 if (previous == NULL)
1701 previous->h_next = next;
1709 // Add symbol to the unresolved symbols hash table
1711 int AddARSymbol(char * sym, struct OFILE * ofile)
1714 printf("AddARSymbol(%s, %s)\n", sym, ofile->o_name);
1716 return AddSymbolToHashList(&arSymbol, sym, ofile, 0L, 0);
1721 // Generate hash value from the 1st 15 characters of the symbol modulo the
1722 // number of buckets in the hash.
1724 int GetHash(char * s)
1726 // For this to be consistent, the symbol MUST be zeroed out beforehand!
1727 // N.B.: strncpy() pads zeroes for us, if the symbol is less than 15 chars.
1731 int i = (c[0] + c[1] + c[2] + c[3] + c[4] + c[5] + c[6] + c[7] + c[8]
1732 + c[9] + c[10] + c[11] + c[12] + c[13] + c[14]) % NBUCKETS;
1738 // Lookup a symbol in the hash table.
1739 // Returns either a pointer to the HREC or NULL if not found.
1741 struct HREC * LookupHREC(char * symbol)
1743 struct HREC * hptr = htable[GetHash(symbol)];
1745 while (hptr != NULL)
1747 //This is utter failure...
1748 // if (symcmp(symbol, hptr->h_sym)) <-- left here for giggles :D - LinkoVitch
1749 // Return hash record pointer if found
1750 if (strcmp(symbol, hptr->h_sym) == 0)
1753 hptr = hptr->h_next;
1761 // Lookup a symbol in the AR symbol table.
1762 // Returns either a pointer to the HREC or NULL if not found.
1764 struct HREC * LookupARHREC(char * symbol)
1766 struct HREC * hptr = arSymbol;
1768 while (hptr != NULL)
1770 // Return hash record pointer if found
1771 if (strcmp(symbol, hptr->h_sym) == 0)
1774 hptr = hptr->h_next;
1782 // Add the imported symbols from this file to unresolved, and the global and
1783 // common (???) symbols to the exported hash table.
1785 // Change old-style commons (type == T_EXTERN, value != 0) to new-style ones
1786 // (type == (T_GLOBAL | T_EXTERN)). [??? O_o]
1787 // [N.B.: Whoever wrote the above didn't know what the fuck they were talking
1788 // about. Commons (globals) are exactly what they are calling 'old
1789 // style'. Also note, that there is no "T_GLOBAL" or "T_EXTERN" symbols
1790 // defined anywhere in the code.]
1792 int AddSymbols(struct OFILE * Ofile)
1794 struct HREC * hptr; // Hash record pointer
1798 printf("AddSymbols: for file %s\n", Ofile->o_name);
1799 printf(" t_bbase = $%X\n", Ofile->o_tbase);
1800 printf(" d_bbase = $%X\n", Ofile->o_dbase);
1801 printf(" o_bbase = $%X\n", Ofile->o_bbase);
1802 printf(" tsize = $%X\n", Ofile->o_header.tsize);
1803 printf(" dsize = $%X\n", Ofile->o_header.dsize);
1804 printf(" bsize = $%X\n", Ofile->o_header.bsize);
1805 printf(" reloc.tsize = $%X\n", Ofile->o_header.absrel.reloc.tsize);
1806 printf(" reloc.dsize = $%X\n", Ofile->o_header.absrel.reloc.dsize);
1809 // Get base pointer, start of sym fixups
1810 char * ptr = Ofile->o_image + 32
1811 + Ofile->o_header.tsize
1812 + Ofile->o_header.dsize
1813 + Ofile->o_header.absrel.reloc.tsize
1814 + Ofile->o_header.absrel.reloc.dsize;
1815 char * sfix = ptr; // Set symbol fixup pointer
1816 char * sstr = sfix + Ofile->o_header.ssize; // Set symbol table pointer
1817 long nsymbols = Ofile->o_header.ssize / 12; // Obtain number of symbols
1821 long index = GetLong(sfix); // Get symbol string index
1822 long type = GetLong(sfix + 4); // Get symbol type
1823 long value = GetLong(sfix + 8); // Get symbol value
1825 if ((Ofile->isArchiveFile) && !(Ofile->o_flags & O_USED))
1827 if ((type & T_GLBL) && (type & (T_SEG | T_ABS)))
1828 if (AddARSymbol(sstr + index, Ofile))
1831 else if (type == T_GLBL)
1833 // Global symbol that may or may not be in the current unit
1834 hptr = LookupHREC(sstr + index);
1837 hptr->h_ofile->o_flags |= O_USED; // Mark .o file as used
1838 // Otherwise, *maybe* add to unresolved list
1841 // Check to see if this is a common symbol; if so, add it to
1845 // Actually, we need to convert this to a BSS symbol,
1846 // increase the size of the BSS segment for this object, &
1847 // add it to the hash list
1848 uint32_t bssLocation = Ofile->o_header.tsize + Ofile->o_header.dsize + Ofile->o_header.bsize;
1849 Ofile->o_header.bsize += value;
1850 Ofile->segSize[BSS] += value;
1852 value = bssLocation;
1853 PutLong(sfix + 4, type);
1854 PutLong(sfix + 8, value);
1857 printf("AddSymbols: Resetting common label to BSS label\n");
1859 if (AddSymbolToHashList(&htable[GetHash(sstr + index)],
1860 sstr + index, Ofile, value, type))
1861 return 1; // Error if addition failed
1863 // Make sure it's not a built-in external...
1864 else if ((strcmp(sstr + index, "_TEXT_E") != 0)
1865 && (strcmp(sstr + index, "_DATA_E") != 0)
1866 && (strcmp(sstr + index, "_BSS_E") != 0))
1868 if (AddUnresolvedSymbol(sstr + index, Ofile))
1869 return 1; // Error if addition failed
1873 else if ((type & T_GLBL) && (type & (T_SEG | T_ABS)))
1875 hptr = LookupHREC(sstr + index);
1877 // Symbol isn't in the table, so try to add it:
1880 if (AddSymbolToHashList(&htable[GetHash(sstr + index)],
1881 sstr + index, Ofile, value, type))
1886 // Symbol already exists, decide what to do about it
1887 // [N.B.: This isn't a check for a common symbol...
1888 // BEWARE OF BAD INTERPRETATIONS!!]
1889 if (iscommon(hptr->h_type))
1891 // Mismatch: common came first; warn and keep the global
1894 printf("Warning: %s: global from ", sstr + index);
1896 printf(" used, common from ");
1897 WriteARName(hptr->h_ofile);
1898 printf(" discarded.\n");
1901 hptr->h_ofile = Ofile;
1902 hptr->h_type = type;
1903 hptr->h_value = value;
1907 // Global exported by another ofile; warn and make this one
1911 printf("Duplicate symbol %s: ", sstr + index);
1912 WriteARName(hptr->h_ofile);
1915 printf(" discarded\n");
1918 // Set the global in this unit to pure external
1919 // (is this a good idea? what if the other one is a ref to
1921 PutLong(sfix + 4, T_GLBL);
1926 sfix += 12; // Increment symbol fixup pointer
1927 nsymbols--; // Decrement num of symbols to process
1930 // Success loading symbols
1936 // Process object file for symbols
1938 int DoItem(struct OFILE * obj)
1940 // Allocate memory for object record ptr
1941 struct OFILE * Ofile = new_ofile();
1945 printf("Out of memory while processing %s\n", obj->o_name);
1949 // Starting after all pathnames, etc., copy .o file name to Ofile
1950 char * temp = PathTail(obj->o_name);
1952 // Check filename length
1953 if (strlen(temp) > FNLEN - 1)
1955 printf("File name too long: %s\n", temp);
1959 // Check archive name length
1960 if (strlen(obj->o_arname) > (FNLEN - 1))
1962 printf("Archive name too long: %s\n", obj->o_arname);
1966 strcpy(Ofile->o_name, temp); // Store filename
1967 strcpy(Ofile->o_arname, obj->o_arname); // Store archive name
1969 // Initialise object record information
1970 Ofile->o_next = NULL;
1974 Ofile->o_flags = obj->o_flags;
1975 Ofile->o_image = obj->o_image;
1976 Ofile->isArchiveFile = obj->isArchiveFile;
1977 Ofile->segSize[TEXT] = obj->segSize[TEXT];
1978 Ofile->segSize[DATA] = obj->segSize[DATA];
1979 Ofile->segSize[BSS] = obj->segSize[BSS];
1980 char * ptr = obj->o_image;
1982 Ofile->o_header.magic = GetLong(ptr);
1983 Ofile->o_header.tsize = GetLong(ptr + 4);
1984 Ofile->o_header.dsize = GetLong(ptr + 8);
1985 Ofile->o_header.bsize = GetLong(ptr + 12);
1986 Ofile->o_header.ssize = GetLong(ptr + 16);
1987 Ofile->o_header.absrel.reloc.tsize = GetLong(ptr + 24);
1988 Ofile->o_header.absrel.reloc.dsize = GetLong(ptr + 28);
1990 // Round BSS off to alignment boundary (??? isn't this already done ???)
1991 Ofile->o_header.bsize = (Ofile->o_header.bsize + secalign) & ~secalign;
1993 if ((Ofile->o_header.dsize & 7) && wflag)
1995 printf("Warning: data segment size of ");
1997 printf(" is not a phrase multiple\n");
2000 // Check for odd segment sizes
2001 if ((Ofile->o_header.tsize & 1) || (Ofile->o_header.dsize & 1)
2002 || (Ofile->o_header.bsize & 1))
2004 printf("Error: odd-sized segment in ");
2006 printf("; link aborted.\n");
2010 if (AddSymbols(Ofile))
2013 // Add this file to the olist
2017 olast->o_next = Ofile;
2025 // Handle items in processing list.
2027 // After loading all objects, archives & include files, we now go and process
2028 // each item on the processing list (plist). Once this is done, we go through
2029 // any unresolved symbols left and see if they have shown up.
2031 int ProcessLists(void)
2033 // Process object file list first (adds symbols from each unit & creates
2035 while (plist != NULL)
2040 struct OFILE * ptemp = plist;
2041 plist = plist->o_next;
2047 // Process the unresolved symbols list. This may involve pulling in symbols
2048 // from any included .a units. Such units are lazy linked by default; we
2049 // generally don't want everything they provide, just what's referenced.
2050 for(uptr=unresolved; uptr!=NULL; )
2053 printf("LookupHREC(%s) => ", uptr->h_sym);
2055 struct HREC * htemp = LookupHREC(uptr->h_sym);
2059 // Found it in the symbol table!
2061 printf("%s in %s (=$%06X)\n", (isglobal(htemp->h_type) ? "global" : "common"), htemp->h_ofile->o_name, htemp->h_value);
2063 // Mark the .o unit that the symbol is in as seen & remove from the
2065 htemp->h_ofile->o_flags |= O_USED;
2066 uptr = RemoveUnresolvedSymbol(uptr);
2073 // Check to see if the unresolved symbol is on the AR symbol list.
2074 htemp = LookupARHREC(uptr->h_sym);
2076 // If the unresolved symbol is in a .o unit that is unused, we can
2077 // drop it; same if the unresolved symbol is in the exported AR
2078 // symbol list. Otherwise, go to the next unresolved symbol.
2079 if (!(uptr->h_ofile->o_flags & O_USED) || (htemp != NULL))
2080 uptr = RemoveUnresolvedSymbol(uptr);
2082 uptr = uptr->h_next;
2084 // Now that we've possibly deleted the symbol from unresolved list
2085 // that was also in the AR list, we add the symbols from this .o
2086 // unit to the symbol table, mark the .o unit as used, and restart
2087 // scanning the unresolved list as there is a good possibility that
2088 // the symbols in the unit we're adding has unresolved symbols as
2092 htemp->h_ofile->o_flags |= O_USED;
2093 AddSymbols(htemp->h_ofile);
2099 // Show files used if the user requests it.
2102 printf("Files used:\n");
2103 struct OFILE * filePtr = olist;
2105 while (filePtr != NULL)
2107 if (filePtr->o_flags & O_USED)
2109 printf(" %s%s%s\n", filePtr->o_name, (filePtr->isArchiveFile ? ":" : ""), (filePtr->isArchiveFile ? filePtr->o_arname : nullStr));
2112 filePtr = filePtr->o_next;
2121 // Extract filename from path
2123 char * PathTail(char * name)
2125 // Find last occurance of PATH_DELIMETER
2126 char * temp = strrchr(name, PATH_DELIMITER);
2128 // Return what was passed in if path delimiter was not found
2137 // Add input file to processing list
2139 int AddToProcessingList(char * ptr, char * fname, char * arname, uint8_t arFile, uint32_t tSize, uint32_t dSize, uint32_t bSize)
2143 // First time object record allocation
2144 plist = new_ofile();
2149 // Next object record allocation
2150 plast->o_next = new_ofile();
2151 plast = plast->o_next;
2156 printf("Out of memory.\n"); // Error if memory allocation fails
2160 // Discard paths from filenames...
2161 fname = PathTail(fname);
2162 arname = PathTail(arname);
2164 // Check for filename length errors...
2165 if (strlen(fname) > (FNLEN - 1))
2167 printf("File name too long: %s (sorry!)\n", fname);
2171 if (strlen(arname) > (FNLEN - 1))
2173 printf("AR file name too long: %s (sorry!)\n", arname);
2177 strcpy(plast->o_name, fname); // Store filename sans path
2178 strcpy(plast->o_arname, arname); // Store archive name sans path
2179 plast->o_image = ptr; // Store data pointer
2180 plast->o_flags = (arFile ? 0 : O_USED); // File is used if NOT in archive
2181 plast->o_next = NULL; // Initialise next record pointer
2182 plast->isArchiveFile = arFile; // Shamus: Temp until can sort it out
2183 plast->segSize[TEXT] = tSize;
2184 plast->segSize[DATA] = dSize;
2185 plast->segSize[BSS] = bSize;
2187 return 0; // Return without errors
2192 // Process in binary include files and add them to the processing list. This
2193 // routine takes in the binary file and creates an 'object' file in memory.
2194 // Sym1/Sym2 point to the start and end of data.
2196 // Image size for include files is:
2197 // Header ....... 32 bytes
2198 // Data ......... dsize
2199 // Sym fixups ... 2 * 12 bytes
2200 // Symbol size .. 4 bytes (Value to include symbols and terminating null)
2201 // Symbols ...... (strlen(sym1) + 1) + (strlen(sym2) + 1)
2202 // Terminate .... 4 bytes (0x00000000)
2204 int LoadInclude(char * fname, int handle, char * sym1, char * sym2, int segment)
2208 unsigned symtype = 0;
2209 uint32_t tSize = 0, dSize = 0, bSize = 0;
2211 long fsize = FileSize(handle); // Get size of include file
2212 long dsize = (fsize + secalign) & ~secalign; // Align size to boundary
2213 int sym1len = strlen(sym1) + 1; // Get sym1 length + null termination
2214 int sym2len = strlen(sym2) + 1; // Get sym2 length + null termination
2215 long size = 32 + dsize + 24 + 4 + sym1len + sym2len + 4;
2217 // Use calloc so the header & fixups initialize to zero
2218 // Allocate object image memory
2219 if ((ptr = calloc(size, 1)) == NULL)
2221 printf("Out of memory while including %s\n", fname);
2226 // Read in binary data
2227 if (read(handle, ptr + 32, fsize) != fsize)
2229 printf("File read error on %s\n", fname);
2237 // Build this image's dummy header
2238 PutLong(ptr, 0x00000107); // Magic number
2242 PutLong(ptr+4, dsize); // Text size
2243 PutLong(ptr+8, 0L); // Data size
2244 symtype = 0x05000000;
2249 PutLong(ptr+4, 0L); // Text size
2250 PutLong(ptr+8, dsize); // Data size
2251 symtype = 0x07000000;
2255 PutLong(ptr+12, 0L); // BSS size
2256 PutLong(ptr+16, 24); // Symbol table size
2257 PutLong(ptr+20, 0L); // Entry point
2258 PutLong(ptr+24, 0L); // TEXT relocation size
2259 PutLong(ptr+28, 0L); // DATA relocation size
2261 sptr = ptr + 32 + dsize; // Set sptr to symbol table location
2263 PutLong(sptr, 4L); // String offset of symbol1
2264 PutLong(sptr+4, symtype); // Symbol type
2265 PutLong(sptr+8, 0x00000000); // Symbol has no value (START)
2266 PutLong(sptr+12, 4L + (sym2len - 1)); // String offset of symbol2
2267 PutLong(sptr+16, symtype); // Symbol type
2268 PutLong(sptr+20, dsize); // Symbol is data size (END)
2270 sptr = ptr + 32 + dsize + 24; // Set sptr to symbol table size loc
2272 PutLong(sptr, sym1len + 4L); // Size of symbol table
2274 sptr = ptr + 32 + dsize + 24 + 4; // Set sptr to symbol table location
2276 for(i=0; i<(sym1len-1); i++) // Write symbol1 to string table
2279 sptr += (sym1len - 1); // Step past symbol string
2280 *sptr = '\0'; // Terminate symbol string
2281 sptr += 1; // Step past termination
2283 for(i=0; i<(sym2len-1); i++) // Write symbol2 to string table
2286 sptr += (sym2len - 1); // Step past symbol string
2287 *sptr = '\0'; // Terminate symbol string
2288 sptr += 1; // Step past termination
2290 PutLong(sptr, 0L); // Terminating long for object file
2292 return AddToProcessingList(ptr, fname, nullStr, 0, tSize, dSize, bSize);
2297 // Takes a file name, gets in its image, puts it on plist. The image may
2298 // already be in memory: If so, the ptr arg is non-null. If so, the file is
2299 // already closed. Note that the file is already open (from DoFile()). RETURNS
2300 // a pointer to the OFILE structure for this file, so you can diddle its flags
2301 // (DoFile sets O_USED for files on the command line).
2303 int LoadObject(char * fname, int fd, char * ptr)
2305 uint32_t tSize = 0, dSize = 0, bSize = 0;
2309 long size = FileSize(fd);
2311 // Allocate memory for file data
2316 printf("Out of memory while processing %s\n", fname);
2321 // Read in file data
2322 if (read(fd, ptr, size) != size)
2324 printf("File read error on %s\n", fname);
2330 tSize = (GetLong(ptr + 4) + secalign) & ~secalign;
2331 dSize = (GetLong(ptr + 8) + secalign) & ~secalign;
2332 bSize = (GetLong(ptr + 12) + secalign) & ~secalign;
2336 // Now add this image to the list of pending ofiles (plist)
2337 return AddToProcessingList(ptr, fname, nullStr, 0, tSize, dSize, bSize);
2340 uint32_t SymTypeAlcToAout(uint32_t alcType)
2342 uint32_t type = T_UNDF;
2344 // Symbol type mappings here are derived from comparing Alcyon and BSD
2345 // object files generated by MADMAC using the "size" utility from jag_utils
2346 // (https://github.com/cubanismo/jag_utils) and the old PC/DOS Atari SDK.
2350 // 1) text | global text == text relocatable symbol in this file
2351 // 2) data | global data == data relocatable symbol in this file
2352 // 3) BSS | global BSS == bss relocatable symbol in this file
2353 // 4) ABS | global ABS == non-relocatable symbol in this file
2354 // 4) <none> | global <none> == undefined global symbol (extern)
2358 // 1) Everything seems to be marked defined.
2359 // 2) There is an explicit "external" bit. It appears to be mutually
2360 // exclusive with the "global" bit, at least in MADMAC's output.
2361 // 3) There are separate "equated" and "equated reg" type bits that
2362 // both represent ABS/non-relocatable values.
2363 if ((alcType & ALCSYM_EQUATED) ||
2364 (alcType & ALCSYM_EQUATED_REG))
2366 else if (alcType & ALCSYM_TEXT)
2368 else if (alcType & ALCSYM_DATA)
2370 else if (alcType & ALCSYM_BSS)
2373 if ((alcType & ALCSYM_GLOBAL) ||
2374 (alcType & ALCSYM_EXTERN))
2380 int LoadAlcyon(char * fname, int fd)
2382 char *ptr, *sptr, *aout, *saout, *traout, *strPtr;
2383 char *trelptr, *drelptr, *relend;
2384 struct ALCHEADER hdr;
2385 struct ALCSYM *alcSyms;
2386 long size = FileSize(fd);
2389 uint32_t numSyms, numTRel, numDRel, i, j;
2391 // Validate the file is at least large enough to contain a valid header
2394 printf("Alcyon object file %s too small to contain header\n", fname);
2398 // Allocate memory for file data
2403 printf("Out of memory while processing %s\n", fname);
2408 // Read in file data
2409 if (read(fd, ptr, size) != size)
2411 printf("File read error on %s\n", fname);
2419 hdr.magic = GetWord(ptr);
2420 hdr.tsize = GetLong(ptr + 2);
2421 hdr.dsize = GetLong(ptr + 6);
2422 hdr.bsize = GetLong(ptr + 10);
2423 hdr.ssize = GetLong(ptr + 14);
2425 // Construct a BSD-style/aout object file in memory from the Alcyon data
2426 numSyms = hdr.ssize / 14;
2428 alcSyms = calloc(numSyms, sizeof(*alcSyms));
2429 if (alcSyms == NULL)
2431 printf("Out of memory while processing %s\n", fname);
2436 sptr = ptr + 0x1c + hdr.tsize + hdr.dsize;
2437 trelptr = sptr + hdr.ssize;
2438 drelptr = trelptr + hdr.tsize;
2439 relend = drelptr + hdr.dsize;
2441 if (relend - ptr > size)
2443 printf("Alcyon object file %s truncated: Header wants %ldB, file is %ldB\n",
2444 fname, relend - ptr, size);
2448 for (i = 0, symStrLen = 0; i < numSyms; i++)
2450 memcpy(alcSyms[i].name, sptr, 8);
2451 alcSyms[i].type = GetWord(sptr + 8);
2452 alcSyms[i].value = GetLong(sptr + 10);
2453 symStrLen += strnlen((char *)alcSyms[i].name, 8) + 1;
2457 for (i = 0, numTRel = 0; trelptr + i < drelptr; i += 2)
2459 uint16_t rel = GetWord(trelptr + i);
2460 if ((rel != ALCREL_ABS) &&
2461 (rel != ALCREL_LONG))
2465 for (i = 0, numDRel = 0; drelptr + i < relend; i += 2)
2467 uint16_t rel = GetWord(drelptr + i);
2468 if ((rel != ALCREL_ABS) &&
2469 (rel != ALCREL_LONG))
2473 aout = malloc(32 + /* header */
2476 numTRel * 8 + /* Text section relocations */
2477 numDRel * 8 + /* Data section relocations */
2478 numSyms * 12 + /* symbol table */
2479 4 + symStrLen + /* string table size + strings */
2480 4 /* NULL-terminator for file */);
2483 printf("Out of memory while processing %s\n", fname);
2489 // Construct the BSD/a.out header.
2490 PutLong(aout, 0x00000107); // Magic number
2492 PutLong(aout+4, hdr.tsize); // Text size
2493 PutLong(aout+8, hdr.dsize); // Data size
2494 PutLong(aout+12, hdr.bsize); // BSS size
2495 PutLong(aout+16, numSyms * 12); // Symbol table size
2496 PutLong(aout+20, 0L); // Entry point
2498 PutLong(aout+24, numTRel * 8); // TEXT relocation size
2499 PutLong(aout+28, numDRel * 8); // DATA relocation size
2501 // Copy the raw text and data segments
2502 memcpy(aout + 32, ptr + 0x1c, hdr.tsize);
2503 memcpy(aout + 32 + hdr.tsize, ptr + 0x1c + hdr.tsize, hdr.dsize);
2505 // Set traout to the start of the relocation tables
2506 traout = aout + 32 + hdr.tsize + hdr.dsize;
2508 // Set saout to symbol table location
2509 saout = traout + numTRel * 8 + numDRel * 8 ;
2511 // Convert the text and data relocations to a.out format
2512 for (i = 0; trelptr + i < relend; i += 2)
2514 uint16_t rel = GetWord(trelptr + i);
2515 uint16_t relFlags = rel & 7;
2516 uint32_t aoutflags = BSDREL_ABS;
2517 uint32_t valoffset = 0;
2518 char *const valaddr = aout + 32 + i;
2519 const uint32_t reladdr = (trelptr + i >= drelptr) ? i - hdr.tsize : i;
2521 if (relFlags == ALCREL_LONG)
2524 rel = GetWord(trelptr + i);
2529 aoutflags |= BSDREL_WORD;
2532 if (relFlags == ALCREL_ABS)
2536 case ALCREL_EXTPCREL:
2537 aoutflags &= ~BSDREL_ABS;
2538 aoutflags |= BSDREL_PCREL;
2541 aoutflags |= BSDREL_GLOBAL;
2542 aoutflags |= (ALCREL_SYMIDX(rel) << BSDREL_SYMIDX_SHIFT);
2545 aoutflags |= BSDREL_SEG_TEXT;
2548 aoutflags |= BSDREL_SEG_DATA;
2549 valoffset = hdr.tsize;
2552 aoutflags |= BSDREL_SEG_BSS;
2553 valoffset = hdr.tsize + hdr.dsize;
2557 printf("Invalid Alcyon relocation flags: 0x%02x\n", relFlags);
2566 if (aoutflags & BSDREL_WORD)
2568 valoffset += GetWord(valaddr);
2569 PutWord(valaddr, (uint16_t)valoffset);
2573 valoffset += GetLong(valaddr);
2574 PutLong(valaddr, valoffset);
2578 PutLong(traout, reladdr);
2579 PutLong(traout+4, aoutflags);
2583 // Done with the Alcyon data.
2588 // Set strPtr to string table location and write string table size
2589 strPtr = saout + numSyms * 12;
2590 PutLong(strPtr, 4 + symStrLen);
2592 for (i = 0, strOff = 4; i < numSyms; i++)
2594 PutLong(saout, strOff); // String offset of symbol
2595 PutLong(saout+4, SymTypeAlcToAout(alcSyms[i].type)); // Symbol type
2596 PutLong(saout+8, alcSyms[i].value); // Symbol value
2599 for (j = 0; j < 8 && alcSyms[i].name[j] != '\0'; j++)
2600 *(strPtr + strOff + j) = alcSyms[i].name[j];
2601 strOff += j; // Step past string
2602 *(strPtr + strOff) = '\0'; // Terminate symbol string
2603 strOff++; // Step past termination
2606 PutLong(strPtr + strOff, 0L); // Terminating long for object file
2608 // Done with the Alcyon symbol table.
2611 // Now add this image to the list of pending ofiles (plist)
2612 return AddToProcessingList(aout, fname, nullStr, 0, hdr.tsize, hdr.dsize, hdr.bsize);
2616 // What it says on the tin: check for a .o suffix on the passed in string
2618 uint8_t HasDotOSuffix(char * s)
2620 char * temp = strrchr(s, '.');
2622 if ((temp == NULL) || (strncmp(temp, ".o", 2) != 0))
2630 // Process an ar archive file (*.a)
2632 int LoadArchive(char * fname, int fd)
2634 // Read in the archive file to memory and process
2635 long size = FileSize(fd);
2636 char * ptr = malloc(size);
2637 char * endPtr = ptr + size;
2638 char * longFilenames = NULL;
2642 printf("Out of memory while processing %s\n", fname);
2647 if (read(fd, ptr, size) != size)
2649 printf("File read error on %s\n", fname);
2657 // Save the pointer for later...
2658 arPtr[arIndex++] = ptr;
2659 char objName[FNLEN];
2662 //printf("\nProcessing AR file \"%s\"...\n", fname);
2665 // Loop through all objects in the archive and process them
2668 memset(objName, 0, 17);
2673 // if ((ptr[i] == '/') || (ptr[i] == ' '))
2674 if ((ptr[i] == ' ') && (i != 0))
2680 objName[i] = ptr[i];
2685 if (ptr[48 + i] == ' ')
2691 objSize[i] = ptr[48 + i];
2694 // Check to see if a long filename was requested
2695 // N.B.: " " is for GNU archives, and "/" is for BSD archives
2696 if ((objName[0] == 0x20) || (objName[0] == '/'))
2698 uint32_t fnSize = atoi(objName + 1);
2700 if (longFilenames != NULL)
2703 char * currentFilename = longFilenames + fnSize;
2705 while (*currentFilename != 0x0A)
2706 objName[i++] = *currentFilename++;
2712 if ((strncmp(objName, "ARFILENAMES/", 12) == 0) || (strncmp(objName, "//", 2) == 0))
2714 longFilenames = ptr + 60;
2716 else if (HasDotOSuffix(objName))
2719 // Strip off any trailing forward slash at end of object name
2720 int lastChar = strlen(objName) - 1;
2722 if (objName[lastChar] == '/')
2723 objName[lastChar] = 0;
2725 //printf("Processing object \"%s\" (size == %i, obj_index == %i)...\n", objName, atoi(objSize), obj_index);
2726 uint32_t tSize = (GetLong(ptr + 60 + 4) + secalign) & ~secalign;
2727 uint32_t dSize = (GetLong(ptr + 60 + 8) + secalign) & ~secalign;
2728 uint32_t bSize = (GetLong(ptr + 60 + 12) + secalign) & ~secalign;
2730 if (AddToProcessingList(ptr + 60, objName, fname, 1, tSize, dSize, bSize))
2734 uint32_t size = atoi(objSize);
2735 size += (size & 0x01 ? 1 : 0);
2738 while (ptr < endPtr);
2745 // Process files (*.o, *.a) passed in on the command line
2747 int ProcessFiles(void)
2750 char magic[8]; // Magic header number (4 bytes for *.o, 8 for *.a)
2752 // Process all file handles
2753 for(i=0; i<(int)hd; i++)
2755 // Verbose mode information
2757 printf("Read file %s%s\n", name[i], (hflag[i] ? " (include)" : ""));
2761 // Attempt to read file magic number (OBJECT/ARCHIVE FILES)
2762 if (read(handle[i], magic, 8) != 8)
2764 printf("Error reading file %s\n", name[i]);
2769 lseek(handle[i], 0L, 0); // Reset to start of input file
2771 // Look for RMAC/MAC/GCC (a.out) object files
2772 if ((GetLong(magic) & 0xFFFF) == 0x0107)
2774 // Process input object file
2775 if (LoadObject(name[i], handle[i], 0L))
2778 // Look for DRI Alcyon C (and old MAC) object files
2779 else if (GetWord(magic) == 0x601A)
2781 // Process Alcyon object file.
2782 if (LoadAlcyon(name[i], handle[i]))
2785 // Otherwise, look for an object archive file
2786 else if (strncmp(magic, "!<arch>\x0A", 8) == 0)
2788 if (LoadArchive(name[i], handle[i]))
2793 // Close file and error
2794 printf("%s is not a supported object or archive file\n", name[i]);
2795 printf("Magic == [%02X][%02X][%02X][%02X]\n", magic[0], magic[1], magic[2], magic[3]);
2803 // If hflag[i] is 1, include this in the data segment; if 2, put it
2805 if (LoadInclude(name[i], handle[i], hsym1[i], hsym2[i], hflag[i] - 1))
2810 // Free include, symbol & object handles
2811 for(i=0; i<(int)hd; i++)
2822 // Reset next handle indicator
2829 // Load newargv with pointers to arguments found in the buffer
2831 int parse(char * buf, char * newargv[])
2836 printf("begin parsing\n");
2840 while (*buf && strchr(",\t\n\r\14 ", *buf))
2844 if (*buf == '\0' || *buf == 26)
2848 printf("No commands in command file\n");
2857 /* test for comment */
2860 /* found a comment; skip to next \n and start over */
2861 while (*buf && *buf != '\n')
2869 printf("Too many arguments in command file\n");
2875 while (!strchr(",\t\n\r\14 ", *buf))
2877 if (*buf == '\0' || *buf == 26)
2879 printf("Finished parsing %d args\n", i);
2889 printf("argv[%d] = \"%s\"\n", i, newargv[i]);
2897 // Process in a link command file
2899 int docmdfile(char * fname)
2901 int fd; // File descriptor
2902 unsigned size; // Command file size
2903 char * ptr; // Pointer
2904 int newargc; // New argument count
2905 char * (*newargv)[]; // New argument value array
2907 // Verbose information
2909 printf("docmdfile(%s)\n", fname);
2911 // Allocate memory for new argument values
2912 newargv = malloc((long)(sizeof(char *) * MAXARGS));
2916 printf("Out of memory.\n");
2920 // Attempt to open and read in the command file
2923 if ((fd = open(fname, _OPEN_FLAGS)) < 0)
2925 printf("Cannot open command file %s.\n", fname);
2929 size = FileSize(fd);
2931 if ((ptr = malloc(size + 1)) == NULL)
2933 printf("Out of memory.\n");
2938 if (read(fd, ptr, size) != (int)size)
2940 printf("Read error on command file %s.\n", fname);
2945 *(ptr + size) = 0; // Null terminate the buffer
2950 printf("No command filename specified\n");
2954 // Parse the command file
2955 if ((newargc = parse(ptr, *newargv)) == -1)
2960 // Process the inputted flags
2961 if (doargs(newargc, *newargv))
2963 printf("docmdfile: doargs returns TRUE\n");
2975 // Take an argument list and parse the command line
2977 int doargs(int argc, char * argv[])
2979 int i = 1; // Iterator
2980 int c; // Command line character
2981 char * ifile, * isym; // File name and symbol name for -i
2983 // Parse through option switches & files
2986 // Process command line switches
2987 if (argv[i][0] == '-')
2991 printf("Illegal option argument: %s\n\n", argv[i]);
2996 c = argv[i++][1]; // Get next character in command line
2998 // Process command line switch
3001 case '?': // Display usage information
3008 case 'A': // Set absolute linking on
3014 printf("Not enough arguments to -a\n");
3018 aflag = 1; // Set abs link flag
3020 // Segment order is TEXT, DATA, BSS
3021 // Text segment can be 'r' or a value
3022 if ((*argv[i] == 'r' || *argv[i] == 'R') && !argv[i][1])
3024 ttype = -1; // TEXT segment is relocatable
3026 else if ((*argv[i] == 'x' || *argv[i] == 'X'))
3028 printf("Error in text-segment address: cannot be contiguous\n");
3031 else if (GetHexValue(argv[i], &tval))
3033 printf("Error in text-segment address: %s is not 'r' or an address.", argv[i]);
3039 // Data segment can be 'r', 'x' or a value
3040 if ((*argv[i] == 'r' || *argv[i] == 'R') && !argv[i][1])
3042 dtype = -1; // DATA segment is relocatable
3044 else if ((*argv[i] == 'x' || *argv[i] == 'X'))
3046 dtype = -2; // DATA follows TEXT
3048 else if (GetHexValue(argv[i], &dval))
3050 printf("Error in data-segment address: %s is not 'r', 'x' or an address.", argv[i]);
3056 // BSS segment can be 'r', 'x' or a value
3057 if ((*argv[i] == 'r' || *argv[i] == 'R') && !argv[i][1])
3059 btype = -1; // BSS segment is relocatable
3061 else if ((*argv[i] == 'x' || *argv[i] == 'X'))
3065 case 'd': case 'D': case '\0':
3066 btype = -3; // BSS follows DATA
3070 btype = -2; // BSS follows TEXT
3073 printf("Error in bss-segment address: data-segment also follows text\n");
3079 btype = -4; // Error
3083 else if (GetHexValue(argv[i], &bval))
3091 printf("Error in bss-segment address: %s is not 'r', 'x[td]', or an address.", argv[i]);
3098 case 'B': // Don't remove muliply defined locals
3105 case 'C': // Process a command file
3108 printf("Not enough arguments to -c\n");
3112 if (docmdfile(argv[i++]))
3119 case 'D': // Wait for "return" before exiting
3127 case 'E': // Output COFF (absolute only)
3131 printf("Warning: -e overridden by -n, output will be headerless\n");
3135 case 'G': // Output source level debugging
3136 printf("\'g\' flag not currently implemented\n");
3139 if (gflag) warn('g', 1);
3144 case 'I': // Include binary file
3147 printf("Not enough arguments to -i\n");
3154 // handle -ii (No truncation)
3155 if ((argv[i-3][2] == 'i') || (argv[i-3][2] == 'I'))
3158 printf("warning: (-ii) COFF format output not specified\n");
3160 // handle -i (Truncation)
3163 if (strlen(isym) > 8)
3167 // Place include files in the DATA segment only
3168 if (DoFile(ifile, DSTSEG_D, isym))
3173 case 'L': // Add local symbols
3180 case 'M': // Produce load symbol map
3187 case 'N': // Output no header to .abs file
3192 printf("Warning: -e overridden by -n, output will be headerless\n");
3197 case 'O': // Specify an output file
3205 printf("No output filename following -o switch\n");
3209 if (strlen(argv[i]) > FARGSIZE - 5)
3211 printf("Output file name too long (sorry!)\n");
3215 strcpy(ofile, argv[i++]);
3218 case 'R': // Section alignment size
3224 switch (argv[i-1][2])
3226 case 'w': case 'W': secalign = 1; break; // Word alignment
3227 case 'l': case 'L': secalign = 3; break; // Long alignment
3228 case 'p': case 'P': secalign = 7; break; // Phrase alignment
3229 case 'd': case 'D': secalign = 15; break; // Double phrase alignment
3230 case 'q': case 'Q': secalign = 31; break; // Quad phrase alignment
3231 default: secalign = 7; break; // Default phrase alignment
3236 case 'S': // Output only global symbols
3243 case 'U': // Undefined symbols
3247 case 'V': // Verbose information
3248 if (!vflag && !versflag)
3256 case 'W': // Show warnings flag
3266 printf("No directory filename following -y switch\n");
3270 if (strlen(argv[i]) > FARGSIZE * 3)
3272 printf("Directory file name too long (sorry!)\n");
3276 strcpy(libdir, argv[i++]);
3279 case 'Z': // Suppress banner flag
3286 printf("unknown option argument `%c'\n", c);
3292 // Not a switch, then process as a file
3293 if (DoFile(argv[i++], 0, NULL))
3298 if (!oflag && vflag)
3300 strcpy(ofile, "output");
3301 printf("Output file is %s[.ext]\n", ofile);
3305 printf("Output file is %s\n", ofile);
3310 // No problems encountered
3316 // Display version information
3318 void ShowVersion(void)
3320 if (displaybanner)// && vflag)
3328 "\nRenamed Linker for Atari Jaguar\n"
3329 "Copyright (c) 199x Allan K. Pratt, 2014-2021 Reboot & Friends\n"
3330 "V%i.%i.%i %s (%s)\n\n", MAJOR, MINOR, PATCH, __DATE__, PLATFORM);
3336 // Display command line help
3341 printf(" %s [-options] file(s)\n", cmdlnexec);
3343 printf("Options:\n");
3344 printf(" -? or -h display usage information\n");
3345 printf(" -a <text> <data> <bss> output absolute file (default: ABS)\n");
3346 printf(" hex value: segment address\n");
3347 printf(" r: relocatable segment\n");
3348 printf(" x[dt]: contiguous segment\n");
3349 printf(" for contiguous bss:\n");
3350 printf(" d(default): follows data segment\n");
3351 printf(" t: follows text segment\n");
3352 printf(" -b don't remove multiply defined local labels\n");
3353 printf(" -c <fname> add contents of <fname> to command line\n");
3354 printf(" -d wait for key after link\n");
3355 printf(" -e output COF absolute file\n");
3356 printf(" -g output source-level debugging\n");
3357 printf(" -i <fname> <label> incbin <fname> and set <label> (trunc to 8 chars)\n");
3358 printf(" -ii <fname> <label> incbin <fname> and set <label> (no truncation)\n");
3359 printf(" -l add local symbols\n");
3360 printf(" -m produce load symbols map\n");
3361 printf(" -n output no file header to absolute file (overrides -e)\n");
3362 printf(" -o <fname> set output name\n");
3363 printf(" -r<size> section alignment size\n");
3364 printf(" w: word (2 bytes)\n");
3365 printf(" l: long (4 bytes)\n");
3366 printf(" p: phrase (8 bytes, default alignment)\n");
3367 printf(" d: double phrase (16 bytes)\n");
3368 printf(" q: quad phrase (32 bytes)\n");
3369 printf(" -s output only global symbols (supresses -l)\n");
3370 printf(" -u allow unresolved symbols (experimental)\n");
3371 printf(" -v set verbose mode\n");
3372 printf(" -w show linker warnings\n");
3373 printf(" -y <fname> set include path (also set by RLNPATH)\n");
3374 printf(" -z suppress banner\n");
3382 void ExitLinker(void)
3386 // Display link status if verbose mode
3388 printf("Link %s.\n", errflag ? "aborted" : "complete");
3390 // Wait for return key if requested
3393 printf("\nPress the [RETURN] key to continue. ");
3394 char * c = fgets(tempbuf, 128, stdin);
3401 int main(int argc, char * argv[])
3403 cmdlnexec = argv[0]; // Obtain executable name
3404 char * s = getenv("RLNPATH"); // Attempt to obtain env variable
3407 strcpy(libdir, s); // Store it if found
3409 // Initialize some vars
3410 tval = dval = bval = 0;
3411 ttype = dtype = btype = 0;
3413 // Parse the command line
3414 if (doargs(argc, argv))
3420 // Check to see if include paths actually exist
3421 if (strlen(libdir) > 0)
3423 DIR * test = opendir(libdir);
3427 printf("Invalid include path: %s\n", libdir);
3435 if (!zflag && !vflag)
3437 ShowVersion(); // Display version information
3438 versflag = 1; // We've dumped the version banner
3441 // Load in specified files/objects and add to processing list
3448 // Work in items in processing list & deal with unresolved list
3455 // Check that there is something to link
3462 // Report unresolved externals
3463 if (unresolved != NULL)
3465 printf("UNRESOLVED SYMBOLS\n");
3467 // Don't list them if two -u's or more
3470 struct HREC * utemp = unresolved;
3472 while (utemp != NULL)
3474 printf("\t%s (", utemp->h_sym);
3475 WriteARName(utemp->h_ofile);
3477 utemp = utemp->h_next;
3488 // Make one output object from input objects
3489 struct OHEADER * header = MakeOutputObject();
3500 printf("TO DO: Partial linking\n");
3503 // Relocatable linking
3506 printf("TO DO: Relocatable linking\n");
3513 printf("Absolute linking (%s)\n", (cflag ? "COF" : "ABS"));
3516 printf("Header magic is 0x%04X\n", (unsigned int)header->magic);
3518 if (WriteOutputFile(header))
3522 // Display the loaded symbols map
3524 if (ShowSymbolLoadMap(header))
3527 // Display segment size summary
3531 printf("+---------+----------+----------+----------+\n");
3532 printf("| Segment | TEXT | DATA | BSS |\n");
3533 printf("| Sizes |----------+----------+----------|\n");
3534 printf("| (Hex) | %8X | %8X | %8X |\n", (unsigned int)header->tsize, (unsigned int)header->dsize, (unsigned int)header->bsize);
3535 printf("+---------+----------+----------+----------+\n\n");