/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* ***** BEGIN LICENSE BLOCK ***** * Version: MPL 1.1/GPL 2.0/LGPL 2.1 * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. * * The Original Code is mozilla.org code. * * The Initial Developer of the Original Code is * Netscape Communications Corporation. * Portions created by the Initial Developer are Copyright (C) 1998 * the Initial Developer. All Rights Reserved. * * Contributor(s): * * Alternatively, the contents of this file may be used under the terms of * either of the GNU General Public License Version 2 or later (the "GPL"), * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), * in which case the provisions of the GPL or the LGPL are applicable instead * of those above. If you wish to allow use of your version of this file only * under the terms of either the GPL or the LGPL, and not to allow others to * use your version of this file under the terms of the MPL, indicate your * decision by deleting the provisions above and replace them with the notice * and other provisions required by the GPL or the LGPL. If you do not delete * the provisions above, a recipient may use your version of this file under * the terms of any one of the MPL, the GPL or the LGPL. * * ***** END LICENSE BLOCK ***** */ /* */ /* *-------------------------------------------------------------------------- * * * *-------------------------------------------------------------------------- * * gtscc - Global To Static C/C++ compiler driver. * * Syntax: * * gtscc [options] -c file.cpp ... * gtscc [options] file.o ... libxx.a ... * * gtscc is a compiler and linker driver/wrapper for Irix only. * gtscc takes all compiler options and passes them onto the Irix * cc/CC compiler/linker. * Typically, gtscc is used in two phases. Phase one is during compilation. * gtscc, the compiler, converts all inline globals to statics, and records * the existence of other globals and how to compile the file in the gtscc * database file. * During linking, globals dependencies are analyzed, and a list of * "convertable" globals is determined. Globals that are not referenced * globally, but are referenced locally are considered convertable. * The linker then recompiles the files that those symbols are in, and * converts them to statics. It also calls the archiver to install * the converted objects into libraries. * Finally the linker is called. * * Created: David Williams, djw@netscape.com, 13-Feb-1997 * *-------------------------------------------------------------------------- */ #include #include #include #include #include #include #include #include #include #if defined(LINUX) && defined(__GLIBC__) #include #else #include #endif #include #include #include #define DEFAULT_MAX_GLOBALS 15500 #define ELFSYM_IS_DEFINED(x) ((x).st_shndx != SHN_UNDEF) #define ELFSYM_IS_UNDEFINED(x) ((x).st_shndx == SHN_UNDEF) #ifdef IRIX #define CC_COMMAND "cc" #define CCC_COMMAND "CC" #define AS_COMMAND "cc" #define LD_COMMAND "CC" #define AR_COMMAND "ar" #define AR_OPTIONS "cr" #else #define HANDLES_DASHSO #define CC_COMMAND "gcc" #define CCC_COMMAND "g++" #define AS_COMMAND "gcc" #define LD_COMMAND "g++" #define AR_COMMAND "ar" #define AR_OPTIONS "cr" #endif #define EH_NEW(type) (type*)malloc(sizeof(type)) #define TRUE 1 #define FALSE 0 #define EH_TAG_FILE 'F' #define EH_TAG_GLOBAL 'G' #define EH_TAG_ZAPPED 'Z' #define EH_TAG_INLINE 'I' #define EH_TAG_UNDEFINED 'U' #define VERBOSITY_USER(x) ((x) > 0) #define VERBOSITY_DEBUG(x) ((x) > 1) #define VERBOSITY_MAJOR(x) ((x) > 2) static char eh_unnamed_object[] = ""; typedef struct { char* name; /* archive */ } EhArchive; typedef struct { char* name; /* name of C/C++ file, relative to rootdir */ char* directory;/* must compile in this directory */ char** cc_args; /* cc -I ..... */ char* as_savefile; time_t compile_time; char* target_object; } EhSource; typedef struct EhObject { struct EhObject* _recompile; /* used for recompilation link list */ unsigned _needs_unzap; char* name; /* name of .o */ EhArchive* archive; /* it is stored in */ EhSource* source; char* pathname; unsigned nusers; } EhObject; typedef enum { EH_SYM_UNDEFINED, EH_SYM_DEFINED, EH_SYM_ZAPPED, EH_SYM_INLINE /* are treated special - they belong to no file */ } EhSymState; typedef struct EhSym { struct EhSym* _next; /* used for link list */ char* name; /* name of symbol */ EhObject* object; /* if symbol is undefined == NULL */ unsigned ngusers; /* number of global users */ unsigned nlusers; /* number of local file users */ #if 0 unsigned section; /* section in elf file */ unsigned index; /* index into symbol table */ unsigned char info; unsigned dirty; #endif EhSymState state; } EhSym; #define EHSYM_ISDEFINED(x) ((x)->object!=NULL && (x)->state==EH_SYM_DEFINED) #define EHSYM_ISZAPPED(x) ((x)->object!=NULL && (x)->state==EH_SYM_ZAPPED) #define EHSYM_ISUNDEFINED(x) ((x)->object == NULL) #define EHSYM_ISUSED(x) ((x)->nusers != 0) #define EHSYM_ISINLINE(x) ((x)->state == EH_SYM_INLINE) #define EH_OBJECT_CANBUILD(x) \ ((x)->source != NULL && (x)->name != eh_unnamed_object) #define USE_HASHING typedef struct { #ifdef USE_HASHING EhSym** heads; unsigned size; #else EhSym* head; #endif unsigned nentries; } EhSymTable; static char* make_relative_pathname(char* buf, char* filename, char* rootdir) { char buf1[MAXPATHLEN]; char buf2[MAXPATHLEN]; char* p; char* q; if (rootdir == NULL) { strcpy(buf, filename); return filename; } if (filename[0] != '/') { if (getcwd(buf2, sizeof(buf2)) == NULL) { fprintf(stderr, "cannot get pwd\n"); return NULL; } strcat(buf2, "/"); strcat(buf2, filename); filename = buf2; } if (realpath(filename, buf1) == NULL) { fprintf(stderr, "realpath(%s,..) failed\n", filename); return NULL; } if (realpath(rootdir, buf2) == NULL) { fprintf(stderr, "realpath(%s,..) failed\n", rootdir); return NULL; } strcat(buf2, "/"); for (p = buf1, q = buf2; *p == *q; p++, q++) ; strcpy(buf, p); return buf; } static EhArchive* EhArchiveNew(char* name, char* rootdir) { EhArchive* archive = EH_NEW(EhArchive); char pathbuf[MAXPATHLEN]; make_relative_pathname(pathbuf, name, rootdir); archive->name = strdup(pathbuf); return archive; } #if 0 /* * This is evil, we should never free anything, because it messes up * interning. */ static void EhSourceDelete(EhSource* source) { unsigned n; if (source->name != NULL) free(source->name); if (source->directory != NULL) free(source->directory); if (source->cc_args != NULL) { for (n = 0; source->cc_args[n] != NULL; n++) free(source->cc_args[n]); free(source->cc_args); } if (source->as_savefile != NULL) free(source->as_savefile); } #endif static EhSource* EhSourceNew(char* name, char** cc_args, char* directory) { EhSource* source = EH_NEW(EhSource); unsigned n; unsigned m; source->name = strdup(name); source->directory = (directory != NULL)? strdup(directory): NULL; source->as_savefile = NULL; source->compile_time = 0; source->target_object = NULL; source->cc_args = NULL; if (cc_args != NULL) { for (n = 0; cc_args[n] != NULL; n++) ; source->cc_args = (char**)malloc(sizeof(char*) * (n+1)); for (m = 0, n = 0; cc_args[n] != NULL;) { if (strcmp(cc_args[n], "-o") == 0 && cc_args[n+1] != NULL) { source->target_object = strdup(cc_args[n+1]); n += 2; } else { source->cc_args[m++] = strdup(cc_args[n++]); } } source->cc_args[m] = NULL; } return source; } static EhObject* EhObjectNewArchiveObject(EhArchive* archive, char* name) { EhObject* object = EH_NEW(EhObject); if (name == eh_unnamed_object) object->name = name; else object->name = strdup(name); object->archive = archive; object->source = NULL; object->_recompile = NULL; object->_needs_unzap = 0; object->pathname = NULL; object->nusers = 0; return object; } static EhObject* EhObjectNew(char* name, char* rootdir) { EhObject* object = EhObjectNewArchiveObject(NULL, name); char pathname[MAXPATHLEN]; make_relative_pathname(pathname, name, rootdir); object->pathname = strdup(pathname); return object; } static EhObject* EhObjectNewFromSource(EhSource* source) { EhObject* object = EhObjectNewArchiveObject(NULL, eh_unnamed_object); object->source = source; return object; } static char* EhObjectGetFilename(EhObject* object, char* buf) { if (object->archive) { strcpy(buf, object->archive->name); strcat(buf, ":"); strcat(buf, object->name); return buf; } else { return object->name; } } static EhSym* EhSymNewDefined(char* name, EhObject* object) { EhSym* sym = EH_NEW(EhSym); sym->name = strdup(name); sym->object = object; sym->state = EH_SYM_DEFINED; sym->ngusers = 0; sym->nlusers = 0; return sym; } static EhSym* EhSymNewInline(char* name) { EhSym* sym = EhSymNewDefined(name, NULL); sym->state = EH_SYM_INLINE; return sym; } static EhSym* EhSymNewUndefined(char* name) { EhSym* sym = EhSymNewDefined(name, NULL); sym->state = EH_SYM_UNDEFINED; return sym; } static EhSym* EhSymNewZapped(char* name, EhObject* object) { EhSym* sym = EhSymNewDefined(name, object); sym->state = EH_SYM_ZAPPED; return sym; } static EhSym* EhSymNewRandomZap(char* name) { EhSym* sym = EhSymNewZapped(name, NULL); return sym; } EhSymTable* EhSymTableNew(unsigned p_size) { EhSymTable* table = EH_NEW(EhSymTable); #ifdef USE_HASHING unsigned size; for (size = 0x1; size < (16*1024); size <<= 1) { if (size >= p_size) break; } table->size = size; table->heads = (EhSym**)calloc(size, sizeof(EhSym*)); #else table->head = NULL; #endif table->nentries = 0; return table; } EhSym* EhSymTableInsert(EhSymTable* table, EhSym* sym) { #ifdef USE_HASHING unsigned long hash = elf_hash(sym->name); unsigned long mask = table->size - 1; unsigned index = (hash & mask); sym->_next = table->heads[index]; table->heads[index] = sym; #else sym->_next = table->head; table->head = sym; #endif table->nentries++; return sym; } EhSym* EhSymTableFind(EhSymTable* table, char* name) { EhSym* sym; EhSym* head; #ifdef USE_HASHING unsigned long hash = elf_hash(name); unsigned long mask = table->size - 1; unsigned index = (hash & mask); head = table->heads[index]; #else head = table->head; #endif for (sym = head; sym != NULL; sym = sym->_next) { if (strcmp(name, sym->name) == 0) break; } return sym; } typedef int (*eh_dump_mappee_t)(EhSym* sym, void* arg); static int EhSymTableMap(EhSymTable* table, eh_dump_mappee_t func, void* arg) { EhSym* sym; EhSym* head; #ifdef USE_HASHING unsigned n; for (n = 0; n < table->size; n++) { head = table->heads[n]; #else head = table->head; { #endif for (sym = head; sym != NULL; sym = sym->_next) { if ((func)(sym, arg) == -1) return -1; } } return 0; } typedef struct { EhObject* o_old; EhObject* o_new; } fixup_info; static int fixup_mappee(EhSym* sym, void* arg) { fixup_info* info = (fixup_info*)arg; if (sym->object == info->o_old) sym->object = info->o_new; return 0; } static EhObject* EhSymTableObjectFixup(EhSymTable* table, EhObject* o_old, EhObject* o_new) { fixup_info info; /* * Now visit every sym that pointed to tmp, and point it * at object. */ info.o_old = o_old; info.o_new = o_new; EhSymTableMap(table, fixup_mappee, &info); return o_new; } static char* safe_fgets(char* buf, unsigned size, FILE* fp) { unsigned nread = 0; if (buf == NULL) buf = (char*)malloc(size); for (;;) { if (fgets(&buf[nread], size - nread, fp) == NULL) { free(buf); return NULL; } if (strchr(buf, '\n') != NULL) return buf; /* * fgets returns n-1 characters and \0 */ nread += (size - nread) - 1; size += 1024; buf = (char*)realloc(buf, size); } } static int EhSymTableSetSymbolState(EhSymTable* table, char* name, EhSymState new_state) { EhSym* sym = EhSymTableFind(table, name); if (sym == NULL) { sym = EhSymNewDefined(name, NULL); EhSymTableInsert(table, sym); } /* new_state must be EH_SYM_DEFINED || EH_SYM_ZAPPED */ if (sym->state == EH_SYM_DEFINED || sym->state == EH_SYM_ZAPPED) { sym->state = new_state; } else if (sym->state == EH_SYM_INLINE) { char* state_name; if (new_state == EH_SYM_DEFINED) state_name = "global"; else state_name = "static"; fprintf(stderr, "WARNING: Symbol %s is an inline.\n" " Forcing the symbol %s will be ignored.\n", name, state_name); } else { /* EH_SYM_UNDEFINED */ /* * This call is being made after objects have started being * read. This is too late. I'm not sure I care though. */ return -1; } return 0; } static int EhSymTableFpLoad(EhSymTable* table, FILE* fp) { char* buf = NULL; /* I hope this is big enough */ char* p; char* name; char* state; EhSym* sym; EhSource* source = NULL; EhObject* object = NULL; char* cc_args[512]; unsigned n; unsigned line_n = 0; char* ctime = NULL; char* directory; char* savefile; while ((buf = safe_fgets(buf, 1024, fp)) != NULL) { if ((p = strchr(buf, '\n')) == NULL) { fprintf(stderr, "line to long: %d\n", line_n); return -1; } *p = '\0'; line_n++; if (buf[0] == '!') /* comment */ continue; for (p = buf; isspace(*p); p++) ; name = p; for (; !isspace(*p); p++) ; *p++ = '\0'; if (name[0] == '\0') continue; for (; isspace(*p); p++) ; state = p; for (; !isspace(*p) && *p != '\0'; p++) ; *p++ = '\0'; if (state[0] == EH_TAG_GLOBAL || state[0] == EH_TAG_ZAPPED || state[0] == EH_TAG_INLINE) { sym = EhSymTableFind(table, name); if (sym == NULL) { /* install a new one */ if (source == NULL && state[0] != EH_TAG_INLINE) { fprintf(stderr, "[%d] found new style symbol (%s) but no source\n", line_n, name); } if (state[0] == EH_TAG_GLOBAL) sym = EhSymNewDefined(name, object); else if (state[0] == EH_TAG_INLINE) sym = EhSymNewInline(name); else sym = EhSymNewZapped(name, object); EhSymTableInsert(table, sym); } else { if (state[0] == EH_TAG_GLOBAL) { if (sym->state != EH_SYM_DEFINED) { fprintf(stderr, "out of sync defined symbol: %s, fixing\n", sym->name); sym->state = EH_SYM_DEFINED; } } else if (state[0] == EH_TAG_INLINE) { if (sym->state != EH_SYM_INLINE) { fprintf(stderr, "out of sync inlined symbol: %s, fixing\n", sym->name); sym->state = EH_SYM_INLINE; } } else { if (sym->state != EH_SYM_ZAPPED) { fprintf(stderr, "out of sync zapped symbol: %s, fixing\n", sym->name); sym->state = EH_SYM_ZAPPED; } } #if 0 /* these are probably "special" symbols like .div */ if (sym->object != object) { fprintf(stderr, "out of sync object for symbol: %s, ignoring\n", sym->name); } #endif } continue; /* no more fields we care about */ } else if (state[0] == EH_TAG_FILE) { directory = p; for (; !isspace(*p) && *p != '\0'; p++) ; *p++ = '\0'; savefile = p; for (; !isspace(*p) && *p != '\0'; p++) ; *p++ = '\0'; ctime = p; for (; !isspace(*p) && *p != '\0'; p++) ; *p++ = '\0'; for (n = 0; *p != '\0';) { for (; isspace(*p); p++) ; cc_args[n++] = p++; for (; !isspace(*p) && *p != '\0'; p++) ; if (*p == '\0') break; *p++ = '\0'; } cc_args[n] = NULL; if (strcmp(directory, ".") == 0) directory = NULL; source = EhSourceNew(name, cc_args, directory); if (ctime != NULL) source->compile_time = (time_t)atoi(ctime); object = EhObjectNewFromSource(source); } else { /* old style symbol list */ sym = EhSymTableFind(table, name); if (sym != NULL) { if (sym->state != EH_SYM_ZAPPED) { fprintf(stderr, "out of sync random zapped symbol: %s, fixing\n", sym->name); sym->state = EH_SYM_ZAPPED; } } else { sym = EhSymNewRandomZap(name); } } } return line_n; } typedef struct { EhSym** vector; unsigned index; } flush_info; static int flush_mappee(EhSym* sym, void* arg) { flush_info* info = (flush_info*)arg; if (sym->state == EH_SYM_INLINE || (sym->object != NULL && sym->state == EH_SYM_DEFINED) || (sym->object != NULL && sym->state == EH_SYM_ZAPPED)) { if (info->vector != NULL) info->vector[info->index] = sym; info->index++; } return 0; } static int flush_compare(const void* ap, const void* bp) { EhSym** ax = (EhSym**)ap; EhSym** bx = (EhSym**)bp; EhSym* a = *ax; EhSym* b = *bx; EhObject* oa = a->object; EhObject* ob = b->object; int foo; if (oa == NULL && ob != NULL) return -1; if (oa != NULL && ob == NULL) return 1; if (oa == NULL && ob == NULL) { foo = strcmp(a->name, b->name); if (foo < 0) return -1; else if (foo > 0) return 1; return 0; } if (oa->source == NULL && ob->source != NULL) return -1; if (oa->source != NULL && ob->source == NULL) return 1; if (oa->source == ob->source) return 0; if (oa->source < ob->source) return -1; if (oa->source > ob->source) return 1; foo = strcmp(a->name, b->name); if (foo < 0) return -1; else if (foo > 0) return 1; return 0; } static void EhSourceFpWrite(EhSource* source, FILE* fp) { unsigned n = 0; fputs(source->name, fp); fputc(' ', fp); fputc(EH_TAG_FILE, fp); fputc(' ', fp); if (source->directory != NULL) fprintf(fp, "%s", source->directory); else fputc('.', fp); fputc(' ', fp); if (source->as_savefile != NULL) fprintf(fp, "%s", source->as_savefile); else fputc('.', fp); fputc(' ', fp); fprintf(fp, "%d", source->compile_time); if (source->target_object != NULL) { fputs(" -o ", fp); fputs(source->target_object, fp); } if (source->cc_args != NULL) { for (n = 0; source->cc_args[n] != NULL; n++) { fputc(' ', fp); fputs(source->cc_args[n], fp); } } if (n < 1) fprintf(stderr, "WARNING: %s has no args\n", source->name); fputc('\n', fp); } static int EhSymTableFpDump(EhSymTable* table, FILE* fp) { flush_info info; unsigned n; EhObject* object = NULL; EhSym** syms; EhSym* sym; unsigned size; info.index = 0; info.vector = NULL; EhSymTableMap(table, flush_mappee, (void*)&info); size = info.index; syms = (EhSym**)malloc(sizeof(EhSym*) * size); info.index = 0; info.vector = syms; EhSymTableMap(table, flush_mappee, (void*)&info); /* sort */ qsort(syms, size, sizeof(EhSym*), flush_compare); /* dump */ for (n = 0; n < size; n++) { sym = syms[n]; if (sym->object != object) { object = sym->object; if (object->source != NULL) { EhSourceFpWrite(object->source, fp); } } if (sym->state == EH_SYM_INLINE) { fprintf(fp, "%s %c\n", sym->name, EH_TAG_INLINE); } else if (object->source != NULL && sym->state == EH_SYM_ZAPPED) { fprintf(fp, "%s %c\n", sym->name, EH_TAG_ZAPPED); } else if (object->source != NULL && sym->state == EH_SYM_DEFINED) { fprintf(fp, "%s %c\n", sym->name, EH_TAG_GLOBAL); } } free(syms); return n; } int djw_debug; char* djw_test_name; int eh_process_object(Elf* elf, EhObject* object, EhSymTable* table) { Elf32_Shdr * shdr; Elf32_Ehdr * ehdr; Elf_Scn * scn; Elf_Data * shstr_data; Elf_Data* sym_data = NULL; Elf_Data* str_data = NULL; Elf_Data* rel_data[4]; int nrel_data = 0; Elf32_Rel* rel_entries; Elf_Data* rela_data[10]; int nrela_data = 0; Elf32_Rela* rela_entries; unsigned int cnt; Elf32_Sym* elf_sym; int i; int j; int k; char* name; EhSym* sym; char buf[MAXPATHLEN]; /* Obtain the .shstrtab data buffer */ if (((ehdr = elf32_getehdr(elf)) == NULL) || ((scn = elf_getscn(elf, ehdr->e_shstrndx)) == NULL) || ((shstr_data = elf_getdata(scn, NULL)) == NULL)) { fprintf(stderr, "problems on %s\n", EhObjectGetFilename(object, buf)); return -1; } /* get the string table */ for (cnt = 1, scn = NULL; (scn = elf_nextscn(elf, scn)); cnt++) { if ((shdr = elf32_getshdr(scn)) == NULL) { fprintf(stderr, "problems on %s, section %d\n", EhObjectGetFilename(object, buf), cnt); return -1; } #if 0 fprintf(stderr, "%s: section %d type %d name %s\n", EhObjectGetFilename(object, buf), cnt, shdr->sh_type, (char*)shstr_data->d_buf + shdr->sh_name); #endif /* * Get the string table. */ if (shdr->sh_type == SHT_STRTAB && #ifdef sun strcmp((char*)shstr_data->d_buf + shdr->sh_name, ".strtab") == 0 && #endif cnt != ehdr->e_shstrndx) { if (str_data != NULL) { fprintf(stderr, "multiple string tables for %s - bailing\n", EhObjectGetFilename(object, buf)); return -1; } str_data = elf_getdata(scn, NULL); } else if (shdr->sh_type == SHT_SYMTAB) { /* look into sym table */ if (sym_data != NULL) { fprintf(stderr, "multiple symbol tables for %s - bailing\n", EhObjectGetFilename(object, buf)); return -1; } sym_data = elf_getdata(scn, NULL); } else if (shdr->sh_type == SHT_REL) { /* look into rel table */ if (nrel_data >= 4) { fprintf(stderr, "too many relocation tables for %s bailing\n", EhObjectGetFilename(object, buf)); return -1; } rel_data[nrel_data++] = elf_getdata(scn, NULL); } else if (shdr->sh_type == SHT_RELA) { /* look into rela table */ if (nrela_data >= 10) { fprintf(stderr, "too many RELA tables for %s bailing\n", EhObjectGetFilename(object, buf)); return -1; } rela_data[nrela_data++] = elf_getdata(scn, NULL); } } if (sym_data == NULL) { fprintf(stderr, "could not load sym table for %s\n", EhObjectGetFilename(object, buf)); return -1; } if (str_data == NULL) { fprintf(stderr, "could not load string table for %s\n", EhObjectGetFilename(object, buf)); return -1; } elf_sym = (Elf32_Sym*)sym_data->d_buf; for (i = 0; i < (sym_data->d_size/sizeof(Elf32_Sym)); i++) { /* * We are only interested in globals. */ if (ELF32_ST_BIND(elf_sym[i].st_info) != STB_GLOBAL) continue; name = (char *)str_data->d_buf + elf_sym[i].st_name; if (djw_test_name != NULL && strcmp(djw_test_name, name) == 0) { printf("found %s\n", name); } sym = EhSymTableFind(table, name); /* * Treat inlines as non-globals */ if (sym != NULL && sym->state == EH_SYM_INLINE) continue; #if 0 printf("name = %s value = %d type = %d, info = %d," " other = %d, size = %d\n", name, elf_sym[i].st_value, ELF32_ST_TYPE(elf_sym[i].st_info), elf_sym[i].st_info, elf_sym[i].st_other, elf_sym[i].st_size); #endif /* defined */ if (ELFSYM_IS_DEFINED(elf_sym[i])) { if (sym != NULL) { if (sym->object == NULL) { /* object undefined */ sym->object = object; } else if (sym->object->name==eh_unnamed_object) { if (object->source != NULL && object->source != sym->object->source) { fprintf(stderr, "warning: symbol %s defined in more than one source file\n" "last time: %s\n" "this time: %s (ignored)\n", sym->name, object->source->name, sym->object->source->name); } else { object->source = sym->object->source; /* * Do a global: sym->object = object; */ EhSymTableObjectFixup(table, sym->object, /*old*/ object); /*new*/ } } else if (sym->object != object) { fprintf(stderr, "warning: symbol %s define in multiple object files\n" "last time: %s\n" "this time: %s (ignored)\n", sym->name, object->name, sym->object->name); } sym->state = EH_SYM_DEFINED; } else { sym = EhSymNewDefined(name, object); EhSymTableInsert(table, sym); } for (k = 0; k < nrel_data; k++) { int nentries = rel_data[k]->d_size/sizeof(Elf32_Rel); rel_entries = (Elf32_Rel*)rel_data[k]->d_buf; for (j = 0; j < nentries; j++) { if (ELF32_R_SYM(rel_entries[j].r_info) == i) { /* locally referenced */ sym->nlusers++; } } } for (k = 0; k < nrela_data; k++) { int nentries = rela_data[k]->d_size/sizeof(Elf32_Rela); rela_entries = (Elf32_Rela*)rela_data[k]->d_buf; for (j = 0; j < nentries; j++) { if (ELF32_R_SYM(rela_entries[j].r_info) == i) { /* locally referenced */ sym->nlusers++; } } } } /* Undefined. */ else if (ELFSYM_IS_UNDEFINED(elf_sym[i])) { if (sym == NULL) { sym = EhSymNewUndefined(name); EhSymTableInsert(table, sym); } sym->ngusers++; } else { #if 1 printf("what is this: " "name = %s value = %d type = %d, " "info = %d, other = %d, size = %d\n", name, elf_sym[i].st_value, ELF32_ST_TYPE(elf_sym[i].st_info), elf_sym[i].st_info, elf_sym[i].st_other, elf_sym[i].st_size); #endif ; }/* type ==... */ } /* for each symbol */ return 0; } int eh_process_file(char* filename, EhSymTable* table, char* rootdir) { Elf* elf; Elf* arf; int fd; Elf_Cmd cmd; Elf_Kind e_kind; EhObject* object; EhArchive* archive; Elf_Arhdr* arhdr; char* name; int rv = 0; if ((fd = open(filename, O_RDONLY)) == -1) { fprintf(stderr, "error opening %s\n", filename); return -1; } elf_version(EV_CURRENT); if ((arf = elf_begin(fd, ELF_C_READ, NULL)) == NULL) { return -1; } e_kind = elf_kind(arf); if (e_kind == ELF_K_ELF) { object = EhObjectNew(filename, rootdir); rv = eh_process_object(arf, object, table); } else if (e_kind == ELF_K_AR) { archive = EhArchiveNew(filename, rootdir); cmd = ELF_C_READ; #if 0 arsyms = elf_getarsym(arf, &narsyms); for (i = 0; i < narsyms && arsyms[i].as_name != NULL; i++) { printf("%s - %d\n", arsyms[i].as_name, arsyms[i].as_off); } arhdr = elf_getarhdr(arf); for (i = 0; arhdr[i].ar_rawname != NULL; i++) { if (arhdr[i].ar_name != NULL) printf("%s\n", arhdr[i].ar_name); else printf("[%s]\n", arhdr[i].ar_rawname); } #endif rv = 0; while ((elf = elf_begin(fd, cmd, arf)) != 0) { e_kind = elf_kind(elf); if (e_kind != ELF_K_ELF) continue; arhdr = elf_getarhdr(elf); if (arhdr != NULL) { if (arhdr->ar_name != NULL) name = arhdr->ar_name; else name = arhdr->ar_rawname; } else { name = eh_unnamed_object; } object = EhObjectNewArchiveObject(archive, name); rv = eh_process_object(elf, object, table); if (rv == -1) break; cmd = elf_next(elf); elf_end(elf); } } elf_end(arf); close(fd); return rv; } static int eh_dump_unused(EhSym* sym, void* arg) { char buf[MAXPATHLEN]; printf(/*"0x%x "*/ "%s %d %d ", /*sym,*/ sym->name, sym->ngusers, sym->nlusers); if (EHSYM_ISINLINE(sym)) printf("%c ", EH_TAG_INLINE); else if (EHSYM_ISZAPPED(sym)) printf("%c ", EH_TAG_ZAPPED); else if (EHSYM_ISDEFINED(sym)) printf("%c ", EH_TAG_GLOBAL); else printf("%c ", EH_TAG_UNDEFINED); if (sym->object != NULL) { printf("%s ", EhObjectGetFilename(sym->object, buf)); if (sym->object->source != NULL) { printf("%s recompilable\n", sym->object->source->name); } else { printf("nosource notrecompilable\n"); } } else { printf("noobject nosource notrecompilable\n"); } return 0; } static void print_dump(EhSymTable* table) { printf("everything\n"); EhSymTableMap(table, eh_dump_unused, NULL); } typedef struct { unsigned ndefined; unsigned nused; /* globally */ unsigned nundefined; unsigned nzapped; unsigned nzapped_nowused; unsigned ninlined; unsigned nunlinked; unsigned ndeadcode; } SummaryInfo; static int eh_summary_mappee(EhSym* sym, void* arg) { SummaryInfo* info = (SummaryInfo*)arg; if (EHSYM_ISDEFINED(sym)) { if (sym->ngusers != 0) info->nused++; else if (sym->object != NULL && sym->object->nusers == 0) info->nunlinked++; else if (sym->nlusers != 0) info->ndefined++; else info->ndeadcode++; } else if (EHSYM_ISZAPPED(sym)) { /* one of ours */ if (sym->ngusers != 0) info->nzapped_nowused++; else info->nzapped++; } else if (EHSYM_ISINLINE(sym)) { /* one of ours */ info->ninlined++; } else { info->nundefined++; } return 0; } static void get_summary(EhSymTable* table, SummaryInfo* info) { info->ndefined = 0; info->nused = 0; info->nundefined = 0; info->nzapped = 0; info->nzapped_nowused = 0; info->ninlined = 0; info->nunlinked = 0; info->ndeadcode = 0; EhSymTableMap(table, eh_summary_mappee, info); } static void print_summary(EhSymTable* table) { SummaryInfo info; get_summary(table, &info); printf("summary:\n" "defined and used: %d\n" "defined but unused globally: %d\n" "total globals in target: %d\n" "--------------------------------\n" "global to statics *: %d\n" "global to statics (now used): %d\n" "inlined to statics *: %d\n" "defined in unlinked objects: %d\n" "defined but unused (deadcode):%d\n" "undefined but used: %d\n", info.nused, info.ndefined, (info.nused + info.ndefined), info.nzapped, info.nzapped_nowused, info.ninlined, info.nunlinked, info.ndeadcode, info.nundefined); } typedef struct EhDirMapEntree { char* dirname; struct EhDirMapEntree* _next; } EhDirMapEntree; typedef struct EhDirMap { EhDirMapEntree* head; } EhDirMap; static EhDirMap* EhDirMapNew(void) { EhDirMap* dm = EH_NEW(EhDirMap); dm->head = NULL; return dm; } static void EhDirMapAddDirectory(EhDirMap* map, char* dirname) { EhDirMapEntree* entree = EH_NEW(EhDirMapEntree); EhDirMapEntree* foo; entree->dirname = strdup(dirname); entree->_next = NULL; if (map->head == NULL) { map->head = entree; } else { for (foo = map->head; foo->_next != NULL; foo = foo->_next) ; foo->_next = entree; } } static char* EhDirMapGetLibName(EhDirMap* map, char* name, char* libbuf) { EhDirMapEntree* foo; struct stat buf; for (foo = map->head; foo != NULL; foo = foo->_next) { sprintf(libbuf, "%s/lib%s.a", foo->dirname, name); if (stat(libbuf, &buf) != -1) return libbuf; } return NULL; } static char* test_for_global(char* buf) { #ifdef IRIX if (strncmp(buf, "\t.globl\t", 8) == 0) return &buf[8]; #else if (strncmp(buf, "\t.global ", 9) == 0) return &buf[9]; #endif return NULL; } static char* test_for_file(char* foo, char* buf) { #ifdef IRIX char* p; char* q; if (strncmp(buf, "\t.file\t", 6) == 0) { for (p = &buf[6]; *p != '"' && *p != '\0'; p++) ; if (*p == '\0') return NULL; p++; /* quote */ q = strchr(p, '"'); if (q == NULL) return NULL; memcpy(foo, p, q - p); foo[q - p] = '\0'; return foo; } #else printf("test_for_file() not implemented\n"); #endif return NULL; } static int EhSourceZapFp(EhSource* source, EhSymTable* table, FILE* fpi, FILE* fpo, unsigned verbosity, unsigned cplusplus) { char* buf = NULL; char* p; int nzap = 0; char* name; EhSym* sym; int i_zapped; EhObject* object = EhObjectNewFromSource(source); char* filename = source->name; char* tmp_name; char foo[256]; unsigned line_n = 0; if (VERBOSITY_DEBUG(verbosity)) fputs("gts: ", stderr); while ((buf = safe_fgets(buf, 4192, fpi)) != NULL) { i_zapped = 0; for (p = buf; *p != '\0' && *p != '\n'; p++) ; *p = '\0'; if ((tmp_name = test_for_file(foo, buf)) != NULL) { if (strcmp(tmp_name, filename) != 0) /* not the same file */ filename = "non local file"; else filename = source->name; } else if ((name = test_for_global(buf)) != NULL) { sym = EhSymTableFind(table, name); /* an inline, we have to treat specially */ if ((filename != source->name && cplusplus != 0) /* inline now */ || (sym != NULL && sym->state == EH_SYM_INLINE)) {/* was inline */ if (!sym) { sym = EhSymNewInline(name); EhSymTableInsert(table, sym); } sym->state = EH_SYM_INLINE; /* just make sure */ if (fpo != NULL) /* always zap inlines we see */ fputs(" # gts", fpo); } else { /* a real global */ if (fpo != NULL && sym != NULL && EHSYM_ISZAPPED(sym)) { if (VERBOSITY_DEBUG(verbosity)) { fprintf(stderr, "%s ", &buf[8]); } nzap++; if (fpo != NULL) fputs(" # gts", fpo); } if (sym != NULL) { if (sym->object == NULL) { sym->object = object; } else if (sym->object != object) { sym->object->source = source; EhSymTableObjectFixup(table, object, sym->object); object = sym->object; } } else { /* install a new one */ sym = EhSymNewDefined(name, object); EhSymTableInsert(table, sym); } } } if (fpo != NULL) { fputs(buf, fpo); fputc('\n', fpo); } line_n++; } if (VERBOSITY_DEBUG(verbosity)) fputc('\n', stderr); return nzap; } static void print_command(char* command, char** args, unsigned verbosity) { int i; FILE* fp = stderr; if (!VERBOSITY_USER(verbosity)) return; fprintf(fp, "%s: ", command); for (i = 0; args[i]; i++) { fprintf(fp, "%s ", args[i]); } fprintf(fp, "\n"); } static int do_command(char* label, char** args, unsigned verbosity) { int status; pid_t child_pid; char* file = args[0]; print_command(label, args, verbosity); if ((child_pid = fork()) == -1) { fprintf(stderr, "could not fork: "); perror(NULL); return -1; } if (child_pid == 0) { /* i am the child */ if (execvp(file, args) == -1) { fprintf(stderr, "could not exec %s: ", file); perror(NULL); exit(3); } /*NOTREACHED*/ } if (waitpid(child_pid, &status, 0) == -1) { fprintf(stderr, "wait on %s failed: ", file); perror(NULL); return -1; } return WEXITSTATUS(status); } static char* suffix_name(char* s) { char* p; if ((p = strrchr(s, '.')) != NULL) return p; else return ""; } static char base_name_buf[MAXPATHLEN]; static char* base_name(char* s) { char* p; if ((p = strrchr(s, '.')) != NULL) { memcpy(base_name_buf, s, p - s); base_name_buf[p - s] = '\0'; s = base_name_buf; } return s; } static char* file_base_name(char *s) { char* p; s = base_name(s); if ((p = strrchr(s, '/')) != NULL) s = &p[1]; return s; } static int EhSourceCompile(EhSource* source, EhSymTable* table, unsigned verbosity, unsigned do_compile, unsigned do_zap, unsigned do_assem) { char* filename = source->name; char** opts = source->cc_args; char asname[MAXPATHLEN]; char o_asname[MAXPATHLEN]; char* cc_opts[256]; unsigned i = 0; unsigned j = 0; FILE* in_fp; FILE* out_fp; int status; int nzap = 0; char* save_prefix = NULL; unsigned do_dash_s = (do_zap != 0 || save_prefix != NULL); char* cc_command; char* use_savefile = NULL; struct timeval start_time; struct timeval end_time; #ifdef DEBUG_djw char savebuf[1024]; #endif unsigned is_cplusplus = 0; #ifdef LINUX gettimeofday(&start_time,NULL); #else gettimeofday(&start_time); #endif /* munge file */ #ifdef HANDLES_DASHSO if (source->target_object != NULL) strcpy(asname, base_name(source->target_object)); else #endif strcpy(asname, file_base_name(filename)); strcat(asname, ".s"); strcpy(o_asname, asname); strcat(o_asname, ".gts_tmp"); if (strcmp(suffix_name(filename), ".cpp") == 0) { cc_command = CCC_COMMAND; is_cplusplus = 1; } else if (strcmp(suffix_name(filename), ".s") == 0) { do_compile = FALSE; cc_command = CC_COMMAND; } else { cc_command = CC_COMMAND; } if (do_compile) { j = 0; cc_opts[j++] = cc_command; cc_opts[j++] = "-c"; if (do_dash_s) { cc_opts[j++] = "-S"; #ifdef HANDLES_DASHSO if (source->target_object != NULL) { cc_opts[j++] = "-o"; cc_opts[j++] = asname; } #endif } else if (source->target_object != NULL) { cc_opts[j++] = "-o"; cc_opts[j++] = source->target_object; } i = 0; while (opts[i] != NULL) cc_opts[j++] = opts[i++]; cc_opts[j++] = filename; cc_opts[j] = NULL; if ((status = do_command("compile", cc_opts, verbosity)) != 0) { fprintf(stderr, "compile failed (returned %d)\n", status); return -1; } if (!do_dash_s) return 0; } /* * Now we have a foo.s file, what do we do with it? */ if (do_zap > 1) { if (use_savefile == NULL) use_savefile = asname; if ((in_fp = fopen(use_savefile, "r")) == NULL) { fprintf(stderr, "could not open %s for reading\n", asname); return -1; } if ((out_fp = fopen(o_asname, "w")) == NULL) { fprintf(stderr, "could not open %s for writing\n", o_asname); return -1; } j = 0; cc_opts[j++] = "gts"; cc_opts[j++] = asname; cc_opts[j++] = o_asname; cc_opts[j++] = NULL; print_command("gts", cc_opts, verbosity); nzap = EhSourceZapFp(source, table, in_fp, out_fp, verbosity, is_cplusplus); fclose(in_fp); fclose(out_fp); j = 0; cc_opts[j++] = "rename"; cc_opts[j++] = o_asname; cc_opts[j++] = asname; cc_opts[j++] = NULL; print_command("rename", cc_opts, verbosity); #ifdef DEBUG_djw strcpy(savebuf, "gts_pre_"); strcat(savebuf, asname); rename(asname, savebuf); #endif if (rename(o_asname, asname) == -1) { fprintf(stderr, "could not rename %s\n", o_asname); return -1; } } else if (do_zap > 0) { /* audit only */ if ((in_fp = fopen(asname, "r")) == NULL) { fprintf(stderr, "could not open %s for reading\n", asname); return -1; } j = 0; cc_opts[j++] = "audit"; cc_opts[j++] = asname; cc_opts[j++] = NULL; print_command("audit", cc_opts, verbosity); nzap = EhSourceZapFp(source, table, in_fp, NULL, verbosity, is_cplusplus); fclose(in_fp); } if (do_assem) { i = 0; j = 0; cc_opts[j++] = AS_COMMAND; cc_opts[j++] = "-c"; if (source->target_object != NULL) { cc_opts[j++] = "-o"; cc_opts[j++] = source->target_object; } while (opts[i] != NULL) cc_opts[j++] = opts[i++]; cc_opts[j++] = asname; cc_opts[j] = NULL; if ((status = do_command("assemble", cc_opts, verbosity)) != 0) { unlink(asname); fprintf(stderr, "gtscc of %s failed (exit status = %d), reverting to %s:\n", filename, status, cc_command); i = 0; j = 0; cc_opts[j++] = cc_command; cc_opts[j++] = "-c"; if (source->target_object != NULL) { cc_opts[j++] = "-o"; cc_opts[j++] = source->target_object; } for (; opts[i]; i++, j++) cc_opts[j] = opts[i]; cc_opts[j++] = filename; cc_opts[j] = NULL; if ((status = do_command("fix-compile", cc_opts, verbosity)) != 0) return -1; return 0; } } if (save_prefix != NULL && save_prefix[0] != '\0') { sprintf(o_asname, save_prefix, file_base_name(filename)); j = 0; cc_opts[j++] = "rename"; cc_opts[j++] = asname; cc_opts[j++] = o_asname; cc_opts[j++] = NULL; print_command("savefile", cc_opts, verbosity); if (rename(asname, o_asname) == -1) { fprintf(stderr, "could not rename %s to %s - sorry\n", asname, o_asname); return -1; } if (source->as_savefile != NULL) free(source->as_savefile); source->as_savefile = strdup(o_asname); } else { j = 0; cc_opts[j++] = "unlink"; cc_opts[j++] = asname; cc_opts[j++] = NULL; print_command("unlink", cc_opts, verbosity); #ifdef DEBUG_djw strcpy(savebuf, "gts_post_"); strcat(savebuf, asname); rename(asname, savebuf); #else unlink(asname); #endif } #ifdef LINUX gettimeofday(&end_time,NULL); #else gettimeofday(&end_time); #endif source->compile_time = ((end_time.tv_sec - start_time.tv_sec) * 1000) + ((end_time.tv_usec - start_time.tv_usec) / 1000); return nzap; } static char target_buf[MAXPATHLEN]; /* this will go away with rel source */ static char* EhSourceGetTarget(EhSource* source) { if (source->target_object != NULL) return source->target_object; strcpy(target_buf, base_name(source->name)); strcat(target_buf, ".o"); return target_buf; } static int EhArchiveUpdate(EhArchive* archive, char* target, char* rootdir, unsigned verbosity) { char* args[8]; unsigned nargs; int status; char pathname[MAXPATHLEN]; pathname[0] = '\0'; if (rootdir != NULL) { strcat(pathname, rootdir); strcat(pathname, "/"); } strcat(pathname, archive->name); #if 0 nargs = 0; args[nargs++] = AR_COMMAND; args[nargs++] = "dc"; args[nargs++] = pathname; args[nargs++] = target; args[nargs++] = NULL; if ((status = do_command("delete from archive", args, verbosity)) != 0) { fprintf(stderr, "archive: %s delete %s failed (status = %d)\n", pathname, target); return -1; } #endif nargs = 0; args[nargs++] = AR_COMMAND; args[nargs++] = AR_OPTIONS; args[nargs++] = pathname; args[nargs++] = target; args[nargs++] = NULL; if ((status = do_command("archive", args, verbosity)) != 0) { fprintf(stderr, "archive: %s <- %s failed (status = %d)\n", pathname, target); return -1; } return 0; } static int EhObjectRebuild(EhObject* object, EhSymTable* table, unsigned verbosity, char* rootdir) { EhSource* source = object->source; char cwd[MAXPATHLEN]; char fullpath[MAXPATHLEN]; int rv = 0; int do_chdir = 0; if (!source) { fprintf(stderr, "wanted to recompile %s, but I don't how\n", object->name); return -1; } #if 0 if (VERBOSITY_USER(verbosity)) #endif fprintf(stderr, "recompiling %s\n", source->name); /* * Check to see if we need to chdir */ if (source->directory != NULL) { if (getcwd(cwd, sizeof(cwd)) == NULL) { fprintf(stderr, "cannot get pwd: cannot compile\n"); return -1; } make_relative_pathname(fullpath, cwd, rootdir); if (strcmp(fullpath, source->directory) != 0) { fullpath[0] = '\0'; if (rootdir != NULL) { strcat(fullpath, rootdir); strcat(fullpath, "/"); } strcat(fullpath, source->directory); if (chdir(fullpath) == -1) { fprintf(stderr, "cannot chdir - can't compile\n"); return -1; } do_chdir++; } } rv = EhSourceCompile(source, table, verbosity, TRUE, /* compile */ 2, /* do zap */ TRUE); /* do assem */ if (do_chdir) { if (chdir(cwd) == -1) { fprintf(stderr, "cannot chdir - this will be very confused\n"); return -1; } } if (rv == -1) { fprintf(stderr, "recompiling %s failed\n", source->name); return -1; } /* do archive */ fullpath[0] = '\0'; if (rootdir != NULL) { strcat(fullpath, rootdir); strcat(fullpath, "/"); } if (source->directory != NULL) strcat(fullpath, source->directory); strcat(fullpath, "/"); strcat(fullpath, EhSourceGetTarget(source)); if (object->archive != NULL) { if (EhArchiveUpdate(object->archive, fullpath, rootdir, verbosity) == -1) return -1; } /* do install */ #if 0 if (rv != -1) { } #endif return rv; } static int object_nusers_mappee(EhSym* sym, void* arg) { if (sym->object != NULL) sym->object->nusers += sym->ngusers; return 0; } typedef struct { EhObject* recompile_list; unsigned recompile_count; unsigned recompile_wish_count; unsigned unzap_count; unsigned zap_count; } RecompileInfo; static int recompile_init_mappee(EhSym* sym, void* arg) { RecompileInfo* info = (RecompileInfo*)arg; if (EHSYM_ISZAPPED(sym) && sym->ngusers != 0) { if (EH_OBJECT_CANBUILD(sym->object)) { sym->state = EH_SYM_DEFINED; if (sym->object->_recompile == NULL) { sym->object->_recompile = info->recompile_list; info->recompile_list = sym->object; info->recompile_count++; } info->unzap_count++; sym->object->_needs_unzap++; } info->recompile_wish_count++; } else if (EHSYM_ISDEFINED(sym) /* it's defined */ && sym->ngusers == 0 /* there are no global users */ && sym->nlusers != 0 /* BUT, ther are local users */ && sym->object->nusers != 0) { /* object is linked */ if (EH_OBJECT_CANBUILD(sym->object)) { sym->state = EH_SYM_ZAPPED; if (sym->object->_recompile == NULL) { sym->object->_recompile = info->recompile_list; info->recompile_list = sym->object; info->recompile_count++; } info->zap_count++; } info->recompile_wish_count++; } return 0; } static char** recompile_compare_prefs; static char** recompile_compare_unprefs; static unsigned match_prefs(char* candidate, char** prefs) { unsigned n; for (n = 0; prefs[n] != NULL; n++) { char* pref = prefs[n]; unsigned len = strlen(pref); if (strncmp(pref, candidate, len) == 0) return n; /* cool */ } return (unsigned)-1; /* big! */ } static int recompile_compare(const void* ap, const void* bp) { EhObject** ax = (EhObject**)ap; EhObject** bx = (EhObject**)bp; EhObject* obj_a = *ax; EhObject* obj_b = *bx; EhSource* src_a = obj_a->source; EhSource* src_b = obj_b->source; unsigned matcha; unsigned matchb; int foo; if (obj_a->_needs_unzap == 0 && obj_b->_needs_unzap != 0) return -1; if (obj_a->_needs_unzap != 0 && obj_b->_needs_unzap == 0) return 1; if (src_a == NULL && src_b != NULL) return 1; if (src_a != NULL && src_b == NULL) return -1; if (src_a == src_b) return 0; if (recompile_compare_unprefs != NULL && src_a->directory != NULL && src_b->directory != NULL) { matcha = match_prefs(src_a->directory, recompile_compare_unprefs); matchb = match_prefs(src_b->directory, recompile_compare_unprefs); if (matcha > matchb) /* greater is good */ return -1; if (matcha < matchb) return 1; } if (recompile_compare_prefs != NULL && src_a->directory != NULL && src_b->directory != NULL) { matcha = match_prefs(src_a->directory, recompile_compare_prefs); matchb = match_prefs(src_b->directory, recompile_compare_prefs); if (matcha > matchb) /* greater is bad */ return 1; if (matcha < matchb) return -1; } /* else same directory probably */ foo = strcmp(src_a->name, src_b->name); if (foo < 0) return -1; if (foo > 0) return 1; return 0; } static int do_recompilation(EhSymTable* table, char* gts_file, unsigned max_globals, char** prefs, char** unprefs, char* rootdir, unsigned verbosity) { SummaryInfo s_info; RecompileInfo info; unsigned size; unsigned n; EhObject* object; EhObject** recompiles; unsigned delta; int rv; unsigned nzaps; EhObject dummy; /* just marks the end of the recomp list */ time_t eta; get_summary(table, &s_info); if ((s_info.nused + s_info.ndefined) <= max_globals) { if (VERBOSITY_USER(verbosity)) fprintf(stderr, "number of globals <= requested max, skipping recompilation\n"); return 0; } /* Init recompilation. */ info.recompile_list = &dummy; /* cannot use NULL, because syms test that */ info.recompile_count = 0; info.recompile_wish_count = 0; info.unzap_count = 0; info.zap_count = 0; EhSymTableMap(table, recompile_init_mappee, (void*)&info); size = info.recompile_count; recompiles = (EhObject**)malloc(sizeof(EhObject*) * size); /* install */ n = 0; for (object = info.recompile_list; object != &dummy; object = object->_recompile) { recompiles[n++] = object; } /* sort */ recompile_compare_prefs = prefs; recompile_compare_unprefs = unprefs; qsort(recompiles, size, sizeof(EhObject*), recompile_compare); /* * sorted ! * less recompile the first n, n = ndefined - max */ delta = (s_info.nused + s_info.ndefined) - max_globals; if (delta > info.zap_count) { fprintf(stderr, "WARNING: there too many globals (%d/%d fixables).\n" " I don't think I can fix this, but I'll try.\n" " You might get lucky.\n", info.zap_count, delta); } if (VERBOSITY_USER(verbosity)) fprintf(stderr, "scheduling recompilation targets:\n"); eta = 0; for (n = 0; n < size; n++) { char* cname = "unknown"; object = recompiles[n]; if (object->source != NULL) { cname = object->source->name; eta += object->source->compile_time; } if (VERBOSITY_DEBUG(verbosity)) fprintf(stderr, "object %s from source %s\n", object->name, cname); } #if 0 if (VERBOSITY_USER(verbosity)) #endif fprintf(stderr, "gts-ing %d symbols, eta = %d minutes\n", delta, eta/(60*1000)); if (gts_file != NULL) { FILE* zap_fp; if ((zap_fp = fopen(gts_file, "w")) == NULL) { perror(0); fprintf(stderr, "WARNING: could not open the gtscc db file %s.\n" " I will continue with the recompilation, but\n" " if you recompile any of the files I touched\n" " I'll have to recompile them after you!\n", gts_file); } else { EhSymTableFpDump(table, zap_fp); fclose(zap_fp); } } for (n = 0, nzaps = 0; n < size && nzaps < delta; n++) { object = recompiles[n]; rv = EhObjectRebuild(object, table, verbosity, rootdir); if (rv == -1) return -1; nzaps += rv; object->_recompile = NULL; /* clean up now */ } if (nzaps < delta) { fprintf(stderr, "WARNING: I wanted to gts %d symbols, but only managed\n" " to get %d of them.\n" " Your link may fail with GOT errors.\n", delta, nzaps); } free(recompiles); return n; } typedef struct FileList { char* name; struct FileList* next; } FileList; static FileList* fileListFind(FileList* list, char* name) { FileList* foo; for (foo = list; foo != NULL; foo = foo->next) { if (strcmp(name, foo->name) == 0) return foo; } return NULL; } static FileList* fileListAppend(FileList** list_a, char* name) { FileList* list = *list_a; FileList* foo; FileList* last; for (foo = list, last = NULL; foo != NULL; last = foo, foo = foo->next) ; if (last == NULL) { foo = EH_NEW(FileList); foo->next = NULL; foo->name = strdup(name); *list_a = foo; } else { foo = EH_NEW(FileList); foo->next = NULL; foo->name = strdup(name); last->next = foo; } return *list_a; } #if 0 static FileList* c_list; #endif static FileList* o_list; #if 0 static char* EhSourceAdjustPathname(EhSource* source, char* rootdir) { char buf[MAXPATHLEN]; char buf2[MAXPATHLEN]; char* p; char* q; char* filename = source->name; if (getcwd(buf, sizeof(buf)) == NULL) { fprintf(stderr, "cannot get pwd\n"); return NULL; } strcat(buf, "/"); strcat(buf, filename); if (rootdir == NULL) { filename = buf; } else { if (realpath(buf, buf2) == NULL) { fprintf(stderr, "realpath() failed: %s\n", buf2); return NULL; } if (realpath(rootdir, buf) == NULL) { fprintf(stderr, "realpath() failed: %s\n", buf); return NULL; } strcat(buf, "/"); for (p = buf, q = buf2; *p == *q; p++, q++) ; filename = q; } free(source->name); source->name = strdup(filename); return source->name; } #endif static unsigned katoi(char *buf) { unsigned base = 1; char* s; for (s = buf; isdigit(*s); s++) ; if (*s == 'k' || *s == 'K') base = 1024; *s = '\0'; return base * atoi(buf); } static void usage(void) { fprintf(stderr, "Usage:\n" "as a compiler:\n" "gtscc [gtscc_options] [compiler_options] -c file.c file.cpp ...\n" "gtscc_options:\n" "-gtsfile the gts database file (use this)\n" "-gtszapsymbol convert symbol \n" "-gtsnozapsymbol don't convert symbol \n" "-gtsrootdir the root for the tree (use this)\n" "-gtsverbose be more verbose (3 levels)\n" "-gtsnozap don't convert globals to statics\n" "-gtsnoupdate don't update the database file\n" "as a linker:\n" "gtscc [gtscc_options] [linker_options] file.o ... libxx.a ...\n" "gtscc_options:\n" "-gtsfile the gts database file (use this)\n" "-gtszapsymbol convert symbol \n" "-gtsnozapsymbol don't convert symbol \n" "-gtsrootdir the root for the tree (use this)\n" "-gtspref please recompile these paths first\n" "-gtsunpref please try to avoid recompiling these\n" "-gtsverbose be more verbose (3 levels)\n" "-gtssummary print a summary of global usage\n" "-gtsdump print a detailed listing of all symbols\n" "-gtsmaxglobals [k] maximum globals allowed in target\n" "-gtsnorecompile don't do the normal recompilation\n" "-gtsnolink don't call linker after recompilation\n" "-gtsnoupdate don't update the database file\n" "-help print this\n" ); } int main(int argc, char** argv) { EhSymTable* table = EhSymTableNew(1000); EhSym* sym; FILE* zap_fp; unsigned n = 0; unsigned verbosity = 0; char* arg_buf[256]; unsigned nargs = 0; EhDirMap* dmap = EhDirMapNew(); unsigned do_dump = 0; unsigned do_summary = 0; unsigned do_link = 1; unsigned in_link = 1; unsigned do_audit = 1; unsigned do_zap = 1; unsigned do_assem = 1; unsigned do_recompile = 1; unsigned do_collect = 1; char* name; char* saveprefix = NULL; char* rootdir = NULL; int rv; EhSource* source = NULL; char* gts_file = NULL; char* path_prefs[32]; unsigned npath_prefs = 0; char* path_un_prefs[32]; unsigned npath_un_prefs = 0; char* suffix; unsigned max_globals = DEFAULT_MAX_GLOBALS; FileList* list; if (elf_version(EV_CURRENT) == EV_NONE) { fprintf(stderr, "something losing about your elf lib - sorry!\n"); return 3; /* library out of date */ /* recover from error */ } arg_buf[nargs] = NULL; for (n = 1; n < argc; n++) { if (strcmp(argv[n], "-help") == 0) { usage(); return 0; } else if (strcmp(argv[n], "-gtssummary") == 0) { do_summary = 1; } else if (strcmp(argv[n], "-gtsdump") == 0) { do_dump = 1; } else if (strcmp(argv[n], "-gtsnorecompile") == 0) { do_recompile = 0; } else if (strcmp(argv[n], "-gtsnolink") == 0) { do_link = 0; } else if (strcmp(argv[n], "-gtsverbose") == 0) { verbosity++; } else if (strcmp(argv[n], "-gtsnoupdate") == 0) { do_collect = 0; } else if (strcmp(argv[n], "-gtsnoaudit") == 0) { do_audit = 0; } else if (strcmp(argv[n], "-gtsnozap") == 0) { do_zap = 0; } else if (strcmp(argv[n], "-gtsrootdir") == 0) { if (argc < n+2) { fprintf(stderr, "-gtsrootdir requires an argument\n"); usage(); return 2; } rootdir = argv[n+1]; n++; } else if (strcmp(argv[n], "-gtsdebugsym") == 0) { if (argc < n+2) { fprintf(stderr, "-gtsdebugsym requires an argument\n"); usage(); return 2; } djw_test_name = argv[n+1]; n++; } else if (strcmp(argv[n], "-gtsmaxglobals") == 0) { if (argc < n+2) { fprintf(stderr, "-gtsmaxglobals requires an argument\n"); usage(); return 2; } max_globals = katoi(argv[n+1]); n++; } else if (strcmp(argv[n], "-gtspref") == 0) { if (argc < n+2) { fprintf(stderr, "-gtspref requires an argument\n"); usage(); return 2; } path_prefs[npath_prefs++] = argv[n+1]; path_prefs[npath_prefs] = NULL; n++; } else if (strcmp(argv[n], "-gtsunpref") == 0) { if (argc < n+2) { fprintf(stderr, "-gtsunpref requires an argument\n"); usage(); return 2; } path_un_prefs[npath_un_prefs++] = argv[n+1]; path_un_prefs[npath_un_prefs] = NULL; n++; } else if (strcmp(argv[n], "-gtssaveprefix") == 0) { if (argc < n+2) { fprintf(stderr, "-gtssaveprefix requires an argument\n"); usage(); return 2; } saveprefix = argv[n+1]; n++; } else if (strcmp(argv[n], "-gtsfile") == 0) { struct stat sbuf; if (argc < n+2) { fprintf(stderr, "-gtsfile requires an argument\n"); usage(); return 2; } gts_file = argv[n+1]; if (stat(gts_file, &sbuf) == -1) { fprintf(stderr, "warning: %s does not exist, will be created\n", gts_file); } else { if ((zap_fp = fopen(gts_file, "r")) == NULL) { fprintf(stderr, "you lose cannot open %s\n", gts_file); usage(); return 2; } if (EhSymTableFpLoad(table, zap_fp) == -1) { fprintf(stderr, "error: failed reading symbols from gtsfile %s\n", argv[n+1]); usage(); return 2; } fclose(zap_fp); } n++; } else if (strcmp(argv[n], "-gtszapsymbol") == 0) { if (argc < n+2) { fprintf(stderr, "-gtszapsymbol requires an argument\n"); usage(); return 2; } EhSymTableSetSymbolState(table, argv[n+1], EH_SYM_ZAPPED); n++; } else if (strcmp(argv[n], "-gtsnozapsymbol") == 0) { if (argc < n+2) { fprintf(stderr, "-gtsnozapsymbol requires an argument\n"); usage(); return 2; } EhSymTableSetSymbolState(table, argv[n+1], EH_SYM_DEFINED); n++; } else if (strcmp(argv[n], "-gtsname") == 0) { if (argc < n+2) { fprintf(stderr, "-gtsname requires an argument\n"); usage(); return 2; } sym = EhSymTableFind(table, argv[n+1]); if (!sym) sym = EhSymNewRandomZap(argv[n+1]); n++; do_audit = 1; } else if (strcmp(argv[n], "-c") == 0) { /* do not link */ in_link = 0; do_link = 0; } else if (strcmp(argv[n], "-S") == 0) { /* do not assem */ do_assem = 0; } else if (strcmp(argv[n], "-o") == 0) { /* parse through */ arg_buf[nargs++] = argv[n++]; arg_buf[nargs++] = argv[n]; arg_buf[nargs] = NULL; } else if (strcmp((suffix = suffix_name(argv[n])), ".cpp") == 0 || strcmp(suffix, ".c") == 0 || strcmp(suffix, ".s") == 0) { char pathname[MAXPATHLEN]; make_relative_pathname(pathname, ".", rootdir); source = EhSourceNew(argv[n], arg_buf, pathname); rv = EhSourceCompile(source, table, verbosity, TRUE, /* compile, .s files ignore anyway */ (do_audit + do_zap), do_assem); if (rv == -1) return 1; #if 0 EhSourceAdjustPathname(source, rootdir); #endif } else if (strcmp(suffix, ".o") == 0 || strcmp(suffix, ".a") == 0) { if (fileListFind(o_list, argv[n]) == NULL) { fileListAppend(&o_list, argv[n]); } else { fprintf(stderr, "%s repeated on command line - ignored\n", argv[n]); } arg_buf[nargs++] = argv[n]; arg_buf[nargs] = NULL; } else if (strncmp(argv[n], "-L", 2) == 0) { EhDirMapAddDirectory(dmap, &argv[n][2]); } else if (strncmp(argv[n], "-l", 2) == 0) { char pathbuf[MAXPATHLEN]; name = EhDirMapGetLibName(dmap, &argv[n][2], pathbuf); if (name != NULL) { if (fileListFind(o_list, name) == NULL) { fileListAppend(&o_list, name); } else { fprintf(stderr, "%s repeated on command line - ignored\n", name); } } else { fprintf(stderr, "unable to resolve library reference %s - ignoring\n", argv[n]); } arg_buf[nargs++] = argv[n]; arg_buf[nargs] = NULL; } else { arg_buf[nargs++] = argv[n]; arg_buf[nargs] = NULL; } } /* * Analyse objects. */ if (o_list != NULL) { for (list = o_list; list != NULL; list = list->next) { if (eh_process_file(list->name, table, rootdir)) { fprintf(stderr, "oops we died around %s\n", list->name); return 1; } } /* look for unused objects */ EhSymTableMap(table, object_nusers_mappee, 0); } if (do_summary) { print_summary(table); } if (do_dump) { print_dump(table); } if (!in_link && gts_file != NULL) { FILE* zap_fp; if ((zap_fp = fopen(gts_file, "w")) == NULL) { perror(0); usage(); return 2; } EhSymTableFpDump(table, zap_fp); fclose(zap_fp); return 0; } /* * Now the fun really starts. */ if (do_recompile) { char** pp = NULL; char** up = NULL; if (npath_prefs > 0) pp = path_prefs; if (npath_un_prefs > 0) up = path_un_prefs; if (!do_collect) gts_file = NULL; rv = do_recompilation(table, gts_file, max_globals, pp, up, rootdir, verbosity); if (rv == -1) return 1; } /* * Finally. */ if (do_link) { int status; arg_buf[nargs+1] = NULL; for (n = nargs; n > 0; n--) arg_buf[n] = arg_buf[n-1]; arg_buf[0] = LD_COMMAND; status = do_command("link", arg_buf, verbosity); if (status == -1) return 3; else return status; } return 0; }