/* Copyright (C) 1995-2001 Activision, Inc. This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ /*********************************************************************** * Methods for finding module functions involved during a crash using * * map files. * ***********************************************************************/ #ifndef NO_NETWORK #define NO_NETWORK /* needed to shield dp stuff in dpprivy.h */ #endif #ifndef AEHMAP_PRIVATE #define AEHMAP_PRIVATE #endif #include #include #include #include "aeh.h" #ifdef _WIN32 #include #include #include "tlstuff.h" #else #define GetTickCount() (0) #endif /* add a name to mapfile's function linked list; * sorted by address from high to low */ static void aeh_map_addFunc(aeh_map_t *aehmap, char *name, unsigned address) { aeh_map_func_t *new_function; aeh_map_func_t *last = NULL; aeh_map_func_t *curr; if (!name) return; new_function = (aeh_map_func_t *)malloc(sizeof(aeh_map_func_t)); if (!new_function) return; memset(new_function, 0, sizeof(aeh_map_func_t)); new_function->start_addr = address; /* CRA: do some extra processing on C++ functions * after this processing C++ functions are in the format Class::Function :) * also do extra processing for fast call functions */ #ifdef _WIN32 if (name[0] == '?') { char buff[BUFFER_SIZE]; int len; UNDECORATESYMNAME pUnDecorateSymbolName = InitUnDecorateSymbolName(); if (pUnDecorateSymbolName && (len=pUnDecorateSymbolName(name, buff, BUFFER_SIZE-1, 0))) { buff[len] = '\0'; new_function->name = strdup(buff); } else new_function->name = strdup(name); } else #endif new_function->name = strdup(name); if (aehmap->firstfunc == NULL) { /* no entries for this mod */ new_function->next = NULL; aehmap->firstfunc = new_function; aehmap->lastfunc = new_function; return; } if (address >= (aehmap->firstfunc)->start_addr) { new_function->next = aehmap->firstfunc; aehmap->firstfunc = new_function; return; } if (address <= (aehmap->lastfunc)->start_addr) { (aehmap->lastfunc)->next = new_function; new_function->next = NULL; aehmap->lastfunc = new_function; return; } curr = aehmap->firstfunc; while (curr && (address < curr->start_addr)) { last = curr; curr = curr->next; } if (last == NULL) return; new_function->next = last->next; last->next = new_function; } #define aeh_map_func_alg_MAGIC 0x7865 static void aeh_map_Unload(aeh_map_t *aehmap, int bWriteAlg); /* Open binary .alg mapfile generated by previous aeh_map_Unload call; * generates a list of function names and their start addresses * returns aeh_RES_EMPTY if could not load file */ static int aeh_map_algRead(aeh_map_t *aehmap, char *algpath) { FILE *fh; int nread, nsize; unsigned short tag; char buffer[BUFFER_SIZE], *ptr; char name[BUFFER_SIZE]; unsigned address; unsigned prefadr = 0; fh = fopen(algpath, "rb"); if (!fh) return aeh_RES_EMPTY; if ((fread(&tag, sizeof(unsigned short), 1, fh) != 1) || (tag != aeh_map_func_alg_MAGIC) || (fread(&prefadr, sizeof(unsigned long), 1, fh) != 1)) { aehDPRINT(("aeh_map_Load: corrupted .alg file\n")); fclose(fh); remove(algpath); return aeh_RES_BAD; } aehmap->load_addr = prefadr; ptr = buffer; nsize = BUFFER_SIZE; while ((nread = fread(ptr, 1, nsize, fh)) > 0) { char *eobuf = ptr + nread; ptr = buffer; while (ptr + (sizeof(unsigned)+sizeof(unsigned short)) < eobuf){ unsigned short namelen; address = *((unsigned*)ptr); namelen = *((unsigned short*)(ptr + sizeof(unsigned))); if (ptr+(sizeof(unsigned)+sizeof(unsigned short)+namelen) < eobuf) { ptr += sizeof(unsigned) + sizeof(unsigned short); memcpy(name, ptr, namelen); name[namelen] = '\0'; ptr += namelen; aeh_map_addFunc(aehmap, name, address); } else break; } if (ptr < eobuf) { int nleft = eobuf - ptr; memcpy(buffer, ptr, nleft); ptr = buffer + nleft; nsize = BUFFER_SIZE - nleft; } } fclose (fh); if (*((unsigned short*)buffer) != aeh_map_func_alg_MAGIC) { /* .alg corrupted; delete it */ aehDPRINT(("aeh_map_Load: corrupted .alg file\n")); aeh_map_Unload(aehmap, 0); remove(algpath); return aeh_RES_BAD; } aehDPRINT(("exit aeh_map_Load time %d\n", GetTickCount())); return aeh_RES_OK; } static int aeh_map_algWrite(aeh_map_t *aehmap, char *algpath) { FILE *fh; int bWriteError = 0; aeh_map_func_t *curr = aehmap->firstfunc; char buffer[BUFFER_SIZE], *ptr; unsigned short tag = aeh_map_func_alg_MAGIC; fh = fopen(algpath, "rb"); if (fh) { /* .alg already exists */ fclose(fh); return aeh_RES_ALREADY; } fh = fopen(algpath, "wb"); if (!fh || (fwrite(&tag, sizeof(unsigned short), 1, fh) != 1) || (fwrite(&(aehmap->load_addr), sizeof(unsigned long), 1, fh) != 1)) { aehDPRINT(("aeh_map_Unload: error writing %s\n", algpath)); if (fh) fclose(fh); remove(algpath); return aeh_RES_BAD; } ptr = buffer; while (curr) { aeh_map_func_t *next = curr->next; unsigned short namelen = strlen(curr->name); if (ptr + (sizeof(unsigned) + sizeof(unsigned short) + namelen) > buffer + BUFFER_SIZE) { unsigned int len = ptr - buffer; if (fwrite(buffer, 1, len, fh) != len) { aehDPRINT(("aeh_map_Unload: error writing %s\n", algpath)); bWriteError = 1; break; } ptr = buffer; } *((unsigned*)ptr) = curr->start_addr; ptr += sizeof(unsigned); *((unsigned short*)ptr) = namelen; ptr += sizeof(unsigned short); memcpy(ptr, curr->name, namelen); ptr += namelen; curr = next; } if (!bWriteError) { if (ptr > buffer) { unsigned int len = ptr - buffer; if (fwrite(buffer, 1, len, fh) != len) bWriteError = 1; } if (!bWriteError && (fwrite(&tag, sizeof(unsigned short), 1, fh) != 1)) bWriteError = 1; } fclose(fh); if (bWriteError) { aehDPRINT(("aeh_map_Unload: error writing %s\n", algpath)); remove(algpath); return aeh_RES_BAD; } return aeh_RES_OK; } /* Open a msdevstudio msvc4.2 .map file and parse it * generates a list of function names and their start addresses * returns aeh_RES_EMPTY if could not load file / file out of date */ static int aeh_map_Load(aeh_map_t *aehmap) { FILE *fp; char buffer[BUFFER_SIZE]; char algpath[aeh_MAX_PATH], *ptr; int done; int crap; char name[BUFFER_SIZE]; unsigned address; unsigned prefadr = 0; #ifdef REQUIRE_CORRECT_LOG int log_correct; #endif if (!aehmap) return aeh_RES_BAD; /* check if have .alg for this mapfile; use if it exists */ strcpy(algpath, aehmap->path); if ((ptr = strstr(algpath, ".map"))) { strcpy(ptr, ".alg"); if (aeh_map_algRead(aehmap, algpath) == aeh_RES_OK) return aeh_RES_OK; } /* read .map for this mapfile */ fp = fopen(aehmap->path, "r"); if (!fp) return aeh_RES_EMPTY; /* search through until ' Address' found; also get preferred address */ aehDPRINT(("call aeh_map_Load time %d\n", GetTickCount())); done = 0; while (!done) { fgets (buffer, BUFFER_SIZE, fp); if (feof (fp)) done = 1; if (strncmp(buffer, " Preferred", strlen(" Preferred")-1) == 0) if (sscanf(buffer, " %*s %*s %*s %*s %x", &prefadr) != 1) aehDPRINT(("aeh_map_Load: error getting preferred address\n")); if (sscanf (buffer, "%s", name) == 1) { if (strcmp (name, "Address") == 0) done = 1; } } if (prefadr) done = 0; else aehDPRINT(("didn't get preferred adr\n")); aehmap->load_addr = prefadr; fgets (buffer, BUFFER_SIZE, fp); /* ignore single blank line */ /* get every line containing a function name and an address */ while (!done) { fgets (buffer, BUFFER_SIZE, fp); /* line must start with ' 0nnn:' where n is a number */ if ((buffer[0] == ' ') && (buffer[1] == '0') && (buffer[5] == ':') && (sscanf(buffer, "%x:%x %s %x", &crap, &crap, name, &address) == 4)) { address = (address - prefadr); aeh_map_addFunc(aehmap, name, address); } if (feof (fp)) done = 1; } fclose (fp); aehDPRINT(("exit aeh_map_Load time %d\n", GetTickCount())); return aeh_RES_OK; } /* Free memory allocated during aeh_map_Load() and if bWriteAlg set, * write aehmap function info to binary .alg file */ static void aeh_map_Unload(aeh_map_t *aehmap, int bWriteAlg) { aeh_map_func_t *curr = aehmap->firstfunc; char algpath[aeh_MAX_PATH], *ptr; strcpy(algpath, aehmap->path); if (bWriteAlg && curr && (ptr = strstr(algpath, ".map"))) { strcpy(ptr, ".alg"); aeh_map_algWrite(aehmap, algpath); } while (curr) { aeh_map_func_t *next = curr->next; /* free all entries */ if (curr->name) free(curr->name); free(curr); curr = next; } } /* Find map entry corresponding the module in aehmodfile */ int aeh_map_Find(aeh_mapcat_t *aehmapcat, aeh_modfile_t *aehmodfile) { unsigned int i; if (!aehmapcat || !aehmodfile) { aehDPRINT(("aeh_map_Find: err: null arguments\n")); return -1; } for (i = 0; i < aehmapcat->nmap; i++) { aeh_map_t *mapbuf = (aehmapcat->map)+i; if (mapbuf->crc == aehmodfile->crc) { if (!mapbuf->firstfunc) { int err = aeh_map_Load(mapbuf); if (err != aeh_RES_OK) { aehDPRINT(("aeh_map_Find: aeh_map_Load return err %d\n", err)); return -1; } } return i; } } return -1; } /*-------------------------------------------------------------------------- Get information from the mapfiles in the mapfile catalog file. Call aeh_mapcat_Destroy() after finished using the returned value of aehmapcat. Input: catpath (path to mapfile catalog file) Output: aehmapcat Return: aeh_RES_NOMEM if memory couldn't be allocated aeh_RES_BUG if catalog file couldn't be opened aeh_RES_BAD if couldn't get catalog info aeh_RES_OK on success --------------------------------------------------------------------------*/ int aeh_mapcat_Create(aeh_mapcat_t *aehmapcat, const char *catpath) { FILE *fp; unsigned int iloaded; char buf[BUFFER_SIZE]; char path[aeh_MAX_PATH]; aeh_map_t mapbuf; if (!aehmapcat || !catpath) return aeh_RES_BAD; if (!(fp = fopen(catpath, "r"))) return aeh_RES_BUG; mapbuf.path = &path[0]; aehmapcat->nmap = 0; while (fgets(buf, BUFFER_SIZE, fp)) { if (sscanf(buf, "%lx %[^\n\r\f]", &mapbuf.crc, mapbuf.path) == 2) aehmapcat->nmap++; } if (!((aehmapcat->map) = (aeh_map_t *)malloc((aehmapcat->nmap) * sizeof(aeh_map_t)))) return aeh_RES_NOMEM; memset(aehmapcat->map, 0, (aehmapcat->nmap) * sizeof(aeh_map_t)); /* load mapfile entries */ if (fseek(fp, 0L, SEEK_SET)) { aehDPRINT(("aeh_mapcat_Create: can't reset file position\n")); return aeh_RES_BAD; } iloaded = 0; while (fgets(buf, BUFFER_SIZE, fp)) { char fullpath[aeh_MAX_PATH]; char *ptr; if (iloaded >= aehmapcat->nmap) { aehDPRINT(("aeh_mapcat_Create: iloaded %d bigger than nmap %d\n", iloaded, aehmapcat->nmap)); return aeh_RES_BAD; } if (sscanf(buf, "%lx %[^\n\r\f]", &mapbuf.crc, mapbuf.path) != 2) { aehDPRINT(("aeh_mapcat_Create: error reading mapfile entry %s\n", buf)); continue; } strcpy(fullpath, catpath); if ((ptr = strrchr(fullpath, aeh_PATH_DELIMIT))) { ptr++; strcpy(ptr, mapbuf.path); (aehmapcat->map)[iloaded].path = strdup(fullpath); } else (aehmapcat->map)[iloaded].path = strdup(mapbuf.path); if ((ptr = strrchr(mapbuf.path, aeh_PATH_DELIMIT))) { *ptr = '\0'; (aehmapcat->map)[iloaded].reldir = strdup(mapbuf.path); } else (aehmapcat->map)[iloaded].reldir = strdup("."); (aehmapcat->map)[iloaded].crc = mapbuf.crc; iloaded++; } fclose(fp); if (iloaded != aehmapcat->nmap) { aehDPRINT(("aeh_mapcat_Create: iloaded %d is not equal to nmap %d\n", iloaded, aehmapcat->nmap)); aeh_mapcat_Destroy(aehmapcat); return aeh_RES_BAD; } return aeh_RES_OK; } /*-------------------------------------------------------------------------- Free memory allocated to aeh_mapcat_t during aehmapcat_Create(). Input: aehmapcat --------------------------------------------------------------------------*/ void aeh_mapcat_Destroy(aeh_mapcat_t *aehmapcat) { unsigned int i; if (aehmapcat && aehmapcat->map) { for (i = 0; i < aehmapcat->nmap; i++) { aeh_map_t *mapbuf = (aehmapcat->map)+i; aeh_map_Unload(mapbuf, 1); if (mapbuf->path) free(mapbuf->path); if (mapbuf->reldir) free(mapbuf->reldir); } free(aehmapcat->map); } }