elspec.c 7.27 KB
#include <sys/types.h>

#include <errno.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

#ifndef __sgi__
#include <PR/ramrom.h>
#endif

#include "makerom.h"


////////////////////////////////////////////////////////////////////////////////
// createElspec()
//
// Create the "elspec" file which is used by the linker.  elspec is a file
//   which contains a series of commands to give more explicit instructions
//   to the linker than can be given on the command line.
//
int createElspec(Wave *wave) {
  FILE *f;
  
  SegmentChain *sc;
  Segment *s;
  Path *p;
  
  if ((f = fopen(wave->elspecFile, "w")) == NULL) {
    fprintf(stderr, "makerom: %s: cannot create\n",
	    wave->elspecFile);
    return(-1);
  }
  
#ifndef __sgi__
  fprintf(f, "PHDRS\n{\n");
  fprintf(f, "\theaders PT_PHDR FILEHDR PHDRS;\n");
  for (sc = wave->segmentChain; sc != NULL; sc = sc->next) {
    s = sc->segment;
    
    if ((s->flags & SEGFLAG_OBJECT) == 0)
      continue;
    fprintf(f, "\t_%s PT_LOAD;\n", s->name);
  }
  fprintf(f, "}\nSECTIONS\n{\n");

  for (sc = wave->segmentChain; sc != NULL; sc = sc->next) {
    s = sc->segment;

    
    if ((s->flags & SEGFLAG_OBJECT) == 0)
      continue;
    
    fprintf(f, "\t. = 0x%x;\n", s->address);
    if (s->flags & SEGFLAG_BOOT)
	fprintf(f, "\tENTRY(_%sSegmentTextStart)\n", s->name);
    fprintf(f, "\t.%s.text ", s->name);
    if (s->textAlign != 0)
      fprintf(f, "ALIGN(0x%x) ", s->textAlign);
    fprintf(f, ": {\n");
    for (p = s->pathList; p != NULL; p = p->next) {
      fprintf(f, "\t\t%s(.text)\n", p->name);
    }
    fprintf(f, "\t\t.%s.text.keep = ABSOLUTE(.);\n\t} : _%s\n", s->name, s->name);
    
    fprintf(f, "\t.%s.data ", s->name);
    if (s->dataAlign != 0)
      fprintf(f, "ALIGN(0x%x) ", s->dataAlign);
    fprintf(f, ": {\n");
    for (p = s->pathList; p != NULL; p = p->next) {
      fprintf(f, "\t\t%s(.data .rodata)\n", p->name);
    }
    fprintf(f, "\t\t.%s.data.keep = ABSOLUTE(.);\n\t}\n", s->name);
    
    fprintf(f, "\t.%s.sdata ", s->name);
    if (s->sdataAlign != 0)
      fprintf(f, "ALIGN(0x%x) ", s->sdataAlign);
    fprintf(f, ": {\n");
    for (p = s->pathList; p != NULL; p = p->next)
      fprintf(f, "\t\tKEEP(%s(.sdata .lit8 .lit4))\n", p->name);
    fprintf(f, "\t}\n");
    
    fprintf(f, "\t.%s.sbss ", s->name);
    if (s->sbssAlign != 0)
      fprintf(f, "ALIGN(0x%x) ", s->sbssAlign);
    fprintf(f, ": {\n");
    for (p = s->pathList; p != NULL; p = p->next)
      fprintf(f, "\t\t%s(.sbss .scommon)\n", p->name);
    fprintf(f, "\t}\n");
    
    fprintf(f, "\t.%s.bss ", s->name);
    if (s->bssAlign != 0)
      fprintf(f, "ALIGN(0x%x) ", s->bssAlign);
    fprintf(f, ": {\n");
    for (p = s->pathList; p != NULL; p = p->next)
      fprintf(f, "\t\t%s(.bss COMMON)\n", p->name);
    fprintf(f, "\t}\n");
  }
  fprintf(f, "\t.mdebug : { *(.mdebug) }\n");
  fprintf(f, "\t/DISCARD/ : { *(.note .reginfo .comment) }\n");
  fprintf(f, "}\n");
#else
  fprintf(f, "$ignoreoverlaps = true\n\n");
  for (sc = wave->segmentChain; sc != NULL; sc = sc->next) {
    s = sc->segment;
    
    if ((s->flags & SEGFLAG_OBJECT) == 0)
      continue;
    
    fprintf(f, "beginseg\n");
    fprintf(f, "\tsegtype LOAD\n");
    fprintf(f, "\tsegflags R X\n");
    fprintf(f, "\tvaddr 0x%x\n", s->address);
    fprintf(f, "\tcontents\n");
    
    fprintf(f, "\tbeginscn .%s.text\n", s->name);
    fprintf(f, "\t\tscntype PROGBITS\n");
    if (s->textAlign != 0)
      fprintf(f, "\t\tscnalign %d\n", s->textAlign);
    fprintf(f, "\t\tscnflags ALLOC EXECINSTR\n");
    for (p = s->pathList; p != NULL; p = p->next) {
      fprintf(f, "\t\tsection .text in object %s\n", p->name);
    }
    fprintf(f, "\tendscn\n");
    
    fprintf(f, "\tbeginscn .%s.data\n", s->name);
    fprintf(f, "\t\tscntype PROGBITS\n");
    if (s->dataAlign != 0)
      fprintf(f, "\t\tscnalign %d\n", s->dataAlign);
    fprintf(f, "\t\tscnflags ALLOC WRITE\n");
    for (p = s->pathList; p != NULL; p = p->next) {
      fprintf(f, "\t\tsection .data in object %s\n", p->name);
      fprintf(f, "\t\tsection .rodata in object %s\n", p->name);
    }
    fprintf(f, "\tendscn\n");
    
    fprintf(f, "\tbeginscn .%s.sdata\n", s->name);
    fprintf(f, "\t\tscntype PROGBITS\n");
    if (s->sdataAlign != 0)
      fprintf(f, "\t\tscnalign %d\n", s->sdataAlign);
    fprintf(f, "\t\tscnflags GPREL ALLOC WRITE\n");
    for (p = s->pathList; p != NULL; p = p->next)
      fprintf(f, "\t\tsection .sdata in object %s\n", p->name);
    fprintf(f, "\tendscn\n");
    
    fprintf(f, "\tbeginscn .%s.sbss\n", s->name);
    fprintf(f, "\t\tscntype NOBITS\n");
    if (s->sbssAlign != 0)
      fprintf(f, "\t\tscnalign %d\n", s->sbssAlign);
    fprintf(f, "\t\tscnflags GPREL ALLOC WRITE\n");
    for (p = s->pathList; p != NULL; p = p->next)
      fprintf(f, "\t\tsection .sbss in object %s\n", p->name);
    fprintf(f, "\tendscn\n");
    
    fprintf(f, "\tbeginscn .%s.bss\n", s->name);
    fprintf(f, "\t\tscntype NOBITS\n");
    if (s->bssAlign != 0)
      fprintf(f, "\t\tscnalign %d\n", s->bssAlign);
    fprintf(f, "\t\tscnflags ALLOC WRITE\n");
    for (p = s->pathList; p != NULL; p = p->next)
      fprintf(f, "\t\tsection .bss in object %s\n", p->name);
    fprintf(f, "\tendscn\n");
    fprintf(f, "endseg\n");
  }
  fprintf(f, "beginseg\n");
  fprintf(f, "\tsegtype noload\n");
  fprintf(f, "\tcontents\n");
  fprintf(f, "\tdefault\n");
  fprintf(f, "\tbeginscn .MIPS.options\n");
  fprintf(f, "\t\tscntype 0x7000000d\n");
  fprintf(f, "\t\tsection .MIPS.options in ldobj\n");
  fprintf(f, "\tendscn\n");
  fprintf(f, "\tbeginscn .reginfo\n");
  fprintf(f, "\t\tscntype 0x70000006\n");
  fprintf(f, "\t\tsection .reginfo in ldobj\n");
  fprintf(f, "\tendscn\n");
  fprintf(f, "endseg\n");
#endif
  fclose(f);
  
  return(0);
}


////////////////////////////////////////////////////////////////////////////////
// runLinker()
//
// Run the linker, using the elspec file and objectlist file which were 
//   generated previously.
//
int runLinker(Wave *wave, char *symbolFile, char *objListFile) {
  char *cmd;
  SegmentChain *sc;
  Segment *s;
  Path *p;
  FILE *objfd;
  
  if ((cmd = (char *)malloc(sysconf(_SC_ARG_MAX))) == NULL) {
    fprintf(stderr, "malloc failed\n");
    return(-1);
  }

  // We now always use nld.  In earlier releases we used the IDO 7.1
  //   linker, but it has bugs in processing an elspec file.  So we're
  //   back to nld.
#ifdef __sgi__
  strcpy(cmd, "$ROOT/usr/lib/PR/nld -g -non_shared -G 0 -elspec ");
#else
  strcpy(cmd, "mips-linux-ld -g --no-check-sections --no-warn-mismatch -non_shared -G 0 -T ");
#endif

  strcat(cmd, wave->elspecFile);
#ifdef __sgi__
  strcat(cmd, " -rom ");
#endif
  if (loadMap)
#ifdef __sgi__
    strcat(cmd, " -m ");
#else
    strcat(cmd, " -M ");
#endif
  strcat(cmd, " -o ");
  strcat(cmd, wave->name);
  strcat(cmd, " ");
  strcat(cmd, symbolFile);
  
#ifdef __sgi__
  strcat(cmd, " -objectlist ");
  strcat(cmd, objListFile);
#endif
  
  // Write out the temporary objectList file.
  objfd = fopen(objListFile, "w");
  for (sc = wave->segmentChain; sc != NULL; sc = sc->next) {
    s = sc->segment;
    if ((s->flags & SEGFLAG_OBJECT) == 0)
      continue;
    for (p = s->pathList; p != NULL; p = p->next) {
      fprintf(objfd, "%s\n", p->name);
    }
  }
  fclose(objfd);

  if (debug) {
    printf("Linking to ELF wave file\n");
    printf("  %s\n", cmd);
  }
  return(execCommand(cmd));
}