EuroAssembler Index Manual Download Source Macros


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.


pflibomf PROGRAM FORMAT=COFF,MODEL=FLAT,WIDTH=32
 INCLUDEHEAD "euroasm.htm" ; Interface (structures, symbols and macros) of other modules.
 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.
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
t7016 t7037 t7106 t7127 t7196 t7217 t7310 t7331 t7424 t7463 t7478 t7499 t7565
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 no more records in file.
.20:MOV AL,[ESI] ; Get 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 not used 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 file load.

.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
t7100 t7103 t7106 t7109 t7112 t7115 t7118 t7121 t7127 t7133 t7139 t7184 t7187
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
     Invoke PflibomfStoreModule::,[%OutputStream],EBX,[%SymBuffer]
     Invoke EaStreamAlign::,[%OutputStream],16
.27: ; Then store module from each linked program.
     ListGetFirst [EBX+PGM.ModulePgmList]
     JZ .36:
.33: MOV EDI,EAX
     SetSt [EDI+PGM.Status],pgmIsModule
     Invoke PflibomfStoreModule::,[%OutputStream],EDI,[%SymBuffer]
     Invoke EaStreamAlign::,[%OutputStream],16
.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:
 .38:BufferStoreByte [%RecBuffer],0
     LOOP .38:
 .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.
     ; Dictionary size will be patched later, at .85:.

     ; Compile library directory.
     BufferRetrieve [%SymBuffer] ; It 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 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 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:  ; Look if the bucket EBX is free.
       MOVZXB EAX,[EDI+EBX]
       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:  ; Block EDI with bucket EBX will be used.
       SAR EDX,1
       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 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.
Invoked from
PfOutput
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▲