1 /*
   2  * Copyright (c) 2000, 2010, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.
   8  *
   9  * This code is distributed in the hope that it will be useful, but WITHOUT
  10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  11  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  12  * version 2 for more details (a copy is included in the LICENSE file that
  13  * accompanied this code).
  14  *
  15  * You should have received a copy of the GNU General Public License version
  16  * 2 along with this work; if not, write to the Free Software Foundation,
  17  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  18  *
  19  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  20  * or visit www.oracle.com if you need additional information or have any
  21  * questions.
  22  *
  23  */
  24 
  25 package sun.jvm.hotspot.debugger.win32.coff;
  26 
  27 import java.io.*;
  28 import java.nio.*;
  29 import java.nio.channels.*;
  30 import java.util.*;
  31 
  32 import sun.jvm.hotspot.utilities.memo.*;
  33 import sun.jvm.hotspot.utilities.Assert;
  34 import sun.jvm.hotspot.debugger.DataSource;
  35 import sun.jvm.hotspot.debugger.MappedByteBufferDataSource;
  36 
  37 /** Top-level factory which parses COFF files, including object files,
  38     Portable Executables and DLLs. Returns {@link
  39     sun.jvm.hotspot.debugger.win32.coff.COFFFile} objects. This class is a
  40     singleton. */
  41 
  42 public class COFFFileParser {
  43   private static COFFFileParser soleInstance;
  44 
  45   // Constants from the file format documentation
  46   private static final int COFF_HEADER_SIZE = 20;
  47   private static final int SECTION_HEADER_SIZE = 40;
  48   private static final int SYMBOL_SIZE = 18;
  49   private static final int RELOCATION_SIZE = 10;
  50   private static final int LINE_NUMBER_SIZE = 6;
  51 
  52   private static final String US_ASCII = "US-ASCII";
  53 
  54   private COFFFileParser() {}
  55 
  56   /** This class is a singleton; returns the sole instance. */
  57   public static COFFFileParser getParser() {
  58     if (soleInstance == null) {
  59       soleInstance = new COFFFileParser();
  60     }
  61     return soleInstance;
  62   }
  63 
  64   public COFFFile parse(String filename) throws COFFException {
  65     try {
  66       File file = new File(filename);
  67       FileInputStream stream = new FileInputStream(file);
  68       MappedByteBuffer buf = stream.getChannel().map(FileChannel.MapMode.READ_ONLY,
  69                                                      0,
  70                                                      file.length());
  71 
  72       // This is pretty confusing. The file format is little-endian
  73       // and so is the CPU. In order for the multi-byte accessors to
  74       // work properly we must NOT change the endianness of the
  75       // MappedByteBuffer. Need to think about this some more and file
  76       // a bug if there is one. (FIXME)
  77       //   buf.order(ByteOrder.nativeOrder());
  78       return parse(new MappedByteBufferDataSource(buf));
  79     } catch (FileNotFoundException e) {
  80       throw new COFFException(e);
  81     } catch (IOException e) {
  82       throw new COFFException(e);
  83     }
  84   }
  85 
  86   public COFFFile parse(DataSource source) throws COFFException {
  87     return new COFFFileImpl(source);
  88   }
  89 
  90   class COFFFileImpl implements COFFFile {
  91     private DataSource file;
  92     private long       filePos;
  93     private boolean isImage;
  94     private long    imageHeaderOffset;
  95     private MemoizedObject header = new MemoizedObject() {
  96         public Object computeValue() {
  97           return new COFFHeaderImpl();
  98         }
  99       };
 100 
 101     COFFFileImpl(DataSource file) throws COFFException {
 102       this.file = file;
 103       initialize();
 104     }
 105 
 106     public boolean isImage() {
 107       return isImage;
 108     }
 109 
 110     public COFFHeader getHeader() {
 111       return (COFFHeaderImpl) header.getValue();
 112     }
 113 
 114     class COFFHeaderImpl implements COFFHeader {
 115       private short machine;
 116       private short numberOfSections;
 117       private int   timeDateStamp;
 118       private int   pointerToSymbolTable;
 119       private int   numberOfSymbols;
 120       private short sizeOfOptionalHeader;
 121       private short characteristics;
 122       private MemoizedObject[] sectionHeaders;
 123       private MemoizedObject[] symbols;
 124 
 125       // Init stringTable at decl time since other fields init'ed in the
 126       // constructor need the String Table.
 127       private MemoizedObject stringTable = new MemoizedObject() {
 128           public Object computeValue() {
 129             // the String Table follows the Symbol Table
 130             int ptr = getPointerToSymbolTable();
 131             if (ptr == 0) {
 132               // no Symbol Table so no String Table
 133               return new StringTable(0);
 134             } else {
 135               return new StringTable(ptr + SYMBOL_SIZE * getNumberOfSymbols());
 136             }
 137           }
 138         };
 139 
 140       COFFHeaderImpl() {
 141         seek(imageHeaderOffset);
 142         machine = readShort();
 143         numberOfSections = readShort();
 144         timeDateStamp = readInt();
 145         pointerToSymbolTable = readInt();
 146         numberOfSymbols = readInt();
 147         // String Table can be accessed at this point because
 148         // pointerToSymbolTable and numberOfSymbols fields are set.
 149         sizeOfOptionalHeader = readShort();
 150         characteristics = readShort();
 151 
 152         // Set up section headers
 153         sectionHeaders = new MemoizedObject[numberOfSections];
 154         for (int i = 0; i < numberOfSections; i++) {
 155           final int secHdrOffset = (int)
 156             (imageHeaderOffset + COFF_HEADER_SIZE + sizeOfOptionalHeader + i * SECTION_HEADER_SIZE);
 157           sectionHeaders[i] = new MemoizedObject() {
 158               public Object computeValue() {
 159                 return new SectionHeaderImpl(secHdrOffset);
 160               }
 161             };
 162         }
 163 
 164         // Set up symbols
 165         symbols = new MemoizedObject[numberOfSymbols];
 166         for (int i = 0; i < numberOfSymbols; i++) {
 167           final int symbolOffset = pointerToSymbolTable + i * SYMBOL_SIZE;
 168           symbols[i] = new MemoizedObject() {
 169               public Object computeValue() {
 170                 return new COFFSymbolImpl(symbolOffset);
 171               }
 172             };
 173         }
 174       }
 175 
 176       public short          getMachineType()          { return machine; }
 177       public short          getNumberOfSections()     { return numberOfSections; }
 178       public int            getTimeDateStamp()        { return timeDateStamp; }
 179       public int            getPointerToSymbolTable() { return pointerToSymbolTable; }
 180       public int            getNumberOfSymbols()      { return numberOfSymbols; }
 181       public short          getSizeOfOptionalHeader() { return sizeOfOptionalHeader; }
 182       public OptionalHeader getOptionalHeader() throws COFFException {
 183         if (getSizeOfOptionalHeader() == 0) {
 184           return null;
 185         }
 186         return new OptionalHeaderImpl((int) (imageHeaderOffset + COFF_HEADER_SIZE));
 187       }
 188       public short          getCharacteristics()      { return characteristics; }
 189       public boolean hasCharacteristic(short characteristic) {
 190         return ((characteristics & characteristic) != 0);
 191       }
 192       public SectionHeader getSectionHeader(int index) {
 193         // NOTE zero-basing of index
 194         return (SectionHeader) sectionHeaders[index - 1].getValue();
 195       }
 196       public COFFSymbol    getCOFFSymbol(int index)    {
 197         return (COFFSymbol) symbols[index].getValue();
 198       }
 199       public int getNumberOfStrings() {
 200         return getStringTable().getNum();
 201       }
 202       public String getString(int i) {
 203         return getStringTable().get(i);
 204       }
 205 
 206       StringTable          getStringTable() { return (StringTable) stringTable.getValue(); }
 207 
 208       // NOTE: can destroy current seek() position!
 209       int rvaToFileOffset(int rva) {
 210         if (rva == 0) return 0;
 211         // Search for section containing RVA
 212         for (int i = 1; i <= getNumberOfSections(); i++) {
 213           SectionHeader sec = getSectionHeader(i);
 214           int va = sec.getVirtualAddress();
 215           int sz = sec.getSize();
 216           if ((va <= rva) && (rva < (va + sz))) {
 217             return sec.getPointerToRawData() + (rva - va);
 218           }
 219         }
 220         throw new COFFException("Unable to find RVA 0x" +
 221                                 Integer.toHexString(rva) +
 222                                 " in any section");
 223       }
 224 
 225       class OptionalHeaderImpl implements OptionalHeader {
 226         private short magic;
 227         private MemoizedObject standardFields;
 228         private MemoizedObject windowsSpecificFields;
 229         private MemoizedObject dataDirectories;
 230 
 231         // We use an offset of 2 because OptionalHeaderStandardFieldsImpl doesn't
 232         // include the 'magic' field.
 233         private static final int STANDARD_FIELDS_OFFSET = 2;
 234         private static final int PE32_WINDOWS_SPECIFIC_FIELDS_OFFSET = 28;
 235         private static final int PE32_DATA_DIRECTORIES_OFFSET = 96;
 236         private static final int PE32_PLUS_WINDOWS_SPECIFIC_FIELDS_OFFSET = 24;
 237         private static final int PE32_PLUS_DATA_DIRECTORIES_OFFSET = 112;
 238 
 239         OptionalHeaderImpl(final int offset) {
 240           seek(offset);
 241           magic = readShort();
 242 
 243           final boolean isPE32Plus = (magic == MAGIC_PE32_PLUS);
 244           final int standardFieldsOffset = offset + STANDARD_FIELDS_OFFSET;
 245           final int windowsSpecificFieldsOffset = offset +
 246             (isPE32Plus
 247              ? PE32_PLUS_WINDOWS_SPECIFIC_FIELDS_OFFSET
 248              : PE32_WINDOWS_SPECIFIC_FIELDS_OFFSET);
 249           final int dataDirectoriesOffset = offset +
 250             (isPE32Plus
 251              ? PE32_PLUS_DATA_DIRECTORIES_OFFSET
 252              : PE32_DATA_DIRECTORIES_OFFSET);
 253 
 254           standardFields = new MemoizedObject() {
 255               public Object computeValue() {
 256                 return new OptionalHeaderStandardFieldsImpl(standardFieldsOffset,
 257                                                             isPE32Plus);
 258               }
 259             };
 260           windowsSpecificFields = new MemoizedObject() {
 261               public Object computeValue() {
 262                 return new OptionalHeaderWindowsSpecificFieldsImpl(windowsSpecificFieldsOffset,
 263                                                                    isPE32Plus);
 264               }
 265             };
 266           dataDirectories = new MemoizedObject() {
 267               public Object computeValue() {
 268                 return new OptionalHeaderDataDirectoriesImpl(dataDirectoriesOffset,
 269                                                              getWindowsSpecificFields().getNumberOfRvaAndSizes());
 270               }
 271             };
 272         }
 273 
 274         public short getMagicNumber() {
 275           return magic;
 276         }
 277 
 278         public OptionalHeaderStandardFields getStandardFields() {
 279           return (OptionalHeaderStandardFields) standardFields.getValue();
 280         }
 281 
 282         public OptionalHeaderWindowsSpecificFields getWindowsSpecificFields() {
 283           return (OptionalHeaderWindowsSpecificFields) windowsSpecificFields.getValue();
 284         }
 285         public OptionalHeaderDataDirectories getDataDirectories() {
 286           return (OptionalHeaderDataDirectories) dataDirectories.getValue();
 287         }
 288       }
 289 
 290       class OptionalHeaderStandardFieldsImpl implements OptionalHeaderStandardFields {
 291         private boolean isPE32Plus;
 292         private byte majorLinkerVersion;
 293         private byte minorLinkerVersion;
 294         private int sizeOfCode;
 295         private int sizeOfInitializedData;
 296         private int sizeOfUninitializedData;
 297         private int addressOfEntryPoint;
 298         private int baseOfCode;
 299         private int baseOfData;  // only set in PE32
 300 
 301         OptionalHeaderStandardFieldsImpl(int offset,
 302                                          boolean isPE32Plus) {
 303           this.isPE32Plus = isPE32Plus;
 304           seek(offset);
 305           majorLinkerVersion = readByte();
 306           minorLinkerVersion = readByte();
 307           sizeOfCode = readInt();
 308           sizeOfInitializedData = readInt();
 309           sizeOfUninitializedData = readInt();
 310           addressOfEntryPoint = readInt();
 311           baseOfCode = readInt();
 312           if (!isPE32Plus) {
 313             // only available in PE32
 314             baseOfData = readInt();
 315           }
 316         }
 317 
 318         public byte getMajorLinkerVersion() { return majorLinkerVersion; }
 319         public byte getMinorLinkerVersion() { return minorLinkerVersion; }
 320         public int getSizeOfCode()              { return sizeOfCode; }
 321         public int getSizeOfInitializedData()   { return sizeOfInitializedData; }
 322         public int getSizeOfUninitializedData() { return sizeOfUninitializedData; }
 323         public int getAddressOfEntryPoint()     { return addressOfEntryPoint; }
 324         public int getBaseOfCode()              { return baseOfCode; }
 325         public int getBaseOfData() throws COFFException {
 326           if (isPE32Plus) {
 327             throw new COFFException("Not present in PE32+ files");
 328           }
 329           return baseOfData;
 330         }
 331       }
 332 
 333       class OptionalHeaderWindowsSpecificFieldsImpl implements OptionalHeaderWindowsSpecificFields {
 334         private long imageBase;
 335         private int sectionAlignment;
 336         private int fileAlignment;
 337         private short majorOperatingSystemVersion;
 338         private short minorOperatingSystemVersion;
 339         private short majorImageVersion;
 340         private short minorImageVersion;
 341         private short majorSubsystemVersion;
 342         private short minorSubsystemVersion;
 343         private int sizeOfImage;
 344         private int sizeOfHeaders;
 345         private int checkSum;
 346         private short subsystem;
 347         private short dllCharacteristics;
 348         private long sizeOfStackReserve;
 349         private long sizeOfStackCommit;
 350         private long sizeOfHeapReserve;
 351         private long sizeOfHeapCommit;
 352         private int loaderFlags;
 353         private int numberOfRvaAndSizes;
 354 
 355         OptionalHeaderWindowsSpecificFieldsImpl(int offset, boolean isPE32Plus) {
 356           seek(offset);
 357 
 358           if (!isPE32Plus) {
 359             imageBase = maskInt(readInt());
 360           } else {
 361             imageBase = readLong();
 362           }
 363           sectionAlignment = readInt();
 364           fileAlignment = readInt();
 365           majorOperatingSystemVersion = readShort();
 366           minorOperatingSystemVersion = readShort();
 367           majorImageVersion = readShort();
 368           minorImageVersion = readShort();
 369           majorSubsystemVersion = readShort();
 370           minorSubsystemVersion = readShort();
 371           readInt(); // Reserved
 372           sizeOfImage = readInt();
 373           sizeOfHeaders = readInt();
 374           checkSum = readInt();
 375           subsystem = readShort();
 376           dllCharacteristics = readShort();
 377           if (!isPE32Plus) {
 378             sizeOfStackReserve = maskInt(readInt());
 379             sizeOfStackCommit  = maskInt(readInt());
 380             sizeOfHeapReserve  = maskInt(readInt());
 381             sizeOfHeapCommit   = maskInt(readInt());
 382           } else {
 383             sizeOfStackReserve = readLong();
 384             sizeOfStackCommit  = readLong();
 385             sizeOfHeapReserve  = readLong();
 386             sizeOfHeapCommit   = readLong();
 387           }
 388           loaderFlags = readInt();
 389           numberOfRvaAndSizes = readInt();
 390         }
 391 
 392         public long getImageBase()              { return imageBase; }
 393         public int getSectionAlignment()        { return sectionAlignment; }
 394         public int getFileAlignment()           { return fileAlignment; }
 395         public short getMajorOperatingSystemVersion() { return majorOperatingSystemVersion; }
 396         public short getMinorOperatingSystemVersion() { return minorOperatingSystemVersion; }
 397         public short getMajorImageVersion()     { return majorImageVersion; }
 398         public short getMinorImageVersion()     { return minorImageVersion; }
 399         public short getMajorSubsystemVersion() { return majorSubsystemVersion; }
 400         public short getMinorSubsystemVersion() { return minorSubsystemVersion; }
 401         public int getSizeOfImage()             { return sizeOfImage; }
 402         public int getSizeOfHeaders()           { return sizeOfHeaders; }
 403         public int getCheckSum()                { return checkSum; }
 404         public short getSubsystem()             { return subsystem; }
 405         public short getDLLCharacteristics()    { return dllCharacteristics; }
 406         public long getSizeOfStackReserve()     { return sizeOfStackReserve; }
 407         public long getSizeOfStackCommit()      { return sizeOfStackCommit; }
 408         public long getSizeOfHeapReserve()      { return sizeOfHeapReserve; }
 409         public long getSizeOfHeapCommit()       { return sizeOfHeapCommit; }
 410         public int getLoaderFlags()             { return loaderFlags; }
 411         public int getNumberOfRvaAndSizes()     { return numberOfRvaAndSizes; }
 412 
 413         private long maskInt(long arg) {
 414           return (arg & 0x00000000FFFFFFFFL);
 415         }
 416       }
 417 
 418       class OptionalHeaderDataDirectoriesImpl implements OptionalHeaderDataDirectories {
 419         private int numberOfRvaAndSizes;
 420         private MemoizedObject[] dataDirectories;
 421         private MemoizedObject   exportDirectoryTable;
 422         private MemoizedObject   debugDirectory;
 423 
 424         private static final int DATA_DIRECTORY_SIZE = 8;
 425 
 426         OptionalHeaderDataDirectoriesImpl(int offset,
 427                                           int numberOfRvaAndSizes) {
 428           this.numberOfRvaAndSizes = numberOfRvaAndSizes;
 429           dataDirectories = new MemoizedObject[numberOfRvaAndSizes];
 430           for (int i = 0; i < numberOfRvaAndSizes; i++) {
 431             final int dirOffset = offset + (i * DATA_DIRECTORY_SIZE);
 432             dataDirectories[i] = new MemoizedObject() {
 433                 public Object computeValue() {
 434                   return new DataDirectoryImpl(dirOffset);
 435                 }
 436               };
 437           }
 438 
 439           exportDirectoryTable = new MemoizedObject() {
 440               public Object computeValue() {
 441                 DataDirectory dir = getExportTable();
 442                 if (dir.getRVA() == 0 || dir.getSize() == 0) {
 443                   return null;
 444                 }
 445                 // ExportDirectoryTableImpl needs both the RVA and the
 446                 // RVA converted to a file offset.
 447                 return new
 448                     ExportDirectoryTableImpl(dir.getRVA(), dir.getSize());
 449               }
 450             };
 451 
 452           debugDirectory = new MemoizedObject() {
 453               public Object computeValue() {
 454                 DataDirectory dir = getDebug();
 455                 if (dir.getRVA() == 0 || dir.getSize() == 0) {
 456                   return null;
 457                 }
 458                 return new DebugDirectoryImpl(rvaToFileOffset(dir.getRVA()), dir.getSize());
 459               }
 460             };
 461         }
 462 
 463         public DataDirectory getExportTable() throws COFFException {
 464           return (DataDirectory) dataDirectories[checkIndex(0)].getValue();
 465         }
 466         public DataDirectory getImportTable() throws COFFException {
 467           return (DataDirectory) dataDirectories[checkIndex(1)].getValue();
 468         }
 469         public DataDirectory getResourceTable() throws COFFException {
 470           return (DataDirectory) dataDirectories[checkIndex(2)].getValue();
 471         }
 472         public DataDirectory getExceptionTable() throws COFFException {
 473           return (DataDirectory) dataDirectories[checkIndex(3)].getValue();
 474         }
 475         public DataDirectory getCertificateTable() throws COFFException {
 476           return (DataDirectory) dataDirectories[checkIndex(4)].getValue();
 477         }
 478         public DataDirectory getBaseRelocationTable() throws COFFException {
 479           return (DataDirectory) dataDirectories[checkIndex(5)].getValue();
 480         }
 481         public DataDirectory getDebug() throws COFFException {
 482           return (DataDirectory) dataDirectories[checkIndex(6)].getValue();
 483         }
 484         public DataDirectory getArchitecture() throws COFFException {
 485           return (DataDirectory) dataDirectories[checkIndex(7)].getValue();
 486         }
 487         public DataDirectory getGlobalPtr() throws COFFException {
 488           return (DataDirectory) dataDirectories[checkIndex(8)].getValue();
 489         }
 490         public DataDirectory getTLSTable() throws COFFException {
 491           return (DataDirectory) dataDirectories[checkIndex(9)].getValue();
 492         }
 493         public DataDirectory getLoadConfigTable() throws COFFException {
 494           return (DataDirectory) dataDirectories[checkIndex(10)].getValue();
 495         }
 496         public DataDirectory getBoundImportTable() throws COFFException {
 497           return (DataDirectory) dataDirectories[checkIndex(11)].getValue();
 498         }
 499         public DataDirectory getImportAddressTable() throws COFFException {
 500           return (DataDirectory) dataDirectories[checkIndex(12)].getValue();
 501         }
 502         public DataDirectory getDelayImportDescriptor() throws COFFException {
 503           return (DataDirectory) dataDirectories[checkIndex(13)].getValue();
 504         }
 505         public DataDirectory getCOMPlusRuntimeHeader() throws COFFException {
 506           return (DataDirectory) dataDirectories[checkIndex(14)].getValue();
 507         }
 508 
 509         public ExportDirectoryTable getExportDirectoryTable() throws COFFException {
 510           return (ExportDirectoryTable) exportDirectoryTable.getValue();
 511         }
 512 
 513         public DebugDirectory getDebugDirectory() throws COFFException {
 514           return (DebugDirectory) debugDirectory.getValue();
 515         }
 516 
 517         private int checkIndex(int index) throws COFFException {
 518           if ((index < 0) || (index >= dataDirectories.length)) {
 519             throw new COFFException("Directory " + index + " unavailable (only " +
 520                                     numberOfRvaAndSizes + " tables present)");
 521           }
 522           return index;
 523         }
 524       }
 525 
 526       class DataDirectoryImpl implements DataDirectory {
 527         int rva;
 528         int size;
 529 
 530         DataDirectoryImpl(int offset) {
 531           seek(offset);
 532           rva  = readInt();
 533           size = readInt();
 534         }
 535 
 536         public int getRVA()  { return rva; }
 537         public int getSize() { return size; }
 538       }
 539 
 540       class ExportDirectoryTableImpl implements ExportDirectoryTable {
 541         private int exportDataDirRVA;
 542         private int offset;
 543         private int size;
 544 
 545         private int exportFlags;
 546         private int timeDateStamp;
 547         private short majorVersion;
 548         private short minorVersion;
 549         private int nameRVA;
 550         private int ordinalBase;
 551         private int addressTableEntries;
 552         private int numberOfNamePointers;
 553         private int exportAddressTableRVA;
 554         private int namePointerTableRVA;
 555         private int ordinalTableRVA;
 556 
 557         private MemoizedObject dllName;
 558 
 559         private MemoizedObject exportNameTable;
 560         private MemoizedObject exportNamePointerTable;
 561         private MemoizedObject exportOrdinalTable;
 562         private MemoizedObject exportAddressTable;
 563 
 564         ExportDirectoryTableImpl(int exportDataDirRVA, int size) {
 565           this.exportDataDirRVA = exportDataDirRVA;
 566           offset = rvaToFileOffset(exportDataDirRVA);
 567           this.size   = size;
 568           seek(offset);
 569           exportFlags = readInt();
 570           timeDateStamp = readInt();
 571           majorVersion = readShort();
 572           minorVersion = readShort();
 573           nameRVA = readInt();
 574           ordinalBase = readInt();
 575           addressTableEntries = readInt();
 576           numberOfNamePointers = readInt();
 577           exportAddressTableRVA = readInt();
 578           namePointerTableRVA = readInt();
 579           ordinalTableRVA = readInt();
 580 
 581           dllName = new MemoizedObject() {
 582               public Object computeValue() {
 583                 seek(rvaToFileOffset(getNameRVA()));
 584                 return readCString();
 585               }
 586             };
 587 
 588           exportNamePointerTable = new MemoizedObject() {
 589               public Object computeValue() {
 590                 int[] pointers = new int[getNumberOfNamePointers()];
 591                 seek(rvaToFileOffset(getNamePointerTableRVA()));
 592                 // Must make two passes to avoid rvaToFileOffset
 593                 // destroying seek() position
 594                 for (int i = 0; i < pointers.length; i++) {
 595                   pointers[i] = readInt();
 596                 }
 597                 for (int i = 0; i < pointers.length; i++) {
 598                   pointers[i] = rvaToFileOffset(pointers[i]);
 599                 }
 600                 return pointers;
 601               }
 602             };
 603 
 604           exportNameTable = new MemoizedObject() {
 605               public Object computeValue() {
 606                 return new ExportNameTable(getExportNamePointerTable());
 607               }
 608             };
 609 
 610           exportOrdinalTable = new MemoizedObject() {
 611               public Object computeValue() {
 612                 // number of ordinals is same as the number of name pointers
 613                 short[] ordinals = new short[getNumberOfNamePointers()];
 614                 seek(rvaToFileOffset(getOrdinalTableRVA()));
 615                 for (int i = 0; i < ordinals.length; i++) {
 616                   ordinals[i] = readShort();
 617                 }
 618                 return ordinals;
 619               }
 620             };
 621 
 622           exportAddressTable = new MemoizedObject() {
 623               public Object computeValue() {
 624                 int[] addresses = new int[getNumberOfAddressTableEntries()];
 625                 seek(rvaToFileOffset(getExportAddressTableRVA()));
 626                 // The Export Address Table values are a union of two
 627                 // possible values:
 628                 //   Export RVA - The address of the exported symbol when
 629                 //       loaded into memory, relative to the image base.
 630                 //       This value doesn't get converted into a file offset.
 631                 //   Forwarder RVA - The pointer to a null-terminated ASCII
 632                 //       string in the export section. This value gets
 633                 //       converted into a file offset because we have to
 634                 //       fetch the string.
 635                 for (int i = 0; i < addresses.length; i++) {
 636                   addresses[i] = readInt();
 637                 }
 638                 return addresses;
 639               }
 640             };
 641         }
 642 
 643         public int   getExportFlags()   { return exportFlags; }
 644         public int   getTimeDateStamp() { return timeDateStamp; }
 645         public short getMajorVersion()  { return majorVersion; }
 646         public short getMinorVersion()  { return minorVersion; }
 647         public int   getNameRVA()       { return nameRVA; }
 648 
 649         public String getDLLName() {
 650           return (String) dllName.getValue();
 651         }
 652 
 653         public int getOrdinalBase()                 { return ordinalBase; }
 654         public int getNumberOfAddressTableEntries() { return addressTableEntries; }
 655         public int getNumberOfNamePointers()        { return numberOfNamePointers; }
 656         public int getExportAddressTableRVA()       { return exportAddressTableRVA; }
 657         public int getNamePointerTableRVA()         { return namePointerTableRVA; }
 658         public int getOrdinalTableRVA()             { return ordinalTableRVA; }
 659 
 660         public String getExportName(int i) {
 661           return getExportNameTable().get(i);
 662         }
 663 
 664         public short  getExportOrdinal(int i) {
 665           return getExportOrdinalTable()[i];
 666         }
 667 
 668         public boolean isExportAddressForwarder(short ordinal) {
 669           int addr = getExportAddress(ordinal);
 670           return ((exportDataDirRVA <= addr) &&
 671               (addr < (exportDataDirRVA + size)));
 672         }
 673 
 674         public String getExportAddressForwarder(short ordinal) {
 675           seek(rvaToFileOffset(getExportAddress(ordinal)));
 676           return readCString();
 677         }
 678 
 679         public int    getExportAddress(short ordinal) {
 680 
 681           ///////////////////////
 682           // FIXME: MAJOR HACK //
 683           ///////////////////////
 684 
 685           // According to the documentation, the first line here is
 686           // correct. However, it doesn't seem to work. The second
 687           // one, however, does.
 688 
 689           // OK, it's probably due to using negative indices in the
 690           // export address table in "real life"...need to rethink
 691           // this when I'm more awake
 692 
 693           //          return getExportAddressTable()[ordinal - ordinalBase];
 694           return getExportAddressTable()[ordinal];
 695         }
 696 
 697         private ExportNameTable getExportNameTable() {
 698           return (ExportNameTable) exportNameTable.getValue();
 699         }
 700 
 701         private int[] getExportNamePointerTable() {
 702           return (int[]) exportNamePointerTable.getValue();
 703         }
 704 
 705         private short[] getExportOrdinalTable() {
 706           return (short[]) exportOrdinalTable.getValue();
 707         }
 708 
 709         private int[] getExportAddressTable() {
 710           return (int[]) exportAddressTable.getValue();
 711         }
 712       }
 713 
 714       class ExportNameTable {
 715         private MemoizedObject[] names;
 716 
 717         ExportNameTable(final int[] exportNamePointerTable) {
 718           names = new MemoizedObject[exportNamePointerTable.length];
 719           for (int i = 0; i < exportNamePointerTable.length; i++) {
 720             final int idx = i;
 721             names[idx] = new MemoizedObject() {
 722                 public Object computeValue() {
 723                   seek(exportNamePointerTable[idx]);
 724                   return readCString();
 725                 }
 726               };
 727             };
 728         }
 729 
 730         String get(int i) {
 731           return (String) names[i].getValue();
 732         }
 733       }
 734 
 735       class DebugDirectoryImpl implements DebugDirectory {
 736         private int offset;
 737         private int size;
 738         private int numEntries;
 739 
 740         private static final int DEBUG_DIRECTORY_ENTRY_SIZE = 28;
 741 
 742         DebugDirectoryImpl(int offset, int size) {
 743           this.offset = offset;
 744           this.size = size;
 745 
 746           if ((size % DEBUG_DIRECTORY_ENTRY_SIZE) != 0) {
 747             throw new COFFException("Corrupt DebugDirectory at offset 0x" +
 748                                     Integer.toHexString(offset));
 749           }
 750 
 751           numEntries = size / DEBUG_DIRECTORY_ENTRY_SIZE;
 752         }
 753 
 754         public int getNumEntries() { return numEntries; }
 755         public DebugDirectoryEntry getEntry(int i) {
 756           if ((i < 0) || (i >= getNumEntries())) throw new IndexOutOfBoundsException();
 757           return new DebugDirectoryEntryImpl(offset + i * DEBUG_DIRECTORY_ENTRY_SIZE);
 758         }
 759       }
 760 
 761       class DebugDirectoryEntryImpl implements DebugDirectoryEntry, DebugTypes {
 762         private int characteristics;
 763         private int timeDateStamp;
 764         private short majorVersion;
 765         private short minorVersion;
 766         private int type;
 767         private int sizeOfData;
 768         private int addressOfRawData;
 769         private int pointerToRawData;
 770 
 771         DebugDirectoryEntryImpl(int offset) {
 772           seek(offset);
 773           characteristics = readInt();
 774           timeDateStamp = readInt();
 775           majorVersion = readShort();
 776           minorVersion = readShort();
 777           type = readInt();
 778           sizeOfData = readInt();
 779           addressOfRawData = readInt();
 780           pointerToRawData = readInt();
 781         }
 782 
 783         public int   getCharacteristics()  { return characteristics; }
 784         public int   getTimeDateStamp()    { return timeDateStamp; }
 785         public short getMajorVersion()     { return majorVersion; }
 786         public short getMinorVersion()     { return minorVersion; }
 787         public int   getType()             { return type; }
 788         public int   getSizeOfData()       { return sizeOfData; }
 789         public int   getAddressOfRawData() { return addressOfRawData; }
 790         public int   getPointerToRawData() { return pointerToRawData; }
 791 
 792         public DebugVC50 getDebugVC50() {
 793           // See whether we can recognize VC++ 5.0 debug information.
 794           try {
 795             if (getType() != IMAGE_DEBUG_TYPE_CODEVIEW) return null;
 796 
 797             int offset = getPointerToRawData();
 798             seek(offset);
 799             if (readByte() == 'N' &&
 800                 readByte() == 'B' &&
 801                 readByte() == '1' &&
 802                 readByte() == '1') {
 803               return new DebugVC50Impl(offset);
 804             }
 805           } catch (COFFException e) {
 806             e.printStackTrace();
 807           }
 808           return null;
 809         }
 810 
 811         public byte  getRawDataByte(int i) {
 812           if (i < 0 || i >= getSizeOfData()) {
 813             throw new IndexOutOfBoundsException();
 814           }
 815           seek(getPointerToRawData() + i);
 816           return readByte();
 817         }
 818       }
 819 
 820       class DebugVC50Impl implements DebugVC50, DebugVC50TypeLeafIndices {
 821         private int lfaBase;
 822 
 823         private int subsectionDirectoryOffset;
 824         private MemoizedObject subsectionDirectory;
 825 
 826         DebugVC50Impl(int offset) {
 827           lfaBase = offset;
 828           seek(offset);
 829           readInt();  // Discard NB11
 830           subsectionDirectoryOffset = globalOffset(readInt());
 831 
 832           // Ensure information is complete
 833           verify();
 834 
 835           subsectionDirectory = new MemoizedObject() {
 836               public Object computeValue() {
 837                 return new DebugVC50SubsectionDirectoryImpl(getSubsectionDirectoryOffset());
 838               }
 839             };
 840         }
 841 
 842         public int getSubsectionDirectoryOffset() {
 843           return subsectionDirectoryOffset;
 844         }
 845 
 846         public DebugVC50SubsectionDirectory getSubsectionDirectory() {
 847           return (DebugVC50SubsectionDirectory) subsectionDirectory.getValue();
 848         }
 849 
 850         private int globalOffset(int offset) {
 851           return offset + lfaBase;
 852         }
 853 
 854         private void verify() {
 855           // Seek to subsection directory manually and look for
 856           // signature following it. This finishes validating that we
 857           // have VC++ 5.0 debug info. Throw COFFException if not
 858           // found; will cause caller to return null.
 859           seek(subsectionDirectoryOffset);
 860           int headerLength = readShort();
 861           int entryLength  = readShort();
 862           int numEntries   = readInt();
 863           int endOffset    = subsectionDirectoryOffset + headerLength + numEntries * entryLength;
 864           seek(endOffset);
 865 
 866           if (readByte() == 'N' &&
 867               readByte() == 'B' &&
 868               readByte() == '1' &&
 869               readByte() == '1') {
 870             return;
 871           }
 872 
 873           throw new COFFException("Did not find NB11 signature at end of debug info");
 874         }
 875 
 876         class DebugVC50SubsectionDirectoryImpl
 877           implements DebugVC50SubsectionDirectory,
 878                      DebugVC50SubsectionTypes {
 879           private int   offset;
 880           private short dirHeaderLength;
 881           private short dirEntryLength;
 882           private int   numEntries;
 883 
 884           DebugVC50SubsectionDirectoryImpl(int offset) {
 885             this.offset = offset;
 886             // Read header
 887             seek(offset);
 888             dirHeaderLength = readShort();
 889             dirEntryLength  = readShort();
 890             numEntries      = readInt();
 891           }
 892 
 893           public short getHeaderLength() { return dirHeaderLength; }
 894           public short getEntryLength()  { return dirEntryLength;  }
 895           public int   getNumEntries()   { return numEntries;      }
 896 
 897           public DebugVC50Subsection getSubsection(int i) {
 898             // Fetch the subsection type and instantiate the correct
 899             // type of subsection based on it
 900             seek(offset + dirHeaderLength + (i * dirEntryLength));
 901             short ssType = readShort();
 902             short iMod   = readShort(); // Unneeded?
 903             int   lfo    = globalOffset(readInt());
 904             int   cb     = readInt();
 905             switch (ssType) {
 906             case SST_MODULE:
 907               return new DebugVC50SSModuleImpl(ssType, iMod, cb, lfo);
 908             case SST_TYPES:
 909               return new DebugVC50SSTypesImpl(ssType, iMod, cb, lfo);
 910             case SST_PUBLIC:
 911               return new DebugVC50SSPublicImpl(ssType, iMod, cb, lfo);
 912             case SST_PUBLIC_SYM:
 913               return new DebugVC50SSPublicSymImpl(ssType, iMod, cb, lfo);
 914             case SST_SYMBOLS:
 915               return new DebugVC50SSSymbolsImpl(ssType, iMod, cb, lfo);
 916             case SST_ALIGN_SYM:
 917               return new DebugVC50SSAlignSymImpl(ssType, iMod, cb, lfo);
 918             case SST_SRC_LN_SEG:
 919               return new DebugVC50SSSrcLnSegImpl(ssType, iMod, cb, lfo);
 920             case SST_SRC_MODULE:
 921               return new DebugVC50SSSrcModuleImpl(ssType, iMod, cb, lfo);
 922             case SST_LIBRARIES:
 923               return new DebugVC50SSLibrariesImpl(ssType, iMod, cb, lfo);
 924             case SST_GLOBAL_SYM:
 925               return new DebugVC50SSGlobalSymImpl(ssType, iMod, cb, lfo);
 926             case SST_GLOBAL_PUB:
 927               return new DebugVC50SSGlobalPubImpl(ssType, iMod, cb, lfo);
 928             case SST_GLOBAL_TYPES:
 929               return new DebugVC50SSGlobalTypesImpl(ssType, iMod, cb, lfo);
 930             case SST_MPC:
 931               return new DebugVC50SSMPCImpl(ssType, iMod, cb, lfo);
 932             case SST_SEG_MAP:
 933               return new DebugVC50SSSegMapImpl(ssType, iMod, cb, lfo);
 934             case SST_SEG_NAME:
 935               return new DebugVC50SSSegNameImpl(ssType, iMod, cb, lfo);
 936             case SST_PRE_COMP:
 937               return new DebugVC50SSPreCompImpl(ssType, iMod, cb, lfo);
 938             case SST_UNUSED:
 939               return null;
 940             case SST_OFFSET_MAP_16:
 941               return new DebugVC50SSOffsetMap16Impl(ssType, iMod, cb, lfo);
 942             case SST_OFFSET_MAP_32:
 943               return new DebugVC50SSOffsetMap32Impl(ssType, iMod, cb, lfo);
 944             case SST_FILE_INDEX:
 945               return new DebugVC50SSFileIndexImpl(ssType, iMod, cb, lfo);
 946             case SST_STATIC_SYM:
 947               return new DebugVC50SSStaticSymImpl(ssType, iMod, cb, lfo);
 948             default:
 949               throw new COFFException("Unknown section type " + ssType);
 950             }
 951           }
 952         }
 953 
 954         ////////////////////////////////////
 955         //                                //
 956         // Implementations of subsections //
 957         //                                //
 958         ////////////////////////////////////
 959 
 960         class DebugVC50SubsectionImpl implements DebugVC50Subsection {
 961           private short ssType;
 962           private short iMod;
 963           private int   ssSize;
 964 
 965           DebugVC50SubsectionImpl(short ssType, short iMod, int ssSize, int offset) {
 966             this.ssType = ssType;
 967             this.iMod   = iMod;
 968             this.ssSize = ssSize;
 969           }
 970 
 971           public short getSubsectionType()        { return ssType; }
 972           public short getSubsectionModuleIndex() { return iMod; }
 973           public int   getSubsectionSize()        { return ssSize; }
 974         }
 975 
 976         class DebugVC50SSModuleImpl extends DebugVC50SubsectionImpl implements DebugVC50SSModule {
 977           private int offset;
 978           private short ovlNumber;
 979           private short iLib;
 980           private short cSeg;
 981           private short style;
 982           private MemoizedObject segInfo;
 983           private MemoizedObject name;
 984 
 985           private static final int HEADER_SIZE   = 8;
 986           private static final int SEG_INFO_SIZE = 12;
 987 
 988           DebugVC50SSModuleImpl(short ssType, short iMod, int ssSize, final int offset) {
 989             super(ssType, iMod, ssSize, offset);
 990             this.offset = offset;
 991             seek(offset);
 992             ovlNumber = readShort();
 993             iLib      = readShort();
 994             cSeg      = readShort();
 995             style     = readShort();
 996             segInfo   = new MemoizedObject() {
 997                 public Object computeValue() {
 998                   int base = offset + HEADER_SIZE;
 999                   DebugVC50SegInfo[] res = new DebugVC50SegInfo[cSeg];
1000                   for (int i = 0; i < cSeg; i++) {
1001                     res[i] = new DebugVC50SegInfoImpl(base);
1002                     base += SEG_INFO_SIZE;
1003                   }
1004                   return res;
1005                 }
1006               };
1007             name      = new MemoizedObject() {
1008                 public Object computeValue() {
1009                   return readLengthPrefixedStringAt(offset + (HEADER_SIZE + cSeg * SEG_INFO_SIZE));
1010                 }
1011               };
1012           }
1013 
1014           public short getOverlayNumber()   { return ovlNumber; }
1015           public short getLibrariesIndex()  { return iLib; }
1016           public short getNumCodeSegments() { return cSeg; }
1017           public short getDebuggingStyle()  { return style; }
1018           public DebugVC50SegInfo getSegInfo(int i) { return ((DebugVC50SegInfo[]) segInfo.getValue())[i]; }
1019           public String getName()           { return (String) name.getValue(); }
1020         }
1021 
1022         class DebugVC50SegInfoImpl implements DebugVC50SegInfo {
1023           private short seg;
1024           private int   offset;
1025           private int   cbSeg;
1026 
1027           DebugVC50SegInfoImpl(int offset) {
1028             seek(offset);
1029             seg = readShort();
1030             readShort(); // pad
1031             offset = readInt();
1032             cbSeg = readInt();
1033           }
1034 
1035           public short getSegment() { return seg; }
1036           public int getOffset() { return offset; }
1037           public int getSegmentCodeSize() { return cbSeg; }
1038         }
1039 
1040         class DebugVC50SSTypesImpl extends DebugVC50SubsectionImpl implements DebugVC50SSTypes {
1041           DebugVC50SSTypesImpl(short ssType, short iMod, int ssSize, int offset) {
1042             super(ssType, iMod, ssSize, offset);
1043           }
1044         }
1045 
1046         class DebugVC50SSPublicImpl extends DebugVC50SubsectionImpl implements DebugVC50SSPublic {
1047           DebugVC50SSPublicImpl(short ssType, short iMod, int ssSize, int offset) {
1048             super(ssType, iMod, ssSize, offset);
1049           }
1050         }
1051 
1052         class DebugVC50SSPublicSymImpl extends DebugVC50SubsectionImpl implements DebugVC50SSPublicSym {
1053           DebugVC50SSPublicSymImpl(short ssType, short iMod, int ssSize, int offset) {
1054             super(ssType, iMod, ssSize, offset);
1055           }
1056         }
1057 
1058         class DebugVC50SSSymbolsImpl extends DebugVC50SubsectionImpl implements DebugVC50SSSymbols {
1059           DebugVC50SSSymbolsImpl(short ssType, short iMod, int ssSize, int offset) {
1060             super(ssType, iMod, ssSize, offset);
1061           }
1062         }
1063 
1064         class DebugVC50SSAlignSymImpl extends DebugVC50SubsectionImpl implements DebugVC50SSAlignSym {
1065           private int offset;
1066 
1067           DebugVC50SSAlignSymImpl(short ssType, short iMod, int ssSize, int offset) {
1068             super(ssType, iMod, ssSize, offset);
1069             this.offset = offset;
1070           }
1071 
1072           public DebugVC50SymbolIterator getSymbolIterator() {
1073             return new DebugVC50SymbolIteratorImpl(offset, getSubsectionSize());
1074           }
1075         }
1076 
1077         class DebugVC50SSSrcLnSegImpl extends DebugVC50SubsectionImpl implements DebugVC50SSSrcLnSeg {
1078           DebugVC50SSSrcLnSegImpl(short ssType, short iMod, int ssSize, int offset) {
1079             super(ssType, iMod, ssSize, offset);
1080           }
1081         }
1082 
1083         class DebugVC50SSSrcModuleImpl extends DebugVC50SubsectionImpl implements DebugVC50SSSrcModule {
1084           private int offset;
1085           private short cFile;
1086           private short cSeg;
1087           private MemoizedObject baseSrcFiles;
1088           private MemoizedObject segOffsets;
1089           private MemoizedObject segs;
1090 
1091           DebugVC50SSSrcModuleImpl(short ssType, short iMod, int ssSize, final int offset) {
1092             super(ssType, iMod, ssSize, offset);
1093 
1094             this.offset = offset;
1095             seek(offset);
1096             cFile = readShort();
1097             cSeg  = readShort();
1098 
1099             baseSrcFiles = new MemoizedObject() {
1100                 public Object computeValue() {
1101                   int[] offsets = new int[getNumSourceFiles()];
1102                   seek(offset + 4);
1103                   for (int i = 0; i < getNumSourceFiles(); i++) {
1104                     offsets[i] = offset + readInt();
1105                   }
1106                   DebugVC50SrcModFileDescImpl[] res = new DebugVC50SrcModFileDescImpl[offsets.length];
1107                   for (int i = 0; i < res.length; i++) {
1108                     res[i] = new DebugVC50SrcModFileDescImpl(offsets[i], offset);
1109                   }
1110                   return res;
1111                 }
1112               };
1113 
1114             segOffsets = new MemoizedObject() {
1115                 public Object computeValue() {
1116                   seek(offset + 4 * (getNumSourceFiles() + 1));
1117                   int[] res = new int[2 * getNumCodeSegments()];
1118                   for (int i = 0; i < 2 * getNumCodeSegments(); i++) {
1119                     res[i] = readInt();
1120                   }
1121                   return res;
1122                 }
1123               };
1124 
1125             segs = new MemoizedObject() {
1126                 public Object computeValue() {
1127                   seek(offset + 4 * (getNumSourceFiles() + 1) + 8 * getNumCodeSegments());
1128                   short[] res = new short[getNumCodeSegments()];
1129                   for (int i = 0; i < getNumCodeSegments(); i++) {
1130                     res[i] = readShort();
1131                   }
1132                   return res;
1133                 }
1134               };
1135           }
1136 
1137           public int getNumSourceFiles()  { return cFile & 0xFFFF; }
1138           public int getNumCodeSegments() { return cSeg & 0xFFFF;  }
1139           public DebugVC50SrcModFileDesc getSourceFileDesc(int i) {
1140             return ((DebugVC50SrcModFileDescImpl[]) baseSrcFiles.getValue())[i];
1141           }
1142 
1143           public int getSegmentStartOffset(int i) {
1144             return ((int[]) segOffsets.getValue())[2*i];
1145           }
1146 
1147           public int getSegmentEndOffset(int i) {
1148             return ((int[]) segOffsets.getValue())[2*i+1];
1149           }
1150 
1151           public int getSegment(int i) {
1152             return ((short[]) segs.getValue())[i] & 0xFFFF;
1153           }
1154         }
1155 
1156         class DebugVC50SrcModFileDescImpl implements DebugVC50SrcModFileDesc {
1157           private short cSeg;
1158           private MemoizedObject baseSrcLn;
1159           private MemoizedObject segOffsets;
1160           private MemoizedObject name;
1161 
1162           DebugVC50SrcModFileDescImpl(final int offset, final int baseOffset) {
1163             seek(offset);
1164             cSeg = readShort();
1165 
1166             baseSrcLn = new MemoizedObject() {
1167                 public Object computeValue() {
1168                   seek(offset + 4);
1169                   int[] offsets = new int[getNumCodeSegments()];
1170                   for (int i = 0; i < getNumCodeSegments(); i++) {
1171                     offsets[i] = baseOffset + readInt();
1172                   }
1173                   DebugVC50SrcModLineNumberMapImpl[] res =
1174                     new DebugVC50SrcModLineNumberMapImpl[getNumCodeSegments()];
1175                   for (int i = 0; i < getNumCodeSegments(); i++) {
1176                     res[i] = new DebugVC50SrcModLineNumberMapImpl(offsets[i]);
1177                   }
1178                   return res;
1179                 }
1180               };
1181 
1182             segOffsets = new MemoizedObject() {
1183                 public Object computeValue() {
1184                   seek(offset + 4 * (getNumCodeSegments() + 1));
1185                   int[] res = new int[2 * getNumCodeSegments()];
1186                   for (int i = 0; i < 2 * getNumCodeSegments(); i++) {
1187                     res[i] = readInt();
1188                   }
1189                   return res;
1190                 }
1191               };
1192 
1193             name = new MemoizedObject() {
1194                 public Object computeValue() {
1195                   seek(offset + 4 + 12 * getNumCodeSegments());
1196                   // NOTE: spec says name length is two bytes, but it's really one
1197                   int cbName = readByte() & 0xFF;
1198                   byte[] res = new byte[cbName];
1199                   readBytes(res);
1200                   try {
1201                     return new String(res, US_ASCII);
1202                   } catch (UnsupportedEncodingException e) {
1203                     throw new COFFException(e);
1204                   }
1205                 }
1206               };
1207           }
1208 
1209           public int getNumCodeSegments() { return cSeg & 0xFFFF; }
1210 
1211           public DebugVC50SrcModLineNumberMap getLineNumberMap(int i) {
1212             return ((DebugVC50SrcModLineNumberMapImpl[]) baseSrcLn.getValue())[i];
1213           }
1214 
1215           public int getSegmentStartOffset(int i) {
1216             return ((int[]) segOffsets.getValue())[2*i];
1217           }
1218 
1219           public int getSegmentEndOffset(int i) {
1220             return ((int[]) segOffsets.getValue())[2*i+1];
1221           }
1222 
1223           public String getSourceFileName() {
1224             return (String) name.getValue();
1225           }
1226         }
1227 
1228         class DebugVC50SrcModLineNumberMapImpl implements DebugVC50SrcModLineNumberMap {
1229           private short seg;
1230           private short cPair;
1231           private MemoizedObject offsets;
1232           private MemoizedObject lineNumbers;
1233 
1234           DebugVC50SrcModLineNumberMapImpl(final int offset) {
1235             seek(offset);
1236             seg = readShort();
1237             cPair = readShort();
1238             offsets = new MemoizedObject() {
1239                 public Object computeValue() {
1240                   seek(offset + 4);
1241                   int[] res = new int[getNumSourceLinePairs()];
1242                   for (int i = 0; i < getNumSourceLinePairs(); i++) {
1243                     res[i] = readInt();
1244                   }
1245                   return res;
1246                 }
1247               };
1248 
1249             lineNumbers = new MemoizedObject() {
1250                 public Object computeValue() {
1251                   seek(offset + 4 * (getNumSourceLinePairs() + 1));
1252                   short[] res = new short[getNumSourceLinePairs()];
1253                   for (int i = 0; i < getNumSourceLinePairs(); i++) {
1254                     res[i] = readShort();
1255                   }
1256                   return res;
1257                 }
1258               };
1259           }
1260 
1261           public int getSegment() { return seg; }
1262           public int getNumSourceLinePairs() { return cPair; }
1263           public int getCodeOffset(int i) {
1264             return ((int[]) offsets.getValue())[i];
1265           }
1266           public int getLineNumber(int i) {
1267             return ((short[]) lineNumbers.getValue())[i] & 0xFFFF;
1268           }
1269         }
1270 
1271         class DebugVC50SSLibrariesImpl extends DebugVC50SubsectionImpl implements DebugVC50SSLibraries {
1272           DebugVC50SSLibrariesImpl(short ssType, short iMod, int ssSize, int offset) {
1273             super(ssType, iMod, ssSize, offset);
1274           }
1275 
1276           // FIXME: NOT FINISHED
1277         }
1278 
1279         class DebugVC50SSSymbolBaseImpl extends DebugVC50SubsectionImpl implements DebugVC50SSSymbolBase {
1280           private int   offset;
1281           private short symHash;
1282           private short addrHash;
1283           private int   cbSymbol;
1284           private int   cbSymHash;
1285           private int   cbAddrHash;
1286 
1287           private static final int HEADER_SIZE = 16;
1288 
1289           DebugVC50SSSymbolBaseImpl(short ssType, short iMod, int ssSize, int offset) {
1290             super(ssType, iMod, ssSize, offset);
1291             this.offset = offset;
1292             seek(offset);
1293             symHash    = readShort();
1294             addrHash   = readShort();
1295             cbSymbol   = readInt();
1296             cbSymHash  = readInt();
1297             cbAddrHash = readInt();
1298           }
1299 
1300           public short getSymHashIndex()  { return symHash; }
1301           public short getAddrHashIndex() { return addrHash; }
1302           public int getSymTabSize()      { return cbSymbol; }
1303           public int getSymHashSize()     { return cbSymHash; }
1304           public int getAddrHashSize()    { return cbAddrHash; }
1305 
1306           public DebugVC50SymbolIterator getSymbolIterator() {
1307             return new DebugVC50SymbolIteratorImpl(offset + HEADER_SIZE, cbSymbol);
1308           }
1309         }
1310 
1311         class DebugVC50SSGlobalSymImpl extends DebugVC50SSSymbolBaseImpl implements DebugVC50SSGlobalSym {
1312           DebugVC50SSGlobalSymImpl(short ssType, short iMod, int ssSize, int offset) {
1313             super(ssType, iMod, ssSize, offset);
1314           }
1315         }
1316         class DebugVC50SSGlobalPubImpl extends DebugVC50SSSymbolBaseImpl implements DebugVC50SSGlobalPub {
1317           DebugVC50SSGlobalPubImpl(short ssType, short iMod, int ssSize, int offset) {
1318             super(ssType, iMod, ssSize, offset);
1319           }
1320         }
1321 
1322         class DebugVC50SSGlobalTypesImpl extends DebugVC50SubsectionImpl implements DebugVC50SSGlobalTypes {
1323           private int offset;
1324           private int cType;
1325 
1326           DebugVC50SSGlobalTypesImpl(short ssType, short iMod, int ssSize, int offset) {
1327             super(ssType, iMod, ssSize, offset);
1328             this.offset = offset;
1329             seek(offset);
1330             readInt(); // Discard "flags"
1331             cType = readInt();
1332           }
1333 
1334           public int getNumTypes() { return cType; }
1335           // FIXME: should memoize these
1336           public int getTypeOffset(int i) {
1337             seek(offset + 4 * (i + 2));
1338             return readInt() + offsetOfFirstType();
1339           }
1340 
1341           public DebugVC50TypeIterator getTypeIterator() {
1342             return new DebugVC50TypeIteratorImpl(this,
1343                                                  offsetOfFirstType(),
1344                                                  cType);
1345           }
1346 
1347           private int offsetOfFirstType() {
1348             return offset + 4 * (getNumTypes() + 2);
1349           }
1350         }
1351 
1352         class DebugVC50SSMPCImpl extends DebugVC50SubsectionImpl implements DebugVC50SSMPC {
1353           DebugVC50SSMPCImpl(short ssType, short iMod, int ssSize, int offset) {
1354             super(ssType, iMod, ssSize, offset);
1355           }
1356         }
1357 
1358         class DebugVC50SSSegMapImpl extends DebugVC50SubsectionImpl implements DebugVC50SSSegMap {
1359           private short cSeg;
1360           private short cSegLog;
1361           private MemoizedObject segDescs;
1362 
1363           DebugVC50SSSegMapImpl(short ssType, short iMod, int ssSize, final int offset) {
1364             super(ssType, iMod, ssSize, offset);
1365             seek(offset);
1366             cSeg = readShort();
1367             cSegLog = readShort();
1368             segDescs = new MemoizedObject() {
1369                 public Object computeValue() {
1370                   DebugVC50SegDesc[] descs = new DebugVC50SegDesc[cSeg];
1371                   for (int i = 0; i < cSeg; i++) {
1372                     descs[i] = new DebugVC50SegDescImpl(offset + 4 + (20 * i));
1373                   }
1374                   return descs;
1375                 }
1376               };
1377           }
1378 
1379           public short getNumSegDesc() { return cSeg; }
1380           public short getNumLogicalSegDesc() { return cSegLog; }
1381           public DebugVC50SegDesc getSegDesc(int i) { return ((DebugVC50SegDesc[]) segDescs.getValue())[i]; }
1382         }
1383 
1384         class DebugVC50SegDescImpl implements DebugVC50SegDesc {
1385           private short flags;
1386           private short ovl;
1387           private short group;
1388           private short frame;
1389           private short iSegName;
1390           private short iClassName;
1391           private int   offset;
1392           private int   cbSeg;
1393 
1394           DebugVC50SegDescImpl(int offset) {
1395             seek(offset);
1396             flags = readShort();
1397             ovl = readShort();
1398             group = readShort();
1399             frame = readShort();
1400             iSegName = readShort();
1401             iClassName = readShort();
1402             offset = readInt();
1403             cbSeg = readInt();
1404           }
1405 
1406           public short getFlags() { return flags; }
1407           public short getOverlayNum() { return ovl; }
1408           public short getGroup() { return group; }
1409           public short getFrame() { return frame; }
1410           public short getName() { return iSegName; }
1411           public short getClassName() { return iClassName; }
1412           public int   getOffset() { return offset; }
1413           public int   getSize() { return cbSeg; }
1414         }
1415 
1416 
1417         class DebugVC50SSSegNameImpl extends DebugVC50SubsectionImpl implements DebugVC50SSSegName {
1418           private int offset;
1419           private int size;
1420           private MemoizedObject names;
1421 
1422           DebugVC50SSSegNameImpl(short ssType, short iMod, int ssSize, int offset) {
1423             super(ssType, iMod, ssSize, offset);
1424             this.offset = offset;
1425             this.size   = ssSize;
1426             seek(offset);
1427             names = new MemoizedObject() {
1428                 public Object computeValue() {
1429                   int i = 0;
1430                   List data = new ArrayList();
1431                   while (i < size) {
1432                     String s = readCString();
1433                     data.add(s);
1434                     i += s.length();
1435                   }
1436                   String[] res = new String[data.size()];
1437                   res = (String[]) data.toArray(res);
1438                   return res;
1439                 }
1440               };
1441           }
1442 
1443           public String getSegName(int i) {
1444             return ((String[]) names.getValue())[i];
1445           }
1446         }
1447 
1448         class DebugVC50SSPreCompImpl extends DebugVC50SubsectionImpl implements DebugVC50SSPreComp {
1449           DebugVC50SSPreCompImpl(short ssType, short iMod, int ssSize, int offset) {
1450             super(ssType, iMod, ssSize, offset);
1451           }
1452         }
1453 
1454         class DebugVC50SSOffsetMap16Impl extends DebugVC50SubsectionImpl implements DebugVC50SSOffsetMap16 {
1455           DebugVC50SSOffsetMap16Impl(short ssType, short iMod, int ssSize, int offset) {
1456             super(ssType, iMod, ssSize, offset);
1457           }
1458         }
1459 
1460         class DebugVC50SSOffsetMap32Impl extends DebugVC50SubsectionImpl implements DebugVC50SSOffsetMap32 {
1461           DebugVC50SSOffsetMap32Impl(short ssType, short iMod, int ssSize, int offset) {
1462             super(ssType, iMod, ssSize, offset);
1463           }
1464         }
1465 
1466         class DebugVC50SSFileIndexImpl extends DebugVC50SubsectionImpl implements DebugVC50SSFileIndex {
1467           private int offset;
1468           private short cMod; // Number of modules in the executable
1469           private short cRef; // Total number of file name references
1470           private MemoizedObject modStart;
1471           private MemoizedObject cRefCnt;
1472           // FIXME: probably useless; needs fixup to be converted into
1473           // indices rather than offsets
1474           private MemoizedObject nameRef;
1475           private MemoizedObject names;
1476 
1477           DebugVC50SSFileIndexImpl(short ssType, short iMod, int ssSize, final int offset) {
1478             super(ssType, iMod, ssSize, offset);
1479             this.offset = offset;
1480             seek(offset);
1481             cMod = readShort();
1482             cRef = readShort();
1483             modStart = new MemoizedObject() {
1484                 public Object computeValue() {
1485                   short[] vals = new short[cMod];
1486                   seek(4 + offset);
1487                   for (int i = 0; i < cMod; i++) {
1488                     vals[i] = readShort();
1489                   }
1490                   return vals;
1491                 }
1492               };
1493             cRefCnt = new MemoizedObject() {
1494                 public Object computeValue() {
1495                   short[] vals = new short[cMod];
1496                   seek(4 + offset + (2 * cMod));
1497                   for (int i = 0; i < cMod; i++) {
1498                     vals[i] = readShort();
1499                   }
1500                   return vals;
1501                 }
1502               };
1503             nameRef = new MemoizedObject() {
1504                 public Object computeValue() {
1505                   int[] vals = new int[cRef];
1506                   seek(4 + offset + (4 * cMod));
1507                   for (int i = 0; i < cMod; i++) {
1508                     vals[i] = readInt();
1509                   }
1510                   return vals;
1511                 }
1512               };
1513             names = new MemoizedObject() {
1514                 public Object computeValue() {
1515                   String[] vals = new String[cRef];
1516                   for (int i = 0; i < cRef; i++) {
1517                     vals[i] = readCString();
1518                   }
1519                   return vals;
1520                 }
1521               };
1522           }
1523 
1524           public short getNumModules()    { return cMod; }
1525           public short getNumReferences() { return cRef; }
1526           public short[] getModStart()    { return (short[]) modStart.getValue(); }
1527           public short[] getRefCount()    { return (short[]) cRefCnt.getValue(); }
1528           public int[] getNameRef()       { return (int[]) nameRef.getValue(); }
1529           public String[] getNames()      { return (String[]) names.getValue(); }
1530         }
1531 
1532         class DebugVC50SSStaticSymImpl extends DebugVC50SSSymbolBaseImpl implements DebugVC50SSStaticSym {
1533           DebugVC50SSStaticSymImpl(short ssType, short iMod, int ssSize, int offset) {
1534             super(ssType, iMod, ssSize, offset);
1535           }
1536         }
1537 
1538         //////////////////////////////////////////////////
1539         //                                              //
1540         // Implementations of symbol and type iterators //
1541         //                                              //
1542         //////////////////////////////////////////////////
1543 
1544         class DebugVC50SymbolIteratorImpl implements DebugVC50SymbolIterator {
1545           private int base;
1546           private int size;
1547           private int pos;
1548           private int curSymSize;
1549           private int curSymType;
1550 
1551           private static final int HEADER_SIZE = 4;
1552 
1553           DebugVC50SymbolIteratorImpl(int base, int size) {
1554             this(base, size, base);
1555           }
1556 
1557           private DebugVC50SymbolIteratorImpl(int base, int size, int pos) {
1558             this.base = base;
1559             this.size = size;
1560             this.pos = pos;
1561             seek(pos);
1562             curSymSize = readShort() & 0xFFFF;
1563             curSymType = readShort() & 0xFFFF;
1564           }
1565 
1566           public boolean done() {
1567             return (pos == (base + size));
1568           }
1569 
1570           public void next() throws NoSuchElementException {
1571             if (done()) throw new NoSuchElementException("No more symbols");
1572             pos += curSymSize + 2;
1573             seek(pos);
1574             curSymSize = readShort() & 0xFFFF;
1575             curSymType = readShort() & 0xFFFF;
1576           }
1577 
1578           public short getLength() {
1579             return (short) curSymSize;
1580           }
1581 
1582           public int getType() {
1583             return curSymType;
1584           }
1585 
1586           public int getOffset() {
1587             return pos + HEADER_SIZE;
1588           }
1589 
1590           /////////////////////////
1591           // S_COMPILE accessors //
1592           /////////////////////////
1593 
1594           public byte getCompilerTargetProcessor() {
1595             symSeek(0);
1596             return readByte();
1597           }
1598 
1599           public int getCompilerFlags() {
1600             symSeek(1);
1601             int res = 0;
1602             for (int i = 0; i < 3; i++) {
1603               int b = readByte() & 0xFF;
1604               res = (res << 8) | b;
1605             }
1606             return res;
1607           }
1608 
1609           public String getComplierVersion() {
1610             return readLengthPrefixedStringAt(4);
1611           }
1612 
1613           //////////////////////////
1614           // S_REGISTER accessors //
1615           //////////////////////////
1616 
1617           public int getRegisterSymbolType() {
1618             symSeek(0);
1619             return readInt();
1620           }
1621 
1622           public short getRegisterEnum() {
1623             symSeek(4);
1624             return readShort();
1625           }
1626 
1627           public String getRegisterSymbolName() {
1628             return readLengthPrefixedStringAt(6);
1629           }
1630 
1631           //////////////////////////
1632           // S_CONSTANT accessors //
1633           //////////////////////////
1634 
1635           public int getConstantType() {
1636             symSeek(0);
1637             return readInt();
1638           }
1639 
1640           public int getConstantValueAsInt() throws DebugVC50WrongNumericTypeException {
1641             return readIntNumericLeafAt(4);
1642           }
1643 
1644           public long getConstantValueAsLong() throws DebugVC50WrongNumericTypeException {
1645             return readLongNumericLeafAt(4);
1646           }
1647 
1648           public float getConstantValueAsFloat() throws DebugVC50WrongNumericTypeException {
1649             return readFloatNumericLeafAt(4);
1650           }
1651 
1652           public double getConstantValueAsDouble() throws DebugVC50WrongNumericTypeException {
1653             return readDoubleNumericLeafAt(4);
1654           }
1655 
1656           public String getConstantName() {
1657             return readLengthPrefixedStringAt(4 + numericLeafLengthAt(4));
1658           }
1659 
1660           /////////////////////
1661           // S_UDT accessors //
1662           /////////////////////
1663 
1664           public int getUDTType() {
1665             symSeek(0);
1666             return readInt();
1667           }
1668 
1669           public String getUDTName() {
1670             return readLengthPrefixedStringAt(4);
1671           }
1672 
1673           /////////////////////////
1674           // S_SSEARCH accessors //
1675           /////////////////////////
1676 
1677           public int getSearchSymbolOffset() {
1678             symSeek(0);
1679             return readInt();
1680           }
1681 
1682           public short getSearchSegment() {
1683             symSeek(4);
1684             return readShort();
1685           }
1686 
1687           /////////////////////
1688           // S_END accessors //
1689           /////////////////////
1690 
1691           // (No accessors)
1692 
1693           //////////////////////
1694           // S_SKIP accessors //
1695           //////////////////////
1696 
1697           // (No accessors)
1698 
1699           ///////////////////////////
1700           // S_CVRESERVE accessors //
1701           ///////////////////////////
1702 
1703           // (No accessors)
1704 
1705           /////////////////////////
1706           // S_OBJNAME accessors //
1707           /////////////////////////
1708 
1709           public int getObjectCodeViewSignature() {
1710             symSeek(0);
1711             return readInt();
1712           }
1713 
1714           public String getObjectName() {
1715             return readLengthPrefixedStringAt(4);
1716           }
1717 
1718           ////////////////////////
1719           // S_ENDARG accessors //
1720           ////////////////////////
1721 
1722           // (No accessors)
1723 
1724           //////////////////////////
1725           // S_COBOLUDT accessors //
1726           //////////////////////////
1727 
1728           // (Elided as they are irrelevant)
1729 
1730           /////////////////////////
1731           // S_MANYREG accessors //
1732           /////////////////////////
1733 
1734           public int getManyRegType() {
1735             symSeek(0);
1736             return readInt();
1737           }
1738 
1739           public byte getManyRegCount() {
1740             symSeek(4);
1741             return readByte();
1742           }
1743 
1744           public byte getManyRegRegister(int i) {
1745             symSeek(5 + i);
1746             return readByte();
1747           }
1748 
1749           public String getManyRegName() {
1750             return readLengthPrefixedStringAt(5 + getManyRegCount());
1751           }
1752 
1753           ////////////////////////
1754           // S_RETURN accessors //
1755           ////////////////////////
1756 
1757           public short getReturnFlags() {
1758             symSeek(0);
1759             return readShort();
1760           }
1761 
1762           public byte getReturnStyle() {
1763             symSeek(2);
1764             return readByte();
1765           }
1766 
1767           public byte getReturnRegisterCount() {
1768             symSeek(3);
1769             return readByte();
1770           }
1771 
1772           public byte getReturnRegister(int i) {
1773             symSeek(4 + i);
1774             return readByte();
1775           }
1776 
1777           ///////////////////////////
1778           // S_ENTRYTHIS accessors //
1779           ///////////////////////////
1780 
1781           public void advanceToEntryThisSymbol() {
1782             seek(pos + 4);
1783             int tmpSymSize = readShort();
1784             int tmpSymType = readShort();
1785             if (Assert.ASSERTS_ENABLED) {
1786               // Make sure that ends of inner and outer symbols line
1787               // up, otherwise need more work
1788               Assert.that(pos + curSymSize + 2 == pos + 4 + tmpSymSize,
1789                           "advanceToEntryThisSymbol needs more work");
1790             }
1791             pos += 4;
1792             curSymSize = tmpSymSize;
1793             curSymType = tmpSymType;
1794           }
1795 
1796           ///////////////////////////////////////////////////////////////////////
1797           //                                                                   //
1798           //                                                                   //
1799           // Symbols for (Intel) 16:32 Segmented and 32-bit Flat Architectures //
1800           //                                                                   //
1801           //                                                                   //
1802           ///////////////////////////////////////////////////////////////////////
1803 
1804           /////////////////////////
1805           // S_BPREL32 accessors //
1806           /////////////////////////
1807 
1808           public int getBPRelOffset() {
1809             symSeek(0);
1810             return readInt();
1811           }
1812 
1813           public int getBPRelType() {
1814             symSeek(4);
1815             return readInt();
1816           }
1817 
1818           public String getBPRelName() {
1819             return readLengthPrefixedStringAt(8);
1820           }
1821 
1822           ///////////////////////////////////////
1823           // S_LDATA32 and S_GDATA32 accessors //
1824           ///////////////////////////////////////
1825 
1826           public int getLGDataType() {
1827             symSeek(0);
1828             return readInt();
1829           }
1830 
1831           public int getLGDataOffset() {
1832             symSeek(4);
1833             return readInt();
1834           }
1835 
1836           public short getLGDataSegment() {
1837             symSeek(8);
1838             return readShort();
1839           }
1840 
1841           public String getLGDataName() {
1842             return readLengthPrefixedStringAt(10);
1843           }
1844 
1845           ///////////////////////
1846           // S_PUB32 accessors //
1847           ///////////////////////
1848 
1849           // FIXME: has the same format as the above; consider updating
1850           // documentation. No separate accessors provided.
1851 
1852           ///////////////////////////////////////
1853           // S_LPROC32 and S_GPROC32 accessors //
1854           ///////////////////////////////////////
1855 
1856           public DebugVC50SymbolIterator getLGProcParent() {
1857             int offs = getLGProcParentOffset();
1858             if (offs == 0) return null;
1859             return new DebugVC50SymbolIteratorImpl(base, size, offs);
1860           }
1861 
1862           public int getLGProcParentOffset() {
1863             symSeek(0);
1864             int offs = readInt();
1865             if (offs == 0) return 0;
1866             return base + offs;
1867           }
1868 
1869           public DebugVC50SymbolIterator getLGProcEnd() {
1870             int offs = getLGProcEndOffset();
1871             return new DebugVC50SymbolIteratorImpl(base, size, offs);
1872           }
1873 
1874           public int getLGProcEndOffset() {
1875             symSeek(4);
1876             int offs = readInt();
1877             if (Assert.ASSERTS_ENABLED) {
1878               Assert.that(offs != 0, "should not have null end offset for procedure symbols");
1879             }
1880             return base + offs;
1881           }
1882 
1883           public DebugVC50SymbolIterator getLGProcNext() {
1884             int offs = getLGProcNextOffset();
1885             if (offs == 0) return null;
1886             return new DebugVC50SymbolIteratorImpl(base, size, offs);
1887           }
1888 
1889           public int getLGProcNextOffset() {
1890             symSeek(8);
1891             int offs = readInt();
1892             if (offs == 0) return 0;
1893             return base + offs;
1894           }
1895 
1896           public int getLGProcLength() {
1897             symSeek(12);
1898             return readInt();
1899           }
1900 
1901           public int getLGProcDebugStart() {
1902             symSeek(16);
1903             return readInt();
1904           }
1905 
1906           public int getLGProcDebugEnd() {
1907             symSeek(20);
1908             return readInt();
1909           }
1910 
1911           public int getLGProcType() {
1912             symSeek(24);
1913             return readInt();
1914           }
1915 
1916           public int getLGProcOffset() {
1917             symSeek(28);
1918             return readInt();
1919           }
1920 
1921           public short getLGProcSegment() {
1922             symSeek(32);
1923             return readShort();
1924           }
1925 
1926           public byte getLGProcFlags() {
1927             symSeek(34);
1928             return readByte();
1929           }
1930 
1931           public String getLGProcName() {
1932             return readLengthPrefixedStringAt(35);
1933           }
1934 
1935           /////////////////////////
1936           // S_THUNK32 accessors //
1937           /////////////////////////
1938 
1939           public DebugVC50SymbolIterator getThunkParent() {
1940             int offs = getThunkParentOffset();
1941             if (offs == 0) return null;
1942             return new DebugVC50SymbolIteratorImpl(base, size, offs);
1943           }
1944 
1945           public int getThunkParentOffset() {
1946             symSeek(0);
1947             int offs = readInt();
1948             if (offs == 0) return 0;
1949             return base + offs;
1950           }
1951 
1952           public DebugVC50SymbolIterator getThunkEnd() {
1953             symSeek(4);
1954             int offs = readInt();
1955             return new DebugVC50SymbolIteratorImpl(base, size, offs);
1956           }
1957 
1958           public int getThunkEndOffset() {
1959             symSeek(4);
1960             int offs = readInt();
1961             if (Assert.ASSERTS_ENABLED) {
1962               Assert.that(offs != 0, "should not have null end offset for thunk symbols");
1963             }
1964             return base + offs;
1965           }
1966 
1967           public DebugVC50SymbolIterator getThunkNext() {
1968             int offs = getThunkNextOffset();
1969             if (offs == 0) return null;
1970             return new DebugVC50SymbolIteratorImpl(base, size, base + offs);
1971           }
1972 
1973           public int getThunkNextOffset() {
1974             symSeek(8);
1975             int offs = readInt();
1976             if (offs == 0) return 0;
1977             return base + offs;
1978           }
1979 
1980           public int getThunkOffset() {
1981             symSeek(12);
1982             return readInt();
1983           }
1984 
1985           public short getThunkSegment() {
1986             symSeek(16);
1987             return readShort();
1988           }
1989 
1990           public short getThunkLength() {
1991             symSeek(18);
1992             return readShort();
1993           }
1994 
1995           public byte getThunkType() {
1996             symSeek(20);
1997             return readByte();
1998           }
1999 
2000           public String getThunkName() {
2001             return readLengthPrefixedStringAt(21);
2002           }
2003 
2004           public short getThunkAdjustorThisDelta() {
2005             symSeek(21 + lengthPrefixedStringLengthAt(21));
2006             return readShort();
2007           }
2008 
2009           public String getThunkAdjustorTargetName() {
2010             return readLengthPrefixedStringAt(23 + lengthPrefixedStringLengthAt(21));
2011           }
2012 
2013           public short getThunkVCallDisplacement() {
2014             symSeek(21 + lengthPrefixedStringLengthAt(21));
2015             return readShort();
2016           }
2017 
2018           public int getThunkPCodeOffset() {
2019             symSeek(21 + lengthPrefixedStringLengthAt(21));
2020             return readInt();
2021           }
2022 
2023           public short getThunkPCodeSegment() {
2024             symSeek(25 + lengthPrefixedStringLengthAt(21));
2025             return readShort();
2026           }
2027 
2028           /////////////////////////
2029           // S_BLOCK32 accessors //
2030           /////////////////////////
2031 
2032           public DebugVC50SymbolIterator getBlockParent() {
2033             int offs = getBlockParentOffset();
2034             if (offs == 0) return null;
2035             return new DebugVC50SymbolIteratorImpl(base, size, offs);
2036           }
2037 
2038           public int getBlockParentOffset() {
2039             symSeek(0);
2040             int offs = readInt();
2041             if (offs == 0) return 0;
2042             return base + offs;
2043           }
2044 
2045           public DebugVC50SymbolIterator getBlockEnd() {
2046             symSeek(4);
2047             int offs = readInt();
2048             return new DebugVC50SymbolIteratorImpl(base, size, offs);
2049           }
2050 
2051           public int getBlockEndOffset() {
2052             symSeek(4);
2053             int offs = readInt();
2054             if (Assert.ASSERTS_ENABLED) {
2055               Assert.that(offs != 0, "should not have null end offset for block symbols");
2056             }
2057             return base + offs;
2058           }
2059 
2060           public int getBlockLength() {
2061             symSeek(8);
2062             return readInt();
2063           }
2064 
2065           public int getBlockOffset() {
2066             symSeek(12);
2067             return readInt();
2068           }
2069 
2070           public short getBlockSegment() {
2071             symSeek(16);
2072             return readShort();
2073           }
2074 
2075           public String getBlockName() {
2076             return readLengthPrefixedStringAt(18);
2077           }
2078 
2079           ////////////////////////
2080           // S_WITH32 accessors //
2081           ////////////////////////
2082 
2083           // FIXME: this is a Pascal construct; ignored for now
2084 
2085           /////////////////////////
2086           // S_LABEL32 accessors //
2087           /////////////////////////
2088 
2089           public int getLabelOffset() {
2090             symSeek(0);
2091             return readInt();
2092           }
2093 
2094           public short getLabelSegment() {
2095             symSeek(4);
2096             return readShort();
2097           }
2098 
2099           public byte getLabelFlags() {
2100             symSeek(6);
2101             return readByte();
2102           }
2103 
2104           public String getLabelName() {
2105             return readLengthPrefixedStringAt(7);
2106           }
2107 
2108           ////////////////////////////
2109           // S_CEXMODEL32 accessors //
2110           ////////////////////////////
2111 
2112           public int getChangeOffset() {
2113             symSeek(0);
2114             return readInt();
2115           }
2116 
2117           public short getChangeSegment() {
2118             symSeek(4);
2119             return readShort();
2120           }
2121 
2122           public short getChangeModel() {
2123             symSeek(6);
2124             return readShort();
2125           }
2126 
2127           ////////////////////////////
2128           // S_VFTTABLE32 accessors //
2129           ////////////////////////////
2130 
2131           public int getVTableRoot() {
2132             symSeek(0);
2133             return readInt();
2134           }
2135 
2136           public int getVTablePath() {
2137             symSeek(4);
2138             return readInt();
2139           }
2140 
2141           public int getVTableOffset() {
2142             symSeek(8);
2143             return readInt();
2144           }
2145 
2146           public short getVTableSegment() {
2147             symSeek(12);
2148             return readShort();
2149           }
2150 
2151           //////////////////////////
2152           // S_REGREL32 accessors //
2153           //////////////////////////
2154 
2155           public int getRegRelOffset() {
2156             symSeek(0);
2157             return readInt();
2158           }
2159 
2160           public int getRegRelType() {
2161             symSeek(4);
2162             return readInt();
2163           }
2164 
2165           public short getRegRelRegister() {
2166             symSeek(8);
2167             return readShort();
2168           }
2169 
2170           public String getRegRelName() {
2171             return readLengthPrefixedStringAt(10);
2172           }
2173 
2174           ///////////////////////////////////////////
2175           // S_LTHREAD32 and S_GTHREAD32 accessors //
2176           ///////////////////////////////////////////
2177 
2178           public int getLThreadType() {
2179             symSeek(0);
2180             return readInt();
2181           }
2182 
2183           public int getLThreadOffset() {
2184             symSeek(4);
2185             return readInt();
2186           }
2187 
2188           public short getLThreadSegment() {
2189             symSeek(8);
2190             return readShort();
2191           }
2192 
2193           public String getLThreadName() {
2194             return readLengthPrefixedStringAt(10);
2195           }
2196 
2197           //----------------------------------------------------------------------
2198           // Internals only below this point
2199           //
2200 
2201           private void symSeek(int offsetInSym) {
2202             seek(pos + HEADER_SIZE + offsetInSym);
2203           }
2204 
2205           private int numericLeafLengthAt(int offsetInSym) {
2206             return DebugVC50Impl.this.numericLeafLengthAt(pos + HEADER_SIZE + offsetInSym);
2207           }
2208 
2209           private int readIntNumericLeafAt(int offsetInSym) {
2210             return DebugVC50Impl.this.readIntNumericLeafAt(pos + HEADER_SIZE + offsetInSym);
2211           }
2212 
2213           private long readLongNumericLeafAt(int offsetInSym) {
2214             return DebugVC50Impl.this.readLongNumericLeafAt(pos + HEADER_SIZE + offsetInSym);
2215           }
2216 
2217           private float readFloatNumericLeafAt(int offsetInSym) {
2218             return DebugVC50Impl.this.readFloatNumericLeafAt(pos + HEADER_SIZE + offsetInSym);
2219           }
2220 
2221           private double readDoubleNumericLeafAt(int offsetInSym) {
2222             return DebugVC50Impl.this.readDoubleNumericLeafAt(pos + HEADER_SIZE + offsetInSym);
2223           }
2224 
2225           private int lengthPrefixedStringLengthAt(int offsetInSym) {
2226             return DebugVC50Impl.this.lengthPrefixedStringLengthAt(pos + HEADER_SIZE + offsetInSym);
2227           }
2228 
2229           private String readLengthPrefixedStringAt(int offsetInSym) {
2230             return DebugVC50Impl.this.readLengthPrefixedStringAt(pos + HEADER_SIZE + offsetInSym);
2231           }
2232         }
2233 
2234         class DebugVC50TypeIteratorImpl implements DebugVC50TypeIterator,
2235                         DebugVC50TypeLeafIndices, DebugVC50MemberAttributes, DebugVC50TypeEnums {
2236           private DebugVC50SSGlobalTypes parent;
2237           private int   base;
2238           private int   numTypes;
2239           private int   typeIndex;
2240           private int   typeRecordOffset;
2241           private int   typeStringOffset;
2242           private int   typeRecordSize;
2243           private int   typeStringLeaf;
2244 
2245           DebugVC50TypeIteratorImpl(DebugVC50SSGlobalTypes parent, int base, int numTypes) {
2246             this(parent, base, numTypes, 0, base);
2247           }
2248 
2249           private DebugVC50TypeIteratorImpl(DebugVC50SSGlobalTypes parent, int base, int numTypes, int curType, int offset) {
2250             this.parent = parent;
2251             this.base = base;
2252             this.numTypes = numTypes;
2253             this.typeIndex = curType;
2254             if (!done()) {
2255               typeRecordOffset = offset;
2256               loadTypeRecord();
2257             }
2258           }
2259 
2260           public boolean done() {
2261             return (typeIndex == numTypes);
2262           }
2263 
2264           public void next() throws NoSuchElementException {
2265             if (done()) throw new NoSuchElementException();
2266             ++typeIndex;
2267             if (!done()) {
2268               typeRecordOffset = parent.getTypeOffset(typeIndex);
2269               loadTypeRecord();
2270             }
2271           }
2272 
2273           public short getLength() {
2274             return (short) typeRecordSize;
2275           }
2276 
2277           public int getTypeIndex() {
2278             return biasTypeIndex(typeIndex);
2279           }
2280 
2281           public int getNumTypes() {
2282             return numTypes;
2283           }
2284 
2285           public boolean typeStringDone() {
2286             return (typeStringOffset - typeRecordOffset - 2) >= typeRecordSize;
2287           }
2288 
2289           public void typeStringNext() throws NoSuchElementException {
2290             if (typeStringDone()) throw new NoSuchElementException();
2291             typeStringOffset += typeStringLength();
2292             loadTypeString();
2293           }
2294 
2295           public int typeStringLeaf() {
2296             return typeStringLeaf;
2297           }
2298 
2299           public int typeStringOffset() {
2300             return typeStringOffset;
2301           }
2302 
2303           ///////////////////////////
2304           // LF_MODIFIER accessors //
2305           ///////////////////////////
2306 
2307           public int getModifierIndex() {
2308             typeSeek(2);
2309             return readInt();
2310           }
2311 
2312           public short getModifierAttribute() {
2313             typeSeek(6);
2314             return readShort();
2315           }
2316 
2317           //////////////////////////
2318           // LF_POINTER accessors //
2319           //////////////////////////
2320 
2321           public int getPointerType() {
2322             typeSeek(2);
2323             return readInt();
2324           }
2325 
2326           public int getPointerAttributes() {
2327             typeSeek(6);
2328             return readInt();
2329           }
2330 
2331           public int getPointerBasedOnTypeIndex() {
2332             typeSeek(10);
2333             return readInt();
2334           }
2335 
2336           public String getPointerBasedOnTypeName() {
2337             return readLengthPrefixedStringAt(14);
2338           }
2339 
2340           public int getPointerToMemberClass() {
2341             typeSeek(10);
2342             return readInt();
2343           }
2344 
2345           public short getPointerToMemberFormat() {
2346             typeSeek(14);
2347             return readShort();
2348           }
2349 
2350           ////////////////////////
2351           // LF_ARRAY accessors //
2352           ////////////////////////
2353 
2354           public int getArrayElementType() {
2355             typeSeek(2);
2356             return readInt();
2357           }
2358 
2359           public int getArrayIndexType() {
2360             typeSeek(6);
2361             return readInt();
2362           }
2363 
2364           public int getArrayLength() throws DebugVC50WrongNumericTypeException {
2365             return readIntNumericLeafAt(10);
2366           }
2367 
2368           public String getArrayName() {
2369             return readLengthPrefixedStringAt(10 + numericLeafLengthAt(10));
2370           }
2371 
2372           /////////////////////////////////////////
2373           // LF_CLASS and LF_STRUCTURE accessors //
2374           /////////////////////////////////////////
2375 
2376           public short getClassCount() {
2377             typeSeek(2);
2378             return readShort();
2379           }
2380 
2381           public short getClassProperty() {
2382             typeSeek(4);
2383             return readShort();
2384           }
2385 
2386           public int getClassFieldList() {
2387             typeSeek(6);
2388             return readInt();
2389           }
2390 
2391           public DebugVC50TypeIterator getClassFieldListIterator() {
2392             int index = unbiasTypeIndex(getClassFieldList());
2393             int offset = parent.getTypeOffset(index);
2394             return new DebugVC50TypeIteratorImpl(parent, base, numTypes, index, offset);
2395           }
2396 
2397           public int getClassDerivationList() {
2398             typeSeek(10);
2399             return readInt();
2400           }
2401 
2402           public int getClassVShape() {
2403             typeSeek(14);
2404             return readInt();
2405           }
2406 
2407           public int getClassSize() throws DebugVC50WrongNumericTypeException {
2408             return readIntNumericLeafAt(18);
2409           }
2410 
2411           public String getClassName() {
2412             return readLengthPrefixedStringAt(18 + numericLeafLengthAt(18));
2413           }
2414 
2415           ////////////////////////
2416           // LF_UNION accessors //
2417           ////////////////////////
2418 
2419           public short getUnionCount() {
2420             typeSeek(2);
2421             return readShort();
2422           }
2423 
2424           public short getUnionProperty() {
2425             typeSeek(4);
2426             return readShort();
2427           }
2428 
2429           public int getUnionFieldList() {
2430             typeSeek(6);
2431             return readInt();
2432           }
2433 
2434           public DebugVC50TypeIterator getUnionFieldListIterator() {
2435             int index = unbiasTypeIndex(getUnionFieldList());
2436             int offset = parent.getTypeOffset(index);
2437             return new DebugVC50TypeIteratorImpl(parent, base, numTypes, index, offset);
2438           }
2439 
2440           public int getUnionSize() throws DebugVC50WrongNumericTypeException {
2441             return readIntNumericLeafAt(10);
2442           }
2443 
2444           public String getUnionName() {
2445             return readLengthPrefixedStringAt(10 + numericLeafLengthAt(10));
2446           }
2447 
2448           ///////////////////////
2449           // LF_ENUM accessors //
2450           ///////////////////////
2451 
2452           public short getEnumCount() {
2453             typeSeek(2);
2454             return readShort();
2455           }
2456 
2457           public short getEnumProperty() {
2458             typeSeek(4);
2459             return readShort();
2460           }
2461 
2462           public int getEnumType() {
2463             typeSeek(6);
2464             return readInt();
2465           }
2466 
2467           public int getEnumFieldList() {
2468             typeSeek(10);
2469             return readInt();
2470           }
2471 
2472           public DebugVC50TypeIterator getEnumFieldListIterator() {
2473             int index = unbiasTypeIndex(getEnumFieldList());
2474             int offset = parent.getTypeOffset(index);
2475             return new DebugVC50TypeIteratorImpl(parent, base, numTypes, index, offset);
2476           }
2477 
2478           public String getEnumName() {
2479             return readLengthPrefixedStringAt(14);
2480           }
2481 
2482           ////////////////////////////
2483           // LF_PROCEDURE accessors //
2484           ////////////////////////////
2485 
2486           public int getProcedureReturnType() {
2487             typeSeek(2);
2488             return readInt();
2489           }
2490 
2491           public byte getProcedureCallingConvention() {
2492             typeSeek(6);
2493             return readByte();
2494           }
2495 
2496           public short getProcedureNumberOfParameters() {
2497             typeSeek(8);
2498             return readShort();
2499           }
2500 
2501           public int getProcedureArgumentList() {
2502             typeSeek(10);
2503             return readInt();
2504           }
2505 
2506           public DebugVC50TypeIterator getProcedureArgumentListIterator() {
2507             int index = unbiasTypeIndex(getProcedureArgumentList());
2508             int offset = parent.getTypeOffset(index);
2509             return new DebugVC50TypeIteratorImpl(parent, base, numTypes, index, offset);
2510           }
2511 
2512           ////////////////////////////
2513           // LF_MFUNCTION accessors //
2514           ////////////////////////////
2515 
2516           public int getMFunctionReturnType() {
2517             typeSeek(2);
2518             return readInt();
2519           }
2520 
2521           public int getMFunctionContainingClass() {
2522             typeSeek(6);
2523             return readInt();
2524           }
2525 
2526           public int getMFunctionThis() {
2527             typeSeek(10);
2528             return readInt();
2529           }
2530 
2531           public byte getMFunctionCallingConvention() {
2532             typeSeek(14);
2533             return readByte();
2534           }
2535 
2536           public short getMFunctionNumberOfParameters() {
2537             typeSeek(16);
2538             return readShort();
2539           }
2540 
2541           public int getMFunctionArgumentList() {
2542             typeSeek(18);
2543             return readInt();
2544           }
2545 
2546           public DebugVC50TypeIterator getMFunctionArgumentListIterator() {
2547             int index = unbiasTypeIndex(getMFunctionArgumentList());
2548             int offset = parent.getTypeOffset(index);
2549             return new DebugVC50TypeIteratorImpl(parent, base, numTypes, index, offset);
2550           }
2551 
2552           public int getMFunctionThisAdjust() {
2553             typeSeek(22);
2554             return readInt();
2555           }
2556 
2557           //////////////////////////
2558           // LF_VTSHAPE accessors //
2559           //////////////////////////
2560 
2561           public short getVTShapeCount() {
2562             typeSeek(2);
2563             return readShort();
2564           }
2565 
2566           public int getVTShapeDescriptor(int i) {
2567             typeSeek(4 + (i / 2));
2568             int val = readByte() & 0xFF;
2569             if ((i % 2) != 0) {
2570               val = val >> 4;
2571             }
2572             return val;
2573           }
2574 
2575           /////////////////////////
2576           // LF_BARRAY accessors //
2577           /////////////////////////
2578 
2579           public int getBasicArrayType() {
2580             typeSeek(2);
2581             return readInt();
2582           }
2583 
2584           ////////////////////////
2585           // LF_LABEL accessors //
2586           ////////////////////////
2587 
2588           public short getLabelAddressMode() {
2589             typeSeek(2);
2590             return readShort();
2591           }
2592 
2593           ///////////////////////////
2594           // LF_DIMARRAY accessors //
2595           ///////////////////////////
2596 
2597           public int getDimArrayType() {
2598             typeSeek(2);
2599             return readInt();
2600           }
2601 
2602           public int getDimArrayDimInfo() {
2603             typeSeek(6);
2604             return readInt();
2605           }
2606 
2607           public String getDimArrayName() {
2608             return readLengthPrefixedStringAt(10);
2609           }
2610 
2611           //////////////////////////
2612           // LF_VFTPATH accessors //
2613           //////////////////////////
2614 
2615           public int getVFTPathCount() {
2616             typeSeek(2);
2617             return readInt();
2618           }
2619 
2620           public int getVFTPathBase(int i) {
2621             typeSeek(6 + (4 * i));
2622             return readInt();
2623           }
2624 
2625           ///////////////////////
2626           // LF_SKIP accessors //
2627           ///////////////////////
2628 
2629           public int getSkipIndex() {
2630             typeSeek(2);
2631             return readInt();
2632           }
2633 
2634           //////////////////////////
2635           // LF_ARGLIST accessors //
2636           //////////////////////////
2637 
2638           public int getArgListCount() {
2639             typeSeek(2);
2640             return readInt();
2641           }
2642 
2643           public int getArgListType(int i) {
2644             typeSeek(6 + (4 * i));
2645             return readInt();
2646           }
2647 
2648           /////////////////////////
2649           // LF_DEFARG accessors //
2650           /////////////////////////
2651 
2652           public int getDefaultArgType() {
2653             typeSeek(2);
2654             return readInt();
2655           }
2656 
2657           public String getDefaultArgExpression() {
2658             return readLengthPrefixedStringAt(6);
2659           }
2660 
2661           //////////////////////////
2662           // LF_DERIVED accessors //
2663           //////////////////////////
2664 
2665           public int getDerivedCount() {
2666             typeSeek(2);
2667             return readInt();
2668           }
2669 
2670           public int getDerivedType(int i) {
2671             typeSeek(6);
2672             return readInt();
2673           }
2674 
2675           ///////////////////////////
2676           // LF_BITFIELD accessors //
2677           ///////////////////////////
2678 
2679           public int getBitfieldFieldType() {
2680             typeSeek(2);
2681             return readInt();
2682           }
2683 
2684           public byte getBitfieldLength() {
2685             typeSeek(6);
2686             return readByte();
2687           }
2688 
2689           public byte getBitfieldPosition() {
2690             typeSeek(7);
2691             return readByte();
2692           }
2693 
2694           ////////////////////////
2695           // LF_MLIST accessors //
2696           ////////////////////////
2697 
2698           public short getMListAttribute() {
2699             typeSeek(2);
2700             return readShort();
2701           }
2702 
2703           public int getMListLength() {
2704             return (getLength() - 6 - (isMListIntroducingVirtual() ? 4 : 0)) / 4;
2705           }
2706 
2707           public int getMListType(int i) {
2708             typeSeek(6 + 4 * i);
2709             return readInt();
2710           }
2711 
2712           public boolean isMListIntroducingVirtual() {
2713             return isIntroducingVirtual(getMListAttribute());
2714           }
2715 
2716           public int getMListVtabOffset() {
2717             typeSeek(6 + 4 * getMListLength());
2718             return readInt();
2719           }
2720 
2721           /////////////////////////
2722           // LF_REFSYM accessors //
2723           /////////////////////////
2724 
2725           public DebugVC50SymbolIterator getRefSym() {
2726             typeSeek(2);
2727             int len = readShort() & 0xFFFF;
2728             return new DebugVC50SymbolIteratorImpl(typeStringOffset + 2, len);
2729           }
2730 
2731           /////////////////////////
2732           // LF_BCLASS accessors //
2733           /////////////////////////
2734 
2735           public short getBClassAttribute() {
2736             typeSeek(2);
2737             return readShort();
2738           }
2739 
2740           public int getBClassType() {
2741             typeSeek(4);
2742             return readInt();
2743           }
2744 
2745           public int getBClassOffset() throws DebugVC50WrongNumericTypeException {
2746             return readIntNumericLeafAt(8);
2747           }
2748 
2749           //////////////////////////
2750           // LF_VBCLASS accessors //
2751           //////////////////////////
2752 
2753           public short getVBClassAttribute() {
2754             typeSeek(2);
2755             return readShort();
2756           }
2757 
2758           public int getVBClassBaseClassType() {
2759             typeSeek(4);
2760             return readInt();
2761           }
2762 
2763           public int getVBClassVirtualBaseClassType() {
2764             typeSeek(8);
2765             return readInt();
2766           }
2767 
2768           public int getVBClassVBPOff() throws DebugVC50WrongNumericTypeException {
2769             return readIntNumericLeafAt(12);
2770           }
2771 
2772           public int getVBClassVBOff() throws DebugVC50WrongNumericTypeException {
2773             return readIntNumericLeafAt(12 + numericLeafLengthAt(12));
2774           }
2775 
2776           ///////////////////////////
2777           // LF_IVBCLASS accessors //
2778           ///////////////////////////
2779 
2780           public short getIVBClassAttribute() {
2781             typeSeek(2);
2782             return readShort();
2783           }
2784 
2785           public int getIVBClassBType() {
2786             typeSeek(4);
2787             return readInt();
2788           }
2789 
2790           public int getIVBClassVBPType() {
2791             typeSeek(8);
2792             return readInt();
2793           }
2794 
2795           public int getIVBClassVBPOff() throws DebugVC50WrongNumericTypeException {
2796             return readIntNumericLeafAt(12);
2797           }
2798 
2799           public int getIVBClassVBOff() throws DebugVC50WrongNumericTypeException {
2800             return readIntNumericLeafAt(12 + numericLeafLengthAt(12));
2801           }
2802 
2803           ////////////////////////////
2804           // LF_ENUMERATE accessors //
2805           ////////////////////////////
2806 
2807           public short getEnumerateAttribute() {
2808             typeSeek(2);
2809             return readShort();
2810           }
2811 
2812           public long getEnumerateValue() {
2813             return readIntNumericLeafAt(4);
2814           }
2815 
2816           public String getEnumerateName() {
2817             return readLengthPrefixedStringAt(4 + numericLeafLengthAt(4));
2818           }
2819 
2820           ////////////////////////////
2821           // LF_FRIENDFCN accessors //
2822           ////////////////////////////
2823 
2824           public int getFriendFcnType() {
2825             typeSeek(4);
2826             return readInt();
2827           }
2828 
2829           public String getFriendFcnName() {
2830             return readLengthPrefixedStringAt(8);
2831           }
2832 
2833           ////////////////////////
2834           // LF_INDEX accessors //
2835           ////////////////////////
2836 
2837           public int getIndexValue() {
2838             typeSeek(4);
2839             return readInt();
2840           }
2841 
2842           public DebugVC50TypeIterator getIndexIterator() {
2843             int index = unbiasTypeIndex(getIndexValue());
2844             int offset = parent.getTypeOffset(index);
2845             return new DebugVC50TypeIteratorImpl(parent, base, numTypes, index, offset);
2846           }
2847 
2848           /////////////////////////
2849           // LF_MEMBER accessors //
2850           /////////////////////////
2851 
2852           public short getMemberAttribute() {
2853             typeSeek(2);
2854             return readShort();
2855           }
2856 
2857           public int getMemberType() {
2858             typeSeek(4);
2859             return readInt();
2860           }
2861 
2862           public int getMemberOffset() throws DebugVC50WrongNumericTypeException {
2863             return readIntNumericLeafAt(8);
2864           }
2865 
2866           public String getMemberName() {
2867             return readLengthPrefixedStringAt(8 + numericLeafLengthAt(8));
2868           }
2869 
2870           ///////////////////////////
2871           // LF_STMEMBER accessors //
2872           ///////////////////////////
2873 
2874           public short getStaticAttribute() {
2875             typeSeek(2);
2876             return readShort();
2877           }
2878 
2879           public int getStaticType() {
2880             typeSeek(4);
2881             return readInt();
2882           }
2883 
2884           public String getStaticName() {
2885             return readLengthPrefixedStringAt(8);
2886           }
2887 
2888           /////////////////////////
2889           // LF_METHOD accessors //
2890           /////////////////////////
2891 
2892           public short getMethodCount() {
2893             typeSeek(2);
2894             return readShort();
2895           }
2896 
2897           public int getMethodList() {
2898             typeSeek(4);
2899             return readInt();
2900           }
2901 
2902           public String getMethodName() {
2903             return readLengthPrefixedStringAt(8);
2904           }
2905 
2906           /////////////////////////////
2907           // LF_NESTEDTYPE accessors //
2908           /////////////////////////////
2909 
2910           public int getNestedType() {
2911             typeSeek(4);
2912             return readInt();
2913           }
2914 
2915           public String getNestedName() {
2916             return readLengthPrefixedStringAt(8);
2917           }
2918 
2919           ///////////////////////////
2920           // LF_VFUNCTAB accessors //
2921           ///////////////////////////
2922 
2923           public int getVFuncTabType() {
2924             typeSeek(4);
2925             return readInt();
2926           }
2927 
2928           ////////////////////////////
2929           // LF_FRIENDCLS accessors //
2930           ////////////////////////////
2931 
2932           public int getFriendClsType() {
2933             typeSeek(4);
2934             return readInt();
2935           }
2936 
2937           ////////////////////////////
2938           // LF_ONEMETHOD accessors //
2939           ////////////////////////////
2940 
2941           public short getOneMethodAttribute() {
2942             typeSeek(2);
2943             return readShort();
2944           }
2945 
2946           public int getOneMethodType() {
2947             typeSeek(4);
2948             return readInt();
2949           }
2950 
2951           public boolean isOneMethodIntroducingVirtual() {
2952             return isIntroducingVirtual(getOneMethodAttribute());
2953           }
2954 
2955           public int getOneMethodVBaseOff() {
2956             typeSeek(8);
2957             return readInt();
2958           }
2959 
2960           public String getOneMethodName() {
2961             int baseLen = 8 + (isOneMethodIntroducingVirtual() ? 4 : 0);
2962             return readLengthPrefixedStringAt(baseLen);
2963           }
2964 
2965           ///////////////////////////
2966           // LF_VFUNCOFF accessors //
2967           ///////////////////////////
2968 
2969           public int getVFuncOffType() {
2970             typeSeek(4);
2971             return readInt();
2972           }
2973 
2974           public int getVFuncOffOffset() {
2975             typeSeek(8);
2976             return readInt();
2977           }
2978 
2979           ///////////////////////////////
2980           // LF_NESTEDTYPEEX accessors //
2981           ///////////////////////////////
2982 
2983           public short getNestedExAttribute() {
2984             typeSeek(2);
2985             return readShort();
2986           }
2987 
2988           public int getNestedExType() {
2989             typeSeek(4);
2990             return readInt();
2991           }
2992 
2993           public String getNestedExName() {
2994             return readLengthPrefixedStringAt(8);
2995           }
2996 
2997           ///////////////////////////////
2998           // LF_MEMBERMODIFY accessors //
2999           ///////////////////////////////
3000 
3001           public short getMemberModifyAttribute() {
3002             typeSeek(2);
3003             return readShort();
3004           }
3005 
3006           public int getMemberModifyType() {
3007             typeSeek(4);
3008             return readInt();
3009           }
3010 
3011           public String getMemberModifyName() {
3012             return readLengthPrefixedStringAt(8);
3013           }
3014 
3015           ////////////////////////////
3016           // Numeric Leaf accessors //
3017           ////////////////////////////
3018 
3019           public short getNumericTypeAt(int byteOffset) {
3020             typeSeek(byteOffset);
3021             return readShort();
3022           }
3023 
3024           public int getNumericLengthAt(int byteOffset)
3025             throws DebugVC50WrongNumericTypeException {
3026             return numericLeafLengthAt(byteOffset);
3027           }
3028 
3029           public int getNumericIntAt(int byteOffset)
3030             throws DebugVC50WrongNumericTypeException {
3031             return readIntNumericLeafAt(byteOffset);
3032           }
3033 
3034           public long getNumericLongAt(int byteOffset)
3035             throws DebugVC50WrongNumericTypeException {
3036             // FIXME
3037             throw new RuntimeException("Unimplemented");
3038           }
3039 
3040           public float getNumericFloatAt(int byteOffset)
3041             throws DebugVC50WrongNumericTypeException {
3042             // FIXME
3043             throw new RuntimeException("Unimplemented");
3044           }
3045 
3046           public double getNumericDoubleAt(int byteOffset)
3047             throws DebugVC50WrongNumericTypeException {
3048             // FIXME
3049             throw new RuntimeException("Unimplemented");
3050           }
3051 
3052           public byte[] getNumericDataAt(int byteOffset)
3053             throws DebugVC50WrongNumericTypeException {
3054             // FIXME
3055             throw new RuntimeException("Unimplemented");
3056           }
3057 
3058           //----------------------------------------------------------------------
3059           // Internals only below this point
3060           //
3061 
3062           private void loadTypeRecord() {
3063             seek(typeRecordOffset);
3064             typeRecordSize = readShort() & 0xFFFF;
3065             typeStringOffset = typeRecordOffset + 2;
3066             loadTypeString();
3067           }
3068 
3069           private void loadTypeString() {
3070             seek(typeStringOffset);
3071             int lo = readByte() & 0xFF;
3072             // See if it is one of the single-byte leaves
3073             if (lo >= LF_PAD0) {
3074               typeStringLeaf = lo;
3075             } else {
3076               int hi = readByte() & 0xFF;
3077               typeStringLeaf = (hi << 8) | lo;
3078             }
3079           }
3080 
3081           private void typeSeek(int offset) {
3082             seek(typeStringOffset + offset);
3083           }
3084 
3085           private int typeStringLength() {
3086             // LF_PAD
3087             if (typeStringLeaf >= 0xF0 && typeStringLeaf <= 0xFF) {
3088               return (typeStringLeaf - 0xF0);
3089             }
3090 
3091             switch (typeStringLeaf) {
3092 
3093               // Leaf indices for type records that can be referenced
3094               // from symbols:
3095             case LF_MODIFIER: return 8;
3096             case LF_POINTER: {
3097               int extraLen = 0;
3098               int attr = (getPointerAttributes() & POINTER_PTRTYPE_MASK) >> POINTER_PTRTYPE_SHIFT;
3099               int mode = (getPointerAttributes() & POINTER_PTRMODE_MASK) >> POINTER_PTRMODE_SHIFT;
3100               if (attr == POINTER_PTRTYPE_BASED_ON_TYPE) {
3101                 extraLen = 4 + numericLeafLengthAt(typeStringOffset + 14);
3102               } else if (mode == POINTER_PTRMODE_PTR_TO_DATA_MEMBER ||
3103                          mode == POINTER_PTRMODE_PTR_TO_METHOD) {
3104                 extraLen = 6;
3105               }
3106               return 10 + extraLen;
3107             }
3108             case LF_ARRAY: {
3109               int temp = 10 + numericLeafLengthAt(10);
3110               return temp + lengthPrefixedStringLengthAt(temp);
3111             }
3112             case LF_CLASS:
3113             case LF_STRUCTURE: {
3114               int temp = 18 + numericLeafLengthAt(18);
3115               return temp + lengthPrefixedStringLengthAt(temp);
3116             }
3117             case LF_UNION: {
3118               int temp = 10 + numericLeafLengthAt(10);
3119               return temp + lengthPrefixedStringLengthAt(temp);
3120             }
3121             case LF_ENUM: {
3122               return 14 + lengthPrefixedStringLengthAt(14);
3123             }
3124             case LF_PROCEDURE: return 14;
3125             case LF_MFUNCTION: return 26;
3126             case LF_VTSHAPE:   return 4 + ((getVTShapeCount() + 1) / 2);
3127             case LF_COBOL0:
3128             case LF_COBOL1:    throw new COFFException("COBOL symbols unimplemented");
3129             case LF_BARRAY:    return 6;
3130             case LF_LABEL:     return 4;
3131             case LF_NULL:      return 2;
3132             case LF_NOTTRAN:   return 2;
3133             case LF_DIMARRAY:  return 10 + lengthPrefixedStringLengthAt(10);
3134             case LF_VFTPATH:   return 6 + 4 * getVFTPathCount();
3135             case LF_PRECOMP:   return 14 + lengthPrefixedStringLengthAt(14);
3136             case LF_ENDPRECOMP: return 6;
3137             case LF_OEM:       throw new COFFException("OEM symbols unimplemented");
3138             case LF_TYPESERVER: return 10 + lengthPrefixedStringLengthAt(10);
3139 
3140             case LF_SKIP:      return 6 + numericLeafLengthAt(6);
3141             case LF_ARGLIST:   return 6 + 4 * getArgListCount();
3142             case LF_DEFARG:    return 6 + lengthPrefixedStringLengthAt(6);
3143               // case LF_FIELDLIST: throw new COFFException("Should not see LF_FIELDLIST leaf");
3144             case LF_FIELDLIST: return 2;
3145             case LF_DERIVED:   return 6 + 4 * getDerivedCount();
3146             case LF_BITFIELD:  return 8;
3147             case LF_METHODLIST: {
3148               return 6 + 4 * getMListLength() + (isMListIntroducingVirtual() ? 4 : 0);
3149             }
3150             case LF_DIMCONU:
3151             case LF_DIMCONLU:
3152             case LF_DIMVARU:
3153             case LF_DIMVARLU:  throw new COFFException("LF_DIMCONU, LF_DIMCONLU, LF_DIMVARU, and LF_DIMVARLU unsupported");
3154             case LF_REFSYM: {
3155               seek(typeStringOffset + 2);
3156               return 4 + readShort();
3157             }
3158 
3159             case LF_BCLASS:  return 8 + numericLeafLengthAt(8);
3160             case LF_VBCLASS:
3161             case LF_IVBCLASS: {
3162               int temp = 12 + numericLeafLengthAt(12);
3163               return temp + numericLeafLengthAt(temp);
3164             }
3165             case LF_ENUMERATE: {
3166               int temp = 4 + numericLeafLengthAt(4);
3167               return temp + lengthPrefixedStringLengthAt(temp);
3168             }
3169             case LF_FRIENDFCN: return 8 + lengthPrefixedStringLengthAt(8);
3170             case LF_INDEX: return 8;
3171             case LF_MEMBER: {
3172               int temp = 8 + numericLeafLengthAt(8);
3173               return temp + lengthPrefixedStringLengthAt(temp);
3174             }
3175             case LF_STMEMBER: return 8 + lengthPrefixedStringLengthAt(8);
3176             case LF_METHOD:   return 8 + lengthPrefixedStringLengthAt(8);
3177             case LF_NESTTYPE: return 8 + lengthPrefixedStringLengthAt(8);
3178             case LF_VFUNCTAB: return 8;
3179             case LF_FRIENDCLS: return 8;
3180             case LF_ONEMETHOD: {
3181               int baseLen = 8 + (isOneMethodIntroducingVirtual() ? 4 : 0);
3182               return baseLen + lengthPrefixedStringLengthAt(baseLen);
3183             }
3184             case LF_VFUNCOFF:  return 12;
3185             case LF_NESTTYPEEX: return 8 + lengthPrefixedStringLengthAt(8);
3186             case LF_MEMBERMODIFY: return 8 + lengthPrefixedStringLengthAt(8);
3187 
3188             // Should not encounter numeric leaves with this routine
3189             case LF_CHAR:
3190             case LF_SHORT:
3191             case LF_USHORT:
3192             case LF_LONG:
3193             case LF_ULONG:
3194             case LF_REAL32:
3195             case LF_REAL64:
3196             case LF_REAL80:
3197             case LF_REAL128:
3198             case LF_QUADWORD:
3199             case LF_UQUADWORD:
3200             case LF_REAL48:
3201             case LF_COMPLEX32:
3202             case LF_COMPLEX64:
3203             case LF_COMPLEX80:
3204             case LF_COMPLEX128:
3205             case LF_VARSTRING:  throw new RuntimeException("Unexpected numeric leaf " + typeStringLeaf +
3206                                                            "in type string");
3207             default:
3208               throw new COFFException("Unrecognized leaf " + typeStringLeaf + " in type string at offset " +
3209                                       typeStringOffset);
3210             }
3211           }
3212 
3213           private boolean isIntroducingVirtual(int mprop) {
3214             int masked = mprop & MEMATTR_MPROP_MASK;
3215             return ((masked == MEMATTR_MPROP_INTRODUCING_VIRTUAL) ||
3216                     (masked == MEMATTR_MPROP_PURE_INTRODUCING_VIRTUAL));
3217           }
3218 
3219           private int numericLeafLengthAt(int offset) {
3220             return DebugVC50Impl.this.numericLeafLengthAt(typeStringOffset + offset);
3221           }
3222 
3223           private int readIntNumericLeafAt(int offset) {
3224             return DebugVC50Impl.this.readIntNumericLeafAt(typeStringOffset + offset);
3225           }
3226 
3227           private int lengthPrefixedStringLengthAt(int offset) {
3228             return DebugVC50Impl.this.lengthPrefixedStringLengthAt(typeStringOffset + offset);
3229           }
3230 
3231           private String readLengthPrefixedStringAt(int offset) {
3232             return DebugVC50Impl.this.readLengthPrefixedStringAt(typeStringOffset + offset);
3233           }
3234         }
3235 
3236         private int numericLeafLengthAt(int absoluteOffset) throws DebugVC50WrongNumericTypeException {
3237           seek(absoluteOffset);
3238           int leaf = readShort() & 0xFFFF;
3239           if (leaf < 0x8000) return 2;
3240           switch (leaf) {
3241           case LF_CHAR:       return 3;
3242           case LF_SHORT:
3243           case LF_USHORT:     return 4;
3244           case LF_LONG:
3245           case LF_ULONG:      return 6;
3246           case LF_REAL32:     return 6;
3247           case LF_REAL64:     return 10;
3248           case LF_REAL80:     return 12;
3249           case LF_REAL128:    return 18;
3250           case LF_QUADWORD:
3251           case LF_UQUADWORD:  return 18;
3252           case LF_REAL48:     return 8;
3253           case LF_COMPLEX32:  return 10;
3254           case LF_COMPLEX64:  return 18;
3255           case LF_COMPLEX80:  return 26;
3256           case LF_COMPLEX128: return 66;
3257             // FIXME: figure out format of variable-length strings
3258           case LF_VARSTRING:  return 4 + readIntNumericLeafAt(absoluteOffset + 2);
3259 
3260           default:
3261             throw new DebugVC50WrongNumericTypeException("Illegal numeric leaf index " + leaf +
3262                                                          " at offset " + absoluteOffset);
3263           }
3264         }
3265 
3266         private int readIntNumericLeafAt(int absoluteOffset) throws DebugVC50WrongNumericTypeException {
3267           seek(absoluteOffset);
3268           int leaf = readShort() & 0xFFFF;
3269           if (leaf < 0x8000) return leaf;
3270           switch (leaf) {
3271           case LF_CHAR:       return readByte() & 0xFF;
3272           case LF_SHORT:
3273           case LF_USHORT:     return readShort() & 0xFFFF;
3274           case LF_LONG:
3275           case LF_ULONG:      return readInt();
3276 
3277           default:
3278             throw new DebugVC50WrongNumericTypeException("Illegal numeric leaf index " + leaf);
3279           }
3280         }
3281 
3282         private long readLongNumericLeafAt(int absoluteOffset) throws DebugVC50WrongNumericTypeException {
3283           seek(absoluteOffset);
3284           int leaf = readShort() & 0xFFFF;
3285           if (leaf < 0x8000) return leaf;
3286           switch (leaf) {
3287           case LF_CHAR:       return readByte() & 0xFF;
3288           case LF_SHORT:
3289           case LF_USHORT:     return readShort() & 0xFFFF;
3290           case LF_LONG:
3291           case LF_ULONG:      return readInt() & 0xFFFFFFFF;
3292           case LF_QUADWORD:
3293           case LF_UQUADWORD:  return readLong();
3294 
3295           default:
3296             throw new DebugVC50WrongNumericTypeException("Illegal numeric leaf index " + leaf);
3297           }
3298         }
3299 
3300         private float readFloatNumericLeafAt(int absoluteOffset) throws DebugVC50WrongNumericTypeException {
3301           seek(absoluteOffset);
3302           int leaf = readShort() & 0xFFFF;
3303           if (leaf != LF_REAL32) {
3304             throw new DebugVC50WrongNumericTypeException("Illegal numeric leaf index " + leaf);
3305           }
3306           return readFloat();
3307         }
3308 
3309         private double readDoubleNumericLeafAt(int absoluteOffset) throws DebugVC50WrongNumericTypeException {
3310           seek(absoluteOffset);
3311           int leaf = readShort() & 0xFFFF;
3312           if (leaf != LF_REAL64) {
3313             throw new DebugVC50WrongNumericTypeException("Illegal numeric leaf index " + leaf);
3314           }
3315           return readDouble();
3316         }
3317 
3318         private int lengthPrefixedStringLengthAt(int absoluteOffset) {
3319           // NOTE: the format of length-prefixed strings is not well
3320           // specified. There is a LF_VARSTRING numeric leaf (the
3321           // format of which is also not specified), but it seems that
3322           // most length-prefixed strings are comprised of a single
3323           // byte length followed by that many bytes of data.
3324           seek(absoluteOffset);
3325           int len = readByte() & 0xFF;
3326           return 1 + len;
3327         }
3328 
3329         private String readLengthPrefixedStringAt(int absoluteOffset) {
3330           // NOTE: it isn't clear whether LF_VARSTRING numeric leaves
3331           // ever show up, or in general what happens when the length
3332           // of the string is > 255 (FIXME)
3333           seek(absoluteOffset);
3334           int len = readByte() & 0xFF;
3335           byte[] res = new byte[len];
3336           int numRead = readBytes(res);
3337           if (numRead != len) {
3338             throw new COFFException("Error reading length prefixed string in symbol at offset " +
3339                                     absoluteOffset);
3340           }
3341           try {
3342             return new String(res, US_ASCII);
3343           } catch (UnsupportedEncodingException e) {
3344             throw new COFFException(e);
3345           }
3346         }
3347 
3348         private int unbiasTypeIndex(int index) {
3349           return index - 0x1000;
3350         }
3351 
3352         private int biasTypeIndex(int index) {
3353           return index + 0x1000;
3354         }
3355       } // Class DebugVC50Impl
3356 
3357       class SectionHeaderImpl implements SectionHeader {
3358         private String name;
3359         private int    virtualSize;
3360         private int    virtualAddress;
3361         private int    sizeOfRawData;
3362         private int    pointerToRawData;
3363         private int    pointerToRelocations;
3364         private int    pointerToLineNumbers;
3365         private short  numberOfRelocations;
3366         private short  numberOfLineNumbers;
3367         private int    characteristics;
3368         private MemoizedObject[] relocations;
3369         private MemoizedObject[] lineNumbers;
3370 
3371         public SectionHeaderImpl(int offset) throws COFFException {
3372           seek(offset);
3373 
3374           // FIXME: compute name lazily
3375 
3376           // Read name
3377           byte[] tmpName = new byte[8];
3378           int numRead = readBytes(tmpName);
3379           if (numRead != 8) {
3380             throw new COFFException("Error reading name of section header at offset " + offset);
3381           }
3382           if (tmpName[0] == (byte) '/') {
3383             // Long name; must find real value in string table
3384             int index = 0;
3385             try {
3386               index = Integer.parseInt(new String(tmpName, 1, tmpName.length - 1, US_ASCII));
3387             } catch (NumberFormatException e) {
3388               throw new COFFException("Error parsing string table index of name of section header " +
3389                                       "at offset " + offset);
3390             } catch (UnsupportedEncodingException e) {
3391               throw new COFFException(e);
3392             }
3393             // Look up in string table
3394             // FIXME: this index value is assumed to be in the valid range
3395             name = getStringTable().get(index);
3396           } else {
3397             try {
3398               int length = 0;
3399               // find last non-NULL
3400               for (; length < tmpName.length && tmpName[length] != '\0';) {
3401                 length++;
3402               }
3403               // don't include NULL chars in returned name String
3404               name = new String(tmpName, 0, length, US_ASCII);
3405             } catch (UnsupportedEncodingException e) {
3406               throw new COFFException(e);
3407             }
3408           }
3409           virtualSize          = readInt();
3410           virtualAddress       = readInt();
3411           sizeOfRawData        = readInt();
3412           pointerToRawData     = readInt();
3413           pointerToRelocations = readInt();
3414           pointerToLineNumbers = readInt();
3415           numberOfRelocations  = readShort();
3416           numberOfLineNumbers  = readShort();
3417           characteristics      = readInt();
3418 
3419           // Set up relocations
3420           relocations = new MemoizedObject[numberOfRelocations];
3421           for (int i = 0; i < numberOfRelocations; i++) {
3422             final int relocOffset = pointerToRelocations + i * RELOCATION_SIZE;
3423             relocations[i] = new MemoizedObject() {
3424                 public Object computeValue() {
3425                   return new COFFRelocationImpl(relocOffset);
3426                 }
3427               };
3428           }
3429 
3430           // Set up line numbers
3431           lineNumbers = new MemoizedObject[numberOfLineNumbers];
3432           for (int i = 0; i < numberOfLineNumbers; i++) {
3433             final int lineNoOffset = pointerToLineNumbers + i * LINE_NUMBER_SIZE;
3434             lineNumbers[i] = new MemoizedObject() {
3435                 public Object computeValue() {
3436                   return new COFFLineNumberImpl(lineNoOffset);
3437                 }
3438               };
3439           }
3440         }
3441 
3442         public String getName() { return name; }
3443         public int getSize() { return virtualSize; }
3444         public int getVirtualAddress() { return virtualAddress; }
3445         public int getSizeOfRawData() { return sizeOfRawData; }
3446         public int getPointerToRawData() { return pointerToRawData; }
3447         public int getPointerToRelocations() { return pointerToRelocations; }
3448         public int getPointerToLineNumbers() { return pointerToLineNumbers; }
3449         public short getNumberOfRelocations() { return numberOfRelocations; }
3450         public short getNumberOfLineNumbers() { return numberOfLineNumbers; }
3451         public int getSectionFlags() { return characteristics; }
3452         public boolean hasSectionFlag(int flag ) {
3453           return ((characteristics & flag) != 0);
3454         }
3455         public COFFRelocation getCOFFRelocation(int index) {
3456           return (COFFRelocation) relocations[index].getValue();
3457         }
3458         public COFFLineNumber getCOFFLineNumber(int index) {
3459           return (COFFLineNumber) lineNumbers[index];
3460         }
3461       }
3462 
3463       class COFFSymbolImpl implements COFFSymbol, COFFSymbolConstants {
3464         private int    offset;
3465         private String name;
3466         private int    value;
3467         private short  sectionNumber;
3468         private short  type;
3469         private byte   storageClass;
3470         private byte   numberOfAuxSymbols;
3471         private MemoizedObject auxFunctionDefinitionRecord = new MemoizedObject() {
3472             public Object computeValue() {
3473               return new AuxFunctionDefinitionRecordImpl(offset + SYMBOL_SIZE);
3474             }
3475           };
3476         private MemoizedObject auxBfEfRecord = new MemoizedObject() {
3477             public Object computeValue() {
3478               return new AuxBfEfRecordImpl(offset + SYMBOL_SIZE);
3479             }
3480           };
3481         private MemoizedObject auxWeakExternalRecord = new MemoizedObject() {
3482             public Object computeValue() {
3483               return new AuxWeakExternalRecordImpl(offset + SYMBOL_SIZE);
3484             }
3485           };
3486         private MemoizedObject auxFileRecord = new MemoizedObject() {
3487             public Object computeValue() {
3488               return new AuxFileRecordImpl(offset + SYMBOL_SIZE);
3489             }
3490           };
3491         private MemoizedObject auxSectionDefinitionsRecord = new MemoizedObject() {
3492             public Object computeValue() {
3493               return new AuxSectionDefinitionsRecordImpl(offset + SYMBOL_SIZE);
3494             }
3495           };
3496 
3497         public COFFSymbolImpl(int offset) throws COFFException {
3498           this.offset = offset;
3499           seek(offset);
3500 
3501           // Parse name
3502           byte[] tmpName = new byte[8];
3503           int numRead = readBytes(tmpName);
3504           if (numRead != 8) {
3505             throw new COFFException("Error reading name of symbol at offset " + offset);
3506           }
3507           if ((tmpName[0] == 0) &&
3508               (tmpName[1] == 0) &&
3509               (tmpName[2] == 0) &&
3510               (tmpName[3] == 0)) {
3511             // It's an offset into the string table.
3512             // FIXME: not sure about byte ordering...
3513             int stringOffset = (tmpName[4] << 24 |
3514                                 tmpName[5] << 16 |
3515                                 tmpName[6] <<  8 |
3516                                 tmpName[7]);
3517             // FIXME: stringOffset is assumed to be in the valid range
3518             name = getStringTable().getAtOffset(stringOffset);
3519           }
3520 
3521           value = readInt();
3522           sectionNumber = readShort();
3523           type = readShort();
3524           storageClass = readByte();
3525           numberOfAuxSymbols = readByte();
3526         }
3527 
3528         public int getOffset()              { return offset; }
3529         public String getName()             { return name; }
3530         public int getValue()               { return value; }
3531         public short getSectionNumber()     { return sectionNumber; }
3532         public short getType()              { return type; }
3533         public byte getStorageClass()       { return storageClass; }
3534         public byte getNumberOfAuxSymbols() { return numberOfAuxSymbols; }
3535         public boolean isFunctionDefinition() {
3536           return ((getStorageClass() == IMAGE_SYM_CLASS_EXTERNAL) &&
3537                   ((getType() >>> 8) == IMAGE_SYM_DTYPE_FUNCTION) &&
3538                   (getSectionNumber() > 0));
3539         }
3540         public AuxFunctionDefinitionRecord getAuxFunctionDefinitionRecord() {
3541           return (AuxFunctionDefinitionRecord) auxFunctionDefinitionRecord.getValue();
3542         }
3543         public boolean isBfOrEfSymbol() {
3544           return ((getName().equals(".bf") || getName().equals(".ef")) &&
3545                   (getStorageClass() == IMAGE_SYM_CLASS_FUNCTION));
3546         }
3547         public AuxBfEfRecord getAuxBfEfRecord() {
3548           return (AuxBfEfRecord) auxBfEfRecord.getValue();
3549         }
3550         public boolean isWeakExternal() {
3551           return ((getStorageClass() == IMAGE_SYM_CLASS_EXTERNAL) &&
3552                   (getSectionNumber() == IMAGE_SYM_UNDEFINED) &&
3553                   (getValue() == 0));
3554         }
3555         public AuxWeakExternalRecord getAuxWeakExternalRecord() {
3556           return (AuxWeakExternalRecord) auxWeakExternalRecord.getValue();
3557         }
3558         public boolean isFile() {
3559           return ((getName().equals(".file")) &&
3560                   (getStorageClass() == IMAGE_SYM_CLASS_FILE));
3561         }
3562         public AuxFileRecord getAuxFileRecord() {
3563           return (AuxFileRecord) auxFileRecord.getValue();
3564         }
3565         public boolean isSectionDefinition() {
3566           // FIXME: not sure how to ensure that symbol name is the
3567           // name of a section.
3568           return ((getName().charAt(0) == '.') &&
3569                   (getStorageClass() == IMAGE_SYM_CLASS_STATIC));
3570         }
3571         public AuxSectionDefinitionsRecord getAuxSectionDefinitionsRecord() {
3572           return (AuxSectionDefinitionsRecord) auxSectionDefinitionsRecord.getValue();
3573         }
3574       }
3575 
3576       class AuxFunctionDefinitionRecordImpl implements AuxFunctionDefinitionRecord {
3577         private int tagIndex;
3578         private int totalSize;
3579         private int pointerToLineNumber;
3580         private int pointerToNextFunction;
3581 
3582         AuxFunctionDefinitionRecordImpl(int offset) {
3583           seek(offset);
3584           tagIndex              = readInt();
3585           totalSize             = readInt();
3586           // NOTE zero-basing of this index
3587           pointerToLineNumber   = readInt() - 1;
3588           pointerToNextFunction = readInt();
3589         }
3590 
3591         public int getTagIndex()              { return tagIndex; }
3592         public int getTotalSize()             { return totalSize; }
3593         public int getPointerToLineNumber()   { return pointerToLineNumber; }
3594         public int getPointerToNextFunction() { return pointerToNextFunction; }
3595         public int getType()                  { return FUNCTION_DEFINITION; }
3596       }
3597 
3598       class AuxBfEfRecordImpl implements AuxBfEfRecord {
3599         private short lineNumber;
3600         private int   pointerToNextFunction;
3601 
3602         AuxBfEfRecordImpl(int offset) {
3603           seek(offset);
3604           readInt();
3605           lineNumber = readShort();
3606           readInt();
3607           readShort();
3608           pointerToNextFunction = readInt();
3609         }
3610 
3611         public short getLineNumber()          { return lineNumber; }
3612         public int getPointerToNextFunction() { return pointerToNextFunction; }
3613         public int getType()                  { return BF_EF_RECORD; }
3614       }
3615 
3616       class AuxWeakExternalRecordImpl implements AuxWeakExternalRecord {
3617         private int tagIndex;
3618         private int characteristics;
3619 
3620         AuxWeakExternalRecordImpl(int offset) {
3621           seek(offset);
3622           tagIndex = readInt();
3623           characteristics = readInt();
3624         }
3625 
3626         public int getTagIndex()        { return tagIndex; }
3627         public int getCharacteristics() { return characteristics; }
3628         public int getType()            { return WEAK_EXTERNAL; }
3629       }
3630 
3631       class AuxFileRecordImpl implements AuxFileRecord {
3632         private String name;
3633 
3634         AuxFileRecordImpl(int offset) {
3635           seek(offset);
3636           byte[] tmpName = new byte[18];
3637           int numRead = readBytes(tmpName);
3638           if (numRead != 18) {
3639             throw new COFFException("Error reading auxiliary file record at offset " + offset);
3640           }
3641           try {
3642             name = new String(tmpName, US_ASCII);
3643           } catch (UnsupportedEncodingException e) {
3644             throw new COFFException(e);
3645           }
3646         }
3647 
3648         public String getName() { return name; }
3649         public int getType()    { return FILE; }
3650       }
3651 
3652       class AuxSectionDefinitionsRecordImpl implements AuxSectionDefinitionsRecord {
3653         private int length;
3654         private short numberOfRelocations;
3655         private short numberOfLineNumbers;
3656         private int checkSum;
3657         private short number;
3658         private byte selection;
3659 
3660         AuxSectionDefinitionsRecordImpl(int offset) {
3661           seek(offset);
3662           length = readInt();
3663           numberOfRelocations = readShort();
3664           numberOfLineNumbers = readShort();
3665           checkSum = readInt();
3666           number = readShort();
3667           selection = readByte();
3668         }
3669 
3670         public int   getLength()              { return length; }
3671         public short getNumberOfRelocations() { return numberOfRelocations; }
3672         public short getNumberOfLineNumbers() { return numberOfLineNumbers; }
3673         public int   getCheckSum()            { return checkSum; }
3674         public short getNumber()              { return number; }
3675         public byte  getSelection()           { return selection; }
3676         public int getType()                  { return SECTION_DEFINITION; }
3677       }
3678 
3679       class COFFRelocationImpl implements COFFRelocation {
3680         private int virtualAddress;
3681         private int symbolTableIndex;
3682         private short type;
3683 
3684         COFFRelocationImpl(int offset) {
3685           seek(offset);
3686           virtualAddress   = readInt();
3687           symbolTableIndex = readInt();
3688           type             = readShort();
3689         }
3690 
3691         public int   getVirtualAddress()     { return virtualAddress; }
3692         public int   getSymbolTableIndex()   { return symbolTableIndex; }
3693         public short getType()               { return type; }
3694       }
3695 
3696       class COFFLineNumberImpl implements COFFLineNumber {
3697         private int   type;
3698         private short lineNumber;
3699 
3700         COFFLineNumberImpl(int offset) {
3701           seek(offset);
3702           type       = readInt();
3703           lineNumber = readShort();
3704         }
3705 
3706         public int getType() {
3707           return type;
3708         }
3709 
3710         public short getLineNumber() {
3711           return lineNumber;
3712         }
3713       }
3714 
3715       class StringTable {
3716         class COFFString {
3717           String str;
3718           int    offset;
3719 
3720           COFFString(String str, int offset) {
3721             this.str = str; this.offset = offset;
3722           }
3723         }
3724 
3725         COFFString[] strings;
3726 
3727         StringTable(int offset) {
3728           if (offset == 0) {
3729             // no String Table
3730             strings = new COFFString[0];
3731             return;
3732           }
3733 
3734           seek(offset);
3735           int length = readInt();  // length includes itself
3736           byte[] data = new byte[length - 4];
3737           int numBytesRead = readBytes(data);
3738           if (numBytesRead != data.length) {
3739             throw new COFFException("Error reading string table (read " +
3740                                     numBytesRead + " bytes, expected to read " + data.length + ")");
3741           }
3742           int numStrings = 0;
3743           int ptr = 0;
3744           for (ptr = 0; ptr < data.length; ptr++) {
3745             if (data[ptr] == 0) {
3746               numStrings++;
3747             }
3748           }
3749           strings = new COFFString[numStrings];
3750           int lastPtr = 0;
3751           ptr = 0;
3752           for (int i = 0; i < numStrings; i++) {
3753             while (data[ptr] != 0) {
3754               ptr++;
3755             }
3756             try {
3757               strings[i] = new COFFString(new String(data, lastPtr, ptr - lastPtr, US_ASCII),
3758                                           offset + ptr + 4);
3759             } catch (UnsupportedEncodingException e) {
3760               throw new COFFException(e);
3761             }
3762             ptr++;
3763             lastPtr = ptr;
3764           }
3765         }
3766 
3767         int getNum() {
3768           return strings.length;
3769         }
3770 
3771         String get(int i) {
3772           return strings[i].str;
3773         }
3774 
3775         /** This version takes an absolute offset in the file */
3776         String getAtOffset(int offset) {
3777           int i = Arrays.binarySearch(strings, new COFFString(null, offset),
3778                                       new Comparator() {
3779                                           public int compare(Object o1, Object o2) {
3780                                             COFFString s1 = (COFFString) o1;
3781                                             COFFString s2 = (COFFString) o2;
3782                                             if (s1.offset == s2.offset) {
3783                                               return 0;
3784                                             } else if (s1.offset < s2.offset) {
3785                                               return -1;
3786                                             } else {
3787                                               return 1;
3788                                             }
3789                                           }
3790                                         });
3791           if (i < 0) {
3792             throw new COFFException("No string found at file offset " + offset);
3793           }
3794           return strings[i].str;
3795         }
3796       }
3797     }
3798 
3799     void initialize() throws COFFException {
3800       // Figure out whether this file is an object file or an image
3801       // (either executable or DLL).
3802       seek(0x3c); // Error here probably indicates file format error
3803       try {
3804         int peOffset = readInt();
3805         seek(peOffset);
3806         if ((readByte() == (byte) 'P') &&
3807             (readByte() == (byte) 'E') &&
3808             (readByte() == (byte) 0) &&
3809             (readByte() == (byte) 0)) {
3810           isImage = true;
3811           imageHeaderOffset = getFilePointer();
3812         }
3813       }
3814       catch (COFFException e) {
3815         // Expect failures here if not image file.
3816       }
3817     }
3818 
3819     byte readByteAt(long offset) throws COFFException {
3820       seek(offset);
3821       return readByte();
3822     }
3823 
3824     byte readByte() throws COFFException {
3825       try {
3826         return file.readByte();
3827       } catch (IOException e) {
3828         throw new COFFException(e.toString() + " at offset 0x" +
3829                                 Long.toHexString(filePos), e);
3830       }
3831     }
3832 
3833     int readBytesAt(long offset, byte[] b) throws COFFException {
3834       seek(offset);
3835       return readBytes(b);
3836     }
3837 
3838     int readBytes(byte[] b) throws COFFException {
3839       try {
3840         return file.read(b);
3841       } catch (IOException e) {
3842         throw new COFFException(e.toString() + " at offset 0x" +
3843                                 Long.toHexString(filePos), e);
3844       }
3845     }
3846 
3847     /** NOTE: reads little-endian short */
3848     short readShortAt(long offset) throws COFFException {
3849       seek(offset);
3850       return readShort();
3851     }
3852 
3853     /** NOTE: reads little-endian short */
3854     short readShort() throws COFFException {
3855       try {
3856         return byteSwap(file.readShort());
3857       } catch (IOException e) {
3858         throw new COFFException(e.toString() + " at offset 0x" +
3859                                 Long.toHexString(filePos), e);
3860       }
3861     }
3862 
3863     /** NOTE: reads little-endian int */
3864     int readIntAt(long offset) throws COFFException {
3865       seek(offset);
3866       return readInt();
3867     }
3868 
3869     /** NOTE: reads little-endian int */
3870     int readInt() throws COFFException {
3871       try {
3872         return byteSwap(file.readInt());
3873       } catch (IOException e) {
3874         throw new COFFException(e.toString() + " at offset 0x" +
3875                                 Long.toHexString(filePos), e);
3876       }
3877     }
3878 
3879     /** NOTE: reads little-endian long */
3880     long readLongAt(long offset) throws COFFException {
3881       seek(offset);
3882       return readLong();
3883     }
3884 
3885     /** NOTE: reads little-endian long */
3886     long readLong() throws COFFException {
3887       try {
3888         return byteSwap(file.readLong());
3889       } catch (IOException e) {
3890         throw new COFFException(e.toString() + " at offset 0x" +
3891                                 Long.toHexString(filePos), e);
3892       }
3893     }
3894 
3895     /** NOTE: reads little-endian float */
3896     float readFloat() throws COFFException {
3897       int i = readInt();
3898       return Float.intBitsToFloat(i);
3899     }
3900 
3901     /** NOTE: reads little-endian double */
3902     double readDouble() throws COFFException {
3903       long l = readLong();
3904       return Double.longBitsToDouble(l);
3905     }
3906 
3907     String readCString() throws COFFException {
3908       List data = new ArrayList();
3909       byte b = 0;
3910       while ((b = readByte()) != 0) {
3911         data.add(new Byte(b));
3912       }
3913       byte[] bytes = new byte[data.size()];
3914       for (int i = 0; i < data.size(); i++) {
3915         bytes[i] = ((Byte) data.get(i)).byteValue();
3916       }
3917       try {
3918         return new String(bytes, US_ASCII);
3919       } catch (UnsupportedEncodingException e) {
3920         throw new COFFException(e);
3921       }
3922     }
3923 
3924     void seek(long offset) throws COFFException {
3925       try {
3926         filePos = offset;
3927         file.seek(offset);
3928       } catch (IOException e) {
3929         throw new COFFException(e.toString() + " at offset 0x" +
3930                                 Long.toHexString(offset), e);
3931       }
3932     }
3933 
3934     long getFilePointer() throws COFFException {
3935       try {
3936         return file.getFilePointer();
3937       } catch (IOException e) {
3938         throw new COFFException(e);
3939       }
3940     }
3941 
3942     short byteSwap(short arg) {
3943       return (short) ((arg << 8) | ((arg >>> 8) & 0xFF));
3944     }
3945 
3946     int byteSwap(int arg) {
3947       return (((int) byteSwap((short) arg)) << 16) | (((int) (byteSwap((short) (arg >>> 16)))) & 0xFFFF);
3948     }
3949 
3950     long byteSwap(long arg) {
3951       return ((((long) byteSwap((int) arg)) << 32) | (((long) byteSwap((int) (arg >>> 32))) & 0xFFFFFFFF));
3952     }
3953 
3954     public void close() throws COFFException {
3955       try {
3956         file.close();
3957       } catch (IOException e) {
3958         throw new COFFException(e);
3959       }
3960     }
3961   }
3962 }