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. INCLUDEHEAD \ ; Include headers of another modules used in this module. ea.htm, \ eaopt.htm, \ exp.htm, \ msg.htm, \ pf.htm, \ pfomf.htm, \ pgm.htm, \ pgmopt.htm, \ reloc.htm, \ sss.htm, \ stm.htm, \ sym.htm, \ syswin.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