Subversion Repositories Kolibri OS

Rev

Blame | Last modification | View Log | Download | RSS feed

  1. /****************************  cof2asm.cpp   ********************************
  2. * Author:        Agner Fog
  3. * Date created:  2007-02-25
  4. * Last modified: 2009-12-20
  5. * Project:       objconv
  6. * Module:        cof2asm.cpp
  7. * Description:
  8. * Module for disassembling PE/COFF file
  9. *
  10. * Copyright 2007-2009 GNU General Public License http://www.gnu.org/licenses
  11. *****************************************************************************/
  12. #include "stdafx.h"
  13.  
  14. CCOF2ASM::CCOF2ASM () {
  15.    // Constructor
  16. }
  17.  
  18. void CCOF2ASM::Convert() {
  19.    // Do the conversion
  20.    if (ImageBase) Disasm.Init(2, ImageBase);     // Executable file or DLL. Set image base
  21.    MakeSectionList();                            // Make Sections list and Relocations list in Disasm
  22.    MakeSymbolList();                             // Make Symbols list in Disasm
  23.    if (ImageBase) {
  24.       // Executable file
  25.       MakeDynamicRelocations();                  // Make dynamic base relocations for executable files
  26.       MakeImportList();                          // Make imported symbols for executable files
  27.       MakeExportList();                          // Make exported symbols for executable files
  28.       MakeListLabels();                          // Put labels on all image directory tables
  29.    }
  30.    Disasm.Go();                                  // Disassemble
  31.    *this << Disasm.OutFile;                      // Take over output file from Disasm
  32. }
  33.  
  34. void CCOF2ASM::MakeSectionList() {
  35.    // Make Sections list and Relocations list in Disasm
  36.    uint32 isec;                                  // Section index
  37.    uint32 irel;                                  // Relocation index
  38.  
  39.    // Loop through sections
  40.    for (isec = 0; isec < (uint32)NSections; isec++) {
  41.  
  42.       // Get section header
  43.       SCOFF_SectionHeader * SectionHeader = &SectionHeaders[isec];
  44.  
  45.       // Section properties
  46.       const char * Name  = GetSectionName(SectionHeader->Name);
  47.       uint8 * Buffer = (uint8*)Buf() + SectionHeader->PRawData;
  48.       uint32 InitSize = SectionHeader->SizeOfRawData;
  49.       uint32 TotalSize = SectionHeader->VirtualSize;
  50.  
  51.       uint32 SectionAddress = SectionHeader->VirtualAddress;
  52.       uint32 Type  = (SectionHeader->Flags & PE_SCN_CNT_CODE) ? 1 : 2;
  53.       if (SectionHeader->Flags & PE_SCN_CNT_UNINIT_DATA) {
  54.          // BSS segment. No data in file
  55.          Buffer = 0;
  56.          Type = 3;
  57.       }
  58.       else if (!(SectionHeader->Flags & (PE_SCN_MEM_WRITE | PE_SCN_MEM_EXECUTE))) {
  59.          // Constant segment
  60.          Type = 4;
  61.       }
  62.       if (SectionHeader->Flags & PE_SCN_LNK_COMDAT) {
  63.          // Communal section
  64.          Type |= 0x1000;
  65.       }
  66.       if (strnicmp(Name,"debug",5) == 0 || strnicmp(Name+1,"debug",5) == 0) {
  67.          // This is a debug section
  68.          Type = 0x10;
  69.       }
  70.       if (strnicmp(Name,".pdata", 6) == 0) {
  71.          // This section has exception information
  72.          Type = 0x11;
  73.       }
  74.  
  75.       uint32 Align = (SectionHeader->Flags & PE_SCN_ALIGN_MASK) / PE_SCN_ALIGN_1;
  76.       if (Align) Align--;
  77.  
  78.       // Save section record
  79.       Disasm.AddSection(Buffer, InitSize, TotalSize, SectionAddress, Type, Align, WordSize, Name);
  80.  
  81.       // Get relocations
  82.       // Pointer to relocation entry
  83.       union {
  84.          SCOFF_Relocation * p;  // pointer to record
  85.          int8 * b;              // used for address calculation and incrementing
  86.       } Reloc;
  87.       Reloc.b = Buf() + SectionHeader->PRelocations;
  88.  
  89.       for (irel = 0; irel < SectionHeader->NRelocations; irel++, Reloc.b += SIZE_SCOFF_Relocation) {
  90.  
  91.          // Relocation properties
  92.          int32 Section = isec + 1;
  93.          uint32 Offset = Reloc.p->VirtualAddress;
  94.          int32 Addend  = 0;
  95.          uint32 TargetIndex = Reloc.p->SymbolTableIndex;
  96.  
  97.          // Translate relocation type
  98.          uint32 Type = 0, Size = 0;
  99.          if (WordSize == 32) {
  100.             // 32 bit relocation types
  101.             // 0 = unknown, 1 = direct, 2 = self-relative, 3 = image-relative, 4 = segment relative
  102.             switch(Reloc.p->Type) {
  103.             case COFF32_RELOC_ABS:  // Ignore
  104.                continue;
  105.             case COFF32_RELOC_DIR32: // Direct, 32 bits
  106.                Type = 1;
  107.                Size = 4;
  108.                break;
  109.             case COFF32_RELOC_REL32: // Self-relative, 32 bits
  110.                Type = 2;
  111.                Size = 4;
  112.                Addend = -4;
  113.                break;
  114.             case COFF32_RELOC_IMGREL: // Image relative, 32 bits
  115.                Type = 4;
  116.                Size = 4;
  117.                break;
  118.             case COFF32_RELOC_SECREL: // Section relative, 32 bits
  119.                Type = 8;
  120.                Size = 4;
  121.                break;
  122.             case COFF32_RELOC_SECTION: // Section index of symbol, 16 bits
  123.                Type = 0x200;
  124.                Size = 2;
  125.                break;
  126.             default: // Other/unknown
  127.                Type = 0;
  128.                Size = 4;
  129.             }
  130.          }
  131.          else { // WordSize = 64
  132.             switch(Reloc.p->Type) {
  133.             case COFF64_RELOC_ABS:  // Ignore
  134.                continue;
  135.             case COFF64_RELOC_ABS32: // Absolute 32 bit
  136.                Type = 1;
  137.                Size = 4;
  138.                break;
  139.             case COFF64_RELOC_ABS64: // Absolute 64 bit
  140.                Type = 1;
  141.                Size = 8;
  142.                break;
  143.             case COFF64_RELOC_IMGREL: // Image relative 32 bit
  144.                Type = 4;
  145.                Size = 4;
  146.                break;
  147.             case COFF64_RELOC_REL32:    // Self-relative, 32 bits
  148.             case COFF64_RELOC_REL32_1:  // Self-relative + 1
  149.             case COFF64_RELOC_REL32_2:  // Self-relative + 2
  150.             case COFF64_RELOC_REL32_3:  // Self-relative + 3
  151.             case COFF64_RELOC_REL32_4:  // Self-relative + 4
  152.             case COFF64_RELOC_REL32_5:  // Self-relative + 5
  153.                Type = 2;
  154.                Size = 4;
  155.                Addend = - (Reloc.p->Type + 4 - COFF64_RELOC_REL32);
  156.                break;
  157.             case COFF64_RELOC_SECREL:   // Section relative
  158.                Type = 8;
  159.                Size = 4;
  160.                break;
  161.             default: // Other/unknown
  162.                Type = 0;
  163.                Size = 4;
  164.             }
  165.          }
  166.          // Save relocation record
  167.          Disasm.AddRelocation(Section, Offset, Addend, Type, Size, TargetIndex);
  168.       }
  169.    }
  170. }
  171.  
  172. void CCOF2ASM::MakeSymbolList() {
  173.    // Make Symbols list in Disasm
  174.    uint32 isym;                                  // Symbol index
  175.    uint32 naux = 0;                              // Number of auxiliary entries in old symbol table
  176.  
  177.    union {                                       // Pointer to old symbol table entries
  178.       SCOFF_SymTableEntry * p;                   // Normal pointer
  179.       int8 * b;                                  // Used for address calculation
  180.    } Sym, SymAux;
  181.  
  182.    // Set pointer to old SymbolTable
  183.    Sym.p = SymbolTable;
  184.  
  185.    // Loop through old symbol table
  186.    for (isym = 0; isym < (uint32)NumberOfSymbols; isym += 1+naux, Sym.b += (1+naux) * SIZE_SCOFF_SymTableEntry) {
  187.  
  188.       // Number of auxiliary entries
  189.       naux = Sym.p->s.NumAuxSymbols;
  190.  
  191.       if (Sym.p->s.SectionNumber != COFF_SECTION_ABSOLUTE
  192.       && (Sym.p->s.SectionNumber < 0
  193.       || (Sym.p->s.StorageClass != COFF_CLASS_EXTERNAL && Sym.p->s.StorageClass != COFF_CLASS_STATIC && Sym.p->s.StorageClass != COFF_CLASS_LABEL))) {
  194.          // Ignore irrelevant symbol table entries
  195.          continue;
  196.       }
  197.  
  198.       // Symbol properties
  199.       uint32 Index   = isym;
  200.       int32  Section = Sym.p->s.SectionNumber;
  201.       uint32 Offset  = Sym.p->s.Value;
  202.       uint32 Size    = 0;
  203.       uint32 Type    = (Sym.p->s.Type == COFF_TYPE_FUNCTION) ? 0x83 : 0;
  204.  
  205.       // Identify segment entries in symbol table
  206.       if (Sym.p->s.Value == 0 && Sym.p->s.StorageClass == COFF_CLASS_STATIC
  207.       && naux && Sym.p->s.Type != 0x20) {
  208.          // Note: The official MS specification says that a symbol table entry
  209.          // is a section if the storage class is static and the value is 0,
  210.          // but I have encountered static functions that meet these criteria.
  211.          // Therefore, I am also checking Type and naux.
  212.          Type = 0x80000082;
  213.       }
  214.  
  215.       const char * Name = GetSymbolName(Sym.p->s.Name);
  216.  
  217.       // Get scope. Note that these values are different from the constants defined in maindef.h
  218.       uint32 Scope = 0;
  219.       if (Sym.p->s.StorageClass == COFF_CLASS_STATIC || Sym.p->s.StorageClass == COFF_CLASS_LABEL) {
  220.          Scope = 2;             // Local
  221.       }
  222.       else if (Sym.p->s.SectionNumber > 0 || (Sym.p->s.SectionNumber == -1 && Sym.p->s.StorageClass == COFF_CLASS_EXTERNAL)) {
  223.          Scope = 4;             // Public
  224.       }
  225.       else {
  226.          Scope = 0x20;          // External
  227.       }
  228.  
  229.       // Check auxiliary symbol table entries
  230.       if (naux && Sym.p->s.Type == COFF_TYPE_FUNCTION) {
  231.          // Function symbol has auxiliary entry. Get information about size
  232.          SymAux.b = Sym.b + SIZE_SCOFF_SymTableEntry;
  233.          Size = SymAux.p->func.TotalSize;
  234.       }
  235.       // Check for special section values
  236.       if (Section < 0) {
  237.          if (Section == COFF_SECTION_ABSOLUTE) {
  238.             // Symbol is an absolute constant
  239.             Section = ASM_SEGMENT_ABSOLUTE;
  240.          }
  241.          else {
  242.             // Debug symbols, etc
  243.             Section = ASM_SEGMENT_ERROR;
  244.          }
  245.       }
  246.  
  247.       // Store new symbol record
  248.       Disasm.AddSymbol(Section, Offset, Size, Type, Scope, Index, Name);
  249.    }
  250. }
  251.  
  252. void CCOF2ASM::MakeDynamicRelocations() {
  253.    // Make dynamic base relocations for executable files
  254.    
  255.    // Find base relocation table
  256.    SCOFF_ImageDirAddress reldir;
  257.    if (!GetImageDir(5, &reldir)) {
  258.       // No base relocation table found
  259.       return;
  260.    }
  261.  
  262.    SCOFF_BaseRelocationBlock * pBaseRelocation;  // Start of dynamic base relocation section
  263.  
  264.    // Beginning of .reloc section is first base relocation block
  265.    pBaseRelocation = &Get<SCOFF_BaseRelocationBlock>(reldir.FileOffset);
  266.  
  267.    uint32 ROffset = 0;                        // Offset into .reloc section
  268.    uint32 BlockEnd;                           // Offset of end of current block
  269.    uint32 PageOffset;                         // Image-relative address of begin of page
  270.  
  271.    // Make pointer to header or entry in .reloc section
  272.    union {
  273.       SCOFF_BaseRelocationBlock * header;
  274.       SCOFF_BaseRelocation * entry;
  275.       int8 * b;
  276.    } Pointer;
  277.  
  278.    // Loop throung .reloc section
  279.    // while (ROffset < reldir.MaxOffset) {
  280.    while (ROffset < reldir.Size) {
  281.       // Set Pointer to current position
  282.       Pointer.header = pBaseRelocation;
  283.       Pointer.b += ROffset;
  284.  
  285.       // Read base relocation block
  286.       PageOffset = Pointer.header->PageRVA;
  287.       BlockEnd = ROffset + Pointer.header->BlockSize;
  288.  
  289.       // Read entries in this block
  290.       ROffset   += sizeof(SCOFF_BaseRelocationBlock);
  291.       Pointer.b += sizeof(SCOFF_BaseRelocationBlock);
  292.       // Loop through entries
  293.       while (ROffset < BlockEnd) {
  294.          // Set Pointer to current position
  295.          Pointer.header = pBaseRelocation;
  296.          Pointer.b += ROffset;
  297.  
  298.          if (Pointer.entry->Type == COFF_REL_BASED_HIGHLOW) {
  299.             // Add relocation record, 32 bit
  300.             // Section = ASM_SEGMENT_IMGREL means offset is image-relative
  301.             // Type = 0x20 means already relocated to image base
  302.             Disasm.AddRelocation(ASM_SEGMENT_IMGREL, Pointer.entry->Offset + PageOffset, 0, 0x21, 4, 0);
  303.          }
  304.          else if (Pointer.entry->Type == COFF_REL_BASED_DIR64) {
  305.             // Add relocation record, 64 bit
  306.             Disasm.AddRelocation(ASM_SEGMENT_IMGREL, Pointer.entry->Offset + PageOffset, 0, 0x21, 8, 0);
  307.          }
  308.  
  309.          // Go to next
  310.          ROffset += sizeof(SCOFF_BaseRelocation);
  311.          if (Pointer.entry->Type == COFF_REL_BASED_HIGHADJ) ROffset += sizeof(SCOFF_BaseRelocation);
  312.       }
  313.       // Finished block. Align by 4
  314.       ROffset = (ROffset + 3) & uint32(-4);
  315.    }
  316. }
  317.  
  318. void CCOF2ASM::MakeImportList() {
  319.    // Make imported symbols for executable files
  320.  
  321.    // Find import table
  322.    SCOFF_ImageDirAddress impdir;
  323.    if (!GetImageDir(1, &impdir)) {
  324.       // No import table found
  325.       return;
  326.    }
  327.  
  328.    // Beginning of import section is import directory
  329.    SCOFF_ImportDirectory * pImportDirectory = &Get<SCOFF_ImportDirectory>(impdir.FileOffset);
  330.  
  331.    // Check if 64 bit
  332.    int Is64bit = OptionalHeader->h64.Magic == COFF_Magic_PE64; // 1 if 64 bit
  333.    uint32 EntrySize = Is64bit ? 8 : 4;           // Size of address table entries
  334.  
  335.    uint32 NameOffset;                            // Offset to name
  336.    const char * SymbolName;                      // Name of symbol
  337.    const char * DLLName;                         // Name of DLL containing symbol
  338.    char NameBuffer[64];                          // Buffer for creating name of ordinal symbols
  339.    uint32 SectionOffset;                         // Section-relative address of current entry
  340.    uint32 HintNameOffset;                        // Section-relative address of hint/name table
  341.    uint32 FirstHintNameOffset = 0;               // First HintNameOffset = start of hint/name table
  342.    uint32 AddressTableOffset;                    // Offset of import address table relative to import lookup table
  343.  
  344.    // Pointer to current import directory entry
  345.    SCOFF_ImportDirectory * ImportEntry = pImportDirectory;
  346.    // Pointer to current import lookup table entry
  347.    int32 * LookupEntry = 0;
  348.    // Pointer to current hint/name table entry
  349.    SCOFF_ImportHintName * HintNameEntry;
  350.  
  351.    // Loop through import directory until null entry
  352.    while (ImportEntry->DLLNameRVA) {
  353.       // Get DLL name
  354.       NameOffset = ImportEntry->DLLNameRVA - impdir.VirtualAddress;
  355.       if (NameOffset < impdir.MaxOffset) {
  356.          DLLName = &Get<char>(impdir.FileOffset + NameOffset);
  357.       }
  358.       else {
  359.          DLLName = "?";
  360.       }
  361.  
  362.       // Get lookup table
  363.       SectionOffset = ImportEntry->ImportLookupTableRVA;
  364.       if (SectionOffset == 0) SectionOffset = ImportEntry->ImportAddressTableRVA;
  365.       if (SectionOffset == 0) continue;
  366.       // Get distance from import lookup table to import address table
  367.       AddressTableOffset = ImportEntry->ImportAddressTableRVA - SectionOffset;
  368.       // Section relative address
  369.       SectionOffset -= impdir.VirtualAddress;
  370.       if (SectionOffset >= impdir.MaxOffset) break;  // Out of range
  371.  
  372.       // Loop through lookup table
  373.       while (1) {
  374.          // Pointer to lookup table entry
  375.          LookupEntry = &Get<int32>(impdir.FileOffset + SectionOffset);
  376.  
  377.          // End when entry is empty
  378.          if (!LookupEntry[0]) break;
  379.  
  380.          if (LookupEntry[Is64bit] < 0) {
  381.             // Imported by ordinal. Give it a name
  382.             strncpy(NameBuffer, DLLName, 20);
  383.             // Remove dot from name
  384.             char * dot = strchr(NameBuffer,'.');
  385.             if (dot) *dot = 0;
  386.             // Add ordinal number to name
  387.             sprintf(NameBuffer+strlen(NameBuffer), "_Ordinal_%i", uint16(LookupEntry[0]));
  388.             SymbolName = NameBuffer;
  389.          }
  390.          else {
  391.             // Find entry in hint/name table
  392.             HintNameOffset = (LookupEntry[0] & 0x7FFFFFFF) - impdir.VirtualAddress;
  393.             if (HintNameOffset >= impdir.MaxOffset) goto LOOPNEXT;  // Out of range
  394.             if (!FirstHintNameOffset) FirstHintNameOffset = HintNameOffset;
  395.             HintNameEntry = &Get<SCOFF_ImportHintName>(impdir.FileOffset + HintNameOffset);
  396.             // Get name
  397.             SymbolName = HintNameEntry->Name;
  398.          }
  399.          // Add symbol
  400.          Disasm.AddSymbol(ASM_SEGMENT_IMGREL, impdir.VirtualAddress + SectionOffset + AddressTableOffset,
  401.             EntrySize, 0xC, 0x20, 0, SymbolName, DLLName);
  402.  
  403.          // Loop next
  404.          LOOPNEXT:
  405.          SectionOffset += EntrySize;
  406.       }
  407.  
  408.       // Loop next
  409.       ImportEntry++;
  410.    }
  411.  
  412.    // Make label for import name table
  413.    if (FirstHintNameOffset) {
  414.       Disasm.AddSymbol(ASM_SEGMENT_IMGREL, impdir.VirtualAddress + FirstHintNameOffset, 1, 1, 1, 0, "Import_name_table");
  415.    }
  416. }
  417.  
  418. void CCOF2ASM::MakeExportList() {
  419.    // Make exported symbols for executable files
  420.  
  421.    // Define entry point
  422.    if (OptionalHeader->h32.AddressOfEntryPoint) {
  423.       Disasm.AddSymbol(ASM_SEGMENT_IMGREL, OptionalHeader->h32.AddressOfEntryPoint, 0, 0x83, 4, 0, "Entry_point");
  424.    }
  425.  
  426.    // Get export table directory address
  427.    SCOFF_ImageDirAddress expdir;
  428.  
  429.    // Exported names
  430.    if (!GetImageDir(0, &expdir)) {
  431.       // No export directory
  432.       return;
  433.    }
  434.  
  435.    // Beginning of export section is export directory
  436.    SCOFF_ExportDirectory * pExportDirectory = &Get<SCOFF_ExportDirectory>(expdir.FileOffset);
  437.  
  438.    // Find ExportAddressTable
  439.    uint32 ExportAddressTableOffset = pExportDirectory->ExportAddressTableRVA - expdir.VirtualAddress;
  440.    if (ExportAddressTableOffset == 0 || ExportAddressTableOffset >= expdir.MaxOffset) {
  441.       // Points outside section
  442.       err.submit(2035);  return;
  443.    }
  444.    uint32 * pExportAddressTable = &Get<uint32>(expdir.FileOffset + ExportAddressTableOffset);
  445.  
  446.    // Find ExportNameTable
  447.    if (pExportDirectory->NamePointerTableRVA == 0) {
  448.        return;  // I don't know why this happens
  449.    }
  450.    uint32 ExportNameTableOffset = pExportDirectory->NamePointerTableRVA - expdir.VirtualAddress;
  451.    if (ExportNameTableOffset == 0 || ExportNameTableOffset >= expdir.MaxOffset) {
  452.       // Points outside section
  453.       err.submit(2035);  return;
  454.    }
  455.    uint32 * pExportNameTable = &Get<uint32>(expdir.FileOffset + ExportNameTableOffset);
  456.  
  457.    // Find ExportOrdinalTable
  458.    uint32 ExportOrdinalTableOffset = pExportDirectory->OrdinalTableRVA - expdir.VirtualAddress;
  459.    if (ExportOrdinalTableOffset == 0 || ExportOrdinalTableOffset >= expdir.MaxOffset) {
  460.       // Points outside section
  461.       err.submit(2035);  return;
  462.    }
  463.    uint16 * pExportOrdinalTable = &Get<uint16>(expdir.FileOffset + ExportOrdinalTableOffset);
  464.  
  465.    // Get further properties
  466.    uint32 NumExports = pExportDirectory->AddressTableEntries;
  467.    uint32 NumExportNames = pExportDirectory->NamePointerEntries;
  468.    uint32 OrdinalBase = pExportDirectory->OrdinalBase;
  469.  
  470.    uint32 i;                                     // Index into pExportOrdinalTable and pExportNameTable
  471.    uint32 Ordinal;                               // Index into pExportAddressTable
  472.    uint32 Address;                               // Image-relative address of symbol
  473.    uint32 NameOffset;                            // Section-relative address of name
  474.    uint32 FirstName = 0;                         // Image-relative address of first name table entry
  475.    const char * Name = 0;                        // Name of symbol
  476.    char NameBuffer[64];                          // Buffer for making name
  477.  
  478.    // Loop through export tables
  479.    for (i = 0; i < NumExports; i++) {
  480.  
  481.       Address = 0;
  482.       Name = "?";
  483.  
  484.       // Get ordinal from table
  485.       Ordinal = pExportOrdinalTable[i];
  486.       // Address table is indexed by ordinal
  487.       if (Ordinal < NumExports) {
  488.          Address = pExportAddressTable[Ordinal];
  489.       }
  490.       // Find name if there is a name list entry
  491.       if (i < NumExportNames) {
  492.          NameOffset = pExportNameTable[i] - expdir.VirtualAddress;
  493.          if (NameOffset && NameOffset < expdir.MaxOffset) {
  494.             Name = &Get<char>(expdir.FileOffset + NameOffset);
  495.             if (FirstName == 0) FirstName = pExportNameTable[i];
  496.          }
  497.       }
  498.       else {
  499.          // No name. Make name from ordinal number
  500.          sprintf(NameBuffer, "Ordinal_%i", Ordinal + OrdinalBase);
  501.          Name = NameBuffer;
  502.       }
  503.  
  504.       // Define symbol
  505.       Disasm.AddSymbol(ASM_SEGMENT_IMGREL, Address, 0, 0x83, 4, 0, Name);
  506.    }
  507.  
  508.    // Make label for export section
  509.    Disasm.AddSymbol(ASM_SEGMENT_IMGREL, expdir.VirtualAddress, 4, 3, 2, 0, "Export_tables");
  510.  
  511.    // Make labels for export tables
  512.    Disasm.AddSymbol(ASM_SEGMENT_IMGREL, ExportAddressTableOffset - expdir.FileOffset + expdir.VirtualAddress, 4, 3, 2, 0, "Export_address_table");
  513.    Disasm.AddSymbol(ASM_SEGMENT_IMGREL, ExportOrdinalTableOffset - expdir.FileOffset + expdir.VirtualAddress, 4, 3, 2, 0, "Export_ordinal_table");
  514.    Disasm.AddSymbol(ASM_SEGMENT_IMGREL, ExportNameTableOffset - expdir.FileOffset + expdir.VirtualAddress, 4, 3, 2, 0, "Export_name_pointer_table");
  515.    Disasm.AddSymbol(ASM_SEGMENT_IMGREL, FirstName, 1, 1, 2, 0, "Export_name_table");
  516. }
  517.  
  518. void CCOF2ASM::MakeListLabels() {
  519.    // Attach names to all image directories
  520.    SCOFF_ImageDirAddress dir;
  521.    uint32 i;
  522.  
  523.    for (i = 0; i < NumImageDirs; i++) {
  524.       if (GetImageDir(i, &dir)) {
  525.          // Found a directory. Make label for it
  526.          Disasm.AddSymbol(ASM_SEGMENT_IMGREL, dir.VirtualAddress, 4, 0, 1, 0, dir.Name);
  527.       }
  528.    }
  529. }
  530.