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