mirror of
https://github.com/rn10950/RetroZilla.git
synced 2024-11-14 03:30:17 +01:00
355 lines
11 KiB
C++
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;
|
|
}
|
|
}
|