| /* |
| * (c) Copyright QNX Software Systems Ltd. 2002. |
| * All Rights Reserved. |
| */ |
| |
| package org.eclipse.cdt.utils.coff; |
| |
| import java.io.IOException; |
| import java.io.RandomAccessFile; |
| |
| import org.eclipse.cdt.utils.coff.Coff.FileHeader; |
| import org.eclipse.cdt.utils.coff.Coff.OptionalHeader; |
| import org.eclipse.cdt.utils.coff.Coff.SectionHeader; |
| import org.eclipse.cdt.utils.coff.Coff.Symbol; |
| import org.eclipse.cdt.utils.coff.Exe.ExeHeader; |
| |
| /** |
| * The PE file header consists of an MS-DOS stub, the PE signalture, the COFF file Header |
| * and an Optional Header. |
| * <pre> |
| * +-------------------+ |
| * | DOS-stub | |
| * +-------------------+ |
| * | file-header | |
| * +-------------------+ |
| * | optional header | |
| * |- - - - - - - - - -| |
| * | | |
| * | data directories | |
| * | | |
| * +-------------------+ |
| * | | |
| * | section headers | |
| * | | |
| * +-------------------+ |
| * | | |
| * | section 1 | |
| * | | |
| * +-------------------+ |
| * | | |
| * | section 2 | |
| * | | |
| * +-------------------+ |
| * | | |
| * | ... | |
| * | | |
| * +-------------------+ |
| * | | |
| * | section n | |
| * | | |
| * +-------------------+ |
| * </pre> |
| */ |
| public class PE { |
| |
| public static final String NL = System.getProperty("line.separator", "\n"); |
| RandomAccessFile rfile; |
| String filename; |
| ExeHeader exeHeader; |
| DOSHeader dosHeader; |
| FileHeader fileHeader; |
| OptionalHeader optionalHeader; |
| NTOptionalHeader ntHeader; |
| ImageDataDirectory[] dataDirectories; |
| SectionHeader[] scnhdrs; |
| Symbol[] symbolTable; |
| byte[] stringTable; |
| |
| public class Attribute { |
| public static final int PE_TYPE_EXE = 1; |
| public static final int PE_TYPE_SHLIB = 2; |
| public static final int PE_TYPE_OBJ = 3; |
| public static final int PE_TYPE_CORE = 4; |
| |
| String cpu; |
| int type; |
| int word; |
| boolean bDebug; |
| boolean isle; |
| |
| public String getCPU() { |
| return cpu; |
| } |
| |
| public int getType() { |
| return type; |
| } |
| |
| public boolean hasDebug() { |
| return bDebug; |
| } |
| |
| public boolean isLittleEndian() { |
| return isle; |
| } |
| |
| public int getWord() { |
| return word; |
| } |
| } |
| |
| /** |
| */ |
| public static class DOSHeader { |
| final static int DOSHDRSZ = 100; |
| byte[] e_res = new byte[8]; /* Reserved words, all 0x0. */ |
| byte[] e_oemid = new byte[2]; /* OEM identifier (for e_oeminfo), 0x0. */ |
| byte[] e_oeminfo = new byte[2]; /* OEM information; e_oemid specific, 0x0. */ |
| byte[] e_res2 = new byte[20]; /* Reserved words, all 0x0. */ |
| int e_lfanew; /* 4 byte File address of new exe header, offset 60(0x3c), 0x80. */ |
| byte[] dos_message = new byte[64]; /* Other stuff, always follow DOS header. */ |
| |
| public DOSHeader(RandomAccessFile file) throws IOException { |
| this(file, file.getFilePointer()); |
| } |
| |
| public DOSHeader(RandomAccessFile file, long offset) throws IOException { |
| file.seek(offset); |
| byte[] hdr = new byte[DOSHDRSZ]; |
| file.readFully(hdr); |
| ReadMemoryAccess memory = new ReadMemoryAccess(hdr, true); |
| memory.getBytes(e_res); |
| memory.getBytes(e_oemid); |
| memory.getBytes(e_oeminfo); |
| memory.getBytes(e_res2); |
| e_lfanew = memory.getInt(); |
| memory.getBytes(dos_message); |
| } |
| |
| public String toString() { |
| StringBuffer buffer = new StringBuffer(); |
| buffer.append("DOS STUB VALUES").append(NL); |
| buffer.append("e_lfanew = ").append(e_lfanew).append(NL); |
| buffer.append(new String(dos_message)).append(NL); |
| return buffer.toString(); |
| } |
| } |
| |
| public static class NTOptionalHeader { |
| |
| public final static int NTHDRSZ = 68; |
| public int ImageBase; // 4 bytes. |
| public int SectionAlignment; // 4 bytes. |
| public int FileAlignment; // 4 bytes. |
| public short MajorOperatingSystemVersion; // 2 bytes. |
| public short MinorOperatingSystemVersion; // 2 bytes. |
| public short MajorImageVersion; // 2 bytes. |
| public short MinorImageVersion; // 2 bytes. |
| public short MajorSubsystemVersion; // 2 bytes. |
| public short MinorSubsystemVersion; // 2 bytes. |
| public byte[] Reserved = new byte[4]; // 4 bytes. |
| public int SizeOfImage; // 4 bytes. |
| public int SizeOfHeaders; // 4 bytes. |
| public int CheckSum; // 4 bytes. |
| public short Subsystem; // 2 bytes. |
| public short DLLCharacteristics; // 2 bytes. |
| public int SizeOfStackReserve; // 4 bytes. |
| public int SizeOfStackCommit; // 4 bytes. |
| public int SizeOfHeapReserve; // 4 bytes. |
| public int SizeOfHeapCommit; // 4 bytes. |
| public int LoaderFlags; // 4 bytes. |
| public int NumberOfRvaAndSizes; // 4 bytes. |
| |
| public NTOptionalHeader(RandomAccessFile file) throws IOException { |
| this(file, file.getFilePointer()); |
| } |
| |
| public NTOptionalHeader(RandomAccessFile file, long offset) throws IOException { |
| file.seek(offset); |
| byte[] hdr = new byte[NTHDRSZ]; |
| file.readFully(hdr); |
| ReadMemoryAccess memory = new ReadMemoryAccess(hdr, true); |
| ImageBase = memory.getInt(); |
| SectionAlignment = memory.getInt(); |
| FileAlignment = memory.getInt(); |
| MajorOperatingSystemVersion = memory.getShort(); |
| MinorOperatingSystemVersion = memory.getShort(); |
| MajorImageVersion = memory.getShort(); |
| MinorImageVersion = memory.getShort(); |
| MajorSubsystemVersion = memory.getShort(); |
| MinorSubsystemVersion = memory.getShort(); |
| memory.getBytes(Reserved); |
| SizeOfImage = memory.getInt(); |
| SizeOfHeaders = memory.getInt(); |
| CheckSum = memory.getInt(); |
| Subsystem = memory.getShort(); |
| DLLCharacteristics = memory.getShort(); |
| SizeOfStackReserve = memory.getInt(); |
| SizeOfStackCommit = memory.getInt(); |
| SizeOfHeapReserve = memory.getInt(); |
| SizeOfHeapCommit = memory.getInt(); |
| LoaderFlags = memory.getInt(); |
| NumberOfRvaAndSizes = memory.getInt(); |
| } |
| |
| public String toString() { |
| StringBuffer buffer = new StringBuffer(); |
| buffer.append("NT OPTIONAL HEADER VALUES").append(NL); |
| buffer.append("ImageBase = ").append(ImageBase).append(NL); |
| buffer.append("SexctionAlignement = ").append(SectionAlignment).append(NL); |
| buffer.append("FileAlignment = ").append(FileAlignment).append(NL); |
| buffer.append("MajorOSVersion = ").append(MajorOperatingSystemVersion).append(NL); |
| buffer.append("MinorOSVersion = ").append(MinorOperatingSystemVersion).append(NL); |
| buffer.append("MajorImageVersion = ").append(MajorImageVersion).append(NL); |
| buffer.append("MinorImageVersion = ").append(MinorImageVersion).append(NL); |
| buffer.append("MajorSubVersion = ").append(MajorSubsystemVersion).append(NL); |
| buffer.append("MinorSubVersion = ").append(MinorSubsystemVersion).append(NL); |
| buffer.append("Reserved = ").append(Reserved).append(NL); |
| buffer.append("SizeOfImage = ").append(SizeOfImage).append(NL); |
| buffer.append("SizeOfHeaders = ").append(SizeOfHeaders).append(NL); |
| buffer.append("CheckSum = ").append(CheckSum).append(NL); |
| buffer.append("Subsystem = ").append(Subsystem).append(NL); |
| buffer.append("DLL = ").append(DLLCharacteristics).append(NL); |
| buffer.append("StackReserve = ").append(SizeOfStackReserve).append(NL); |
| buffer.append("StackCommit = ").append(SizeOfStackCommit).append(NL); |
| buffer.append("HeapReserve = ").append(SizeOfHeapReserve).append(NL); |
| buffer.append("HeapCommit = ").append(SizeOfHeapCommit).append(NL); |
| buffer.append("LoaderFlags = ").append(LoaderFlags).append(NL);; |
| buffer.append("#Rva size = ").append(NumberOfRvaAndSizes).append(NL); |
| return buffer.toString(); |
| } |
| } |
| |
| public class ImageDataDirectory { |
| public int rva; |
| public int size; |
| |
| public ImageDataDirectory(int r, int s) { |
| rva = r; |
| size = s; |
| } |
| |
| public String toString() { |
| StringBuffer buffer = new StringBuffer(); |
| buffer.append("rva = ").append(rva).append(" "); |
| buffer.append("size = ").append(size).append(NL); |
| return buffer.toString(); |
| } |
| } |
| |
| public class ImportDirectoryEntry { |
| public final static int ENTRYSZ = 20; |
| public int rva; |
| public int timestamp; |
| public int forwarder; |
| public int name; |
| public int thunk; |
| |
| public ImportDirectoryEntry(RandomAccessFile file) throws IOException { |
| this(file, file.getFilePointer()); |
| } |
| |
| public ImportDirectoryEntry(RandomAccessFile file, long offset) throws IOException { |
| file.seek(offset); |
| byte[] bytes = new byte[ENTRYSZ]; |
| file.readFully(bytes); |
| ReadMemoryAccess memory = new ReadMemoryAccess(bytes, true); |
| rva = memory.getInt(); |
| timestamp = memory.getInt(); |
| forwarder = memory.getInt(); |
| name = memory.getInt(); |
| thunk = memory.getInt(); |
| } |
| |
| public String toString() { |
| StringBuffer buffer = new StringBuffer(); |
| buffer.append("rva = ").append(rva); |
| buffer.append(" timestamp = ").append(timestamp); |
| buffer.append(" forwarder = ").append(forwarder); |
| buffer.append(" name = ").append(name); |
| buffer.append(" thunk = ").append(thunk).append(NL); |
| return buffer.toString(); |
| } |
| } |
| |
| public PE (String filename) throws IOException { |
| this(filename, 0); |
| } |
| |
| public PE(String filename, long pos) throws IOException { |
| this(filename, pos, true); |
| } |
| |
| public PE (String filename, long pos, boolean filter) throws IOException { |
| try { |
| rfile = new RandomAccessFile(filename, "r"); |
| this.filename = filename; |
| rfile.seek(pos); |
| |
| // Object files do not have exe/dos header. |
| try { |
| exeHeader = new ExeHeader(rfile); |
| dosHeader = new DOSHeader(rfile); |
| // Jump the Coff header, and Check the sig. |
| rfile.seek(dosHeader.e_lfanew); |
| byte[] sig = new byte[4]; |
| rfile.readFully(sig); |
| if (!((sig[0] == 'P') && (sig[1] == 'E') |
| && (sig[2] == '\0') && (sig[3] == '\0'))) { |
| throw new IOException("Not a PE format"); |
| } |
| } catch (IOException e) { |
| rfile.seek(pos); |
| } |
| |
| fileHeader = new Coff.FileHeader(rfile, rfile.getFilePointer()); |
| |
| // Check if this a valid machine. |
| switch (fileHeader.f_magic) { |
| case PEConstants.IMAGE_FILE_MACHINE_ALPHA: |
| case PEConstants.IMAGE_FILE_MACHINE_ARM: |
| case PEConstants.IMAGE_FILE_MACHINE_ALPHA64: |
| case PEConstants.IMAGE_FILE_MACHINE_I386: |
| case PEConstants.IMAGE_FILE_MACHINE_IA64: |
| case PEConstants.IMAGE_FILE_MACHINE_M68K: |
| case PEConstants.IMAGE_FILE_MACHINE_MIPS16: |
| case PEConstants.IMAGE_FILE_MACHINE_MIPSFPU: |
| case PEConstants.IMAGE_FILE_MACHINE_MIPSFPU16: |
| case PEConstants.IMAGE_FILE_MACHINE_POWERPC: |
| case PEConstants.IMAGE_FILE_MACHINE_R3000: |
| case PEConstants.IMAGE_FILE_MACHINE_R4000: |
| case PEConstants.IMAGE_FILE_MACHINE_R10000: |
| case PEConstants.IMAGE_FILE_MACHINE_SH3: |
| case PEConstants.IMAGE_FILE_MACHINE_SH4: |
| case PEConstants.IMAGE_FILE_MACHINE_THUMB: |
| // Ok; |
| break; |
| |
| default: |
| throw new IOException("Unknow machine/format"); |
| } |
| |
| if (fileHeader.f_opthdr > 0) { |
| optionalHeader = new Coff.OptionalHeader(rfile, rfile.getFilePointer()); |
| ntHeader = new NTOptionalHeader(rfile, rfile.getFilePointer()); |
| } |
| } finally { |
| if (rfile != null) { |
| rfile.close(); |
| rfile = null; |
| } |
| } |
| } |
| |
| public Attribute getAttribute() { |
| Attribute attrib = new Attribute(); |
| FileHeader filhdr = getFileHeader(); |
| |
| // Machine type. |
| switch (filhdr.f_magic) { |
| case PEConstants.IMAGE_FILE_MACHINE_UNKNOWN: |
| attrib.cpu = "none"; |
| break; |
| case PEConstants.IMAGE_FILE_MACHINE_ALPHA: |
| attrib.cpu = "alpha"; |
| break; |
| case PEConstants.IMAGE_FILE_MACHINE_ARM: |
| attrib.cpu = "arm"; |
| break; |
| case PEConstants.IMAGE_FILE_MACHINE_ALPHA64: |
| attrib.cpu = "arm64"; |
| break; |
| case PEConstants.IMAGE_FILE_MACHINE_I386: |
| attrib.cpu = "x86"; |
| break; |
| case PEConstants.IMAGE_FILE_MACHINE_IA64: |
| attrib.cpu = "ia64"; |
| break; |
| case PEConstants.IMAGE_FILE_MACHINE_M68K: |
| attrib.cpu = "m68k"; |
| break; |
| case PEConstants.IMAGE_FILE_MACHINE_MIPS16: |
| attrib.cpu = "mips16"; |
| break; |
| case PEConstants.IMAGE_FILE_MACHINE_MIPSFPU: |
| attrib.cpu = "mipsfpu"; |
| break; |
| case PEConstants.IMAGE_FILE_MACHINE_MIPSFPU16: |
| attrib.cpu = "mipsfpu16"; |
| break; |
| case PEConstants.IMAGE_FILE_MACHINE_POWERPC: |
| attrib.cpu = "powerpc"; |
| break; |
| case PEConstants.IMAGE_FILE_MACHINE_R3000: |
| attrib.cpu = "r3000"; |
| break; |
| case PEConstants.IMAGE_FILE_MACHINE_R4000: |
| attrib.cpu = "r4000"; |
| break; |
| case PEConstants.IMAGE_FILE_MACHINE_R10000: |
| attrib.cpu = "r10000"; |
| break; |
| case PEConstants.IMAGE_FILE_MACHINE_SH3: |
| attrib.cpu = "sh3"; |
| break; |
| case PEConstants.IMAGE_FILE_MACHINE_SH4: |
| attrib.cpu = "sh4"; |
| break; |
| case PEConstants.IMAGE_FILE_MACHINE_THUMB: |
| attrib.cpu = "thumb"; |
| break; |
| } |
| |
| /* PE characteristics, FileHeader.f_flags. */ |
| if ((filhdr.f_flags & PEConstants.IMAGE_FILE_DLL) != 0) { |
| attrib.type = Attribute.PE_TYPE_SHLIB; |
| } else if ((filhdr.f_flags & PEConstants.IMAGE_FILE_EXECUTABLE_IMAGE) != 0) { |
| attrib.type = Attribute.PE_TYPE_EXE; |
| } else { |
| attrib.type = Attribute.PE_TYPE_OBJ; |
| } |
| |
| // For PE always assume little endian unless otherwise. |
| attrib.isle = true; |
| // Little Endian. |
| if ((filhdr.f_flags & PEConstants.IMAGE_FILE_BYTES_REVERSED_LO) != 0) { |
| attrib.isle = true; |
| } |
| // Big Endian. |
| if ((filhdr.f_flags & PEConstants.IMAGE_FILE_BYTES_REVERSED_HI) != 0) { |
| attrib.isle = false; |
| } |
| |
| // No debug information. |
| if ((filhdr.f_flags & PEConstants.IMAGE_FILE_DEBUG_STRIPPED) != 0) { |
| attrib.bDebug = false; |
| } else { |
| attrib.bDebug = true; |
| } |
| |
| // sizeof word. |
| if ((filhdr.f_flags & PEConstants.IMAGE_FILE_16BIT_MACHINE) != 0) { |
| attrib.word = 16; |
| } |
| if ((filhdr.f_flags & PEConstants.IMAGE_FILE_32BIT_MACHINE) != 0) { |
| attrib.word = 32; |
| } |
| return attrib; |
| } |
| |
| public static boolean isExeHeader(byte[] e_signature) { |
| if (e_signature.length < 2 || e_signature[0] != 'M' || e_signature[1] != 'Z') |
| return false; |
| return true; |
| } |
| |
| public static Attribute getAttributes(String file) throws IOException { |
| PE pe = new PE(file); |
| Attribute attrib = pe.getAttribute(); |
| pe.dispose(); |
| return attrib; |
| } |
| |
| |
| public void dispose() throws IOException { |
| if (rfile != null) { |
| rfile.close(); |
| rfile = null; |
| } |
| } |
| |
| protected void finalize() throws Throwable { |
| try { |
| dispose(); |
| } finally { |
| super.finalize(); |
| } |
| } |
| |
| public ExeHeader getExeHeader() { |
| return exeHeader; |
| } |
| |
| public DOSHeader getDOSHeader() { |
| return dosHeader; |
| } |
| |
| public FileHeader getFileHeader() { |
| return fileHeader; |
| } |
| |
| public OptionalHeader getOptionalHeader() { |
| return optionalHeader; |
| } |
| |
| public NTOptionalHeader getNTOptionalHeader() { |
| return ntHeader; |
| } |
| |
| public ImageDataDirectory[] getImageDataDirectories() throws IOException { |
| if (dataDirectories == null) { |
| RandomAccessFile accessFile = getRandomAccessFile(); |
| long offset = 0; |
| if (dosHeader != null) { |
| offset = dosHeader.e_lfanew + 4/*NT SIG*/; |
| } |
| offset += FileHeader.FILHSZ + OptionalHeader.AOUTHDRSZ + NTOptionalHeader.NTHDRSZ; |
| accessFile.seek(offset); |
| dataDirectories = new ImageDataDirectory[PEConstants.IMAGE_NUMBEROF_DIRECTORY_ENTRIES]; |
| byte[] data = new byte[dataDirectories.length * (4 + 4)]; |
| accessFile.readFully(data); |
| ReadMemoryAccess memory = new ReadMemoryAccess(data, true); |
| for (int i = 0; i < dataDirectories.length; i++) { |
| int rva = memory.getInt(); |
| int size = memory.getInt(); |
| dataDirectories[i] = new ImageDataDirectory(rva, size); |
| } |
| } |
| return dataDirectories; |
| } |
| public SectionHeader[] getSectionHeaders() throws IOException { |
| if (scnhdrs == null) { |
| RandomAccessFile accessFile = getRandomAccessFile(); |
| scnhdrs = new SectionHeader[fileHeader.f_nscns]; |
| long offset = 0; |
| if (dosHeader != null) { |
| offset = dosHeader.e_lfanew + 4 /* NT SIG */; |
| } |
| offset += FileHeader.FILHSZ + fileHeader.f_opthdr; |
| for (int i = 0; i < scnhdrs.length; i++, offset += SectionHeader.SCNHSZ) { |
| scnhdrs[i] = new SectionHeader(accessFile, offset); |
| } |
| } |
| return scnhdrs; |
| } |
| |
| public Symbol[] getSymbols() throws IOException { |
| if (symbolTable == null) { |
| RandomAccessFile accessFile = getRandomAccessFile(); |
| long offset = fileHeader.f_symptr; |
| symbolTable = new Symbol[fileHeader.f_nsyms]; |
| for (int i = 0; i < symbolTable.length; i++, offset += Symbol.SYMSZ) { |
| symbolTable[i] = new Symbol(accessFile, offset); |
| NTOptionalHeader ntHeader = getNTOptionalHeader(); |
| // FIXME: What is this again ? |
| if (ntHeader != null) |
| symbolTable[i].n_value += ntHeader.ImageBase + ntHeader.FileAlignment; |
| } |
| } |
| return symbolTable; |
| } |
| |
| public byte[] getStringTable() throws IOException { |
| if (stringTable == null) { |
| if (fileHeader.f_nsyms > 0) { |
| RandomAccessFile accessFile = getRandomAccessFile(); |
| long symbolsize = Symbol.SYMSZ * fileHeader.f_nsyms; |
| long offset = fileHeader.f_symptr + symbolsize; |
| accessFile.seek(offset); |
| byte[] bytes = new byte[4]; |
| accessFile.readFully(bytes); |
| int str_len = ReadMemoryAccess.getIntLE(bytes); |
| if (str_len > 4) { |
| str_len -= 4; |
| stringTable = new byte[str_len]; |
| accessFile.seek(offset + 4); |
| accessFile.readFully(stringTable); |
| } else { |
| stringTable = new byte[0]; |
| } |
| } else { |
| stringTable = new byte[0]; |
| } |
| } |
| return stringTable; |
| } |
| |
| public String toString() { |
| StringBuffer buffer = new StringBuffer(); |
| if (exeHeader != null) { |
| buffer.append(exeHeader); |
| } |
| if (dosHeader != null) { |
| buffer.append(dosHeader); |
| } |
| buffer.append(fileHeader); |
| if (optionalHeader != null) { |
| buffer.append(optionalHeader); |
| } |
| if (ntHeader != null) { |
| buffer.append(ntHeader); |
| } |
| try { |
| ImageDataDirectory[] dirs = getImageDataDirectories(); |
| for (int i = 0; i < dirs.length; i++) { |
| buffer.append("Entry ").append(i); |
| buffer.append(" ").append(dirs[i]); |
| } |
| } catch (IOException e) { |
| e.printStackTrace(); |
| } |
| |
| try { |
| SectionHeader[] sections = getSectionHeaders(); |
| for (int i = 0; i < sections.length; i++) { |
| buffer.append(sections[i]); |
| } |
| } catch (IOException e) { |
| e.printStackTrace(); |
| } |
| |
| try { |
| Symbol[] symbols = getSymbols(); |
| for (int i = 0; i < symbols.length; i++) { |
| buffer.append(symbols[i]); |
| } |
| } catch (IOException e) { |
| e.printStackTrace(); |
| } |
| |
| try { |
| byte[] bytes = getStringTable(); |
| String[] strings = Coff.getStringTable(bytes); |
| for (int i = 0; i < strings.length; i++) { |
| buffer.append(strings[i]); |
| } |
| } catch (IOException e) { |
| e.printStackTrace(); |
| } |
| return buffer.toString(); |
| } |
| |
| RandomAccessFile getRandomAccessFile () throws IOException { |
| if (rfile == null) { |
| rfile = new RandomAccessFile(filename, "r"); |
| } |
| return rfile; |
| } |
| public static void main(String[] args) { |
| try { |
| PE pe = new PE(args[0]); |
| System.out.println(pe); |
| } catch (IOException e) { |
| e.printStackTrace(); |
| } |
| } |
| } |