SSS is a common class for objects of five types: group, segment, section, structure, external-pseudosegment.
SSS objects are stored on Pgm.SssList.
Flags in SSS.Status::sssTypeMask specify which type the object actually represents.
sssSegment and sssSection may be set simultaneously
and such object represents both segment and its base section.
Base section is the one with the same name as its hosting segment,
its address is identical with the first byte of the segment.
sssGroup and sssSegment may not be set simultaneously
(even when it is base segment of the group).
Base segment of the group is the one which was declared first (compared to other
members of the group) and has therefore the lowest VA.
Flag sssExtern identifies the object as an auxilliary pseudosegment
which belongs to external symbol mentioned in program. Each external symbol has its own unique
pseudosegment. No other flag in sssTypeMask is set together with sssExtern
. See also Symbol and Segment binding.
Default implicit segments are created at PgmCreateProgram by SssCreateImplicit for each purpose (code, rodata, data, bss, stack), actual default segment names depend on the program format and model. Other SSS object may be created in the same program with explicit STRUC, GROUP, SEGMENT, [section], EXTERN statements, they can be used instead. Unused default segments will be abandoned when they are empty (nothing was emitted to them).
Structures are handled semistatically, SSS object is created or updated in
PseudoSTRUC, where all its buffers
and properties are cleared, except for SSS.Name and SSS.Top.
Then it is rebuild from D* statements again in each pass.
SSS structure is not cleared on pass transition, which allows to forward
reference structured data in source and declare STRUC/ENDSTRUC block later.
Tree of program groups, segments and sections (but not external pseudosegments) is maintained with members
.SegmPtr and .GroupPtr and it can be inspected using the statement
%DISPLAY with operand GROUP, SEGMENT
or SECTION (all operands display the same tree).
EUROASM NOWARN=2101
sss PROGRAM FORMAT=COFF,MODEL=FLAT,WIDTH=32
INCLUDEHEAD euroasm.htm, \ Interface (structures, symbols and macros) of other modules used in this source.
dict.htm,ea.htm,eaopt.htm,exp.htm,ii.htm,msg.htm,pass.htm,pf.htm, \
pfcoff.htm,pgm.htm,pgmopt.htm,reloc.htm,stm.htm,sym.htm,var.htm
sss HEAD ; Start of module interface.
.Top - .Bottom
. It is not necessarily equal to .EmitBuffer size.
.Bottom represents virtual address of the structure/section/segment/group.
.Status:sssNotBSS is set when the object contains initialized data.
In the case of structure it is set when at least one structure member was defined with initialized data value,
so the instances of the structure will be placed is section with
PURPOSE=DATA if AUTOSEGMENT=ENABLED.
| Object type | .Bottom | .GroupPtr | .SegmPtr |
|---|---|---|---|
| sssStructure | 0 | 0 | 0 |
| sssSection | section VA | 0 or ^explicit group | ^segment |
| sssSegment | 0 | 0 or ^explicit group | ^self |
| sssGroup | 0 | ^self | 0 or ^base segment |
| sssExtern | 0 | 0 | ^extern |
| Object type | .Bottom | .GroupPtr | .SegmPtr |
|---|---|---|---|
| sssStructure | 0 | 0 | 0 |
| sssSection | N/A | N/A | N/A |
| sssSegment | segment VA | ^explicit or implicit group | ^self |
| sssGroup | group VA | ^self | ^base segment |
| sssExtern | extern VA | ^explicit or implicit group | ^extern |
SSS STRUC ; +00h. .NamePtr D D ; Object name without[]. In case of sssExtern it is the symbol name. .NameSize D D ; Size of object name. .ClassPtr D D ; Unquoted identifier specified bySEGMENT CLASS='ClassName'. It may be empty. .ClassSize D D ; Netto size of segment ClassName. ; +10h. .BottomLow D D ; Base offset of section in segment or segment's bottom address. 0 when unlinked yet and in sssStructure.. .BottomHigh D D ; Bottom is 0 at assembly time for structures, segments, groups. .SegmPtr D D ; ^SSS sssSegment where this section|segment|group belongs to. It may point to itself. Never 0. .GroupPtr D D ; ^SSS sssGroup where this section|segment|group belongs to. It may point to itself, or 0 for nongrouped segments. ; +20h. .Purpose D D ; Purpose of the section or segment in SssPurposeEncoding. .SymPtr D D ; ^SYM associated with this sssSegment|sssSection|sssExtern. 0 in sssStructure. .LinePtr D D ; Pointer to the physical line in mapped source which declared the object. .Status D D ; Boolean properties of the object in SssEncoding. ; +30h. .OrgLow D D ; Origin pointer {offset of $}. Reset to .Bottom at start of each pass. ` .OrgHigh D D ; Member .Org is misused at link-time for file address of segment contents. .TopLow D D ; Top limit of section/segment. Maximum of .Org ever reached. Not reset at start of pass. .TopHigh D D ; SSS.Top-SSS.Bottom is object's virtual size. Emitted size is in .EmitBuffer, invalid in BSS and STRUC. ; +40h. .PgmPool D D ; ^POOL where structure-member names will be stored persistently. .EmitBuffer D D ; ^BUFFER with emitted code/data. .RelocBuffer D D ; ^BUFFER with RELOC records. .SssOrdBuffer D D ; ^BUFFER with DWORD pointers to ordered section (if sssSegment), or ; ^BUFFER with DWORD pointers to ordered segments (if sssGroup), or ; ^BUFFER with MEMBER records (if sssStructure). Otherwise 0. ; +50h. .Alignment D D ; Segment's own alignment specified bySEGMENT ALIGN=. Power of 2. .SegmIndex D D ; Ordinal of this segment in COFF|ELF section header table or in OMF SGMDEF|GRPDEF record. .NameIndex D D ; Ordinal of symbol of this segment in COFF|ELF SymbolTable|OMF LNAMES ordinal. .sh_link D D ; Value of PFELF_SHDR.sh_link. ; +60h. .sh_info D D ; Value of PFELF_SHDR.sh_info. .RelocatedSss D D ; ^SSS which is relocated by this [.rel.name] ELF section. .SVA EQU .RelocatedSss ; SVA used in PgmLink and RelocResolve. Model dependend. .BottomFA D D ; Aligned file address of raw data. .TopFA D D ; File address of end of raw data. ENDSTRUC SSS ; +70h.
; SSS type specifies the kind of SSS object.
sssStructure EQU 0x0000_0001 ; Object is a structure.
sssSection EQU 0x0000_0002 ; Object is a section of segment. It may be set together with sssSegment.
sssSegment EQU 0x0000_0004 ; Object is a segment.
sssGroup EQU 0x0000_0008 ; Object is a group of segments.
sssExtern EQU 0x0000_0010 ; Object is an external pseudosegment.
sssTypeMask EQU sssStructure|sssExtern|sssGroup|sssSegment|sssSection
; Auxilliary properties of SSS object.
sssImport EQU 0x0000_0020 ; External pseudosegment belongs to an imported symbol. It may be set together with sssExtern.
sssImagePrefix EQU 0x0000_0040 ; Pseudosegment which represents PSP in COM|BOOT format (0x0100|0x7C00 bytes).
sssOrdered EQU 0x0000_0200 ; Already stored in PgmOrderSegments.
sssDontCare EQU 0x0000_0400 ; Ignore whether the segment is initialized or not.
sssNotBSS EQU 0x0000_0800 ; Emited content of the object is initialized. Reset in noninitialized sections.
; COMBINE= property of segment.
sssPublic EQU 0x0000_1000 ; Segment will be combined with other segments of the same name.
sssPrivate EQU 0x0000_2000 ; Segment must not be combined with others.
sssCommon EQU 0x0000_4000 ; Segment may be overlaped.
sssStack EQU 0x0000_8000 ; Segment may be concatenated.
sssCombineMask EQU sssPublic|sssPrivate|sssCommon|sssStack
; Attributes of external symbols postponed to link time.
sssExtAttr EQU 0x000F_0000 ; dictAttr* (0..9) <<16. Sync with relocExtAttr.
; Assembly properties.
sssImplicit EQU 0x0010_0000 ; Group/segment was created by default in PgmCreateProgram.
sssDefinedInPass EQU 0x0020_0000 ; Object was already declared in this pass. Reset in PassCreate.
sssDefinedInGroup EQU 0x0040_0000 ; Object was declared in GROUP pseudoinstruction.
sssUsed EQU 0x0080_0000 ; Structure/section/segment was referred at least once (sssDirty).
; WIDTH= property of segment. Encoding must match pgmoptWidthMask.
sssWidth16 EQU 0x0100_0000 ; Realmode 16bit program segment.
sssWidth32 EQU 0x0200_0000 ; Realmode or protected 32bit program segment.
sssWidth64 EQU 0x0400_0000 ; 64bit mode program segment.
sssWidthMask EQU sssWidth16|sssWidth32|sssWidth64 ; This must match pgmoptWidthMask.
; EQU 0x0800_0000 ; Not used.
; Linker properties.
sssMarshaled EQU 0x1000_0000 ; Section was already marshaled (combined) to its base segment.
sssCombined EQU 0x2000_0000 ; Segment|group from linked module was already combined into the main program.
sssLinked EQU 0x4000_0000 ; Segment|group bottom is fixed inside ImageBase frame.
sssResolved EQU 0x8000_0000 ; This external pseudosegment is resolved (replaced with the segment of matching public symbol).
EXPORT .. RESERVED) must match the order of indexes in
PFPE_encodings, i.e. the order
of special data directories in optional header.
%SssPurposeList %SET \ Purposes of PE-COFF special directories 1..16
EXPORT, IMPORT, RESOURCE, EXCEPTION, SECURITY, BASERELOC, DEBUG, COPYRIGHT, \
GLOBALPTR, TLS, LOAD_CONFIG, BOUND_IMPORT, IAT, DELAY_IMPORT, CLR, RESERVED, \
\ General purposes 17..23
CODE, DATA, RODATA, LITERAL, BSS, STACK, DRECTVE, \
\ ELF special section purposes 24..32
PHDR, SYMBOLS, DYNAMIC, STRINGS, RELOC, GOT, PLT, HASH, INTERP
%Value %SETA 1
PURPOSE %FOR %SssPurposeList
sssPurpose%PURPOSE EQU %Value
%Value %SETA %Value << 1 ; >>
%ENDFOR PURPOSE
sssPurposeNONE = 0x0000_0000
; Mask of all possible segment purposes:
sssPurposeAny = 0xFFFF_FFFF
; Special purposes of .DataDirectory in PE optional header:
sssPurposeOptionalMask = 0x0000_FFFF ; EXPORT,IMPORT,RESOURCE,...
; Special purposes of ELF-based formats: PHDR, SYMBOLS, DYNAMIC, STRINGS, RELOC, GOT, PLT, HASH, INTERP:
sssPurposeElfMask = 0xFF80_0000
; Special purpose of PSP in COM|BOOT format:
sssPurposePSP = sssPurposePHDR
; Regular (ordinary) purposes: CODE, RODATA, DATA, BSS, STACK:
sssPurposeRegular = sssPurposeCODE|sssPurposeDATA|sssPurposeRODATA|sssPurposeBSS|sssPurposeSTACK
; Purposes of segments with initialized contents:
sssPurposeInitMask = ~ (sssPurposeBSS|sssPurposeSTACK|sssPurposePSP)
ENDHEAD sss ; End of module interface.
sssExtern, sssGroup, sssSegment, sssSection, sssStructure
. or 0 (any type of SSS objects indulges).
sssPrivate,sssPublic,sssCommon,sssStack
or 0 (any combine mask indulges).
SssFindByName Procedure SssType, SssCombine, NamePtr, NameSize, ProgPtr
MOV EAX,[%ProgPtr]
TEST EAX
JNZ .10:
Invoke PgmGetCurrent::
JC .90:
.10:MOV EAX,[EAX+PGM.SssList]
MOV EDX,[%SssType]
MOV ECX,[%SssCombine]
ListGetFirst EAX
.30:STC
JZ .90:
TEST EDX
JZ .40: ; Do not constrain object type when none was specified.
JNSt [EAX+SSS.Status],EDX,.80: ; Skip if object type does not match.
.40:JECXZ .70: ; Do not constrain combine method when none was specified.
JNSt [EAX+SSS.Status],ECX,.80: ; Skip if combine method does not match.
.70:Compare [%NamePtr],[%NameSize],[EAX+SSS.NamePtr],[EAX+SSS.NameSize]
JE .90:
.80:ListGetNext EAX
JMP .30:
.90:MOV [%ReturnEAX],EAX
EndProcedure SssFindByName
SssFindByIndex Procedure SegmIndex, ProgPtr
XOR EAX,EAX
MOV ECX,[%SegmIndex]
STC
JECXZ .90:
MOV EAX,[%ProgPtr]
TEST EAX
JNZ .10:
Invoke PgmGetCurrent::
JC .90:
.10:MOV EAX,[EAX+PGM.SssList]
ListGetFirst EAX
.30:STC
JZ .90:
CMP [EAX+SSS.SegmIndex],ECX
JE .90:
ListGetNext EAX
JMP .30:
.90:MOV [%ReturnEAX],EAX
EndProcedure SssFindByIndex
sssOrdered flag set are ignored. Stored objects are flagged sssOrdered.sssGroup or sssSegment.
SssFindByPurpose Procedure SssType, Purpose, Initialized, GroupPtr, ProgPtr, OutBuffer
MOV ESI,[%SssType]
MOV EDX,[%Purpose]
MOV ECX,[%Initialized]
MOV EDI,[%GroupPtr]
MOV EAX,[%ProgPtr]
MOV EBX,[%OutBuffer]
BufferClear EBX
ListGetFirst [EAX+PGM.SssList]
JZ .80:
.10:JNSt [EAX+SSS.Status],ESI,.70:
JNSt [EAX+SSS.Purpose],EDX,.70:
JSt [EAX+SSS.Status],sssGroup,.30:
CMP [EAX+SSS.GroupPtr],EDI
JNE .70:
.30:JSt ECX,sssDontCare,.50: ; Use this segment.
JECXZ .40:
JNSt [EAX+SSS.Status],ECX,.70:
JMP .50
.40:JSt [EAX+SSS.Status],sssNotBSS,.70:
.50:BufferStoreDword EBX,EAX
.70:ListGetNext EAX
JNZ .10:
.80:BufferRetrieve EBX
MOV [%ReturnESI],ESI
MOV [%ReturnECX],ECX
TEST ECX ; Set ZF when no SSS object was found.
EndProcedure SssFindByPurpose
SssCheckDirty Procedure SssPtr, PgmPtr
MOV EBX,[%SssPtr]
MOV EDI,[%PgmPtr]
MOV EDX,[EBX+SSS.Status]
JSt EDX,sssStructure, .NotDirty:
JSt EDX,sssUsed, .Dirty:
JNSt EDX,sssSection,.55:
; Section is dirty if any data was defined or reserved in it.
MOV EAX,[EBX+SSS.TopLow]
OR EAX,[EBX+SSS.TopHigh]
JNZ .Dirty:
; Section is dirty if any data was emitted in it.
MOV ECX,[EBX+SSS.EmitBuffer]
JECXZ .40:
BufferRetrieve ECX
JECXZ .40: ; If no data was emitted to this section.
.Dirty:SetSt [EBX+SSS.Status],sssUsed ; Mark it as used, thus the next time SssCheckDirty will know faster.
STC
JMP .90:
.40: ; Section is dirty if symbols belongs to it.
ListGetFirst [EDI+PGM.SymList]
JZ .55: ; If no symbol was emitted to the section EBX yet.
.45: JSt [EAX+SYM.Status],symSe,.50: ; Segment symbols do not count.
MOV ECX,[EAX+SYM.Section]
JECXZ .50:
CMP ECX,EBX
JE .48:
MOV ECX,[ECX+SSS.SegmPtr]
JECXZ .50:
CMP ECX,EBX
JNE .50:
.48: JSt [EAX+SYM.Status],symDefined|symDefInPass,.Dirty: ; Forward-referenced symbols do not count.
.50: ListGetNext EAX
JNZ .45:
.55: JNSt EDX,sssSegment,.70:
; Segment is dirty if any other section belongs to it beside its base section.
ListGetFirst [EDI+PGM.SssList]
JZ .70:
.60: JNSt [EAX+SSS.Status],sssSection,.65:
CMP [EAX+SSS.SegmPtr],EBX ; Does section EAX belong to the inspected segment EBX?
JNE .65: ; Skip when it does not belong to our segment.
CMP EAX,EBX ; Is it the base section, identical with its segment?
JNE .Dirty:
.65: ListGetNext EAX
JNZ .60:
.70: JNSt EDX,sssGroup,.NotDirty:
; Group is dirty if any segment belongs to it.
ListGetFirst [EDI+PGM.SssList]
JZ .NotDirty:
.75: JNSt [EAX+SSS.Status],sssSegment,.80:
CMP [EAX+SSS.GroupPtr],EBX ; Does the segment EAX belong to the inspected group?
JNE .80:
Invoke SssCheckDirty,EAX,EDI ; Group is dirty if any of its segment is dirty.
JC .Dirty:
.80: ListGetNext EAX
JNZ .75:
.NotDirty:
CLC
.90: EndProcedure SssCheckDirty
SSS.Class and B)
SSS.Name and set the segment purpose
according to a string case-insensitively found in the names, following theese rules:
SssGuessPurpose Procedure Segment
MOV EBX,[%Segment]
MOV ESI,[EBX+SSS.ClassPtr]
MOV ECX,[EBX+SSS.ClassSize]
CALL .Guess:
MOV ESI,[EBX+SSS.NamePtr]
MOV ECX,[EBX+SSS.NameSize]
CALL .Guess:
.Guess: PROC1
; Input: ESI,ECX is the name, EBX is the segment SSS.
; Output: Guessed purpose (or 0) is written to [EBX+SSS.Purpose]. EAX,ECX,EDX,ESI,EDI clobbered.
TEST ECX
JZ .90: ; If name empty.
XOR EAX,EAX
CMP [EBX+SSS.Purpose],EAX
JNZ .90: ; If purpose was already set.
Invoke EaBufferReserve::, SssGuessPurpose
MOV EDX,EAX
BufferStore EDX,ESI,ECX
BufferRetrieve EDX
MOV EDI,ESI
.10:LODSB ; Convert the name to upper case.
CMP AL,'a'
JB .14:
CMP AL,'z'
JA .14:
SUB AL,'a'-'A'
.14:STOSB
LOOP .10:
; Search for string 'DRECTVE'.
BufferRetrieve EDX
MOV EDI,ESI ; EDI,ECX is now the uppercased name.
.18:MOV AL,'D'
REPNE SCASB
JNE .22:
CMP ECX,6
JB .22:
CMPD [EDI],'RECT'
JNE .18:
CMPW [EDI+4],'VE'
JNE .18:
MOV EAX,sssPurposeDRECTVE
JMP .86:
.22:; Search for string 'STACK'.
BufferRetrieve EDX
MOV EDI,ESI ; EDI,ECX is now the uppercased name.
.26:MOV AL,'S'
REPNE SCASB
JNE .30:
CMP ECX,4
JB .30:
CMPD [EDI],'TACK'
MOV EAX,sssPurposeSTACK ; If 'STACK' found in the name.
JE .86:
JMP .26:
.30:; Search for string 'BSS'.
BufferRetrieve EDX
MOV EDI,ESI ; EDI,ECX is now the uppercased name.
.34:MOV AL,'B'
REPNE SCASB
JNE .38:
CMP ECX,2
JB .38:
CMPW [EDI],'SS'
MOV EAX,sssPurposeBSS ; If 'BSS' found in the name.
JE .86:
JMP .34:
.38:; Search for string 'RODATA'.
BufferRetrieve EDX
MOV EDI,ESI ; EDI,ECX is now the uppercased name.
.42:MOV AL,'R'
REPNE SCASB
JNE .46:
CMP ECX,5
JB .46:
CMPD [EDI],'ODAT'
JNE .42:
CMPB [EDI+4],'A'
MOV EAX,sssPurposeRODATA ; If 'RODATA' found in the name.
JE .86:
JMP .42:
.46:; Search for string 'RDATA'.
BufferRetrieve EDX
MOV EDI,ESI ; EDI,ECX is now the uppercased name.
.48:MOV AL,'R'
REPNE SCASB
JNE .50:
CMP ECX,6
JB .50:
CMPD [EDI],'DATA'
MOV EAX,sssPurposeRODATA ; If 'RDATA' found in the name.
JE .86:
JMP .48:
.50:; Search for string 'UDATA'.
BufferRetrieve EDX
MOV EDI,ESI ; EDI,ECX is now the uppercased name.
.54:MOV AL,'U'
REPNE SCASB
JNE .58:
CMP ECX,4
JB .58:
CMPD [EDI],'DATA'
MOV EAX,sssPurposeBSS ; If 'UDATA' found in the name.
JE .86:
JMP .54:
.58:; Search for string 'DATA'.
BufferRetrieve EDX
MOV EDI,ESI ; EDI,ECX is now the uppercased name.
.62:MOV AL,'D'
REPNE SCASB
JNE .66:
CMP ECX,3
JB .66:
CMPD [EDI-1],'DATA'
MOV EAX,sssPurposeDATA ; If 'DATA' found in the name.
JE .86:
JMP .62:
.66:; Search for string 'CODE'.
BufferRetrieve EDX
MOV EDI,ESI ; EDI,ECX is now the uppercased name.
.70:MOV AL,'C'
REPNE SCASB
JNE .74:
CMP ECX,3
JB .74:
CMPD [EDI-1],'CODE'
MOV EAX,sssPurposeCODE ; If 'CODE' found in the name.
JE .86:
JMP .70:
.74:; Search for string 'TEXT'.
BufferRetrieve EDX
MOV EDI,ESI ; EDI,ECX is now the uppercased name.
.78:MOV AL,'T'
REPNE SCASB
JNE .82:
CMP ECX,3
JB .82:
CMPD [EDI-1],'TEXT'
MOV EAX,sssPurposeCODE ; If 'TEXT' found in the name.
JE .86:
JMP .78:
.82:XOR EAX,EAX ; No known purpose guessed.
.86:SetSt [EBX+SSS.Purpose],EAX
Invoke EaBufferRelease::,EDX
.90:RET
ENDP1 .Guess:
CMPD [EBX+SSS.Purpose],0 ; Return ZF=1 if purpose was not guessed.
EndProcedure SssGuessPurpose
SssGetSegm Procedure PgmPtr, SegmentPurpose
MOV EDX,[%SegmentPurpose]
MOV ECX,[%PgmPtr]
SUB EAX,EAX
JECXZ .80:
ListGetFirst [ECX+PGM.SssList]
JZ .80:
.20: JNSt [EAX+SSS.Status],sssSegment,.40: ; Skip sections and structures.
JSt [EAX+SSS.Status],sssUsed,.30:
JSt [EAX+SSS.Status],sssImplicit,.40: ; In the first pass avoid default segments.
.30: JSt [EAX+SSS.Purpose],EDX, .90: ; If found used segment with purpose EDX.
.40: ListGetNext EAX
JNZ .20:
ListGetFirst [ECX+PGM.SssList] ; Second pass of segment list.
.60: JNSt [EAX+SSS.Status],sssSegment,.70: ; Skip sections and structures.
JSt [EAX+SSS.Purpose],EDX, .90: ; If found a segment with purpose EDX.
.70: ListGetNext EAX
JNZ .60:
.80: STC ; Not found.
.90: MOV [%ReturnEAX],EAX
EndProcedure SssGetSegm
SssCheckPurpose Procedure SectPtr, Purpose
MOV EDX,[%Purpose]
MOV EBX,[%SectPtr]
TEST EBX
JZ .10:
TEST [EBX+SSS.Purpose],EDX
JNZ .90: ; If purpose matches, do nothing.
JSt EDX,sssPurposeBSS,.90: ; Uninitialized data may be emitted anywhere, do nothing.
JNSt [EBX+SSS.Purpose],sssPurposeBSS,.90:
.10: MOV EAX,'3206'
JSt EDX,sssPurposeCODE,.20:
MOV EAX,'3205'
.20: Msg PgmStatus=pgmLastPass,EAX,EBX ; '3205 Emitting data to section [!1S] with PURPOSE=BSS.
STC ; '3206 Emitting code to section [!1S] with PURPOSE=BSS.
.90:EndProcedure SssCheckPurpose
@LT1 @LT2 @LT4 @LT8 @LT16 @LT32 @LT64
. It will be created when didn't exist yet. The segment to which the literal section belongs is selected with these rules:
@LT will be created with purpose RODATA+LITERAL.
SssCreate@LT Procedure DataType, Stm
SssName LocalVar Size=8 ; Room for string "@LT"+decimal number.
SUB ECX,ECX ; Initialization of %SssName.
LEA EDI,[%SssName]
MOV EAX,"@LTx"
STOSD
XOR EAX,EAX
STOSD
MOV [%ReturnEAX],EAX
Invoke ExpWidthOfDataType::,[%DataType] ; Find section alignment and name.
CMP CL,10 ; Aligment requested by the datatype.
JNE .10:
MOV CL,8 ; Alignment of TBYTE is 8.
.10: OR EAX,ECX
JNZ .15:
Msg '6676',[%DataType] ; Wrong literal type "!1Z".
STC
JMP .90:
.15: LEA ESI,[%SssName]
MOV EAX,ECX
MOV EBX,ECX ; Section alignment (1,2,4,8,16,32,64).
LEA EDI,[ESI+3] ; Construct literal section name.
StoD EDI
SUB EDI,ESI
MOV ECX,EDI ; Section name size.
Invoke SssFindByName,sssSection,0,ESI,ECX,0 ; Try to find [@LT*] section by name.
MOV [%ReturnEAX],EAX
JC .25: ; Skip when [@LT*] section does not exist yet.
MOV ECX,[EAX+SSS.SegmPtr]
SetSt [EAX+SSS.Status],sssUsed
SetSt [EAX+SSS.Purpose],sssPurposeLITERAL
JECXZ .20:
SetSt [ECX+SSS.Status],sssUsed
.20:JMP .90: ; Segment or section @LT* was found.
.25: ; New literal section will be created. ESI,ECX=Name, EBX=alignment (1,2,4,8,16,32,64).
MOV EDI,[%Stm]
MOV EDX,[EDI+STM.Program]
ListGetLast [EDX+PGM.SssList] ; 1.pass: find the last segment with purpose LITERAL & (DATA|RODATA).
JZ .70:
.30:JNSt [EAX+SSS.Status],sssSegment,.35:
JNSt [EAX+SSS.Purpose],sssPurposeLITERAL,.35:
JSt [EAX+SSS.Purpose],sssPurposeRODATA|sssPurposeDATA,.80:
.35:ListGetPrev EAX
JNZ .30:
ListGetLast [EDX+PGM.SssList] ; 2.pass: find the last RODATA segment.
.40:JNSt [EAX+SSS.Status],sssSegment,.45:
JSt [EAX+SSS.Purpose],sssPurposeRODATA,.80:
.45:ListGetPrev EAX
JNZ .40:
ListGetLast [EDX+PGM.SssList] ; 3.pass: find the last DATA segment.
.50:JNSt [EAX+SSS.Status],sssSegment,.60:
JSt [EAX+SSS.Purpose],sssPurposeDATA,.80:
.60:ListGetPrev EAX
JNZ .50:
.70: ; No data segment found, we have to create one. Its name will be [@LT].
MOV EAX,pgmoptWidthMask
AND EAX,[EDX+PGM.Pgmopt.Status] ; Section width will be taken from program width.
OR EAX,sssSegment+sssPrivate+sssNotBSS+sssImplicit+sssUsed
LEA EDI,[%SssName]
Invoke SssCreateSe,[%Stm],0,EDI,3,EAX,sssPurposeRODATA+sssPurposeLITERAL,64
.80:MOV EDI,EAX ; Data segment where our literal section will belong to.
SetSt [EDI+SSS.Status],sssUsed
SetSt [EDI+SSS.Purpose],sssPurposeLITERAL ; Next time prefer this very segment for other literals.
; New literal section will be created in data segment EDI. ESI,ECX=Name, EDX=PGM, EBX=alignment (1,2,4,8,16,32,64).
MOV EAX,[EDI+SSS.Status]
AND EAX,sssCombineMask|sssWidthMask ; Properties inherited from segment EDI.
OR EAX,sssSection+sssNotBSS+sssImplicit+sssUsed
Invoke SssCreateSe,[%Stm],EDI,ESI,ECX,EAX,sssPurposeRODATA+sssPurposeLITERAL,EBX
MOV [%ReturnEAX],EAX
.90:EndProcedure SssCreate@LT
[@RT0], [@RT1], [@RT2] etc
and it belongs to the last code segment declared in current program which has
PUPRPOSE=CODE+LITERALS set, or if none such exists, to the last code segment.
[@RT0] hosts code literals defined with pseudoinstruction DI, e.g.
LEA ESI,[=8*I"STOSD"], or with INSTR literal, e.g. CALL =I"RET".
[@RT1] hosts PROC1/ENDPROC1 blocks and
=I"machine instruction" code-literal data.
[@RT2], [@RT3] etc.
@RT will be created first..Name and .Purpose will be inspected
to choose the name of created/reused @RTx section.
When PreviousSect is NULL, procedure returns pointer to section [@RT0].
=INSTR literal.
SssCreate@RT Procedure PreviousSect, Stm
SssName LocalVar Size=8 ; Room for string "@RT"+decimal number.
LEA EDI,[%SssName] ; Initialization os %SssName.
MOVD [EDI],"@RT0"
MOV EDX,4
MOV EBX,[%PreviousSect]
TEST EBX
JZ .10:
MOVD [EDI],"@RT1"
JNSt [EBX+SSS.Purpose],sssPurposeLITERAL,.10: ; If we weren't in literal section already.
MOV ECX,[EBX+SSS.NameSize]
MOV ESI,[EBX+SSS.NamePtr]
CMP ECX,4
JB .10: ; Name too short, it cannot be [@RTx].
Compare ESI,3,EDI
JNE .10: ; Skip when PreviousSect does not start with "@RT".
SUB ECX,3 ; Previous (current) section already is runtime, select the next one.
ADD ESI,3 ; Skip "@RT" in section name.
LodD ESI,Size=ECX
INC EAX
MOV EDX,EDI ; Temporary save pointer to the new @RT* name.
ADD EDI,3
StoD EDI,Size=5,Align=left ; Overwrite @RT number.
SUB EDI,EDX
XCHG EDI,EDX
.10: ; EDI,EDX is now required section name, in most cases "@RT0" or "@RT1".
Invoke SssFindByName,sssSection,0,EDI,EDX,0 ; Try to find section by name.
MOV [%ReturnEAX],EAX
JC .30: ; Skip when [@RT*] section does not exist yet.
MOV ECX,[EAX+SSS.SegmPtr]
SetSt [EAX+SSS.Status],sssUsed
SetSt [EAX+SSS.Purpose],sssPurposeLITERAL
JECXZ .20:
SetSt [ECX+SSS.Status],sssUsed
.20: JMP .90: ; Section @RT* was found, it will be reused as is.
.30: ; New code-literal section will be created in code segment. EDI,EDX=section name.
MOV EBX,[%Stm]
MOV ECX,[EBX+STM.Program]
; Find the last segment with code purpose where the new runtime section will belong to.
; First prefer segment with purpose LITERALS, if any.
ListGetLast [ECX+PGM.SssList] ; 1.pass: find the last segment with purpose CODE & LITERAL.
JZ .70:
.40: JNSt [EAX+SSS.Status],sssSegment,.45:
JNSt [EAX+SSS.Purpose],sssPurposeLITERAL,.45: ; First prefer segment with PURPOSE=LITERALS.
JSt [EAX+SSS.Purpose],sssPurposeCODE,.80: ; If code segment found.
.45: ListGetPrev EAX
JNZ .40:
.50: ListGetLast [ECX+PGM.SssList] ; 2.pass: find the last segment with purpose CODE.
.55: JNSt [EAX+SSS.Status],sssSegment,.60:
JSt [EAX+SSS.Purpose],sssPurposeCODE,.80: ; If code segment found.
.60: ListGetPrev EAX
JNZ .55:
.70: ; No code segment found, we have to create one. Its name will be [@RT].
MOV EAX,pgmoptWidthMask
AND EAX,[ECX+PGM.Pgmopt.Status] ; Segment width will be taken from program width.
OR EAX,sssSegment+sssPrivate+sssNotBSS+sssImplicit+sssUsed
Invoke SssCreateSe,[%Stm],0,EDI,3,EAX,sssPurposeCODE+sssPurposeLITERAL,1
.80: MOV EBX,EAX ; Code segment where our runtime section will belong to.
SetSt [EBX+SSS.Status],sssUsed
SetSt [EBX+SSS.Purpose],sssPurposeLITERAL ; Next time prefer this very segment for other literals.
; New runtime literal section will be created in code segment EBX. EDI,EDX=Name, ECX=PGM.
MOV EAX,[EBX+SSS.Status]
AND EAX,sssCombineMask|sssWidthMask ; Properties inherited from the segment EBX.
OR EAX,sssSection+sssNotBSS+sssImplicit+sssUsed
Invoke SssCreateSe,[%Stm],EBX,EDI,EDX,EAX,sssPurposeCODE+sssPurposeLITERAL,1
MOV [%ReturnEAX],EAX
.90:EndProcedure SssCreate@RT
CODE+DATA+LITERALS.
SssPurposeToText Procedure sssPurpose, Buffer
SUB ECX,ECX ; ECX specifies whether to use separator +.
MOV EBX,[%Buffer]
MOV EDX,[%sssPurpose]
PURPOSE %FOR %SssPurposeList
JNSt EDX,sssPurpose%PURPOSE,.Not%PURPOSE:
BufferStore EBX,=B'+',ECX ; ECX is either 0 or 1.
MOV EDI,Dict_Purpose%PURPOSE::
BufferStore EBX,[EDI+0],[EDI+4]
MOV CL,1
.Not%PURPOSE:
%ENDFOR PURPOSE
BufferStoreByte EBX,0 ; Terminating zero.
EndProcedure SssPurposeToText
SssCreateExtern Procedure SymbolPtr, ProgramPtr
MOV EBX,[%SymbolPtr]
JNSt [EBX+SYM.Status],symExtern|symImport,.90:
MOV EDX,[%ProgramPtr]
Invoke SssFindByName,sssExtern,0,[EBX+SYM.NamePtr],[EBX+SYM.NameSize],EDX
JNC .50:
; A new extern pseudosection will be created.
ListNew [EDX+PGM.SssList],Zeroed=yes
JC .90:
.50:MOV EDI,EAX ; ^SSS.
SetSt [EDI+SSS.Status],sssExtern+sssCommon
JNSt [EBX+SYM.Status],symImport,.60:
SetSt [EDI+SSS.Status],sssImport
.60:MOVB [EBX+SYM.Status],'A'
MOV [EBX+SYM.Section],EDI
MOV [EDI+SSS.SegmPtr],EDI ; Extern pseudosegment is its own segment.
MOV [EDI+SSS.SymPtr],EBX ; Reference to the external/imported symbol from its pseudosegment.
MOV ESI,[EBX+SYM.NamePtr]
MOV ECX,[EBX+SYM.NameSize]
MOV EDX,[EBX+SYM.LinePtr]
MOV [EDI+SSS.NamePtr],ESI
MOV [EDI+SSS.NameSize],ECX
CMPD [EDI+SSS.LinePtr],0
JNZ .90:
MOV [EDI+SSS.LinePtr],EDX
SUB ECX,ECX
RstSt [EBX+SYM.Status],symEstimated
MOV [EBX+SYM.OffsetLow],ECX
MOV [EBX+SYM.OffsetHigh],ECX
.90:EndProcedure SssCreateExtern
SssGetCoffCharacteristics Procedure Segment
MOV EBX,[%Segment]
MOV ESI,[EBX+SSS.Alignment]
BSF EDX,ESI ; EDX is now 0=B, 1=W, 2=D, 3=Q, 4=O, .. 13=8192.
CMP DL,13
JBE .80:
MOV DL,4
.80:INC EDX
SHL EDX,20 ; Conversion to SectionHeader.Characteristics alignment.
MOV ESI,[EBX+SSS.Purpose]
PURPOSE %FOR %SssPurposeList
JNSt ESI,sssPurpose%PURPOSE, .Not%PURPOSE
PUSHD .Not%PURPOSE: ; Prepare return address from subprocedure .%PURPOSE.
JMP .%PURPOSE:
.Not%PURPOSE:
%ENDFOR PURPOSE
JMP .90:
.CODE:
OR EDX,pfcoffSCN_CNT_CODE+pfcoffSCN_MEM_EXECUTE+pfcoffSCN_MEM_READ
RET
.DATA:
.DYNAMIC:
.GOT:
OR EDX,pfcoffSCN_CNT_INITIALIZED_DATA+pfcoffSCN_MEM_WRITE+pfcoffSCN_MEM_READ
RET
.BSS:
.STACK:
OR EDX,pfcoffSCN_CNT_UNINITIALIZED_DATA+pfcoffSCN_MEM_WRITE+pfcoffSCN_MEM_READ
RET
.DEBUG:
.BASERELOC:
OR EDX,pfcoffSCN_MEM_DISCARDABLE+pfcoffSCN_CNT_INITIALIZED_DATA+pfcoffSCN_MEM_SHARED+pfcoffSCN_MEM_READ
RET
.LITERAL:
.RESERVED:
.PHDR:
.SYMBOLS:
.STRINGS:
.RELOC:
.INTERP:
RET
.DRECTVE:
OR EDX,pfcoffSCN_MEM_DISCARDABLE+pfcoffSCN_LNK_INFO+pfcoffSCN_LNK_REMOVE
RET
.IMPORT:
.PLT:
OR EDX,pfcoffSCN_CNT_CODE+pfcoffSCN_CNT_INITIALIZED_DATA+pfcoffSCN_MEM_SHARED+pfcoffSCN_MEM_EXECUTE+pfcoffSCN_MEM_READ
RET
.IAT:
OR EDX,pfcoffSCN_CNT_INITIALIZED_DATA+pfcoffSCN_MEM_SHARED+pfcoffSCN_MEM_READ
RET
.GLOBALPTR:
OR EDX,pfcoffSCN_GPREL
RET
.RESOURCE:
OR EDX,pfcoffSCN_MEM_DISCARDABLE+pfcoffSCN_MEM_SHARED
.RODATA:
.HASH:
OR EDX,pfcoffSCN_CNT_INITIALIZED_DATA+pfcoffSCN_MEM_READ
RET
.EXPORT:
.EXCEPTION:
.SECURITY:
.COPYRIGHT:
.TLS:
.LOAD_CONFIG:
.BOUND_IMPORT:
.DELAY_IMPORT:
.CLR:
OR EDX,pfcoffSCN_MEM_READ
RET
.90:MOV [%ReturnEAX],EDX
EndProcedure SssGetCoffCharacteristics
Group.Bottom will be the lowest bottom and Group.Top
the highest top of all its segments.
Group.SVA will be the lowest segment virtual address of all its segments, and
Group.BottomFA will be the lowest file bottom and Group.TopFA
the highest file top of all its segments.
SSS.Status:sssNotBSS flag.
It is set when at least one its segment has initialized contents.
SssResizeGroup Procedure Group, Program
MOV EBX,[%Program]
MOV EDI,[%Group]
XOR EAX,EAX ; Initialize group bounderies to temporary values .Bottom=-1, .Top=0.
MOV [EDI+SSS.TopFA],EAX
MOV [EDI+SSS.TopLow],EAX
MOV [EDI+SSS.TopHigh],EAX
NOT EAX
MOV [EDI+SSS.SVA],EAX
MOV [EDI+SSS.BottomFA],EAX
MOV [EDI+SSS.BottomLow],EAX
MOV [EDI+SSS.BottomHigh],EAX
RstSt [EDI+SSS.Status],sssNotBSS
SetSt [EDI+SSS.Status],sssLinked ; Assume that all segments of group EDI are already linked.
ListGetFirst [EBX+PGM.SssList] ; Walk through all segments.
.10:MOV ECX,EAX
JNSt [ECX+SSS.Status],sssSegment,.80:
MOV EAX,[ECX+SSS.GroupPtr]
CMP EDI,EAX ; Does the segment ECX belong to group EDI?
JE .20:
TEST EAX
JZ .80: ; Ignore ungrouped segments.
MOV EAX,[EAX+SSS.GroupPtr] ; EAX might be a group from linked module, refering to a group in base program.
CMP EDI,EAX ; Does the segment ECX belong to group EDI?
JNE .80:
.20:; ECX is a segment of group EDI. Let's adjust purpose, status and group's .Bottom and .Top.
JNSt [ECX+SSS.Status],sssNotBSS,.30: ; Group is sssNotBSS if any of segment is sssNotBSS.
SetSt [EDI+SSS.Status],sssNotBSS
.30:JSt [ECX+SSS.Status],sssLinked,.35: ; Group is sssLinked if all its segments are sssLinked.
RstSt [EDI+SSS.Status],sssLinked
.35:MOV EAX,[ECX+SSS.Purpose]
OR [EDI+SSS.Purpose],EAX
MOV EDX,[ECX+SSS.BottomHigh]
MOV EAX,[ECX+SSS.BottomLow]
CMP EDX,[EDI+SSS.BottomHigh]
JA .50:
JB .40:
CMP EAX,[EDI+SSS.BottomLow]
JAE .50:
.40:MOV [EDI+SSS.BottomLow],EAX
MOV [EDI+SSS.BottomHigh],EDX ; Group RVA of its bottom was updated.
.50:MOV EAX,[ECX+SSS.BottomFA]
CMP EAX,[EDI+SSS.BottomFA]
JAE .55:
MOV [EDI+SSS.BottomFA],EAX ; Group FA of its bottom was updated.
.55:MOV EAX,[ECX+SSS.SVA]
CMP EAX,[EDI+SSS.SVA]
JAE .60:
MOV [EDI+SSS.SVA],EAX ; Group SVA was updated.
.60:MOV EDX,[ECX+SSS.TopHigh]
MOV EAX,[ECX+SSS.TopLow]
CMP EDX,[EDI+SSS.TopHigh]
JB .70:
JA .65:
CMP EAX,[EDI+SSS.TopLow]
JBE .70:
.65:MOV [EDI+SSS.TopLow],EAX
MOV [EDI+SSS.TopHigh],EDX ; Group RVA of its top was updated.
.70:MOV EAX,[ECX+SSS.TopFA]
CMP EAX,[EDI+SSS.TopFA]
JBE .80:
MOV [EDI+SSS.TopFA],EAX ; Group FA of its top was updated.
.80:ListGetNext ECX ; The next segment.
JNZ .10:
XOR EAX,EAX ; If no segment of the group EDI was found, set group .Bottom and .Top back to 0.
NOT EAX
CMP EAX,[EDI+SSS.BottomLow]
JNE .90:
CMP EAX,[EDI+SSS.BottomHigh]
JNE .90:
XOR EAX,EAX ; If no segment of the group EDI was found, revert group .Bottom and .Top back to 0.
MOV [EDI+SSS.BottomLow],EAX
MOV [EDI+SSS.BottomHigh],EAX
MOV [EDI+SSS.BottomFA],EAX
MOV [EDI+SSS.SVA],EAX
.90:EndProcedure SssResizeGroup
SssCombine is invoked when BasePgm.ModulePgmList contains one or more modules
which were loaded into the BasePgm source due to pseudoinstruction LINK.
Groups and external pseudosegments from those modules
are copied (merged) into the BasePgm, their COMBINE= method is ignored.
When the combined SssObject is a public segment, this procedure will combine it
with the homonymous segment if it exists in the BasePgm.
Emited contents of combined public segment is appended to the homonymous object in BasePgm and its
SSS.Bottom is elevated. Relocations are just copied verbatim but not fixed yet.
Segments with nonpublic COMBINE property are combined according to the table below.
When a common segment has COMBINE=COMMON, it is similary related to the homonymous common segment in BasePgm but its
SSS.Bottom is not elevated and it's left at 0.
SSS.Top is set to the greatest of all common homonymous segments.
Private segments are copied from modules to the BasePgm.
SSS.Status of all combined objects is marked as sssCombined.
SSS.SegmPtr of the combined segment in modules is updated to point at the target segment in BasePgm.
Implicit symbols (symSe associated with each segment and group) of combined module are also combined
to the BasePgm and SYM.SymbPtr points to their counterpart in BasePgm.
| SSS object types | sssExtern | sssGroup | sssSegment +sssPublic |
sssSegment +sssStack | sssSegment +sssCommon | sssSegment +sssPrivate |
|---|---|---|---|---|---|---|
| sssExtern | COMMON | PRIVATE | PRIVATE | PRIVATE | PRIVATE | PRIVATE |
| sssGroup | PRIVATE | COMMON | PRIVATE | PRIVATE | PRIVATE | PRIVATE |
| sssSegment+sssPublic | PRIVATE | PRIVATE | PUBLIC | PUBLIC | PUBLIC | PRIVATE |
| sssSegment+sssStack | PRIVATE | PRIVATE | PUBLIC | PUBLIC | PUBLIC | PRIVATE |
| sssSegment+sssCommon | PRIVATE | PRIVATE | PUBLIC | PUBLIC | COMMON | PRIVATE |
| sssSegment+sssPrivate | PRIVATE | PRIVATE | PRIVATE | PRIVATE | PRIVATE | PRIVATE |
SssCombine Procedure SssObject, BasePgm
AlignStuff LocalVar ; Alignment stuff 0x00 or 0x90 between publicly combined segments.
MOV ESI,[%SssObject]
MOV EBX,[%BasePgm]
XOR EDI,EDI
MOV EAX,[ESI+SSS.Status]
JSt EAX,sssCombined,.90: ; When the object is already combined, do nothing.
SetSt [ESI+SSS.Status],sssCombined
AND EAX,sssSegment|sssExtern|sssGroup
Dispatch EAX,sssSegment,sssExtern,sssGroup
JMP .90: ; Ignore other SSS types (actually no section or structure can pass thru PgmLoad).
.sssGroup: ; Homonymous groups always merge as COMMON. They have no emitted and reloc contents.
;; JSt [EBX+PGM.Pgmopt.Status],pgmoptDynamic,.90: ; Do not combine groups from ELFSO modules.
Invoke SssFindByName,sssGroup,0,[ESI+SSS.NamePtr],[ESI+SSS.NameSize],EBX ; Search for the same group in BasePgm.
JNC .10:
ListStore [EBX+PGM.SssList],ESI ; Group ESI defined in linked module wasn't found in BasePgm EBX. Copy it there.
.10:MOV [ESI+SSS.GroupPtr],EAX ; Group EAX was found in BasePgm.
MOV [ESI+SSS.SegmPtr],EAX ; Combine abandoned group ESI to the group EAX.
JMP .90:
.sssExtern: ; Homonymous extern pseudosegments always merge as COMMON. They have no emitted and reloc contents.
Invoke SssFindByName,sssExtern,0,[ESI+SSS.NamePtr],[ESI+SSS.NameSize],EBX ; Search for the same extern pseudosegment in BasePgm.
JNC .20:
ListStore [EBX+PGM.SssList],ESI ; sssExtern ESI defined in linked module wasn't found in BasePgm EBX. Copy it there.
.20:MOV [ESI+SSS.SegmPtr],EAX
JMP .90:
.sssSegment: ; Homonymous segments combine according to their COMBINE= property.
MOV EAX,sssCombineMask
AND EAX,[ESI+SSS.Status]
Dispatch EAX,sssPublic,sssCommon,sssStack
JMP .PRIVATE: ; Private segment does not combine.
.sssCommon: ; Segment ESI is COMBINE=COMMON.
Invoke SssFindByName,sssSegment,sssCommon,[ESI+SSS.NamePtr],[ESI+SSS.NameSize],EBX
JNC .COMMON: ; Only if both segments have COMBINE=COMMON.
; Homonymous COMMON segment was not found, try PUBLIC or STACK.
Invoke SssFindByName,sssSegment,sssPublic|sssStack,[ESI+SSS.NamePtr],[ESI+SSS.NameSize],EBX
JNC .PUBLIC:
JMP .PRIVATE:
.sssStack: ; Segment ESI is COMBINE=STACK.
Invoke SssFindByName,sssSegment,sssStack|sssPublic|sssCommon,[ESI+SSS.NamePtr],[ESI+SSS.NameSize],EBX
JNC .PUBLIC: ; Segments with stack-combine method link as public (they are concatenated).
JMP .PRIVATE:
.sssPublic: ; Segment ESI is COMBINE=PUBLIC.
Invoke SssFindByName,sssSegment,sssPublic,[ESI+SSS.NamePtr],[ESI+SSS.NameSize],EBX
JNC .PUBLIC: ; Only if both segments have COMBINE=PUBLIC.
; Homonymous PUBLIC segment was not found, try COMMON or STACK.
.75:Invoke SssFindByName,sssSegment,sssCommon|sssStack,[ESI+SSS.NamePtr],[ESI+SSS.NameSize],EBX
JNC .PUBLIC:
;JMP .PRIVATE:
; The actual combine method procedures are defined here.
; EBX=BasePgm, ESI=linked segment, EAX=homonymous segment in BasePgm or 0 when not found.
.PRIVATE: ; PRIVATE combining method. Combined segment ESI is copied to the BasePgm EBX. Error if a homonymous segment already exists there.
Invoke SssFindByName,sssSegment,0,[ESI+SSS.NamePtr],[ESI+SSS.NameSize],EBX
JNC .E7862: ; Linked private segment [!1S] is already declared in program "!2S".
ListStore [EBX+PGM.SssList],ESI
MOV EDI,EAX ; EDI is the copy of segment in BasePgm.
MOV [ESI+SSS.SegmPtr],EDI
MOV EDX,[ESI+SSS.SymPtr]
TEST EDX
JZ .90:
ListStore [EBX+PGM.SymList],EDX
MOV [EAX+SYM.Section],EDI ; EAX is the copy of symSe symbol in BasePgm.
MOV [EDI+SSS.SymPtr],EAX
TEST EDX
JZ .90:
MOV [EDX+SYM.SymbPtr],EAX ; EDX is the abandoned symSe in loaded module.
JMP .90:
.E7862: Msg '7862',ESI,EBX ; Linked private segment [!1S] is already declared in program "!2S".
JMP .90:
.COMMON: ; COMMON combining method. Both combined (ESI) and base (EAX) segments remain on common bottom=0.
MOV EDI,EAX ; Homonymous segment which was found in BasePgm.
CMPD [EDI+SSS.GroupPtr],0
JNZ .C1: ; Keep the existing group of base segment.
MOV EAX,[ESI+SSS.GroupPtr] ; When nongrouped, assign it to the group of combined segment.
MOV [EDI+SSS.GroupPtr],EAX
.C1:MOV [ESI+SSS.SegmPtr],EDI ; Let the abandoned combined segment ESI refer to the base segment EDI.
MOV EDX,[ESI+SSS.TopHigh] ; New base segment's virtual size will be set to the greater of both.
MOV EAX,[ESI+SSS.TopLow]
CMP EDX,[EDI+SSS.TopHigh]
JB .C3:
JA .C2:
CMP EAX,[EDI+SSS.TopLow]
JBE .C3:
.C2:MOV [EDI+SSS.TopLow],EAX
MOV [EDI+SSS.TopHigh],EDX
.C3:JMP .90:
.E8525:Msg '8525',[%SssObject] ; Size of segment [!1S] exceeded 4 GB.
JMP .90:
.PUBLIC: ; PUBLIC combining method of combined (ESI) and base (EAX) segment.
MOV EBX,EAX ; EBX is now the homonymous segment in BasePgm.
MOV EDI,ESI ; EDI is now the combined (linked) segment.
SetSt [EDI+SSS.Status],sssCombined
MOV [EDI+SSS.SegmPtr],EBX ; Let the combined segment EDI refer to the existing base segment EBX.
CMPD [EBX+SSS.GroupPtr],0
JNZ .P0:
MOV EAX,[EDI+SSS.GroupPtr] ; Copy group assignment from combined segment EDI to base segment EBX.
MOV [EBX+SSS.GroupPtr],EAX
.P0:MOV EAX,[EDI+SSS.Purpose] ; Merge properties of public segment combination.
OR [EBX+SSS.Purpose],EAX
XOR ECX,ECX ; Prepare alignment stuff in CL (0x00 or 0x90).
JNSt EAX,sssPurposeCODE,.P1:
MOV CL,0x90
.P1:MOV [%AlignStuff],ECX
MOV EAX,sssNotBSS
AND EAX,[EDI+SSS.Status]
OR [EBX+SSS.Status],EAX ; Merge initialized-data status.
MOV EAX,[EDI+SSS.Alignment]
CMP EAX,[EBX+SSS.Alignment]
JNA .P2:
MOV [EBX+SSS.Alignment],EAX ; Use the greater alignment from both segments, if they are different.
.P2:JNSt [EDI+SSS.Purpose],sssPurposeRegular,.P4: ; Width of literal|PE-special purpose segments is not checked.
MOV EAX,sssWidthMask ; Width of segments with standard purpose CODE|RODATA|DATA|BSS|STACK must match.
MOV EDX,EAX
AND EAX,[EDI+SSS.Status] ; Width of linked segment (sssWidth16|32|64).
JZ .P4: ; Segment from the combined segment may have unspecified width, this is OK.
AND EDX,[EBX+SSS.Status] ; Width of base segment.
JNZ .P3:
MOV EDX,EAX ; Base segment may have been created by PseudoGROUP with unspecified width, this is OK.
OR [EBX+SSS.Status],EDX
.P3:CMP EAX,EDX ; Check segment width match.
JE .P4:
Msg '7718',EDI ; Cannot combine segments [!1S] which have different width.
JMP .90:
.P4:MOV EAX,[EBX+SSS.TopLow] ; EAX=virtual unaligned size of base segment.
Invoke ExpAlign::,EAX,[EDI+SSS.Alignment],0 ; Align base top according to the alignment of combined segment.
ADD EAX,ECX ; Add alignment stuff size to the base top. EAX is now aligned bottom of linked segment EDI, i.e. its elevation.
JC .E8525: ; Size of segment [!1S] exceeded 4 GB.
JNSt [EBX+SSS.Status],sssNotBSS,.P5: ; Skip when uninitialized segments are combined.
BufferRetrieve [EBX+SSS.EmitBuffer] ; Set the emitted size of base segment equal to its virtual size EAX.
SUB ECX,EAX ; Align emitted contents of base segment EBX.
NEG ECX ; ECX is now the nonnegative size of alignment stuff.
BufferResize [EBX+SSS.EmitBuffer],ECX,Stuff=[%AlignStuff] ; EAX is the size of base segment EBX, also the new bottom of combined segment EDI.
.P5: ; VA of segments EBX and EDI was 0 but VA of the appended segment EDI will be elevated by EAX (aligned size of segment EBX).
ADD [EDI+SSS.BottomLow],EAX ; Elevate VA of appended segment EDI. It will be used later to update symbols and relocations.
ADD [EDI+SSS.TopLow],EAX
JC .E8525: ; Size of segment [!1S] exceeded 4 GB.
MOV EAX,[EDI+SSS.TopLow]
MOV EDX,[EDI+SSS.TopHigh]
MOV [EBX+SSS.TopLow],EAX ; Update the top of base segment, which was appended with combined segment.
MOV [EBX+SSS.TopHigh],EDX
MOV ECX,[EDI+SSS.SymPtr]
JECXZ .82:
ADD [ECX+SYM.OffsetLow],EAX ; Update symSe associated with combined segment EDI.
.82:JNSt [EBX+SSS.Status],sssNotBSS,.90: ; There is no emitted contents and no relocations in BSS segment.
BufferRetrieve [EDI+SSS.EmitBuffer] ; Copy the emitted contents of combined segment EDI to the aligned base segment EBX.
BufferStore [EBX+SSS.EmitBuffer],ESI,ECX
BufferRetrieve [EDI+SSS.RelocBuffer] ; Copy relocations of combined segment EDI unchanged to the base segment EBX.
BufferStore [EBX+SSS.RelocBuffer],ESI,ECX ; They will be elevated later in RelocCombine.
.90:EndProcedure SssCombine
SssEmitAlignment Procedure Section, HowManyBytes, OutBuffer
MOV ECX,[%HowManyBytes]
MOV EDI,[%Section]
TEST ECX
JZ .90:
MOV EBX,[%OutBuffer]
JSt [EDI+SSS.Purpose],sssPurposeCODE,.60:
; Alignment stuff in noncode section is 0x00.
BufferResize EBX,ECX,Stuff=0x00
JMP .90:
.60:; Alignment stuff in code section.
MOV EDX,9
JECXZ .90:
SUB ECX,EDX
JB .80:
PUSH ECX
CALL .Nop:
POP ECX
JC .90:
JMP .60:
.80:ADD EDX,ECX
CALL .Nop:
.90:EndProcedure SssEmitAlignment
SssEmitAlignment.Nop: PROC ; Emit EDX bytes of NOP1..NOP9 to the buffer EBX. EDI=^SSS.
[.data] ; The following table defines offsets of NOP opcode in the string .N:
;NOPx 1 2 3 4 5 6 7 8 9
.16b086:DB 75, 76, 75, 76, 75, 76, 75, 76, 75
.16b686:DB 33, 32, 73, 28, 23, 67, 66, 9, 57
.32b386:DB 33, 32, 54, 50, 49, 43, 36, 35, 34
.32b686:DB 33, 32, 29, 24, 18, 17, 10, 1, 0
;64bX64 EQU 032b686
; 0 1 2 3 4 5 6 7 8 9
.N:DB 0x66,0x0F,0x1F,0x84,0x20,0x00,0x00,0x00,0x00,0x67,\
\ 10 11 12 13 14 15 16 17 18 19
0x0F,0x1F,0x80,0x00,0x00,0x00,0x00,0x66,0x0F,0x1F,\
\ 20 21 22 23 24 25 26 27 28 29
0x44,0x20,0x00,0x67,0x0F,0x1F,0x40,0x00,0x67,0x0F,\
\ 30 31 32 33 34 35 36 37 38 39
0x1F,0x00,0x66,0x90,0x66,0x3E,0x8D,0x84,0x20,0x00,\
\ 40 41 42 43 44 45 46 47 48 49
0x00,0x00,0x00,0x8D,0x80,0x00,0x00,0x00,0x00,0x3E,\
\ 50 51 52 53 54 55 56 57 58 59
0x8D,0x44,0x20,0x00,0x8D,0x40,0x00,0x67,0x0F,0x1F,\
\ 60 61 62 63 64 65 66 67 68 69
0x84,0x20,0x00,0x00,0x00,0x00,0x66,0x67,0x0F,0x1F,\
\ 70 71 72 73 74 75 76 77 78 79
0x44,0x20,0x00,0x66,0x67,0x90,0x87,0xC9,0x87,0xD2,\
\ 80 81 82 83
0x87,0xDB,0x87,0xE4
[.text]
JNSt [Ea.Eaopt.Machine::],iiCPU_686, .20:
; Machine supports real NOP.
MOV ESI, .32b686:
JSt [EDI+SSS.Status],sssWidth32 | sssWidth64, .50:
MOV ESI, .16b686:
JMP .50:
.20:; Machine does not support real NOP.
MOV ESI, .16b086:
JSt [EDI+SSS.Status],sssWidth16, .50:
MOV ESI, .32b386:
.50:MOV ECX,EDX ; Stuff size 1..9
MOVZX EDX,[ESI+EDX-1],DATA=BYTE
LEA ESI,[.N: + EDX] ; ESI points to the real NOP opcode (ECX bytes long).
BufferStore EBX,ESI,ECX
Msg cc=C,'9314',SssEmitAlignment ; Allocation error storing to buffer in !1H.
RET
ENDP SssEmitAlignment.Nop:
Stm.Program.SssList
and initializes its .Name, .LinePtr, .GroupPtr, .Status.
Stm.LinePtr.Stm.Program must specify
PGM object which the segment/section/strucutre is created for.
SssCreateGroup Procedure Stm, NamePtr, NameSize, SssStatus, SssPurpose
MOV ECX,[%Stm]
MOV EBX,[ECX+STM.Program]
MOV EDI,[ECX+STM.LinePtr]
MOV EDX,[EBX+PGM.Pool]
ListNew [EBX+PGM.SssList],Zeroed=yes ; Allocate room for the new group.
.10: MOV [%ReturnEAX],EAX
JC .90:
MOV [EAX+SSS.LinePtr],EDI
MOV EDI,EAX ; EDI is now pointer to the new group.
MOV ECX,[%NameSize]
PoolStore EDX,[%NamePtr],ECX ; Make the Name permanent during PGM lifetime.
JC .10:
MOV [EDI+SSS.NamePtr],EAX ; EAX,ECX is now nonvolatile group name.
MOV [EDI+SSS.NameSize],ECX
MOV [EDI+SSS.PgmPool],EDX
BufferCreate EDX,Size=64
MOV [EDI+SSS.SssOrdBuffer],EAX ; Buffer for the pointers to segments which belong to the group.
MOV ECX,[%SssPurpose]
MOV EDX,[%SssStatus]
SetSt EDX,sssGroup+sssDefinedInPass
JSt EDX,sssImplicit,.70:
SetSt EDX,sssUsed
.70: SetSt [EDI+SSS.Status],EDX
SetSt [EDI+SSS.Purpose],ECX
MOV [EDI+SSS.GroupPtr],EDI
.90:EndProcedure SssCreateGroup ; Group does not store emitted data, relocations, members.
Stm.Program.SssList.
.Name is made program-persistent. LinePtr is taken from Stm.LinePtr.[NewSegment] SEGMENT or [NewSection].
SssCreateSe Procedure Stm, SegmPtr, NamePtr, NameSize, SssStatus, Purpose, Alignment
MOV ECX,[%Stm]
MOV EBX,[ECX+STM.Program]
MOV EDX,[EBX+PGM.Pool]
MOV EDI,[ECX+STM.LinePtr]
ListNew [EBX+PGM.SssList],Zeroed=yes ; Allocate room for the new section.
MOV [%ReturnEAX],EAX
JC .90:
MOV [EAX+SSS.LinePtr],EDI
MOV EDI,EAX ; EDI now points to the new segment.
MOV ECX,[%NameSize]
PoolStore EDX,[%NamePtr],ECX ; Make the Name permanent during PGM lifetime.
JC .90:
MOV [EDI+SSS.NamePtr],EAX
MOV [EDI+SSS.NameSize],ECX
MOV [EDI+SSS.PgmPool],EDX
BufferCreate EDX,Size=16K
MOV [EDI+SSS.EmitBuffer],EAX
BufferCreate EDX,Size=4K
MOV [EDI+SSS.RelocBuffer],EAX
MOV EAX,[%SssStatus]
MOV [EDI+SSS.Status],EAX
JNSt EAX,sssSegment|sssGroup,.50:
BufferCreate EDX,Size=32
MOV [EDI+SSS.SssOrdBuffer],EAX
.50: MOV ECX,[%Alignment]
MOV EDX,[%Purpose]
MOV [EDI+SSS.Alignment],ECX
MOV [EDI+SSS.Purpose],EDX
MOV ECX,[%SegmPtr] ; ^SSS with segment where the section EDI belongs to.
TEST ECX
JNZ .70:
MOV ECX,EDI ; Segment EDI points to itself if %SegmPtr is not specified.
.70: MOV [EDI+SSS.SegmPtr],ECX
Invoke SymCreateSe::,EDI,EBX ; Create a symbol associated with the segment EDI.
.90:EndProcedure SssCreateSe
Stm.Program.SssList
and initializes its .Name, .LinePtr, .SegmPtr, .EmitBuffer, .RelocBuffer, .Status, .Purpose, .Alignment.
Stm.LinePtr.
symSe type with the same name.
It represent the bottom of segment for relocation purposes.
SssCreateStructure Procedure Stm, NamePtr, NameSize, SssStatus, Alignment
; SSS.Name.
MOV ECX,[%Stm]
MOV EBX,[ECX+STM.Program]
MOV EDI,[ECX+STM.LinePtr]
MOV EDX,[EBX+PGM.Pool]
ListNew [EBX+PGM.SssList],Zeroed=yes ; Allocate room for the new structure.
MOV [%ReturnEAX],EAX
JC .90:
MOV [EAX+SSS.LinePtr],EDI
MOV EDI,EAX ; EDI is now pointer to the new structure.
MOV ECX,[%NameSize]
PoolStore EDX,[%NamePtr],ECX ; Make the Name permanent during PGM lifetime.
JC .90:
MOV [EDI+SSS.NamePtr],EAX ; ESI,ECX is now nonvolatile nonlocal structure name.
MOV [EDI+SSS.NameSize],ECX
MOV [EDI+SSS.PgmPool],EDX
MOV EAX,[%SssStatus]
MOV ECX,[%Alignment]
MOV [EDI+SSS.Status],EAX
MOV [EDI+SSS.Alignment],ECX
SetSt [EDI+SSS.Purpose],sssPurposeDATA
MOV ECX,512 ; Initial buffer size for structures.
BufferCreate EDX,Size=ECX
MOV [EDI+SSS.EmitBuffer],EAX
BufferCreate EDX,Size=ECX
MOV [EDI+SSS.RelocBuffer],EAX
BufferCreate EDX,Size=ECX
MOV [EDI+SSS.SssOrdBuffer],EAX
MOVD [EDI+SSS.SegmIndex],-1 ; pfcoffSYM_ABSOLUTE
; The just defined strucure name might have been forward referenced sooner and thus incorrectly had created a symbol.
Invoke SymFindByName::,0,[EDI+SSS.NamePtr],[EDI+SSS.NameSize],EBX
JC .80: ; If it's not the case.
JNSt [EAX+SYM.Status],symDefined,.40:
JSt [EAX+SYM.Status],symSe,.80:
Msg '6612',EAX,[EAX+SYM.LinePtr] ; Cannot declare structure "!1S" when such symbol was declared at !2@.
JMP .90:
.40: ; False created but not defined symbol EAX will be removed, because its name belongs to the structure.
ListRemove [EBX+PGM.SymList],EAX
.80: CLC
.90:EndProcedure SssCreateStructure
[@RT0],[$RT1],[@RT2]...)[@LT64],[@LT32],,,[@LT1])[@LT64],[@LT32],,,[@LT1])Segment.SssOrdBuffer
. Each stored section is marked as sssMarshaled to avoid duplications.Segment.SssOrdBuffer is filled with DWORD pointers to its
SSS sections in the desired order. At least one pointer is always stored.
SssOrderSections Procedure Segment, Program
LitVal LocalVar ; Ordinal Nr of literal section 1,2,3.. or 64,32,16,8,4,2,1.
LitName LocalVar Size=12 ; Room for section name "@RT1","@RT2","@RT3".. or "@LT64","@LT32".."@LT1".
MOV EBX,[%Program]
MOV EDI,[%Segment]
MOV EDX,[EDI+SSS.SssOrdBuffer]
BufferClear EDX
; 0. Base section, identical with segment EDI.
BufferStoreDword EDX,EDI
SetSt [EDI+SSS.Status],sssMarshaled
; 1. Non-literal sections of segment EDI.
ListGetFirst [EBX+PGM.SssList]
.11: JSt [EAX+SSS.Status],sssMarshaled,.19:
JNSt [EAX+SSS.Status],sssSection,.19:
CMP [EAX+SSS.SegmPtr],EDI ; Does the section EAX belong to Segment EDI?
JNE .19:
JSt [EAX+SSS.Purpose],sssPurposeLITERAL,.19:; Leave literal sections to next steps.
JNSt [EAX+SSS.Purpose],sssPurposeCODE,.19: ; Prefer nonliteral code sections.
BufferStoreDword EDX,EAX
SetSt [EAX+SSS.Status],sssMarshaled
.19: ListGetNext EAX
JNZ .11:
; 2. Literal code sections by name in alphabetical order.
JNSt [EDI+SSS.Purpose],sssPurposeCODE,.30: ; If segment EDI cannot host @RT sections.
MOVD [%LitVal],0 ; Start with [@RT0].
MOVD [%LitName],'@RT0'
.21: LEA ESI,[%LitName]
MOV EAX,[%LitVal]
LEA EDI,[ESI+3] ; Skip characters '@RT'.
StoD EDI
SUB EDI,ESI ; Number of characters in literal section name.
Invoke SssFindByName, sssSection,0,ESI,EDI,EBX
MOV EDI,[%Segment]
JNC .28:
CMPD [%LitVal],0
JE .29: ; After [@RT0] try to find [@RT1] etc.
JMP .30: ; If no section above @RT0 exists, neither the following one can exist.
.28: JSt [EAX+SSS.Status],sssMarshaled,.29:
CMP [EAX+SSS.SegmPtr],EDI
JNE .29: ; If the literal section doesn't belong to our segment.
BufferStoreDword EDX,EAX
SetSt [EAX+SSS.Status],sssMarshaled
.29: INCD [%LitVal] ; Prepare for the next code literal section.
JMP .21:
; 3. Nonliteral rodata sections.
.30: JNSt [EDI+SSS.Purpose],sssPurposeRODATA,.40:
ListGetFirst [EBX+PGM.SssList]
.31: JSt [EAX+SSS.Status],sssMarshaled,.39:
JNSt [EAX+SSS.Status],sssSection,.39:
CMP [EAX+SSS.SegmPtr],EDI ; Does the section EAX belong to Segment EDI?
JNE .39:
JSt [EAX+SSS.Purpose],sssPurposeLITERAL,.39:; Leave literal sections to next steps.
JNSt [EAX+SSS.Purpose],sssPurposeRODATA,.39:; Prefer nonliteral rodata sections.
BufferStoreDword EDX,EAX
SetSt [EAX+SSS.Status],sssMarshaled
.39: ListGetNext EAX
JNZ .31:
; 4. Read-only data literal sections in descending order of their alignment ([@LT64], [@LT32],..[@LT1]).
.40: JNSt [EDI+SSS.Purpose],sssPurposeRODATA|sssPurposeDATA,.60: ; If segment EDI cannot host @LT sections.
MOVD [%LitVal],64 ; Start with @LT section which has the strongest alignment.
MOVD [%LitName],'@LT*'
.41: LEA ESI,[%LitName]
MOV EAX,[%LitVal]
LEA EDI,[ESI+3] ; Skip characters '@LT'.
StoD EDI ; [%LitVal] now contains '@LT64','@LT32','@LT16', '@LT8', '@LT4' etc.
SUB EDI,ESI ; Number of characters in literal section name.
Invoke SssFindByName,sssSection,0,ESI,EDI,EBX
MOV EDI,[%Segment]
JC .49: ; If no such section was present.
JSt [EAX+SSS.Status],sssMarshaled,.49:
CMP [EAX+SSS.SegmPtr],EDI
JNE .49: ; If the literal section doesn't belong to our segment.
BufferStoreDword EDX,EAX
SetSt [EAX+SSS.Status],sssMarshaled
.49: SARD [%LitVal],1 ; Prepare for the next data literal section.
JNC .41: ; CF=1 if [@LT1] has just been marshaled. Otherwise try the next @LT*.
; 5. Nonliteral data sections.
.50: JNSt [EDI+SSS.Purpose],sssPurposeDATA,.60:
ListGetFirst [EBX+PGM.SssList]
.51: JSt [EAX+SSS.Status],sssMarshaled,.59:
JNSt [EAX+SSS.Status],sssSection,.59:
CMP [EAX+SSS.SegmPtr],EDI ; Does the section EAX belong to Segment EDI?
JNE .59:
JSt [EAX+SSS.Purpose],sssPurposeLITERAL,.59:; Leave literal sections to next step.
JNSt [EAX+SSS.Purpose],sssPurposeDATA,.59: ; Prefer nonliteral rodata sections.
BufferStoreDword EDX,EAX
SetSt [EAX+SSS.Status],sssMarshaled
.59: ListGetNext EAX
JNZ .51:
; 6. Data literal sections in descending order of their alignment ([@LT64], [@LT32],..[@LT1]).
.60: JNSt [EDI+SSS.Purpose],sssPurposeRODATA|sssPurposeDATA,.70: ; If segment EDI cannot host @LT* sections.
MOVD [%LitVal],64 ; Start with @LT section which has the strongest alignment.
MOVD [%LitName],'@LT*'
.61: LEA ESI,[%LitName]
MOV EAX,[%LitVal]
LEA EDI,[ESI+3] ; Skip characters '@LT'.
StoD EDI ; [%LitVal] now contains '@LT64','@LT32','@LT16', '@LT8', '@LT4' etc.
SUB EDI,ESI ; Number of characters in literal section name.
Invoke SssFindByName,sssSection,0,ESI,EDI,EBX
MOV EDI,[%Segment]
JC .69: ; If no such section was present.
JSt [EAX+SSS.Status],sssMarshaled,.69:
CMP [EAX+SSS.SegmPtr],EDI
JNE .79: ; If the literal section doesn't belong to our segment.
BufferStoreDword EDX,EAX
SetSt [EAX+SSS.Status],sssMarshaled
.69: SARD [%LitVal],1 ; Prepare for the next data literal section.
JNC .61: ; CF=1 if [@LT1] has just been marshaled. Otherwise try the next @LT*.
.70: ; 7. All other nonBSS sections which wasn't marshaled yet.
ListGetFirst [EBX+PGM.SssList]
.71: JSt [EAX+SSS.Status],sssMarshaled,.79:
JNSt [EAX+SSS.Status],sssSection,.79:
CMP [EAX+SSS.SegmPtr],EDI ; Does the section EAX belong to Segment EDI?
JNE .79:
JNSt [EAX+SSS.Status],sssNotBSS,.79: ; Leave literal sections to next step.
BufferStoreDword EDX,EAX
SetSt [EAX+SSS.Status],sssMarshaled
.79: ListGetNext EAX
JNZ .71:
.80: ; 8. All BSS sections.
ListGetFirst [EBX+PGM.SssList]
.81: JSt [EAX+SSS.Status],sssMarshaled,.89:
JNSt [EAX+SSS.Status],sssSection,.89:
CMP [EAX+SSS.SegmPtr],EDI ; Does the section EAX belong to Segment EDI?
JNE .89:
JNSt [EAX+SSS.Purpose],sssPurposeBSS,.89:
BufferStoreDword EDX,EAX
SetSt [EAX+SSS.Status],sssMarshaled
.89: ListGetNext EAX
JNZ .81:
.90: ; 9. All other STACK sections.
ListGetFirst [EBX+PGM.SssList]
.91: JSt [EAX+SSS.Status],sssMarshaled,.99:
JNSt [EAX+SSS.Status],sssSection,.99:
CMP [EAX+SSS.SegmPtr],EDI ; Does the section EAX belong to Segment EDI?
JNE .99:
JNSt [EAX+SSS.Purpose],sssPurposeBSS,.99:
BufferStoreDword EDX,EAX
SetSt [EAX+SSS.Status],sssMarshaled
.99: ListGetNext EAX
JNZ .91:
EndProcedure SssOrderSections
SSS.Bottom and SSS.Top is adjusted
by virtual sizes of previous sections, in the order specified in
Segment.SssOrdBuffer, aligned by section's SSS.Alignment.
SSS.Bottom and SSS.Top of each section is adjusted.
SSS.Org is reset to the new SSS.Bottom.
SssLinkSections Procedure Segment
OrdPtr LocalVar ; Pointer to an array of pointers to sections.
OrdTop LocalVar ; Pointer behind the end of array.
MOV EBX,[%Segment]
BufferRetrieve [EBX+SSS.SssOrdBuffer] ; Pass 1 - merge purposes of linked sections.
ADD ECX,ESI ; ESI..ECX is array of pointers to sections.
MOV [%OrdPtr],ESI
MOV [%OrdTop],ECX
.01: MOV ESI,[%OrdPtr]
CMP ESI,[%OrdTop]
JNB .09:
MOV EDI,[ESI] ; EDI is the linked section, EBX is its segment.
ADDD [%OrdPtr],4 ; Prepare for the next section.
JNSt [EDI+SSS.Status],sssNotBSS,.03: ; If any section is initialized, its segment EBX is too.
SetSt [EBX+SSS.Status],sssNotBSS
.03: MOV EAX,[EDI+SSS.Purpose]
OR [EBX+SSS.Purpose],EAX ; Merge purposes of each section to its segment EBX.
JMP .01:
.09: BufferRetrieve [EBX+SSS.SssOrdBuffer] ; Pass 2 - update virtual addresses of linked sections.
MOV [%OrdPtr],ESI
.10: MOV ESI,[%OrdPtr]
CMP ESI,[%OrdTop]
JNB .90: ; End when there are no more sections.
MOV EDI,[ESI] ; EDI is the linked section, EBX is its segment.
ADDD [%OrdPtr],4 ; Prepare for the next section.
JNSt [EBX+SSS.Status],sssNotBSS,.50: ; When the segment is uninitialized.
; Segment is initialized. Its final .Top will be determined by emitted size.
BufferRetrieve [EBX+SSS.EmitBuffer]
MOV EAX,ECX
XOR EDX,EDX ; EDX:EAX is the top of initialized segment, i.e. its emitted and virtual size.
MOV [EBX+SSS.TopLow],EAX
MOV [EBX+SSS.TopHigh],EDX
.20: CMP EDI,EBX ; Do nothing else with the 1st section, which is identical with segment EBX.
JE .10: ; Link the next section.
Invoke ExpAlign::,EAX,[EDI+SSS.Alignment],0 ; Align the top of segment EBX by section's own alignment. Return stuff size in ECX.
JECXZ .30: ; If no alignment is necessary.
ADD EAX,ECX
ADC EDX,0 ; EDX:EAX is the new aligned top of segment EBX and bottom of section EDI.
MOV [EBX+SSS.TopLow],EAX
MOV [EBX+SSS.TopHigh],EDX
.30: MOV [EDI+SSS.BottomLow],EAX
MOV [EDI+SSS.BottomHigh],EDX
MOV ESI,0x90 ; Prepare alignment stuff in SIL.
JSt [EBX+SSS.Purpose],sssPurposeCODE,.32:
XOR ESI,ESI
.32: BufferResize [EBX+SSS.EmitBuffer],ECX,Stuff=ESI ; Align the emitted contents of segment EBX.
BufferRetrieve [EDI+SSS.EmitBuffer]
BufferStore [EBX+SSS.EmitBuffer],ESI,ECX ; Copy the emitted contents of section EDI to segment EBX.
BufferRetrieve [EBX+SSS.EmitBuffer] ; Reread the concatenated contents.
MOV [EBX+SSS.TopLow],ECX
MOV [EDI+SSS.TopLow],ECX
BufferRetrieve [EDI+SSS.RelocBuffer]
BufferStore [EBX+SSS.RelocBuffer],ESI,ECX ; Copy unchanged relocations from section EDI to the base segment EBX.
JMP .10: ; The next section.
.50: ; Segment is uninitialized. Its size is calculated from virtual sizes of its sections.
MOV EAX,[EBX+SSS.TopLow] ; Bottom of segment EBX is always 0.
MOV EDX,[EBX+SSS.TopHigh] ; EDX:EAX is top of uninitialized base segment, i.e. its virtual size.
CMP EDI,EBX ; Do nothing else with the 1st section, which is identical with segment EBX.
JE .10:
Invoke ExpAlign::,EAX,[EDI+SSS.Alignment],0 ; Align the top of segment EBX by section's own alignment. Return stuff size in ECX.
JECXZ .60: ; If no alignment is necessary.
ADD EAX,ECX
ADC EDX,0 ; EDX:EAX is the new aligned top of segment EBX and bottom of section EDI.
MOV [EBX+SSS.TopLow],EAX
MOV [EBX+SSS.TopHigh],EDX
.60: MOV [EDI+SSS.BottomLow],EAX
MOV [EDI+SSS.BottomHigh],EDX
ADD [EDI+SSS.TopLow],EAX
ADC [EDI+SSS.TopHigh],EDX
MOV EAX,[EDI+SSS.TopLow]
MOV EDX,[EDI+SSS.TopHigh]
MOV [EBX+SSS.TopLow],EAX
MOV [EBX+SSS.TopHigh],EDX
JMP .10: ; Link the next section.
.90:EndProcedure SssLinkSections
SSS.SegmPtr, SSS.GroupPtr, SSS.SymPtr
and replaces them with pointers to the corresponding objects in BasePgm.
SssResolve Procedure BasePgm
MOV EBX,[%BasePgm]
ListGetFirst [EBX+PGM.SssList]
JZ .90:
.10:JNSt [EAX+SSS.Status],sssSegment|sssGroup|sssExtern,.80:
MOV ECX,[EAX+SSS.SegmPtr]
JECXZ .30:
MOV ESI,[ECX+SSS.SegmPtr]
MOV [EAX+SSS.SegmPtr],ESI
.30:MOV ECX,[EAX+SSS.GroupPtr]
JECXZ .50:
MOV ESI,[ECX+SSS.GroupPtr]
MOV [EAX+SSS.GroupPtr],ESI
.50:MOV EDX,[EAX+SSS.SymPtr]
TEST EDX
JZ .80:
MOV ECX,[EDX+SYM.SymbPtr]
JECXZ .80:
MOV [EAX+SSS.SymPtr],ECX
.80:ListGetNext EAX
JNZ .10:
.90:EndProcedure SssResolve
SssCreateImplicit creates implicit groups and segments when a new program is initialized.
Implicit common group [COMGROUP] is created in TINY model (default in formats BIN, BOOT, COM), it assumes
CS=DS=SS=PARA# [COMGROUP].
No groups are created in FLAT memory model, segment registers are established by the program loader.
Implicit segment names are [.text], [.rodata], [.data], [.bss] when PROGRAM MODEL=FLAT
, and [CODE], [RODATA], [DATA], [BSS], [STACK] otherwise.
Pseudosegment [PSP] is created in program formats COM and BOOT.
Implicit groups [CGROUP], [DGROUP], [SGROUP] are created in near memory models
and the segment registers is assumed to contain paragraph address of the group:
CS=PARA# [CGROUP], DS=PARA# [DGROUP], SS=PARA# [SGROUP].
Usually there is just one code segment in near monocode MZ program (MODEL=TINY | SMALL | COMPACT),
and if more code segments with different names are combined (from linked modules, for instance), they will be grouped here
into an implicit code group [CGROUP] which should be used as their addressing frame.
Similary, in near monodata MZ program (MODEL=TINY | SMALL | MEDIUM
) all data, read-only data and uninitialized data segments are grouped in [DGROUP] and segment register DS is assumed loaded with
DS=PARA# [DGROUP].
PGM.SssList is populated with implicit groups and segments.
SssCreateImplicit Procedure Program
CGROUP LocalVar ; ^SSS [CGROUP] or 0.
DGROUP LocalVar ; ^SSS [DGROUP] or 0.
SGROUP LocalVar ; ^SSS [SGROUP] or 0.
PSPsize LocalVar ; SIZE# [PSP] or 0.
SssStm LocalVar Size=SIZE#STM ; Temporary STM required by SssCreateSe.
ClearLocalVar
; Prepare temporary statement EBX.
LEA EBX,[%SssStm]
MOV EDI,[%Program]
MOV [EBX+STM.Program],EDI
MOV EAX,[EDI+PGM.LinePtr]
MOV [EBX+STM.LinePtr],EAX
; Create pseudosegment [PSP] in formats COM and BOOT.
MOV EAX,[EDI+PGM.Pgmopt.Status]
AND EAX,pgmoptFormatMask
Dispatch AL,pgmoptCOM,pgmoptBOOT
JMP .20:
.pgmoptCOM:
MOVD [%PSPsize],0x0100
JMPS .20:
.pgmoptBOOT:
MOVD [%PSPsize],0x7C00
.20: ; Create implicit groups according to model.
MOV EAX,[EDI+PGM.Pgmopt.Status]
MOV ESI,pgmoptWidthMask ; Identical with sssWidthMask.
MOV ECX,EAX
AND ESI,EAX ; Default segment width is adopted from program width.
OR ESI,sssGroup+sssImplicit+sssPublic ; Status of implicit groups.
AND EAX,pgmoptModelMask
Dispatch EAX,pgmoptFLAT,pgmoptTINY,pgmoptSMALL,pgmoptMEDIUM,pgmoptCOMPACT,pgmoptLARGE,pgmoptHUGE
.pgmoptTINY:
Invoke SssCreateGroup,EBX,=B"COMGROUP",8,ESI,sssPurposeCODE+sssPurposeDATA+sssPurposeRODATA+sssPurposeBSS+sssPurposeSTACK
Invoke SymCreateSe::,EAX,[EBX+STM.Program]
MOV [%CGROUP],EAX
MOV [%DGROUP],EAX
MOV [%SGROUP],EAX
CALL .CreatePSP:
JMP .pgmoptREAL:
.pgmoptCOMPACT:
.pgmoptSMALL:
Invoke SssCreateGroup,EBX,=B"CGROUP",6,ESI,sssPurposeCODE
Invoke SymCreateSe::,EAX,[EBX+STM.Program]
MOV [%CGROUP],EAX
CALL .CreatePSP:
.40: JSt [EDI+PGM.Pgmopt.Status],pgmoptCOMPACT,.pgmoptLARGE:
.pgmoptMEDIUM:
Invoke SssCreateGroup,EBX,=B"DGROUP",6,ESI,sssPurposeDATA+sssPurposeRODATA+sssPurposeBSS
Invoke SymCreateSe::,EAX,[EBX+STM.Program]
MOV [%DGROUP],EAX
.pgmoptLARGE:
.pgmoptHUGE:
Invoke SssCreateGroup,EBX,=B"SGROUP",6,ESI,sssPurposeSTACK
Invoke SymCreateSe::,EAX,[EBX+STM.Program]
MOV [%SGROUP],EAX
.pgmoptREAL: ; Real-mode programs use implicit segments [CODE],[DATA] etc.
RstSt ESI,sssGroup
SetSt ESI,sssSegment+sssSection ; Prepare status of implicit segments.
Invoke SssCreateSe::,EBX,0,=B"CODE",4,ESI,sssPurposeCODE,16
MOV EDX,[%CGROUP]
MOV [EAX+SSS.GroupPtr],EDX
Invoke SssCreateSe::,EBX,0,=B"RODATA",6,ESI,sssPurposeRODATA,16
MOV EDX,[%DGROUP]
MOV [EAX+SSS.GroupPtr],EDX
Invoke SssCreateSe::,EBX,0,=B"DATA",4,ESI,sssPurposeDATA,16
MOV [EAX+SSS.GroupPtr],EDX
Invoke SssCreateSe::,EBX,0,=B"BSS",3,ESI,sssPurposeBSS,16
MOV [EAX+SSS.GroupPtr],EDX
Invoke SssCreateSe::,EBX,0,=B"STACK",5,ESI,sssPurposeSTACK,16
MOV EDX,[%SGROUP]
MOV [EAX+SSS.GroupPtr],EDX
JMP .90:
.CreatePSP: PROC ; Create segment PSP in group EAX with size [%PSPsize]. EBX=^PGM, EAX=^SSS group. Output:EDX=^SSS group, [%PSPsize]-0.
MOV EDX,EAX
MOV ECX,[%PSPsize]
JECXZ .P9:
Invoke SssCreateSe,EBX,0,=B'PSP',3,sssImagePrefix+sssSegment+sssImplicit+sssUsed+sssDefinedInPass,sssPurposePSP,0
MOV [EAX+SSS.TopLow],ECX
MOV [EAX+SSS.GroupPtr],EDX
BufferStoreDword [EDX+SSS.SssOrdBuffer],EAX ; Add segment [PSP] to the group EDX.
MOVD [%PSPsize],0
.P9: RET
ENDP .CreatePSP:
.pgmoptFLAT:
RstSt ESI,sssGroup
SetSt ESI,sssSegment+sssSection ; Prepare status of implicit segments.
Invoke SssCreateSe::,EBX,0,=B".text",5,ESI,sssPurposeCODE,16
Invoke SssCreateSe::,EBX,0,=B".rodata",7,ESI,sssPurposeRODATA,16
Invoke SssCreateSe::,EBX,0,=B".data",5,ESI,sssPurposeDATA,16
Invoke SssCreateSe::,EBX,0,=B".bss",4,ESI,sssPurposeBSS,16
.90:EndProcedure SssCreateImplicit
Sss.Org
before storing the data from %EmitBuffer to Sss.EmitBuffer. It is non-negative when the emitted data
need alignment, either by explicit keyword ALIGN= in the current statement,
or when AUTOALIGN is enabled.
$ EQU expression, too.
AlignBytes can also be negative in statements like $ EQU $-8.
SssEmit Procedure Sss, EmitBuffer, RelocBuffer, AlignBytes
OldOrg LocalVar ; Origin before alignment and emitting.
MOV EDI,[%Sss]
MOV EAX,[%AlignBytes]
CDQ
MOV ECX,[EDI+SSS.OrgLow]
MOV [%OldOrg],ECX
ADD EAX,ECX
MOV EBX,EAX
ADC EDX,[EDI+SSS.OrgHigh] ; EBX and EDX:EAX is aligned origin between 0 and 4G.
JZ .10:
.E6555:Msg '6555' ; Offset out of section limits.
JMP .90:
.10:BufferRetrieve [%EmitBuffer] ; Get the emitted size of current statement.
ADD EAX,ECX
ADC EDX,0
MOV [EDI+SSS.OrgLow],EAX
MOV [EDI+SSS.OrgHigh],EDX ; Establish new origin.
CMP EDX,[EDI+SSS.TopHigh]
JA .12:
CMP EAX,[EDI+SSS.TopLow]
JBE .15
.12:MOV [EDI+SSS.TopLow],EAX
MOV [EDI+SSS.TopHigh],EDX
.15:BufferRetrieve [EDI+SSS.EmitBuffer]
MOV EDX,ECX ; ECX and EDX is the old emited contents size.
SUB EAX,ECX
JNG .20: ; EAX is the required SSS.EmitBuffer enlargement, if positive.
BufferNew [EDI+SSS.EmitBuffer],EAX,Zeroed=No ; Enlarge the size of SSS.EmitBuffer claimed contents.
.20:BufferRetrieve [EDI+SSS.EmitBuffer] ; ESI points to the bottom of of (enlarged) contents in SSS.EmitBuffer.
MOV ECX,EBX ; EBX is aligned origin.
SUB ECX,EDX ; EDX is previous size of SSS.EmitBuffer claimed contents.
JNA .40: ; Skip when no alignment stuff is required.
XOR EAX,EAX
JNSt [EDI+SSS.Purpose],sssPurposeCODE,.30:
MOV AL,0x90 ; The stuff is either 0 or NOP.
.30:MOV EDI,ESI ; SSS.EmitBuffer bottom.
ADD EDI,[%OldOrg] ; ECX bytes of alignment stuff AL will be emitted at EDI.
REP STOSB ; Emit the alignment stuff.
.40:MOV EDI,ESI ; SSS.EmitBuffer bottom.
MOV EDX,ESI
ADD EDI,EBX
BufferRetrieve [%EmitBuffer]
REP MOVSB ; Emit the instruction|data.
BufferRetrieve [%RelocBuffer] ; Now store relocations, if there are any.
JECXZ .90: ; If %RelocBuffer is unspecified or empty.
MOV EDI,[%Sss]
.60:ADD [ESI+RELOC.OrgLow],EBX ; Patch the relocation by EBX=temporary SSS.Org.
MOV [ESI+RELOC.Section],EDI
BufferStore [EDI+SSS.RelocBuffer],ESI,SIZE#RELOC
ADD ESI,SIZE# RELOC
SUB ECX,SIZE#RELOC
JA .60: ; The next relocation (there may be two in one statement).
.90:EndProcedure SssEmit
RELOC.Org
is fixed up according to relocation type.
Segment.RelocBuffer are then marked as relocResolved.
SssRelocResolve Procedure Segment, Program
;;EmittedBottom LocalVar ; Pointer to the bottom of emitted data (withing the contents of SSS.EmitBuffer).
;;EmittedPtr LocalVar ; Pointer to the relocated word|dword in emitted data. Always between %EmittedBottom and %EmittedTop.
;;EmittedTop LocalVar ; Pointer to the top of emitted data.
;ůRelocBottom LocalVar ; Pointer to the first RELOC record.
;RelocTop LocalVar ; Pointer to the top of array of RELOC records.
;Pgmopt LocalVar ; Program format and model (cache copy of Program.Pgmopt.Status).
MOV EBX,[%Program]
MOV EAX,[%Segment]
BufferRetrieve [EAX+SSS.RelocBuffer]
ADD ECX,ESI
.20: CMP ESI,ECX ; Walk through relocations in the loop .20: .. .Skip:.
JNB .90:
Invoke RelocResolve::,ESI,EBX
ADD ESI,SIZE# RELOC
JMP .20: ; The next RELOC record.
.90:EndProcedure SssRelocResolve
SssUpdateByEmit Procedure SssObject
MOV EDI,[%SssObject]
TEST EDI
JZ .90:
BufferRetrieve [EDI+SSS.EmitBuffer]
MOV EAX,[EDI+SSS.BottomLow]
MOV EDX,[EDI+SSS.BottomHigh]
ADD EAX,ECX
ADC EDX,0
MOV [EDI+SSS.TopLow],EAX
MOV [EDI+SSS.TopHigh],EDX
ADD ECX,[EDI+SSS.BottomFA]
MOV [EDI+SSS.TopFA],ECX
.90:EndProcedure SssUpdateByEmit
ENDPROGRAM sss