NASM - The Netwide Assembler
NASM Forum => Using NASM => Topic started by: Mich on March 04, 2016, 03:55:46 AM
-
I'm experimenting with enter/leave
From the docs:
%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
-
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:
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:
$ 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.
-
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:
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
-
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:
%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 (http://forum.nasm.us/index.php?topic=2201.msg9708#msg9708) 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:
;;; 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:
;; 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.
-
Btw, I notice you're doing newlines by using format strings. Just a heads up, NASM supports C string escapes using the `-quoted strings.
szHelloMessage: db `Hello, world\r\n`, 0
For more information, check out Section 3.4.2 (http://www.nasm.us/doc/nasmdoc3.html#section-3.4.2) of the NASM Manual: