NASM Assembly while loop counter - c

I'm writing a while loop in assembly to compile in the Linux terminal with nasm and gcc. The program compares x and y until y >= x and reports number of loops at the end. Here's the code:
segment .data
out1 db "It took ", 10, 0
out2 db "iterations to complete loop. That seems like a lot.", 10, 0
x db 10
y db 2
count db 0
segment .bss
segment .text
global main
extern printf
main:
mov eax, x
mov ebx, y
mov ecx, count
jmp lp ;jump to loop lp
lp:
cmp ebx, eax ;compare x and y
jge end ;jump to end if y >= x
inc eax ;add 1 to x
inc ebx ;add 2 to y
inc ebx
inc ecx ;add 1 to count
jp lp ;repeat loop
end:
push out1 ;print message part 1
call printf
push count ;print count
call printf
push out2 ;print message part 2
call printf
;mov edx, out1 ;
;call print_string ;
;
;mov edx, ecx ;these were other attempts to print
;call print_int ;using an included file
;
;mov edx, out2 ;
;call print_string ;
This is compiled and run in the terminal with:
nasm -f elf test.asm
gcc -o test test.o
./test
Terminal output comes out as:
It took
iterations to complete loop. That seems like a lot.
Segmentation fault (core dumped)
I can't see anything wrong with the logic. I think it's syntactical but we've only just started learning assembly and I've tried all sorts of different syntax like brackets around variables and using ret at the end of a segment, but nothing seems to work. I've also searched for segmentation faults but I haven't found anything really helpful. Any help would be appreciated because I'm an absolute beginner.

The reason it crashes is probably that your main function doesn't have a ret instruction. Also be sure to set eax to 0 to signal success:
xor eax, eax ; or `mov eax, 0` if you're more comfortable with that
ret
Additionally, global variables designate pointers, not values. mov eax, x sets eax to the address of x. You need to write back to it if you want anything to happen (or not use global variables).
Finally, you're calling printf with a single non-string argument:
push count ;print count
call printf
The first argument needs to be a format string, like "%i". Here, count is a pointer to a null byte, so you get nothing instead. Off my head, you should try this:
out3 db "%i ", 0
; snip
push ecx
push out3
call printf

I think your problem might just be that you are referencing the addresses of your constants and not their intrinsic value. One must think of a label in nasm as a pointer rather than a value. To access it you just need to use [label]:
segment .data
x dw 42
segment .text
global main
extern printf
main:
mov eax, x
push eax
call printf ; will print address of x (like doing cout<<&x in C++)
mov eax, [x]
push eax
call printf ; will print 42
sub esp, 8
xor eax, eax
ret
PS:I don't think anyone has mentioned it but volatile registers are modified very often when calling external code (C or C++ or other) since at compilation those functions you use are "translated" to assembly and then linked with your asm file. The PC is not a human so it is not distinguishing between what was written in high-level or low-level, the processor is just reading opcodes and operands stored in registers and memory, hence why an external function when using low-level language (call printf) is going to modify (or not! always depends on compiler and architecture) registers that you are also using.
To solve this there are various solutions:
You check what registers are not being modified by using gcc your_c_file.c -S and then in the file your_c_file.swill be the pre-prepared assembly code your compiler has produced from your C file. (It tends to be quite hard to figure out what is what and if you are going to use this method check out Name Mangling, to see how func names will be changed.)
Push all the registers you want to save to stack, and then after the call pop them back to their registers keeping in mind LIFO method.
Use the instructions PUSHA and POPAwhich push or pop all registers respectively.
This is the NASM manual chapter 3 which explains the basis of the language to use: http://www.csie.ntu.edu.tw/~comp03/nasm/nasmdoc3.html
Hope you managed to solve it.

Related

x86 Assembly Language: Filling an array with values generated by a subroutine

Edit: Thank you so much for all your help! I really appreciate it because for some reason I am having some trouble conceptualizing assembly but I'm piecing it together.
1) I am using the debugger to step through line by line. The problem, Unhandled exception at 0x0033366B in Project2.exe: 0xC0000005: Access violation writing location 0x00335FF8 occurs at this line:
mov [arrayfib + ebp*4], edx
Is think maybe this because the return statement from the other loop is not able to be accessed by the main procedure, but not sure - having a hard time understanding what is happening here.
2) I have added additional comments, hopefully making this somewhat clearer. For context: I've linked the model I've used to access the Fibonacci numbers, and my goal is to fill this array with the values, looping from last to first.
.386
.model flat, stdcall
.stack 4096
INCLUDE Irvine32.inc
ExitProcess PROTO, dwExitCode: DWORD
.data
arrayfib DWORD 35 DUP (99h)
COMMENT !
eax = used to calculate fibonacci numbers
edi = also used to calculate fibonacci numbers
ebp = number of fibonacci sequence being calculated
edx = return value of fibonacci number requested
!
.code
main PROC
;stores n'th value to calculate fibonacci sequence to
mov ebp, 30
mov edx, 0
;recursive call to fibonacci sequence procedure
call FibSequence
mov [arrayfib + ebp*4], edx
dec ebp
jnz FibSequence
mov esi, OFFSET arrayfib
mov ecx, LENGTHOF arrayfib
mov ebx, TYPE arrayfib
call DumpMem
INVOKE ExitProcess, 0
main ENDP
;initializes 0 and 1 as first two fibonacci numbers
FibSequence PROC
mov eax, 0
mov edi, 1
;subrracts 2 from fibonacci number to be calculated. if less than 0, jumps to finish,
;else adds two previous fibonacci numbers together
L1:
sub ebp, 2
cmp ebp, 0
js FINISH
add eax, edi
add edi, eax
LOOP L1
;stores odd fibonacci numbers
FINISH:
test eax, 1
jz FINISHEVEN
mov edx, eax
ret
;stores even fibonacci numbers
FINISHEVEN:
mov edx, edi
ret
FibSequence ENDP
END main
Your Fibonacci function destroys EBP, returning with it less than zero.
If your array is at the start of a page, then arrafib + ebp*4] will try to access the previous page and fault. Note the fault address of 0x00335FF8 - the last 3 hex digits are the offset within a 4k virtual page, an 0x...3FF8 = 0x...4000 + 4*-2.
So this is exactly what happened: EBP = -2 when your mov store executed.
(It's normal for function calls to destroy their register args in typical calling conventions, although using EBP for arg-passing is unusual. Normally on Windows you'd pass args in ECX and/or EDX, and return in EAX. Or on the stack, but that sucks for performance.)
(There's a lot of other stuff that doesn't make sense about your Fibonacci function too, e.g. I think you want jmp L1 not loop L1 which is like dec ecx / jnz L1 without setting flags).
In assembly language, every instruction has a specific effect on the architectural state (register values and memory contents). You can look up this effect in an instruction-set reference manual like https://www.felixcloutier.com/x86/index.html.
There is no "magic" that preserves registers for you. (Well, MASM will push/pop for you if you write stuff like proc foo uses EBP, but until you understand what that's doing for you it's better not to have the assembler adding instructions to you code.)
If you want a function to preserve its caller's EBP value, you need to write the function that way. (e.g. mov to copy the value to another register.) See What are callee and caller saved registers? for more about this idea.
maybe this because the return statement from the other loop is not able to be accessed by the main procedure
That doesn't make any sense.
ret is just how we write pop eip. There are no non-local effects, you just have to make sure that ESP is pointing to the address (pushed by call) that you want to jump to. Aka the return address.

Multiplying values in an array using IMUL instruction produces incorrect values

I'm picking up ASM language and trying out the IMUL function on Ubuntu Eclipse C++, but for some reason I just cant seem to get the desired output from my code.
Required:
Multiply the negative elements of an integer array int_array by a specified integer inum
Here's my code for the above:
C code:
#include <stdio.h>
extern void multiply_function();
// Variables
int iaver, inum;
int int_ar[10] = {1,2,3,4,-9,6,7,8,9,10};
int main()
{
inum = 2;
multiply_function();
for(int i=0; i<10; i++){
printf("%d ",int_ar[i]);
}
}
ASM code:
extern int_ar
extern inum
global multiply_function
multiply_function:
enter 0,0
mov ecx, 10
mov eax, inum
multiply_loop:
cmp [int_ar +ecx*4-4], dword 0
jg .ifpositive
mov ebx, [int_ar +ecx*4-4]
imul ebx
cdq
mov [int_ar +ecx*4-4], eax
loop multiply_loop
leave
ret
.ifpositive:
loop multiply_loop
leave
ret
The Problem
For an array of: {1,2,3,4,-9,6,7,8,9,10} and inum, I get the output {1,2,3,4,-1210688460,6,7,8,9,10} which hints at some sort of overflow occurring.
Is there something I'm missing or understood wrong about how the IMUL function in assembly language for x86 works?
Expected Output
The output I expected is {1,2,3,4,-18,6,7,8,9,10}
My Thought Process
My thought process for the above task:
1) Find which array elements in array are negative, for each positive element found, do nothing and continue loop to next element
cmp [int_ar +ecx*4-4], dword 0
jg .ifpositive
.ifpositive:
loop multiply_loop
leave
ret
2) Upon finding the negative element, move its value into register EBX which will serve as SRC in the IMUL SRC function. Then extend register EAX to EAX-EDX where the result is stored in:
mov ebx, [int_ar +ecx*4-4]
imul ebx
cdq
3) Move the result into the negative element of the array by using MOV:
mov [int_ar +ecx*4-4], eax
4) Loop through to the next array element and repeat the above 1)-3)
Reason for Incorrect Values
If we look past the inefficiencies and unneeded code and deal with the real issue it comes down to this instruction:
mov eax, inum
What is inum? You created and initialized a global variable in C called inum with:
int iaver, inum;
[snip]
inum = 2;
inum as a variable is essentially a label to a memory location containing an int (32-bit value). In your assembly code you need to treat inum as a pointer to a value, not the value itself. In your assembly code you need to change:
mov eax, inum
to:
mov eax, [inum]
What your version does is moves the address of inum into EAX. Your code ended up multiplying the address of the variable by the negative numbers in your array. That cause the erroneous values you see. the square brackets around inum tell the assembler you want to treat inum as a memory operand, and that you want to move the 32-bit value at inuminto EAX.
Calling Convention
You appear to be creating a 32-bit program and running it on 32-bit Ubuntu. I can infer the possibility of a 32-bit Linux by the erroneous value of -1210688460 being returned. -1210688460 = 0xB7D65C34 divide by -9 and you get 804A06C. Programs on 32-bit Linux are usually loaded starting at 0x8048000
Whether running on 32-bit Linux or 64-bit Linux, assembly code linked with 32-bit C/C++ programs need to abide by the CDECL calling convention:
cdecl
The cdecl (which stands for C declaration) is a calling convention that originates from the C programming language and is used by many C compilers for the x86 architecture.1 In cdecl, subroutine arguments are passed on the stack. Integer values and memory addresses are returned in the EAX register, floating point values in the ST0 x87 register. Registers EAX, ECX, and EDX are caller-saved, and the rest are callee-saved. The x87 floating point registers ST0 to ST7 must be empty (popped or freed) when calling a new function, and ST1 to ST7 must be empty on exiting a function. ST0 must also be empty when not used for returning a value.
Your code clobbers EAX, EBX, ECX, and EDX. You are free to destroy the contents of EAX, ECX, and EDX but you must preserve EBX. If you don't you can cause problems for the C code calling the function. After you do the enter 0,0 instruction you can push ebx and just before each leave instruction you can do pop ebx
If you were to use -O1, -O2, or -O3 GCC compiler options to enable optimizations your program may not work as expected or crash altogether.

What is the correct way to loop if I use ECX in loop (Assembly)

I am currently learning assembly language, and I have a program which outputs "Hello World!" :
section .text
global _start
_start:
mov ebx, 1
mov ecx, string
mov edx, string_len
mov eax, 4
int 0x80
mov eax, 1
int 0x80
section .data
string db "Hello World!", 10, 0
string_len equ $ - string
I understand how this code works. But, now, I want to display 10 times the line. The code which I saw on the internet to loop is the following:
mov ecx, 5
start_loop:
; the code here would be executed 5 times
loop start_loop
Problem : I tried to implement the loop on my code but it outputs an infinite loop. I also noticed that the loop needs ECX and the write function also needs ECX. What is the correct way to display 10 times my "Hello World!" ?
This is my current code (which produces infinite loop):
section .text
global _start
_start:
mov ecx, 10
myloop:
mov ebx, 1 ;file descriptor
mov ecx, string
mov edx, string_len
mov eax, 4 ; write func
int 0x80
loop myloop
mov eax, 1 ;exit
int 0x80
section .data
string db "Hello World!", 10, 0
string_len equ $ - string
Thank you very much
loop uses the ecx register. This is easy the remember because the c stands for counter. You however overwrite the ecx register so that will never work!
The easiest fix is to use a different register for your loop counter, and avoid the loop instruction.
mov edi,10 ;registers are general purpose
loop:
..... do loop stuff as above
;push edi ;no need save the register
int 0x80
.... ;unless you yourself are changing edi
int 0x80
;pop edi ;restore the register. Remember to always match push/pop pairs.
sub edi,1 ;sub sometimes faster than dec and never slower
jnz loop
There is no right or wrong way to loop.
(Unless you're looking for every last cycle, which you are not, because you've got a system call inside the loop.)
The disadvantage of loop is that it's slower than the equivalent sub ecx,1 ; jnz start_of_loop.
The advantage of loop is that it uses less instruction bytes. Think of it as a code-size optimization you can use if ECX happens to be a convenient register for looping, but at the cost of speed on some CPUs.
Note that the use of dec reg + jcc label is discouraged for some CPUs (Silvermont / Goldmont being still relevant, Pentium 4 not). Because dec only alters part of the flags register it can require an extra merging uop. Mainstream Intel and AMD rename parts of EFLAGS separately so there's no penalty for dec / jnz (because jnz only reads one of the flags written by dec), and can even micro-fuse into a dec-and-branch uop on Sandybridge-family. (But so can sub). sub is never worse, except code-size, so you may want to use sub reg,1 for the forseeable future.
A system call using int 80h does not alter any registers other than eax, so as long as you remember not to mess with edi you don't need the push/pop pair. Pick a register you don't use inside the loop, or you could just use a stack location as your loop counter directly instead of push/pop of a register.

Printing out Twice NASM

My question: Why is it printing twice when I'm making one call to printf?
Note that yes, I'm aware I'm allocating space in heap for a variable stored on the stack. I'm only doing this to get used to malloc, pointers, and 'arrays' in NASM.
Compiling in x64 bit machine with:
nasm -f elf32 -o TEMP.o file.asm
and:
gcc -m32 -o exec TEMP.o
extern exit, printf, malloc, free
global main
section .data
format db "%s", 10
msg: db "Hello!!",10
BUF equ $-msg + 1
section .text
main:
push BUF ; How many bytes do we want to allocate
call malloc ; ptr stored in EAX
add esp, 4 ; clear the last thing on the stack (BUF)
mov esi, eax ; new source index at malloc pointer
xor ecx, ecx ; clear ECX (counter for us)
loop:
mov dl, [msg+ecx] ; mov letter into dl
mov BYTE [esi+ecx], dl ; cat dl onto array
inc ecx ; add 1 to our ounter
cmp ecx, BUF-1d
jl loop
xor edx, edx
mov BYTE [esi+ecx], dl
add esp, 4
mov esi, eax
push esi ;
push format
call printf
add esp, 4*2
push esi
call free
push 0
call exit
add esp, 4
You only need to end your strings with 0
format db "%s", 10, 0
msg: db "Hello!!",10 ,0
Okay i'v read the comments just now and see you said you had code that is meant to insert the 0s, i'd check that, because i copied/pasted your code and only added the 0s on the ends of the strings to make it output one string, i didn't even notice the insertion code let alone touch it, but i can only assume that is where your problem is.

Calling _printf in assembly loop, only outputting once

I'm learning assembly and I have a very basic loop here
segment .data
msg: db '%d',10,0
segment .text
global _asm_main
extern _printf
_asm_main:
push DWORD 5 ; Should loop 5 times
call dump_stack
add esp,4
ret
dump_stack:
push ebp
mov ebp, esp
mov ecx, 0
loop_start:
cmp ecx,[ebp+8] ;compare to the first param of dump_stack, (5)
jnle loop_end
push ecx ;push the value of my loop onto the stack
push DWORD msg ;push the msg (%d) should just print the value of my loop
call _printf
add esp, 8 ;clear the stack
inc ecx ;increment ecx
jmp loop_start ; go back to my loop start
loop_end:
mov esp, ebp
pop ebp
ret
My output looks something like this
program.exe
0
Just 0, then a newline. I tried to verify the loop was executing by moving my printf to the loop_end part, and it came out with ecx as 6, which is correct. So the loop is executing but printf is not... What am I doing wrong?
(Also, the function is called dump stack because it was initially supposed to dump the details of the stack, but that didn't work because of the same reason here)
And I am compiling with nasm -f win32 program.asm -o program.o
Then I have a cpp file that includes windows.h, and I compiled it with gcc -c include
and finally I linked them with gcc -o program program.o include.o
and I run program.exe
My guess is that printf() modifies ecx, it becomes >= [ebp+8] and your loop body executes only once. If that's the case, you need to read up on the calling conventions used in your compiler and manually preserve and restore the so-called volatile registers (which a called function can freely modify without restoring). Note that there may be several different calling conventions. Use the debugger!

Resources