News:

Masm32 SDK description, downloads and other helpful links
Message to All Guests

Main Menu

Stackframes

Started by JK, April 05, 2021, 09:46:30 PM

Previous topic - Next topic

jj2007

Quote from: JK on April 06, 2021, 08:29:34 AMNow RSP is at the bottom of all data, which must not be overwritten. It can be freely used for whatever i want (as long as 16 alignment is ensured before calls). According to what i read about the 64 bit ABI, such a layout is not forbidden. No one forces you to build a stack frame in a way, that you must not change RSP inside procedures, this is a decision taken for optimisation reasons, but it´s not a must IMHO.

Be careful. Use the FillShadowSpace macro to see what happens to your shadow space if you call one of the rare WinAPI functions (Sleep, for example) that actually use it :cool:
  FillShadowSpace
   int 3
   push rsi
   push rdi
  jinvoke Sleep, 100
  pop rdi
  pop rsi

hutch--

John has given the same warning that I have DON'T alter the stack with PUSH/POP instructions. If you really have to preserve data in that manner, use a LOCAL and MOV the data to it in the normal manner,

mov reg, data
mov localname, reg

Unless you enjoy p*ssing around trying to find why that app will not start, don't make a mess of the stack.

A couple of basic things here, creative genius and personal preference certainly have their place but that place is not how the mechanics of the operating system work. Get the mechanics of the OS as they are designed to work THEN apply creative genius and personal preference and you will get what you are after.

jj2007

Quote from: hutch-- on April 06, 2021, 01:08:31 PMUnless you enjoy p*ssing around trying to find why that app will not start, don't make a mess of the stack

Practically all apps will start, unfortunately. The problem will bite you much later, as most WinAPI calls don't use the shadow space. Until now, I found only Sleep() being a shadow space user.

This is Win7-64. I wonder how the situation is on Win10, built with more recent compilers... any evidence from the UAsm/AsmC/ML64 developers?

hutch--

With about 4 years of practice, I hve yet to see misaligned procedures do anything else than not start. No doubt the OS loader will do something but the notion of "start" is the app appearing on the screen and with misaligned code via stack blunders, you try and run it and nothing happens and you don't get told anything either.

Effectively the OS loader spits the dummy and the app will not run.

This is on Win 10 64 bit which I have been using for the last 5 years.

jj2007

Quote from: hutch-- on April 06, 2021, 08:47:59 PMI hve yet to see misaligned procedures do anything else than not start.

Most developers grasp quickly the concept of align 16. The real issue are the subtle problems that may arise when you push+pop pairwise. It looks good because the stack remains aligned for use with xmm regs, and most of the time nothing bad happens, but...

KradMoonRa

Still learning the bad way...

Part of the assembly that I' have to code, and to research plataform performance SIMD beneficts in UASM.

Not only I have to align the stack before a call to procedure, I have to save stack space for all the defaults arguments and only the defaults arguments for the platform convention, 4*8+1*8 Win, 6*8+1*8 Lin.

Some times using sp as beneficts.
Some times abusing sp as segment faults.

This run/runs perfectly....


; Constructor
procstart _uasm_CPUFeatures_Init, callconv, void, < >, < >, infolevel:dword
    ifdef __x32__
        ifdef __windows__
            mov     __uasm_dt_CPUFeatures_infolevel,       dp0()
            xor             dp0(),                    dp0()
        endif
        ifdef __unix__
            mov     __uasm_dt_CPUFeatures_infolevel,       infolevel
            ;mov             [dp0()+4],                null
        endif
    endif ;__x32__
    ifdef __x64__
            mov     __uasm_dt_CPUFeatures_infolevel,       dp0()
            xor             dp0(),                    dp0()
    endif ;__x64__

    ifdef __x32__
            push                ebx
                                                                            ; detect if cpuidinstruction supported by microprocessor:
            pushfd
            pop                 eax
            btc                 eax,                    21                  ; check if cpuidbit can toggle
            push                eax
            popfd
            pushfd
            pop                 ebx
            xor                 ebx,                    eax
            bt                  ebx,                    21
            jc                  CPUInitNoID                                 ; cpuidnot supported
            xor                 eax,                    eax                 ; 0
            ; /* %eax=00H, %ecx %ebx */
            mov     __uasm_dt_CPUFeatures_CPUID,             true

            cpuid                                                           ; get number of cpuidfunctions
            test                eax,                    eax
            jnz                 CPUInitIdentificable                        ; function 1 not supported
    CPUInitNoID:
            .if (__uasm_dt_CPUFeatures_infolevel >= 1) ;infolevel >= 1
            push                edi
            ; processor has no CPUID
            mov      dword ptr [edi],                   '8038'              ; Write text '80386 or 80486'
            mov      dword ptr [edi+4],                 '6 or'
            mov      dword ptr [edi+8],                 ' 804'
            mov      dword ptr [edi+12],                '86'                ; End with 0

            mov     __uasm_dt_CPUFeatures_ProcessorName,     edi            ; Pointer to result
            pop                 edi
            .endif
            pop                 ebx
            jmp                 CPUInitEND
    endif ;__x32__

    ifdef __x32__
            pop                 ebx
    CPUInitIdentificable:
            push                ebp
            mov                 ebp,                esp
            sub                 esp,                16          ; 3*4=12+4 Align 8
            ;mov                [esp],               esp
            mov                [esp],               ebx
            mov                [esp+4],             esi
            mov                [esp+8],             edi
            ;push                esp
            ;push                ebp
            ;push                ebx
            ;push                esi
            ;push                edi
    endif ;__x32__
    ifdef __x64__
            push                rbp
            mov                 rbp,                rsp
        ifdef __windows__
            sub                 rsp,                64          ; 7*8=56+8 Align 16
        else
            sub                 rsp,                48          ; 5*8=40+8 Align 16
        endif
            ;mov                [rsp],               rsp
            mov                [rsp],               rbx
        ifdef __windows__
            mov                [rsp+8],             rsi
            mov                [rsp+16],            rdi
            mov                [rsp+24],            r11
            mov                [rsp+32],            r12
            mov                [rsp+40],            r14
            mov                [rsp+48],            r15
        else
            mov                [rsp+8],             r11
            mov                [rsp+16],            r12
            mov                [rsp+24],            r14
            mov                [rsp+32],            r15
        endif
            ;push                rbx
        ;ifdef __windows__
            ;push                rsi
            ;push                rdi
        ;endif
            ;push                rsp
            ;push                rbp
            ;push                r11
            ;push                r12
            ;push                r14
            ;push                r15
    endif ;__x64__
    ;.........................blablablablablas------------------
    ;.........................blablablablablas------------------
    ;.........................blablablablablas------------------
    ;.........................blablablablablas------------------
    ;.........................blablablablablas------------------

not_supported:
    ifdef __x32__
            mov                 edi,               [esp+8]
            mov                 esi,               [esp+4]
            mov                 ebx,               [esp]
            ;mov                 esp,               [esp]
            add                 esp,                16
            mov                 esp,                ebp
            pop                 ebp
            ;pop                edi
            ;pop                esi
            ;pop                ebx
            ;pop                ebp
            ;pop                esp
    endif ;__x32__
    ifdef __x64__
        ifdef __windows__
            mov                 r15,               [rsp+48]
            mov                 r14,               [rsp+40]
            mov                 r12,               [rsp+32]
            mov                 r11,               [rsp+24]
            mov                 rdi,               [rsp+16]
            mov                 rsi,               [rsp+8]
        else
            mov                 r15,               [rsp+32]
            mov                 r14,               [rsp+24]
            mov                 r12,               [rsp+16]
            mov                 r11,               [rsp+8]
        endif
            mov                 rbx,               [rsp]
            ;mov                 rsp,               [rsp]
        ifdef __windows__
            add                 rsp,                64
        else
            add                 rsp,                48
        endif
            mov                 rsp,                rbp
            pop                 rbp
            ;pop                r15
            ;pop                r14
            ;pop                r12
            ;pop                r11
        ;ifdef __windows__
            ;pop                rdi
            ;pop                rsi
        ;endif
            ;pop                rbx
            ;pop                rbp
            ;pop                rsp
    endif ;__x64__
    CPUInitEND:  ; finished
            ret
procend



public main
main proc (dword) argc:dword, argv:ptr ptr byte, envp:ptr ptr byte
    ; space for 4 arguments + 16byte aligned stack
    sub             rsp,            28h
    call            _uasm_CPUFeatures_Init
    call            _uasm_CPUFeatures_ProcessorName
    mov             rp0(),          rret()
    call            printf
    mov             rp0(),          cstr(stringwith, " With caches sizes:"," L1= ","%I64d"," bytes, L2= ", "%I64d"," bytes, L3= ","%I64d"," bytes.")
    mov             rp1(),          __uasm_dt_CPUFeatures_DataCacheSizeL1
    mov             rp2(),          __uasm_dt_CPUFeatures_DataCacheSizeL2
    mov             rp3(),          __uasm_dt_CPUFeatures_DataCacheSizeL3
    call            printf
    mov             rp0(),          cstr(datawith, 10,"With:",10,0)
    call            printf
    call            _uasm_CPUFeatures_Fin
    xor             eax,            eax
    xor             ecx,            ecx
    call            exit
    add             rsp,            28h
    ret
main endp
The uasmlib

JK

@jj (re post #15)

i give another example, consider this code:
;option stackbase:RBP
;option win64:7

include windows.inc
includelib kernel32.lib
includelib user32.lib                                 ;DrawTextEx


.code


testit proc uses rbx rsi rdi, x:dword, f4:real4, z:qword, f8:real4, n:qword
;*************************************************************************************
; proc
;*************************************************************************************
local tx :qword                                       ;-8h     size 8
LOCAL ty :qword                                       ;-10h    size 8
                                                      ;total size of locals = 10h

  nop
int 3

  lea rax, x                                          ;address of first argument
  lea rax, tx                                         ;address of first local


mov rax, 0ABCDEFh
push rax
push rax
push rax
push rax
push rax
push rax

  invoke Sleep, 10
;  invoke DrawTextEx, 0, 0, 0, 0, 0, 0                 ;sub rsp,38 -> sub rsp,48 (6 arguments)

pop RAX
pop RAX
pop RAX
pop RAX
pop RAX
pop RAX


ret


testit endp


;*************************************************************************************


start proc uses rbx rsi rdi ;r15
;*************************************************************************************
; main proc
;*************************************************************************************
local x  :dword                                       ;-4h      size 4
local f4 :real4                                       ;-8h      size 4
local z  :Qword                                       ;-10h     size 8
local f8 :REAL8                                       ;-18h     size 8
local n  :qword                                       ;-20h     size 8
local r  :RECT                                        ;-30h     size 10h


  nop                                                 ;procedure code starts here
  lea rax, x                                          ;address of first local

  nop                                                 ;invoke starts here
  invoke testit, x, 1.4, z, f8, n                     ;5 arguments
  invoke ExitProcess, 0
  ret


start endp


end start


please compile it once like it is and another time with options set (remove comment in the first two lines), the resulting code will be fundamentally different in how the stack pointer moves.

Version 1:
;*************************************************************************************
; wo. any options set -> RBP points to top of locals, 1. arg is RBP + 10
;*************************************************************************************
;00000000012C1000 | 55                       | push rbp                                |
;00000000012C1001 | 48:8BEC                  | mov rbp,rsp                             |
;00000000012C1004 | 48:83C4 F0               | add rsp,FFFFFFFFFFFFFFF0                | 10h for locals
;00000000012C1008 | 53                       | push rbx                                |
;00000000012C1009 | 56                       | push rsi                                |
;00000000012C100A | 57                       | push rdi                                |
;00000000012C100B | 90                       | nop                                     |
;00000000012C100C | CC                       | int3                                    |
;00000000012C100D | 48:8D45 10               | lea rax,qword ptr ss:[rbp+10]           | 1. argument
;00000000012C1011 | 48:8D45 F8               | lea rax,qword ptr ss:[rbp-8]            | 1. local
;00000000012C1015 | 48:C7C0 EFCDAB00         | mov rax,ABCDEF                          |
;00000000012C101C | 50                       | push rax                                |
;00000000012C101D | 50                       | push rax                                |
;00000000012C101E | 50                       | push rax                                |
;00000000012C101F | 50                       | push rax                                |
;00000000012C1020 | 50                       | push rax                                |
;00000000012C1021 | 50                       | push rax                                |
;00000000012C1022 | 48:83EC 20               | sub rsp,20                              | make room for 4 arguments
;00000000012C1026 | B9 0A000000              | mov ecx,A                               | arg 1
;00000000012C102B | FF15 CF0F0000            | call qword ptr ds:[<&Sleep>]            |
;00000000012C1031 | 48:83C4 20               | add rsp,20                              | correct stack
;00000000012C1035 | 58                       | pop rax                                 |
;00000000012C1036 | 58                       | pop rax                                 |
;00000000012C1037 | 58                       | pop rax                                 |
;00000000012C1038 | 58                       | pop rax                                 |
;00000000012C1039 | 58                       | pop rax                                 |
;00000000012C103A | 58                       | pop rax                                 |
;00000000012C103B | 5F                       | pop rdi                                 |
;00000000012C103C | 5E                       | pop rsi                                 |
;00000000012C103D | 5B                       | pop rbx                                 |
;00000000012C103E | C9                       | leave                                   |
;00000000012C103F | C3                       | ret                                     |
;00000000012C1040 | 55                       | push rbp                                |
;00000000012C1041 | 48:8BEC                  | mov rbp,rsp                             |
;00000000012C1044 | 48:83C4 D0               | add rsp,FFFFFFFFFFFFFFD0                |
;00000000012C1048 | 53                       | push rbx                                |
;00000000012C1049 | 56                       | push rsi                                |
;00000000012C104A | 57                       | push rdi                                |
;00000000012C104B | 90                       | nop                                     |
;00000000012C104C | 48:8D45 FC               | lea rax,qword ptr ss:[rbp-4]            | 1. local (DWORD)
;00000000012C1050 | 90                       | nop                                     | invoke starts here
;00000000012C1051 | 48:83EC 30               | sub rsp,30                              | make room for 5 arguments
;00000000012C1055 | 8B4D FC                  | mov ecx,dword ptr ss:[rbp-4]            | arg 1
;00000000012C1058 | B8 3333B33F              | mov eax,3FB33333                        |
;00000000012C105D | 66:0F6EC8                | movd xmm1,eax                           | arg 2
;00000000012C1061 | 4C:8B45 F0               | mov r8,qword ptr ss:[rbp-10]            | arg 3
;00000000012C1065 | 66:0F6E5D E8             | movd xmm3,dword ptr ss:[rbp-18]         | arg 4
;00000000012C106A | 48:8B45 E0               | mov rax,qword ptr ss:[rbp-20]           |
;00000000012C106E | 48:894424 20             | mov qword ptr ss:[rsp+20],rax           | arg 5
;00000000012C1073 | E8 88FFFFFF              | call uasm_stack_test_64.12C1000         |
;00000000012C1078 | 48:83C4 30               | add rsp,30                              | correct stack
;00000000012C107C | 48:83EC 20               | sub rsp,20                              | make room for 4 arguments
;00000000012C1080 | 33C9                     | xor ecx,ecx                             |
;00000000012C1082 | FF15 800F0000            | call qword ptr ds:[<&RtlExitUserProcess |
;00000000012C1088 | 48:83C4 20               | add rsp,20                              |
;00000000012C108C | 5F                       | pop rdi                                 |
;00000000012C108D | 5E                       | pop rsi                                 |
;00000000012C108E | 5B                       | pop rbx                                 |
;00000000012C108F | C9                       | leave                                   |
;00000000012C1090 | C3                       | ret                                     |


Version 2:
;*************************************************************************************
; Option stackbase:rbp + option win65:7
;*************************************************************************************
;00000000013A1000 | 894C24 08                | mov dword ptr ss:[rsp+8],ecx            | copy arg 1 to shadow space
;00000000013A1004 | 48:55                    | push rbp                                |
;00000000013A1006 | 53                       | push rbx                                |
;00000000013A1007 | 56                       | push rsi                                |
;00000000013A1008 | 57                       | push rdi                                |
;00000000013A1009 | 48:83EC 38               | sub rsp,38                              | make room for locals (10h)
;                                                                                      | + next call (20h)
;                                                                                      | + 8 bit stack alignment = 38h
;00000000013A100D | 48:8D6C24 30             | lea rbp,qword ptr ss:[rsp+30]           |
;00000000013A1012 | 90                       | nop                                     |
;00000000013A1013 | CC                       | int3                                    |
;00000000013A1014 | 48:8D45 30               | lea rax,qword ptr ss:[rbp+30]           | 1. argument
;00000000013A1018 | 48:8D45 F8               | lea rax,qword ptr ss:[rbp-8]            | 1. local
;00000000013A101C | 48:C7C0 EFCDAB00         | mov rax,ABCDEF                          |
;00000000013A1023 | 50                       | push rax                                |
;00000000013A1024 | 50                       | push rax                                |
;00000000013A1025 | 50                       | push rax                                |
;00000000013A1026 | 50                       | push rax                                |
;00000000013A1027 | 50                       | push rax                                |
;00000000013A1028 | 50                       | push rax                                |
;00000000013A1029 | B9 0A000000              | mov ecx,A                               | arg 1
;00000000013A102E | FF15 CC0F0000            | call qword ptr ds:[<&Sleep>]            |
;00000000013A1034 | 58                       | pop rax                                 |
;00000000013A1035 | 58                       | pop rax                                 |
;00000000013A1036 | 58                       | pop rax                                 |
;00000000013A1037 | 58                       | pop rax                                 |
;00000000013A1038 | 58                       | pop rax                                 |
;00000000013A1039 | 58                       | pop rax                                 |
;00000000013A103A | 48:8D65 08               | lea rsp,qword ptr ss:[rbp+8]            |
;00000000013A103E | 5F                       | pop rdi                                 |
;00000000013A103F | 5E                       | pop rsi                                 |
;00000000013A1040 | 5B                       | pop rbx                                 |
;00000000013A1041 | 5D                       | pop rbp                                 |
;00000000013A1042 | C3                       | ret                                     |
;00000000013A1043 | 48:55                    | push rbp                                |
;00000000013A1045 | 53                       | push rbx                                |
;00000000013A1046 | 56                       | push rsi                                |
;00000000013A1047 | 57                       | push rdi                                |
;00000000013A1048 | 48:83EC 68               | sub rsp,68                              | make room for locals + next call
;00000000013A104C | 48:8D6C24 50             | lea rbp,qword ptr ss:[rsp+50]           |
;00000000013A1051 | 90                       | nop                                     |
;00000000013A1052 | 48:8D45 E4               | lea rax,qword ptr ss:[rbp-1C]           | 1. local (DWORD)
;00000000013A1056 | 90                       | nop                                     | invoke starts here
;00000000013A1057 | 8B4D E4                  | mov ecx,dword ptr ss:[rbp-1C]           | arg 1
;00000000013A105A | B8 3333B33F              | mov eax,3FB33333                        |
;00000000013A105F | 66:0F6EC8                | movd xmm1,eax                           | arg 2
;00000000013A1063 | 4C:8B45 F8               | mov r8,qword ptr ss:[rbp-8]             | arg 3
;00000000013A1067 | 66:0F6E5D F0             | movd xmm3,dword ptr ss:[rbp-10]         | arg 4
;00000000013A106C | 48:8B45 E8               | mov rax,qword ptr ss:[rbp-18]           |
;00000000013A1070 | 48:894424 20             | mov qword ptr ss:[rsp+20],rax           | arg 5
;00000000013A1075 | E8 86FFFFFF              | call uasm_stack_test_64.13A1000         |
;00000000013A107A | 33C9                     | xor ecx,ecx                             |
;00000000013A107C | FF15 860F0000            | call qword ptr ds:[<&RtlExitUserProcess |
;00000000013A1082 | 48:8D65 18               | lea rsp,qword ptr ss:[rbp+18]           |
;00000000013A1086 | 5F                       | pop rdi                                 |
;00000000013A1087 | 5E                       | pop rsi                                 |
;00000000013A1088 | 5B                       | pop rbx                                 |
;00000000013A1089 | 5D                       | pop rbp                                 |
;00000000013A108A | C3                       | ret                                     |


when stepping through the code you will see, that in the first version all pushes in "testit" are left as the are, while in the second version 4 of them (Sleep´s shadow space) are overwritten by zero!

The first version makes room for the arguments of a new call before each new call and corrects the stack to where is was after the call. Therefore the called procedure can do with it´s arguments and it´s shadow space whatever it pleases, without affecting, what has been pushed before.

The second version sets RSP once (and for all) at procedure entry to a value, which will be suitable for all calls in this procedure. That is, it pre-calculates (see, what happens to "sub rsp,38" if you uncomment the DrawTextEx line, it turns to "sub rsp,48") the highest space needed for the arguments of coming calls, then adds the required space for the locals of this procedure and finally adjusts RSP to 16 bit. This saves all "sub RSP, ... - add RSP, ..." pairs enclosing calls in the first version. It is more efficient in this respect!

But with this approach pushes before calls are not possible, because the called procedure might overwrite, what has been pushed (your example).

So my claim is: with a stack pointer handling like in version 1 pushes and pops aren´t a problem, with version 2 pushes and pops will definitely cause problems, as you (and hutch and others) pointed out.


JK

jj2007

Quote from: JK on April 07, 2021, 12:10:25 AMplease compile it once like it is

Sorry, I can't compile it. My environment variables are not set to any path, so "include windows.inc" will not work. Besides, there are three or four competing 64-bit SDKs around (plus my own), and the info on how to use them is scattered all over the place. Afaik none of them has an installer comparable to the Masm32 SDK, so I watch with awe what all of you are doing, but if I assemble 64-bit code it's with JBasic only...

Quote from: jj2007 on March 30, 2021, 02:08:44 AM
Attached the installer of the JBasic library

hutch--

I am still fascinated at how you guys are trapped with STDCALL from Win32 in 64 bit. push/call notation belongs to a bygone era. If you need to save and restore registers, just create a LOCAL and MOV the register into the LOCAL.

; pseudo code
LOCAL .rax :QWORD
; .....
mov .rax, rax
; on exit
mov rax, .rax

JK

@jj,

sorry i cannot supply an istaller! This is. what i use in a batch file:

Assembler: UASM V2.52: (...\uasm64.exe /c -win64 -Zp8 /win64 /D_WIN64 /Cp /W2 /I ...)
Linker: MS´s link.exe V 14.20.27508.1: (...\LINK.EXE /LARGEADDRESSAWARE:NO /SUBSYSTEM:CONSOLE /RELEASE /VERSION:4.0 /MACHINE:X64 /LIBPATH: ...)
Include files: http://www.terraspace.co.uk/WinInc209.zip
Lib files: MASM64 SDK


JK

jj2007


JK

MASM64 SDK: http://www.masm32.com/download/install64.zip

jj2007

Quote from: JK on April 07, 2021, 02:10:53 AM
MASM64 SDK: http://www.masm32.com/download/install64.zip

Over two years old, so it can't be the current version. Do you think the \Masm32\install64\m64lib stuff will still work?

JK


johnsa

I wouldn't link with /largeaddressaware:no , that shouldn't be necessary for a 64bit image, you're basically telling the OS that the 64bit exe can't deal with addresses > 4gb which will come back and bite you later if you want to do anything with large memory allocs or file mapping etc.

I see JKs idea, he wants to be able to do whatever he feels like inside his proc, if that means pushing/popping so be it.
I personally don't see the point, I just want to get stuff done and care not about the stack frame or alignment etc.. most of those nano level optimisations will have no positive benefit (but this is just me). Even with the clever prologue/epilogue optimisations an assembler is still no match for a compiler that can auto-inline, tail-call eliminations etc. If that level of optimisation is a concern then in some regards C is actually a better general purpose bet. (I hate to say it as a die-hard assembler fanatic).

If you really want to abuse yourself, you could forego using PROC at all and go oldskool, or make a custom invoke/proc macro.
IE...  XPROC myFunction, arg1, DWORD, arg2, QWORD and then generate a basic prologue with no automatic reservation.

Just a thought :)