Mercurial > hg > truffle
diff agent/src/share/classes/sun/jvm/hotspot/debugger/win32/coff/COFFFileParser.java @ 0:a61af66fc99e jdk7-b24
Initial load
author | duke |
---|---|
date | Sat, 01 Dec 2007 00:00:00 +0000 |
parents | |
children | c18cbe5936b8 |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/agent/src/share/classes/sun/jvm/hotspot/debugger/win32/coff/COFFFileParser.java Sat Dec 01 00:00:00 2007 +0000 @@ -0,0 +1,3933 @@ +/* + * Copyright 2000-2004 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code 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 General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +package sun.jvm.hotspot.debugger.win32.coff; + +import java.io.*; +import java.nio.*; +import java.nio.channels.*; +import java.util.*; + +import sun.jvm.hotspot.utilities.memo.*; +import sun.jvm.hotspot.utilities.Assert; +import sun.jvm.hotspot.debugger.DataSource; +import sun.jvm.hotspot.debugger.MappedByteBufferDataSource; + +/** Top-level factory which parses COFF files, including object files, + Portable Executables and DLLs. Returns {@link + sun.jvm.hotspot.debugger.win32.coff.COFFFile} objects. This class is a + singleton. */ + +public class COFFFileParser { + private static COFFFileParser soleInstance; + + // Constants from the file format documentation + private static final int COFF_HEADER_SIZE = 20; + private static final int SECTION_HEADER_SIZE = 40; + private static final int SYMBOL_SIZE = 18; + private static final int RELOCATION_SIZE = 10; + private static final int LINE_NUMBER_SIZE = 6; + + private static final String US_ASCII = "US-ASCII"; + + private COFFFileParser() {} + + /** This class is a singleton; returns the sole instance. */ + public static COFFFileParser getParser() { + if (soleInstance == null) { + soleInstance = new COFFFileParser(); + } + return soleInstance; + } + + public COFFFile parse(String filename) throws COFFException { + try { + File file = new File(filename); + FileInputStream stream = new FileInputStream(file); + MappedByteBuffer buf = stream.getChannel().map(FileChannel.MapMode.READ_ONLY, + 0, + file.length()); + + // This is pretty confusing. The file format is little-endian + // and so is the CPU. In order for the multi-byte accessors to + // work properly we must NOT change the endianness of the + // MappedByteBuffer. Need to think about this some more and file + // a bug if there is one. (FIXME) + // buf.order(ByteOrder.nativeOrder()); + return parse(new MappedByteBufferDataSource(buf)); + } catch (FileNotFoundException e) { + throw new COFFException(e); + } catch (IOException e) { + throw new COFFException(e); + } + } + + public COFFFile parse(DataSource source) throws COFFException { + return new COFFFileImpl(source); + } + + class COFFFileImpl implements COFFFile { + private DataSource file; + private long filePos; + private boolean isImage; + private long imageHeaderOffset; + private MemoizedObject header = new MemoizedObject() { + public Object computeValue() { + return new COFFHeaderImpl(); + } + }; + + COFFFileImpl(DataSource file) throws COFFException { + this.file = file; + initialize(); + } + + public boolean isImage() { + return isImage; + } + + public COFFHeader getHeader() { + return (COFFHeaderImpl) header.getValue(); + } + + class COFFHeaderImpl implements COFFHeader { + private short machine; + private short numberOfSections; + private int timeDateStamp; + private int pointerToSymbolTable; + private int numberOfSymbols; + private short sizeOfOptionalHeader; + private short characteristics; + private MemoizedObject[] sectionHeaders; + private MemoizedObject[] symbols; + + private MemoizedObject stringTable = new MemoizedObject() { + public Object computeValue() { + int ptr = getPointerToSymbolTable(); + if (ptr == 0) { + return new StringTable(0); + } else { + return new StringTable(ptr + SYMBOL_SIZE * getNumberOfSymbols()); + } + } + }; + + COFFHeaderImpl() { + seek(imageHeaderOffset); + machine = readShort(); + numberOfSections = readShort(); + timeDateStamp = readInt(); + pointerToSymbolTable = readInt(); + numberOfSymbols = readInt(); + sizeOfOptionalHeader = readShort(); + characteristics = readShort(); + + // Set up section headers + sectionHeaders = new MemoizedObject[numberOfSections]; + for (int i = 0; i < numberOfSections; i++) { + final int secHdrOffset = (int) + (imageHeaderOffset + COFF_HEADER_SIZE + sizeOfOptionalHeader + i * SECTION_HEADER_SIZE); + sectionHeaders[i] = new MemoizedObject() { + public Object computeValue() { + return new SectionHeaderImpl(secHdrOffset); + } + }; + } + + // Set up symbols + symbols = new MemoizedObject[numberOfSymbols]; + for (int i = 0; i < numberOfSymbols; i++) { + final int symbolOffset = pointerToSymbolTable + i * SYMBOL_SIZE; + symbols[i] = new MemoizedObject() { + public Object computeValue() { + return new COFFSymbolImpl(symbolOffset); + } + }; + } + } + + public short getMachineType() { return machine; } + public short getNumberOfSections() { return numberOfSections; } + public int getTimeDateStamp() { return timeDateStamp; } + public int getPointerToSymbolTable() { return pointerToSymbolTable; } + public int getNumberOfSymbols() { return numberOfSymbols; } + public short getSizeOfOptionalHeader() { return sizeOfOptionalHeader; } + public OptionalHeader getOptionalHeader() throws COFFException { + if (getSizeOfOptionalHeader() == 0) { + return null; + } + return new OptionalHeaderImpl((int) (imageHeaderOffset + COFF_HEADER_SIZE)); + } + public short getCharacteristics() { return characteristics; } + public boolean hasCharacteristic(short characteristic) { + return ((characteristics & characteristic) != 0); + } + public SectionHeader getSectionHeader(int index) { + // NOTE zero-basing of index + return (SectionHeader) sectionHeaders[index - 1].getValue(); + } + public COFFSymbol getCOFFSymbol(int index) { + return (COFFSymbol) symbols[index].getValue(); + } + public int getNumberOfStrings() { + return getStringTable().getNum(); + } + public String getString(int i) { + return getStringTable().get(i); + } + + StringTable getStringTable() { return (StringTable) stringTable.getValue(); } + + // NOTE: can destroy current seek() position! + int rvaToFileOffset(int rva) { + if (rva == 0) return 0; + // Search for section containing RVA + for (int i = 1; i <= getNumberOfSections(); i++) { + SectionHeader sec = getSectionHeader(i); + int va = sec.getVirtualAddress(); + int sz = sec.getSize(); + if ((va <= rva) && (rva < (va + sz))) { + return sec.getPointerToRawData() + (rva - va); + } + } + throw new COFFException("Unable to find RVA 0x" + + Integer.toHexString(rva) + + " in any section"); + } + + class OptionalHeaderImpl implements OptionalHeader { + private short magic; + private MemoizedObject standardFields; + private MemoizedObject windowsSpecificFields; + private MemoizedObject dataDirectories; + + private static final int STANDARD_FIELDS_OFFSET = 2; + private static final int PE32_WINDOWS_SPECIFIC_FIELDS_OFFSET = 28; + private static final int PE32_DATA_DIRECTORIES_OFFSET = 96; + private static final int PE32_PLUS_WINDOWS_SPECIFIC_FIELDS_OFFSET = 24; + private static final int PE32_PLUS_DATA_DIRECTORIES_OFFSET = 112; + + OptionalHeaderImpl(final int offset) { + seek(offset); + magic = readShort(); + + final boolean isPE32Plus = (magic == MAGIC_PE32_PLUS); + final int standardFieldsOffset = offset + STANDARD_FIELDS_OFFSET; + final int windowsSpecificFieldsOffset = offset + + (isPE32Plus + ? PE32_PLUS_WINDOWS_SPECIFIC_FIELDS_OFFSET + : PE32_WINDOWS_SPECIFIC_FIELDS_OFFSET); + final int dataDirectoriesOffset = offset + + (isPE32Plus + ? PE32_PLUS_DATA_DIRECTORIES_OFFSET + : PE32_DATA_DIRECTORIES_OFFSET); + + standardFields = new MemoizedObject() { + public Object computeValue() { + return new OptionalHeaderStandardFieldsImpl(standardFieldsOffset, + isPE32Plus); + } + }; + windowsSpecificFields = new MemoizedObject() { + public Object computeValue() { + return new OptionalHeaderWindowsSpecificFieldsImpl(windowsSpecificFieldsOffset, + isPE32Plus); + } + }; + dataDirectories = new MemoizedObject() { + public Object computeValue() { + return new OptionalHeaderDataDirectoriesImpl(dataDirectoriesOffset, + getWindowsSpecificFields().getNumberOfRvaAndSizes()); + } + }; + } + + public short getMagicNumber() { + return magic; + } + + public OptionalHeaderStandardFields getStandardFields() { + return (OptionalHeaderStandardFields) standardFields.getValue(); + } + + public OptionalHeaderWindowsSpecificFields getWindowsSpecificFields() { + return (OptionalHeaderWindowsSpecificFields) windowsSpecificFields.getValue(); + } + public OptionalHeaderDataDirectories getDataDirectories() { + return (OptionalHeaderDataDirectories) dataDirectories.getValue(); + } + } + + class OptionalHeaderStandardFieldsImpl implements OptionalHeaderStandardFields { + private boolean isPE32Plus; + private byte majorLinkerVersion; + private byte minorLinkerVersion; + private int sizeOfCode; + private int sizeOfInitializedData; + private int sizeOfUninitializedData; + private int addressOfEntryPoint; + private int baseOfCode; + private int baseOfData; + + OptionalHeaderStandardFieldsImpl(int offset, + boolean isPE32Plus) { + this.isPE32Plus = isPE32Plus; + seek(offset); + majorLinkerVersion = readByte(); + minorLinkerVersion = readByte(); + sizeOfCode = readInt(); + sizeOfInitializedData = readInt(); + sizeOfUninitializedData = readInt(); + addressOfEntryPoint = readInt(); + baseOfCode = readInt(); + if (isPE32Plus) { + baseOfData = readInt(); + } + } + + public byte getMajorLinkerVersion() { return majorLinkerVersion; } + public byte getMinorLinkerVersion() { return minorLinkerVersion; } + public int getSizeOfCode() { return sizeOfCode; } + public int getSizeOfInitializedData() { return sizeOfInitializedData; } + public int getSizeOfUninitializedData() { return sizeOfUninitializedData; } + public int getAddressOfEntryPoint() { return addressOfEntryPoint; } + public int getBaseOfCode() { return baseOfCode; } + public int getBaseOfData() throws COFFException { + if (isPE32Plus) { + throw new COFFException("Not present in PE32+ files"); + } + return baseOfData; + } + } + + class OptionalHeaderWindowsSpecificFieldsImpl implements OptionalHeaderWindowsSpecificFields { + private long imageBase; + private int sectionAlignment; + private int fileAlignment; + private short majorOperatingSystemVersion; + private short minorOperatingSystemVersion; + private short majorImageVersion; + private short minorImageVersion; + private short majorSubsystemVersion; + private short minorSubsystemVersion; + private int sizeOfImage; + private int sizeOfHeaders; + private int checkSum; + private short subsystem; + private short dllCharacteristics; + private long sizeOfStackReserve; + private long sizeOfStackCommit; + private long sizeOfHeapReserve; + private long sizeOfHeapCommit; + private int loaderFlags; + private int numberOfRvaAndSizes; + + OptionalHeaderWindowsSpecificFieldsImpl(int offset, boolean isPE32Plus) { + seek(offset); + + if (!isPE32Plus) { + imageBase = maskInt(readInt()); + } else { + imageBase = readLong(); + } + sectionAlignment = readInt(); + fileAlignment = readInt(); + majorOperatingSystemVersion = readShort(); + minorOperatingSystemVersion = readShort(); + majorImageVersion = readShort(); + minorImageVersion = readShort(); + majorSubsystemVersion = readShort(); + minorSubsystemVersion = readShort(); + readInt(); // Reserved + sizeOfImage = readInt(); + sizeOfHeaders = readInt(); + checkSum = readInt(); + subsystem = readShort(); + dllCharacteristics = readShort(); + if (!isPE32Plus) { + sizeOfStackReserve = maskInt(readInt()); + sizeOfStackCommit = maskInt(readInt()); + sizeOfHeapReserve = maskInt(readInt()); + sizeOfHeapCommit = maskInt(readInt()); + } else { + sizeOfStackReserve = readLong(); + sizeOfStackCommit = readLong(); + sizeOfHeapReserve = readLong(); + sizeOfHeapCommit = readLong(); + } + loaderFlags = readInt(); + numberOfRvaAndSizes = readInt(); + } + + public long getImageBase() { return imageBase; } + public int getSectionAlignment() { return sectionAlignment; } + public int getFileAlignment() { return fileAlignment; } + public short getMajorOperatingSystemVersion() { return majorOperatingSystemVersion; } + public short getMinorOperatingSystemVersion() { return minorOperatingSystemVersion; } + public short getMajorImageVersion() { return majorImageVersion; } + public short getMinorImageVersion() { return minorImageVersion; } + public short getMajorSubsystemVersion() { return majorSubsystemVersion; } + public short getMinorSubsystemVersion() { return minorSubsystemVersion; } + public int getSizeOfImage() { return sizeOfImage; } + public int getSizeOfHeaders() { return sizeOfHeaders; } + public int getCheckSum() { return checkSum; } + public short getSubsystem() { return subsystem; } + public short getDLLCharacteristics() { return dllCharacteristics; } + public long getSizeOfStackReserve() { return sizeOfStackReserve; } + public long getSizeOfStackCommit() { return sizeOfStackCommit; } + public long getSizeOfHeapReserve() { return sizeOfHeapReserve; } + public long getSizeOfHeapCommit() { return sizeOfHeapCommit; } + public int getLoaderFlags() { return loaderFlags; } + public int getNumberOfRvaAndSizes() { return numberOfRvaAndSizes; } + + private long maskInt(long arg) { + return (arg & 0x00000000FFFFFFFFL); + } + } + + class OptionalHeaderDataDirectoriesImpl implements OptionalHeaderDataDirectories { + private int numberOfRvaAndSizes; + private MemoizedObject[] dataDirectories; + private MemoizedObject exportDirectoryTable; + private MemoizedObject debugDirectory; + + private static final int DATA_DIRECTORY_SIZE = 8; + + OptionalHeaderDataDirectoriesImpl(int offset, + int numberOfRvaAndSizes) { + this.numberOfRvaAndSizes = numberOfRvaAndSizes; + dataDirectories = new MemoizedObject[numberOfRvaAndSizes]; + for (int i = 0; i < numberOfRvaAndSizes; i++) { + final int dirOffset = offset + (i * DATA_DIRECTORY_SIZE); + dataDirectories[i] = new MemoizedObject() { + public Object computeValue() { + return new DataDirectoryImpl(dirOffset); + } + }; + } + + exportDirectoryTable = new MemoizedObject() { + public Object computeValue() { + DataDirectory dir = getExportTable(); + if (dir.getRVA() == 0 || dir.getSize() == 0) { + return null; + } + return new ExportDirectoryTableImpl(rvaToFileOffset(dir.getRVA()), dir.getSize()); + } + }; + + debugDirectory = new MemoizedObject() { + public Object computeValue() { + DataDirectory dir = getDebug(); + if (dir.getRVA() == 0 || dir.getSize() == 0) { + return null; + } + return new DebugDirectoryImpl(rvaToFileOffset(dir.getRVA()), dir.getSize()); + } + }; + } + + public DataDirectory getExportTable() throws COFFException { + return (DataDirectory) dataDirectories[checkIndex(0)].getValue(); + } + public DataDirectory getImportTable() throws COFFException { + return (DataDirectory) dataDirectories[checkIndex(1)].getValue(); + } + public DataDirectory getResourceTable() throws COFFException { + return (DataDirectory) dataDirectories[checkIndex(2)].getValue(); + } + public DataDirectory getExceptionTable() throws COFFException { + return (DataDirectory) dataDirectories[checkIndex(3)].getValue(); + } + public DataDirectory getCertificateTable() throws COFFException { + return (DataDirectory) dataDirectories[checkIndex(4)].getValue(); + } + public DataDirectory getBaseRelocationTable() throws COFFException { + return (DataDirectory) dataDirectories[checkIndex(5)].getValue(); + } + public DataDirectory getDebug() throws COFFException { + return (DataDirectory) dataDirectories[checkIndex(6)].getValue(); + } + public DataDirectory getArchitecture() throws COFFException { + return (DataDirectory) dataDirectories[checkIndex(7)].getValue(); + } + public DataDirectory getGlobalPtr() throws COFFException { + return (DataDirectory) dataDirectories[checkIndex(8)].getValue(); + } + public DataDirectory getTLSTable() throws COFFException { + return (DataDirectory) dataDirectories[checkIndex(9)].getValue(); + } + public DataDirectory getLoadConfigTable() throws COFFException { + return (DataDirectory) dataDirectories[checkIndex(10)].getValue(); + } + public DataDirectory getBoundImportTable() throws COFFException { + return (DataDirectory) dataDirectories[checkIndex(11)].getValue(); + } + public DataDirectory getImportAddressTable() throws COFFException { + return (DataDirectory) dataDirectories[checkIndex(12)].getValue(); + } + public DataDirectory getDelayImportDescriptor() throws COFFException { + return (DataDirectory) dataDirectories[checkIndex(13)].getValue(); + } + public DataDirectory getCOMPlusRuntimeHeader() throws COFFException { + return (DataDirectory) dataDirectories[checkIndex(14)].getValue(); + } + + public ExportDirectoryTable getExportDirectoryTable() throws COFFException { + return (ExportDirectoryTable) exportDirectoryTable.getValue(); + } + + public DebugDirectory getDebugDirectory() throws COFFException { + return (DebugDirectory) debugDirectory.getValue(); + } + + private int checkIndex(int index) throws COFFException { + if ((index < 0) || (index >= dataDirectories.length)) { + throw new COFFException("Directory " + index + " unavailable (only " + + numberOfRvaAndSizes + " tables present)"); + } + return index; + } + } + + class DataDirectoryImpl implements DataDirectory { + int rva; + int size; + + DataDirectoryImpl(int offset) { + seek(offset); + rva = readInt(); + size = readInt(); + } + + public int getRVA() { return rva; } + public int getSize() { return size; } + } + + class ExportDirectoryTableImpl implements ExportDirectoryTable { + private int offset; + private int size; + + private int exportFlags; + private int timeDateStamp; + private short majorVersion; + private short minorVersion; + private int nameRVA; + private int ordinalBase; + private int addressTableEntries; + private int numberOfNamePointers; + private int exportAddressTableRVA; + private int namePointerTableRVA; + private int ordinalTableRVA; + + private MemoizedObject dllName; + + private MemoizedObject exportNameTable; + private MemoizedObject exportNamePointerTable; + private MemoizedObject exportOrdinalTable; + private MemoizedObject exportAddressTable; + + ExportDirectoryTableImpl(int offset, int size) { + this.offset = offset; + this.size = size; + seek(offset); + exportFlags = readInt(); + timeDateStamp = readInt(); + majorVersion = readShort(); + minorVersion = readShort(); + nameRVA = readInt(); + ordinalBase = readInt(); + addressTableEntries = readInt(); + numberOfNamePointers = readInt(); + exportAddressTableRVA = readInt(); + namePointerTableRVA = readInt(); + ordinalTableRVA = readInt(); + + dllName = new MemoizedObject() { + public Object computeValue() { + seek(rvaToFileOffset(getNameRVA())); + return readCString(); + } + }; + + exportNamePointerTable = new MemoizedObject() { + public Object computeValue() { + int[] pointers = new int[getNumberOfNamePointers()]; + seek(rvaToFileOffset(getNamePointerTableRVA())); + // Must make two passes to avoid rvaToFileOffset + // destroying seek() position + for (int i = 0; i < pointers.length; i++) { + pointers[i] = readInt(); + } + for (int i = 0; i < pointers.length; i++) { + pointers[i] = rvaToFileOffset(pointers[i]); + } + return pointers; + } + }; + + exportNameTable = new MemoizedObject() { + public Object computeValue() { + return new ExportNameTable(getExportNamePointerTable()); + } + }; + + exportOrdinalTable = new MemoizedObject() { + public Object computeValue() { + short[] ordinals = new short[getNumberOfNamePointers()]; + seek(rvaToFileOffset(getOrdinalTableRVA())); + for (int i = 0; i < ordinals.length; i++) { + ordinals[i] = readShort(); + } + return ordinals; + } + }; + + exportAddressTable = new MemoizedObject() { + public Object computeValue() { + int[] addresses = new int[getNumberOfAddressTableEntries()]; + seek(rvaToFileOffset(getExportAddressTableRVA())); + // Must make two passes to avoid rvaToFileOffset + // destroying seek() position + for (int i = 0; i < addresses.length; i++) { + addresses[i] = readInt(); + } + for (int i = 0; i < addresses.length; i++) { + addresses[i] = rvaToFileOffset(addresses[i]); + } + return addresses; + } + }; + } + + public int getExportFlags() { return exportFlags; } + public int getTimeDateStamp() { return timeDateStamp; } + public short getMajorVersion() { return majorVersion; } + public short getMinorVersion() { return minorVersion; } + public int getNameRVA() { return nameRVA; } + + public String getDLLName() { + return (String) dllName.getValue(); + } + + public int getOrdinalBase() { return ordinalBase; } + public int getNumberOfAddressTableEntries() { return addressTableEntries; } + public int getNumberOfNamePointers() { return numberOfNamePointers; } + public int getExportAddressTableRVA() { return exportAddressTableRVA; } + public int getNamePointerTableRVA() { return namePointerTableRVA; } + public int getOrdinalTableRVA() { return ordinalTableRVA; } + + public String getExportName(int i) { + return getExportNameTable().get(i); + } + + public short getExportOrdinal(int i) { + return getExportOrdinalTable()[i]; + } + + public boolean isExportAddressForwarder(short ordinal) { + int addr = getExportAddress(ordinal); + return ((offset <= addr) && (addr < (offset + size))); + } + + public String getExportAddressForwarder(short ordinal) { + seek(getExportAddress(ordinal)); + return readCString(); + } + + public int getExportAddress(short ordinal) { + + /////////////////////// + // FIXME: MAJOR HACK // + /////////////////////// + + // According to the documentation, the first line here is + // correct. However, it doesn't seem to work. The second + // one, however, does. + + // OK, it's probably due to using negative indices in the + // export address table in "real life"...need to rethink + // this when I'm more awake + + // return getExportAddressTable()[ordinal - ordinalBase]; + return getExportAddressTable()[ordinal]; + } + + private ExportNameTable getExportNameTable() { + return (ExportNameTable) exportNameTable.getValue(); + } + + private int[] getExportNamePointerTable() { + return (int[]) exportNamePointerTable.getValue(); + } + + private short[] getExportOrdinalTable() { + return (short[]) exportOrdinalTable.getValue(); + } + + private int[] getExportAddressTable() { + return (int[]) exportAddressTable.getValue(); + } + } + + class ExportNameTable { + private MemoizedObject[] names; + + ExportNameTable(final int[] exportNamePointerTable) { + names = new MemoizedObject[exportNamePointerTable.length]; + for (int i = 0; i < exportNamePointerTable.length; i++) { + final int idx = i; + names[idx] = new MemoizedObject() { + public Object computeValue() { + seek(exportNamePointerTable[idx]); + return readCString(); + } + }; + }; + } + + String get(int i) { + return (String) names[i].getValue(); + } + } + + class DebugDirectoryImpl implements DebugDirectory { + private int offset; + private int size; + private int numEntries; + + private static final int DEBUG_DIRECTORY_ENTRY_SIZE = 28; + + DebugDirectoryImpl(int offset, int size) { + this.offset = offset; + this.size = size; + + if ((size % DEBUG_DIRECTORY_ENTRY_SIZE) != 0) { + throw new COFFException("Corrupt DebugDirectory at offset 0x" + + Integer.toHexString(offset)); + } + + numEntries = size / DEBUG_DIRECTORY_ENTRY_SIZE; + } + + public int getNumEntries() { return numEntries; } + public DebugDirectoryEntry getEntry(int i) { + if ((i < 0) || (i >= getNumEntries())) throw new IndexOutOfBoundsException(); + return new DebugDirectoryEntryImpl(offset + i * DEBUG_DIRECTORY_ENTRY_SIZE); + } + } + + class DebugDirectoryEntryImpl implements DebugDirectoryEntry, DebugTypes { + private int characteristics; + private int timeDateStamp; + private short majorVersion; + private short minorVersion; + private int type; + private int sizeOfData; + private int addressOfRawData; + private int pointerToRawData; + + DebugDirectoryEntryImpl(int offset) { + seek(offset); + characteristics = readInt(); + timeDateStamp = readInt(); + majorVersion = readShort(); + minorVersion = readShort(); + type = readInt(); + sizeOfData = readInt(); + addressOfRawData = readInt(); + pointerToRawData = readInt(); + } + + public int getCharacteristics() { return characteristics; } + public int getTimeDateStamp() { return timeDateStamp; } + public short getMajorVersion() { return majorVersion; } + public short getMinorVersion() { return minorVersion; } + public int getType() { return type; } + public int getSizeOfData() { return sizeOfData; } + public int getAddressOfRawData() { return addressOfRawData; } + public int getPointerToRawData() { return pointerToRawData; } + + public DebugVC50 getDebugVC50() { + // See whether we can recognize VC++ 5.0 debug information. + try { + if (getType() != IMAGE_DEBUG_TYPE_CODEVIEW) return null; + + int offset = getPointerToRawData(); + seek(offset); + if (readByte() == 'N' && + readByte() == 'B' && + readByte() == '1' && + readByte() == '1') { + return new DebugVC50Impl(offset); + } + } catch (COFFException e) { + e.printStackTrace(); + } + return null; + } + + public byte getRawDataByte(int i) { + if (i < 0 || i >= getSizeOfData()) { + throw new IndexOutOfBoundsException(); + } + seek(getPointerToRawData() + i); + return readByte(); + } + } + + class DebugVC50Impl implements DebugVC50, DebugVC50TypeLeafIndices { + private int lfaBase; + + private int subsectionDirectoryOffset; + private MemoizedObject subsectionDirectory; + + DebugVC50Impl(int offset) { + lfaBase = offset; + seek(offset); + readInt(); // Discard NB11 + subsectionDirectoryOffset = globalOffset(readInt()); + + // Ensure information is complete + verify(); + + subsectionDirectory = new MemoizedObject() { + public Object computeValue() { + return new DebugVC50SubsectionDirectoryImpl(getSubsectionDirectoryOffset()); + } + }; + } + + public int getSubsectionDirectoryOffset() { + return subsectionDirectoryOffset; + } + + public DebugVC50SubsectionDirectory getSubsectionDirectory() { + return (DebugVC50SubsectionDirectory) subsectionDirectory.getValue(); + } + + private int globalOffset(int offset) { + return offset + lfaBase; + } + + private void verify() { + // Seek to subsection directory manually and look for + // signature following it. This finishes validating that we + // have VC++ 5.0 debug info. Throw COFFException if not + // found; will cause caller to return null. + seek(subsectionDirectoryOffset); + int headerLength = readShort(); + int entryLength = readShort(); + int numEntries = readInt(); + int endOffset = subsectionDirectoryOffset + headerLength + numEntries * entryLength; + seek(endOffset); + + if (readByte() == 'N' && + readByte() == 'B' && + readByte() == '1' && + readByte() == '1') { + return; + } + + throw new COFFException("Did not find NB11 signature at end of debug info"); + } + + class DebugVC50SubsectionDirectoryImpl + implements DebugVC50SubsectionDirectory, + DebugVC50SubsectionTypes { + private int offset; + private short dirHeaderLength; + private short dirEntryLength; + private int numEntries; + + DebugVC50SubsectionDirectoryImpl(int offset) { + this.offset = offset; + // Read header + seek(offset); + dirHeaderLength = readShort(); + dirEntryLength = readShort(); + numEntries = readInt(); + } + + public short getHeaderLength() { return dirHeaderLength; } + public short getEntryLength() { return dirEntryLength; } + public int getNumEntries() { return numEntries; } + + public DebugVC50Subsection getSubsection(int i) { + // Fetch the subsection type and instantiate the correct + // type of subsection based on it + seek(offset + dirHeaderLength + (i * dirEntryLength)); + short ssType = readShort(); + short iMod = readShort(); // Unneeded? + int lfo = globalOffset(readInt()); + int cb = readInt(); + switch (ssType) { + case SST_MODULE: + return new DebugVC50SSModuleImpl(ssType, iMod, cb, lfo); + case SST_TYPES: + return new DebugVC50SSTypesImpl(ssType, iMod, cb, lfo); + case SST_PUBLIC: + return new DebugVC50SSPublicImpl(ssType, iMod, cb, lfo); + case SST_PUBLIC_SYM: + return new DebugVC50SSPublicSymImpl(ssType, iMod, cb, lfo); + case SST_SYMBOLS: + return new DebugVC50SSSymbolsImpl(ssType, iMod, cb, lfo); + case SST_ALIGN_SYM: + return new DebugVC50SSAlignSymImpl(ssType, iMod, cb, lfo); + case SST_SRC_LN_SEG: + return new DebugVC50SSSrcLnSegImpl(ssType, iMod, cb, lfo); + case SST_SRC_MODULE: + return new DebugVC50SSSrcModuleImpl(ssType, iMod, cb, lfo); + case SST_LIBRARIES: + return new DebugVC50SSLibrariesImpl(ssType, iMod, cb, lfo); + case SST_GLOBAL_SYM: + return new DebugVC50SSGlobalSymImpl(ssType, iMod, cb, lfo); + case SST_GLOBAL_PUB: + return new DebugVC50SSGlobalPubImpl(ssType, iMod, cb, lfo); + case SST_GLOBAL_TYPES: + return new DebugVC50SSGlobalTypesImpl(ssType, iMod, cb, lfo); + case SST_MPC: + return new DebugVC50SSMPCImpl(ssType, iMod, cb, lfo); + case SST_SEG_MAP: + return new DebugVC50SSSegMapImpl(ssType, iMod, cb, lfo); + case SST_SEG_NAME: + return new DebugVC50SSSegNameImpl(ssType, iMod, cb, lfo); + case SST_PRE_COMP: + return new DebugVC50SSPreCompImpl(ssType, iMod, cb, lfo); + case SST_UNUSED: + return null; + case SST_OFFSET_MAP_16: + return new DebugVC50SSOffsetMap16Impl(ssType, iMod, cb, lfo); + case SST_OFFSET_MAP_32: + return new DebugVC50SSOffsetMap32Impl(ssType, iMod, cb, lfo); + case SST_FILE_INDEX: + return new DebugVC50SSFileIndexImpl(ssType, iMod, cb, lfo); + case SST_STATIC_SYM: + return new DebugVC50SSStaticSymImpl(ssType, iMod, cb, lfo); + default: + throw new COFFException("Unknown section type " + ssType); + } + } + } + + //////////////////////////////////// + // // + // Implementations of subsections // + // // + //////////////////////////////////// + + class DebugVC50SubsectionImpl implements DebugVC50Subsection { + private short ssType; + private short iMod; + private int ssSize; + + DebugVC50SubsectionImpl(short ssType, short iMod, int ssSize, int offset) { + this.ssType = ssType; + this.iMod = iMod; + this.ssSize = ssSize; + } + + public short getSubsectionType() { return ssType; } + public short getSubsectionModuleIndex() { return iMod; } + public int getSubsectionSize() { return ssSize; } + } + + class DebugVC50SSModuleImpl extends DebugVC50SubsectionImpl implements DebugVC50SSModule { + private int offset; + private short ovlNumber; + private short iLib; + private short cSeg; + private short style; + private MemoizedObject segInfo; + private MemoizedObject name; + + private static final int HEADER_SIZE = 8; + private static final int SEG_INFO_SIZE = 12; + + DebugVC50SSModuleImpl(short ssType, short iMod, int ssSize, final int offset) { + super(ssType, iMod, ssSize, offset); + this.offset = offset; + seek(offset); + ovlNumber = readShort(); + iLib = readShort(); + cSeg = readShort(); + style = readShort(); + segInfo = new MemoizedObject() { + public Object computeValue() { + int base = offset + HEADER_SIZE; + DebugVC50SegInfo[] res = new DebugVC50SegInfo[cSeg]; + for (int i = 0; i < cSeg; i++) { + res[i] = new DebugVC50SegInfoImpl(base); + base += SEG_INFO_SIZE; + } + return res; + } + }; + name = new MemoizedObject() { + public Object computeValue() { + return readLengthPrefixedStringAt(offset + (HEADER_SIZE + cSeg * SEG_INFO_SIZE)); + } + }; + } + + public short getOverlayNumber() { return ovlNumber; } + public short getLibrariesIndex() { return iLib; } + public short getNumCodeSegments() { return cSeg; } + public short getDebuggingStyle() { return style; } + public DebugVC50SegInfo getSegInfo(int i) { return ((DebugVC50SegInfo[]) segInfo.getValue())[i]; } + public String getName() { return (String) name.getValue(); } + } + + class DebugVC50SegInfoImpl implements DebugVC50SegInfo { + private short seg; + private int offset; + private int cbSeg; + + DebugVC50SegInfoImpl(int offset) { + seek(offset); + seg = readShort(); + readShort(); // pad + offset = readInt(); + cbSeg = readInt(); + } + + public short getSegment() { return seg; } + public int getOffset() { return offset; } + public int getSegmentCodeSize() { return cbSeg; } + } + + class DebugVC50SSTypesImpl extends DebugVC50SubsectionImpl implements DebugVC50SSTypes { + DebugVC50SSTypesImpl(short ssType, short iMod, int ssSize, int offset) { + super(ssType, iMod, ssSize, offset); + } + } + + class DebugVC50SSPublicImpl extends DebugVC50SubsectionImpl implements DebugVC50SSPublic { + DebugVC50SSPublicImpl(short ssType, short iMod, int ssSize, int offset) { + super(ssType, iMod, ssSize, offset); + } + } + + class DebugVC50SSPublicSymImpl extends DebugVC50SubsectionImpl implements DebugVC50SSPublicSym { + DebugVC50SSPublicSymImpl(short ssType, short iMod, int ssSize, int offset) { + super(ssType, iMod, ssSize, offset); + } + } + + class DebugVC50SSSymbolsImpl extends DebugVC50SubsectionImpl implements DebugVC50SSSymbols { + DebugVC50SSSymbolsImpl(short ssType, short iMod, int ssSize, int offset) { + super(ssType, iMod, ssSize, offset); + } + } + + class DebugVC50SSAlignSymImpl extends DebugVC50SubsectionImpl implements DebugVC50SSAlignSym { + private int offset; + + DebugVC50SSAlignSymImpl(short ssType, short iMod, int ssSize, int offset) { + super(ssType, iMod, ssSize, offset); + this.offset = offset; + } + + public DebugVC50SymbolIterator getSymbolIterator() { + return new DebugVC50SymbolIteratorImpl(offset, getSubsectionSize()); + } + } + + class DebugVC50SSSrcLnSegImpl extends DebugVC50SubsectionImpl implements DebugVC50SSSrcLnSeg { + DebugVC50SSSrcLnSegImpl(short ssType, short iMod, int ssSize, int offset) { + super(ssType, iMod, ssSize, offset); + } + } + + class DebugVC50SSSrcModuleImpl extends DebugVC50SubsectionImpl implements DebugVC50SSSrcModule { + private int offset; + private short cFile; + private short cSeg; + private MemoizedObject baseSrcFiles; + private MemoizedObject segOffsets; + private MemoizedObject segs; + + DebugVC50SSSrcModuleImpl(short ssType, short iMod, int ssSize, final int offset) { + super(ssType, iMod, ssSize, offset); + + this.offset = offset; + seek(offset); + cFile = readShort(); + cSeg = readShort(); + + baseSrcFiles = new MemoizedObject() { + public Object computeValue() { + int[] offsets = new int[getNumSourceFiles()]; + seek(offset + 4); + for (int i = 0; i < getNumSourceFiles(); i++) { + offsets[i] = offset + readInt(); + } + DebugVC50SrcModFileDescImpl[] res = new DebugVC50SrcModFileDescImpl[offsets.length]; + for (int i = 0; i < res.length; i++) { + res[i] = new DebugVC50SrcModFileDescImpl(offsets[i], offset); + } + return res; + } + }; + + segOffsets = new MemoizedObject() { + public Object computeValue() { + seek(offset + 4 * (getNumSourceFiles() + 1)); + int[] res = new int[2 * getNumCodeSegments()]; + for (int i = 0; i < 2 * getNumCodeSegments(); i++) { + res[i] = readInt(); + } + return res; + } + }; + + segs = new MemoizedObject() { + public Object computeValue() { + seek(offset + 4 * (getNumSourceFiles() + 1) + 8 * getNumCodeSegments()); + short[] res = new short[getNumCodeSegments()]; + for (int i = 0; i < getNumCodeSegments(); i++) { + res[i] = readShort(); + } + return res; + } + }; + } + + public int getNumSourceFiles() { return cFile & 0xFFFF; } + public int getNumCodeSegments() { return cSeg & 0xFFFF; } + public DebugVC50SrcModFileDesc getSourceFileDesc(int i) { + return ((DebugVC50SrcModFileDescImpl[]) baseSrcFiles.getValue())[i]; + } + + public int getSegmentStartOffset(int i) { + return ((int[]) segOffsets.getValue())[2*i]; + } + + public int getSegmentEndOffset(int i) { + return ((int[]) segOffsets.getValue())[2*i+1]; + } + + public int getSegment(int i) { + return ((short[]) segs.getValue())[i] & 0xFFFF; + } + } + + class DebugVC50SrcModFileDescImpl implements DebugVC50SrcModFileDesc { + private short cSeg; + private MemoizedObject baseSrcLn; + private MemoizedObject segOffsets; + private MemoizedObject name; + + DebugVC50SrcModFileDescImpl(final int offset, final int baseOffset) { + seek(offset); + cSeg = readShort(); + + baseSrcLn = new MemoizedObject() { + public Object computeValue() { + seek(offset + 4); + int[] offsets = new int[getNumCodeSegments()]; + for (int i = 0; i < getNumCodeSegments(); i++) { + offsets[i] = baseOffset + readInt(); + } + DebugVC50SrcModLineNumberMapImpl[] res = + new DebugVC50SrcModLineNumberMapImpl[getNumCodeSegments()]; + for (int i = 0; i < getNumCodeSegments(); i++) { + res[i] = new DebugVC50SrcModLineNumberMapImpl(offsets[i]); + } + return res; + } + }; + + segOffsets = new MemoizedObject() { + public Object computeValue() { + seek(offset + 4 * (getNumCodeSegments() + 1)); + int[] res = new int[2 * getNumCodeSegments()]; + for (int i = 0; i < 2 * getNumCodeSegments(); i++) { + res[i] = readInt(); + } + return res; + } + }; + + name = new MemoizedObject() { + public Object computeValue() { + seek(offset + 4 + 12 * getNumCodeSegments()); + // NOTE: spec says name length is two bytes, but it's really one + int cbName = readByte() & 0xFF; + byte[] res = new byte[cbName]; + readBytes(res); + try { + return new String(res, US_ASCII); + } catch (UnsupportedEncodingException e) { + throw new COFFException(e); + } + } + }; + } + + public int getNumCodeSegments() { return cSeg & 0xFFFF; } + + public DebugVC50SrcModLineNumberMap getLineNumberMap(int i) { + return ((DebugVC50SrcModLineNumberMapImpl[]) baseSrcLn.getValue())[i]; + } + + public int getSegmentStartOffset(int i) { + return ((int[]) segOffsets.getValue())[2*i]; + } + + public int getSegmentEndOffset(int i) { + return ((int[]) segOffsets.getValue())[2*i+1]; + } + + public String getSourceFileName() { + return (String) name.getValue(); + } + } + + class DebugVC50SrcModLineNumberMapImpl implements DebugVC50SrcModLineNumberMap { + private short seg; + private short cPair; + private MemoizedObject offsets; + private MemoizedObject lineNumbers; + + DebugVC50SrcModLineNumberMapImpl(final int offset) { + seek(offset); + seg = readShort(); + cPair = readShort(); + offsets = new MemoizedObject() { + public Object computeValue() { + seek(offset + 4); + int[] res = new int[getNumSourceLinePairs()]; + for (int i = 0; i < getNumSourceLinePairs(); i++) { + res[i] = readInt(); + } + return res; + } + }; + + lineNumbers = new MemoizedObject() { + public Object computeValue() { + seek(offset + 4 * (getNumSourceLinePairs() + 1)); + short[] res = new short[getNumSourceLinePairs()]; + for (int i = 0; i < getNumSourceLinePairs(); i++) { + res[i] = readShort(); + } + return res; + } + }; + } + + public int getSegment() { return seg; } + public int getNumSourceLinePairs() { return cPair; } + public int getCodeOffset(int i) { + return ((int[]) offsets.getValue())[i]; + } + public int getLineNumber(int i) { + return ((short[]) lineNumbers.getValue())[i] & 0xFFFF; + } + } + + class DebugVC50SSLibrariesImpl extends DebugVC50SubsectionImpl implements DebugVC50SSLibraries { + DebugVC50SSLibrariesImpl(short ssType, short iMod, int ssSize, int offset) { + super(ssType, iMod, ssSize, offset); + } + + // FIXME: NOT FINISHED + } + + class DebugVC50SSSymbolBaseImpl extends DebugVC50SubsectionImpl implements DebugVC50SSSymbolBase { + private int offset; + private short symHash; + private short addrHash; + private int cbSymbol; + private int cbSymHash; + private int cbAddrHash; + + private static final int HEADER_SIZE = 16; + + DebugVC50SSSymbolBaseImpl(short ssType, short iMod, int ssSize, int offset) { + super(ssType, iMod, ssSize, offset); + this.offset = offset; + seek(offset); + symHash = readShort(); + addrHash = readShort(); + cbSymbol = readInt(); + cbSymHash = readInt(); + cbAddrHash = readInt(); + } + + public short getSymHashIndex() { return symHash; } + public short getAddrHashIndex() { return addrHash; } + public int getSymTabSize() { return cbSymbol; } + public int getSymHashSize() { return cbSymHash; } + public int getAddrHashSize() { return cbAddrHash; } + + public DebugVC50SymbolIterator getSymbolIterator() { + return new DebugVC50SymbolIteratorImpl(offset + HEADER_SIZE, cbSymbol); + } + } + + class DebugVC50SSGlobalSymImpl extends DebugVC50SSSymbolBaseImpl implements DebugVC50SSGlobalSym { + DebugVC50SSGlobalSymImpl(short ssType, short iMod, int ssSize, int offset) { + super(ssType, iMod, ssSize, offset); + } + } + class DebugVC50SSGlobalPubImpl extends DebugVC50SSSymbolBaseImpl implements DebugVC50SSGlobalPub { + DebugVC50SSGlobalPubImpl(short ssType, short iMod, int ssSize, int offset) { + super(ssType, iMod, ssSize, offset); + } + } + + class DebugVC50SSGlobalTypesImpl extends DebugVC50SubsectionImpl implements DebugVC50SSGlobalTypes { + private int offset; + private int cType; + + DebugVC50SSGlobalTypesImpl(short ssType, short iMod, int ssSize, int offset) { + super(ssType, iMod, ssSize, offset); + this.offset = offset; + seek(offset); + readInt(); // Discard "flags" + cType = readInt(); + } + + public int getNumTypes() { return cType; } + // FIXME: should memoize these + public int getTypeOffset(int i) { + seek(offset + 4 * (i + 2)); + return readInt() + offsetOfFirstType(); + } + + public DebugVC50TypeIterator getTypeIterator() { + return new DebugVC50TypeIteratorImpl(this, + offsetOfFirstType(), + cType); + } + + private int offsetOfFirstType() { + return offset + 4 * (getNumTypes() + 2); + } + } + + class DebugVC50SSMPCImpl extends DebugVC50SubsectionImpl implements DebugVC50SSMPC { + DebugVC50SSMPCImpl(short ssType, short iMod, int ssSize, int offset) { + super(ssType, iMod, ssSize, offset); + } + } + + class DebugVC50SSSegMapImpl extends DebugVC50SubsectionImpl implements DebugVC50SSSegMap { + private short cSeg; + private short cSegLog; + private MemoizedObject segDescs; + + DebugVC50SSSegMapImpl(short ssType, short iMod, int ssSize, final int offset) { + super(ssType, iMod, ssSize, offset); + seek(offset); + cSeg = readShort(); + cSegLog = readShort(); + segDescs = new MemoizedObject() { + public Object computeValue() { + DebugVC50SegDesc[] descs = new DebugVC50SegDesc[cSeg]; + for (int i = 0; i < cSeg; i++) { + descs[i] = new DebugVC50SegDescImpl(offset + 4 + (20 * i)); + } + return descs; + } + }; + } + + public short getNumSegDesc() { return cSeg; } + public short getNumLogicalSegDesc() { return cSegLog; } + public DebugVC50SegDesc getSegDesc(int i) { return ((DebugVC50SegDesc[]) segDescs.getValue())[i]; } + } + + class DebugVC50SegDescImpl implements DebugVC50SegDesc { + private short flags; + private short ovl; + private short group; + private short frame; + private short iSegName; + private short iClassName; + private int offset; + private int cbSeg; + + DebugVC50SegDescImpl(int offset) { + seek(offset); + flags = readShort(); + ovl = readShort(); + group = readShort(); + frame = readShort(); + iSegName = readShort(); + iClassName = readShort(); + offset = readInt(); + cbSeg = readInt(); + } + + public short getFlags() { return flags; } + public short getOverlayNum() { return ovl; } + public short getGroup() { return group; } + public short getFrame() { return frame; } + public short getName() { return iSegName; } + public short getClassName() { return iClassName; } + public int getOffset() { return offset; } + public int getSize() { return cbSeg; } + } + + + class DebugVC50SSSegNameImpl extends DebugVC50SubsectionImpl implements DebugVC50SSSegName { + private int offset; + private int size; + private MemoizedObject names; + + DebugVC50SSSegNameImpl(short ssType, short iMod, int ssSize, int offset) { + super(ssType, iMod, ssSize, offset); + this.offset = offset; + this.size = ssSize; + seek(offset); + names = new MemoizedObject() { + public Object computeValue() { + int i = 0; + List data = new ArrayList(); + while (i < size) { + String s = readCString(); + data.add(s); + i += s.length(); + } + String[] res = new String[data.size()]; + res = (String[]) data.toArray(res); + return res; + } + }; + } + + public String getSegName(int i) { + return ((String[]) names.getValue())[i]; + } + } + + class DebugVC50SSPreCompImpl extends DebugVC50SubsectionImpl implements DebugVC50SSPreComp { + DebugVC50SSPreCompImpl(short ssType, short iMod, int ssSize, int offset) { + super(ssType, iMod, ssSize, offset); + } + } + + class DebugVC50SSOffsetMap16Impl extends DebugVC50SubsectionImpl implements DebugVC50SSOffsetMap16 { + DebugVC50SSOffsetMap16Impl(short ssType, short iMod, int ssSize, int offset) { + super(ssType, iMod, ssSize, offset); + } + } + + class DebugVC50SSOffsetMap32Impl extends DebugVC50SubsectionImpl implements DebugVC50SSOffsetMap32 { + DebugVC50SSOffsetMap32Impl(short ssType, short iMod, int ssSize, int offset) { + super(ssType, iMod, ssSize, offset); + } + } + + class DebugVC50SSFileIndexImpl extends DebugVC50SubsectionImpl implements DebugVC50SSFileIndex { + private int offset; + private short cMod; // Number of modules in the executable + private short cRef; // Total number of file name references + private MemoizedObject modStart; + private MemoizedObject cRefCnt; + // FIXME: probably useless; needs fixup to be converted into + // indices rather than offsets + private MemoizedObject nameRef; + private MemoizedObject names; + + DebugVC50SSFileIndexImpl(short ssType, short iMod, int ssSize, final int offset) { + super(ssType, iMod, ssSize, offset); + this.offset = offset; + seek(offset); + cMod = readShort(); + cRef = readShort(); + modStart = new MemoizedObject() { + public Object computeValue() { + short[] vals = new short[cMod]; + seek(4 + offset); + for (int i = 0; i < cMod; i++) { + vals[i] = readShort(); + } + return vals; + } + }; + cRefCnt = new MemoizedObject() { + public Object computeValue() { + short[] vals = new short[cMod]; + seek(4 + offset + (2 * cMod)); + for (int i = 0; i < cMod; i++) { + vals[i] = readShort(); + } + return vals; + } + }; + nameRef = new MemoizedObject() { + public Object computeValue() { + int[] vals = new int[cRef]; + seek(4 + offset + (4 * cMod)); + for (int i = 0; i < cMod; i++) { + vals[i] = readInt(); + } + return vals; + } + }; + names = new MemoizedObject() { + public Object computeValue() { + String[] vals = new String[cRef]; + for (int i = 0; i < cRef; i++) { + vals[i] = readCString(); + } + return vals; + } + }; + } + + public short getNumModules() { return cMod; } + public short getNumReferences() { return cRef; } + public short[] getModStart() { return (short[]) modStart.getValue(); } + public short[] getRefCount() { return (short[]) cRefCnt.getValue(); } + public int[] getNameRef() { return (int[]) nameRef.getValue(); } + public String[] getNames() { return (String[]) names.getValue(); } + } + + class DebugVC50SSStaticSymImpl extends DebugVC50SSSymbolBaseImpl implements DebugVC50SSStaticSym { + DebugVC50SSStaticSymImpl(short ssType, short iMod, int ssSize, int offset) { + super(ssType, iMod, ssSize, offset); + } + } + + ////////////////////////////////////////////////// + // // + // Implementations of symbol and type iterators // + // // + ////////////////////////////////////////////////// + + class DebugVC50SymbolIteratorImpl implements DebugVC50SymbolIterator { + private int base; + private int size; + private int pos; + private int curSymSize; + private int curSymType; + + private static final int HEADER_SIZE = 4; + + DebugVC50SymbolIteratorImpl(int base, int size) { + this(base, size, base); + } + + private DebugVC50SymbolIteratorImpl(int base, int size, int pos) { + this.base = base; + this.size = size; + this.pos = pos; + seek(pos); + curSymSize = readShort() & 0xFFFF; + curSymType = readShort() & 0xFFFF; + } + + public boolean done() { + return (pos == (base + size)); + } + + public void next() throws NoSuchElementException { + if (done()) throw new NoSuchElementException("No more symbols"); + pos += curSymSize + 2; + seek(pos); + curSymSize = readShort() & 0xFFFF; + curSymType = readShort() & 0xFFFF; + } + + public short getLength() { + return (short) curSymSize; + } + + public int getType() { + return curSymType; + } + + public int getOffset() { + return pos + HEADER_SIZE; + } + + ///////////////////////// + // S_COMPILE accessors // + ///////////////////////// + + public byte getCompilerTargetProcessor() { + symSeek(0); + return readByte(); + } + + public int getCompilerFlags() { + symSeek(1); + int res = 0; + for (int i = 0; i < 3; i++) { + int b = readByte() & 0xFF; + res = (res << 8) | b; + } + return res; + } + + public String getComplierVersion() { + return readLengthPrefixedStringAt(4); + } + + ////////////////////////// + // S_REGISTER accessors // + ////////////////////////// + + public int getRegisterSymbolType() { + symSeek(0); + return readInt(); + } + + public short getRegisterEnum() { + symSeek(4); + return readShort(); + } + + public String getRegisterSymbolName() { + return readLengthPrefixedStringAt(6); + } + + ////////////////////////// + // S_CONSTANT accessors // + ////////////////////////// + + public int getConstantType() { + symSeek(0); + return readInt(); + } + + public int getConstantValueAsInt() throws DebugVC50WrongNumericTypeException { + return readIntNumericLeafAt(4); + } + + public long getConstantValueAsLong() throws DebugVC50WrongNumericTypeException { + return readLongNumericLeafAt(4); + } + + public float getConstantValueAsFloat() throws DebugVC50WrongNumericTypeException { + return readFloatNumericLeafAt(4); + } + + public double getConstantValueAsDouble() throws DebugVC50WrongNumericTypeException { + return readDoubleNumericLeafAt(4); + } + + public String getConstantName() { + return readLengthPrefixedStringAt(4 + numericLeafLengthAt(4)); + } + + ///////////////////// + // S_UDT accessors // + ///////////////////// + + public int getUDTType() { + symSeek(0); + return readInt(); + } + + public String getUDTName() { + return readLengthPrefixedStringAt(4); + } + + ///////////////////////// + // S_SSEARCH accessors // + ///////////////////////// + + public int getSearchSymbolOffset() { + symSeek(0); + return readInt(); + } + + public short getSearchSegment() { + symSeek(4); + return readShort(); + } + + ///////////////////// + // S_END accessors // + ///////////////////// + + // (No accessors) + + ////////////////////// + // S_SKIP accessors // + ////////////////////// + + // (No accessors) + + /////////////////////////// + // S_CVRESERVE accessors // + /////////////////////////// + + // (No accessors) + + ///////////////////////// + // S_OBJNAME accessors // + ///////////////////////// + + public int getObjectCodeViewSignature() { + symSeek(0); + return readInt(); + } + + public String getObjectName() { + return readLengthPrefixedStringAt(4); + } + + //////////////////////// + // S_ENDARG accessors // + //////////////////////// + + // (No accessors) + + ////////////////////////// + // S_COBOLUDT accessors // + ////////////////////////// + + // (Elided as they are irrelevant) + + ///////////////////////// + // S_MANYREG accessors // + ///////////////////////// + + public int getManyRegType() { + symSeek(0); + return readInt(); + } + + public byte getManyRegCount() { + symSeek(4); + return readByte(); + } + + public byte getManyRegRegister(int i) { + symSeek(5 + i); + return readByte(); + } + + public String getManyRegName() { + return readLengthPrefixedStringAt(5 + getManyRegCount()); + } + + //////////////////////// + // S_RETURN accessors // + //////////////////////// + + public short getReturnFlags() { + symSeek(0); + return readShort(); + } + + public byte getReturnStyle() { + symSeek(2); + return readByte(); + } + + public byte getReturnRegisterCount() { + symSeek(3); + return readByte(); + } + + public byte getReturnRegister(int i) { + symSeek(4 + i); + return readByte(); + } + + /////////////////////////// + // S_ENTRYTHIS accessors // + /////////////////////////// + + public void advanceToEntryThisSymbol() { + seek(pos + 4); + int tmpSymSize = readShort(); + int tmpSymType = readShort(); + if (Assert.ASSERTS_ENABLED) { + // Make sure that ends of inner and outer symbols line + // up, otherwise need more work + Assert.that(pos + curSymSize + 2 == pos + 4 + tmpSymSize, + "advanceToEntryThisSymbol needs more work"); + } + pos += 4; + curSymSize = tmpSymSize; + curSymType = tmpSymType; + } + + /////////////////////////////////////////////////////////////////////// + // // + // // + // Symbols for (Intel) 16:32 Segmented and 32-bit Flat Architectures // + // // + // // + /////////////////////////////////////////////////////////////////////// + + ///////////////////////// + // S_BPREL32 accessors // + ///////////////////////// + + public int getBPRelOffset() { + symSeek(0); + return readInt(); + } + + public int getBPRelType() { + symSeek(4); + return readInt(); + } + + public String getBPRelName() { + return readLengthPrefixedStringAt(8); + } + + /////////////////////////////////////// + // S_LDATA32 and S_GDATA32 accessors // + /////////////////////////////////////// + + public int getLGDataType() { + symSeek(0); + return readInt(); + } + + public int getLGDataOffset() { + symSeek(4); + return readInt(); + } + + public short getLGDataSegment() { + symSeek(8); + return readShort(); + } + + public String getLGDataName() { + return readLengthPrefixedStringAt(10); + } + + /////////////////////// + // S_PUB32 accessors // + /////////////////////// + + // FIXME: has the same format as the above; consider updating + // documentation. No separate accessors provided. + + /////////////////////////////////////// + // S_LPROC32 and S_GPROC32 accessors // + /////////////////////////////////////// + + public DebugVC50SymbolIterator getLGProcParent() { + int offs = getLGProcParentOffset(); + if (offs == 0) return null; + return new DebugVC50SymbolIteratorImpl(base, size, offs); + } + + public int getLGProcParentOffset() { + symSeek(0); + int offs = readInt(); + if (offs == 0) return 0; + return base + offs; + } + + public DebugVC50SymbolIterator getLGProcEnd() { + int offs = getLGProcEndOffset(); + return new DebugVC50SymbolIteratorImpl(base, size, offs); + } + + public int getLGProcEndOffset() { + symSeek(4); + int offs = readInt(); + if (Assert.ASSERTS_ENABLED) { + Assert.that(offs != 0, "should not have null end offset for procedure symbols"); + } + return base + offs; + } + + public DebugVC50SymbolIterator getLGProcNext() { + int offs = getLGProcNextOffset(); + if (offs == 0) return null; + return new DebugVC50SymbolIteratorImpl(base, size, offs); + } + + public int getLGProcNextOffset() { + symSeek(8); + int offs = readInt(); + if (offs == 0) return 0; + return base + offs; + } + + public int getLGProcLength() { + symSeek(12); + return readInt(); + } + + public int getLGProcDebugStart() { + symSeek(16); + return readInt(); + } + + public int getLGProcDebugEnd() { + symSeek(20); + return readInt(); + } + + public int getLGProcType() { + symSeek(24); + return readInt(); + } + + public int getLGProcOffset() { + symSeek(28); + return readInt(); + } + + public short getLGProcSegment() { + symSeek(32); + return readShort(); + } + + public byte getLGProcFlags() { + symSeek(34); + return readByte(); + } + + public String getLGProcName() { + return readLengthPrefixedStringAt(35); + } + + ///////////////////////// + // S_THUNK32 accessors // + ///////////////////////// + + public DebugVC50SymbolIterator getThunkParent() { + int offs = getThunkParentOffset(); + if (offs == 0) return null; + return new DebugVC50SymbolIteratorImpl(base, size, offs); + } + + public int getThunkParentOffset() { + symSeek(0); + int offs = readInt(); + if (offs == 0) return 0; + return base + offs; + } + + public DebugVC50SymbolIterator getThunkEnd() { + symSeek(4); + int offs = readInt(); + return new DebugVC50SymbolIteratorImpl(base, size, offs); + } + + public int getThunkEndOffset() { + symSeek(4); + int offs = readInt(); + if (Assert.ASSERTS_ENABLED) { + Assert.that(offs != 0, "should not have null end offset for thunk symbols"); + } + return base + offs; + } + + public DebugVC50SymbolIterator getThunkNext() { + int offs = getThunkNextOffset(); + if (offs == 0) return null; + return new DebugVC50SymbolIteratorImpl(base, size, base + offs); + } + + public int getThunkNextOffset() { + symSeek(8); + int offs = readInt(); + if (offs == 0) return 0; + return base + offs; + } + + public int getThunkOffset() { + symSeek(12); + return readInt(); + } + + public short getThunkSegment() { + symSeek(16); + return readShort(); + } + + public short getThunkLength() { + symSeek(18); + return readShort(); + } + + public byte getThunkType() { + symSeek(20); + return readByte(); + } + + public String getThunkName() { + return readLengthPrefixedStringAt(21); + } + + public short getThunkAdjustorThisDelta() { + symSeek(21 + lengthPrefixedStringLengthAt(21)); + return readShort(); + } + + public String getThunkAdjustorTargetName() { + return readLengthPrefixedStringAt(23 + lengthPrefixedStringLengthAt(21)); + } + + public short getThunkVCallDisplacement() { + symSeek(21 + lengthPrefixedStringLengthAt(21)); + return readShort(); + } + + public int getThunkPCodeOffset() { + symSeek(21 + lengthPrefixedStringLengthAt(21)); + return readInt(); + } + + public short getThunkPCodeSegment() { + symSeek(25 + lengthPrefixedStringLengthAt(21)); + return readShort(); + } + + ///////////////////////// + // S_BLOCK32 accessors // + ///////////////////////// + + public DebugVC50SymbolIterator getBlockParent() { + int offs = getBlockParentOffset(); + if (offs == 0) return null; + return new DebugVC50SymbolIteratorImpl(base, size, offs); + } + + public int getBlockParentOffset() { + symSeek(0); + int offs = readInt(); + if (offs == 0) return 0; + return base + offs; + } + + public DebugVC50SymbolIterator getBlockEnd() { + symSeek(4); + int offs = readInt(); + return new DebugVC50SymbolIteratorImpl(base, size, offs); + } + + public int getBlockEndOffset() { + symSeek(4); + int offs = readInt(); + if (Assert.ASSERTS_ENABLED) { + Assert.that(offs != 0, "should not have null end offset for block symbols"); + } + return base + offs; + } + + public int getBlockLength() { + symSeek(8); + return readInt(); + } + + public int getBlockOffset() { + symSeek(12); + return readInt(); + } + + public short getBlockSegment() { + symSeek(16); + return readShort(); + } + + public String getBlockName() { + return readLengthPrefixedStringAt(18); + } + + //////////////////////// + // S_WITH32 accessors // + //////////////////////// + + // FIXME: this is a Pascal construct; ignored for now + + ///////////////////////// + // S_LABEL32 accessors // + ///////////////////////// + + public int getLabelOffset() { + symSeek(0); + return readInt(); + } + + public short getLabelSegment() { + symSeek(4); + return readShort(); + } + + public byte getLabelFlags() { + symSeek(6); + return readByte(); + } + + public String getLabelName() { + return readLengthPrefixedStringAt(7); + } + + //////////////////////////// + // S_CEXMODEL32 accessors // + //////////////////////////// + + public int getChangeOffset() { + symSeek(0); + return readInt(); + } + + public short getChangeSegment() { + symSeek(4); + return readShort(); + } + + public short getChangeModel() { + symSeek(6); + return readShort(); + } + + //////////////////////////// + // S_VFTTABLE32 accessors // + //////////////////////////// + + public int getVTableRoot() { + symSeek(0); + return readInt(); + } + + public int getVTablePath() { + symSeek(4); + return readInt(); + } + + public int getVTableOffset() { + symSeek(8); + return readInt(); + } + + public short getVTableSegment() { + symSeek(12); + return readShort(); + } + + ////////////////////////// + // S_REGREL32 accessors // + ////////////////////////// + + public int getRegRelOffset() { + symSeek(0); + return readInt(); + } + + public int getRegRelType() { + symSeek(4); + return readInt(); + } + + public short getRegRelRegister() { + symSeek(8); + return readShort(); + } + + public String getRegRelName() { + return readLengthPrefixedStringAt(10); + } + + /////////////////////////////////////////// + // S_LTHREAD32 and S_GTHREAD32 accessors // + /////////////////////////////////////////// + + public int getLThreadType() { + symSeek(0); + return readInt(); + } + + public int getLThreadOffset() { + symSeek(4); + return readInt(); + } + + public short getLThreadSegment() { + symSeek(8); + return readShort(); + } + + public String getLThreadName() { + return readLengthPrefixedStringAt(10); + } + + //---------------------------------------------------------------------- + // Internals only below this point + // + + private void symSeek(int offsetInSym) { + seek(pos + HEADER_SIZE + offsetInSym); + } + + private int numericLeafLengthAt(int offsetInSym) { + return DebugVC50Impl.this.numericLeafLengthAt(pos + HEADER_SIZE + offsetInSym); + } + + private int readIntNumericLeafAt(int offsetInSym) { + return DebugVC50Impl.this.readIntNumericLeafAt(pos + HEADER_SIZE + offsetInSym); + } + + private long readLongNumericLeafAt(int offsetInSym) { + return DebugVC50Impl.this.readLongNumericLeafAt(pos + HEADER_SIZE + offsetInSym); + } + + private float readFloatNumericLeafAt(int offsetInSym) { + return DebugVC50Impl.this.readFloatNumericLeafAt(pos + HEADER_SIZE + offsetInSym); + } + + private double readDoubleNumericLeafAt(int offsetInSym) { + return DebugVC50Impl.this.readDoubleNumericLeafAt(pos + HEADER_SIZE + offsetInSym); + } + + private int lengthPrefixedStringLengthAt(int offsetInSym) { + return DebugVC50Impl.this.lengthPrefixedStringLengthAt(pos + HEADER_SIZE + offsetInSym); + } + + private String readLengthPrefixedStringAt(int offsetInSym) { + return DebugVC50Impl.this.readLengthPrefixedStringAt(pos + HEADER_SIZE + offsetInSym); + } + } + + class DebugVC50TypeIteratorImpl implements DebugVC50TypeIterator, + DebugVC50TypeLeafIndices, DebugVC50MemberAttributes, DebugVC50TypeEnums { + private DebugVC50SSGlobalTypes parent; + private int base; + private int numTypes; + private int typeIndex; + private int typeRecordOffset; + private int typeStringOffset; + private int typeRecordSize; + private int typeStringLeaf; + + DebugVC50TypeIteratorImpl(DebugVC50SSGlobalTypes parent, int base, int numTypes) { + this(parent, base, numTypes, 0, base); + } + + private DebugVC50TypeIteratorImpl(DebugVC50SSGlobalTypes parent, int base, int numTypes, int curType, int offset) { + this.parent = parent; + this.base = base; + this.numTypes = numTypes; + this.typeIndex = curType; + if (!done()) { + typeRecordOffset = offset; + loadTypeRecord(); + } + } + + public boolean done() { + return (typeIndex == numTypes); + } + + public void next() throws NoSuchElementException { + if (done()) throw new NoSuchElementException(); + ++typeIndex; + if (!done()) { + typeRecordOffset = parent.getTypeOffset(typeIndex); + loadTypeRecord(); + } + } + + public short getLength() { + return (short) typeRecordSize; + } + + public int getTypeIndex() { + return biasTypeIndex(typeIndex); + } + + public int getNumTypes() { + return numTypes; + } + + public boolean typeStringDone() { + return (typeStringOffset - typeRecordOffset - 2) >= typeRecordSize; + } + + public void typeStringNext() throws NoSuchElementException { + if (typeStringDone()) throw new NoSuchElementException(); + typeStringOffset += typeStringLength(); + loadTypeString(); + } + + public int typeStringLeaf() { + return typeStringLeaf; + } + + public int typeStringOffset() { + return typeStringOffset; + } + + /////////////////////////// + // LF_MODIFIER accessors // + /////////////////////////// + + public int getModifierIndex() { + typeSeek(2); + return readInt(); + } + + public short getModifierAttribute() { + typeSeek(6); + return readShort(); + } + + ////////////////////////// + // LF_POINTER accessors // + ////////////////////////// + + public int getPointerType() { + typeSeek(2); + return readInt(); + } + + public int getPointerAttributes() { + typeSeek(6); + return readInt(); + } + + public int getPointerBasedOnTypeIndex() { + typeSeek(10); + return readInt(); + } + + public String getPointerBasedOnTypeName() { + return readLengthPrefixedStringAt(14); + } + + public int getPointerToMemberClass() { + typeSeek(10); + return readInt(); + } + + public short getPointerToMemberFormat() { + typeSeek(14); + return readShort(); + } + + //////////////////////// + // LF_ARRAY accessors // + //////////////////////// + + public int getArrayElementType() { + typeSeek(2); + return readInt(); + } + + public int getArrayIndexType() { + typeSeek(6); + return readInt(); + } + + public int getArrayLength() throws DebugVC50WrongNumericTypeException { + return readIntNumericLeafAt(10); + } + + public String getArrayName() { + return readLengthPrefixedStringAt(10 + numericLeafLengthAt(10)); + } + + ///////////////////////////////////////// + // LF_CLASS and LF_STRUCTURE accessors // + ///////////////////////////////////////// + + public short getClassCount() { + typeSeek(2); + return readShort(); + } + + public short getClassProperty() { + typeSeek(4); + return readShort(); + } + + public int getClassFieldList() { + typeSeek(6); + return readInt(); + } + + public DebugVC50TypeIterator getClassFieldListIterator() { + int index = unbiasTypeIndex(getClassFieldList()); + int offset = parent.getTypeOffset(index); + return new DebugVC50TypeIteratorImpl(parent, base, numTypes, index, offset); + } + + public int getClassDerivationList() { + typeSeek(10); + return readInt(); + } + + public int getClassVShape() { + typeSeek(14); + return readInt(); + } + + public int getClassSize() throws DebugVC50WrongNumericTypeException { + return readIntNumericLeafAt(18); + } + + public String getClassName() { + return readLengthPrefixedStringAt(18 + numericLeafLengthAt(18)); + } + + //////////////////////// + // LF_UNION accessors // + //////////////////////// + + public short getUnionCount() { + typeSeek(2); + return readShort(); + } + + public short getUnionProperty() { + typeSeek(4); + return readShort(); + } + + public int getUnionFieldList() { + typeSeek(6); + return readInt(); + } + + public DebugVC50TypeIterator getUnionFieldListIterator() { + int index = unbiasTypeIndex(getUnionFieldList()); + int offset = parent.getTypeOffset(index); + return new DebugVC50TypeIteratorImpl(parent, base, numTypes, index, offset); + } + + public int getUnionSize() throws DebugVC50WrongNumericTypeException { + return readIntNumericLeafAt(10); + } + + public String getUnionName() { + return readLengthPrefixedStringAt(10 + numericLeafLengthAt(10)); + } + + /////////////////////// + // LF_ENUM accessors // + /////////////////////// + + public short getEnumCount() { + typeSeek(2); + return readShort(); + } + + public short getEnumProperty() { + typeSeek(4); + return readShort(); + } + + public int getEnumType() { + typeSeek(6); + return readInt(); + } + + public int getEnumFieldList() { + typeSeek(10); + return readInt(); + } + + public DebugVC50TypeIterator getEnumFieldListIterator() { + int index = unbiasTypeIndex(getEnumFieldList()); + int offset = parent.getTypeOffset(index); + return new DebugVC50TypeIteratorImpl(parent, base, numTypes, index, offset); + } + + public String getEnumName() { + return readLengthPrefixedStringAt(14); + } + + //////////////////////////// + // LF_PROCEDURE accessors // + //////////////////////////// + + public int getProcedureReturnType() { + typeSeek(2); + return readInt(); + } + + public byte getProcedureCallingConvention() { + typeSeek(6); + return readByte(); + } + + public short getProcedureNumberOfParameters() { + typeSeek(8); + return readShort(); + } + + public int getProcedureArgumentList() { + typeSeek(10); + return readInt(); + } + + public DebugVC50TypeIterator getProcedureArgumentListIterator() { + int index = unbiasTypeIndex(getProcedureArgumentList()); + int offset = parent.getTypeOffset(index); + return new DebugVC50TypeIteratorImpl(parent, base, numTypes, index, offset); + } + + //////////////////////////// + // LF_MFUNCTION accessors // + //////////////////////////// + + public int getMFunctionReturnType() { + typeSeek(2); + return readInt(); + } + + public int getMFunctionContainingClass() { + typeSeek(6); + return readInt(); + } + + public int getMFunctionThis() { + typeSeek(10); + return readInt(); + } + + public byte getMFunctionCallingConvention() { + typeSeek(14); + return readByte(); + } + + public short getMFunctionNumberOfParameters() { + typeSeek(16); + return readShort(); + } + + public int getMFunctionArgumentList() { + typeSeek(18); + return readInt(); + } + + public DebugVC50TypeIterator getMFunctionArgumentListIterator() { + int index = unbiasTypeIndex(getMFunctionArgumentList()); + int offset = parent.getTypeOffset(index); + return new DebugVC50TypeIteratorImpl(parent, base, numTypes, index, offset); + } + + public int getMFunctionThisAdjust() { + typeSeek(22); + return readInt(); + } + + ////////////////////////// + // LF_VTSHAPE accessors // + ////////////////////////// + + public short getVTShapeCount() { + typeSeek(2); + return readShort(); + } + + public int getVTShapeDescriptor(int i) { + typeSeek(4 + (i / 2)); + int val = readByte() & 0xFF; + if ((i % 2) != 0) { + val = val >> 4; + } + return val; + } + + ///////////////////////// + // LF_BARRAY accessors // + ///////////////////////// + + public int getBasicArrayType() { + typeSeek(2); + return readInt(); + } + + //////////////////////// + // LF_LABEL accessors // + //////////////////////// + + public short getLabelAddressMode() { + typeSeek(2); + return readShort(); + } + + /////////////////////////// + // LF_DIMARRAY accessors // + /////////////////////////// + + public int getDimArrayType() { + typeSeek(2); + return readInt(); + } + + public int getDimArrayDimInfo() { + typeSeek(6); + return readInt(); + } + + public String getDimArrayName() { + return readLengthPrefixedStringAt(10); + } + + ////////////////////////// + // LF_VFTPATH accessors // + ////////////////////////// + + public int getVFTPathCount() { + typeSeek(2); + return readInt(); + } + + public int getVFTPathBase(int i) { + typeSeek(6 + (4 * i)); + return readInt(); + } + + /////////////////////// + // LF_SKIP accessors // + /////////////////////// + + public int getSkipIndex() { + typeSeek(2); + return readInt(); + } + + ////////////////////////// + // LF_ARGLIST accessors // + ////////////////////////// + + public int getArgListCount() { + typeSeek(2); + return readInt(); + } + + public int getArgListType(int i) { + typeSeek(6 + (4 * i)); + return readInt(); + } + + ///////////////////////// + // LF_DEFARG accessors // + ///////////////////////// + + public int getDefaultArgType() { + typeSeek(2); + return readInt(); + } + + public String getDefaultArgExpression() { + return readLengthPrefixedStringAt(6); + } + + ////////////////////////// + // LF_DERIVED accessors // + ////////////////////////// + + public int getDerivedCount() { + typeSeek(2); + return readInt(); + } + + public int getDerivedType(int i) { + typeSeek(6); + return readInt(); + } + + /////////////////////////// + // LF_BITFIELD accessors // + /////////////////////////// + + public int getBitfieldFieldType() { + typeSeek(2); + return readInt(); + } + + public byte getBitfieldLength() { + typeSeek(6); + return readByte(); + } + + public byte getBitfieldPosition() { + typeSeek(7); + return readByte(); + } + + //////////////////////// + // LF_MLIST accessors // + //////////////////////// + + public short getMListAttribute() { + typeSeek(2); + return readShort(); + } + + public int getMListLength() { + return (getLength() - 6 - (isMListIntroducingVirtual() ? 4 : 0)) / 4; + } + + public int getMListType(int i) { + typeSeek(6 + 4 * i); + return readInt(); + } + + public boolean isMListIntroducingVirtual() { + return isIntroducingVirtual(getMListAttribute()); + } + + public int getMListVtabOffset() { + typeSeek(6 + 4 * getMListLength()); + return readInt(); + } + + ///////////////////////// + // LF_REFSYM accessors // + ///////////////////////// + + public DebugVC50SymbolIterator getRefSym() { + typeSeek(2); + int len = readShort() & 0xFFFF; + return new DebugVC50SymbolIteratorImpl(typeStringOffset + 2, len); + } + + ///////////////////////// + // LF_BCLASS accessors // + ///////////////////////// + + public short getBClassAttribute() { + typeSeek(2); + return readShort(); + } + + public int getBClassType() { + typeSeek(4); + return readInt(); + } + + public int getBClassOffset() throws DebugVC50WrongNumericTypeException { + return readIntNumericLeafAt(8); + } + + ////////////////////////// + // LF_VBCLASS accessors // + ////////////////////////// + + public short getVBClassAttribute() { + typeSeek(2); + return readShort(); + } + + public int getVBClassBaseClassType() { + typeSeek(4); + return readInt(); + } + + public int getVBClassVirtualBaseClassType() { + typeSeek(8); + return readInt(); + } + + public int getVBClassVBPOff() throws DebugVC50WrongNumericTypeException { + return readIntNumericLeafAt(12); + } + + public int getVBClassVBOff() throws DebugVC50WrongNumericTypeException { + return readIntNumericLeafAt(12 + numericLeafLengthAt(12)); + } + + /////////////////////////// + // LF_IVBCLASS accessors // + /////////////////////////// + + public short getIVBClassAttribute() { + typeSeek(2); + return readShort(); + } + + public int getIVBClassBType() { + typeSeek(4); + return readInt(); + } + + public int getIVBClassVBPType() { + typeSeek(8); + return readInt(); + } + + public int getIVBClassVBPOff() throws DebugVC50WrongNumericTypeException { + return readIntNumericLeafAt(12); + } + + public int getIVBClassVBOff() throws DebugVC50WrongNumericTypeException { + return readIntNumericLeafAt(12 + numericLeafLengthAt(12)); + } + + //////////////////////////// + // LF_ENUMERATE accessors // + //////////////////////////// + + public short getEnumerateAttribute() { + typeSeek(2); + return readShort(); + } + + public long getEnumerateValue() { + return readIntNumericLeafAt(4); + } + + public String getEnumerateName() { + return readLengthPrefixedStringAt(4 + numericLeafLengthAt(4)); + } + + //////////////////////////// + // LF_FRIENDFCN accessors // + //////////////////////////// + + public int getFriendFcnType() { + typeSeek(4); + return readInt(); + } + + public String getFriendFcnName() { + return readLengthPrefixedStringAt(8); + } + + //////////////////////// + // LF_INDEX accessors // + //////////////////////// + + public int getIndexValue() { + typeSeek(4); + return readInt(); + } + + public DebugVC50TypeIterator getIndexIterator() { + int index = unbiasTypeIndex(getIndexValue()); + int offset = parent.getTypeOffset(index); + return new DebugVC50TypeIteratorImpl(parent, base, numTypes, index, offset); + } + + ///////////////////////// + // LF_MEMBER accessors // + ///////////////////////// + + public short getMemberAttribute() { + typeSeek(2); + return readShort(); + } + + public int getMemberType() { + typeSeek(4); + return readInt(); + } + + public int getMemberOffset() throws DebugVC50WrongNumericTypeException { + return readIntNumericLeafAt(8); + } + + public String getMemberName() { + return readLengthPrefixedStringAt(8 + numericLeafLengthAt(8)); + } + + /////////////////////////// + // LF_STMEMBER accessors // + /////////////////////////// + + public short getStaticAttribute() { + typeSeek(2); + return readShort(); + } + + public int getStaticType() { + typeSeek(4); + return readInt(); + } + + public String getStaticName() { + return readLengthPrefixedStringAt(8); + } + + ///////////////////////// + // LF_METHOD accessors // + ///////////////////////// + + public short getMethodCount() { + typeSeek(2); + return readShort(); + } + + public int getMethodList() { + typeSeek(4); + return readInt(); + } + + public String getMethodName() { + return readLengthPrefixedStringAt(8); + } + + ///////////////////////////// + // LF_NESTEDTYPE accessors // + ///////////////////////////// + + public int getNestedType() { + typeSeek(4); + return readInt(); + } + + public String getNestedName() { + return readLengthPrefixedStringAt(8); + } + + /////////////////////////// + // LF_VFUNCTAB accessors // + /////////////////////////// + + public int getVFuncTabType() { + typeSeek(4); + return readInt(); + } + + //////////////////////////// + // LF_FRIENDCLS accessors // + //////////////////////////// + + public int getFriendClsType() { + typeSeek(4); + return readInt(); + } + + //////////////////////////// + // LF_ONEMETHOD accessors // + //////////////////////////// + + public short getOneMethodAttribute() { + typeSeek(2); + return readShort(); + } + + public int getOneMethodType() { + typeSeek(4); + return readInt(); + } + + public boolean isOneMethodIntroducingVirtual() { + return isIntroducingVirtual(getOneMethodAttribute()); + } + + public int getOneMethodVBaseOff() { + typeSeek(8); + return readInt(); + } + + public String getOneMethodName() { + int baseLen = 8 + (isOneMethodIntroducingVirtual() ? 4 : 0); + return readLengthPrefixedStringAt(baseLen); + } + + /////////////////////////// + // LF_VFUNCOFF accessors // + /////////////////////////// + + public int getVFuncOffType() { + typeSeek(4); + return readInt(); + } + + public int getVFuncOffOffset() { + typeSeek(8); + return readInt(); + } + + /////////////////////////////// + // LF_NESTEDTYPEEX accessors // + /////////////////////////////// + + public short getNestedExAttribute() { + typeSeek(2); + return readShort(); + } + + public int getNestedExType() { + typeSeek(4); + return readInt(); + } + + public String getNestedExName() { + return readLengthPrefixedStringAt(8); + } + + /////////////////////////////// + // LF_MEMBERMODIFY accessors // + /////////////////////////////// + + public short getMemberModifyAttribute() { + typeSeek(2); + return readShort(); + } + + public int getMemberModifyType() { + typeSeek(4); + return readInt(); + } + + public String getMemberModifyName() { + return readLengthPrefixedStringAt(8); + } + + //////////////////////////// + // Numeric Leaf accessors // + //////////////////////////// + + public short getNumericTypeAt(int byteOffset) { + typeSeek(byteOffset); + return readShort(); + } + + public int getNumericLengthAt(int byteOffset) + throws DebugVC50WrongNumericTypeException { + return numericLeafLengthAt(byteOffset); + } + + public int getNumericIntAt(int byteOffset) + throws DebugVC50WrongNumericTypeException { + return readIntNumericLeafAt(byteOffset); + } + + public long getNumericLongAt(int byteOffset) + throws DebugVC50WrongNumericTypeException { + // FIXME + throw new RuntimeException("Unimplemented"); + } + + public float getNumericFloatAt(int byteOffset) + throws DebugVC50WrongNumericTypeException { + // FIXME + throw new RuntimeException("Unimplemented"); + } + + public double getNumericDoubleAt(int byteOffset) + throws DebugVC50WrongNumericTypeException { + // FIXME + throw new RuntimeException("Unimplemented"); + } + + public byte[] getNumericDataAt(int byteOffset) + throws DebugVC50WrongNumericTypeException { + // FIXME + throw new RuntimeException("Unimplemented"); + } + + //---------------------------------------------------------------------- + // Internals only below this point + // + + private void loadTypeRecord() { + seek(typeRecordOffset); + typeRecordSize = readShort() & 0xFFFF; + typeStringOffset = typeRecordOffset + 2; + loadTypeString(); + } + + private void loadTypeString() { + seek(typeStringOffset); + int lo = readByte() & 0xFF; + // See if it is one of the single-byte leaves + if (lo >= LF_PAD0) { + typeStringLeaf = lo; + } else { + int hi = readByte() & 0xFF; + typeStringLeaf = (hi << 8) | lo; + } + } + + private void typeSeek(int offset) { + seek(typeStringOffset + offset); + } + + private int typeStringLength() { + // LF_PAD + if (typeStringLeaf >= 0xF0 && typeStringLeaf <= 0xFF) { + return (typeStringLeaf - 0xF0); + } + + switch (typeStringLeaf) { + + // Leaf indices for type records that can be referenced + // from symbols: + case LF_MODIFIER: return 8; + case LF_POINTER: { + int extraLen = 0; + int attr = (getPointerAttributes() & POINTER_PTRTYPE_MASK) >> POINTER_PTRTYPE_SHIFT; + int mode = (getPointerAttributes() & POINTER_PTRMODE_MASK) >> POINTER_PTRMODE_SHIFT; + if (attr == POINTER_PTRTYPE_BASED_ON_TYPE) { + extraLen = 4 + numericLeafLengthAt(typeStringOffset + 14); + } else if (mode == POINTER_PTRMODE_PTR_TO_DATA_MEMBER || + mode == POINTER_PTRMODE_PTR_TO_METHOD) { + extraLen = 6; + } + return 10 + extraLen; + } + case LF_ARRAY: { + int temp = 10 + numericLeafLengthAt(10); + return temp + lengthPrefixedStringLengthAt(temp); + } + case LF_CLASS: + case LF_STRUCTURE: { + int temp = 18 + numericLeafLengthAt(18); + return temp + lengthPrefixedStringLengthAt(temp); + } + case LF_UNION: { + int temp = 10 + numericLeafLengthAt(10); + return temp + lengthPrefixedStringLengthAt(temp); + } + case LF_ENUM: { + return 14 + lengthPrefixedStringLengthAt(14); + } + case LF_PROCEDURE: return 14; + case LF_MFUNCTION: return 26; + case LF_VTSHAPE: return 4 + ((getVTShapeCount() + 1) / 2); + case LF_COBOL0: + case LF_COBOL1: throw new COFFException("COBOL symbols unimplemented"); + case LF_BARRAY: return 6; + case LF_LABEL: return 4; + case LF_NULL: return 2; + case LF_NOTTRAN: return 2; + case LF_DIMARRAY: return 10 + lengthPrefixedStringLengthAt(10); + case LF_VFTPATH: return 6 + 4 * getVFTPathCount(); + case LF_PRECOMP: return 14 + lengthPrefixedStringLengthAt(14); + case LF_ENDPRECOMP: return 6; + case LF_OEM: throw new COFFException("OEM symbols unimplemented"); + case LF_TYPESERVER: return 10 + lengthPrefixedStringLengthAt(10); + + case LF_SKIP: return 6 + numericLeafLengthAt(6); + case LF_ARGLIST: return 6 + 4 * getArgListCount(); + case LF_DEFARG: return 6 + lengthPrefixedStringLengthAt(6); + // case LF_FIELDLIST: throw new COFFException("Should not see LF_FIELDLIST leaf"); + case LF_FIELDLIST: return 2; + case LF_DERIVED: return 6 + 4 * getDerivedCount(); + case LF_BITFIELD: return 8; + case LF_METHODLIST: { + return 6 + 4 * getMListLength() + (isMListIntroducingVirtual() ? 4 : 0); + } + case LF_DIMCONU: + case LF_DIMCONLU: + case LF_DIMVARU: + case LF_DIMVARLU: throw new COFFException("LF_DIMCONU, LF_DIMCONLU, LF_DIMVARU, and LF_DIMVARLU unsupported"); + case LF_REFSYM: { + seek(typeStringOffset + 2); + return 4 + readShort(); + } + + case LF_BCLASS: return 8 + numericLeafLengthAt(8); + case LF_VBCLASS: + case LF_IVBCLASS: { + int temp = 12 + numericLeafLengthAt(12); + return temp + numericLeafLengthAt(temp); + } + case LF_ENUMERATE: { + int temp = 4 + numericLeafLengthAt(4); + return temp + lengthPrefixedStringLengthAt(temp); + } + case LF_FRIENDFCN: return 8 + lengthPrefixedStringLengthAt(8); + case LF_INDEX: return 8; + case LF_MEMBER: { + int temp = 8 + numericLeafLengthAt(8); + return temp + lengthPrefixedStringLengthAt(temp); + } + case LF_STMEMBER: return 8 + lengthPrefixedStringLengthAt(8); + case LF_METHOD: return 8 + lengthPrefixedStringLengthAt(8); + case LF_NESTTYPE: return 8 + lengthPrefixedStringLengthAt(8); + case LF_VFUNCTAB: return 8; + case LF_FRIENDCLS: return 8; + case LF_ONEMETHOD: { + int baseLen = 8 + (isOneMethodIntroducingVirtual() ? 4 : 0); + return baseLen + lengthPrefixedStringLengthAt(baseLen); + } + case LF_VFUNCOFF: return 12; + case LF_NESTTYPEEX: return 8 + lengthPrefixedStringLengthAt(8); + case LF_MEMBERMODIFY: return 8 + lengthPrefixedStringLengthAt(8); + + // Should not encounter numeric leaves with this routine + case LF_CHAR: + case LF_SHORT: + case LF_USHORT: + case LF_LONG: + case LF_ULONG: + case LF_REAL32: + case LF_REAL64: + case LF_REAL80: + case LF_REAL128: + case LF_QUADWORD: + case LF_UQUADWORD: + case LF_REAL48: + case LF_COMPLEX32: + case LF_COMPLEX64: + case LF_COMPLEX80: + case LF_COMPLEX128: + case LF_VARSTRING: throw new RuntimeException("Unexpected numeric leaf " + typeStringLeaf + + "in type string"); + default: + throw new COFFException("Unrecognized leaf " + typeStringLeaf + " in type string at offset " + + typeStringOffset); + } + } + + private boolean isIntroducingVirtual(int mprop) { + int masked = mprop & MEMATTR_MPROP_MASK; + return ((masked == MEMATTR_MPROP_INTRODUCING_VIRTUAL) || + (masked == MEMATTR_MPROP_PURE_INTRODUCING_VIRTUAL)); + } + + private int numericLeafLengthAt(int offset) { + return DebugVC50Impl.this.numericLeafLengthAt(typeStringOffset + offset); + } + + private int readIntNumericLeafAt(int offset) { + return DebugVC50Impl.this.readIntNumericLeafAt(typeStringOffset + offset); + } + + private int lengthPrefixedStringLengthAt(int offset) { + return DebugVC50Impl.this.lengthPrefixedStringLengthAt(typeStringOffset + offset); + } + + private String readLengthPrefixedStringAt(int offset) { + return DebugVC50Impl.this.readLengthPrefixedStringAt(typeStringOffset + offset); + } + } + + private int numericLeafLengthAt(int absoluteOffset) throws DebugVC50WrongNumericTypeException { + seek(absoluteOffset); + int leaf = readShort() & 0xFFFF; + if (leaf < 0x8000) return 2; + switch (leaf) { + case LF_CHAR: return 3; + case LF_SHORT: + case LF_USHORT: return 4; + case LF_LONG: + case LF_ULONG: return 6; + case LF_REAL32: return 6; + case LF_REAL64: return 10; + case LF_REAL80: return 12; + case LF_REAL128: return 18; + case LF_QUADWORD: + case LF_UQUADWORD: return 18; + case LF_REAL48: return 8; + case LF_COMPLEX32: return 10; + case LF_COMPLEX64: return 18; + case LF_COMPLEX80: return 26; + case LF_COMPLEX128: return 66; + // FIXME: figure out format of variable-length strings + case LF_VARSTRING: return 4 + readIntNumericLeafAt(absoluteOffset + 2); + + default: + throw new DebugVC50WrongNumericTypeException("Illegal numeric leaf index " + leaf + + " at offset " + absoluteOffset); + } + } + + private int readIntNumericLeafAt(int absoluteOffset) throws DebugVC50WrongNumericTypeException { + seek(absoluteOffset); + int leaf = readShort() & 0xFFFF; + if (leaf < 0x8000) return leaf; + switch (leaf) { + case LF_CHAR: return readByte() & 0xFF; + case LF_SHORT: + case LF_USHORT: return readShort() & 0xFFFF; + case LF_LONG: + case LF_ULONG: return readInt(); + + default: + throw new DebugVC50WrongNumericTypeException("Illegal numeric leaf index " + leaf); + } + } + + private long readLongNumericLeafAt(int absoluteOffset) throws DebugVC50WrongNumericTypeException { + seek(absoluteOffset); + int leaf = readShort() & 0xFFFF; + if (leaf < 0x8000) return leaf; + switch (leaf) { + case LF_CHAR: return readByte() & 0xFF; + case LF_SHORT: + case LF_USHORT: return readShort() & 0xFFFF; + case LF_LONG: + case LF_ULONG: return readInt() & 0xFFFFFFFF; + case LF_QUADWORD: + case LF_UQUADWORD: return readLong(); + + default: + throw new DebugVC50WrongNumericTypeException("Illegal numeric leaf index " + leaf); + } + } + + private float readFloatNumericLeafAt(int absoluteOffset) throws DebugVC50WrongNumericTypeException { + seek(absoluteOffset); + int leaf = readShort() & 0xFFFF; + if (leaf != LF_REAL32) { + throw new DebugVC50WrongNumericTypeException("Illegal numeric leaf index " + leaf); + } + return readFloat(); + } + + private double readDoubleNumericLeafAt(int absoluteOffset) throws DebugVC50WrongNumericTypeException { + seek(absoluteOffset); + int leaf = readShort() & 0xFFFF; + if (leaf != LF_REAL64) { + throw new DebugVC50WrongNumericTypeException("Illegal numeric leaf index " + leaf); + } + return readDouble(); + } + + private int lengthPrefixedStringLengthAt(int absoluteOffset) { + // NOTE: the format of length-prefixed strings is not well + // specified. There is a LF_VARSTRING numeric leaf (the + // format of which is also not specified), but it seems that + // most length-prefixed strings are comprised of a single + // byte length followed by that many bytes of data. + seek(absoluteOffset); + int len = readByte() & 0xFF; + return 1 + len; + } + + private String readLengthPrefixedStringAt(int absoluteOffset) { + // NOTE: it isn't clear whether LF_VARSTRING numeric leaves + // ever show up, or in general what happens when the length + // of the string is > 255 (FIXME) + seek(absoluteOffset); + int len = readByte() & 0xFF; + byte[] res = new byte[len]; + int numRead = readBytes(res); + if (numRead != len) { + throw new COFFException("Error reading length prefixed string in symbol at offset " + + absoluteOffset); + } + try { + return new String(res, US_ASCII); + } catch (UnsupportedEncodingException e) { + throw new COFFException(e); + } + } + + private int unbiasTypeIndex(int index) { + return index - 0x1000; + } + + private int biasTypeIndex(int index) { + return index + 0x1000; + } + } // Class DebugVC50Impl + + class SectionHeaderImpl implements SectionHeader { + private String name; + private int virtualSize; + private int virtualAddress; + private int sizeOfRawData; + private int pointerToRawData; + private int pointerToRelocations; + private int pointerToLineNumbers; + private short numberOfRelocations; + private short numberOfLineNumbers; + private int characteristics; + private MemoizedObject[] relocations; + private MemoizedObject[] lineNumbers; + + public SectionHeaderImpl(int offset) throws COFFException { + seek(offset); + + // FIXME: compute name lazily + + // Read name + byte[] tmpName = new byte[8]; + int numRead = readBytes(tmpName); + if (numRead != 8) { + throw new COFFException("Error reading name of section header at offset " + offset); + } + if (tmpName[0] == (byte) '/') { + // Long name; must find real value in string table + int index = 0; + try { + index = Integer.parseInt(new String(tmpName, 1, tmpName.length - 1, US_ASCII)); + } catch (NumberFormatException e) { + throw new COFFException("Error parsing string table index of name of section header " + + "at offset " + offset); + } catch (UnsupportedEncodingException e) { + throw new COFFException(e); + } + // Look up in string table + name = getStringTable().get(index); + } else { + try { + name = new String(tmpName, US_ASCII); + } catch (UnsupportedEncodingException e) { + throw new COFFException(e); + } + } + virtualSize = readInt(); + virtualAddress = readInt(); + sizeOfRawData = readInt(); + pointerToRawData = readInt(); + pointerToRelocations = readInt(); + pointerToLineNumbers = readInt(); + numberOfRelocations = readShort(); + numberOfLineNumbers = readShort(); + characteristics = readInt(); + + // Set up relocations + relocations = new MemoizedObject[numberOfRelocations]; + for (int i = 0; i < numberOfRelocations; i++) { + final int relocOffset = pointerToRelocations + i * RELOCATION_SIZE; + relocations[i] = new MemoizedObject() { + public Object computeValue() { + return new COFFRelocationImpl(relocOffset); + } + }; + } + + // Set up line numbers + lineNumbers = new MemoizedObject[numberOfLineNumbers]; + for (int i = 0; i < numberOfLineNumbers; i++) { + final int lineNoOffset = pointerToLineNumbers + i * LINE_NUMBER_SIZE; + lineNumbers[i] = new MemoizedObject() { + public Object computeValue() { + return new COFFLineNumberImpl(lineNoOffset); + } + }; + } + } + + public String getName() { return name; } + public int getSize() { return virtualSize; } + public int getVirtualAddress() { return virtualAddress; } + public int getSizeOfRawData() { return sizeOfRawData; } + public int getPointerToRawData() { return pointerToRawData; } + public int getPointerToRelocations() { return pointerToRelocations; } + public int getPointerToLineNumbers() { return pointerToLineNumbers; } + public short getNumberOfRelocations() { return numberOfRelocations; } + public short getNumberOfLineNumbers() { return numberOfLineNumbers; } + public int getSectionFlags() { return characteristics; } + public boolean hasSectionFlag(int flag ) { + return ((characteristics & flag) != 0); + } + public COFFRelocation getCOFFRelocation(int index) { + return (COFFRelocation) relocations[index].getValue(); + } + public COFFLineNumber getCOFFLineNumber(int index) { + return (COFFLineNumber) lineNumbers[index]; + } + } + + class COFFSymbolImpl implements COFFSymbol, COFFSymbolConstants { + private int offset; + private String name; + private int value; + private short sectionNumber; + private short type; + private byte storageClass; + private byte numberOfAuxSymbols; + private MemoizedObject auxFunctionDefinitionRecord = new MemoizedObject() { + public Object computeValue() { + return new AuxFunctionDefinitionRecordImpl(offset + SYMBOL_SIZE); + } + }; + private MemoizedObject auxBfEfRecord = new MemoizedObject() { + public Object computeValue() { + return new AuxBfEfRecordImpl(offset + SYMBOL_SIZE); + } + }; + private MemoizedObject auxWeakExternalRecord = new MemoizedObject() { + public Object computeValue() { + return new AuxWeakExternalRecordImpl(offset + SYMBOL_SIZE); + } + }; + private MemoizedObject auxFileRecord = new MemoizedObject() { + public Object computeValue() { + return new AuxFileRecordImpl(offset + SYMBOL_SIZE); + } + }; + private MemoizedObject auxSectionDefinitionsRecord = new MemoizedObject() { + public Object computeValue() { + return new AuxSectionDefinitionsRecordImpl(offset + SYMBOL_SIZE); + } + }; + + public COFFSymbolImpl(int offset) throws COFFException { + this.offset = offset; + seek(offset); + + // Parse name + byte[] tmpName = new byte[8]; + int numRead = readBytes(tmpName); + if (numRead != 8) { + throw new COFFException("Error reading name of symbol at offset " + offset); + } + if ((tmpName[0] == 0) && + (tmpName[1] == 0) && + (tmpName[2] == 0) && + (tmpName[3] == 0)) { + // It's an offset into the string table. + // FIXME: not sure about byte ordering... + int stringOffset = (tmpName[4] << 24 | + tmpName[5] << 16 | + tmpName[6] << 8 | + tmpName[7]); + name = getStringTable().getAtOffset(stringOffset); + } + + value = readInt(); + sectionNumber = readShort(); + type = readShort(); + storageClass = readByte(); + numberOfAuxSymbols = readByte(); + } + + public int getOffset() { return offset; } + public String getName() { return name; } + public int getValue() { return value; } + public short getSectionNumber() { return sectionNumber; } + public short getType() { return type; } + public byte getStorageClass() { return storageClass; } + public byte getNumberOfAuxSymbols() { return numberOfAuxSymbols; } + public boolean isFunctionDefinition() { + return ((getStorageClass() == IMAGE_SYM_CLASS_EXTERNAL) && + ((getType() >>> 8) == IMAGE_SYM_DTYPE_FUNCTION) && + (getSectionNumber() > 0)); + } + public AuxFunctionDefinitionRecord getAuxFunctionDefinitionRecord() { + return (AuxFunctionDefinitionRecord) auxFunctionDefinitionRecord.getValue(); + } + public boolean isBfOrEfSymbol() { + return ((getName().equals(".bf") || getName().equals(".ef")) && + (getStorageClass() == IMAGE_SYM_CLASS_FUNCTION)); + } + public AuxBfEfRecord getAuxBfEfRecord() { + return (AuxBfEfRecord) auxBfEfRecord.getValue(); + } + public boolean isWeakExternal() { + return ((getStorageClass() == IMAGE_SYM_CLASS_EXTERNAL) && + (getSectionNumber() == IMAGE_SYM_UNDEFINED) && + (getValue() == 0)); + } + public AuxWeakExternalRecord getAuxWeakExternalRecord() { + return (AuxWeakExternalRecord) auxWeakExternalRecord.getValue(); + } + public boolean isFile() { + return ((getName().equals(".file")) && + (getStorageClass() == IMAGE_SYM_CLASS_FILE)); + } + public AuxFileRecord getAuxFileRecord() { + return (AuxFileRecord) auxFileRecord.getValue(); + } + public boolean isSectionDefinition() { + // FIXME: not sure how to ensure that symbol name is the + // name of a section. + return ((getName().charAt(0) == '.') && + (getStorageClass() == IMAGE_SYM_CLASS_STATIC)); + } + public AuxSectionDefinitionsRecord getAuxSectionDefinitionsRecord() { + return (AuxSectionDefinitionsRecord) auxSectionDefinitionsRecord.getValue(); + } + } + + class AuxFunctionDefinitionRecordImpl implements AuxFunctionDefinitionRecord { + private int tagIndex; + private int totalSize; + private int pointerToLineNumber; + private int pointerToNextFunction; + + AuxFunctionDefinitionRecordImpl(int offset) { + seek(offset); + tagIndex = readInt(); + totalSize = readInt(); + // NOTE zero-basing of this index + pointerToLineNumber = readInt() - 1; + pointerToNextFunction = readInt(); + } + + public int getTagIndex() { return tagIndex; } + public int getTotalSize() { return totalSize; } + public int getPointerToLineNumber() { return pointerToLineNumber; } + public int getPointerToNextFunction() { return pointerToNextFunction; } + public int getType() { return FUNCTION_DEFINITION; } + } + + class AuxBfEfRecordImpl implements AuxBfEfRecord { + private short lineNumber; + private int pointerToNextFunction; + + AuxBfEfRecordImpl(int offset) { + seek(offset); + readInt(); + lineNumber = readShort(); + readInt(); + readShort(); + pointerToNextFunction = readInt(); + } + + public short getLineNumber() { return lineNumber; } + public int getPointerToNextFunction() { return pointerToNextFunction; } + public int getType() { return BF_EF_RECORD; } + } + + class AuxWeakExternalRecordImpl implements AuxWeakExternalRecord { + private int tagIndex; + private int characteristics; + + AuxWeakExternalRecordImpl(int offset) { + seek(offset); + tagIndex = readInt(); + characteristics = readInt(); + } + + public int getTagIndex() { return tagIndex; } + public int getCharacteristics() { return characteristics; } + public int getType() { return WEAK_EXTERNAL; } + } + + class AuxFileRecordImpl implements AuxFileRecord { + private String name; + + AuxFileRecordImpl(int offset) { + seek(offset); + byte[] tmpName = new byte[18]; + int numRead = readBytes(tmpName); + if (numRead != 18) { + throw new COFFException("Error reading auxiliary file record at offset " + offset); + } + try { + name = new String(tmpName, US_ASCII); + } catch (UnsupportedEncodingException e) { + throw new COFFException(e); + } + } + + public String getName() { return name; } + public int getType() { return FILE; } + } + + class AuxSectionDefinitionsRecordImpl implements AuxSectionDefinitionsRecord { + private int length; + private short numberOfRelocations; + private short numberOfLineNumbers; + private int checkSum; + private short number; + private byte selection; + + AuxSectionDefinitionsRecordImpl(int offset) { + seek(offset); + length = readInt(); + numberOfRelocations = readShort(); + numberOfLineNumbers = readShort(); + checkSum = readInt(); + number = readShort(); + selection = readByte(); + } + + public int getLength() { return length; } + public short getNumberOfRelocations() { return numberOfRelocations; } + public short getNumberOfLineNumbers() { return numberOfLineNumbers; } + public int getCheckSum() { return checkSum; } + public short getNumber() { return number; } + public byte getSelection() { return selection; } + public int getType() { return SECTION_DEFINITION; } + } + + class COFFRelocationImpl implements COFFRelocation { + private int virtualAddress; + private int symbolTableIndex; + private short type; + + COFFRelocationImpl(int offset) { + seek(offset); + virtualAddress = readInt(); + symbolTableIndex = readInt(); + type = readShort(); + } + + public int getVirtualAddress() { return virtualAddress; } + public int getSymbolTableIndex() { return symbolTableIndex; } + public short getType() { return type; } + } + + class COFFLineNumberImpl implements COFFLineNumber { + private int type; + private short lineNumber; + + COFFLineNumberImpl(int offset) { + seek(offset); + type = readInt(); + lineNumber = readShort(); + } + + public int getType() { + return type; + } + + public short getLineNumber() { + return lineNumber; + } + } + + class StringTable { + class COFFString { + String str; + int offset; + + COFFString(String str, int offset) { + this.str = str; this.offset = offset; + } + } + + COFFString[] strings; + + StringTable(int offset) { + if (offset == 0) { + strings = new COFFString[0]; + return; + } + + seek(offset); + int length = readInt(); + byte[] data = new byte[length - 4]; + int numBytesRead = readBytes(data); + if (numBytesRead != data.length) { + throw new COFFException("Error reading string table (read " + + numBytesRead + " bytes, expected to read " + data.length + ")"); + } + int numStrings = 0; + int ptr = 0; + for (ptr = 0; ptr < data.length; ptr++) { + if (data[ptr] == 0) { + numStrings++; + } + } + strings = new COFFString[numStrings]; + int lastPtr = 0; + ptr = 0; + for (int i = 0; i < numStrings; i++) { + while (data[ptr] != 0) { + ptr++; + } + try { + strings[i] = new COFFString(new String(data, lastPtr, ptr - lastPtr, US_ASCII), + offset + ptr + 4); + } catch (UnsupportedEncodingException e) { + throw new COFFException(e); + } + ptr++; + lastPtr = ptr; + } + } + + int getNum() { + return strings.length; + } + + String get(int i) { + return strings[i].str; + } + + /** This version takes an absolute offset in the file */ + String getAtOffset(int offset) { + int i = Arrays.binarySearch(strings, new COFFString(null, offset), + new Comparator() { + public int compare(Object o1, Object o2) { + COFFString s1 = (COFFString) o1; + COFFString s2 = (COFFString) o2; + if (s1.offset == s2.offset) { + return 0; + } else if (s1.offset < s2.offset) { + return -1; + } else { + return 1; + } + } + }); + if (i < 0) { + throw new COFFException("No string found at file offset " + offset); + } + return strings[i].str; + } + } + } + + void initialize() throws COFFException { + // Figure out whether this file is an object file or an image + // (either executable or DLL). + seek(0x3c); // Error here probably indicates file format error + try { + int peOffset = readInt(); + seek(peOffset); + if ((readByte() == (byte) 'P') && + (readByte() == (byte) 'E') && + (readByte() == (byte) 0) && + (readByte() == (byte) 0)) { + isImage = true; + imageHeaderOffset = getFilePointer(); + } + } + catch (COFFException e) { + // Expect failures here if not image file. + } + } + + byte readByteAt(long offset) throws COFFException { + seek(offset); + return readByte(); + } + + byte readByte() throws COFFException { + try { + return file.readByte(); + } catch (IOException e) { + throw new COFFException(e.toString() + " at offset 0x" + + Long.toHexString(filePos), e); + } + } + + int readBytesAt(long offset, byte[] b) throws COFFException { + seek(offset); + return readBytes(b); + } + + int readBytes(byte[] b) throws COFFException { + try { + return file.read(b); + } catch (IOException e) { + throw new COFFException(e.toString() + " at offset 0x" + + Long.toHexString(filePos), e); + } + } + + /** NOTE: reads little-endian short */ + short readShortAt(long offset) throws COFFException { + seek(offset); + return readShort(); + } + + /** NOTE: reads little-endian short */ + short readShort() throws COFFException { + try { + return byteSwap(file.readShort()); + } catch (IOException e) { + throw new COFFException(e.toString() + " at offset 0x" + + Long.toHexString(filePos), e); + } + } + + /** NOTE: reads little-endian int */ + int readIntAt(long offset) throws COFFException { + seek(offset); + return readInt(); + } + + /** NOTE: reads little-endian int */ + int readInt() throws COFFException { + try { + return byteSwap(file.readInt()); + } catch (IOException e) { + throw new COFFException(e.toString() + " at offset 0x" + + Long.toHexString(filePos), e); + } + } + + /** NOTE: reads little-endian long */ + long readLongAt(long offset) throws COFFException { + seek(offset); + return readLong(); + } + + /** NOTE: reads little-endian long */ + long readLong() throws COFFException { + try { + return byteSwap(file.readLong()); + } catch (IOException e) { + throw new COFFException(e.toString() + " at offset 0x" + + Long.toHexString(filePos), e); + } + } + + /** NOTE: reads little-endian float */ + float readFloat() throws COFFException { + int i = readInt(); + return Float.intBitsToFloat(i); + } + + /** NOTE: reads little-endian double */ + double readDouble() throws COFFException { + long l = readLong(); + return Double.longBitsToDouble(l); + } + + String readCString() throws COFFException { + List data = new ArrayList(); + byte b = 0; + while ((b = readByte()) != 0) { + data.add(new Byte(b)); + } + byte[] bytes = new byte[data.size()]; + for (int i = 0; i < data.size(); i++) { + bytes[i] = ((Byte) data.get(i)).byteValue(); + } + try { + return new String(bytes, US_ASCII); + } catch (UnsupportedEncodingException e) { + throw new COFFException(e); + } + } + + void seek(long offset) throws COFFException { + try { + filePos = offset; + file.seek(offset); + } catch (IOException e) { + throw new COFFException(e.toString() + " at offset 0x" + + Long.toHexString(offset), e); + } + } + + long getFilePointer() throws COFFException { + try { + return file.getFilePointer(); + } catch (IOException e) { + throw new COFFException(e); + } + } + + short byteSwap(short arg) { + return (short) ((arg << 8) | ((arg >>> 8) & 0xFF)); + } + + int byteSwap(int arg) { + return (((int) byteSwap((short) arg)) << 16) | (((int) (byteSwap((short) (arg >>> 16)))) & 0xFFFF); + } + + long byteSwap(long arg) { + return ((((long) byteSwap((int) arg)) << 32) | (((long) byteSwap((int) (arg >>> 32))) & 0xFFFFFFFF)); + } + + public void close() throws COFFException { + try { + file.close(); + } catch (IOException e) { + throw new COFFException(e); + } + } + } +}