RetroZilla/gc/boehm/xSYM/SymFiles.cpp
2015-10-20 23:03:22 -04:00

355 lines
11 KiB
C++

/* -*- 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 <beard@netscape.com>
*
* 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 <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <Memory.h>
#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;
}
}