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 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 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.
BasePgm.ModulePgmList.
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 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.
Pgm.ModulePgmList.
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 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