Back to index

lightning-sunbird  0.9+nobinonly
SymFiles.cpp
Go to the documentation of this file.
00001 /* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
00002  *
00003  * The contents of this file are subject to the Netscape Public
00004  * License Version 1.1 (the "License"); you may not use this file
00005  * except in compliance with the License. You may obtain a copy of
00006  * the License at http://www.mozilla.org/NPL/
00007  *
00008  * Software distributed under the License is distributed on an "AS
00009  * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
00010  * implied. See the License for the specific language governing
00011  * rights and limitations under the License.
00012  *
00013  * The Original Code is Mozilla Communicator client code, released
00014  * March 31, 1998.
00015  *
00016  * The Initial Developer of the Original Code is Netscape
00017  * Communications Corporation.  Portions created by Netscape are
00018  * Copyright (C) 1998 Netscape Communications Corporation. All
00019  * Rights Reserved.
00020  *
00021  * Contributor(s):
00022  *
00023  * Patrick C. Beard <beard@netscape.com>
00024  *
00025  * Alternatively, the contents of this file may be used under the
00026  * terms of the GNU Public License (the "GPL"), in which case the
00027  * provisions of the GPL are applicable instead of those above.
00028  * If you wish to allow use of your version of this file only
00029  * under the terms of the GPL and not to allow others to use your
00030  * version of this file under the NPL, indicate your decision by
00031  * deleting the provisions above and replace them with the notice
00032  * and other provisions required by the GPL.  If you do not delete
00033  * the provisions above, a recipient may use your version of this
00034  * file under either the NPL or the GPL.
00035  */
00036 
00037 #include <stdio.h>
00038 #include <stdlib.h>
00039 #include <string.h>
00040 #include <Memory.h>
00041 
00042 #include "SymFiles.h"
00043 #include "sym_file.h"
00044 #include "gc.h"
00045 
00046 struct sym_file {
00047        // file data structures.
00048        FSSpec mFileSpec;
00049        SInt16 mFileRef;
00050        UInt8* mPageBuffer;
00051        UInt32 mPageOffset;
00052        UInt32 mBufferOffset;
00053        
00054        DiskSymbolHeaderBlock mHeader;
00055        ResourceTableEntry mCodeEntry;
00056        UInt8** mNameTable;
00057        
00058        sym_file(const FSSpec* spec);
00059        ~sym_file();
00060        
00061        void* operator new(size_t n) { return ::GC_malloc(n); }
00062        void operator delete(void* ptr) { return ::GC_free(ptr); }
00063        
00064        bool init();
00065        bool open();
00066        bool close();
00067        bool seek(UInt32 position);
00068        UInt32 read(void* buffer, UInt32 count);
00069 
00070        const UInt8* getName(UInt32 nameIndex);
00071 };
00072 
00073 sym_file::sym_file(const FSSpec* spec)
00074        :      mFileSpec(*spec), mFileRef(-1), mPageBuffer(NULL), mPageOffset(0), mBufferOffset(0),
00075               mNameTable(NULL)
00076 {
00077 }
00078 
00079 sym_file::~sym_file()
00080 {
00081        // this seems to die when we are being debugged.
00082        if (mFileRef != -1) {
00083               ::FSClose(mFileRef);
00084               mFileRef = -1;
00085        }
00086        
00087        if (mPageBuffer != NULL) {
00088               GC_free(mPageBuffer);
00089               mPageBuffer = NULL;
00090        }
00091        
00092        if (mNameTable != NULL) {
00093               UInt8** nameTable = mNameTable;
00094               UInt16 pageCount = mHeader.dshb_nte.dti_page_count;
00095               while (pageCount-- > 0) {
00096                      GC_free(*nameTable++);
00097               }
00098               GC_free(mNameTable);
00099               mNameTable = NULL;
00100        }
00101 }
00102 
00103 bool sym_file::init()
00104 {
00105        // open the file.
00106        if (!open())
00107               return false;
00108 
00109        // read the header. should make sure it is a valid .xSYM file, etc.
00110        DiskSymbolHeaderBlock& header = mHeader;
00111        long count = sizeof(header);
00112        if (::FSRead(mFileRef, (long*) &count, &header) != noErr || count != sizeof(header))
00113               return false;
00114 
00115        // read the name table.
00116        // mNameTable = new UInt8*[header.dshb_nte.dti_page_count];
00117        mNameTable = (UInt8**) GC_malloc_ignore_off_page(header.dshb_nte.dti_page_count * sizeof(UInt8*));
00118        if (mNameTable == NULL)
00119               return false;
00120        
00121        // seek to first page of the name table.
00122        ::SetFPos(mFileRef, fsFromStart, header.dshb_nte.dti_first_page * header.dshb_page_size);
00123        // read all of the pages into memory, for speed.
00124        for (int i = 0; i < header.dshb_nte.dti_page_count; i++) {
00125               // allocate each page atomically, so GC won't have to scan them.
00126               UInt8* page = mNameTable[i] = (UInt8*) GC_malloc_atomic(header.dshb_page_size);
00127               if (page == NULL)
00128                      return false;
00129               count = header.dshb_page_size;
00130               if (::FSRead(mFileRef, &count, page) != noErr || count != header.dshb_page_size)
00131                      return false;
00132        }
00133 
00134        // allocate the page buffer and initialize offsets.
00135        // mPageBuffer = (UInt8*) new UInt8[header.dshb_page_size];
00136        mPageBuffer = (UInt8*) GC_malloc_atomic(header.dshb_page_size);
00137 
00138        // read the RTE tables.
00139        seek(header.dshb_rte.dti_first_page * header.dshb_page_size + sizeof(ResourceTableEntry));
00140        for (int i = 1; i <= header.dshb_rte.dti_object_count; i++) {
00141               ResourceTableEntry resEntry;
00142               if (read(&resEntry, sizeof(resEntry)) == sizeof(resEntry)) {
00143                      switch (resEntry.rte_ResType) {
00144                      case 'CODE':
00145                             mCodeEntry = resEntry;
00146                             break;
00147                      case 'DATA':
00148                             break;
00149                      }
00150               }
00151        }
00152        
00153        return true;
00154 }
00155 
00156 bool sym_file::open()
00157 {
00158        if (mFileRef == -1) {
00159               // open the specified symbol file.
00160               if (::FSpOpenDF(&mFileSpec, fsRdPerm, &mFileRef) != noErr)
00161                      return false;
00162        }
00163        return true;
00164 }
00165 
00166 bool sym_file::close()
00167 {
00168        if (mFileRef != -1) {
00169               ::FSClose(mFileRef);
00170               mFileRef = -1;
00171        }
00172        return true;
00173 }
00174 
00175 bool sym_file::seek(UInt32 position)
00176 {
00177        if (position != (mPageOffset + mBufferOffset)) {
00178               // if position is off current page, the resync the buffer.
00179               UInt32 pageSize = mHeader.dshb_page_size;
00180               if (position < mPageOffset || position >= (mPageOffset + pageSize)) {
00181                      // make sure the file is open.
00182                      if (!open())
00183                             return false;
00184                      // read the nearest page to this offset.
00185                      mPageOffset = (position - (position % pageSize));
00186                      if (::SetFPos(mFileRef, fsFromStart, mPageOffset) != noErr)
00187                             return false;
00188                      long count = pageSize;
00189                      if (::FSRead(mFileRef, &count, mPageBuffer) != noErr)
00190                             return false;
00191               }
00192               mBufferOffset = (position - mPageOffset);
00193        }
00194        return true;
00195 }
00196 
00197 UInt32 sym_file::read(void* buffer, UInt32 count)
00198 {
00199        // we don't handle reads that aren't on the current page.
00200        if ((mBufferOffset + count) < mHeader.dshb_page_size) {
00201               ::BlockMoveData(mPageBuffer + mBufferOffset, buffer, count);
00202               mBufferOffset += count;
00203               return count;
00204        }
00205        return 0;
00206 }
00207 
00208 const UInt8* sym_file::getName(UInt32 nameIndex)
00209 {
00210        UInt32 nameOffset = 2 * nameIndex;
00211        const UInt8* page = mNameTable[nameOffset / mHeader.dshb_page_size];
00212        const UInt8* name = page + (nameOffset % mHeader.dshb_page_size);
00213        return name;
00214 }
00215 
00216 static bool getFileReferenceTableEntry(sym_file* symbols,
00217                                                                  const FileReference& fileRef,
00218                                                                  FileReferenceTableEntry* frte)
00219 {
00220        const DiskSymbolHeaderBlock& header = symbols->mHeader;
00221        UInt32 fileRefsPerPage = (header.dshb_page_size / sizeof(FileReferenceTableEntry));
00222        UInt32 fileRefPage = (fileRef.fref_frte_index / fileRefsPerPage);
00223        UInt32 fileRefIndex = (fileRef.fref_frte_index % fileRefsPerPage);
00224 
00225        // seek to the specified frte.
00226        symbols->seek((header.dshb_frte.dti_first_page + fileRefPage) * header.dshb_page_size + fileRefIndex * sizeof(FileReferenceTableEntry));
00227        return (symbols->read(frte, sizeof(FileReferenceTableEntry)) == sizeof(FileReferenceTableEntry));
00228 }
00229 
00230 static bool getContainedStatementTableEntry(sym_file* symbols,
00231                                                                       UInt32 statementIndex,
00232                                                                       ContainedStatementsTableEntry* statementEntry)
00233 {
00234        const DiskSymbolHeaderBlock& header = symbols->mHeader;
00235        UInt32 entriesPerPage = (header.dshb_page_size / sizeof(ContainedStatementsTableEntry));
00236        UInt32 entryPage = (statementIndex / entriesPerPage);
00237        UInt32 entryIndex = (statementIndex % entriesPerPage);
00238        
00239        // seek to the specified statement entry.
00240        symbols->seek((header.dshb_csnte.dti_first_page + entryPage) * header.dshb_page_size + entryIndex * sizeof(ContainedStatementsTableEntry));
00241        return (symbols->read(statementEntry, sizeof(ContainedStatementsTableEntry)) == sizeof(ContainedStatementsTableEntry));
00242 }
00243 
00244 inline bool isMeatyModule(const ModulesTableEntry& moduleEntry)
00245 {
00246        return (moduleEntry.mte_kind == kModuleKindProcedure) || (moduleEntry.mte_kind == kModuleKindFunction);
00247 }
00248 
00249 inline UInt32 delta(UInt32 x, UInt32 y)
00250 {
00251        if (x > y)
00252               return (x - y);
00253        else
00254               return (y - x);
00255 }
00256 
00257 int get_source(sym_file* symbols, UInt32 codeOffset, char symbolName[256], char fileName[256], UInt32* fileOffset)
00258 {
00259        const DiskSymbolHeaderBlock& header = symbols->mHeader;
00260        const ResourceTableEntry& codeEntry = symbols->mCodeEntry;
00261 
00262        // since module entries can't span pages, must compute which page module entry size.
00263        UInt32 modulesPerPage = (header.dshb_page_size / sizeof(ModulesTableEntry));
00264 
00265        // search for MTE nearest specified offset.
00266        // seek to first MTE.
00267        for (UInt16 i = codeEntry.rte_mte_first; i <= codeEntry.rte_mte_last; i++) {
00268               ModulesTableEntry moduleEntry;
00269               UInt32 modulePage = (i / modulesPerPage);
00270               UInt32 moduleIndex = (i % modulesPerPage);
00271               symbols->seek((header.dshb_mte.dti_first_page + modulePage) * header.dshb_page_size + moduleIndex * sizeof(ModulesTableEntry));
00272               if (symbols->read(&moduleEntry, sizeof(moduleEntry)) == sizeof(moduleEntry)) {
00273                      if (isMeatyModule(moduleEntry) && (codeOffset >= moduleEntry.mte_res_offset) && (codeOffset - moduleEntry.mte_res_offset) < moduleEntry.mte_size) {
00274                             FileReferenceTableEntry frte;
00275                             if (getFileReferenceTableEntry(symbols, moduleEntry.mte_imp_fref, &frte)) {
00276                                    UInt32 length;
00277                                    // get the name of the symbol.
00278                                    const UInt8* moduleName = symbols->getName(moduleEntry.mte_nte_index);
00279                                    // printf("module name = %#s\n", moduleName);
00280                                    // trim off the leading "."
00281                                    length = moduleName[0] - 1;
00282                                    BlockMoveData(moduleName + 2, symbolName, length);
00283                                    symbolName[length] = '\0';
00284                                    // get the name of the file.
00285                                    const UInt8* name = symbols->getName(frte.frte_fn.nte_index);
00286                                    length = name[0];
00287                                    BlockMoveData(name + 1, fileName, length);
00288                                    fileName[length] = '\0';
00289                                    // printf("file name = %s\n", fileName);
00290                                    // try to refine the location, using the contained statements table entries.
00291                                    UInt32 closestFileOffset = moduleEntry.mte_imp_fref.fref_offset;
00292                                    UInt32 closestCodeOffset = moduleEntry.mte_res_offset;
00293                                    UInt32 closestCodeDelta = 0xFFFFFFFF;
00294                                    UInt32 currentFileOffset, currentCodeOffset = moduleEntry.mte_res_offset, currentCodeDelta;
00295                                    for (UInt32 j = moduleEntry.mte_csnte_idx_1; j <= moduleEntry.mte_csnte_idx_2; j++) {
00296                                           // only consider offsets less than the actual code offset, so we'll be sure
00297                                           // to match the nearest line before the code offset. this could probably be
00298                                           // a termination condition as well.
00299                                           if (currentCodeOffset > codeOffset)
00300                                                  break;
00301                                           ContainedStatementsTableEntry statementEntry;
00302                                           if (getContainedStatementTableEntry(symbols, j, &statementEntry)) {
00303                                                  switch (statementEntry.csnte_file.change) {
00304                                                  case kSourceFileChange:
00305                                                         currentFileOffset = statementEntry.csnte_file.fref.fref_offset;
00306                                                         break;
00307                                                  case kEndOfList:
00308                                                         break;
00309                                                  default:
00310                                                         currentFileOffset += statementEntry.csnte.file_delta;
00311                                                         currentCodeOffset = moduleEntry.mte_res_offset + statementEntry.csnte.mte_offset;
00312                                                         if (currentCodeOffset <= codeOffset) {
00313                                                                currentCodeDelta = delta(currentCodeOffset, codeOffset);
00314                                                                if (currentCodeDelta < closestCodeDelta) {
00315                                                                       closestFileOffset = currentFileOffset;
00316                                                                       closestCodeOffset = currentCodeOffset;
00317                                                                       closestCodeDelta = currentCodeDelta;
00318                                                                }
00319                                                         }
00320                                                         break;
00321                                                  }
00322                                           }
00323                                    }
00324                                    *fileOffset = closestFileOffset;
00325                                    // printf("closest file offset = %d\n", closestFileOffset);
00326                                    // printf("closest code offset = %d\n", closestCodeOffset);
00327                                    return 1;
00328                             }
00329                      }
00330               }
00331        }
00332        return 0;
00333 }
00334 
00335 sym_file* open_sym_file(const FSSpec* symSpec)
00336 {
00337        sym_file* symbols = new sym_file(symSpec);
00338        if (symbols != NULL) {
00339               if (!symbols->init()) {
00340                      delete symbols;
00341                      return NULL;
00342               }
00343               // don't leave the file open, if possible.
00344               symbols->close();
00345        }
00346        return symbols;
00347 }
00348 
00349 void close_sym_file(sym_file* symbols)
00350 {
00351        if (symbols != NULL) {
00352               delete symbols;
00353        }
00354 }