| Index | Manual | Download | Source | Macros | Embedded word Case ins.
Sitemap | Links | Forum | Tests | Projects

# pflibomf.htm
Class
PFLIBOMF_SYMBOL
Procedures
PflibomfCompile
PflibomfLoadPgm
PflibomfStoreModule

This source PFLIBOMF creates and loads object and import library in OMF standard.

             EUROASM NOWARN=2101
    pflibomf PROGRAM FORMAT=COFF,MODEL=FLAT,WIDTH=32
             INCLUDEHEAD euroasm.htm, \  Interface (structures, symbols and macros) of other modules used in this source.
                         ea.htm,eaopt.htm,exp.htm,msg.htm,pf.htm,pfomf.htm,pgm.htm, \
                         pgmopt.htm,reloc.htm,sss.htm,stm.htm,sym.htm

     pflibomf HEAD ; Start module interface.

↑ PFLIBOMF_SYMBOL
  €ASM internal structure which describes symbol names in LIBOMF and LIBCOF dictionary. The dictionary contains records for each public symbol.
  In LIBOMF the member .ModPtr is OWORD aligned and it represents file address of the module (of its LIBHDR record) from the start of library file. Member .ModIndex is unused.
  In LIBCOF the member .ModPtr is WORD aligned and it temporarily represents offset of the module (of its archive header) from the first module. The offset will be elevated by the size of COFF-archive signature plus size of both linker members plus size of longnames member, before it is stored to LIBCOF dictionary, i.e. to linker members.

    PFLIBOMF_SYMBOL STRUC
    .NamePtr  D D ; Pointer to the name of public symbol from the module.
    .NameSize D D ; Netto size of the symbol name.
    .ModPtr   D D ; Pointer to the archive member header followed with object module.
    .ModIndex D D ; 1-based ordinal number of object module in the COFF library.
    ENDSTRUC PFLIBOMF_SYMBOL

     ENDHEAD pflibomf  ; End of module interface.

↑ PflibomfLoadPgm BasePgm, ObjBegin, ObjSize, FileNamePtr

  PflibomfLoadPgm reads all modules from an OMF library file and converts it to PGM structures which then will be stored on `BasePgm.ModulePgmList`.
  The module starts with LHEADR or THEADR record and it terminates with MODEND or MODEND32 record.

  The actual job is performed by PfomfLoadModule, a common procedure used by PflibomfLoadPgm and PfomfLoadPgm.

Input
  BasePgm is pointer to an existing PGM to which the library is linked/imported.
  ObjBegin is pointer to the contents of library file mapped in memory by the caller.
  ObjSize is number of bytes in the file.
  FileNamePtr is pointer to zero-terminated imported file name (used in error reports).
Output
  Linked/imported modules in the form of PGM are stored on `BasePgm.ModulePgmList`.
Error
  Errors are reported with macro Msg.
Invokes
  PfomfLoadModule PfomfLoadRecord
Tested by
  t8206

    PflibomfLoadPgm Procedure  BasePgm, ObjBegin, ObjSize, FileNamePtr
    ObjEnd   LocalVar                      ; Pointer to the end of object file mapped in memory.
    ModBegin LocalVar                      ; Pointer to the 1st module record (LHEADR or THEADR).
        MOV ESI,[%ObjBegin]
        MOV ECX,[%ObjSize]
        XOR EAX,EAX
        ADD ECX,ESI
        MOV [%ModBegin],EAX
        MOV [%ObjEnd],ECX
        MOV EDI,ESI
    .NextRecord:                           ; Check OMF record which starts at EDI.
        Invoke PfomfLoadRecord::,EDI,[%ObjBegin],[%ObjEnd],[%FileNamePtr]
        JC .90:                            ; Abort on error. E8532 was already reported.
        MOV EDI,ESI                        ; The current record.
        TEST ECX
        JZ .90:                            ; If there are no more records in file.
    .20:MOV AL,[ESI]                       ; Get the record type.
        LEA EDI,[ESI+ECX]                  ; Let EDI point to the next OMF record.
        Dispatch AL,THEADR,LHEADR,MODEND,MODEND32,LIBHDR,LIBEND
        JMP .NextRecord:                   ; Other record types are irrelevant here.

        ; Record handlers
        ; Input: ESI=^current record.
        ;        ECX=curent record brutto size.
        ;        EDI=^next record.
    .LIBHDR:                               ; alias MSLIBR identifies a file of OMF modules followed by library dictionary.
        CMP ECX,12
        JB .E8533:                         ; LIBHDR record must be 16..512 bytes long.
        MOV EAX,[ESI+3]                    ; Get dictionary offset (dictionary is ignored by €ASM).
        ADD EAX,[%ObjBegin]
        MOV [%ObjEnd],EAX                  ; Update the end of loaded contents.
        TESTB [ESI+9],1                    ; Library flag "case sensitive".
        Msg cc=Z,'3653'                    ; Case insensitivity of linked and imported symbols is not supported.
    .LIBEND:                               ; This should be the last record in file. Ignore.
        JMP .NextRecord:

    .W3656:                                ; Module started with record type !1Bh at "!2$"[!3Hh] is not terminated with MODEND. Ignored.
        MOV EAX,[%ModBegin]
        MOV CL,[EAX]
        SUB EAX,[%ObjBegin]
        Msg '3656',ECX,[%FileNamePtr],EAX
        JMP .NextRecord:

    .E8533:                                ; Abort linking due to error in file at record ESI.
        MOV CL,[ESI]
        SUB ESI,[%ObjBegin]
        Msg '8533',ECX,[%FileNamePtr],ESI  ; Invalid OMF record type !1Bh at "!2$"[!3Hh].
        JMP .90:                           ; Abort the loading of file.

    .LHEADR:
    .THEADR:
       MOV EAX,[%ModBegin]
       TEST EAX
       JNZ .W3656:                         ; Previous module started at EAX but wasn't closed by MODEND yet.
       MOV [%ModBegin],ESI
       JMP .NextRecord:

    .MODEND32:
    .MODEND:
        MOV EAX,[%ModBegin]
        TEST EAX
        JZ .50:                            ; MODEND terminates module which wasn't introduced by THEADR/LHEADR. Ignore.
        Invoke PfomfLoadModule::,[%BasePgm],[%ObjBegin],EAX,EDI,[%FileNamePtr],pgmoptLIBOMF
    .50:XOR EAX,EAX
        MOV [%ModBegin],EAX
        ; In OMF library MODEND may be followed by 0x00 alignment bytes. Skip them now.
    .60:CMP EDI,[%ObjEnd]                  ; EDI should point to the record which follows MODEND.
        JNB .90:
        CMPB [EDI],0                       ; Is it the intermodule alignment stuff?
        JNE .NextRecord:
        INC EDI
        JMP .60:
    .90: EndProcedure PflibomfLoadPgm

↑ PflibomfCompile OutputStream, Pgm

  PflibomfCompile is constructor for object/import library of OMF modules.
  This procedure will create OMF library file with one module created from the base program, if not empty, and with one module created from each linked program loaded on `Pgm.ModulePgmList`.

  Each library member starts with LHEADR record and it ends with MODEND/MODEND32 record. Library page size (alignment of LHEADR records) is fixed at 16.

  Library modules are followed by hashed dictionary of public/exported names, as specified in [OMF], Appendix 2.

Input
  OutputStream is pointer to an empty STREAM for the output file contents.
  Pgm is pointer to a base PGM which may be empty or which represents a completely assembled module. Other library modules are loaded on `Pgm.ModulePgmList`.
Output
  OutputStream is filled with output file contents.
Error
  Errors are reported with macro Msg.
Invoked from
  PfOutput
Invokes
  EaBufferRelease EaBufferReserve EaStreamAlign ExpGetNextPrime PflibomfStoreModule PfomfStoreRecord
Tested by
  t8206 t8550

    PflibomfCompile Procedure OutputStream, Pgm
    RecBuffer     LocalVar ; Buffer for the contents of one OMF record (without record type, length, checksum).
    SymBuffer     LocalVar ; Buffer for PFLIBOMF_SYMBOL records.
    NrOfModules   LocalVar ; Number of modules in library.
    DirPtr        LocalVar ; Pointer to 1st directory block on Pgm.Pool.
    NrOfBlocks    LocalVar ; Prime number of used directory blocks, 512 bytes each.
    FileAddr      LocalVar ; File-address of symbol's module. Always OWORD aligned.
    NameSize      LocalVar ; Number of bytes in the name of the symbol inserted to dictionary (+1).
    FwdPtr        LocalVar ; Pointer to the first character of symbol name.
    BwdPtr        LocalVar ; Pointer behind the last  character of symbol name.
    BucketX       LocalVar ; Ordinal number of bucket 0..36.
    BucketD       LocalVar ; Overflow delta bucket 1..36.
    BlockX        LocalVar ; Ordinal number of block 0..[%NrOfBlocks]-1.
    BlockD        LocalVar ; Overflow delta block    1..[%NrOfBlocks]-1.
    BlockXstart   LocalVar ; Ordinal number of block 0..[%NrOfBlocks]-1 before delta applied.
        Invoke EaBufferReserve::,PflibomfCompile
        MOV [%RecBuffer],EAX
        Invoke EaBufferReserve::,PflibomfCompile
        MOV [%SymBuffer],EAX
        MOV EBX,[%Pgm]
        ; LIBHDR library header. It is 16 bytes long.
        BufferStoreDword [%RecBuffer],0             ; Padding.
        BufferStoreDword [%RecBuffer],0             ; The actual contents of LIBHDR will be
        BufferStoreDword [%RecBuffer],0             ;   patched later, at .40: and .85:,
        Invoke PfomfStoreRecord::,[%OutputStream],LIBHDR,[%RecBuffer]
        ; First store module from the base program EBX.
        SetSt [EBX+PGM.Status],pgmIsModule
        MOV EDX,pgmoptWidthMask+pgmoptModelMask
        AND EDX,[EBX+PGM.Pgmopt.Status]
        Invoke PflibomfStoreModule::,[%OutputStream],EBX,[%SymBuffer]
        Invoke EaStreamAlign::,[%OutputStream],16,0
    .27:; Then store a module from each linked program.
        ListGetFirst [EBX+PGM.ModulePgmList]
        JZ .36:
    .33:MOV EDI,EAX
        SetSt [EDI+PGM.Status],pgmIsModule
        SetSt [EDI+PGM.Pgmopt.Status],EDX           ; Use module properties MODEL= and WIDTH= from the base LIBOMF program.
        Invoke PflibomfStoreModule::,[%OutputStream],EDI,[%SymBuffer]
        Invoke EaStreamAlign::,[%OutputStream],16,0
    .34:ListGetNext EDI                             ; The next linked program.
        JNZ .33:
    .36:; LIBEND record. Its size will align the output to 512.
        StreamGetSize [%OutputStream]
        ADD EAX,3+1
        NEG EAX
        MOV ECX,0x0000_01FF
        AND ECX,EAX                                 ; Size of 512 alignment stuff.
        JZ .40:
        BufferResize [%RecBuffer],ECX
    .40:Invoke PfomfStoreRecord::,[%OutputStream],LIBEND,[%RecBuffer]
        ; The library body is written, now patch the LIBHDR record on OutputStream.
        MOV EBX,[%OutputStream]
        StreamGetSize EBX                           ; EAX is now the dictionary offset, 512-aligned.
        MOV EDI,[EBX+STREAM.ReadPtr]                ; EDI now points to the beginning of streamed file data.
        MOV [EDI+3],EAX                             ; Patch the file address of dictionary inside LIBHDR record.
        ; Compile library directory.  Dictionary size will be patched later, at .85:.
        BufferRetrieve [%SymBuffer]                 ; Direcory contains FA and symbol name of each module,
        LEA EDX,[ESI+ECX]                           ;     as stored by PflibomfStoreModule at .35:.
        ; Initialize the directory. First try to guess the required number of blocks.
        SUB EAX,EAX                                 ; EAX will keep the estimated total size of names.
    .43:CMP ESI,EDX
        JNB .46:
        ADD EAX,[ESI+PFLIBOMF_SYMBOL.NameSize]
        ADD EAX,1+2+1                               ; Add PascalSize byte + WORD of module paragraph FA + alignment.
        ADD ESI,SIZE# PFLIBOMF_SYMBOL               ; The next record.
        JMP .43:
    .46:SUB EDX,EDX
        MOV ECX,512-38                              ; Block room for names.
        DIV ECX                                     ; Get required number of blocks.
        TEST EAX
        JNZ .47:
        INC EAX
    .47:MOVD [%NrOfBlocks],EAX                      ; Estimation based on total name size.
    .48:Invoke ExpGetNextPrime::,[%NrOfBlocks]      ; Used number of blocks must be prime.
        MsgUnexpected cc=C
        MOV [%NrOfBlocks],EAX                       ; Round the NrOfBlocks up to the nearest prime number.
        SAL EAX,9                                   ; Each block is 29 = 512 bytes long.
        MOV ECX,EAX
        MOV EBX,[%Pgm]
        PoolNew [EBX+PGM.Pool],EAX,Align=WORD       ; Allocate %NrOfBlocks * 512 bytes.
        MOV [%DirPtr],EAX
        MOV EDI,EAX
        MOV ESI,EAX
        SAR ECX,2
        XOR EAX,EAX
        REP STOSD                                   ; Clear all blocks.
        MOV ECX,[%NrOfBlocks]                       ; At least 2.
    .50:MOVB [ESI+37],38/2                          ; Initialize byte-pointer to the free position in block.
        ADD ESI,512
        LOOP .50:                                   ; The next block.
        ; Populate directory blocks with public symbol names from %SymBuffer.
        BufferRetrieve [%SymBuffer]
        LEA EDX,[ESI+ECX]                           ; End of PFLIBOMF_SYMBOL records.
    .53:CMP ESI,EDX
        JNB .85:                                    ; If there are no more symbols in library.
        MOV EAX,[ESI+PFLIBOMF_SYMBOL.ModPtr]
        MOV [%FileAddr],EAX
        MOV EDI,[ESI+PFLIBOMF_SYMBOL.NamePtr]
        MOV [%FwdPtr],EDI
        MOV EAX,[ESI+PFLIBOMF_SYMBOL.NameSize]      ; Symbol netto NameSize. Never exceeds 255.
        MOV [%NameSize],EAX
        ADD EAX,EDI
        MOV [%BwdPtr],EAX
        PUSH EDX,ESI
          ; Initialize the hash engine.
          MOV EDI,[%BwdPtr]
          MOV ESI,[%FwdPtr]
          MOV EAX,[%NameSize]
          OR AL,0x20                                ; Pascal-size byte is converted to lowercase, too (sic!).
          MOV [%BlockX],EAX
          MOV [%BucketD],EAX
          XOR EAX,EAX
          MOV [%BlockD],EAX
          MOV [%BucketX],EAX
          ; Compute hash.
    .58:  DEC EDI
          MOV AL,[EDI]                              ; Read backward.
          OR AL,0x20                                ; Convert character to lower case.
          MOV ECX,[%BucketX]
          MOV EDX,[%BlockD]
          ROR CX,2
          ROL DX,2
          XOR ECX,EAX
          XOR EDX,EAX
          MOV [%BucketX],ECX
          MOV [%BlockD],EDX
          CMP EDI,[%FwdPtr]
          JBE .60:
          LODSB                                     ; Read forward.
          OR AL,0x20                                ; Convert character to lower case.
          MOV ECX,[%BlockX]
          MOV EDX,[%BucketD]
          ROL CX,2
          ROR DX,2
          XOR ECX,EAX
          XOR EDX,EAX
          MOV [%BlockX],ECX
          MOV [%BucketD],EDX
          JMP .58:
    .60:  MOV ECX,37
          MOV EAX,[%BucketX]
          SUB EDX,EDX
          DIV ECX
          MOV [%BucketX],EDX
          MOV EAX,[%BucketD]
          SUB EDX,EDX
          DIV ECX
          TEST EDX
          JNZ .63:
          INC EDX
     .63: MOV [%BucketD],EDX
          MOV ECX,[%NrOfBlocks]
          MOV EAX,[%BlockX]
          SUB EDX,EDX
          DIV ECX
          MOV [%BlockX],EDX
          MOV [%BlockXstart],EDX
          MOV EAX,[%BlockD]
          SUB EDX,EDX
          DIV ECX
          TEST EDX
          JNZ .66:
          INC EDX
    .66:  MOV [%BlockD],EDX                         ; Hashes are computed now.
          ; Toss the symbol name to dictionary. First calculate required record size.
          MOV ECX,[%NameSize]
          ADD ECX,1+2+1                             ; Byte PascalName size + word FileAddr + optional alignment.
          AND ECX,-2                                ; ECX is now required aligned record size.
    .68:  MOV EDI,[%BlockX]
          MOV EBX,[%BucketX]                        ; 0..36.
          SAL EDI,9
          ADD EDI,[%DirPtr]                         ; EDI now points to the directory block selected by BlockX.
    .70:  MOVZXB EAX,[EDI+EBX]                      ; Look if the bucket EBX is free.
          TEST EAX
          JZ .76:                                   ; If bucket is unoccupied.
          ADD EBX,[%BucketD]                        ; Otherwise apply bucket delta hash.
          MOV EAX,EBX
          SUB EDX,EDX
          MOV EBX,37
          DIV EBX                                   ; Modulo the bucket ordinal by 37.
          MOV EBX,EDX
          CMP EBX,[%BucketX]
          JNE .70:                                  ; Go to try another bucket in the same block EDI.
          ; Starting bucket was reached again, ergo all buckets in block EDI are occupied.
    .72:  MOVB [EDI+37],0xFF                        ; Mark the block as full.
    .73:  ; Block EDI is full, try another one.
          MOV EDI,[%BlockX]                         ; Current block ordinal 0..[%NrOfBlocks]-1.
          ADD EDI,[%BlockD]                         ; Add delta to block ordinal.
          MOV EAX,EDI
          SUB EDX,EDX
          MOV EBX,[%NrOfBlocks]
          DIV EBX                                   ; Modulo the block ordinal by [%NrOfBlocks].
          MOV [%BlockX],EDX                         ; Store as the new current block ordinal.
          CMP EDX,[%BlockXstart]
          JNE .68:                                  ; Try to find a free bucket in the new block EDI.
          ; This is a rare case when NrOfBlocks was underestimated.
          ; Allocated space for blocks will be abandoned
          ;    and building of the directory starts from scratch with bigger NrOfBlocks.
          JMP .48:                                  ; Increase %NrOfBlocks to the nearest prime and start again.
    .76:  ; Bucket EBX is free. Inspect if there's enough free space for the required size ECX.
          MOV AL,[EDI+37]                           ; Free word offset in block.
          CMP AL,0xFF
          JE .73:                                   ; If block EDI is full, go to apply %BlockD and try another one.
          SAL EAX,1                                 ; EAX is now block offset of free space.
          LEA EDX,[EAX+ECX]                         ; Block offset of potential end of name.
          CMP EDX,512
          JA .72:                                   ; If overflow.
          JB .78:
          ; This was the last symbol, no more free space left. Mark the block as full.
          DEC EDX                                   ; EDX is now 0x1FF which will signalize full block at pseudobucket 37.
    .78:  SAR EDX,1                                 ; Block EDI with bucket EBX will be used.
          MOV [EDI+37],DL                           ; New free word offset in block.
          LEA EDX,[EDI+EAX]                         ; Pointer to the free space.
          SAR EAX,1
          MOV [EDI+EBX],AL                          ; Write the word address to bucket EBX.
          MOV EAX,[%NameSize]                       ; Never above 255.
          MOV [EDX],AL                              ; Pascal name size byte.
          INC EDX
          CopyTo EDX,[%FwdPtr],Size=EAX             ; Transfer the symbol name.
          ADD EDX,EAX
          MOV EAX,[%FileAddr]
          SHR EAX,4                                 ; Convert FileAddr to paragraph file address of module (LHEADR).
          MOV [EDX],AX
    .80:POP ESI,EDX
        ADD ESI,SIZE# PFLIBOMF_SYMBOL
        JMP .53:                                    ; Go to fetch the next symbol.
    .85:; NrOfSymbols is finally known, patch the LIBHDR record on OutputStream.
        MOV EBX,[%OutputStream]
        MOV EDI,[EBX+STREAM.ReadPtr]                ; EDI is now beginning of streamed file data.
        MOV EAX,[%NrOfBlocks]
        MOV [EDI+7],AX                              ; Patch the dictionary size in blocks inside LIBHDR record.
        MOVB [EDI+9],0000_0001b                     ; Flag as case-sensitive.
        MOVB [EDI+15],0                             ; Checksum is no longer valid, change it to 0.
        MOV ESI,[%DirPtr]
        MOV ECX,[%NrOfBlocks]
        SAL ECX,9
        StreamStore [%OutputStream],ESI,ECX
    .90:Invoke EaBufferRelease::,[%SymBuffer]
        Invoke EaBufferRelease::,[%RecBuffer]
        EndProcedure PflibomfCompile

↑ PflibomfStoreModule Stream, Module, SymbolBuf
  PflibomfStoreModule will store one OMF module to the Stream if it contains at least one public/export/import symbol.
  Module file address and name of its public/exported symbol(s) will be stored to SymbolBuf.
Input
  Stream is pointer to a STREAM for the output library file contents. The stream may already contain previously stored library modules.
  Module is pointer to a PGM representing the module being added to library.
  SymbolBuf is pointer to the output BUFFER where module name will be stored in the form of PFLIBOMF_SYMBOL record.
Output
  OutputStream is filled with OMF module contents (records LIBHDR..MODEND).
Error
  Errors are reported with macro Msg.
Invokes
  PfomfStoreModule
Invoked by
  PflibomfCompile

    PflibomfStoreModule Procedure Stream, Module, SymbolBuf
    LibSymbol  LocalVar Size=SIZE# PFLIBOMF_SYMBOL
         MOV EBX,[%Module]
         SetSt [EBX+PGM.Status],pgmIsModule
         ; Fill SymbolBuf with global symbol identificators.
         LEA EDI,[%LibSymbol]
         StreamGetSize [%Stream]                     ; Previously stored modules?
         MOV [EDI+PFLIBOMF_SYMBOL.ModPtr],EAX
         MOVD [EDI+PFLIBOMF_SYMBOL.ModIndex],0       ; Module index is not employed in LIBOMF format.
         RstSt [EBX+PGM.Status],pgmSelected
         ListGetFirst [EBX+PGM.SymList]
         JZ .90:
    .10: JNSt [EAX+SYM.Status],symPublic|symExport|symImport,.30:
         SetSt [EBX+PGM.Status],pgmSelected          ; At least one global symbol exists.
         MOV ESI,[EAX+SYM.NamePtr]
         MOV ECX,[EAX+SYM.NameSize]
         MOV [EDI+PFLIBOMF_SYMBOL.NamePtr],ESI
         XOR EDX,EDX
         DEC DL                                      ; EDX=255.
         CMP ECX,EDX
         JBE .20:
         Msg '3654',EAX                              ; Name of symbol "!1S" exceeds 255 characters. Truncated.
         XCHG ECX,EDX
    .20: MOV [EDI+PFLIBOMF_SYMBOL.NameSize],ECX
         BufferStore [%SymbolBuf],EDI,SIZE# PFLIBOMF_SYMBOL
    .30: ListGetNext EAX                             ; The next symbol.
         JNZ .10:
    .40: JNSt [EBX+PGM.Status],pgmSelected,.90:
         Invoke PfomfStoreModule::,[%Stream],EBX
    .90:EndProcedure PflibomfStoreModule

      ENDPROGRAM pflibomf

▲Back to the top▲