Author Topic: %local  (Read 9035 times)

Offline Mich

  • Jr. Member
  • *
  • Posts: 8
%local
« on: March 04, 2016, 03:55:46 AM »
I'm experimenting with enter/leave
From the docs:
Code: [Select]
%push mycontext ; save the current context
%stacksize flat; tell NASM to use ebp
%assign %$localsize 0 ; see text for explanation
%local old_ax:word, old_dx:word

I would like to create:
%local buffer:byte 256

This compiles but allocates 4bytes only, Nasm version 2.12
buffer @ ebp-4
What is the proper way to allocate memory on the stack other than dword size?

Regards,
Klod

Offline Bryant Keller

  • Forum Moderator
  • Full Member
  • *****
  • Posts: 360
  • Country: us
    • About Bryant Keller
Re: %local
« Reply #1 on: March 05, 2016, 12:20:28 AM »
I would like to create:
%local buffer:byte 256

This compiles but allocates 4bytes only, Nasm version 2.12
buffer @ ebp-4
What is the proper way to allocate memory on the stack other than dword size?

NASM will automatically adjust the stack size for proper alignment (in this case, 4-byte alignment). If you're not too worried about the memory usage, you could use %rep/%endrep to create your array.. for example:

Code: [Select]
    BITS 32
    CPU 586

    EXTERN printf
    EXTERN exit
    GLOBAL main
   
main:
    %push
    %stacksize flat
    %assign %$localsize 0
    %assign %$elements 0
    %rep 256
    %local %[my_array_%{$elements}]:byte
    %assign %$elements %$elements + 1
    %endrep
    enter %$localsize, 0
    leave
    mov eax, [my_array_0]
    mov ebx, [my_array_255]
    ret
    %pop

When preprocessed it looks like:

Code: [Select]
$ nasm -e test.asm
%line 1+1 test.asm
[bits 32]
[cpu 586]

[extern printf]
[extern exit]
[global main]

main:
%line 17+1 test.asm
 enter 1024, 0
 leave
 mov eax, [(ebp-4)]
 mov ebx, [(ebp-1024)]
 ret

It's still doing dword alignment, but at least it's an array. :) As far as I know, if you want a true byte array, you'll just have to do it manually.

About Bryant Keller
bkeller@about.me

Offline Mich

  • Jr. Member
  • *
  • Posts: 8
Re: %local
« Reply #2 on: March 07, 2016, 04:56:25 AM »
Hi Bryant,
Thanks for your answer.
I have played around with your suggestion and wrote a little test piece, its Windows using c-runtime library for i/o and no macros, so it should be easy to run on U-nix:

Code: [Select]
BITS 32

    EXTERN printf
    EXTERN getchar
    EXTERN exit



Section .data
nl db 10,13,0
SzPurpose db "Test enter/leave instruction",0
szBar db "-------------------------------",0
format db "%s   Stack address %X",0
format1 db "Stack address proc2 %x %x %x",0
format2 db "%s %s %s",0

Section .code
Global Start
Start:

push dword nl
push dword nl
push dword SzPurpose
push format2
call printf
sub esp,4*4


call proc1

push dword nl
push dword szBar
push dword nl
push format2
call printf
sub esp,4*4

call proc2

call getchar
call exit

proc1:
    %push
    %stacksize flat
    %assign %$localsize 0
    %assign %$elements 0
    %rep 8
    %local %[my_array_%{$elements}]:byte
    %assign %$elements %$elements + 1
    %endrep
    enter %$localsize, 0
   
    mov al,65h ;fill array with letter e
    lea edi,[my_array_7]
    mov ecx, dword 31 ;one less than buffer size
    .e:
mov [edi],al
inc edi
dec ecx
cmp ecx,0
jne .e
mov [edi],byte 0 ;apend 0 to end of string

lea eax, [my_array_7]
push dword eax
lea eax, [my_array_7]
push dword eax
push format
call printf
sub esp, 3*4
leave
; %undef my_array_7  <--scope issue

%pop
    ret
   
   
proc2:
    %push
    %stacksize flat
    %assign %$localsize 0
    %assign %$elements 0
    %local pr2Arg1:dword
    %rep 8
    %local %[array_%{$elements}]:byte
    %assign %$elements %$elements + 1
    %endrep
    %warning %$localsize  %[array_%{$elements}]  %$elements
    enter %$localsize, 0
   
    lea eax,[pr2Arg1]
    push eax
    lea eax,[array_7]
push dword eax
lea eax,[my_array_0]
push dword eax
push format1
call printf
sub esp, 4*4
leave
%pop
ret

NAsm -fwin32; golink /console \entry Start  \user32.dll kernel32.dll  crtdll.dll

I allocate buffers on the stack as multiple dwords and thought your option would work for my requirements despite that each dword allocated would have its own name. On closer examination I found a scope issue. Nasm will complain if I don't define a local but will not complain if that local was defined in another proc as local. I have never considered this to be a problem since %define x ebp-4 defined in proc1 would not cause conflict in proc2 when %assign x ebp-8.

I have just spent 2 month tracking down the worst bug in my amateur carrier. After upgrading to a windows 7 machine, I transfered all my test code samples/libraries and bugsinga, the antivirus program quarantined dozens of these gems. I tried recompiling them but they were already quarantined before Nasm was finished compiling. The programs would compile and run under my XP setup like on day 1.
A combination proc,local uses call ADDR macro instructions would push an extra argument on the stack for some calls. Combined with an incorrect-scoped argument... I suppose stack overflow is the term?

I decided to continue to manage stack variable manually and consider de-allocating arguments on exit of procs De-allocating above array would be to cumbersome. I will aim to produce “robust” in the future...

Mich

Offline Bryant Keller

  • Forum Moderator
  • Full Member
  • *****
  • Posts: 360
  • Country: us
    • About Bryant Keller
Re: %local
« Reply #3 on: March 10, 2016, 03:20:34 AM »
Well, unfortunately the my_array_# symbols are defined in global scope. You can tell when a variable is scoped locally to a context by the prefix of %$. It would make sense that %local would allow you to create variables with locally scoped names but apparently this isn't the case. Using %local %$myInteger:dword just throws an error. Using %undef seems to be the solution here, but that's going to require you to do something like the following at the end of each context:

Code: [Select]
   %assign %$elements 0
   %rep 8
      %undef %[my_array_%{$elements}]
   %endrep

If you're looking to make your code more structured and avoid having to do the manual stack setup and stuff, you should check out NASMX, it'll handle this kinda stuff for you. Recently I posted an implementation of a turtle graphics demo in the NASMX sub-forum using GNU's Guile Scheme interpreter as a back-end scripting engine for the program. In that example, you should note the start_gnuplot procedure:

Code: [Select]
;;; start_gnuplot : nothing -> ptrdiff_t.
;;; create an instance of the 'gnuplot' process and produce a
;;; file descriptor for use later on.
proc start_gnuplot
locals
    local fdOutput, ptrdiff_t
    local iaPipes, uint32_t, 3
endlocals

    lea eax, [var(.iaPipes)]
    invoke pipe, eax
    invoke fork

    cmp eax, 0
    jne .parent

.child:

    lea eax, [var(.iaPipes)]
    invoke dup2, dword [eax], STDIN_FILENO
    invoke execlp, szGnuPlot, NULL
    return

.parent:

    lea eax, [var(.iaPipes)]
    invoke fdopen, dword [eax + 4], szWriteMode
    mov dword [var(.fdOutput)], eax

    invoke fprintf, dword [var(.fdOutput)], szInitString, \
        dword WIDTH, dword WIDTH, dword HEIGHT, dword HEIGHT

    invoke fflush, dword [var(.fdOutput)]

    return dword [var(.fdOutput)]

endproc

In this example, the local variable iaPipes is an array of 3 uint32_t data-types. NASMX uses the var() and argv() macros to access variables and arguments. You might notice that the variables have a dot '.' in front of the names, this is to bind the variables locally within the scope of the procedure, at the end of the procedure, these variables loose scope completely.

The only real down-side to NASMX is the overhead it adds to assembling your source. I run an old IA32 processor (Pentium III) and it's not enough to bother me, but I can't deny that loading all those macros and symbols within NASMX does add to NASM's workload.

Another option would be to do it manually (as you suggested), but make use of structures to organize your stack data. This lets you localize variables within structures that are based off of the original procedure names and use RESB/RESW/RESD for defining stack variables the same way you do other variables.. this means that an array of 512 bytes can be defined using just .myArray RESB 512 within the procedures local variables structure. I used to do this a lot in my old examples:

Code: [Select]
;; Build with:
;;  nasm -f elf -o test.o test.asm
;;  gcc -o test test.o
;; Execution:
;;  $ ./test
;;  a=10
;;  $

   BITS 32

   %define sizeof(_x_) _x_ %+ _size

   EXTERN printf
   GLOBAL main

SECTION .data

strFormat DB "a=%d", 10, 0

SECTION .text

MyFunction:
;; Demonstrate stack and local variable usage.

   ;; MyFunction Argument Definitions
   STRUC MFA
   .x RESD 1
   ENDSTRUC
   %define MFA.(x) ((Ebp + 8) + MFA. %+ x)
   ;; --

   ;; MyFunction Local Variable Definitions
   STRUC MFL
   .t RESD 1
   ENDSTRUC
   %define MFL.(x) ((Ebp - sizeof(MFL)) + MFL. %+ x)
   ;; --

;BEGIN STACK FRAME

   Push Ebp
   Mov Ebp, Esp
   Sub Esp, sizeof(MFL)

;BEGIN PROCEDURE BODY

   ;; --[ Store Argument in Eax ]--
   Mov Eax, [MFA.(x)]
   ;; --

   ;; --[ Store Eax in Local ]--
   Mov [MFL.(t)], Eax
   ;; --

   ;; --[ Store Local in Eax ]--
   Mov Eax, [MFL.(t)]
   ;; --

;END PROCEDURE BODY

   Leave
   Ret sizeof(MFA) ; Stdcall calling convention - callee cleans up stack.

main:
;; Application entrypoint

   ;; Application environment
   STRUC ENV
   .argc RESD 1
   .argv RESD 1
   .envp RESD 1
   ENDSTRUC
   %define ENV.(x) ((Ebp + 8) + ENV. %+ x)
   ;; --

;BEGIN STACK FRAME

   Push Ebp
   Mov Ebp, Esp

;BEGIN PROCEDURE BODY

   Push dword 10
   Call MyFunction

   Push Eax
   Push dword strFormat
   Call printf
   Add Esp, (2 * 4)

;END PROCEDURE BODY

   Leave
   Ret ; C calling convention - caller cleans up stack.

As the example code shows, I also used to have a  strange style of capitalization that I (thankfully) quit doing long ago. ;D This has the down-side of having several globally scoped symbols (the structure names) being used by the procedures, so there is a chance of namespace collision when working with a large codebase. This sounds like a minute chance, but it's actually that very reason why I stopped doing it, on a few large projects the 3 letter naming convention I was using commonly had me spending more time trying to think up names for structures than it took to actually develop the code.

About Bryant Keller
bkeller@about.me

Offline Bryant Keller

  • Forum Moderator
  • Full Member
  • *****
  • Posts: 360
  • Country: us
    • About Bryant Keller
Re: %local
« Reply #4 on: March 10, 2016, 03:29:24 AM »
Btw, I notice you're doing newlines by using format strings. Just a heads up, NASM supports C string escapes using the `-quoted strings.

Code: [Select]
szHelloMessage: db `Hello, world\r\n`, 0
For more information, check out Section 3.4.2 of the NASM Manual:

About Bryant Keller
bkeller@about.me