]> Shamusworld >> Repos - rln/commitdiff
Add Alcyon C object file support
authorJames Jones <atari@theinnocuous.com>
Sat, 18 Apr 2020 09:45:06 +0000 (02:45 -0700)
committerShamus Hammons <jlhamm@acm.org>
Tue, 16 Aug 2022 02:27:58 +0000 (21:27 -0500)
Support ingesting the DRI Alcyon C object file
format in addition to a.out object files. The load
file routine simply translates the Alcyon object
file into an a.out file before passing it on to
the existing processing workflow.

The motivation here was to enable linking of the
binary-only cinepak decompression GPU routines
provided in the developer files. ALN can handle
these files just fine, and now RLN can too.

However, that is a very simple object files To
fully exercise the relocation table translation
code, contrived assembly files with all types of
relocations were assembled as Alcyon C object
files using MADMAC and BSD/a.out object files
using RMAC (Note RMAC had to be used to generate
the a.out files because MADMAC generates invalid
a.out relocations for certain relocation types
that it handles fine in Alcyon format), and then
both object files were linked as COFF executables
using RLN. The resulting COFF files were verified
to be identical.

rln.c
rln.h

diff --git a/rln.c b/rln.c
index 78ed41053bbc2a6f8b43d5a68c50f186987f2818..4a83410238337b3669ce8178ec012e56c7913d21 100644 (file)
--- a/rln.c
+++ b/rln.c
@@ -671,11 +671,11 @@ int RelocateSegment(struct OFILE * ofile, int flag)
                // object file image
                addr = GetLong(rptr);
                rflg = GetLong(rptr + 4);
-               glblreloc = (rflg & 0x00000010 ? 1 : 0);// Set global relocation flag
-               absreloc  = (rflg & 0x00000040 ? 1 : 0); // Set absolute relocation flag
-               relreloc  = (rflg & 0x000000A0 ? 1 : 0); // Set relative relocation flag
-               wordreloc = (rflg & 0x00000002 ? 1 : 0); // Set word relocation flag
-               opreloc   = (rflg & 0x00000004 ? 1 : 0); // Set OP relocation flag
+               glblreloc = (rflg & BSDREL_GLOBAL ? 1 : 0);
+               absreloc  = (rflg & BSDREL_ABS ? 1 : 0);
+               relreloc  = (rflg & BSDREL_PCREL ? 1 : 0);
+               wordreloc = (rflg & BSDREL_WORD ? 1 : 0);
+               opreloc   = (rflg & BSDREL_OP ? 1 : 0);
 
                // Additional processing required for global relocations
                if (glblreloc)
@@ -684,7 +684,7 @@ int RelocateSegment(struct OFILE * ofile, int flag)
                        // for it in the globals hash table to obtain information on that
                        // symbol. For the hash calculation to work correctly it must be
                        // placed in a 'clean' string before looking it up.
-                       symidx = GetLong(symtab + ((rflg >> 8) * 12));
+                       symidx = GetLong(symtab + (BSDREL_SYMIDX(rflg) * 12));
                        memset(sym, 0, SYMLEN);
                        strcpy(sym, symbols + symidx);
                        olddata = newdata = 0;   // Initialise old and new segment data
@@ -709,32 +709,32 @@ int RelocateSegment(struct OFILE * ofile, int flag)
                        olddata |= saveBits2; // Restore upper 3 bits of data addr
                }
 
-               if (rflg & 0x01)
+               if (rflg & BSDREL_MOVEI)
                        olddata = _SWAPWORD(olddata);
 
                // Process record dependant on segment it relates to; TEXT, DATA or
                // BSS. Construct a new relocated segment long word based on the
                // required segment base address, the segment data offset in the
                // resulting COF file and the offsets from the incoming object file.
-               swcond = (rflg & 0xFFFFFF00);
+               swcond = (rflg & BSDREL_SEGMASK);
 
                if (!glblreloc)
                {
                        switch (swcond)
                        {
-                       case 0x00000200:          // Absolute Value
+                       case BSDREL_SEG_ABS:
                                break;
-                       case 0x00000400:          // TEXT segment relocation record
+                       case BSDREL_SEG_TEXT:
                                // SCPCD : the symbol point to a text segment, we should use the textoffset
                                newdata = tbase + textoffset + olddata;
 
                                break;
-                       case 0x00000600:          // DATA segment relocation record
+                       case BSDREL_SEG_DATA:
                                newdata = dbase + dataoffset
                                        + (olddata - ofile->o_header.tsize);
 
                                break;
-                       case 0x00000800:          // BSS segment relocation record
+                       case BSDREL_SEG_BSS:
                                newdata = bbase + bssoffset
                                        + (olddata - (ofile->o_header.tsize
                                        + ofile->o_header.dsize));
@@ -754,7 +754,7 @@ int RelocateSegment(struct OFILE * ofile, int flag)
                        // Flip the new long word segment data if the relocation record
                        // indicated a RISC MOVEI instruction and place the resulting data
                        // back in the COF segment
-                       if (rflg & 0x01)
+                       if (rflg & BSDREL_MOVEI)
                                newdata = _SWAPWORD(newdata);
 
                        if (wordreloc)
@@ -2375,6 +2375,280 @@ int LoadObject(char * fname, int fd, char * ptr)
        return AddToProcessingList(ptr, fname, nullStr, 0, tSize, dSize, bSize);
 }
 
+uint32_t SymTypeAlcToAout(uint32_t alcType)
+{
+       uint32_t type = T_UNDF;
+
+       // Symbol type mappings here are derived from comparing Alcyon and BSD
+       // object files generated by MADMAC using the "size" utility from jag_utils
+       // (https://github.com/cubanismo/jag_utils) and the old PC/DOS Atari SDK.
+       //
+       // In BSD/a.out:
+       //
+       // 1)   text | global text == text relocatable symbol in this file
+       // 2)   data | global data == data relocatable symbol in this file
+       // 3)   BSS  | global BSS  == bss relocatable symbol in this file
+       // 4)   ABS  | global ABS  == non-relocatable symbol in this file
+       // 4) <none> | global <none> == undefined global symbol (extern)
+       //
+       // In DRI/Alcyon:
+       //
+       // 1) Everything seems to be marked defined.
+       // 2) There is an explicit "external" bit. It appears to be mutually
+       //    exclusive with the "global" bit, at least in MADMAC's output.
+       // 3) There are separate "equated" and "equated reg" type bits that
+       //    both represent ABS/non-relocatable values.
+       if ((alcType & ALCSYM_EQUATED) ||
+               (alcType & ALCSYM_EQUATED_REG))
+               type |= T_ABS;
+       else if (alcType & ALCSYM_TEXT)
+               type |= T_TEXT;
+       else if (alcType & ALCSYM_DATA)
+               type |= T_DATA;
+       else if (alcType & ALCSYM_BSS)
+               type |= T_BSS;
+
+       if ((alcType & ALCSYM_GLOBAL) ||
+               (alcType & ALCSYM_EXTERN))
+               type |= T_GLBL;
+
+       return type;
+}
+
+int LoadAlcyon(char * fname, int fd)
+{
+       char *ptr, *sptr, *aout, *saout, *traout, *strPtr;
+    char *trelptr, *drelptr, *relend;
+       struct ALCHEADER hdr;
+       struct ALCSYM *alcSyms;
+       long size = FileSize(fd);
+       size_t symStrLen;
+       size_t strOff;
+       uint32_t numSyms, numTRel, numDRel, i, j;
+
+       // Validate the file is at least large enough to contain a valid header
+       if (size < 0x1c)
+       {
+               printf("Alcyon object file %s too small to contain header\n", fname);
+               return 1;
+       }
+
+       // Allocate memory for file data
+       ptr = malloc(size);
+
+       if (ptr == NULL)
+       {
+               printf("Out of memory while processing %s\n", fname);
+               close(fd);
+               return 1;
+       }
+
+       // Read in file data
+       if (read(fd, ptr, size) != size)
+       {
+               printf("File read error on %s\n", fname);
+               close(fd);
+               free(ptr);
+               return 1;
+       }
+
+       close(fd);
+
+       hdr.magic = GetWord(ptr);
+       hdr.tsize = GetLong(ptr + 2);
+       hdr.dsize = GetLong(ptr + 6);
+       hdr.bsize = GetLong(ptr + 10);
+       hdr.ssize = GetLong(ptr + 14);
+
+       // Construct a BSD-style/aout object file in memory from the Alcyon data
+       numSyms = hdr.ssize / 14;
+
+       alcSyms = calloc(numSyms, sizeof(*alcSyms));
+       if (alcSyms == NULL)
+       {
+               printf("Out of memory while processing %s\n", fname);
+               free(ptr);
+               return 1;
+       }
+
+       sptr = ptr + 0x1c + hdr.tsize + hdr.dsize;
+       trelptr = sptr + hdr.ssize;
+       drelptr = trelptr + hdr.tsize;
+       relend = drelptr + hdr.dsize;
+
+       if (relend - ptr > size)
+       {
+               printf("Alcyon object file %s truncated: Header wants %ldB, file is %ldB\n",
+                          fname, relend - ptr, size);
+               return 1;
+       }
+
+       for (i = 0, symStrLen = 0; i < numSyms; i++)
+       {
+               memcpy(alcSyms[i].name, sptr, 8);
+               alcSyms[i].type = GetWord(sptr + 8);
+               alcSyms[i].value = GetLong(sptr + 10);
+               symStrLen += strnlen((char *)alcSyms[i].name, 8) + 1;
+               sptr += 14;
+       }
+
+       for (i = 0, numTRel = 0; trelptr + i < drelptr; i += 2)
+       {
+               uint16_t rel = GetWord(trelptr + i);
+               if ((rel != ALCREL_ABS) &&
+                       (rel != ALCREL_LONG))
+                       numTRel++;
+       }
+
+       for (i = 0, numDRel = 0; drelptr + i < relend; i += 2)
+       {
+               uint16_t rel = GetWord(drelptr + i);
+               if ((rel != ALCREL_ABS) &&
+                       (rel != ALCREL_LONG))
+                       numDRel++;
+       }
+
+       aout = malloc(32 + /* header */
+                                 hdr.tsize +
+                                 hdr.dsize +
+                                 numTRel * 8 + /* Text section relocations */
+                                 numDRel * 8 + /* Data section relocations */
+                                 numSyms * 12 + /* symbol table */
+                                 4 + symStrLen + /* string table size + strings */
+                                 4 /* NULL-terminator for file */);
+       if (aout == NULL)
+       {
+               printf("Out of memory while processing %s\n", fname);
+               free(alcSyms);
+               free(ptr);
+               return 1;
+       }
+
+       // Construct the BSD/a.out header.
+       PutLong(aout, 0x00000107);                              // Magic number
+
+       PutLong(aout+4, hdr.tsize);                             // Text size
+       PutLong(aout+8, hdr.dsize);                             // Data size
+       PutLong(aout+12, hdr.bsize);                    // BSS size
+       PutLong(aout+16, numSyms * 12);                 // Symbol table size
+       PutLong(aout+20, 0L);                                   // Entry point
+
+       PutLong(aout+24, numTRel * 8);                  // TEXT relocation size
+       PutLong(aout+28, numDRel * 8);                  // DATA relocation size
+
+       // Copy the raw text and data segments
+       memcpy(aout + 32, ptr + 0x1c, hdr.tsize);
+       memcpy(aout + 32 + hdr.tsize, ptr + 0x1c + hdr.tsize, hdr.dsize);
+
+       // Set traout to the start of the relocation tables
+       traout = aout + 32 + hdr.tsize + hdr.dsize;
+
+       // Set saout to symbol table location
+       saout = traout + numTRel * 8 + numDRel * 8 ;
+
+       // Convert the text and data relocations to a.out format
+       for (i = 0; trelptr + i < relend; i += 2)
+       {
+               uint16_t rel = GetWord(trelptr + i);
+               uint16_t relFlags = rel & 7;
+               uint32_t aoutflags = BSDREL_ABS;
+               uint32_t valoffset = 0;
+               char *const valaddr = aout + 32 + i;
+               const uint32_t reladdr = (trelptr + i >= drelptr) ? i - hdr.tsize : i;
+
+               if (relFlags == ALCREL_LONG)
+               {
+                       i += 2;
+                       rel = GetWord(trelptr + i);
+                       relFlags = rel & 7;
+               }
+               else
+               {
+                       aoutflags |= BSDREL_WORD;
+               }
+
+               if (relFlags == ALCREL_ABS)
+                       continue;
+
+               switch (relFlags) {
+               case ALCREL_EXTPCREL:
+            aoutflags &= ~BSDREL_ABS;
+                       aoutflags |= BSDREL_PCREL;
+            /* Fall through */
+               case ALCREL_EXTABS:
+                       aoutflags |= BSDREL_GLOBAL;
+            aoutflags |= (ALCREL_SYMIDX(rel) << BSDREL_SYMIDX_SHIFT);
+                       break;
+               case ALCREL_TEXT:
+                       aoutflags |= BSDREL_SEG_TEXT;
+                       break;
+               case ALCREL_DATA:
+                       aoutflags |= BSDREL_SEG_DATA;
+                       valoffset = hdr.tsize;
+                       break;
+               case ALCREL_BSS:
+                       aoutflags |= BSDREL_SEG_BSS;
+                       valoffset = hdr.tsize + hdr.dsize;
+                       break;
+
+               default:
+                       printf("Invalid Alcyon relocation flags: 0x%02x\n", relFlags);
+                       free(alcSyms);
+                       free(ptr);
+                       free(aout);
+                       return 1;
+               }
+
+               if (valoffset != 0)
+               {
+                       if (aoutflags & BSDREL_WORD)
+                       {
+                               valoffset += GetWord(valaddr);
+                               PutWord(valaddr, (uint16_t)valoffset);
+                       }
+                       else
+                       {
+                               valoffset += GetLong(valaddr);
+                               PutLong(valaddr, valoffset);
+                       }
+               }
+
+               PutLong(traout,         reladdr);
+               PutLong(traout+4,       aoutflags);
+               traout += 8;
+       }
+
+       // Done with the Alcyon data.
+       free(ptr);
+       ptr = NULL;
+       sptr = NULL;
+
+       // Set strPtr to string table location and write string table size
+       strPtr = saout + numSyms * 12;
+       PutLong(strPtr, 4 + symStrLen);
+
+       for (i = 0, strOff = 4; i < numSyms; i++)
+       {
+               PutLong(saout,    strOff);           // String offset of symbol
+               PutLong(saout+4,  SymTypeAlcToAout(alcSyms[i].type)); // Symbol type
+               PutLong(saout+8,  alcSyms[i].value); // Symbol value
+               saout += 12;
+
+               for (j = 0; j < 8 && alcSyms[i].name[j] != '\0'; j++)
+                       *(strPtr + strOff + j) = alcSyms[i].name[j];
+               strOff += j;                         // Step past string
+               *(strPtr + strOff) = '\0';           // Terminate symbol string
+               strOff++;                            // Step past termination
+       }
+
+       PutLong(strPtr + strOff, 0L);          // Terminating long for object file
+
+       // Done with the Alcyon symbol table.
+       free(alcSyms);
+
+       // Now add this image to the list of pending ofiles (plist)
+       return AddToProcessingList(aout, fname, nullStr, 0, hdr.tsize, hdr.dsize, hdr.bsize);
+}
 
 //
 // What it says on the tin: check for a .o suffix on the passed in string
@@ -2539,6 +2813,13 @@ int ProcessFiles(void)
                                if (LoadObject(name[i], handle[i], 0L))
                                        return 1;
                        }
+                       // Look for DRI Alcyon C (and old MAC) object files
+                       else if (GetWord(magic) == 0x601A)
+                       {
+                               // Process Alcyon object file.
+                               if (LoadAlcyon(name[i], handle[i]))
+                                       return 1;
+                       }
                        // Otherwise, look for an object archive file
                        else if (strncmp(magic, "!<arch>\x0A", 8) == 0)
                        {
diff --git a/rln.h b/rln.h
index 8409102d6a4404ba42ce447f1f352d9cb0da4c42..ebb575ce4ecb70712fcd14f4fc4af00e62ca804c 100644 (file)
--- a/rln.h
+++ b/rln.h
 #include <stdint.h>
 #include <dirent.h>
 
+// Alcyon object file header structures.
+//
+// Same as an Atari ST/GEMDOS/TOS executable file header.
+//
+// References:
+//  http://cd.textfiles.com/ataricompendium/BOOK/HTML/CHAP2.HTM#processes
+//  https://mikro.naprvyraz.sk/docs/GEM/GEMDOS.TXT
+//  MADMAC source from Landon Dyer (See below for URL)
+//
+// Note the above disagree on the last entry in the header layout: In practice
+// the files MADMAC produces and that ALN consumes use the header layout from
+// the Atari Compendium page.
+struct ALCHEADER
+{
+       uint16_t magic;                                 // $601A
+       uint32_t tsize;                                 // text segment size
+       uint32_t dsize;                                 // data segment size
+       uint32_t bsize;                                 // BSS segment size
+       uint32_t ssize;                                 // symbol table size
+       uint32_t reserved0;                             // unused
+       uint32_t reserved1;                             // unused
+       uint16_t absflag;                               // Always '0' (relocatable) for obj files
+};
+
+// Alcyon/DRI symbol type bits - From size.c:show_dri_symbol_type() in
+// https://github.com/cubanismo/jag_utils, as well as
+// the GEMDOS Reference Manual, 4/4/86, "Executable Files" section, in the
+// table "Values For Symbol Types," available various places, e.g.,
+// https://mikro.naprvyraz.sk/docs/GEM/GEMDOS.TXT
+#define ALCSYM_DEFINED         0x8000
+#define ALCSYM_EQUATED         0x4000
+#define ALCSYM_GLOBAL          0x2000
+#define ALCSYM_EQUATED_REG     0x1000
+#define ALCSYM_EXTERN          0x0800
+#define ALCSYM_DATA                    0x0400
+#define ALCSYM_TEXT                    0x0200
+#define ALCSYM_BSS                     0x0100
+
+// Alcyon/DRI symbol relocation flags - Derived from mark.c in the
+// original MADMAC sources released by Landon Dyer on his blog:
+//
+// https://dadhacker-125488.ingress-alpha.ewp.live/
+//
+// At this link from his September 2nd, 2008 post:
+//
+// http://www.dadhacker.com/Downloads/madmac.zip
+//
+// Downloaded from the Internet Archive Wayback Machine November 2015
+// snapshot of the above URL, here:
+//
+// https://web.archive.org/web/20151120225539/http://www.dadhacker.com/Downloads/madmac.zip
+//
+// Another valuable source of information on the Alcyon relocation data,
+// and the Alcyon/DRI C object file format in general is the Sozobon 2.0
+// source code:
+//
+// https://sourceforge.net/projects/sozobon/files/
+//
+// Sozobon is a reimplementation of the original Atari Alcyon C compiler
+// suite that can generate and link Alcyon/DRI object files compatible
+// with the original. Take a look at the jas/cpy.c file for an alternate
+// implementation of Alcyon/DRI relocation section data generation than
+// the one in MADMAC, as well as an alternate Alcyon/DRI relocation
+// data processing implementation in ld/rel.c.
+#define ALCREL_ABS                     0x0000  // No relocation at this location
+#define ALCREL_DATA                    0x0001  // Local address from data segment
+#define ALCREL_TEXT                    0x0002  // Local address from text segment
+#define ALCREL_BSS                     0x0003  // Local address from BSS segment
+#define ALCREL_EXTABS          0x0004  // External fixup: Absolute address
+#define ALCREL_LONG                    0x0005  // Relocation type is in next word
+#define ALCREL_EXTPCREL                0x0006  // External fixup: PC relative address
+#define ALCREL_SYMIDX(rval)    ((rval) >> 3)   // 0-based index of ext fixup symbol
+
+struct ALCSYM
+{
+       uint8_t name[8];                                // fixed-size, padded with zeros.  NOT NUL-terminated!
+       uint16_t type;                                  // symbol type mask, from ALCSYM_* flags above.
+       uint32_t value;                                 // value
+};
+
 struct OHEADER
 {
        uint32_t magic;                                 // $0107 for .o, $601B for .abs
@@ -111,6 +191,21 @@ struct OHEADER
 
 #define new_oheader()   (struct OHEADER *)malloc(sizeof(struct OHEADER))
 
+// BSD/a.out object relocation flags.
+#define BSDREL_MOVEI           0x00000001      // Word-swapped (I.e., JRISC movei) long
+#define BSDREL_WORD                    0x00000002
+#define BSDREL_OP                      0x00000004      // Object Processor relocation
+#define BSDREL_GLOBAL          0x00000010      // AKA external reference
+#define BSDREL_ABS                     0x00000040
+#define BSDREL_PCREL           0x000000A0      // Note this implies BSDREL_GLOBAL
+#define BSDREL_SYMIDX_SHIFT    8                       // Bits to shift
+#define BSDREL_SYMIDX(_rf)     ((_rf) >> BSDREL_SYMIDX_SHIFT)
+#define BSDREL_SEGMASK         0xFFFFFF00
+#define BSDREL_SEG_ABS         0x00000200
+#define BSDREL_SEG_TEXT                0x00000400
+#define BSDREL_SEG_DATA                0x00000600
+#define BSDREL_SEG_BSS         0x00000800
+
 struct ARHEADER
 {
        uint8_t a_fname[14];