| /* |
| * (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 java.text.DateFormat; |
| import java.util.ArrayList; |
| import java.util.Date; |
| import java.util.List; |
| |
| public class Coff { |
| |
| public static final String NL = System.getProperty("line.separator", "\n"); |
| FileHeader filehdr; |
| OptionalHeader opthdr; |
| RandomAccessFile rfile; |
| long startingOffset; |
| byte[] string_table; |
| SectionHeader[] scnhdrs; |
| Symbol[] symbols; |
| |
| public static class FileHeader { |
| public final static int FILHSZ = 20; |
| |
| public final static int F_RELFLG = 0x0001; // relocation info stripped from file |
| public final static int F_EXEC = 0x0002; // file is executable |
| // (no unresolved external references) |
| public final static int F_LNNO = 0x0004; // line numbers stripped from file |
| public final static int F_LSYMS = 0x0008; // local symbols stripped from file |
| public final static int F_AR16WR = 0x0080; // file is 16-bit little-endian |
| public final static int F_AR32WR = 0x0100; // file is 32-bit little-endian |
| public final static int F_AR32W = 0x0200; // file is 32-bit big-endian |
| public final static int F_DYNLOAD = 0x1000;// rs/6000 aix: dynamically |
| // loadable w/imports & exports |
| public final static int F_SHROBJ = 0x2000; // rs/6000 aix: file is a shared object |
| public final static int F_DLL = 0x2000; // PE format DLL. |
| |
| public int f_magic; /* 00-01 2 bytes: magic number */ |
| public int f_nscns; /* 02-03 2 bytes: number of sections: 2 bytes */ |
| public int f_timdat; /* 04-07 4 bytes: time & date stamp */ |
| public int f_symptr; /* 08-11 4 bytes: file pointer to symtab */ |
| public int f_nsyms; /* 12-15 4 bytes: number of symtab entries */ |
| public int f_opthdr; /* 16-17 2 bytes: sizeof(optional hdr) */ |
| public int f_flags; /* 18-19 2 bytes: flags */ |
| |
| public FileHeader (RandomAccessFile file) throws IOException { |
| this(file, file.getFilePointer()); |
| } |
| |
| public FileHeader (RandomAccessFile file, long offset) throws IOException { |
| file.seek(offset); |
| byte[] hdr = new byte[FILHSZ]; |
| file.readFully(hdr); |
| ReadMemoryAccess memory = new ReadMemoryAccess(hdr, true); |
| f_magic = memory.getUnsignedShort(); |
| f_nscns = memory.getUnsignedShort(); |
| f_timdat = memory.getInt(); |
| f_symptr = memory.getInt(); |
| f_nsyms = memory.getInt(); |
| f_opthdr = memory.getUnsignedShort(); |
| f_flags = memory.getUnsignedShort(); |
| } |
| |
| public boolean isStrip() { |
| return (f_flags & F_RELFLG) == F_RELFLG; |
| } |
| |
| public boolean isExec() { |
| return (f_flags & F_EXEC) == F_EXEC; |
| } |
| |
| public boolean isDebug() { |
| return !((f_flags & F_LNNO) == F_LNNO); |
| } |
| |
| public String toString() { |
| StringBuffer buffer = new StringBuffer(); |
| buffer.append("FILE HEADER VALUES").append(NL); |
| |
| buffer.append("f_magic = ").append(f_magic).append(NL); |
| buffer.append("f_nscns = ").append(f_nscns).append(NL); |
| |
| buffer.append("f_timdat = "); |
| buffer.append(DateFormat.getDateInstance().format(new Date(f_timdat))); |
| buffer.append(NL); |
| |
| buffer.append("f_symptr = ").append(f_symptr).append(NL); |
| buffer.append("f_nsyms = ").append(f_nsyms).append(NL); |
| buffer.append("f_opthdr = ").append(f_opthdr).append(NL); |
| buffer.append("f_flags = ").append(f_flags).append(NL); |
| return buffer.toString(); |
| } |
| } |
| |
| public static class OptionalHeader { |
| public final static int AOUTHDRSZ = 28; |
| |
| public short magic; /* 2 bytes: type of file */ |
| public short vstamp; /* 2 bytes: version stamp */ |
| public int tsize; /* 4 bytes: text size in bytes, padded to FW bdry*/ |
| public int dsize; /* 4 bytes: initialized data " " */ |
| public int bsize; /* 4 bytes: uninitialized data " " */ |
| public int entry; /* 4 bytes: entry pt. */ |
| public int text_start; /* 4 bytes: base of text used for this file */ |
| public int data_start; /* 4 bytes: base of data used for this file */ |
| |
| public OptionalHeader(RandomAccessFile file) throws IOException { |
| this(file, file.getFilePointer() + FileHeader.FILHSZ); |
| } |
| |
| public OptionalHeader(RandomAccessFile file, long offset) throws IOException { |
| file.seek(offset); |
| byte[] hdr = new byte[AOUTHDRSZ]; |
| file.readFully(hdr); |
| ReadMemoryAccess memory = new ReadMemoryAccess(hdr, true); |
| magic = memory.getShort(); |
| vstamp = memory.getShort(); |
| tsize = memory.getInt(); |
| dsize = memory.getInt(); |
| bsize = memory.getInt(); |
| entry = memory.getInt(); |
| text_start = memory.getInt(); |
| data_start = memory.getInt(); |
| } |
| |
| public String toString() { |
| StringBuffer buffer = new StringBuffer(); |
| buffer.append("OPTIONAL HEADER VALUES").append(NL); |
| buffer.append("magic = ").append(magic).append(NL); |
| buffer.append("vstamp = ").append(vstamp).append(NL); |
| buffer.append("tsize = ").append(tsize).append(NL); |
| buffer.append("dsize = ").append(dsize).append(NL); |
| buffer.append("bsize = ").append(bsize).append(NL); |
| buffer.append("entry = ").append(entry).append(NL); |
| buffer.append("text_start = ").append(text_start).append(NL); |
| buffer.append("data_start = ").append(data_start).append(NL); |
| return buffer.toString(); |
| } |
| } |
| |
| public static class SectionHeader { |
| |
| public final static int SCNHSZ = 40; |
| |
| /* names of "special" sections */ |
| public final static String _TEXT = ".text"; |
| public final static String _DATA = ".data"; |
| public final static String _BSS = ".bss"; |
| public final static String _COMMENT = ".comment"; |
| public final static String _LIB = ".lib"; |
| |
| /* s_flags "type". */ |
| public final static int STYP_REG = 0x0000; /* "regular": allocated, relocated, |
| loaded */ |
| public final static int STYP_DSECT = 0x0001; /* "dummy": relocated only */ |
| public final static int STYP_NOLOAD = 0x0002; /* "noload": allocated, relocated, |
| not loaded */ |
| public final static int STYP_GROUP = 0x0004; /* "grouped": formed of input |
| sections */ |
| public final static int STYP_PAD = 0x0008; /* "padding": not allocated, not |
| relocated, loaded */ |
| public final static int STYP_COPY = 0x0010; /* "copy": for decision function |
| used by field update; |
| not allocated, not relocated, |
| loaded; reloc & lineno entries |
| processed normally */ |
| public final static int STYP_TEXT = 0x0020; /* section contains text only. */ |
| public final static int S_SHRSEG = 0x0020; /* In 3b Update files (output of |
| ogen), sections which appear in |
| SHARED segments of the Pfile |
| will have the S_SHRSEG flag set |
| by ogen, to inform dufr that |
| updating 1 copy of the proc. will |
| update all process invocations. */ |
| public final static int STYP_DATA = 0x0040; /* section contains data only */ |
| public final static int STYP_BSS = 0x0080; /* section contains bss only */ |
| public final static int S_NEWFCN = 0x0100; /* In a minimal file or an update |
| file, a new function (as |
| compared with a replaced |
| function) */ |
| public final static int STYP_INFO = 0x0200; /* comment: not allocated not |
| relocated, not loaded */ |
| public final static int STYP_OVER = 0x0400; /* overlay: relocated not allocated |
| or loaded */ |
| public final static int STYP_LIB = 0x0800; /* for .lib: same as INFO */ |
| public final static int STYP_MERGE = 0x2000; /* merge section -- combines with |
| text, data or bss sections only */ |
| public final static int STYP_REVERSE_PAD = 0x4000; /* section will be padded |
| with no-op instructions |
| wherever padding is necessary |
| and there is a word of |
| contiguous bytes beginning on a |
| word boundary. */ |
| |
| public final static int STYP_LIT = 0x8020; /* Literal data (like STYP_TEXT) */ |
| |
| |
| public byte[] s_name= new byte[8]; // 8 bytes: section name |
| public int s_paddr; // 4 bytes: physical address, aliased s_nlib |
| public int s_vaddr; // 4 bytes: virtual address |
| public int s_size; // 4 bytes: section size |
| public int s_scnptr; // 4 bytes: file ptr to raw data for section |
| public int s_relptr; // 4 bytes: file ptr to relocation |
| public int s_lnnoptr; // 4 bytes: file ptr to line numbers |
| public int s_nreloc; // 2 bytes: number of relocation entries |
| public int s_nlnno; // 2 bytes: number of line number entries |
| public int s_flags; // 4 bytes: flags |
| |
| RandomAccessFile sfile; |
| |
| public SectionHeader(RandomAccessFile file, long offset) throws IOException { |
| sfile = file; |
| file.seek(offset); |
| byte[] hdr = new byte[SCNHSZ]; |
| file.readFully(hdr); |
| ReadMemoryAccess memory = new ReadMemoryAccess(hdr, true); |
| memory.getBytes(s_name); |
| s_paddr = memory.getInt(); |
| s_vaddr = memory.getInt(); |
| s_size = memory.getInt(); |
| s_scnptr = memory.getInt(); |
| s_relptr = memory.getInt(); |
| s_lnnoptr = memory.getInt(); |
| s_nreloc = memory.getUnsignedShort(); |
| s_nlnno = memory.getUnsignedShort(); |
| s_flags = memory.getInt(); |
| } |
| |
| public byte[] getRawData() throws IOException { |
| byte[] data = new byte[s_size]; |
| sfile.seek(s_scnptr); |
| sfile.readFully(data); |
| return data; |
| } |
| |
| public Reloc[] getRelocs() throws IOException { |
| Reloc[] relocs = new Reloc[s_nreloc]; |
| sfile.seek(s_relptr); |
| for (int i = 0; i < s_nreloc; i++) { |
| relocs[i] = new Reloc(sfile); |
| } |
| return relocs; |
| } |
| |
| public Lineno[] getLinenos() throws IOException { |
| Lineno[] lines = new Lineno[s_nlnno]; |
| sfile.seek(s_lnnoptr); |
| for (int i = 0; i < s_nlnno; i++) { |
| lines[i] = new Lineno(sfile); |
| } |
| return lines; |
| } |
| |
| public String toString() { |
| StringBuffer buffer = new StringBuffer(); |
| buffer.append("SECTION HEADER VALUES").append(NL); |
| buffer.append(new String(s_name)).append(NL); |
| buffer.append("s_paddr = ").append(s_paddr).append(NL); |
| buffer.append("s_vaddr = ").append(s_vaddr).append(NL); |
| buffer.append("s_size = ").append(s_size).append(NL); |
| buffer.append("s_scnptr = ").append(s_scnptr).append(NL); |
| buffer.append("s_relptr = ").append(s_relptr).append(NL); |
| buffer.append("s_lnnoptr = ").append(s_lnnoptr).append(NL); |
| buffer.append("s_nreloc = ").append(s_nreloc).append(NL); |
| buffer.append("s_nlnno = ").append(s_nlnno).append(NL); |
| buffer.append("s_flags = ").append(s_flags).append(NL); |
| ///* |
| try { |
| Reloc[] rcs = getRelocs(); |
| for (int i = 0; i < rcs.length; i++) { |
| buffer.append(rcs[i]); |
| } |
| } catch (IOException e) { |
| } |
| try { |
| Lineno[] nos = getLinenos(); |
| for (int i = 0; i < nos.length; i++) { |
| buffer.append(nos[i]); |
| } |
| } catch (IOException e) { |
| } |
| //*/ |
| return buffer.toString(); |
| } |
| } |
| |
| public static class Reloc { |
| public static final int RELSZ = 16; |
| public int r_vaddr; /* 4 byte: Pointer to an area in raw data that represents a |
| referenced address. */ |
| public int r_symndx; /* 4 byte: Index into symbol table. */ |
| public int r_type; /* 2 byte(unsigned short): Type of address reference. */ |
| |
| public Reloc(RandomAccessFile file) throws IOException { |
| this(file, file.getFilePointer()); |
| } |
| |
| public Reloc(RandomAccessFile file, long offset) throws IOException { |
| file.seek(offset); |
| byte[] bytes = new byte[RELSZ]; |
| file.readFully(bytes); |
| ReadMemoryAccess memory = new ReadMemoryAccess(bytes, true); |
| r_vaddr = memory.getInt(); |
| r_symndx = memory.getInt(); |
| r_type = memory.getUnsignedShort(); |
| } |
| |
| public String toString() { |
| StringBuffer buffer = new StringBuffer(); |
| buffer.append("RELOC VALUES").append(NL); |
| buffer.append("r_vaddr = ").append(r_vaddr); |
| buffer.append(" r_symndx = ").append(r_symndx).append(NL); |
| return buffer.toString(); |
| } |
| } |
| |
| public static class Lineno { |
| public final static int LINESZ = 6; |
| public int l_addr; /* long. Index into symbol table if l_linn0 == 0. |
| Break-pointable address if l_lnno > 0. */ |
| public int l_lnno; /* unsigned short. Line number */ |
| |
| public Lineno(RandomAccessFile file) throws IOException { |
| this(file, file.getFilePointer()); |
| } |
| |
| public Lineno(RandomAccessFile file, long offset) throws IOException { |
| file.seek(offset); |
| byte[] bytes = new byte[LINESZ]; |
| file.readFully(bytes); |
| ReadMemoryAccess memory = new ReadMemoryAccess(bytes, true); |
| l_addr = memory.getInt(); |
| l_lnno = memory.getUnsignedShort(); |
| } |
| |
| public String toString() { |
| StringBuffer buffer = new StringBuffer(); |
| if (l_lnno == 0) { |
| buffer.append("Function address = ").append(l_addr).append(NL); |
| } else { |
| buffer.append("line# ").append(l_lnno); |
| buffer.append(" at address = ").append(l_addr).append(NL); |
| } |
| return buffer.toString(); |
| } |
| } |
| |
| public static class Symbol { |
| public final static int SYMSZ = 18; |
| public final static int SYMNMLEN = 8; |
| |
| /* Derived types, in n_type. */ |
| public final static int DT_NON = 0; /* no derived type */ |
| public final static int DT_PTR = 1; /* pointer */ |
| public final static int DT_FCN = 2; /* function */ |
| public final static int DT_ARY = 3; /* array */ |
| |
| public final static int N_TMASK = 0x30; |
| public final static int N_BTSHFT = 4; |
| public final static int N_TSHIFT = 2; |
| |
| |
| public byte[] _n_name = new byte[SYMNMLEN]; /* Symbol name, or pointer into |
| string table if symbol name |
| is greater than SYMNMLEN. */ |
| public int n_value; /* long. Symbol;s value: dependent on section number, |
| storage class and type. */ |
| public short n_scnum; /* short, Section number. */ |
| public int n_type; /* Unsigned short. Symbolic type. */ |
| public byte n_sclass; /* char, Storage class. */ |
| public byte n_numaux; /* char. Nuymber of auxiliary enties. */ |
| |
| public Symbol(RandomAccessFile file) throws IOException { |
| this(file, file.getFilePointer()); |
| } |
| |
| public Symbol(RandomAccessFile file, long offset) throws IOException { |
| file.seek(offset); |
| byte[] bytes = new byte[SYMSZ]; |
| file.readFully(bytes); |
| ReadMemoryAccess memory = new ReadMemoryAccess(bytes, true); |
| memory.getBytes(_n_name); |
| n_value = memory.getInt(); |
| n_scnum = memory.getShort(); |
| n_type = memory.getUnsignedShort(); |
| n_sclass = memory.getByte(); |
| n_numaux = memory.getByte(); |
| } |
| |
| public boolean isLongName() { |
| return (_n_name[0] == 0); |
| } |
| |
| public String getName() { |
| for (int i = 0; i < _n_name.length; i++) { |
| if (_n_name[i] == 0) { |
| return new String(_n_name, 0, i); |
| } |
| } |
| return ""; |
| } |
| |
| public String getName(byte[] table) { |
| if (table.length > 0 && isLongName()) { |
| ReadMemoryAccess memory = new ReadMemoryAccess(_n_name, true); |
| memory.getInt(); // pass over the first 4 bytes. |
| // The first for bytes of the string table represent the |
| // number of bytes in the string table. |
| int offset = memory.getInt() - 4; |
| if (offset > 0) { |
| for (int i = offset; i < table.length; i++) { |
| if (table[i] == 0) { |
| return new String(table, offset, i - offset); |
| } |
| } |
| } |
| } |
| return getName(); |
| } |
| |
| public boolean isPointer() { |
| return (n_type & N_TMASK) == (DT_PTR << N_BTSHFT); |
| } |
| |
| public boolean isFunction() { |
| return (n_type & N_TMASK) == (DT_FCN << N_BTSHFT); |
| } |
| |
| public boolean isArray() { |
| return (n_type & N_TMASK) == (DT_ARY << N_BTSHFT); |
| } |
| |
| public String toString() { |
| return getName(); |
| } |
| |
| } |
| |
| public FileHeader getFileHeader() throws IOException { |
| return filehdr; |
| } |
| |
| public OptionalHeader getOptionalHeader() throws IOException { |
| return opthdr; |
| } |
| |
| public SectionHeader[] getSectionHeaders() throws IOException { |
| if (scnhdrs == null) { |
| scnhdrs = new SectionHeader[getFileHeader().f_nscns]; |
| long sec = FileHeader.FILHSZ + getFileHeader().f_opthdr; |
| for (int i = 0; i < scnhdrs.length; i++, sec += SectionHeader.SCNHSZ) { |
| scnhdrs[i] = new SectionHeader(rfile, sec); |
| } |
| } |
| return scnhdrs; |
| } |
| |
| public Symbol[] getSymbols() throws IOException { |
| if (symbols == null) { |
| long offset = getFileHeader().f_symptr; |
| rfile.seek(offset); |
| symbols = new Symbol[getFileHeader().f_nsyms]; |
| for (int i = 0; i < symbols.length; i++) { |
| symbols[i] = new Symbol(rfile); |
| } |
| } |
| return symbols; |
| } |
| |
| public byte[] getStringTable() throws IOException { |
| if (string_table == null) { |
| long symbolsize = Symbol.SYMSZ * getFileHeader().f_nsyms; |
| long offset = getFileHeader().f_symptr + symbolsize; |
| rfile.seek(offset); |
| byte[] bytes = new byte[4]; |
| rfile.readFully(bytes); |
| int str_len = ReadMemoryAccess.getIntLE(bytes); |
| if (str_len > 4) { |
| str_len -= 4; |
| string_table = new byte[str_len]; |
| rfile.seek(offset + 4); |
| rfile.readFully(string_table); |
| } else { |
| string_table = new byte[0]; |
| } |
| } |
| return string_table; |
| } |
| |
| public String toString() { |
| StringBuffer buffer = new StringBuffer(); |
| try { |
| FileHeader header = null; |
| header = getFileHeader(); |
| if (header != null) { |
| buffer.append(header); |
| } |
| } catch (IOException e) { |
| e.printStackTrace(); |
| } |
| try { |
| OptionalHeader opt = null; |
| opt = getOptionalHeader(); |
| if (opt != null) { |
| buffer.append(opt); |
| } |
| } catch (IOException e) { |
| e.printStackTrace(); |
| } |
| try { |
| SectionHeader[] sections = getSectionHeaders(); |
| for (int i = 0; i < sections.length; i++) { |
| buffer.append(sections[i]); |
| } |
| } catch (IOException e) { |
| } |
| |
| try { |
| Symbol[] table = getSymbols(); |
| for (int i = 0; i < table.length; i++) { |
| buffer.append(table[i].getName(getStringTable())).append(NL); |
| } |
| } catch (IOException e) { |
| } |
| |
| // try { |
| // String[] strings = getStringTable(getStringTable()); |
| // for (int i = 0; i < strings.length; i++) { |
| // buffer.append(strings[i]); |
| // } |
| // } catch (IOException e) { |
| // e.printStackTrace(); |
| // } |
| return buffer.toString(); |
| } |
| |
| public static String[] getStringTable(byte[] bytes) { |
| List aList = new ArrayList(); |
| int offset = 0; |
| for (int i = 0; i < bytes.length; i++) { |
| if (bytes[i] == 0) { |
| aList.add(new String(bytes, offset, i - offset)); |
| offset = i + 1; |
| } |
| } |
| return (String[])aList.toArray(new String[0]); |
| } |
| |
| public Coff(String filename) throws IOException { |
| this(new RandomAccessFile(filename, "r"), 0); |
| } |
| |
| public Coff(RandomAccessFile file, long offset) throws IOException { |
| commonSetup(file, offset); |
| } |
| |
| void commonSetup(RandomAccessFile file, long offset) throws IOException { |
| startingOffset = offset; |
| rfile = file; |
| try { |
| filehdr = new FileHeader(rfile, offset); |
| if (filehdr.f_opthdr > 0) { |
| opthdr = new OptionalHeader(rfile, startingOffset + 20); |
| } |
| } finally { |
| if (filehdr == null) { |
| rfile.close(); |
| } |
| } |
| } |
| |
| public static void main(String[] args) { |
| try { |
| Coff coff = new Coff(args[0]); |
| System.out.println(coff); |
| } catch (IOException e) { |
| e.printStackTrace(); |
| } |
| } |
| } |