From: James Jones Date: Sat, 18 Apr 2020 09:45:06 +0000 (-0700) Subject: Add Alcyon C object file support X-Git-Tag: v1.7.2~1 X-Git-Url: http://shamusworld.gotdns.org/cgi-bin/gitweb.cgi?p=rln;a=commitdiff_plain;h=304cdee3e0d3253c22eb380a76e30b32b720bb77 Add Alcyon C object file support 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. --- diff --git a/rln.c b/rln.c index 78ed410..4a83410 100644 --- 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) | global == 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, "!\x0A", 8) == 0) { diff --git a/rln.h b/rln.h index 8409102..ebb575c 100644 --- a/rln.h +++ b/rln.h @@ -83,6 +83,86 @@ #include #include +// 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];