/* Output tables which say what global initializers need to be called at program startup, and what global destructors need to be called at program termination, for GNU C++ compiler. Copyright (C) 1987 Free Software Foundation, Inc. Hacked by Michael Tiemann (tiemann@mcc.com) COFF Changes by Dirk Grunwald (grunwald@flute.cs.uiuc.edu) This file is part of GNU CC. GNU CC is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to anyone for the consequences of using it or for whether it serves any particular purpose or works at all, unless he says so in writing. Refer to the GNU CC General Public License for full details. Everyone is granted permission to copy, modify and redistribute GNU CC, but only under the conditions described in the GNU CC General Public License. A copy of this license is supposed to have been given to you along with GNU CC so you can know your rights and responsibilities. It should be in a file named COPYING. Among other things, the copyright notice and this notice must be preserved on all copies. */ /* This file contains all the code needed for the program `collect'. `collect' is run on all object files that are processed by GNU C++, to create a list of all the file-level initialization and destruction that need to be performed. It generates an assembly file which holds the tables which are walked by the global init and delete routines. The format of the tables are an integer length, followed by the list of function pointers for the routines to be called. Constructors are called in the order they are laid out in the table. Destructors are called in the reverse order of the way they lie in the table. */ #include "config.h" /* * This may not always be true, but I can't find another flag * to use. */ #ifdef SDB_DEBUGGING_INFO #define COFF #define UMAX /* need to define this conditionally */ #endif /* For this file, some special macros. These should be merged into tm.h some time. */ #undef ASM_OUTPUT_INT #undef ASM_OUTPUT_LABELREF #if defined(COFF) #define ASM_OUTPUT_INT(FILE,VALUE) \ fprintf (FILE, "\t.double %d\n", VALUE) #define ASM_OUTPUT_PTR_INT_SUM(FILE,PTRNAME,VALUE) \ fprintf (FILE, "\t.double _%s+%d\n", PTRNAME, VALUE) #define ASM_OUTPUT_LABELREF(FILE,NAME) \ fprintf (FILE, "\t.double _%s\n", NAME); #else #if defined(hp9000s300) #define ASM_OUTPUT_INT(FILE,VALUE) \ fprintf (FILE, "\tlong %d\n", VALUE) #define ASM_OUTPUT_PTR_INT_SUM(FILE,PTRNAME,VALUE) \ fprintf (FILE, "\tlong _%s+%d\n", PTRNAME, VALUE) #define ASM_OUTPUT_LABELREF(FILE,NAME) \ fprintf (FILE, "\tlong _%s\n", NAME); #else #define ASM_OUTPUT_INT(FILE,VALUE) \ fprintf (FILE, "\t.long %d\n", VALUE) #define ASM_OUTPUT_PTR_INT_SUM(FILE,PTRNAME,VALUE) \ fprintf (FILE, "\t.long _%s+%d\n", PTRNAME, VALUE) #define ASM_OUTPUT_LABELREF(FILE,NAME) \ fprintf (FILE, "\t.long _%s\n", NAME); #endif /* hp9000s300 */ #endif /* COFF */ #include #include #include #include #include #ifdef UMAX #include #endif extern int xmalloc (); extern void free (); #ifndef CTOR_TABLE_NAME #define CTOR_TABLE_NAME "__CTOR_LIST__" #endif #ifndef DTOR_TABLE_NAME #define DTOR_TABLE_NAME "__DTOR_LIST__" #endif enum error_code { OK, BAD_MAGIC, NO_NAMELIST, FOPEN_ERROR, FREAD_ERROR, FWRITE_ERROR, RANDOM_ERROR, }; enum error_code process (); enum error_code process_a (), process_o (); enum error_code coalesce (); void assemble_name (); /* Files for holding the assembly code for table of constructor function pointer addresses and list of destructor function pointer addresses. */ static FILE *outfile; /* Default outfile name, or take name from argv with -o option. */ static char *outfile_name = "a.out"; /* The table of destructors elements must be laid out in the reverse order that the table of constructors is laid out. Thus, is easiest to just cons up a list of destructors to call, and then write them out when they have all been collected. */ struct dtor_list_elem { struct dtor_list_elem *next; char *name; } *dtor_chain; main (argc, argv) int argc; char *argv[]; { int i, nerrs = 0; enum error_code code; FILE *fp; char *main_input_filename; if (argc > 2 && !strcmp (argv[1], "-o")) { outfile_name = argv[2]; i = 3; } else i = 1; if ((outfile = fopen (outfile_name, "w")) == NULL) { perror ("collect"); exit (-1); } main_input_filename = outfile_name; ASM_FILE_START(outfile); fprintf (outfile, "%s\n", TEXT_SECTION_ASM_OP); ASM_GLOBALIZE_LABEL (outfile, CTOR_TABLE_NAME); ASM_OUTPUT_LABEL (outfile, CTOR_TABLE_NAME); for (; i < argc; i++) { char buf[80]; /* This is a library, skip it. */ if (argv[i][0] == '-' && argv[i][1] == 'l') continue; if ((fp = fopen (argv[i], "r")) == NULL) { sprintf (buf, "collect `%s'", argv[i]); perror (buf); exit (-1); } switch (code = process (fp, argv[i])) { case OK: break; case BAD_MAGIC: fprintf (stderr, "file `%s' has a bad magic number for collect\n", argv[i]); exit (-1); case NO_NAMELIST: fprintf (stderr, "file `%s' has a no namelist for collect\n", argv[i]); exit (-1); case RANDOM_ERROR: fprintf (stderr, "random error while processing file `%s':\n", argv[i]); perror ("collect"); exit (-1); case FOPEN_ERROR: fprintf (stderr, "fopen(3S) error while processing file `%s' in collect\n", argv[i]); exit (-1); case FREAD_ERROR: fprintf (stderr, "fread(3S) error while processing file `%s' in collect\n", argv[i]); exit (-1); case FWRITE_ERROR: fprintf (stderr, "fwrite(3S) error while processing file `%s' in collect\n", argv[i]); exit (-1); default: abort (); } fclose (fp); } switch (code = coalesce ()) { case OK: fclose (outfile); exit (0); case FREAD_ERROR: perror ("fread(3S) failed in collect, at end"); break; case FWRITE_ERROR: perror ("fwrite(3S) failed in collect, at end"); break; case FOPEN_ERROR: perror ("fopen(3S) failed in collect, at end"); break; case RANDOM_ERROR: fprintf (stderr, "random error in collect, at end"); break; } exit (-1); } #ifdef COFF #include enum error_code process (fp, filename) FILE *fp; char *filename; { LDFILE *ldptr; do { if ((ldptr = ldopen(filename, ldptr)) != NULL ) { if (!ISCOFF( HEADER(ldptr).f_magic ) ) { return BAD_MAGIC; } else { int symbols = HEADER(ldptr).f_nsyms; int symindex; for (symindex = 0; symindex < symbols; symindex++ ) { SYMENT symbol; char *symbol_name; extern char *ldgetname(); ldtbread(ldptr, symindex, &symbol); symbol_name = ldgetname(ldptr, &symbol); if (! strncmp ("__GLOBAL_$", symbol_name, 10)) { if (symbol_name[10] == 'I') { ASM_OUTPUT_LABELREF (outfile, symbol_name+1); } else { struct dtor_list_elem *new = (struct dtor_list_elem *) xmalloc (sizeof (struct dtor_list_elem)); new->next = dtor_chain; new->name = (char *)xmalloc (strlen (symbol_name)); strcpy (new->name, symbol_name+1); dtor_chain = new; } } } } } else { return( RANDOM_ERROR ); } } while ( ldclose(ldptr) == FAILURE ) ; return ( OK ); } /****** taken from sdbout.c ******/ /* Tell the assembler the source file name. On systems that use SDB, this is done whether or not -g, so it is called by ASM_FILE_START. ASM_FILE is the assembler code output file, INPUT_NAME is the name of the main input file. */ /* void */ sdbout_filename (asm_file, input_name) FILE *asm_file; char *input_name; { int len = strlen (input_name); char *na = input_name + len; /* NA gets INPUT_NAME sans directory names. */ while (na > input_name) { if (na[-1] == '/') break; na--; } #ifdef ASM_OUTPUT_SOURCE_FILENAME ASM_OUTPUT_SOURCE_FILENAME (asm_file, na); #else fprintf (asm_file, "\t.file\t\"%s\"\n", na); #endif } #else /* Figure out the type of file we need to process. Currently, only .o and .a formats are acceptable. */ enum error_code process (fp, filename) FILE *fp; char *filename; { struct stat file_stat; union { char ar_form[SARMAG]; struct exec a_out_form; } header; int size; if (fstat (fp->_file, &file_stat)) return RANDOM_ERROR; size = file_stat.st_size; if (fread (header.ar_form, SARMAG, 1, fp) < 1) return RANDOM_ERROR; if (strncmp (ARMAG, header.ar_form, SARMAG)) { fseek (fp, 0, 0); if (fread (&header.a_out_form, sizeof (struct exec), 1, fp) < 1) return RANDOM_ERROR; if (N_BADMAG (header.a_out_form)) return BAD_MAGIC; return process_o (fp, &header.a_out_form, size); } return process_a (fp); } enum error_code process_o (fp, header, size) FILE *fp; struct exec *header; int size; { int symoff, symend; #ifndef hp9000s300 struct nlist *nelem, *nelems, *nend; char *strtab; #else struct nlist_ *nelem, *nelems, *nend; #endif /* hp9000s300 */ if (N_BADMAG (*header)) return BAD_MAGIC; #ifndef hp9000s300 symoff = N_SYMOFF (*header); symend = N_STROFF (*header); #else symoff = LESYM_OFFSET (*header); symend = DNTT_OFFSET (*header); #endif /* hp9000s300 */ if (symoff == symend) return NO_NAMELIST; fseek (fp, symoff - sizeof (struct exec), 1); #ifndef hp9000s300 nelems = (struct nlist *)alloca (symend - symoff); #else nelems = (struct nlist_ *)alloca (symend - symoff); #endif /* hp9000s300 */ if (fread (nelems, sizeof (char), symend - symoff, fp) < symend - symoff) return FREAD_ERROR; #ifndef hp9000s300 strtab = (char *)alloca ((char *)size - (char *)symend); if (fread (strtab, sizeof (char), (char *)size - (char *)symend, fp) < ((char *)size - (char *)symend) * sizeof (char)) return FREAD_ERROR; nend = (struct nlist *)((char *)nelems + symend - symoff); for (nelem = nelems; nelem < nend; nelem++) #else nend = (struct nlist_ *)((char *)nelems + symend - symoff); for (nelem = nelems; nelem < nend; ) #endif /* hp9000s300 */ { #ifndef hp9000s300 int strindex = nelem->n_un.n_strx; #else int symlen = nelem->n_length; char p[255]; memcpy(p, (char *) (++nelem), symlen); p[symlen]='\0'; /* printf("'%s'\n",p); */ #endif /* hp9000s300 */ #ifndef hp9000s300 if (strindex) #else nelem = (struct nlist_ *)((char *)nelem + symlen); if (symlen) #endif /* hp9000s300 */ { #ifndef hp9000s300 char *p = strtab+strindex; #endif /* hp9000s300 */ if (! strncmp ("__GLOBAL_$", p, 10)) { if (p[10] == 'I') { ASM_OUTPUT_LABELREF (outfile, p+1); } else { struct dtor_list_elem *new = (struct dtor_list_elem *)xmalloc (sizeof (struct dtor_list_elem)); new->next = dtor_chain; new->name = (char *)xmalloc (strlen (p)); strcpy (new->name, p+1); dtor_chain = new; } } } return OK; } enum error_code process_a (fp) FILE *fp; { struct ar_hdr header; struct exec exec_header; int size; enum error_code code; while (! feof (fp)) { char c; #ifdef hp9000s300 int curpos; #endif /* hp9000s300 */ if (fread (&header, sizeof (struct ar_hdr), 1, fp) < 1) return RANDOM_ERROR; size = atoi (header.ar_size); #ifdef hp9000s300 curpos = ftell(fp); #endif /* hp9000s300 */ #ifndef hp9000s300 if (fread (&exec_header, sizeof (struct exec), 1, fp) < 1) return RANDOM_ERROR; #else /* if the name starts with /, it's an index file */ if (header.ar_name[0] != '/') { if (fread (&exec_header, sizeof (struct exec), 1, fp) < 1) return RANDOM_ERROR; #endif /* hp9000s300 */ code = process_o (fp, &exec_header, size); if (code != OK) return code; #ifdef hp9000s300 } if (fseek(fp,(long) curpos + size,0)) return RANDOM_ERROR; #endif /* hp9000s300 */ if ((c = getc (fp)) == '\n') ; else ungetc (c, fp); c = getc (fp); if (c != EOF) ungetc (c, fp); } return OK; } #endif enum error_code coalesce () { int dtor_offset; /* NULL-terminate the list of constructors. */ ASM_OUTPUT_INT (outfile, 0); /* Now layout the destructors. */ fprintf (outfile, "%s\n", DATA_SECTION_ASM_OP); ASM_GLOBALIZE_LABEL (outfile, DTOR_TABLE_NAME); ASM_OUTPUT_LABEL (outfile, DTOR_TABLE_NAME); if (dtor_chain) { ASM_OUTPUT_PTR_INT_SUM (outfile, DTOR_TABLE_NAME, sizeof (int)); dtor_offset = 3 * sizeof (int); while (dtor_chain) { if (dtor_chain->next) ASM_OUTPUT_PTR_INT_SUM (outfile, DTOR_TABLE_NAME, dtor_offset); else ASM_OUTPUT_INT (outfile, 0); dtor_offset += 2 * sizeof (int); ASM_OUTPUT_LABELREF (outfile, dtor_chain->name); dtor_chain = dtor_chain->next; } } ASM_OUTPUT_INT (outfile, 0); fclose (outfile); return OK; } /* Output to FILE a reference to the assembler name of a C-level name NAME. If NAME starts with a *, the rest of NAME is output verbatim. Otherwise NAME is transformed in an implementation-defined way (usually by the addition of an underscore). Many macros in the tm file are defined to call this function. Swiped from `varasm.c' */ void assemble_name (file, name) FILE *file; char *name; { if (name[0] == '*') fputs (&name[1], file); else fprintf (file, "_%s", name); } int xmalloc (size) int size; { int result = malloc (size); if (! result) { fprintf (stderr, "Virtual memory exhausted\n"); exit (-1); } return result; }