/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- * * The contents of this file are subject to the Netscape 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/NPL/ * * 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 Communicator client code, released * March 31, 1998. * * The Initial Developer of the Original Code is Netscape * Communications Corporation. Portions created by Netscape are * Copyright (C) 1998 Netscape Communications Corporation. All * Rights Reserved. * * Contributor(s): * * Patrick C. Beard * * Alternatively, the contents of this file may be used under the * terms of the GNU Public License (the "GPL"), in which case the * provisions of the GPL are applicable instead of those above. * If you wish to allow use of your version of this file only * under the terms of the GPL and not to allow others to use your * version of this file under the NPL, indicate your decision by * deleting the provisions above and replace them with the notice * and other provisions required by the GPL. If you do not delete * the provisions above, a recipient may use your version of this * file under either the NPL or the GPL. */ #include #include #include #include #include "SymFiles.h" #include "sym_file.h" #include "gc.h" struct sym_file { // file data structures. FSSpec mFileSpec; SInt16 mFileRef; UInt8* mPageBuffer; UInt32 mPageOffset; UInt32 mBufferOffset; DiskSymbolHeaderBlock mHeader; ResourceTableEntry mCodeEntry; UInt8** mNameTable; sym_file(const FSSpec* spec); ~sym_file(); void* operator new(size_t n) { return ::GC_malloc(n); } void operator delete(void* ptr) { return ::GC_free(ptr); } bool init(); bool open(); bool close(); bool seek(UInt32 position); UInt32 read(void* buffer, UInt32 count); const UInt8* getName(UInt32 nameIndex); }; sym_file::sym_file(const FSSpec* spec) : mFileSpec(*spec), mFileRef(-1), mPageBuffer(NULL), mPageOffset(0), mBufferOffset(0), mNameTable(NULL) { } sym_file::~sym_file() { // this seems to die when we are being debugged. if (mFileRef != -1) { ::FSClose(mFileRef); mFileRef = -1; } if (mPageBuffer != NULL) { GC_free(mPageBuffer); mPageBuffer = NULL; } if (mNameTable != NULL) { UInt8** nameTable = mNameTable; UInt16 pageCount = mHeader.dshb_nte.dti_page_count; while (pageCount-- > 0) { GC_free(*nameTable++); } GC_free(mNameTable); mNameTable = NULL; } } bool sym_file::init() { // open the file. if (!open()) return false; // read the header. should make sure it is a valid .xSYM file, etc. DiskSymbolHeaderBlock& header = mHeader; long count = sizeof(header); if (::FSRead(mFileRef, (long*) &count, &header) != noErr || count != sizeof(header)) return false; // read the name table. // mNameTable = new UInt8*[header.dshb_nte.dti_page_count]; mNameTable = (UInt8**) GC_malloc_ignore_off_page(header.dshb_nte.dti_page_count * sizeof(UInt8*)); if (mNameTable == NULL) return false; // seek to first page of the name table. ::SetFPos(mFileRef, fsFromStart, header.dshb_nte.dti_first_page * header.dshb_page_size); // read all of the pages into memory, for speed. for (int i = 0; i < header.dshb_nte.dti_page_count; i++) { // allocate each page atomically, so GC won't have to scan them. UInt8* page = mNameTable[i] = (UInt8*) GC_malloc_atomic(header.dshb_page_size); if (page == NULL) return false; count = header.dshb_page_size; if (::FSRead(mFileRef, &count, page) != noErr || count != header.dshb_page_size) return false; } // allocate the page buffer and initialize offsets. // mPageBuffer = (UInt8*) new UInt8[header.dshb_page_size]; mPageBuffer = (UInt8*) GC_malloc_atomic(header.dshb_page_size); // read the RTE tables. seek(header.dshb_rte.dti_first_page * header.dshb_page_size + sizeof(ResourceTableEntry)); for (int i = 1; i <= header.dshb_rte.dti_object_count; i++) { ResourceTableEntry resEntry; if (read(&resEntry, sizeof(resEntry)) == sizeof(resEntry)) { switch (resEntry.rte_ResType) { case 'CODE': mCodeEntry = resEntry; break; case 'DATA': break; } } } return true; } bool sym_file::open() { if (mFileRef == -1) { // open the specified symbol file. if (::FSpOpenDF(&mFileSpec, fsRdPerm, &mFileRef) != noErr) return false; } return true; } bool sym_file::close() { if (mFileRef != -1) { ::FSClose(mFileRef); mFileRef = -1; } return true; } bool sym_file::seek(UInt32 position) { if (position != (mPageOffset + mBufferOffset)) { // if position is off current page, the resync the buffer. UInt32 pageSize = mHeader.dshb_page_size; if (position < mPageOffset || position >= (mPageOffset + pageSize)) { // make sure the file is open. if (!open()) return false; // read the nearest page to this offset. mPageOffset = (position - (position % pageSize)); if (::SetFPos(mFileRef, fsFromStart, mPageOffset) != noErr) return false; long count = pageSize; if (::FSRead(mFileRef, &count, mPageBuffer) != noErr) return false; } mBufferOffset = (position - mPageOffset); } return true; } UInt32 sym_file::read(void* buffer, UInt32 count) { // we don't handle reads that aren't on the current page. if ((mBufferOffset + count) < mHeader.dshb_page_size) { ::BlockMoveData(mPageBuffer + mBufferOffset, buffer, count); mBufferOffset += count; return count; } return 0; } const UInt8* sym_file::getName(UInt32 nameIndex) { UInt32 nameOffset = 2 * nameIndex; const UInt8* page = mNameTable[nameOffset / mHeader.dshb_page_size]; const UInt8* name = page + (nameOffset % mHeader.dshb_page_size); return name; } static bool getFileReferenceTableEntry(sym_file* symbols, const FileReference& fileRef, FileReferenceTableEntry* frte) { const DiskSymbolHeaderBlock& header = symbols->mHeader; UInt32 fileRefsPerPage = (header.dshb_page_size / sizeof(FileReferenceTableEntry)); UInt32 fileRefPage = (fileRef.fref_frte_index / fileRefsPerPage); UInt32 fileRefIndex = (fileRef.fref_frte_index % fileRefsPerPage); // seek to the specified frte. symbols->seek((header.dshb_frte.dti_first_page + fileRefPage) * header.dshb_page_size + fileRefIndex * sizeof(FileReferenceTableEntry)); return (symbols->read(frte, sizeof(FileReferenceTableEntry)) == sizeof(FileReferenceTableEntry)); } static bool getContainedStatementTableEntry(sym_file* symbols, UInt32 statementIndex, ContainedStatementsTableEntry* statementEntry) { const DiskSymbolHeaderBlock& header = symbols->mHeader; UInt32 entriesPerPage = (header.dshb_page_size / sizeof(ContainedStatementsTableEntry)); UInt32 entryPage = (statementIndex / entriesPerPage); UInt32 entryIndex = (statementIndex % entriesPerPage); // seek to the specified statement entry. symbols->seek((header.dshb_csnte.dti_first_page + entryPage) * header.dshb_page_size + entryIndex * sizeof(ContainedStatementsTableEntry)); return (symbols->read(statementEntry, sizeof(ContainedStatementsTableEntry)) == sizeof(ContainedStatementsTableEntry)); } inline bool isMeatyModule(const ModulesTableEntry& moduleEntry) { return (moduleEntry.mte_kind == kModuleKindProcedure) || (moduleEntry.mte_kind == kModuleKindFunction); } inline UInt32 delta(UInt32 x, UInt32 y) { if (x > y) return (x - y); else return (y - x); } int get_source(sym_file* symbols, UInt32 codeOffset, char symbolName[256], char fileName[256], UInt32* fileOffset) { const DiskSymbolHeaderBlock& header = symbols->mHeader; const ResourceTableEntry& codeEntry = symbols->mCodeEntry; // since module entries can't span pages, must compute which page module entry size. UInt32 modulesPerPage = (header.dshb_page_size / sizeof(ModulesTableEntry)); // search for MTE nearest specified offset. // seek to first MTE. for (UInt16 i = codeEntry.rte_mte_first; i <= codeEntry.rte_mte_last; i++) { ModulesTableEntry moduleEntry; UInt32 modulePage = (i / modulesPerPage); UInt32 moduleIndex = (i % modulesPerPage); symbols->seek((header.dshb_mte.dti_first_page + modulePage) * header.dshb_page_size + moduleIndex * sizeof(ModulesTableEntry)); if (symbols->read(&moduleEntry, sizeof(moduleEntry)) == sizeof(moduleEntry)) { if (isMeatyModule(moduleEntry) && (codeOffset >= moduleEntry.mte_res_offset) && (codeOffset - moduleEntry.mte_res_offset) < moduleEntry.mte_size) { FileReferenceTableEntry frte; if (getFileReferenceTableEntry(symbols, moduleEntry.mte_imp_fref, &frte)) { UInt32 length; // get the name of the symbol. const UInt8* moduleName = symbols->getName(moduleEntry.mte_nte_index); // printf("module name = %#s\n", moduleName); // trim off the leading "." length = moduleName[0] - 1; BlockMoveData(moduleName + 2, symbolName, length); symbolName[length] = '\0'; // get the name of the file. const UInt8* name = symbols->getName(frte.frte_fn.nte_index); length = name[0]; BlockMoveData(name + 1, fileName, length); fileName[length] = '\0'; // printf("file name = %s\n", fileName); // try to refine the location, using the contained statements table entries. UInt32 closestFileOffset = moduleEntry.mte_imp_fref.fref_offset; UInt32 closestCodeOffset = moduleEntry.mte_res_offset; UInt32 closestCodeDelta = 0xFFFFFFFF; UInt32 currentFileOffset, currentCodeOffset = moduleEntry.mte_res_offset, currentCodeDelta; for (UInt32 j = moduleEntry.mte_csnte_idx_1; j <= moduleEntry.mte_csnte_idx_2; j++) { // only consider offsets less than the actual code offset, so we'll be sure // to match the nearest line before the code offset. this could probably be // a termination condition as well. if (currentCodeOffset > codeOffset) break; ContainedStatementsTableEntry statementEntry; if (getContainedStatementTableEntry(symbols, j, &statementEntry)) { switch (statementEntry.csnte_file.change) { case kSourceFileChange: currentFileOffset = statementEntry.csnte_file.fref.fref_offset; break; case kEndOfList: break; default: currentFileOffset += statementEntry.csnte.file_delta; currentCodeOffset = moduleEntry.mte_res_offset + statementEntry.csnte.mte_offset; if (currentCodeOffset <= codeOffset) { currentCodeDelta = delta(currentCodeOffset, codeOffset); if (currentCodeDelta < closestCodeDelta) { closestFileOffset = currentFileOffset; closestCodeOffset = currentCodeOffset; closestCodeDelta = currentCodeDelta; } } break; } } } *fileOffset = closestFileOffset; // printf("closest file offset = %d\n", closestFileOffset); // printf("closest code offset = %d\n", closestCodeOffset); return 1; } } } } return 0; } sym_file* open_sym_file(const FSSpec* symSpec) { sym_file* symbols = new sym_file(symSpec); if (symbols != NULL) { if (!symbols->init()) { delete symbols; return NULL; } // don't leave the file open, if possible. symbols->close(); } return symbols; } void close_sym_file(sym_file* symbols) { if (symbols != NULL) { delete symbols; } }