Pgm represents a program defined with source text between PROGRAM/ENDPROGRAM statements. Product of Pgm assembly is binary/object/executable file.
In other assemblers it is often referred as module or assembly unit . EuroAssembler accepts more than one program in a source file.
Pgm has its own memory pool.
pgm PROGRAM FORMAT=COFF,MODEL=FLAT,WIDTH=32 INCLUDEHEAD "euroasm.htm" ; Interface (structures, symbols and macros) of other modules.
pgm HEAD ; Start of module interface.
PGM STRUC .NamePtr D D ; Pointer to the program name. .NameSize D D ; Size of the program name. .PassNr D D ; Pass number. .PassPtr D D ; Pointer to PASS or 0 when no pass is initialized. ; +10h. .Status D D ; Program Boolean flags in PgmEncoding. .CurrentSect D D ; Pointer to SSS with .Status:sssSection - a section which Pgm emits to. .CurrentStm D D ; Pointer to STM with the last parsed statement. .LinePtr D D ; Pointer to physical line with current statement of this PROGRAM. ; +20h. .SymList D D ; Pointer to LIST with program symbols. .SssList D D ; Pointer to LIST with groups, segments, sections and structures. .SssPtrBuf D D ; Pointer to BUFFER with DWORD pointers to SSS in image order. .ModulePgmList D D ; Pointer to LIST of PGM modules loaded from linked/imported files. ; +30h. ; Synchronous tables describing files included in the program. .InclFilesNr D D ; Number of DWORDS in .InclFilesTable,.InclFileTimeTable,.InclLinePtrTable. .InclFilesTable D D ; Pointer to the table of DWORDs, each contains pointer to FILE structure. .InclLinePtrTable D D ; Pointer to the table of DWORDs, each contains LinePtr to INCLUDE* statement. .InclFileTimeTable D D ; Pointer to the table of DWORDs, each contains DosDateTime of included file. ; +40h. ; Synchronous tables describing files linked in the program. .MaxLinks D D ; Copy of EUROASM MAXLINKS= option which was in charge on PgmCreateProgram. .LinkFilesNr D D ; Number of DWORDS in .LinkFileNamesTable and .LinkLinePtrTable. .LinkFileNamesTable D D ; Pointer to the table of DWORDs, each contains pointer to ASCIIZ filename. .LinkLinePtrTable D D ; Pointer to the table of DWORDs, each contains LinePtr to LINK statement. ; +50h. .Errorlevel D D ; Errorlevel of this program. .NrOfInitSegments D D ; How many groups+segments in image are initialized (trailing BSS and STACK do not count). .Pool D D ; Program pool. .EntryExp DS EXP ; Program entry evaluated to EXP structure by PgmEvalEntry. .Pgmopt DS PGMOPT ; Program options of this program. .Eaopt DS EAOPT ; EUROASM options valid at the start of this program. ENDSTRUC PGM
; Assembly flags. pgmEnvelope = 0x0000_0001 ; Artificial PROGRAM block encapsulating all source file statements. pgmEnvelDirty = 0x0000_0002 ; Something was emitted to envelope program, do list its PROGRAM/ENDPROGRAM statements. pgmNoMsg = 0x0100_0008 ; Used to suppress Msg during ChunkSubHead detection. pgmLastPass = 0x0000_0010 ; Program is in the final pass. pgmLastJustSet = 0x0000_0020 ; Set together with pgmLastPass, reset in SrcAssemble when used. pgmFixingPass = 0x0000_0080 ; Pass number last but one (MAXPASSES-1). ; Link flags. pgmLinking = 0x0000_0100 ; Program was finally assembled, now it is linked. pgmSelected = 0x0001_0000 ; Module is referenced, it will be combined and linked to base program. pgmImpLibMember = 0x0002_0000 ; Module is a pure import library member. pgmIsModule = 0x0004_0000 ; This program is OMF library module. It requires 512 alignment.
ENDHEAD pgm [.text]
PgmCreateProgram Procedure StmPtr MOV EBX,[%StmPtr] PoolNew [Src.Pool::],SIZE#PGM MOV EDI,EAX ; ^PGM. MOV [%ReturnEAX],EAX ; Initialize program object members. Clear EDI,Size=SIZE#PGM MOV EAX,[EBX+STM.ChunkPtr] MOV ECX,'0470' ; Assembling program "!1S". JNSt [EAX+CHUNK.Status],chunkEnvelope,.10: MOV ECX,'0270' ; Assembling source "!1S". SetSt [EDI+PGM.Status],pgmEnvelope .10:Invoke CtxPeek::,ctxPROGRAM,0 MsgUnexpected cc=C Msg ECX,EAX ; Context Assembling source/program "!1S". MOV ESI,[EAX+CTX.NamePtr] MOV ECX,[EAX+CTX.NameSize] MOV [EDI+PGM.NamePtr],ESI MOV [EDI+PGM.NameSize],ECX PoolCreate Size=%EaPoolSize, ErrorHandler=EaMallocError:: MOV [EDI+PGM.Pool],EAX MOV EDX,EAX MOV [EDI+PGM.CurrentStm],EBX MOV EAX,[EBX+STM.LinePtr] MOV [EDI+PGM.LinePtr],EAX ListCreate EDX,SIZE#SYM MOV [EDI+PGM.SymList],EAX ListCreate EDX,SIZE#PGM MOV [EDI+PGM.ModulePgmList],EAX PoolNew EDX,SIZE#PASS MOV [EDI+PGM.PassPtr],EAX ListCreate EDX,SIZE#SSS MOV [EDI+PGM.SssList],EAX BufferCreate [EDI+PGM.Pool],Size=16*4 ; Estimate max.16 segments in the program. MOV [EDI+PGM.SssPtrBuf],EAX ; Default segments on SssList will be created later, when PROGRAM keyword will have been assembled to Pgmopt. LEA ESI,[EDI+PGM.Eaopt] CopyTo ESI, Ea.Eaopt::, Size=SIZE#EAOPT LEA ESI,[EDI+PGM.Pgmopt] CopyTo ESI, Src.Pgmopt::, Size=SIZE#PGMOPT ; Initialize Pgmopt from source Pgmopt. ; Allocate included files tables Pgm.InclFilesTable,Pgm.InclFileTimeTable,Pgm.InclLinePtrTable. MOV ECX,[Ea.Eaopt.MaxInclusions::] INC ECX SHL ECX,2 PoolNew [EDI+PGM.Pool],ECX MOV [EDI+PGM.InclFilesTable],EAX PoolNew [EDI+PGM.Pool],ECX MOV [EDI+PGM.InclFileTimeTable],EAX PoolNew [EDI+PGM.Pool],ECX MOV [EDI+PGM.InclLinePtrTable],EAX ; Allocate linked files tables Pgm.LinkFileNamesTable, Pgm.LinkLinePtrTable. MOV ECX,[Ea.Eaopt::+EAOPT.MaxLinks] MOV [EDI+PGM.MaxLinks],ECX INC ECX SHL ECX,2 PoolNew [EDI+PGM.Pool],ECX MOV [EDI+PGM.LinkFileNamesTable],EAX PoolNew [EDI+PGM.Pool],ECX MOV [EDI+PGM.LinkLinePtrTable],EAX ; Start with program pass number 1. INCD [EDI+PGM.PassNr] EndProcedure PgmCreateProgram
PgmDestroy Procedure PgmPtr, StmPtr MOV EBX,[%PgmPtr] JNSt [EBX+PGM.Status],pgmEnvelope,.50: ; Envelope program errorlevel will be combined with Src.Errorlevel. MOV EAX,[Src.Errorlevel::] CMP EAX,[EBX+PGM.Errorlevel] JNA .30: MOV [EBX+PGM.Errorlevel],EAX .30: Invoke ChunkTotalLines:: ; Count lines in source+includes. MOV EDX,EAX Invoke PgmCheckDirty,EBX ; Returns CF=1 if something was emitted or linked to the program. SBB EDI,EDI ; EDI is now -1 if program dirty, 0 if empty. JZ .40: ; If ending envelope program is empty, output file is not generated. Invoke PfOutput::,EBX ; Write envelope program to disk, possibly overwriting embedded program with the same name. .40: Msg '0750',EBX,EDX,[EBX+PGM.PassNr],[EBX+PGM.Errorlevel] ; Source program "!1S" (!2D lines) assembled in !3D passes with errorlevel !4D. TEST EDI JZ .80: ; Empty envelope program does not list maps and globals. JMPS .60: .50: ; Program EBX is not an envelope. Invoke PgmCheckDirty,EBX ; Returns CF=1 if something was emitted or linked to the program. SBB EDI,EDI ; EDI is now -1 if program dirty, 0 if empty. Invoke PfOutput::,EBX ; Generate output file (even when empty). Msg '0650',EBX,[EBX+PGM.PassNr],[EBX+PGM.Errorlevel] ; Program "!1S" assembled in !2D passes with errorlevel !3D. .60: ; Common continuation for both envelope and embedded program. JNSt [EBX+PGM.Pgmopt.Status],pgmoptLISTMAP,.70: Invoke PgmListMap,EBX,[%StmPtr] ; List map of linked segments and global symbols. .70: JNSt [EBX+PGM.Pgmopt.Status],pgmoptLISTGLOBALS,.80: Invoke PgmListGlobals,EBX,[%StmPtr] .80: PoolDestroy [EBX+PGM.Pool] Msg cc=C,'2575','Pgm',EDX ; Deallocation of virtual memory !1C.Pool !2Hh failed. .90:EndProcedure PgmDestroy
PgmGetCurrent Procedure SUB EAX,EAX MOV [%ReturnEAX],EAX .20: Invoke CtxPeek::, ctxPROGRAM,EAX JC .90: MOV ECX,[EAX+CTX.ObjPtr] JECXZ .20: MOV [%ReturnEAX],ECX .90:EndProcedure PgmGetCurrent
LISTLITERALS=ENABLED
.PgmListLiterals Procedure Pgm LSFound LocalVar ; Boolean flag. Nonzero if subproc.ListSection
found the literal section. SnPtr LocalVar ; Pointer to a symbol. SnBuffer LocalVar ; Temporary buffer to keep pointers to literals before sorting. @RTnum LocalVar ; Ordinal of runtime literal section (1,2,3..). Stm LocalVar Size=SIZE#STM ; Fake statement used to feed StmListing. SssName LocalVar Size=8 ; Room for section name, "@LT##" LEA EDI,[%SssName] MOV EAX,"@LT#" STOSD XOR EAX,EAX STOSD MOV [%@RTnum],EAX Invoke EaBufferReserve::,PgmListLiterals MOV [%SnBuffer],EAX PUSHD [Src.Lst.Status::] ; Save the global listing status during PgmListLiteral's performance. LEA EBX,[%Stm] Invoke StmCreate::,EBX ; List data literals. MOV EAX,"LT64" CALL .ListSection: MOV EAX,"LT32" CALL .ListSection: MOV EAX,"LT16" CALL .ListSection: MOV EAX,"LT8" CALL .ListSection: MOV EAX,"LT4" CALL .ListSection: MOV EAX,"LT2" CALL .ListSection: MOV EAX,"LT1" CALL .ListSection: ; List code literals. MOV EAX,"RT0" CALL .ListSection: MOVD [%SssName],"@RT1" .50:MOV EAX,[%@RTnum] INC EAX MOV [%@RTnum],EAX LEA EDI,[%SssName+3] StoD EDI,Size=2,Signed=no CMP AL,9 MOV EAX,[%SssName+1] JA .60: AND EAX,0x00FF_FFFF .60:CALL .ListSection: CMPD [%LSFound],0 JNZ .50: ; If @RT section found, try the next one. Invoke StmDestroy::,EBX POPD [Src.Lst.Status::] Invoke EaBufferRelease::,[%SnBuffer] JMP .99: .ListSection: PROC ; List literals from one section. ; Input: EAX=section name without '@', e.g. "LT8". ; EBX=Pointer to %Stm. ; Output:EBX,EBP unchanged, other register undefined. MOVD [%LSFound],0 LEA EDI,[%SssName] MOV ECX,4 ; Section name size. MOV [EDI+1],EAX TEST EAX,0xFF00_0000 JZ .S1: INC ECX .S1:Invoke SssFind::,sssSection,0,EDI,ECX,0 JC .S9: JNSt [EAX+SSS.Purpose],sssPurposeLITERAL,.S9: MOV EDI,EAX ; Literal section found. MOV [%LSFound],EAX BufferClear [%SnBuffer] MOV EDX,[%Pgm] MOV [EDX+PGM.CurrentSect],EDI ; Find all literal symbols which belong to section EDI. ListGetFirst [EDX+PGM.SymList] JZ .S4: .S2:; EAX is now pointer to a symbol from program's symbol list. JNSt [EAX+SYM.Status],symLiteral,.S3: ; If not a literal symbol. CMP [EAX+SYM.Section],EDI JNE .S3: ; If the symbol does not belong to listed section. MOV [%SnPtr],EAX LEA ECX,[%SnPtr] BufferStore [%SnBuffer],ECX,4 .S3:ListGetNext EAX ; Get the next symbol. JNZ .S2: .S4:BufferRetrieve [%SnBuffer] SAR ECX,2 ; ECX is now number of symbols. JECXZ .S9: ; Symbols whose pointers are now in array at ESI will be sorted by offset and then listed. ; Otherwise in recursive literals, such as=W =W value
, the inner literal with higher offset ; would be listed sooner, which looked weird. ShellSort ESI,ECX,4,.ByOffset: .ByOffset: PROC1 ; Callback sorting procedure. PUSH EBX MOV EBX,[ESI] MOV EDX,[EDI] MOV EAX,[EDX+SYM.OffsetHigh] CMP EAX,[EBX+SYM.OffsetHigh] JNE .B5: MOV EAX,[EDX+SYM.OffsetLow] CMP EAX,[EBX+SYM.OffsetLow] .B5: JNC .B9: ; If in ascending order. MOV [ESI],EDX ; Otherwise swap both elements. MOV [EDI],EBX .B9:POP EBX RET ENDP1 .ByOffset: ; ESI is now an sorted array of ECX pointers to symbols which will be dumped as literal. .S5:LODSD PUSH ECX,ESI MOV EDX,EAX ; EDX is now ^SYM. SetSt [Src.Lst.Status::],lstLiteral ; Tell StmListing how to treat this object. RstSt [Src.Lst.Status::],lstNoData+lstTRUE+lstFALSE Invoke StmClean::,EBX MOV EAX,[%Pgm] MOV [EBX+STM.Program],EAX MOV [EAX+PGM.CurrentSect],EDI MOV [EBX+STM.Section],EDI BufferStore [EBX+STM.SrcBuffer],[EDX+SYM.NamePtr],[EDX+SYM.NameSize] BufferStore [EBX+STM.SrcBuffer],=W(0x0A0D),2 MOV EAX,[EDX+SYM.Size] MOV [EBX+STM.Size],EAX MOV EAX,[EDX+SYM.OffsetLow] MOV [EBX+STM.OffsetLow],EAX MOV [EDI+SSS.OrgLow],EAX ; Prepare emitted data of this literal to STM.EmitBuffer. SUB EAX,[EDI+SSS.BottomLow] BufferRetrieve [EDI+SSS.EmitBuffer] ADD ESI,EAX MOV ECX,[EBX+STM.Size] BufferStore [EBX+STM.EmitBuffer],ESI,ECX ; Prepare relocations of this literal to STM.RelocBuffer. BufferRetrieve [EDI+SSS.RelocBuffer] JECXZ .S7: .S6:BufferStore [EBX+STM.RelocBuffer],ESI,SIZE#RELOC ADD ESI,SIZE#RELOC SUB ECX,SIZE#RELOC JA .S6: .S7:Invoke StmListing::,EBX .S8:POP ESI,ECX LOOP .S5: .S9:RET ENDP .ListSection: .99:EndProcedure PgmListLiterals
PROGRAM
pseudoinstruction.PgmParameters Procedure PgmPtr, StmPtr MOV EBX,[%StmPtr] BufferRetrieve [EBX+STM.KeyBuffer] LEA EDX,[ESI+ECX] .40:CMP ESI,EDX JNB .90: ; If no more keywords. MOV ECX,ESI LODSD ; Pointer to keyword name, e.g.MODEL=
. MOV EDI,EAX LODSD ; Keyword name size. Invoke DictLookup::, DictPgmopt::, EDI,EAX Msg cc=C,'7821',ECX,DictPgmopt:: ; Unknown PROGRAM keyword "!1S". Expected one of !2L. MOV ECX,EAX ; Pgmopt index. LODSD ; Pointer to keyword value, e.g.SMALL
. MOV EDI,EAX LODSD ; Keyword value size. JC .40: ; Skip when the option was unknown {E7821). StripSpaces EDI,EAX MOV EBX,[%PgmPtr] %ord %SETA 0 opt %FOR %PgmoptList %ord %SETA %ord+1 %IF "%opt" == "ENTRY" %EXITFOR opt %ENDIF %ENDFOR opt CMP ECX,%ord << 16 + varTypeSysPgmopt ; is the parameter ENTRY? >> JNE .70: Invoke PgmGlobalEntry,EBX,EDI,EAX .70:LEA EBX,[EBX+PGM.Pgmopt] Invoke PgmoptAssemble::, EBX,ECX,EDI,EAX JMP .40: .90:EndProcedure PgmParameters
ENTRY=
specifies an identifier and declares a global symbol from it.PgmGlobalEntry Procedure Program, EntryPtr, EntrySize TempStm LocalVar Size=SIZE# STM ClearLocalVar LEA EDI,[%TempStm] MOV EBX,[%Program] MOV ESI,[%EntryPtr] MOV ECX,[%EntrySize] MOV [EDI+STM.Program],EBX LEA EDX,[ESI+ECX] Invoke ExpParseIdentifier::,ESI,EDX,expFullstop JC .90: Invoke SymCreate::,symReferenced+symGlobalRef+symEntry,ESI,EAX,EDI .90: EndProcedure PgmGlobalEntry
PROGRAM LISTMAP=ON
from PgmDestroy after segments have been combined, ordered, linked and streamed by
PfOutput.
PgmListMap Procedure Pgm, StmPtr MsgBuffer LocalVar ; Local copy of statement MsgBuffer. Number LocalVar Size=20 MOV ESI,[%StmPtr] MOV EBX,[%Pgm] MOV EAX,[ESI+STM.MsgBuffer] MOV [%MsgBuffer],EAX JNSt [EBX+PGM.Status],pgmEnvelope,.10: ; Envelope program is listed only if something was emitted to it. JNSt [EBX+PGM.Status],pgmEnvelDirty,.90: .10: BufferStore [%MsgBuffer],=B'| **** ListMap "',23 BufferStore [%MsgBuffer],[EBX+PGM.Pgmopt.OutFilePtr],[EBX+PGM.Pgmopt.OutFileSize] BufferStore [ESI+STM.MsgBuffer],=B'",groups=',9 ; Count linked groups and segments. SUB EDI,EDI ; Group counter. SUB EDX,EDX ; Segment counter. BufferRetrieve [EBX+PGM.SssPtrBuf] SHR ECX,2 JZ .22: .13: LODSD JNSt [EAX+SSS.Status],sssGroup,.16: INC EDI .16: JNSt [EAX+SSS.Status],sssSegment,.19: INC EDX .19: LOOP .13: .22: MOV EAX,EDI ; Number of groups in image. LEA EDI,[%Number] MOV ECX,EDI StoD EDI SUB EDI,ECX ; ECX,EDI is now decimal number of groups. MOV ESI,[%StmPtr] BufferStore [%MsgBuffer],ECX,EDI BufferStore [%MsgBuffer],=B',segments=',10 MOV EAX,EDX ; Number of segments in image. LEA EDI,[%Number] MOV ECX,EDI StoD EDI SUB EDI,ECX ; ECX,EDI is now decimal number of segments. BufferStore [%MsgBuffer],ECX,EDI BufferStore [%MsgBuffer],=B',entry=',7 ; List entry=. LEA ESI,[EBX+PGM.EntryExp] LEA EDI,[%Number] MOV EAX,[ESI+EXP.Status] MOV ECX,[ESI+EXP.Seg] MOV EDX,EDI Dispatch AL,'A','N','F' JMP .34: ; Ignore wrong entry expression. .F: MOV EAX,ECX ; Para value. StoH EDI,Size=4 BufferStore [%MsgBuffer],EDX,4 BufferStore [%MsgBuffer],=B'h:',2 MOV EAX,[ESI+EXP.Low] MOV EDI,EDX StoH EDI,Size=8 BufferStore [%MsgBuffer],EDX,8 BufferStore [%MsgBuffer],=B'h',1 JMP .34: .25: MOV ECX,[ESI+EXP.Seg] JMP .28: .N: .A: BufferStore [%MsgBuffer],=B'[',1 MOV ECX,[ESI+EXP.Sym] MOV EAX,[ESI+EXP.Low] JECXZ .25: Invoke SymFrameAddress::,ECX,[%Pgm] ; Return EDX:EAX=offset, ECX=group. .28: JECXZ .31: BufferStore [%MsgBuffer],[ECX+SSS.NamePtr],[ECX+SSS.NameSize] .31: BufferStore [%MsgBuffer],=B']:',2 MOV EDX,EDI StoH EDI,Size=8 BufferStore [%MsgBuffer],EDX,8 BufferStore [%MsgBuffer],=B'h',1 .34: ; List stack=. BufferStore [%MsgBuffer],=B',stack=',7 ; Find the last STACK segment to EDX. MOV EAX,[%Pgm] XOR EDX,EDX BufferRetrieve [EAX+PGM.SssPtrBuf] SHR ECX,2 JECXZ .49: .37: LODSD JNSt [EAX+SSS.Purpose],sssPurposeSTACK,.40: MOV EDX,EAX .40: LOOP .37: TEST EDX JZ .49: ; If no stack. BufferStore [%MsgBuffer],='[',1 BufferStore [%MsgBuffer],[EDX+SSS.NamePtr],[EDX+SSS.NameSize] BufferStore [%MsgBuffer],=']:',2 LEA ESI,[EBX+PGM.Pgmopt] MOV EAX,[ESI+PGMOPT.Status] Dispatch AL,%PfPgmoptList .pgmoptRSRC: .pgmoptPE: .pgmoptDLL: .pgmoptCOFF: MOV EAX,[ESI+PGMOPT.SizeOfStackCommitLow] MOV EDX,[ESI+PGMOPT.SizeOfStackCommitHigh] JMP .43: .pgmoptLIBOMF: .pgmoptLIBCOF: .pgmoptOMF: .pgmoptMZ: ; Stack pointer SP equals the size of the last stack segment. MOV EDI,EDX MOV EAX,[EDI+SSS.TopLow] MOV EDX,[EDI+SSS.TopHigh] SUB EAX,[EDI+SSS.BottomLow] SBB EDX,[EDI+SSS.BottomHigh] JMP .43: .pgmoptBIN: .pgmoptCOM: ; Stack pointer is fixed to the end of [COM] segment. MOV EAX,0xFFFE SUB EDX,EDX .43: LEA EDI,[%Number] MOV ECX,EDI TEST EDX JZ .46: XCHG EAX,EDX ; Stack size is bigger than 4G. StoH EDI,Size=8 XCHG EDX,EAX .46: StoH EDI,Size=8 SUB EDI,ECX BufferStore [%MsgBuffer],ECX,EDI BufferStoreByte [%MsgBuffer],'h' .49: ; Terminate ListMap header line. BufferStoreWord [%MsgBuffer],0x0A0D ; List groups and segments, one per line. BufferRetrieve [EBX+PGM.SssPtrBuf] SHR ECX,2 JZ .90: .52: LODSD ; A loop .52: .. .85: displays all ECX groups and their segments. .55: PUSH ECX,ESI MOV ESI,EAX ; ^SSS. BufferStore [%MsgBuffer],=B'| ',9 JNSt [ESI+SSS.Status],sssSegment,.58: BufferStoreWord [%MsgBuffer],' ' ; Indent segment by two spaces. .58: BufferStoreByte [%MsgBuffer],'[' BufferStore [%MsgBuffer],[ESI+SSS.NamePtr],[ESI+SSS.NameSize] MOV EBX,[%Pgm] MOV EAX,[EBX+PGM.Pgmopt.Status] CMP AL,pgmoptBIN ; Program formats which link at VA rather than RVA. JE .61: CMP AL,pgmoptPE JE .61: CMP AL,pgmoptDLL JE .61: BufferStore [%MsgBuffer],=B'],RVA=',6 JMP .63: .61: BufferStore [%MsgBuffer],=B'],VA=',5 .63: LEA EDI,[%Number] MOV EDX,[ESI+SSS.BottomHigh] MOV EAX,[ESI+SSS.BottomLow] TEST EDX MOV EDX,EDI JZ .65: XCHG EAX,ECX ; Temporary save EAX to ECX. MOV EAX,[ESI+SSS.BottomHigh] StoH EDI,Size=8 XCHG ECX,EAX .65: StoH EDI,Size=8 SUB EDI,EDX BufferStore [%MsgBuffer],EDX,EDI BufferStore [%MsgBuffer],=B'h,size=',7 LEA EDI,[%Number] MOV EAX,[ESI+SSS.TopLow] MOV EDX,[ESI+SSS.TopHigh] SUB EAX,[ESI+SSS.BottomLow] SBB EDX,[ESI+SSS.BottomHigh] JNZ .67: LEA EDI,[%Number] MOV EDX,EDI StoH EDI,Size=8 SUB EDI,EDX PUSH EAX BufferStore [%MsgBuffer],EDX,EDI BufferStoreWord [%MsgBuffer],'h=' POP EAX LEA EDI,[%Number] MOV EDI,EDX StoD EDI SUB EDI,EDX BufferStore [%MsgBuffer],EDX,EDI .67: JNSt [ESI+SSS.Status],sssGroup,.79: BufferStore [%MsgBuffer],=B',group',6 PUSH ECX,ESI MOV EDI,ESI ; Find segments which belong to group EDI. MOV EBX,[%Pgm] BufferRetrieve [EBX+PGM.SssPtrBuf] SHR ECX,2 .70: LODSD MOV EBX,EAX JNSt [EBX+SSS.Status],sssSegment,.76: MOV EAX,[EBX+SSS.GroupPtr] CMP EDI,EAX ; Does the segment EBX belong to group EDI? JE .73: TEST EAX JZ .76: MOV EAX,[EAX+SSS.GroupPtr] ; Group EAX might be from a linked module. CMP EDI,EAX JNE .76: .73: BufferStoreWord [%MsgBuffer],' [' BufferStore [%MsgBuffer],[EBX+SSS.NamePtr],[EBX+SSS.NameSize] BufferStoreByte [%MsgBuffer],']' .76: LOOP .70: POP ESI,ECX JMP .82: .79: BufferStore [%MsgBuffer],=B',width=',7 MOV EBX,[ESI+SSS.Status] MOV EAX,sssWidthMask AND EAX,EBX LEA EDI,[%Number] MOV EDX,EDI SHR EAX,20 StoD EDI SUB EDI,EDX BufferStore [%MsgBuffer],EDX,EDI BufferStore [%MsgBuffer],=B',align=',7 MOV EAX,[ESI+SSS.Alignment] MOV EDI,EDX CMP EAX,8 JNA .80: ; Stronger alignment than 8 display as hexa. StoH EDI,Size=4 MOV AL,'h' STOSB JMPS .81: .80: StoD EDI .81: SUB EDI,EDX BufferStore [%MsgBuffer],EDX,EDI BufferStore [%MsgBuffer],=B',purpose=',9 Invoke EaBufferReserve::,PgmListMap Invoke SssPurposeToText::,[ESI+SSS.Purpose],EAX BufferRetrieve EAX DEC ECX ; Omit zero terminator. BufferStore [%MsgBuffer],ESI,ECX Invoke EaBufferRelease::,EAX .82: BufferStoreWord [%MsgBuffer],0x0A0D .85: POP ESI,ECX .88: LOOP .52: ; The next group/segment. .90: EndProcedure PgmListMap
ENDPROGRAM
statement and below ListMap.
PROGRAM LISTGLOBALS=ON
.PgmListGlobals Procedure Pgm, StmPtr MsgBuffer LocalVar ; Local copy of statement's MsgBuffer hired to display the list of symbols. InpBuffer LocalVar ; Temporary buffer with pointers to global symbols. OutBuffer LocalVar ; Temporary buffer with pointers to global symbols. CountGlb LocalVar ; Counter of global symbols. CountPub LocalVar ; Counter of public symbols. CountExt LocalVar ; Counter of extern symbols. CountExp LocalVar ; Counter of export symbols. CountImp LocalVar ; Counter of import symbols. Number LocalVar Size=12 ; Room for decadic format of counters. ClearLocalVar MOV ESI,[%StmPtr] MOV EBX,[%Pgm] MOV EAX,[ESI+STM.MsgBuffer] MOV [%MsgBuffer],EAX JNSt [EBX+PGM.Status],pgmEnvelope,.10: JNSt [EBX+PGM.Status],pgmEnvelDirty,.90: ; Do not display header when source envelope was not used. .10: BufferStore [%MsgBuffer],=B'| **** ListGlobals "',27 ; Display header. BufferStore [%MsgBuffer],[EBX+PGM.Pgmopt+PGMOPT.OutFilePtr],[EBX+PGM.Pgmopt+PGMOPT.OutFileSize] BufferStore [%MsgBuffer],=B'",Global=',9 Invoke EaBufferReserve::,PgmListGlobals MOV [%InpBuffer],EAX Invoke EaBufferReserve::,PgmListGlobals MOV [%OutBuffer],EAX ; Step 1: store pointers to all symbols with nonstandard scope to input buffer in loop .13: .. .16: ListGetFirst [EBX+PGM.SymList] JZ .40: .13: JNSt [EAX+SYM.Status],symScopeMask,.16: ; Skip standard private symbols. BufferStoreDword [%InpBuffer],EAX .16: ListGetNext EAX JNZ .13: ; Step 2: Sort global symbols alphabeticaly by name. Invoke EaBufferSort::,[%InpBuffer] ; Step 3: merge public and extern/import symbols with the same name ; and count and copy them to output buffer in loop .20: .. .43:. BufferRetrieve [%InpBuffer] LEA EDX,[ESI+ECX] ; End of pointer array. .20: CMP ESI,EDX JNB .40: LODSD CMP ESI,EDX JNB .26: MOV EBX,[ESI] ; EAX and EBX are now pointers to neighbour symbols, perhaps with the same name. Compare [EAX+SYM.NamePtr],[EAX+SYM.NameSize],[EBX+SYM.NamePtr],[EBX+SYM.NameSize] JNE .26: ; Both symbols EAX and EBX have the same name. Let's find which is the public|export one. ADD ESI,4 ; Skip the 2nd pointer because they will be merged. JSt [EAX+SYM.Status],symPublic|symExport,.23: XCHG EAX,EBX ; EAX is public symbol now. .23: MOV EBX,[EBX+SYM.Status] AND EBX,symImport+symExtern SetSt EBX,symGlobal SetSt [EAX+SYM.Status],EBX ; Merged public with extern. .26: BufferStoreDword [%OutBuffer],EAX .30: ; Update global/public/extern/export/import counter. MOV EBX,[EAX+SYM.Status] LEA ECX,[%CountExp] JSt EBX,symExport,.36: LEA ECX,[%CountImp] JSt EBX,symImport,.36: LEA ECX,[%CountPub] JNSt EBX,symPublic,.32: JNSt EBX,symExtern,.36: JMP .34: .32: LEA ECX,[%CountExt] JSt EBX,symExtern,.36: .34: LEA ECX,[%CountGlb] .36: INCD [ECX] ; Increment one of five counters. JMP .20: .40: MOV EAX,[%CountGlb] LEA EDI,[%Number] MOV EDX,EDI StoD EDI SUB EDI,EDX BufferStore [%MsgBuffer],EDX,EDI BufferStore [%MsgBuffer],=B',Public=',8 MOV EAX,[%CountPub] LEA EDI,[%Number] StoD EDI SUB EDI,EDX BufferStore [%MsgBuffer],EDX,EDI BufferStore [%MsgBuffer],=B',Extern=',8 MOV EAX,[%CountExt] LEA EDI,[%Number] StoD EDI SUB EDI,EDX BufferStore [%MsgBuffer],EDX,EDI BufferStore [%MsgBuffer],=B',eXport=',8 MOV EAX,[%CountExp] LEA EDI,[%Number] StoD EDI SUB EDI,EDX BufferStore [%MsgBuffer],EDX,EDI BufferStore [%MsgBuffer],=B',Import=',8 MOV EAX,[%CountImp] LEA EDI,[%Number] StoD EDI SUB EDI,EDX BufferStore [%MsgBuffer],EDX,EDI BufferStore [%MsgBuffer],=W(0x0A0D),2 ; List symbols, sorted by name. BufferRetrieve [%OutBuffer] SAR ECX,2 JZ .80: .43: LODSD ; List properties of global symbol EAX. PUSH ECX,ESI MOV EBX,EAX BufferStore [%MsgBuffer],=B'| ',9 BufferStore [%MsgBuffer],[EBX+SYM.NamePtr],[EBX+SYM.NameSize] JSt [EBX+SYM.Status],symForwarded,.68: JNSt [EBX+SYM.Status],symImport,.53: JNSt [EBX+SYM.Status],symImportedByOrd,.46: ; Symbol imported by ordinal number will be listed as Name(OrdinalNr). BufferStore [%MsgBuffer],=B'(',1 ; List ordinal number in parenthesis. MOV EAX,[EBX+SYM.OrdinalNr] LEA EDI,[%Number] MOV EDX,EDI StoD EDI SUB EDI,EDX BufferStore [%MsgBuffer],EDX,EDI JMP .50: .46: MOV ECX,[EBX+SYM.InterNameSize] JECXZ .53: ; If internal name is empty, do not display it. ; Symbol imported by name will be listed as Name{InterName} if Name<>InterName. Compare [EBX+SYM.NamePtr],[EBX+SYM.NameSize],[EBX+SYM.InterNamePtr],ECX JE .53: ; Do not display internal name when it is empty or the same. BufferStore [%MsgBuffer],=B'{',1 ; List internal name in curly brackets. BufferStore [%MsgBuffer],[EBX+SYM.InterNamePtr],[EBX+SYM.InterNameSize] .50: BufferStore [%MsgBuffer],=B'}',1 .53: BufferStore [%MsgBuffer],=B',[',2 ; List group name in brackets. Invoke SymFrameAddress::,EBX,[%Pgm] ; Returns EDX:EAX=offset, ECX=^group. JECXZ .56: ; Scalar symbol has empty segment name. BufferStore [%MsgBuffer],[ECX+SSS.NamePtr],[ECX+SSS.NameSize] .56: BufferStore [%MsgBuffer],=B']:',2 LEA EDI,[%Number] MOV EDX,EDI StoH EDI,Size=8 SUB EDI,EDX BufferStore [%MsgBuffer],EDX,EDI ; List symbol offset. BufferStore [%MsgBuffer],=B"h,",2 MOV EDX,[%Pgm] MOV EAX,[EDX+PGM.Pgmopt.Status] JNSt EAX,pgmoptExecutable|pgmoptImage,.70: CMP AL,pgmoptBIN JE .60: CMP AL,pgmoptPE JE .60: CMP AL,pgmoptDLL JE .60: BufferStoreByte [%MsgBuffer],'R' .60: BufferStore [%MsgBuffer],=B'VA=',3 .63: SUB EAX,EAX MOV ECX,[EBX+SYM.Section] JECXZ .66: MOV ECX,[ECX+SSS.SegmPtr] MOV EAX,[ECX+SSS.BottomLow] .66: ADD EAX,[EBX+SYM.OffsetLow] LEA EDI,[%Number] MOV EDX,EDI StoH EDI,Size=8 SUB EDI,EDX BufferStore [%MsgBuffer],EDX,EDI ; List symbol VA. BufferStoreByte [%MsgBuffer],'h' .68: BufferStoreByte [%MsgBuffer],',' ; Display scope=. .70: BufferStore [%MsgBuffer],=B"scope='",7 MOV ECX,[EBX+SYM.Status] MOV AL,'X' JSt ECX,symExport,.76: MOV AL,'I' JSt ECX,symImport,.76: MOV AL,'P' JNSt ECX,symPublic,.72: JNSt ECX,symExtern,.76: JMP .74: .72: MOV AL,'E' JSt ECX,symExtern,.76: .74: MOV AL,'G' .76: MOV AH,"'" BufferStoreWord [%MsgBuffer],EAX ; Display lib= of imported and forwarded symbols. JNSt [EBX+SYM.Status],symImport|symForwarded,.78: MOV ECX,[EBX+SYM.DllNameSize] JECXZ .78: BufferStore [%MsgBuffer],=B',lib="',6 BufferStore [%MsgBuffer],[EBX+SYM.DllNamePtr],[EBX+SYM.DllNameSize] BufferStoreByte [%MsgBuffer],'"' .78: ; Display fwd= of forwarded symbols. JNSt [EBX+SYM.Status],symForwarded,.79: BufferStore [%MsgBuffer],=B',fwd=',5 BufferStore [%MsgBuffer],[EBX+SYM.InterNamePtr],[EBX+SYM.InterNameSize] .79: BufferStoreWord [%MsgBuffer],0x0A0D POP ESI,ECX LOOP .43: ; The next global symbol. .80: Invoke EaBufferRelease::,[%OutBuffer] Invoke EaBufferRelease::,[%InpBuffer] .90:EndProcedure PgmListGlobals
Program.EntryExp
is evaluated.PgmEvalEntry Procedure PgmPtr MOV EDX,[%PgmPtr] LEA EDI,[EDX+PGM.EntryExp] MOV ECX,[EDX+PGM.Pgmopt.EntrySize] MOV ESI,[EDX+PGM.Pgmopt.EntryPtr] StripSpaces ESI,ECX JECXZ .90: ; If ENTRY is empty, leave EntryExp empty as well. Invoke ExpEval::,EDI,ESI,ECX,[EDX+PGM.CurrentStm] Invoke ExpReportError::,EDI JC .90: .50: CMPB [EDI+EXP.Status],'#' ; Did ENTRY evaluated with error? JE .90: ; Ignore error if not the final pass. MOV ECX,[EDI+EXP.Sym] JECXZ .90: ; If the ENTRY= referred some symbol, make it global. SetSt [ECX+SYM.Status],symGlobal+symUsed .90:EndProcedure PgmEvalEntry
Pgm.Status:pgmLinking
is set, which happens at link time,
it will also concatenate emit and reloc buffer of all sections
to their segment and then discard the sections.
PgmLinkSections Procedure Pgm MOV EBX,[%Pgm] ListGetFirst [EBX+PGM.SssList] JZ .90: .10: JNSt [EAX+SSS.Status],sssSegment,.80: Invoke SssLinkSegment::,EAX,EBX .80: ListGetNext EAX JNZ .10: .90:EndProcedure PgmLinkSections
PgmCreateImplicitSegments Procedure PgmPtr SssStm LocalVar Size=SIZE#STM ; Fake statement to satisfy SssCreate. LEA EBX,[%SssStm] MOV EDI,[%PgmPtr] MOV [EBX+STM.Program],EDI MOV EAX,[EDI+PGM.LinePtr] MOV [EBX+STM.LinePtr],EAX MOV ESI,[EDI+PGM.Pgmopt.SectionAlign] CMP ESI,16 JAE .20: MOV ESI,16 ; Default segment alignment is typically 1K in PE|DLL, otherwise at least 16. .20: MOV EAX,[EDI+PGM.Pgmopt.Status] MOV ECX,pgmoptWidthMask ; Identical with sssWidthMask. AND ECX,EAX ; Default segment width is adopted from program width. MOV EDX,pgmoptFormatMask AND EDX,EAX Dispatch EDX,%PfPgmoptList Msg '9979' ; Internal error: Missing format handler in PgmCreateImplicitSegments. ; Format handlers input: ; EAX=Pgmopt.Status, EDX=pgmoptFormat, ECX=pgmoptWidth=sssWidth, EDI=^Pgm, EBX=^Stm, ESI=Alignment. .pgmoptPE: ; 32bit protected-mode COFF-based formats use by default segments [.data], [.bss], [.text], no groups. .pgmoptDLL: .pgmoptCOFF: .pgmoptLIBCOF: OR ECX,sssSegment+sssSection+sssImplicit+sssPublic ; Create implicit code segment [.text]: Invoke SssCreate::,EBX,0,=B".text",5,ECX,sssPurposeCODE,ESI ; Create implicit data segment [.data]: Invoke SssCreate::,EBX,0,=B".data",5,ECX,sssPurposeDATA,ESI ; Create implicit bss segment [.bss]: Invoke SssCreate::,EBX,0,=B".bss",4,ECX,sssPurposeBSS,ESI JMP .90: .pgmoptBIN: ; Format BIN uses by default one common segment [BIN], no groups: OR ECX,sssSegment+sssSection+sssImplicit+sssPublic Invoke SssCreate::,EBX,0,=B"BIN",3,ECX,sssPurposeCODE+sssPurposeDATA+sssPurposeBSS+sssPurposeSTACK,ESI JMP .90: .pgmoptCOM: ; Format COM uses by default one common segment [COM]. It begins with 256bytes virtual section [PSP]. OR ECX,sssSegment+sssSection+sssImplicit+sssPublic Invoke SssCreate::,EBX,0,=B"COM",3,ECX,sssPurposeCODE+sssPurposeDATA+sssPurposeBSS+sssPurposeSTACK,ESI JMP .90: .pgmoptMZ: .pgmoptOMF: ; 16bit formats OMF and MZ use by default segments [CODE], [DATA], [BSS], [STACK], no groups: .pgmoptLIBOMF: ; Create implicit code segment [CODE]: OR ECX,sssSegment+sssSection+sssImplicit+sssPublic Invoke SssCreate::,EBX,0,=B"CODE",4,ECX,sssPurposeCODE,ESI ; Create implicit data segment [DATA]: Invoke SssCreate::,EBX,0,=B"DATA",4,ECX,sssPurposeDATA,ESI ; Create implicit bss segment [BSS]: Invoke SssCreate::,EBX,0,=B"BSS",3,ECX,sssPurposeBSS,ESI ; Create implicit stack segment [STACK]: AND ECX,~sssCombineMask OR ECX,sssStack Invoke SssCreate::,EBX,0,=B"STACK",5,ECX,sssPurposeSTACK,2 ; Implicit stack is WORD aligned. .pgmoptRSRC: ; Special formats do not create segments. .90:EndProcedure PgmCreateImplicitSegments
Programmer may declare explicit group(s) of segments, their names will be respected here. Otherwise this procedure creates implicite group, depending on the chosen memory model.
Procedure CreateImplicitGroups is employed when an executable program is about to link,
segments of all linked modules have just been combined but not sorted yet into the final order.
If the program format is not executable, or if MODEL=FLAT
, this procedure does nothing.
When a monocode program is created (MODEL=TINY | SMALL | COMPACT
), it usually has
just one public code segment. If more than one code segments with different names are combined
(from linked modules, for instance), they will be grouped here into an
implicit code group which should be used as their addressing frame.
The name of the implicite group is the same as its first segment.
Code transfers withing implicit code group are by default NEAR and their relocations
can be resolved at link time.
In multicode program models (MODEL=MEDIUM | LARGE | HUGE
) each segment,
which wasn't assigned to an explicit group yet, will be accompanied with its own implicit group of the same name.
Similary in monodata programs (MODEL=TINY | SMALL | MEDIUM
)
data and BSS segments are grouped into an implicit data group.
In multidata models (MODEL=COMPACT | LARGE | HUGE
), each nonstack segment without PURPOSE=CODE
and without explicit group will be assigned to its own implicit group with the same name.
Pure stack segments (PURPOSE=STACK
) in nonflat model are grouped to an
implicit stack group.
If MODEL=TINY
, one common implicit group with the same name as its first segment
will be created here for all segments of any purpose.
PgmCreateImplicitGroups Procedure Program MOV EBX,[%Program] CMPB [EBX+PGM.Pgmopt.Status],pgmoptBIN JE .05: JNSt [EBX+PGM.Pgmopt.Status],pgmoptExecutable,.90: .05: JSt [EBX+PGM.Pgmopt.Status],pgmoptFLAT,.90: ; First segments pass will find explicit code, data, stack groups in a loop .10: .. .20:. SUB ECX,ECX ; Pointer to the 1st code group (CGROUP). SUB EDI,EDI ; Pointer to the 1st data group (DGROUP). SUB ESI,ESI ; Pointer to the 1st stack group (SGROUP). ListGetFirst [EBX+PGM.SssList] JZ .90: .10: JNSt [EAX+SSS.Status],sssSegment,.20: CMP [EAX+SSS.Purpose],sssPurposeSTACK JNE .13: TEST ESI ; Was SGROUP already found? JNZ .13: MOV ESI,[EAX+SSS.GroupPtr] .13: JNSt [EAX+SSS.Purpose],sssPurposeCODE,.15: TEST ECX ; Was CGROUP already found? JNZ .15: MOV ECX,[EAX+SSS.GroupPtr] ; Use the first code segment as CGROUP. .15: JNSt [EAX+SSS.Purpose],sssPurposeDATA|sssPurposeBSS,.20: TEST EDI ; Was DGROUP already found? JNZ .20: MOV EDI,[EAX+SSS.GroupPtr] .20: ListGetNext EAX JNZ .10: JNSt [EBX+PGM.Pgmopt.Status],pgmoptTINY,.25: ; TINY model will have one common group used as addressing frame. TEST ECX JNZ .23: ; Prefer CGROUP, if exists. MOV ECX,EDI TEST ECX JNZ .23: MOV ECX,ESI TEST ECX JNZ .23: ; No explicit group was declared in TINY model, ; so clone an implicite group from the 1st segment. ListGetFirst [EBX+PGM.SssList] CALL .MakeGroup: MOV ECX,EDX SetSt [ECX+SSS.Purpose],sssPurposeCODE+sssPurposeDATA+sssPurposeBSS+sssPurposeSTACK .23: MOV EDI,ECX MOV ESI,ECX .25: ; ECX,EDI,ESI are now explicit code, data, stack groups, or 0 if no such group was declared in sources. ; Second segment pass in the loop .30: .. .80:. ; Each nongrouped-yet segment will be assigned to a group, ; either explicit when ECX/EDI/ESI is nonzero, or implicite (cloned from the segment). ListGetFirst [EBX+PGM.SssList] .30: JNSt [EAX+SSS.Status],sssSegment,.80: ; Skip if EAX is a group. CMP [EAX+SSS.Purpose],sssPurposeSTACK JNE .40: ; Stack segment regroup to implicit group ESI. TEST ESI JNZ .35: ; Skip if implicit stack group exists. CALL .MakeGroup: MOV ESI,EDX ; ESI is now the cloned group. .35: MOV [EAX+SSS.GroupPtr],ESI ; Assign the segment EAX to group ESI. JMP .80: .MakeGroup:PROC ; Create a new group as a clone of segment EAX. ; Input: EAX=^SSS with ungrouped segment, EBX=^PGM. ; Output: EDX=^SSS with the cloned group. MOV EDX,EAX ListStore [EBX+PGM.SssList],EDX XCHG EAX,EDX SetSt [EDX+SSS.Status],sssGroup+sssImplicit RstSt [EDX+SSS.Status],sssSegment MOV [EDX+SSS.GroupPtr],EDX ; Group always refers to itself. MOVD [EDX+SSS.EmitBuffer],0 ; No emit and reloc buffer in groups. MOVD [EDX+SSS.RelocBuffer],0 RET ENDP .MakeGroup: .40: JNSt [EAX+SSS.Purpose],sssPurposeCODE,.60: ; Code segment (implicit group is ECX). JSt [EBX+PGM.Pgmopt.Status],pgmoptTINY | pgmoptSMALL | pgmoptCOMPACT, .50: ; EAX is code segment in multicode model. Regroup to CGROUP only if it wasn't in a group. CMPD [EAX+SSS.GroupPtr],0 JNZ .80: CALL .MakeGroup: MOV [EAX+SSS.GroupPtr],EDX JMP .80: .50: ; EAX is code segment in monocode model. Regroup segment EAX to group ECX. TEST ECX JNZ .55: CALL .MakeGroup: MOV ECX,EDX .55: MOV [EAX+SSS.GroupPtr],ECX JMP .80: .60: JNSt [EAX+SSS.Purpose],sssPurposeDATA|sssPurposeBSS,.80: ; Data segment (implicit group is EDI). JSt [EBX+PGM.Pgmopt.Status],pgmoptTINY | pgmoptSMALL | pgmoptMEDIUM, .70: ; EAX is data segment in multidata model. Make self-group if it wasn't in a group. CMPD [EAX+SSS.GroupPtr],0 JNZ .80: CALL .MakeGroup: MOV [EAX+SSS.GroupPtr],EDX JMP .80: .70: ; EAX is data segment in monodata model. Regroup segment EAX to group EDI. TEST EDI JNZ .75: CALL .MakeGroup: MOV EDI,EDX .75: MOV [EAX+SSS.GroupPtr],EDI .80: ListGetNext EAX ; The next segment. JNZ .30: .90:EndProcedure PgmCreateImplicitGroups
Pgm.NrOfInitSegments
.
If there are more pointers in Pgm.SssPtrBuf than Pgm.NrOfInitSegments,
they all are uninitialized and their raw data may be omitted from image.PgmOrderSegments Procedure Pgm ; Define three internal subprocedures: .UpdateGroup:PROC1 ; Update .Status:sssNotBSS flag of the group. ; Input: EAX=^group, EDX=^Pgm. Destroys EDI. PUSH ECX MOV EDI,EAX ; EDI is now the updated group in base segment. ; Its flag sssNotBSS will be updated from all its segments. ListGetFirst [EDX+PGM.SssList] .U1:JNSt [EAX+SSS.Status],sssSegment,.U8: ; EAX is now a segment of program EDX. MOV ECX,[EAX+SSS.GroupPtr] ; ECX is now the group of segment EAX. CMP EDI,ECX JE .U6: ; If it is our segment. JECXZ .U8: ; ECX might be a group from linked segment. MOV ECX,[ECX+SSS.GroupPtr] CMP EDI,ECX JNE .U8: .U6:; EAX is our segment. Update our group EDI from its flag. JNSt [EAX+SSS.Status],sssNotBSS,.U8: SetSt [EDI+SSS.Status],sssNotBSS ; At least one segment of the group EDI has initialized data. JMP .U9: ; No need to search further. .U8:ListGetNext EAX ; The next segment. JNZ .U1: .U9:MOV EAX,EDI ; Restore group EAX. POP ECX RET ENDP1 .UpdateGroup: .StoreInitGroup:PROC1 ; Store ptr to initialized group and all its segments. ; Input: EAX=^group, EDX=^Pgm. EBX=output buffer. Destroys ECX,ESI,EDI. MOV EDI,EAX ; Group with flag sssNotBSS set. BufferStoreDword EBX,EDI ; Store initialized group EDI. BufferRetrieve EBX MOV ESI,ECX ; Save index to the 1st initialized grouped segment (for PgmOrderSegmDo). ListGetFirst [EDX+PGM.SssList] ; Now store all segments of group EDI. .I2:JNSt [EAX+SSS.Status],sssSegment,.I8: MOV ECX,[EAX+SSS.GroupPtr] ; ECX is now the group of segment EAX. CMP EDI,ECX JE .I6: ; If segment EAX belongs to the group EDI. JECXZ .I8: MOV ECX,[ECX+SSS.GroupPtr] ; ECX might be a group from linked segment. CMP EDI,ECX JNE .I8: .I6:BufferStoreDword EBX,EAX ; Store initialized segment EAX from group EDI. .I8:ListGetNext EAX JNZ .I2: ; The next segment. Invoke PgmOrderSegmDo,EBX,ESI ; Reorder segments with $ in their name. MOV EAX,EDI ; Restore group EAX. RET ENDP1 .StoreInitGroup: .StoreBssGroup:PROC1 ; ; Store ptr to uninitialized group and all its segments. ; Input: EAX=^group, EDX=^Pgm. EBX=output buffer. Destroys ECX,ESI,EDI. MOV EDI,EAX ; Group with flag sssNotBSS reset. BufferStoreDword EBX,EDI ; Store uninitialized group EDI. BufferRetrieve EBX MOV ESI,ECX ; Save index to the 1st uninitialized grouped segment (for PgmOrderSegmDo). ListGetFirst [EDX+PGM.SssList] ; Store all segments of group EDI. .B2:JNSt [EAX+SSS.Status],sssSegment,.B8: MOV ECX,[EAX+SSS.GroupPtr] ; ECX is now the group of segment EAX. CMP EDI,ECX JE .B6: ; If segment EAX belongs to the group EDI. JECXZ .B8: MOV ECX,[ECX+SSS.GroupPtr] ; ECX might be a group from linked segment. CMP EDI,ECX JNE .B8: .B6:BufferStoreDword EBX,EAX ; Store segment EAX from group EDI. .B8:ListGetNext EAX JNZ .B2: ; The next segment. Invoke PgmOrderSegmDo,EBX,ESI ; Reorder segments with $ in their name. MOV EAX,EDI ; Restore group EAX. RET ENDP1 .StoreBssGroup: ; Start the main procedure PgmOrderSegments: MOV EDX,[%Pgm] MOV EBX,[EDX+PGM.SssPtrBuf] ; The output buffer for ordered segments pointers. TEST EBX JNZ .10: BufferCreate [EDX+PGM.Pool],Size=64 MOV [EDX+PGM.SssPtrBuf],EAX MOV EBX,EAX .10:BufferClear EBX ; Pass 1 - for each group update its SSS.Status:sssNotBSS flag. ListGetFirst [EDX+PGM.SssList] JZ .90: .11:JNSt [EAX+SSS.Status],sssGroup,.19: CALL .UpdateGroup: .19:ListGetNext EAX ; The next group. JNZ .11: ; Pass 2: store initialized groups and all their segments. ListGetFirst [EDX+PGM.SssList] .21:JNSt [EAX+SSS.Status],sssGroup,.29: JNSt [EAX+SSS.Status],sssNotBSS,.29: CALL .StoreInitGroup: .29:ListGetNext EAX ; The next group. JNZ .21: ; Pass 3: store initialized nongrouped segments. XOR ESI,ESI DEC ESI ; Initialize the PgmOrderSegmDo index as unused (-1). ListGetFirst [EDX+PGM.SssList] .31:JNSt [EAX+SSS.Status],sssSegment,.39: CMPD [EAX+SSS.GroupPtr],0 ; Nongrouped segment? JNZ .39: JNSt [EAX+SSS.Status],sssNotBSS,.39: BufferStoreDword EBX,EAX ; Store initialized ungrouped segment EAX. TEST ESI JNS .39: ; Skip if index is already marked in ESI. BufferRetrieve EBX LEA ESI,[ECX-4] ; Save index to the 1st initialized ungrouped segment (for PgmOrderSegmDo). .39:ListGetNext EAX JNZ .31: ; The next segment. Invoke PgmOrderSegmDo,EBX,ESI ; Reorder segments with $ in their name. ; Pass 4: store uninitialized groups and all their segments. ListGetFirst [EDX+PGM.SssList] .41:JNSt [EAX+SSS.Status],sssGroup,.49: JSt [EAX+SSS.Status],sssNotBSS,.49: CALL .StoreBssGroup: .49:ListGetNext EAX ; The next group. JNZ .41: ; Pass 5: store uninitialized nongrouped segments. XOR ESI,ESI DEC ESI ; Initialize the PgmOrderSegmDo index as unused. ListGetFirst [EDX+PGM.SssList] .51:JNSt [EAX+SSS.Status],sssSegment,.59: CMPD [EAX+SSS.GroupPtr],0 JNZ .59: JSt [EAX+SSS.Status],sssNotBSS,.59: BufferStoreDword EBX,EAX ; Store uninitialized ungrouped segment EAX. TEST ESI JNS .59: ; Skip if index is already marked in ESI. BufferRetrieve EBX LEA ESI,[ECX-4] ; Save index to the 1st object for PgmOrderSegmDo. .59:ListGetNext EAX JNZ .51: ; The next segment. Invoke PgmOrderSegmDo,EBX,ESI ; Step 6: find number of initialized objects in .SssPtrBuf EBX. BufferRetrieve EBX MOV EBX,ECX SHR EBX,2 ; Number of all stored SSS objects. ; Walk through the objects backward. .62:CMP ECX,4 JB .69: ; If buffer bottom reached. MOV EAX,[ESI+ECX-4] ; The last stored pointer. JSt [EAX+SSS.Status],sssNotBSS,.69: ; If the last initialized segment found. DEC EBX ; Segment EAX is uninitialized, omit it from output count. SUB ECX,4 JMP .62: .69:MOV [EDX+PGM.NrOfInitSegments],EBX .90:EndProcedure PgmOrderSegments
PgmOrderSegmDo Procedure SegmPtrBuffer, Index1st OutBuf LocalVar SortBuf LocalVar Prefix$Ptr LocalVar ; Pointer to segment name. Prefix$Size LocalVar ; Number of characters up to $. Invoke EaBufferReserve::,PgmOrderSegmDo MOV [%OutBuf],EAX Invoke EaBufferReserve::,PgmOrderSegmDo MOV [%SortBuf],EAX BufferRetrieve [%SegmPtrBuffer] LEA EDX,[ESI+ECX] ; End of pointers. MOV ECX,[%Index1st] TEST ECX JS .90: BufferStore [%OutBuf],ESI,ECX ; Copy the fixed part of input buffer. ADD ESI,ECX ; ESI..EDX is an array of pointers to the investigated segments. JMP .20: .10:BufferStoreDword [%OutBuf],EBX .20:CMP ESI,EDX JNB .80: LODSD MOV EBX,EAX TEST EBX JZ .20: ; Skip if this segment was already stored to %OutBuf. JNSt [EBX+SSS.Status],sssSegment,.10: MOV EDI,[EBX+SSS.NamePtr] MOV ECX,[EBX+SSS.NameSize] MOV [%Prefix$Ptr],EDI MOV AL,'$' REPNE SCASB JNE .10: SUB EDI,[EBX+SSS.NamePtr] MOV [%Prefix$Size],EDI BufferStoreDword [%SortBuf],EBX ; EBX is segment with $ in name. MOVD [ESI-4],0 ; Mark the input segment as investigated. ; Now find and store all remaining segments matching [%Prefix$size]. PUSH ESI .30: CMP ESI,EDX JNB .40: LODSD MOV EBX,EAX TEST EBX JZ .30: PUSH ESI MOV ECX,[%Prefix$Size] MOV ESI,[%Prefix$Ptr] MOV EDI,[EBX+SSS.NamePtr] REPE CMPSB POP ESI JNE .30: ; Another matching segment found. BufferStoreDword [%SortBuf],EBX MOVD [ESI-4],0 ; Mark the input segment as investigated. JMP .30: .40: Invoke EaBufferSort::,[%SortBuf] BufferRetrieve [%SortBuf] BufferStore [%OutBuf],ESI,ECX BufferClear [%SortBuf] ; Prepare %SortBuf for the next group. .50:POP ESI JMP .20: .80: ; Copy re-sorted pointers from temporary %OutBuf the input/output %SegmPtrBuffer. BufferClear [%SegmPtrBuffer] BufferRetrieve [%OutBuf] BufferStore [%SegmPtrBuffer],ESI,ECX .90:Invoke EaBufferRelease::,[%OutBuf] Invoke EaBufferRelease::,[%SortBuf] EndProcedure PgmOrderSegmDo
PgmCombine merges linkable programs which were loaded from disk file(s) by
PfLoad, stored on Pgm.ModulePgmList
and marked pgmSelected
by PgmSelectModules.
Global symbols, all groups and segments including their emitted contents and relocations
are copied to the base program.
Public segments with matching names are concatenated into one segment
and offset of symbols and relocations from appended public segments is
adjusted to correspond with updated segment bottom.
Common and stack-combined segments with the same name are merged into one segment, too.
Private segments are simply added to the BasePgm.SssList
.
When PgmCombine ends its job, extern symbols coexist in BasePgm with public symbols of the same name, as well as with their corresponding extern pseudosegments. The process of matching EXTERN and PUBLIC will take place later in PgmLinkImage or in 3rd party linker.
Relocations in joined public segment are fixed-up relative to their segment bottom but not
finally resolved, because all segments of BasePgm still origin at RVA=0 and their order is not set yet.
Linking of segments to an output image will take place later in
PgmLinkImage invoked by PfFormatCompile
.
Segments of linked/imported programs, which were loaded on Pgm.ModulePgmList
, are emptied and they may be abandoned when PgmCombine terminates,
because information from all loaded programs is completely combined into the BasePgm.
This procedure is not invoked when BasePgm is a library in LIBOMF or LIBCOF format.
BasePgm.ModulePgmList
are combined to the BasePgm.
PgmCombine Procedure BasePgm MOV EBX,[%BasePgm] ListGetFirst [EBX+PGM.ModulePgmList] JZ .24: .10:MOV EDI,EAX ; EDI is now one of the modules which were loaded by LINK statement. JNSt [EDI+PGM.Status],pgmSelected,.20: ; Skip the unused library module (smart linking). .12:; Combine segments, groups, externs of loaded module EDI to the base program EBX in loop .14: .. .16:. ListGetFirst [EDI+PGM.SssList] ; EAX is now one of the SSS objects of linked module. JZ .20: .14:Invoke SssCombine::,EAX,EBX ; Combine linked segment/group/extern EAX to the main program EBX. .16:ListGetNext EAX JNZ .14: ; The next SSS object of linked module EDI. .20:ListGetNext EDI ; The next linked/imported program. JNZ .10: .24:; Update each group's .Bottom and .Top to match the lowest and highest VA of all its segments. ListGetFirst [EBX+PGM.SssList] ; Walk through groups in BasePgm. JZ .34: .26:JNSt [EAX+SSS.Status],sssGroup,.30: Invoke SssResizeGroup::,EAX,EBX .30:ListGetNext EAX JNZ .26: ; The next group. .34:; Combine and fixup program entry plus global symbols from all referenced modules in loop .36: .. .64:. ListGetFirst [EBX+PGM.ModulePgmList] JZ .82: .36:MOV EDI,EAX ; Linked module. JSt [EBX+PGM.Pgmopt.Status],pgmoptLinkable,.37: ; Combine nonreferenced module when a linkable is compiled. JNSt [EDI+PGM.Status],pgmSelected,.64: ; Skip unused library module. .37:LEA ECX,[EDI+PGM.EntryExp] CMPB [ECX+EXP.Status+0],'A' JNE .40: ; Skip if evaluated entry is scalar or none. ; Update evaluated address entry symbol. MOV ESI,[ECX+EXP.Seg] TEST ESI JNZ .38: Msg '7711',EDI ; Invalid program entry point "!1S". JMP .40: .38:JNSt [ESI+SSS.Status],sssSegment,.40: ; Skip if the entry is an external symbol. MOV EAX,[ESI+SSS.BottomLow] ; Bottom of linked segment might have been elevated MOV EDX,[ESI+SSS.BottomHigh] ; from 0 to delta EDX:EAX in first combine pass. MOV ESI,[ESI+SSS.SegmPtr] SUB EAX,[ESI+SSS.BottomLow] SBB EDX,[ESI+SSS.BottomHigh] ADD [ECX+EXP.Low],EAX ; Fixup the evaluated entry symbol by delta. ADC [ECX+EXP.High],EDX MOV [ECX+EXP.Seg],ESI ; Updated evaluated entry ECX from linked program EDI will be copied to base program EBX. LEA ESI,[EBX+PGM.EntryExp] CMPD [ESI+EXP.Status],0 ; Evaluated entry in base program should be empty. Msg cc=NZ,'8544',EDI,EBX ; The entry specified in program "!1S" collides with entry in "!2S". JNZ .40: CopyTo ESI,ECX,Size=SIZE#EXP ; Copy evaluated entry ECX to the base program. .40:; Copy and update global symbols from linked module EDI in the loop .46: .. .60:. ListGetFirst [EDI+PGM.SymList] JZ .64: ; If no symbol exists in module EDI. .46:MOV ESI,EAX ; Symbol ESI from linked module EDI is global (otherwise it wouldn't get loaded). MOV ECX,[ESI+SYM.Section] ; Symbol ESI might have been defined in a segment from linked Pgm. JECXZ .50: ; If the symbol is scalar. MOV EAX,[ECX+SSS.BottomLow] ; Bottom is nonzero if symbol ESI was defined in the linked segment ECX, MOV EDX,[ECX+SSS.BottomHigh] ; it might have been elevated from 0 to delta EDX:EAX. MOV ECX,[ECX+SSS.SegmPtr] ; ECX now refers to the new homonymous segment in base program. ; If ECX is an extern pseudosegment, it hasn't been elevated, so the fixup delta is void (EDX:EAX=0). ADD [ESI+SYM.OffsetLow],EAX ; Fixup the symbol by delta EDX:EAX. ADC [ESI+SYM.OffsetHigh],EDX MOV [ESI+SYM.Section],ECX .50:JNSt [ESI+SYM.Status],symPublic|symExport,.54: ; Combine linked public|exported symbol ESI into base program EBX. Invoke SymFindByName::,symPublic|symExport,[ESI+SYM.NamePtr],[ESI+SYM.NameSize],EBX JC .53: MOV ECX,[EAX+SYM.Status] JSt ECX,symDefined,.E8540: AND ECX,symImport+symExport CopyTo EAX,ESI,Size=SIZE# SYM SetSt [EAX+SYM.Status],ECX JMP .60: .E8540:Msg '8540',ESI,[EAX+SYM.LinePtr] ; Public symbol "!1S" was already defined at !2@. .53:ListStore [EBX+PGM.SymList],ESI ; Copy global symbol ESI to the base program EBX. JMP .60: .54:JNSt [ESI+SYM.Status],symExtern|symImport,.60: ; Combine linked external|imported symbol ESI into base program EBX. Invoke SymFindByName::,symExtern|symImport,[ESI+SYM.NamePtr],[ESI+SYM.NameSize],EBX JNC .56: ; Skip when symbol with this name already is in base program. ListStore [EBX+PGM.SymList],ESI ; Copy global symbol ESI to the base program EBX. .56:; EAX is now a homonymous external|imported symbol in base program EBX. ; Update some of its properties from symbol ESI. MOV ECX,symPropMask+symDefined+symReferenced+symQueried+symUsed+symDefInPass+symFixed+symImport AND ECX,[ESI+SYM.Status] OR [EAX+SYM.Status],ECX ; Let's update base symbol EAX if linked symbol ESI is imported and EAX is not. ; This happens when a symbol is global or double-colon-terminated in base program ; and explicitly declared as IMPORT in linked module. MOV ECX,[ESI+SYM.DllNameSize] JECXZ .58: MOV [EAX+SYM.DllNameSize],ECX MOV ECX,[ESI+SYM.DllNamePtr] MOV [EAX+SYM.DllNamePtr],ECX .58:Invoke SssCreateExtern::,EAX,EBX ; Make sure that is has its pseudosegment. .60:ListGetNext ESI ; The next symbol. JNZ .46: .64:ListGetNext EDI ; The next linked program. JNZ .36: ; Fixup all symbols combined now in base program EBX. ListGetFirst [EBX+PGM.SymList] JZ .70: .66:Invoke SymFixup::,EAX ; Elevate symbol offset if it was defined in concatenated segment. ListGetNext EAX JNZ .66: .70:; Fixup all relocations combined now in base program EBX. ListGetFirst [EBX+PGM.SssList] JZ .90: .74:BufferRetrieve [EAX+SSS.RelocBuffer] JECXZ .80: .77:Invoke RelocFixup::,ESI ADD ESI,SIZE# RELOC SUB ECX,SIZE# RELOC JA .77: .80:ListGetNext EAX JNZ .74: ; Relocations in the next segment. .82:; Add default library for imported symbols without explicit LIB= specification. ListGetFirst [EBX+PGM.SymList] JZ .90: .84:JNSt [EAX+SYM.Status],symImport,.88: MOV ECX,[EAX+SYM.DllNameSize] TEST ECX JNZ .88: MOV [EAX+SYM.DllNamePtr],=B'%EaDefaultDllName' MOV [EAX+SYM.DllNameSize],%EaDefaultDllNameSize .88:ListGetNext EAX JNZ .84: .90:MOVD [EBX+PGM.ModulePgmList],0 ; Invalidate loaded modules, though they still occupy Pgm.Pool. EndProcedure PgmCombine
PgmLinkImage is invoked when assembly of the executable | image
(BIN, COM, MZ, PE, DLL) program format ends, all sections have been joined within their segment by
SssLinkSection
, all segments from all linked programs have been combined by
PgmCombine, groups and segments were sorted by
PgmOrderSegments and their pointers stored to
Pgm.SssPtrBuf
in the final order.
PgmLinkImage links program segments as PRIVATE one after another into virtual image.
Image starts at virtual address (VA) specified by PROGRAM IMAGEBASE=
.
Alignment of segments withing the image is calculated as the greatest value from
segment.Align, SectionAlign, FileAlign
.
Each segment's .Bottom and .Top is adjusted to their new aligned VA.
The actual raw contents is not concatenated yet by this procedure, see
PfFormatCompile.
Bottoms of all segments were zero (related to RVA=0) when PgmLinkImage was invoked
but this will be changed here.
PgmLinkImage Procedure Pgm, StubSize OrgLow LocalVar ; Image virtual address. It starts at Pgm.Pgmopt.ImageBase. OrgHigh LocalVar ; High dword of image VA. Alignment LocalVar ; Strongest alignment from PGMOPT.FileAlign and PGMOPT.SectionAlign. Base LocalVar ; PGMOPT.ImageBaseLow in format BIN, otherwise 0. XOR ECX,ECX MOV EBX,[%Pgm] MOV [%Base],ECX ; Initialize Org with ImageBase. MOV EAX,[EBX+PGM.Pgmopt.ImageBaseLow] MOV EDX,[EBX+PGM.Pgmopt.ImageBaseHigh] MOV ECX,[EBX+PGM.Pgmopt.Status] CMP CL,pgmoptBIN JNE .10: MOV [%Base],EAX .10:; Formats PE and DLL will reserve at least one section alignment at the beginning of image ; for the copy of stub. VA of other executable images starts directly at ImageBase. ADD EAX,[%StubSize] ADC EDX,0 MOV [%OrgLow],EAX MOV [%OrgHigh],EDX ; Initialize image alignment. MOV EAX,[EBX+PGM.Pgmopt+PGMOPT.FileAlign] MOV EDX,[EBX+PGM.Pgmopt+PGMOPT.SectionAlign] CMP EAX,EDX JAE .15: XCHG EAX,EDX .15:MOV [%Alignment],EAX ; Maximum from FileAlign and SectionAlign. ; Compute new VA of bottom and top for each segment. ; The segment order has already been specified in Pgm.SssPtrBuf. ; All segments start at .Bottom=0. BufferRetrieve [EBX+PGM.SssPtrBuf] ; ESI,ECX is now a sorted array of pointers to SSS. SHR ECX,2 ; Number of linked segments. JZ .90: ; If no segments in image. .20:PUSH ECX LODSD ; Load pointer to the linked segment. JNSt [EAX+SSS.Status],sssSegment,.40: ; Skip groups in this pass. MOV ECX,[EAX+SSS.Alignment] ; Segment's own alignment. CMP ECX,[%Alignment] JA .30: MOV ECX,[%Alignment] .30: ; ECX is now the strongest alignment from segment/file/section align. MOV EDI,[%OrgLow] MOV EDX,[%OrgHigh] Invoke ExpAlign::,EDI,ECX,0 ; Return unaligned stuff size in ECX. ADD EDI,ECX ADC EDX,0 ; EDX:EDI is now the new aligned segment VA. SUB EDI,[EAX+SSS.BottomLow] SBB EDX,[EAX+SSS.BottomHigh] ; EDI:EDX is now delta of relocation. ADD [EAX+SSS.BottomLow],EDI ADC [EAX+SSS.BottomHigh],EDX ADD [EAX+SSS.TopLow],EDI ADC [EAX+SSS.TopHigh],EDX MOV EDI,[EAX+SSS.TopLow] MOV EDX,[EAX+SSS.TopHigh] MOV [%OrgLow],EDI MOV [%OrgHigh],EDX .40:POP ECX LOOP .20: ; The next segment. ; For each group compute its new VA of bottom and top (it will be shown in ListMap). BufferRetrieve [EBX+PGM.SssPtrBuf] ; ESI,ECX is now a sorted array of pointers to SSS. SHR ECX,2 .50:LODSD JNSt [EAX+SSS.Status],sssGroup,.60: Invoke SssResizeGroup::,EAX,EBX .60:LOOP .50: ; Match extern/imported symbols to the public ones. Invoke SymResolveImage::,EBX ; Resolve relocations in each segment. BufferRetrieve [EBX+PGM.SssPtrBuf] ; ESI,ECX is now an sorted array of pointers to SSS. SAR ECX,2 .70:LODSD JNSt [EAX+SSS.Status],sssSegment,.80: Invoke RelocResolveImage::,EAX,EBX .80:LOOP .70: .90:EndProcedure PgmLinkImage
PgmConcatenateImage will allocate a buffer on Pgm.Pool
and fill the buffer with concatenated raw emitted contents of initialized program segments.
It is invoked when executable output file BIN, COM, MZ
(but not PE, DLL
) is compiled.
Segment order is specified by Pgm.SssPtrBuf
.
Only initialized segments are streamed. Their count was stored to Pgm.NrOfInitSegments
by PgmLinkImage.
Differences between virtual size (Segment.Top - Segment.Bottom
) and emitted size, if any, are stuffed with 0 or with NOP (if both segments have
PURPOSE=CODE
.
Total allocated size of the buffer is the sum of virtual sizes of all initialized segments.
Netto buffered size is less when the emitted size of the last segment is lower than its virtual size.
PgmConcatenateImage Procedure Pgm SegmPtrBuf LocalVar ; Temporary buffer with max. Pgm.NrOfInitSegments number of pointers (groups are omitted). SegmLeft LocalVar ; Number of not-yet-streamed segments. EmitSize LocalVar ; Size of segment.EmitBuffer. VirtSize LocalVar ; segment.Top - segment.Bottom. TotalSize LocalVar ; Sum of virtual sizes of all initialized segments. MOV EBX,[%Pgm] Invoke EaBufferReserve::,PgmConcatenateImage MOV [%SegmPtrBuf],EAX MOV EDX,EAX ; Copy pointers to initialized segments from Pgm.SssPtrBuf to %SegmPtrBuf EDX. BufferRetrieve [EBX+PGM.SssPtrBuf] ; Pointers to groups and segments. SHR ECX,2 JZ .25: ; If no segment to stream. MOV EAX,[EBX+PGM.NrOfInitSegments] CMP ECX,EAX JBE .10: XCHG EAX,ECX JECXZ .25: ; If no initialized segment to stream. .10:LODSD JNSt [EAX+SSS.Status],sssSegment,.20: ; Skip groups. BufferStoreDword EDX,EAX .20:LOOP .10: .25:BufferRetrieve EDX ; Reread pointers to initialized segments. SUB EAX,EAX SUB EDX,EDX SHR ECX,2 MOV [%SegmLeft],ECX MOV [%TotalSize],EAX MOV [%EmitSize],EAX MOV [%VirtSize],EAX JZ .27: ; Compute total virtual size of all initialized segments to EDX:EAX. .26:MOV EDI,[ESI] ADD ESI,4 ADD EAX,[EDI+SSS.TopLow] ADC EDX,[EDI+SSS.TopHigh] SUB EAX,[EDI+SSS.BottomLow] SBB EDX,[EDI+SSS.BottomHigh] Msg cc=NZ,'8525',EDI ; Size of segment [!1S] exceeded 4 GB. LOOP .26: .27:BufferCreate [EBX+PGM.Pool],Size=EAX ; Preallocate buffer with total image virtual size. MOV [%ReturnEAX],EAX JNC .28: Msg '9316' ; Allocation error creating image buffer. JMP .90: .28:; Stream the contents of SegmLeft segments to buffer [%ReturnEAX]. BufferRetrieve [%SegmPtrBuf] ; Reread pointer array to ESI. ; Store raw contents of segments in the loop .30: .. .80:. .30:MOV ECX,[%SegmLeft] DEC ECX JS .90: ; Quit when no segment is left to stream. MOV [%SegmLeft],ECX LODSD MOV EBX,EAX ; EBX is a segment to stream. MOV ECX,[%VirtSize] SUB ECX,[%EmitSize] JNA .55: ; Emitted size of previous segment was less than its virtual size. Align. XOR EDX,EDX ; Alignment stuff. CMPD [EBX+SSS.Purpose],sssPurposeCODE JNE .40: MOV EAX,[ESI-8] ; EAX is the previous segment. CMPD [EAX+SSS.Purpose],sssPurposeCODE JNE .40: MOV DL,0x90 ; Alignment stuff is 0x90 only when both segments have PURPOSE=CODE. .40:; Buffer ECX bytes of alignment stuff DL pending from previous segment. BufferStoreByte [%ReturnEAX],EDX LOOP .40: .55:CMPD [%SegmLeft],0 JNA .60: MOV EDI,[ESI] ; Peek the next segment. MOV EAX,[EDI+SSS.BottomLow] MOV EDX,[EDI+SSS.BottomHigh] JMP .65: .60:; If segment EBX is the last, rather than peek the next one use top of it. MOV EAX,[EBX+SSS.TopLow] MOV EDX,[EBX+SSS.TopHigh] .65:; EDX:EAX is now the bottom of the next segment (or top of the last one). SUB EAX,[EBX+SSS.BottomLow] SBB EDX,[EBX+SSS.BottomHigh] JZ .70: Msg '8524',EBX ; Size of segment [!1S] exceeded 4 GB. .70:MOV [%VirtSize],EAX PUSH ESI BufferRetrieve [EBX+SSS.EmitBuffer] MOV [%EmitSize],ECX CMP ECX,EAX JNA .75: XCHG ECX,EAX ; If emitted size is bigger than virtual size, truncate raw data. .75: BufferStore [%ReturnEAX],ESI,ECX ; Store raw emitted data. .80:POP ESI JMP .30: .90:Invoke EaBufferRelease::,[%SegmPtrBuf] EndProcedure PgmConcatenateImage
PgmDetectImportModule tries to detect if the Pgm (just loaded module) is
a module from import library, i.e. if it contains import info only,
and it will set the flag Pgm.Status:pgmImpLibMember
in this case.
Module is pure import library member if
Import libraries might have been created by a third-party librarian, such as ALIB.exe, The only code they have is an indirect proxy jump through IAT table to dynamically loaded function, and a public symbol referring to this proxy jump. Imported module may also contain an extern symbol with decorated internal name.
Pgm.Status
.PgmDetectImportModule Procedure Pgm MOV EBX,[%Pgm] RstSt [EBX+PGM.Status],pgmImpLibMember ; First assume it is an ordinary object module. SUB ECX,ECX ; Count symbols in module EBX into counter ECX. ListGetFirst [EBX+PGM.SymList] JZ .90: ; No symbols, not an import library member. .10:INC ECX ListGetNext EAX JNZ .10: CMP ECX,3 JA .90: ; Too many symbols, not an import library member. SUB ECX,ECX ; Count segments in module EBX into counter ECX. ListGetFirst [EBX+PGM.SssList] JZ .90: ; No segment, not an import library member. .20:JNSt [EAX+SSS.Status],sssSegment,.30: INC ECX MOV EDX,EAX ; Remember the segment in EDX. .30:ListGetNext EAX JNZ .20: CMP ECX,1 JNE .90: ; Too many segments, not an import library member. ; EDX is the one and only segment. MOV ECX,[EDX+SSS.NameSize] MOV ESI,[EDX+SSS.NamePtr] CMP ECX,5 JB .90: ; Segment name is too short, not IDATA/IMPORT. ; Convert the segment name ESI,ECX to uppercase. Invoke EaBufferReserve::,PgmDetectImportModule MOV EBX,EAX BufferStore EBX,ESI,ECX BufferRetrieve EBX MOV EDI,ESI .40:LODSB ; Convert the letter to upper case. CMP AL,'a' JB .50: CMP AL,'z' JA .50: SUB AL,'a'-'A' .50:STOSB LOOP .40: BufferRetrieve EBX ; ESI,ECX is now uppercased segment name. Invoke EaBufferRelease::,EBX MOV EDI,ESI ; Search the name for 'IMPORT' or 'IDATA'. MOV AL,'I' .60:REPNE SCASB JNE .90: CMP ECX,4 JB .90: CMPD [EDI],'DATA' JE .70: CMP ECX,5 JB .90: CMPB [EDI],'M' JNE .60: CMPD [EDI+1],'PORT' JNE .60: .70:BufferRetrieve [EDX+SSS.EmitBuffer] CMP ECX,7 JA .90: CMP CL,6 JB .90: ; Segment name contains 'IMPORT' or 'IDATA' and its code is 6..7 bytes long. Module is import library member. MOV EBX,[%Pgm] SetSt [EBX+PGM.Status],pgmImpLibMember .90:EndProcedure PgmDetectImportModule
PgmCheckDirty Procedure Pgm MOV EBX,[%Pgm] MOV ECX,[EBX+PGM.LinkFilesNr] JECXZ .10: STC JMP .90: ; Dirty. .10:ListGetFirst [EBX+PGM.SssList] JZ .NotDirty: .20:Invoke SssCheckDirty::,EAX,EBX JC .90: ; Dirty ListGetNext EAX JNZ .20: .NotDirty: CLC .90: EndProcedure PgmCheckDirty
PgmCheckGlobal Procedure Program MOV EBX,[%Program] ListGetFirst [EBX+PGM.SymList] JZ .90: .10: JSt [EAX+SYM.Status],symPublic|symExport|symImport,.90: ; Return with ZF=0. ListGetNext EAX JNZ .10: .90: EndProcedure PgmCheckGlobal
PgmSelectModules provides smart linking
of modules to executable file. It will inspect each module loaded on
Program.ModulePgmList
whether some of its public|export symbols
is referenced by some extern|import symbol defined in the base program
or in other non-import modules, and vice versa. Such module will be marked
pgmSelected
for later combining. Unreferenced modules from OMF or COFF
libraries will not be combined and linked.
When the base Program format is linkable (OMF,COFF,LIBOMF,LIBCOF) instead of executable,
all its loaded modules will be marked pgmSelected
regardless if they are referenced or not (perhaps they will be
referenced later, when the base Program would get linked to an executable format).
When any of the linked program is a standalone module (COFF,OMF) and not a library member (pgmoptLibMember=0
),
it will be marked pgmSelected
regardless if its symbols are referenced or not
(it was explicitly required with LINK module.obj
).
PGM.Status:pgmSelected
.
PgmSelectModules Procedure Program
PubBuf LocalVar ; Pointer to a buffer which contains DWORD pointers to all public|export symbols.
ExtBuf LocalVar ; Pointer to a buffer which contains DWORD pointers to all extern|import symbols.
Invoke EaBufferReserve::,PgmSelectModules
MOV [%PubBuf],EAX
MOV ECX,EAX
Invoke EaBufferReserve::,PgmSelectModules
MOV [%ExtBuf],EAX
MOV EDX,EAX
MOV EBX,[%Program]
SetSt [EBX+PGM.Status],pgmSelected ; The base program is always selected.
; Smart linking will be performed when the base program is not linkable (it's an executable image).
JNSt [EBX+PGM.Pgmopt.Status],pgmoptLinkable,.14:
; Otherwise Pass 1 will mark all modules pgmSelected
; when the base program being compiled is a linkable module or library.
ListGetFirst [EBX+PGM.ModulePgmList]
JZ .90: ; Nothing to do when no module was loaded.
.10:SetSt [EAX+PGM.Status],pgmSelected
ListGetNext EAX
JNZ .10:
JMP .90:
.14:; Pass 2 collects pointers to global symbols
; into ExtBuf EDX and PubBuf ECX from all non-import-only modules.
Invoke PgmStoreGlobalSymbols,EBX,ECX,EDX ; Collect globals from the base program.
ListGetFirst [EBX+PGM.ModulePgmList]
JZ .90: ; Nothing to do when no module was loaded.
.18:JSt [EAX+PGM.Status],pgmImpLibMember,.22: ; Ignore pure import library members.
Invoke PgmStoreGlobalSymbols,EAX,ECX,EDX ; Collect globals from the loaded module.
.22:ListGetNext EAX
JNZ .18:
.26:; Pass 3 inspects each module and flags the module
; as pgmSelected if any of its public symbols is referenced by any symbol in ExtBuf,
; or if the module is standalone COFF|OMF.
ListGetFirst [EBX+PGM.ModulePgmList]
.30:MOV EBX,EAX ; EBX is now the inspected module.
JSt [EBX+PGM.Status],pgmSelected,.54: ; Skip inspection when already referenced.
JSt [EBX+PGM.Pgmopt.Status],pgmoptLibMember,.34:
SetSt [EBX+PGM.Status],pgmSelected ; Select standalone COFF|OMF modules.
JMP .54:
.34:ListGetFirst [EBX+PGM.SymList]
JZ .54: ; Skip the module EBX when it has no symbols.
.38:MOV EDI,EAX ; EDI is now a symbol from module EBX.
JNSt [EDI+SYM.Status],symPublic|symExport,.50:
; EDI is public symbol in loaded module EBX. Find if it is referenced.
BufferRetrieve [%ExtBuf]
SHR ECX,2
JZ .58:
.42:LODSD ; EAX is now one of external/imported symbols. Compare names of symbols EAX and EDI.
Compare [EAX+SYM.NamePtr],[EAX+SYM.NameSize],[EDI+SYM.NamePtr],[EDI+SYM.NameSize]
JNE .46:
SetSt [EBX+PGM.Status],pgmSelected ; The module was referenced, it will be combined and linked.
JMP .54: ; Break further inspecting of other symbols in module EBX.
.46:LOOP .42:
.50:ListGetNext EDI ; The next symbol.
JNZ .38:
.54: ListGetNext EBX ; The next loaded module.
JNZ .30:
.58:; Pass 4 collects pointers to global symbols.
; into ExtBuf EDX and PubBuf ECX again (as in Pass 2), but this time
; from referenced modules only.
MOV EBX,[%Program]
MOV ECX,[%PubBuf]
MOV EDX,[%ExtBuf]
BufferClear ECX
BufferClear EDX
Invoke PgmStoreGlobalSymbols,EBX,ECX,EDX ; Collect globals from the base program.
ListGetFirst [EBX+PGM.ModulePgmList]
.62:JNSt [EAX+PGM.Status],pgmSelected,.66: ; Skip not referenced modules.
Invoke PgmStoreGlobalSymbols,EAX,ECX,EDX ; Collect globals from the loaded module.
.66:ListGetNext EAX
JNZ .62:
; Pass 5 inspects each module and flags it as pgmSelected
; if any of its public|import symbols is referenced by any symbol in ExtBuf.
ListGetFirst [EBX+PGM.ModulePgmList]
.70:MOV EBX,EAX ; EBX is now the inspected module.
ListGetFirst [EBX+PGM.SymList]
JZ .88: ; Skip the module EBX when it has no symbols.
.74:MOV EDI,EAX ; EDI is now a symbol from module EBX.
JNSt [EDI+SYM.Status],symPublic|symImport,.86:
; EDI is public|import symbol in loaded module EBX. Find if it is referenced.
BufferRetrieve [%ExtBuf]
SHR ECX,2
JZ .86:
.78:LODSD ; EAX is now one of external/imported symbols. Compare names of symbols EAX and EDI.
Compare [EAX+SYM.NamePtr],[EAX+SYM.NameSize],[EDI+SYM.NamePtr],[EDI+SYM.NameSize]
JNE .82:
SetSt [EBX+PGM.Status],pgmSelected ; The module was referenced, it will be combined and linked.
JMP .88: ; Break further inspecting of other symbols in module EBX.
.82:LOOP .78:
.86:ListGetNext EDI ; The next symbol.
JNZ .74:
.88: ListGetNext EBX ; The next loaded module.
JNZ .70:
.90: Invoke EaBufferRelease::,[%ExtBuf]
Invoke EaBufferRelease::,[%PubBuf]
EndProcedure PgmSelectModules
PgmStoreGlobalSymbols Procedure PgmPtr,PubBuffer,ExtBuffer MOV EDI,[%PgmPtr] MOV ECX,[%PubBuffer] MOV EDX,[%ExtBuffer] ListGetFirst [EDI+PGM.SymList] JZ .90: .30: JNSt [EAX+SYM.Status],symExtern|symImport,.50: BufferStoreDword EDX,EAX .50: JNSt [EAX+SYM.Status],symPublic|symExport,.80: BufferStoreDword ECX,EAX .80: ListGetNext EAX ; The next symbol. JNZ .30: .90: EndProcedure PgmStoreGlobalSymbols
BasePgm.ModulePgmList
. The module is marked pgmImpModule
and it contains only the imported symbol.
symImport+'A'
.
PgmCreateImportModule Procedure BasePgmPtr, DllNamePtr, DllNameSize, OrdinalNr, \ SymStatus, SymNamePtr, SymNameSize, \ SymInterNamePtr, SymInterNameSize MOV EBX,[%BasePgmPtr] MOV EDX,[EBX+PGM.Pool] ListNew [EBX+PGM.ModulePgmList],Zeroed=yes MOV EDI,EAX MOV [EDI+PGM.Pool],EDX ; Let module's pool parasite on BasePgm.Pool. SetSt [EDI+PGM.Status],pgmImpLibMember MOV EAX,pgmoptWidthMask AND EAX,[EBX+PGM.Pgmopt.Status] OR EAX,pgmoptLibMember SetSt [EDI+PGM.Pgmopt.Status],EAX ListCreate EDX,SIZE#SSS MOV [EDI+PGM.SssList],EAX ListCreate EDX,SIZE#SYM MOV [EDI+PGM.SymList],EAX ListNew EAX,Zeroed=yes MOV EBX,EAX ; EBX is now pointer to the imported symbol. MOV EAX,[%SymStatus] SetSt [EBX+SYM.Status],EAX MOV ESI,[%DllNamePtr] MOV ECX,[%DllNameSize] PoolStore EDX,ESI,ECX MOV [EBX+SYM.DllNamePtr],EAX MOV [EBX+SYM.DllNameSize],ECX MOV ESI,[%SymNamePtr] MOV ECX,[%SymNameSize] PoolStore EDX,ESI,ECX MOV [EDI+PGM.NamePtr],EAX MOV [EDI+PGM.NameSize],ECX MOV [EBX+SYM.NamePtr],EAX MOV [EBX+SYM.NameSize],ECX MOV [EBX+SYM.InterNamePtr],EAX MOV [EBX+SYM.InterNameSize],ECX MOV ESI,[%SymInterNamePtr] MOV ECX,[%SymInterNameSize] JECXZ .80: PoolStore EDX,ESI,ECX MOV [EBX+SYM.InterNamePtr],EAX MOV [EBX+SYM.InterNameSize],ECX .80:MOV EAX,[%OrdinalNr] MOV [EBX+SYM.OrdinalNr],EAX Invoke SssCreateExtern::,EBX,EDI EndProcedure PgmCreateImportModule
ENDPROGRAM pgm