/* * Copyright (c) 2000, 2010, Oracle and/or its affiliates. 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.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; // Init stringTable at decl time since other fields init'ed in the // constructor need the String Table. private MemoizedObject stringTable = new MemoizedObject() { public Object computeValue() { // the String Table follows the Symbol Table int ptr = getPointerToSymbolTable(); if (ptr == 0) { // no Symbol Table so no String Table 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(); // String Table can be accessed at this point because // pointerToSymbolTable and numberOfSymbols fields are set. 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; // We use an offset of 2 because OptionalHeaderStandardFieldsImpl doesn't // include the 'magic' field. 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; // only set in PE32 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) { // only available in PE32 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; } // ExportDirectoryTableImpl needs both the RVA and the // RVA converted to a file offset. return new ExportDirectoryTableImpl(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 exportDataDirRVA; 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 exportDataDirRVA, int size) { this.exportDataDirRVA = exportDataDirRVA; offset = rvaToFileOffset(exportDataDirRVA); 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() { // number of ordinals is same as the number of name pointers 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())); // The Export Address Table values are a union of two // possible values: // Export RVA - The address of the exported symbol when // loaded into memory, relative to the image base. // This value doesn't get converted into a file offset. // Forwarder RVA - The pointer to a null-terminated ASCII // string in the export section. This value gets // converted into a file offset because we have to // fetch the string. for (int i = 0; i < addresses.length; i++) { addresses[i] = readInt(); } 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 ((exportDataDirRVA <= addr) && (addr < (exportDataDirRVA + size))); } public String getExportAddressForwarder(short ordinal) { seek(rvaToFileOffset(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 // FIXME: this index value is assumed to be in the valid range name = getStringTable().get(index); } else { try { int length = 0; // find last non-NULL for (; length < tmpName.length && tmpName[length] != '\0';) { length++; } // don't include NULL chars in returned name String name = new String(tmpName, 0, length, 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]); // FIXME: stringOffset is assumed to be in the valid range 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) { // no String Table strings = new COFFString[0]; return; } seek(offset); int length = readInt(); // length includes itself 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); } } } }