]> Shamusworld >> Repos - rln/blobdiff - rln.c
Add Alcyon C object file support
[rln] / rln.c
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)
                        {