Creating named variables in the stack
Asked Answered
I

2

5

Is there any way to create named variables in the stack rather refer to them by offset:

sub esp, 0x10 ; 4 variables of 4 bytes

mov DWORD [ebp-4], 0xf ; 1st var
mov DWORD [ebp-8], 0xff  ; 2nd var
; and so on
Interchangeable answered 21/2, 2017 at 11:35 Comment(1)
You can create "equ" for offsets, but it's not very robust solution, as they will work only when base pointer is correct, so it's still up to programmer to do all the type/ptr checking. Plus if you have several functions in single file, you may have clashing names for local variables. When I was young and not very smart, I did big project in ASM, and I used r32 + equ_offsets to mimic OOP, that part worked OK-ish. If you group several variables into "objects", create functions to manipulate them, like lea ebx,[ebp-30] ; object "X" instance call Xsomething... Then I finally learned C++.Clishmaclaver
D
8

FASM

FASM supports local variables through ad-hoc crafted macros that are shipped with FASM itself.

include 'win32ax.inc' 

.code

  start:
        mov ax, 1

        call foo


  proc foo

      ;Local variables

      locals
        var1 dd ?
        var2 dw ?
      endl


      ;Use them as expected
      mov eax, [var1]
      lea ebx, [var2]

   ret
  endp 

That is compiled1 into

:00401000 66B80100                mov ax, 0001
:00401004 E800000000              call 00401009
:00401009 55                      push ebp
:0040100A 89E5                    mov ebp, esp
:0040100C 83EC08                  sub esp, 00000008
:0040100F 8B45F8                  mov eax, dword ptr [ebp-08]
:00401012 8D5DFC                  lea ebx, dword ptr [ebp-04]
:00401015 C9                      leave
:00401016 C3                      ret

NASM

NASM support local variables too with the use of the %local directive.

Quoting from the manual:

silly_swap: 
    %push mycontext             ; save the current context 
    %stacksize small            ; tell NASM to use bp 
    %assign %$localsize 0       ; see text for explanation 
    %local old_ax:word, old_dx:word 

    enter   %$localsize,0   ; see text for explanation 
    mov     [old_ax],ax     ; swap ax & bx 
    mov     [old_dx],dx     ; and swap dx & cx 
    mov     ax,bx 
    mov     dx,cx 
    mov     bx,[old_ax] 
    mov     cx,[old_dx] 
    leave                   ; restore old bp 
    ret                     ; 

    %pop                        ; restore original context

The %$localsize variable is used internally by the %local directive and must be defined within the current context before the %local directive may be used.

Other sloppy approaches

One could

%define SUPER_VAR ebp-4
%define MEGA_VAR ebp-8

mov DWORD [SUPER_VAR], 0xf
mov DWORD [MEGA_VAR], 0xff

However this hides the fact that the variables are in the stack and assume a correctly set frame pointer.

A slightly better approach:

%define SUPER_VAR 4
%define MEGA_VAR 8

mov DWORD [ebp-SUPER_VAR], 0xf
mov DWORD [ebp-MEGA_VAR], 0xff

The assembly programmer way

Real assembler programmers use comments[citation needed] to state the intentions of their code.
And write code only in vi. Or it was emacs?

mov DWORD [ebp-4], 0xf           ;SUPER_VAR
mov DWORD [ebp-8], 0xff          ;MEGA_VAR

The key strengths of the assembly language are its simple syntax and the full information approach (nothing is hidden, the programmer controls everything).

While there is nothing wrong with using high-level macros2, mixing high and low level approaches result in a source file that is harder to parse for an expert.

Furthermore it makes little sense from an ontological point of view: if you want to use high-level features then a language like C is better suited and the use of assembly must be reconsidered. Au contraire, if you want to learn how to do low-level programming then such macros are an hindrance to the learning process.

Finally, macros are not magic. While very flexible soon or later a programmer will encounter their limit.
For example, I haven't dug into the FASM and NASM support for aligned local variables.


1 At this point this is not assembly anymore...
2 High-level macros let you refactor the code easily, which is very important. One should pause a moment though, questioning itself about the choice made of using assembly when important refactoring is expected/needed.

Diadelphous answered 21/2, 2017 at 12:59 Comment(3)
"Real assembler programmers use comments" ... when reading other people's code I sometimes think it's rather "It was hard for me to code it, so it should be hard for you to read it" ;-)Khartoum
@Khartoum plus that moment, when you understand it better than the author :/ ... ;) ... I agree about the comment thing, completely valid when you use the assembly for performance related things, rewriting only few bottlenecks of larger project. I can imagine how having a bit more complex language structures may help with large ASM project, but from my own experience (ASM files over 500kB in total) the names for locals are definitely not the biggest problem with large asm projects :) ). (I agree even when I'm not a real programmer, as I don't use vi or emacs, just joe)Clishmaclaver
The %local link changed to nasm.us/doc/nasmdoc4.html#section-4.10.3Durward
C
0

The FASM native way of doing such things is through virtual directive:

For arguments:

virtual at ebp + 8
  .arg1 dd ?
  .arg2 dq ?
end virtual

For local variables:

virtual at ebp - 10h  ; the offset is the size of the local variables area.
  .var1        dd ?
  .local_array rb 12
end virtual

The different kind of proc macros use virtual internally.

Coffey answered 10/4, 2017 at 11:35 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.