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), although they may have identical names.
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.
Default implicit segments are created at PgmCreateProgram by PgmCreateImplicitSegments for each purpose (code,data,bss,stack), actual default segment names depend on the program format and model. Other SSS object may be created with explicit STRUC, GROUP, SEGMENT, [section], EXTERN statements, and the unused default segments will be abandoned in this case.
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).
sss PROGRAM FORMAT=COFF,MODEL=FLAT,WIDTH=32 INCLUDEHEAD "euroasm.htm" ; Interface (structures, symbols and macros) of other modules.
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 ; Pointer to object name without []. In case of sssExtern it is the symbol name.
.NameSize D D ; Size of object name.
.ClassPtr D D ; CLASS= property of segment. Unquoted identifier.
.ClassSize D D ; Netto size of segment property CLASS= without quotes.
; +10h.
.Status D D ; Boolean properties of the object in SssEncoding.
.Purpose D D ; Purpose of the section or segment in SssPurposeEncoding.
.SegmIndex D D ; Index of this segment in COFF SectionTable (1..)/OMF SGMDEF ordinal/GRPDEF record (1..).
.NameIndex D D ; Index of this segment in COFF SymbolTable (0..) /OMF LNAMES ordinal (1..).
; +20h.
.BottomLow D D ; Base offset of section in segment. Greater than 0 in non-base sections only.
.BottomHigh D D ; Bottom is 0 at assembly time for structures, segments, groups.
.OrgLow D D ; Origin pointer {offset of $}. Reset to .Bottom at start of each pass.
.OrgHigh D D ;
; +30h.
.TopLow D D ; Used limit of section/segment. Maximum of .Org ever reached. Not reset at start of pass.
.TopHigh D D ; Used limit of section/segment. Maximum of .Org ever reached.
D D ; Unused.
.Alignment D D ; 0=unspec 1=B,2=W,4=D,8=Q,16=O,32=Y,64=Z,128,256 etc.
; +40h.
.LinePtr D D ; Physical line which declared the object.
.SymPtr D D ; Ptr to a public SYM when sssExtern was resolved in PgmLinkImage.
.GroupPtr D D ; Ptr to group object (another SSS structure) which this segment/group belongs to.
.SegmPtr D D ; Ptr to segment object (another SSS structure) which this section belongs to.
; +50h.
.PgmPool D D ; Pointer to POOL where structure-member names will be stored persistently.
.EmitBuffer D D ; Pointer to BUFFER with emitted code/data.
.RelocBuffer D D ; Pointer to BUFFER with RELOC records.
.MemberBuffer D D ; Pointer to BUFFER with MEMBER records. Used with sssStructure.
ENDSTRUC SSS
; 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.
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.
sssNotBSS EQU 0x0000_0800 ; The object is initialized.
; COMBINE= property of segment.
sssPublic EQU 0x0000_1000 ; Segment will be combined with other segments with 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.
; 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.
; Miscellaneous properties.
sssOrgEqu EQU 0x0800_0000 ; Just emitted statement was $ EQU something. Used in SssEmit.
sssDefinedInPass EQU 0x1000_0000 ; Object was already declared in this pass. Reset in PassCreate.
sssDefinedInGroup EQU 0x2000_0000 ; Object was declared in GROUP pseudoinstruction.
sssImplicit EQU 0x4000_0000 ; Group/segment was created by default in PgmCreateProgram.
sssUsed EQU 0x8000_0000 ; Structure/section/segment was referred at least once (sssDirty).
EXPORT .. RESERVED) must match the order of indexes in
PFPE_encodings, i.e. the order
of special data directories in optional header.
%SssPurposeList %SET \
EXPORT, IMPORT, RESOURCE, EXCEPTION, SECURITY, BASERELOC, DEBUG, COPYRIGHT, \
GLOBALPTR, TLS, LOAD_CONFIG, BOUND_IMPORT, IAT, DELAY_IMPORT, CLR, RESERVED, \
CODE, DATA, BSS, STACK, LITERAL, DRECTVE
%Value %SETA 1
PURPOSE %FOR %SssPurposeList
sssPurpose%PURPOSE EQU %Value
%Value %SETA %Value << 1 ; >>
%ENDFOR PURPOSE
; The first 16 purposes specify .DataDirectory in PE optional header:
sssPurposeOptionalMask EQU 0x0000_FFFF ; EXPORT,IMPORT,RESOURCE,...
; Segments with initialized contents:
sssPurposeInitMask EQU sssPurposeOptionalMask | sssPurposeCODE | sssPurposeDATA
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).
SssFind 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 SssFind
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 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 as used, thus next time SssCheckDirty will know faster.
STC
JMP .90:
.40: ; Section is dirty if any symbols belongs to it.
ListGetFirst [EDI+PGM.SymList]
JZ .55: ; If nothing was emitted to section EBX.
.45: MOV ECX,[EAX+SYM.Section]
JECXZ .50:
CMP ECX,EBX
JE .Dirty:
MOV ECX,[ECX+SSS.SegmPtr]
JECXZ .50:
CMP ECX,EBX
JE .Dirty:
.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 the section belong to inspected segment?
JNE .65: ; If 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 belong to inspected group?
JE .Dirty:
.80: ListGetNext EAX
JNZ .75:
.NotDirty:
CLC
.90: EndProcedure SssCheckDirty
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.
JECXZ .G99: ; If name empty.
XOR EAX,EAX
CMP [EBX+SSS.Purpose],EAX
JNZ .G99: ; If purpose was already set.
Invoke EaBufferReserve::, SssGuessPurpose
MOV EDX,EAX
BufferStore EDX,ESI,ECX
BufferRetrieve EDX
MOV EDI,ESI
.G01:LODSB ; Convert the name to upper case.
CMP AL,'a'
JB .G05:
CMP AL,'z'
JA .G05:
SUB AL,'a'-'A'
.G05:STOSB
LOOP .G01:
; Search for string 'DRECTVE'.
BufferRetrieve EDX
MOV EDI,ESI ; EDI,ECX is now the uppercased name.
.G10:MOV AL,'D'
REPNE SCASB
JNE .G20:
CMP ECX,6
JB .G20:
CMPD [EDI],'RECT'
JNE .G10:
CMPW [EDI+4],'VE'
JNE .G10:
MOV EAX,sssPurposeDRECTVE
JMP .G90:
.G20:; Search for string 'STACK'.
BufferRetrieve EDX
MOV EDI,ESI ; EDI,ECX is now the uppercased name.
.G25:MOV AL,'S'
REPNE SCASB
JNE .G30:
CMP ECX,4
JB .G30:
CMPD [EDI],'TACK'
MOV EAX,sssPurposeSTACK ; If 'STACK' found in the name.
JE .G90:
JMP .G25:
.G30:; Search for string 'BSS'.
BufferRetrieve EDX
MOV EDI,ESI ; EDI,ECX is now the uppercased name.
.G35:MOV AL,'B'
REPNE SCASB
JNE .G40:
CMP ECX,2
JB .G40:
CMPW [EDI],'SS'
MOV EAX,sssPurposeBSS ; If 'BSS' found in the name.
JE .G90:
JMP .G35:
.G40:; Search for string 'UDATA'.
BufferRetrieve EDX
MOV EDI,ESI ; EDI,ECX is now the uppercased name.
.G45:MOV AL,'U'
REPNE SCASB
JNE .G50:
CMP ECX,4
JB .G50:
CMPD [EDI],'DATA'
MOV EAX,sssPurposeBSS ; If 'UDATA' found in the name.
JE .G90:
JMP .G45:
.G50:; Search for string 'DATA'.
BufferRetrieve EDX
MOV EDI,ESI ; EDI,ECX is now the uppercased name.
.G55:MOV AL,'D'
REPNE SCASB
JNE .G60:
CMP ECX,3
JB .G60:
CMPD [EDI-1],'DATA'
MOV EAX,sssPurposeDATA ; If 'DATA' found in the name.
JE .G90:
JMP .G55:
.G60:; Search for string 'CODE'.
BufferRetrieve EDX
MOV EDI,ESI ; EDI,ECX is now the uppercased name.
.G65:MOV AL,'C'
REPNE SCASB
JNE .G70:
CMP ECX,3
JB .G70:
CMPD [EDI-1],'CODE'
MOV EAX,sssPurposeCODE ; If 'CODE' found in the name.
JE .G90:
JMP .G65:
.G70:; Search for string 'TEXT'.
BufferRetrieve EDX
MOV EDI,ESI ; EDI,ECX is now the uppercased name.
.G75:MOV AL,'T'
REPNE SCASB
JNE .G80:
CMP ECX,3
JB .G80:
CMPD [EDI-1],'TEXT'
MOV EAX,sssPurposeCODE ; If 'TEXT' found in the name.
JE .G90:
JMP .G75:
.G80:XOR EAX,EAX ; No known purpose guessed.
.G90:SetSt [EBX+SSS.Purpose],EAX
Invoke EaBufferRelease::,EDX
.G99:RET
ENDP1 .Guess:
.90: XOR EAX,EAX
CMP [EBX+SSS.Purpose],EAX ; 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
Stm.Program.SssList
and initializes its .Name, .LinePtr, .SegmPtr, .EmitBuffer, .RelocBuffer, .Status, .Purpose, .Alignment.
Stm.LinePtr.
Stm.Program must specify
PGM object which the segment/section/strucutre is created for.
sssStructure it must be unique among symbols and it may be local.
SssCreate Procedure Stm, Segment, NamePtr, NameSize, SssStatus, Purpose, Alignment
MOV EAX,[%Stm]
MOV EBX,[EAX+STM.Program]
; SSS.Name.
Invoke EaBufferReserve::, SssCreate
MOV EDX,symDelocalNone ; Segment and section names will not be delocalized when begin with ..
JSt [%SssStatus],sssGroup|sssSegment|sssSection|sssExtern,.10:
MOV EDX,symDelocal ; Structure names beginning with . will be prefixed with current namespace.
.10: Invoke SymDelocalName::,[%NamePtr],[%NameSize],EAX,EDX
BufferRetrieve EAX
Invoke EaBufferRelease::,EAX
MOV EDX,[%SssStatus]
JNSt EDX,sssSegment,.20:
Invoke SssFind,sssSegment,0,ESI,ECX,0 ; Created segment must be unique, unless it is implicit.
JC .20:
MOV EDI,EAX
JSt EDX,sssImplicit,.25: ; Do not create new implicit segment when it already exists.
MOV [%ReturnEAX],EAX
Msg '7836',EAX,[EAX+SSS.LinePtr] ; Segment [!1S] was already defined in !2@.
STC
JMP .90:
.20: PoolStore [EBX+PGM.Pool],ESI,ECX ; Make the Name permanent during PGM lifetime.
MOV ESI,EAX ; ESI,ECX is now nonvolatile SSS name.
ListNew [EBX+PGM.SssList],Zeroed=yes ; Allocate room for the new object.
JC .90:
MOV EDI,EAX ; EDI is now the new (empty) SSS object.
MOV [%ReturnEAX],EAX
MOV [EDI+SSS.NamePtr],ESI
MOV [EDI+SSS.NameSize],ECX
; SSS.Status.
.25: MOV [EDI+SSS.Status],EDX
; SSS.Purpose.
MOV EAX,[%Purpose]
MOV [EDI+SSS.Purpose],EAX
; SSS.Alignment.
MOV EAX,[%Alignment]
MOV [EDI+SSS.Alignment],EAX
; SSS.LinePtr.
MOV ECX,[%Stm]
MOV EAX,[ECX+STM.LinePtr]
MOV [EDI+SSS.LinePtr],EAX
; Dynamic memory objects.
MOV EAX,[EBX+PGM.Pool]
MOV [EDI+SSS.PgmPool],EAX
JNSt EDX,sssGroup,.30:
; A group of segments is created.
MOV [EDI+SSS.GroupPtr],EDI
JMP .90: ; Group does not store emitted data and relocations.
.30:MOV ECX,256 ; Initial buffer size for structures.
JSt EDX,sssStructure,.32:
MOV ECX,16K ; Initial buffer size for segments and sections.
.32:BufferCreate EAX,Size=ECX
MOV [EDI+SSS.EmitBuffer],EAX
BufferCreate [EBX+PGM.Pool],Size=ECX
MOV [EDI+SSS.RelocBuffer],EAX
JNSt EDX,sssStructure,.60:
; A structure is created.
BufferCreate [EBX+PGM.Pool],Size=32*SIZE#MEMBER ; Initial estimated capacity for 32 members.
MOV [EDI+SSS.MemberBuffer],EAX
MOVD [EDI+SSS.SegmIndex],-1 ; pfcoffSYM_ABSOLUTE
; Just defined strucure name might have been forward referenced sooner and thus incorrectly have created a symbol.
Invoke SymFindByName::,0,[EDI+SSS.NamePtr],[EDI+SSS.NameSize],0
JC .50: ; If it's not the case.
JNSt [EAX+SYM.Status],symDefined,.40:
Msg cc=NE,'6612',EAX,[EAX+SYM.LinePtr] ; Cannot declare structure "!1S" when such symbol was declared at !2@.
.40:; False created but not defined symbol EAX will be removed, because its name belongs to the structure.
ListRemove [EBX+PGM.SymList],EAX
.50:CLC
JMP .90:
; SSS.SegmPtr.
.60:MOV [EDI+SSS.SegmPtr],EDI ; In case of sssSegment it points to itself.
JSt EDX,sssSegment,.90:
; A section is created, perhaps a literal one.
MOV EDX,[%Segment]
TEST EDX
JNZ .80:
; If no explicit %Segment specified, the section will be created in the current segment of the program.
MOV EAX,[EBX+PGM.CurrentSect] ; In case of sssSection .SegmPtr points to parent segment.
MOV EDX,[EAX+SSS.SegmPtr] ; Parent segment.
.80:MOV [EDI+SSS.SegmPtr],EDX
; Section inherits some of its properties from parent segment.
MOV EAX,[EDX+SSS.ClassPtr]
MOV ECX,[EDX+SSS.ClassSize]
MOV [EDI+SSS.ClassPtr],EAX
MOV [EDI+SSS.ClassSize],ECX
MOV EAX,[EDX+SSS.Purpose]
RstSt EAX,sssPurposeLITERAL ; This purpose is not inherited to section.
MOV [EDI+SSS.Purpose],EAX
MOV EAX,[EDX+SSS.Status]
AND EAX,sssCombineMask+sssWidthMask ; ~sssTypeMask
OR [EDI+SSS.Status],EAX
.90:EndProcedure SssCreate
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 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
and it belongs to the last data segment declared in current program
which has the PURPOSE=DATA+LITERALS set. If none such segment is found,
the last segment with PURPOSE=DATA is selected.
@LT will be created first.
SssCreate@LT Procedure DataType, Stm
SssName LocalVar Size=8 ; Room for string "@LT"+decimal number.
SUB ECX,ECX ; Initialization.
LEA EDI,[%SssName]
MOV EAX,"@LTx"
STOSD
XOR EAX,EAX
STOSD
MOV [%ReturnEAX],EAX
Invoke ExpWidthOfDataType::,[%DataType] ; Decide 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 SssFind,sssSection,0,ESI,ECX,0 ; Try to find section by name.
MOV [%ReturnEAX],EAX
JC .20: ; If [@LTx] section does not exist yet.
MOV ECX,[EAX+SSS.SegmPtr]
SetSt [EAX+SSS.Status],sssUsed
SetSt [EAX+SSS.Purpose],sssPurposeLITERAL
JECXZ .18:
SetSt [ECX+SSS.Status],sssUsed
.18:JMP .90: ; Segment or section @LTx was found.
.20: ; New literal section will be created in data segment.
; ESI,ECX=Name, EBX=alignment (1,2,4,8,16,32,64).
MOV EDI,[%Stm]
MOV EDX,[EDI+STM.Program]
; Find the last segment with data purpose where the new literal section will belong to.
; First prefer segment with purpose LITERAL, if any.
ListGetLast [EDX+PGM.SssList]
JZ .70:
.30:JNSt [EAX+SSS.Status],sssSegment,.40:
JNSt [EAX+SSS.Purpose],sssPurposeLITERAL,.40:
JSt [EAX+SSS.Purpose],sssPurposeDATA,.80: ; If data segment found.
.40:ListGetPrev EAX
JNZ .30:
ListGetLast [EDX+PGM.SssList] ; In 2nd pass accept any data segment (without literals preference).
.50:JNSt [EAX+SSS.Status],sssSegment,.60:
JSt [EAX+SSS.Purpose],sssPurposeDATA,.80: ; If data segment found.
.60:ListGetPrev EAX
JNZ .50:
.70: ; No data segment found, create one. Its name will be "@LT".
LEA EAX,[EDX+PGM.Pgmopt]
LEA EDI,[%SssName]
MOV EAX,[EAX+PGMOPT.Status]
AND EAX,pgmoptWidthMask ; Section width will be taken from program width.
OR EAX,sssSegment+sssPrivate+sssNotBSS+sssImplicit+sssUsed
Invoke SssCreate,[%Stm],0,EDI,3,EAX,sssPurposeDATA+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 more 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
PUSH EAX ; New section status.
Invoke SssCreate,[%Stm],EDI,ESI,ECX,EAX,sssPurposeDATA+sssPurposeLITERAL,EBX
MOV ESI,EAX ; ^SSS - just created section.
POP EAX ; CreateSection may have incorrectly set .Status, .Puprpose and .SegmPtr.
MOV [ESI+SSS.Status],EAX
SetSt [ESI+SSS.Purpose],sssPurposeDATA+sssPurposeLITERAL
MOV [ESI+SSS.SegmPtr],EDI
MOV [%ReturnEAX],ESI
.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.
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: ; If does not start with "@RT".
; Current section already is runtime, select the next one.
SUB ECX,3
ADD ESI,3 ; Skip "@RT" in section name.
LodD ESI,Size=ECX
INC EAX
MOV EDX,EDI ; Temporary save pointer to 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 SssFind,sssSection,0,EDI,EDX,0 ; Try to find section by name.
MOV [%ReturnEAX],EAX
JC .30: ; If [@RTx] 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 @RTx 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]
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] ; In 2nd pass accept any code segment without sssPurposeLITERAL preference.
.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, create one. Its name will be "@RT".
MOV EAX,[ECX+PGM.Pgmopt+PGMOPT.Status]
AND EAX,pgmoptWidthMask ; Segment width will be taken from program width.
OR EAX,sssSegment+sssPrivate+sssNotBSS+sssImplicit+sssUsed
Invoke SssCreate,[%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 more literals.
; New runtime section will be created in data segment EBX. EDI,EDX=Name, ECX=PGM.
SetSt [EBX+SSS.Status],sssUsed
MOV EAX,[EBX+SSS.Status]
AND EAX,sssCombineMask|sssWidthMask ; Properties inherited from segment.
OR EAX,sssSection+sssNotBSS+sssImplicit+sssUsed
PUSH EAX ; New section status.
Invoke SssCreate,[%Stm],EBX,EDI,EDX,EAX,sssPurposeCODE+sssPurposeLITERAL,1
MOV ESI,EAX ; ^SSS just created.
POP EAX ; CreateSection may have incorrectly set .Status, Purpose and .SegmPtr.
MOV [ESI+SSS.Status],EAX
SetSt [ESI+SSS.Purpose],sssPurposeCODE+sssPurposeLITERAL
MOV [ESI+SSS.SegmPtr],EBX
MOV [%ReturnEAX],ESI
.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 SssFind,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:
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:
RET
.DRECTVE:
OR EDX,pfcoffSCN_MEM_DISCARDABLE+pfcoffSCN_LNK_INFO+pfcoffSCN_LNK_REMOVE
RET
.IMPORT:
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_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
@LT64,@LT32,@LT16,@LT8,@LT4,@LT2,@LT1,@RT0,@RT1,@RT2,@RT3....Program.Status:pgmLinking is set, which happens at link time, sections are linked not only virtually,
but their contents in .EmitBuffer and .RelocBuffer is appended to the base segment,
and linked section is discarded.
SssLinkSegment Procedure Segm, Program
OrgLow LocalVar ; Segment origin.
OrgHigh LocalVar
LitVal LocalVar ; Ordinal Nr of literal section 1,2,3.. or 64,32,16,8,4,2,1.
LitName LocalVar Size=16 ; Room for section name "@RT1","@RT2","@RT3".. or "@LT64","@LT32".."@LT1".
MOV EBX,[%Program]
MOV EDI,[%Segm]
; Step 1: link the base section which is identical with the segment EDI itself.
SUB EAX,EAX ; Origin starts at VA=0.
SUB EDX,EDX
Invoke SssLinkSection, EDI,EDI,EBX,EAX,EDX
MOV [%OrgLow],EAX
MOV [%OrgHigh],EDX
; Step 2: link nonbase nonliteral sections of segment EDI.
ListGetFirst [EBX+PGM.SssList]
.10: MOV ESI,EAX
JNSt [ESI+SSS.Status],sssSection,.20: ; If ESI is not a section.
CMP ESI,EDI
JE .20: ; Skip the base section which is already linked.
CMP [ESI+SSS.SegmPtr],EDI
JNE .20: ; Skip when the section ESI does not belong to segment EDI.
JSt [ESI+SSS.Purpose],sssPurposeLITERAL,.20: ; Skip literal sections.
Invoke SssLinkSection,ESI,EDI,EBX,[%OrgLow],[%OrgHigh]
MOV [%OrgLow],EAX
MOV [%OrgHigh],EDX
.20: ListGetNext ESI ; Find the next SSS object to EAX.
JNZ .10:
JNSt [EDI+SSS.Purpose],sssPurposeDATA,.50:
; Step 3: link the data literal sections which belong to segment EDI.
MOVD [%LitVal],64 ; Start with @LT section which has the strongest alignment.
MOVD [%LitName],'@LT*'
.30: 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 litereal section name.
Invoke SssFind,sssSection,0,ESI,EDI,0
JC .40: ; If no such section was present.
JNSt [EAX+SSS.Purpose],sssPurposeLITERAL,.40: ; If section [@LT*] was not literal (weird).
MOV EDI,[%Segm]
CMP [EAX+SSS.SegmPtr],EDI
JNE .40: ; If the literal section doesn't belong to our segment.
CMP EAX,EDI
JE .40: ; If this is already linked base section.
Invoke SssLinkSection, EAX,EDI,EBX,[%OrgLow],[%OrgHigh]
MOV [%OrgLow],EAX ; Save origin where the next section will start at.
MOV [%OrgHigh],EDX
.40: SARD [%LitVal],1 ; Prepare for the next data literal section.
JNC .30: ; CF=1 if [@LT1] has just been linked.
.50: MOV EDI,[%Segm]
JNSt [EDI+SSS.Purpose],sssPurposeCODE,.90:
; Step 4: link the code literal sections which belong to segment EDI.
MOVD [%LitVal],0 ; Start with [@RT0].
MOVD [%LitName],'@RT0'
.60: LEA ESI,[%LitName]
MOV EAX,[%LitVal]
LEA EDI,[ESI+3] ; Skip characters '@RT'.
StoD EDI
SUB EDI,ESI
Invoke SssFind, sssSection,0,ESI,EDI,0
JNC .70:
CMPD [%LitVal],0
JE .80:
JMP .90: ; If none section above @RT0 exists, neither the following one can exist. All sections are linked.
.70: JNSt [EAX+SSS.Purpose],sssPurposeLITERAL,.80: ; If section [@RT*] was not literal (weird).
MOV EDI,[%Segm]
CMP [EAX+SSS.SegmPtr],EDI
JNE .80: ; If runtime section doesn't belong to our segment.
CMP EAX,EDI
JE .80: ; If this is already linked base section.
Invoke SssLinkSection,EAX,EDI,EBX,[%OrgLow],[%OrgHigh]
MOV [%OrgLow],EAX
MOV [%OrgHigh],EDX
.80: INCD [%LitVal] ; Prepare for the next code literal section.
JMP .60:
.90: ; Update segment virtual size if emitted size is lower.
JNSt [EBX+PGM.Status],pgmLinking,.99:
MOV EDI,[%Segm]
JNSt [EDI+SSS.Status],sssNotBSS,.99:
BufferRetrieve [EDI+SSS.EmitBuffer]
MOV [EDI+SSS.TopLow],ECX
.99: EndProcedure SssLinkSegment
SssLinkSection virtually links one section Sect of specified Segment at specified origin, i.e. it updates section's VA (bottom & top) when it needs to be relocated at the end of each assembly pass. It also updates offset of symbols which belong to that section.
If PgmPtr.Status:pgmLinking is set, which happens at link time,
the section is linked not only virtually, but also its contents in Sect.EmitBuffer and
Sect.RelocBuffer is appended to the base Segment and the section is then discarded.
Sect.Alignment and used as the new VA of the section.
SssLinkSection Procedure Sect, Segment, PgmPtr, OrigLow, OrigHigh
MOV ESI,[%Sect]
MOV EDI,[%Segment]
MOV EBX,[%PgmPtr]
CMP ESI,EDI ; Test if it is the base section (identical with segment),
JE .90: ; Base section doesn't need relocation.
MOV EAX,[%OrigLow]
MOV EDX,[%OrigHigh]
Invoke ExpAlign::,EAX,[ESI+SSS.Alignment],0 ; Align origin by section alignment.
ADD EAX,ECX ; ECX is size of alignment stuff.
ADC EDX,0 ; EDX:EAX is now new aligned section's bottom.
SUB EAX,[ESI+SSS.BottomLow] ; Subtract old section bottom.
SBB EDX,[ESI+SSS.BottomHigh] ; EDX:EAX is now delta (section relocation value).
JNZ .10:
TEST EAX
JZ .30: ; If delta=0, no section relocation is required.
.10:JSt [EBX+PGM.Status],pgmLastPass,.30: ; If the final pass just ended.
JNSt [EBX+PGM.Status],pgmFixingPass,.15:
TEST EDX
JS .30: ; If the fixing pass just ended, only positive relocation is allowed.
.15:; Relocation of section ESI by delta EDX:EAX.
ADD [ESI+SSS.BottomLow],EAX
ADC [ESI+SSS.BottomHigh],EDX
ADD [ESI+SSS.TopLow],EAX
ADC [ESI+SSS.TopHigh],EDX
MOV ECX,EAX
; Symbols which belong to the section ESI will be relocated by EDX:ECX.
ListGetFirst [EBX+PGM.SymList]
JZ .30:
.20:CMP [EAX+SYM.Section],ESI
JNE .25: ; Skip if the symbol EAX is not from section ESI.
ADD [EAX+SYM.OffsetLow],ECX
ADC [EAX+SYM.OffsetHigh],EDX
RstSt [EAX+SYM.Status],symFixed
.25:ListGetNext EAX
JNZ .20:
.30:JNSt [ESI+SSS.Status],sssNotBSS,.35:
SetSt [EDI+SSS.Status],sssNotBSS ; If section contains static data, segment does so as well.
.35:JNSt [EBX+PGM.Status],pgmLinking,.85:
; At link-time the contents of section ESI will be flushed to segment EDI.
JNSt [EDI+SSS.Status],sssNotBSS,.60: ; Skip if no initialized data to flush.
; Segment contains initialized data or code, contents .EmitBuffer is valid.
; Fill the intersection gap.
MOV EAX,[ESI+SSS.BottomLow] ; Aligned and updated section's bottom.
MOV EDX,[ESI+SSS.BottomHigh]
BufferRetrieve [EDI+SSS.EmitBuffer] ; Segment's contents.
MOV ESI,[%Sect]
SUB EAX,ECX ; Subtract emitted size of segment EDI.
SBB EDX,0 ; EDX:EAX is now size of gap between contents. It may be negative.
JNZ .40:
TEST EAX
JZ .55: ; If no alignment stuff necessary.
.40:TEST EDX
JS .50:
MOV ECX,EAX ; ECX bytes of segment contents will be filled with intersection stuff.
XOR EAX,EAX
JNSt [EDI+SSS.Purpose],sssPurposeCODE,.45:
JSt [EDI+SSS.Purpose],sssPurposeDATA|sssPurposeBSS|sssPurposeSTACK|sssPurposeOptionalMask,.45:
MOV AL,0x90 ; Intersection stuff is NOP only when the segment is pure CODE.
.45:BufferStoreByte [EDI+SSS.EmitBuffer],EAX
LOOP .45:
JMP .55:
.50:NEG EAX
BufferDecrement [EDI+SSS.EmitBuffer],Size=EAX
.55:; Copy section emitted contents to the segment EDI.
BufferRetrieve [ESI+SSS.EmitBuffer]
BufferStore [EDI+SSS.EmitBuffer],ESI,ECX
.60: ; Copy section relocations to the segment EDI.
MOV ESI,[%Sect]
BufferRetrieve [ESI+SSS.RelocBuffer]
JECXZ .80:
MOV EDX,SIZE# RELOC
.65:MOV EAX,[ESI+RELOC.Target] ; EAX is now section which the relocation refers to.
TEST EAX ; RELOC.Target may be 0, which is treated as segment at absolute address 0.
JZ .75:
MOV EAX,[EAX+SSS.SegmPtr]
MOV [ESI+RELOC.Target],EAX ; Replace relocation's section with relocation's segment.
.75:BufferStore [EDI+SSS.RelocBuffer],ESI,EDX
ADD ESI,EDX
SUB ECX,EDX
JA .65: ; The next RELOC object.
.80:MOV ESI,[%Sect]
MOV EAX,[ESI+SSS.TopLow]
MOV EDX,[ESI+SSS.TopHigh]
MOV [EDI+SSS.TopLow],EAX
MOV [EDI+SSS.TopHigh],EDX
ListRemove [EBX+PGM.SssList],ESI ; Discard the linked section ESI.
.85:MOV EAX,[ESI+SSS.BottomLow] ; Althoug the section might have been discarded, its data are still available.
MOV EDX,[ESI+SSS.BottomHigh]
MOV [ESI+SSS.OrgLow],EAX ; Reset VA of the section for the next pass.
MOV [ESI+SSS.OrgHigh],EDX
.90:JNSt [EBX+PGM.Status],pgmLinking,.95:
RstSt [EDI+SSS.Status],sssSection ; The base segment is not a section any longer.
.95:MOV EAX,[EDI+SSS.BottomLow]
MOV EDX,[EDI+SSS.BottomHigh]
MOV [EDI+SSS.OrgLow],EAX ; Reset VA of the segment for the next pass.
MOV [EDI+SSS.OrgHigh],EDX
MOV EAX,[ESI+SSS.TopLow]
MOV EDX,[ESI+SSS.TopHigh]
MOV [%ReturnEAX],EAX
MOV [%ReturnEDX],EDX
EndProcedure SssLinkSection
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.TopLow],EAX
MOV [EDI+SSS.TopHigh],EAX
NOT EAX
MOV [EDI+SSS.BottomLow],EAX
MOV [EDI+SSS.BottomHigh],EAX
ListGetFirst [EBX+PGM.SssList] ; Walk through segments.
.30:MOV ECX,EAX
JNSt [ECX+SSS.Status],sssSegment,.70:
MOV EAX,[ECX+SSS.GroupPtr]
CMP EDI,EAX
JE .36:
TEST EAX
JZ .70:
MOV EAX,[EAX+SSS.GroupPtr] ; EAX might be a group from linked module.
CMP EDI,EAX
JNE .70:
.36:; ECX is a segment of group EDI. Adjust group's .Bottom and .Top.
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 bottom was updated.
.50:MOV EDX,[ECX+SSS.TopHigh]
MOV EAX,[ECX+SSS.TopLow]
CMP EDX,[EDI+SSS.TopHigh]
JB .70:
JA .60:
CMP EAX,[EDI+SSS.TopLow]
JBE .70:
.60:MOV [EDI+SSS.TopLow],EAX
MOV [EDI+SSS.TopHigh],EDX ; Group top was updated.
.70:ListGetNext ECX ; The next segment.
JNZ .30:
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, set group .Bottom and .Top back to 0.re>
MOV [EDI+SSS.BottomLow],EAX
MOV [EDI+SSS.BottomHigh],EAX
.90:EndProcedure SssResizeGroup
.Bottom will be used to fixup symbols and relocations which may refer to it.
| SSS object types | sssExtern | sssGroup | sssSegment +sssPublic |
sssSegment +sssStack | sssSegment +sssCommon | sssSegment +sssPrivate |
|---|---|---|---|---|---|---|
| sssExtern | COMMON | PRIVATE | PRIVATE | PRIVATE | PRIVATE | PRIVATE |
| sssGroup | PRIVATE | PUBLIC | 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 only affects members SSS.Bottom, SSS.Top, SSS.SegmPtr, SSS.GroupPtr of the input object.
Emitted contents and relocations are copied from the input object to BasePgm but not fixed up yet.
Relocation origins will be fixed up later, in RelocFixup
invoked from PgmCombine.87:.
Linked SssObject's references (members .GroupPtr and .SegmPtr) are redirected to their new position on
BasePgm.SssList.
Global symbols and relocations of linked program are not updated here.
SssCombine Procedure SssObject, BasePgm
MOV ESI,[%SssObject]
MOV EBX,[%BasePgm]
MOV EAX,[ESI+SSS.Status]
AND EAX,sssSegment+sssExtern+sssGroup
Dispatch EAX,sssSegment,sssExtern,sssGroup
JMP .End: ; Ignore other SSS types (actually no section or structure can pass thru PgmLoad).
.sssGroup: ; Groups always combine as PUBLIC. They have no contents.
Invoke SssFind,sssGroup,0,[ESI+SSS.NamePtr],[ESI+SSS.NameSize],EBX ; Search in BasePgm.
JNC .G5: ; Do not copy linked group to base program if it already exists there.
ListStore [EBX+PGM.SssList],ESI ; Copy group ESI specified in linked module.
.G5:MOV [EAX+SSS.GroupPtr],EAX ; Pointer to an existing group in the base program (to itself).
MOV [ESI+SSS.GroupPtr],EAX ; Make abandoned linked group refer to the base group with the same name.
JMP .End:
.sssExtern: ; Extern pseudosegments always combine as COMMON. They have no contents.
; If the pseudosegment belons to imported symbol, the combined pseudosegment will be flagged sssImport.
Invoke SssFind,sssExtern,0,[ESI+SSS.NamePtr],[ESI+SSS.NameSize],EBX ; Search in BasePgm.
JNC .E5: ; Do not copy linked extern pseudosegment to base program, if it already exists there.
ListStore [EBX+PGM.SssList],ESI ; Otherwise add the linked extern to BasePgm.
.E5:MOV [EAX+SSS.SegmPtr],EAX ; Pointer to an existing EXTERN in the base program (to itself).
MOV [ESI+SSS.SegmPtr],EAX ; Make abandoned linked extern refer to the base extern with the same name.
JNSt [ESI+SSS.Status],sssImport,.End:
SetSt [EAX+SSS.Status],sssImport
JMP .End:
.sssSegment: ; Segments combine according to their COMBINE= property.
MOV EAX,sssCombineMask
AND EAX,[ESI+SSS.Status]
Dispatch EAX,sssPublic,sssCommon,sssStack
; Undispatched object uses combine method PRIVATE.
.PRIVATE: ; PRIVATE combining. ; Linked non-existing-yet or private segment ESI
; will be copied to base Pgm.SssList, although homonymous segment may already happily exist there.
ListStore [EBX+PGM.SssList],ESI ; Copy old segment to base program including its contents.
MOV [EAX+SSS.SegmPtr],EAX ; Let the new just linked segment refer to itself.
MOV [ESI+SSS.SegmPtr],EAX ; Also let the abandoned linked segment refer to the new location in BasePgm.
XOR EAX,EAX
MOV [ESI+SSS.EmitBuffer],EAX ; Invalidate contents of old linked segment.
MOV [ESI+SSS.RelocBuffer],EAX
JMP .End:
.sssCommon: ; Combine method COMMON.
Invoke SssFind,sssSegment,sssCommon,[ESI+SSS.NamePtr],[ESI+SSS.NameSize],EBX
JNC .COMMON: ; Only if both segments have COMBINE=COMMON.
Invoke SssFind,sssSegment,sssPublic|sssStack,[ESI+SSS.NamePtr],[ESI+SSS.NameSize],EBX
JNC .PUBLIC: ; Combine COMMON segment with PUBLIC|STACK segment as PUBLIC.
JMP .PRIVATE:; Combine COMMON segment with PRIVATE segment as PRIVATE.
.sssStack: ; Combine method STACK.
Invoke SssFind,sssSegment,sssStack,[ESI+SSS.NamePtr],[ESI+SSS.NameSize],EBX
JNC .PUBLIC: ; Segments with stack-combine method link as public (they are concatenated).
Invoke SssFind,sssSegment,sssPublic|sssCommon,[ESI+SSS.NamePtr],[ESI+SSS.NameSize],EBX
JNC .PUBLIC: ; Combine STACK segment with PUBLIC|COMMON segment as PUBLIC.
JMP .PRIVATE:; Combine STACK segment with PRIVATE segment as PRIVATE.
.sssPublic: ; Combine method PUBLIC.
Invoke SssFind,sssSegment,sssPublic,[ESI+SSS.NamePtr],[ESI+SSS.NameSize],EBX
JNC .PUBLIC: ; Combine PUBLIC segment with PUBLIC segment as PUBLIC.
Invoke SssFind,sssSegment,sssStack|sssCommon,[ESI+SSS.NamePtr],[ESI+SSS.NameSize],EBX
JNC .PUBLIC: ; Combine PUBLIC segment with STACK|COMMON segment as PUBLIC.
JMP .PRIVATE:; Combine PUBLIC segment with PRIVATE segment as PRIVATE.
.COMMON: ; COMMON combining. Both linked (ESI) and base (EAX) segments remain on bottom=0.
MOV EDI,EAX ; Base segment to which is the segment ESI combined.
MOV [ESI+SSS.SegmPtr],EDI ; Let abandoned linked segment refer to base segment.
MOV EDX,[ESI+SSS.TopHigh] ; New base segment 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 .End:
.PUBLIC: ; PUBLIC combining. Linked segment ESI will be aligned and appended to base segment EAX.
MOV EDI,EAX
MOV [ESI+SSS.SegmPtr],EDI ; Let the linked segment refer to the existing base segment EDI.
; Merge properties of public segment combination.
MOV EAX,[ESI+SSS.Purpose] ; Linked purpose.
OR [EDI+SSS.Purpose],EAX
MOV EAX,sssNotBSS
AND EAX,[ESI+SSS.Status]
OR [EDI+SSS.Status],EAX
MOV EAX,[ESI+SSS.Alignment]
CMP EAX,[EDI+SSS.Alignment]
JNA .P2:
MOV [EDI+SSS.Alignment],EAX ; Use greater alignment from both segments, if they are different.
.P2:JSt [ESI+SSS.Purpose],sssPurposeDRECTVE|sssPurposeOptionalMask,.P4:
; Width of segments with standard purpose CODE|DATA|BSS|STACK must match.
MOV EAX,sssWidthMask
MOV EDX,EAX
AND EAX,[ESI+SSS.Status]
JZ .P4: ; Segment from the linked segment may have unspecified width, this is OK.
AND EDX,[EDI+SSS.Status]
CMP EAX,EDX ; Check segment width match.
JE .P4:
Msg '7718',ESI ; Cannot link segments [!1S] which have different width.
JMP .End:
.P4:MOV EAX,[EDI+SSS.TopLow]
MOV EDX,[EDI+SSS.TopHigh] ; EDX:EAX is virtual base segment size.
Invoke ExpAlign::,EAX,[ESI+SSS.Alignment],0 ; Align base top according to linked-segment alignment.
ADD EAX,ECX ; Add alignment stuff size to the base top.
ADC EDX,0 ; EDX:EAX is now aligned bottom of linked segment ESI, i.e. delta for future fixup.
ADD [ESI+SSS.BottomLow],EAX
ADC [ESI+SSS.BottomHigh],EDX
ADD [ESI+SSS.TopLow],EAX
ADC [ESI+SSS.TopHigh],EDX
Msg cc=NZ,'8525',ESI ; Size of segment [!1S] exceeded 4 GB.
MOV EAX,[ESI+SSS.TopLow]
MOV EDX,[ESI+SSS.TopHigh]
MOV [EDI+SSS.TopLow],EAX ; Update the new top of base segment.
MOV [EDI+SSS.TopHigh],EDX
MOV EBX,ESI
; Copy the raw contents of combined segment EBX to base segment EDI.
BufferRetrieve [EDI+SSS.EmitBuffer]
SUB ECX,[EBX+SSS.BottomLow]
JBE .P5:
; Emitted contents in base segment is above its virtual size. Truncate.
BufferDecrement [EDI+SSS.EmitBuffer],Size=ECX
JMP .P7:
.P5: ; Emitted contents in base segment is below virtual size. Supplement some stuff.
NEG ECX
JZ .P7: ; If emitted contents in base segment exactly matches its virtual size.
XOR EDX,EDX ; Default alignment stuff is 0x00.
JNSt [EBX+SSS.Purpose],sssPurposeCODE,.P6:
JNSt [EDI+SSS.Purpose],sssPurposeCODE,.P6:
JSt [EDI+SSS.Purpose],sssPurposeDATA|sssPurposeBSS|sssPurposeSTACK,.P6:
MOV DL,0x90 ; Alignment stuff is 0x90 only when both combined segments have pure CODE purpose.
.P6:BufferStoreByte [EDI+SSS.EmitBuffer],EDX ; Base segment alignment padding.
DEC ECX
JNZ .P6:
.P7:BufferRetrieve [EBX+SSS.EmitBuffer]
BufferStore [EDI+SSS.EmitBuffer],ESI,ECX
; Copy relocations of combined segment EBX to base segment EDI.
BufferRetrieve [EBX+SSS.RelocBuffer]
BufferStore [EDI+SSS.RelocBuffer],ESI,ECX
.End: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.
.10:BufferStoreByte EBX,0x00
DEC ECX
JNZ .10:
JMP .90:
.Nop:PROC ; Emit EDX bytes of NOP1..NOP9 to buffer EBX. EDI=^SSS.
[.data] ; Following table defines offsets of NOP opcode in 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 real NOP opcode (ECX bytes long).
BufferStore EBX,ESI,ECX
Msg cc=C,'9314',SssEmitAlignment ;Allocation error storing to buffer in !1H.
RET
ENDP .Nop:
.60:; Alignment stuff in code section.
MOV EDX,9
JECXZ .90:
SUB ECX,EDX
JB .80:
PUSH ECX
CALL .Nop:
POP ECX
JMP .60:
.80:ADD EDX,ECX
CALL .Nop:
.90:EndProcedure SssEmitAlignment
Sss.Org
before storing the data from %EmitBuffer to Stm.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.
Alignment can also be negative in statements like $ EQU $-8.
SssEmit Procedure Sss, EmitBuffer, RelocBuffer, Alignment
MOV EDI,[%Sss]
MOV EAX,[%Alignment]
CDQ
BufferResize [EDI+SSS.EmitBuffer],EAX
ADD EAX,[EDI+SSS.OrgLow]
ADC EDX,[EDI+SSS.OrgHigh] ; EDX:EAX is now the new aligned .Org.
CMP EDX,[EDI+SSS.BottomHigh]
JA .20:
JB .E6555:
CMP EAX,[EDI+SSS.BottomLow]
JAE .20:
.E6555:Msg '6555' ; Offset out of section limits.
MOV EAX,[EDI+SSS.BottomLow] ; Negative %Alignment underflowed, let Sss.Org = Sss.Bottom.
MOV EDX,[EDI+SSS.BottomHigh]
.20: MOV [EDI+SSS.OrgLow],EAX ; Sss.Org is now aligned as requested by %Alignment.
MOV [EDI+SSS.OrgHigh],EDX
SUB EAX,[EDI+SSS.TopLow]
SBB EDX,[EDI+SSS.TopHigh]
JB .30:
; Aligned .Org is EAX bytes above old .Top,
; the gap is virgin yet and it will be populated with alignment stuff.
MOV EBX,EAX
BufferDecrement [EDI+SSS.EmitBuffer],Size=EBX
Invoke SssEmitAlignment, EDI,EBX,[EDI+SSS.EmitBuffer]
.30: MOV EBX,[EDI+SSS.OrgLow]
MOV EDX,[EDI+SSS.OrgHigh]
BufferRetrieve [%EmitBuffer]
JECXZ .50:
ADD [EDI+SSS.OrgLow],ECX
ADC [EDI+SSS.OrgHigh],0
BufferStore [EDI+SSS.EmitBuffer],ESI,ECX
SetSt [EDI+SSS.Status],sssUsed
.50: ; Now store relocations, if any.
BufferRetrieve [%RelocBuffer] ; If not the final pass, RelocBuffer was not specified.
JECXZ .70: ; If %RelocBuffer is unspecified or empty.
; There are unpatched relocations in statement. ESI points to RELOC, EDX:EBX is aligned Org before emit.
.60: ADD [ESI+RELOC.OrgLow],EBX
ADC [ESI+RELOC.OrgHigh],EDX ; Relocation position is now patched.
MOV [ESI+RELOC.Section],EDI
BufferStore [EDI+SSS.RelocBuffer],ESI,SIZE#RELOC
ADD ESI,SIZE# RELOC
SUB ECX,SIZE#RELOC
JA .60: ; If there are more relocations in one statement.
Invoke RelocUniq::,[EDI+SSS.RelocBuffer]
.70: ; Update Sss.Top if it is below the new Sss.Org.
MOV EAX,[EDI+SSS.OrgLow]
MOV EDX,[EDI+SSS.OrgHigh]
CMP EDX,[EDI+SSS.TopHigh]
JB .90:
JA .80:
CMP EAX,[EDI+SSS.TopLow]
JBE .90:
.80: MOV [EDI+SSS.TopLow],EAX
MOV [EDI+SSS.TopHigh],EDX
.90:EndProcedure SssEmit
ENDPROGRAM sss