| /* |
| * 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); |
| } |
| } |
| } |
| } |