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