I'm currently working with a nasm program in Assembly and I'm trying to write a program to determine if the first command line arg given is a leap year or not. I've successfully processed argument handling and the proper message displays when a user enters anything but one command line arg, but now I'm confused about how to "access" the year provided and mod it with numbers. Here is my code so far
global main
extern puts
extern printf
extern atoi
section .text
main:
push r13
push r14
sub rsp, 8
cmp rdi, 2
jne error1 ; jump if aguments != 1
jmp done
error1:
mov edi, badArgs
call puts
jmp done
done:
pop r14
pop r13
add rsp, 8
ret
badArgs:
db "Requires exactly one argument", 5, 0
The line in question occurs right after my jne line which checks for bad args. Now I want to isolate the argument (which I believe is located in the rsi register) and mod it with the number 4 to begin my leap year cheks. How do I isolate this value and mod it with 4?
Related
My program is composed of two files: main.c and core.s and runs on a 32 bit virtual machine of lubuntu linux.
Main.c takes in an integer and passes it to the assembly function void printFunc(int x). The assembly function in turn calls to a C function to check the parity of x. If x is even the function will print 4x and if x is odd it will print 8x. The print call must be done within the assembly function.
section .text
global printFunc
extern c_checkValidity
extern printf
section .data ; data section
fmt: db "%d", 10, 0 ; The printf format, "\n", '0'
printFunc:
push ebp ; code for handling stack I've seen from
mov ebp, esp ; other examples online
pushad
mov ebx, eax ; copy and input value
push eax ; Move input onto stack
call c_checkValidity ; Call C function, return value is in eax
cmp eax, 1 ; Check result, 1 indicates even
je multby4 ; If even, do mult by 4
jmp multby8 ; Otherwise odd, do mult by 8
multby4: ;INPUT WAS EVEN
sal ebx, 2 ; left shift by 2 is equivalent to multiplying by 4
jmp exitcode ; print and exit code
multby8: ;INPUT WAS ODD
sal ebx, 3 ; left shift by 3 is equivalent to multiplying by 8
jmp exitcode ; print and exit code
exitcode:
mov eax,ebx ; move value to eax to keep as default return value of func
push ebx ; Push final answer to the stack
push dword fmt ; Push print format to the stack
call printf ; Print answer
mov eax, ebx ; Copy final answer as return value
popad
mov esp, ebp ; return stack pointer to what it was before operation
pop ebp ; get rid of saved pointer
ret ; return state to caller
The integer input is received, parity tested, and printed to stdout correctly. A segfault occurs somewhere after call printf has successfully executed. When I use gdb to try and backtrace the segfault the report says "0x0804a0f in exitcode ()". Presumably this is the address of the code during operation that causes the segfault?
It is clear to me that I have failed to properly handle the stack pointer register (esp?) in some way. I've tried searching this site and others for examples of how to properly address the stack before returning control to the caller but to no avail. Certainly I would love to make the code work and any advice on how to fix the code is appreciated but I am primarily asking for an explanation on what I should be tracking and maintaining in general to return from an assembly function to a caller (extra appreciation if that explanation includes how to pass back values to the caller).
I`m trying to make a while loop that prints from 0 through 10 but have some errors...
Compile with these:
nasm -f elf myprog.asm
gcc -m32 -o myprog myprog.o
Errors:
at output you can see 134513690.. lots of....
and at the last line a segmentation fault
This is the code:
SECTION .text
global main
extern printf
main:
xor eax,eax ; eax = 0
myloop:
cmp eax,10 ; eax = 10?
je finish ; If true finish
push eax ; Save eax value
push number ; push number value on stack
call printf
pop eax
inc eax ; eax + 1
add esp,80 ; Im not sure what is this
jmp myloop ; jump to myloop
number db "%d",10,0 ; This is how i print the numbers
finish:
mov eax,1
mov ebx,0
int 0x80
There's one real error in this code; the function call cleanup isn't quite right. I would change the myloop section to be like this:
myloop:
cmp eax,10 ; eax = 10?
je finish ; If true finish
push eax ; Save eax value
push number ; push number value on stack
call printf
add esp, 4 ; move past the `push number` line
pop eax
inc eax ; eax + 1
jmp myloop ; jump to myloop
The biggest difference is that instead of adding 80 to esp (and I'm not sure why you were doing that), you're only adding the size of the argument pushed. Also, previously the wrong value was getting popped as eax, but switching the order of the add and the pop fixes this.
A few problems, you need to push the "number" not as address, but as numeral.
push dword number
After you call printf, you need to restore the stack, ESP.
Basically when you "push" a register, it gets stored in the stack. Since you push twice (two arguments), you need to restore 8 bytes.
When you "pop eax", you're retrieving the top of the stack, which is "number", not the counter. Therefore, you just need to do
pop eax
pop eax
then there is no need to restore the ESP by adding since it is done by popping.
Basically, after the first iteration, eax points at an address, so it will never be equal to 10.
Further reading about Stack Pointer and Base Pointer:
Ebp, esp and stack frame in assembly with nasm
I am just starting to learn NASM, and I am doing a first program involving a matrix in a text file. The file contains an N*N matrix, where the first line contains N, and the other lines each contain one row of the matrix. To start along my way in completing my larger task, i borrowed some code that reads a file line by line and outputs each line to the console.
I intend to read in the first line, convert it from string to integer, move that to a register i will use as a counter, then print out that many lines of the array. I figure even if N=7 and i fiddle with the top line of the file to say 3, if i get 3 lines printed then it works! However, this didn't work. I got it to print out always one line, suggesting that the number i read in and converted to int wasn't converted properly. I tried to output this number after conversion, but attempting to do so causes a seg fault, to my suprise!
Here is my code for NASM under Linux:
; this program demonstrates how to open files for reading
; It reads a text file line by line and displays it on the screen
extern fopen
extern fgets
extern fclose
extern printf
extern exit
global main
segment .data
readmode: db "r",0
filename: db "hw6_1.dat",0 ; filename to open
error1: db "Cannot open file",10,0
format_1: db "%d",0
segment .bss
buflen: equ 256 ; buffer length
buffer: resd buflen ; input buffer
segment .text
main: pusha
; OPENING FILE FOR READING
push readmode ; 1- push pointer to openmode
push filename ; 2- push pointer to filename
call fopen ; fopen retuns a filehandle in eax
add esp, 8 ; or 0 if it cannot open the file
cmp eax, 0
jnz .L1
push error1 ; report an error and exit
call printf
add esp, 4
jmp .L4
; READING FROM FILE
.L1: mov ebx, eax ; save filepointer of opened file in ebx
; Get first line and pass to ecx
push ebx
push dword buflen
push buffer
call fgets
add esp, 12
cmp eax, 0
je .L3
;convert string -> numeric
push buffer
call parseInt
mov ecx, eax
.L2:
;debug
push ecx
push format_1
call printf
add esp, 8
push ebx ; 1- push filehandle for fgets
push dword buflen ; 2- push max number of read chars
push buffer ; 3- push pointer to text buffer
call fgets ; get a line of text
add esp, 12 ; clean up the stack
cmp eax, 0 ; eax=0 in case of error or EOF
je .L3
push buffer ; output the read string
call printf
add esp, 4 ; clean up the stack
dec ecx
cmp ecx, 0
jg .L2
;CLOSING FILE
.L3: push ebx ; push filehandle
call fclose ; close file
add esp, 4 ; clean up stack
.L4: popa
call exit
parseInt:
push ebp
mov ebp, esp
push ebx
push esi
mov esi, [ebp+8] ; esi points to the string
xor eax, eax ; clear the accumulator
.I1 cmp byte [esi], 0 ; end of string?
je .I2
mov ebx, 10
mul ebx ; eax *= 10
xor ebx, ebx
mov bl, [esi] ; bl = character
sub bl, 48 ; ASCII conversion
add eax, ebx
inc esi
jmp .I1
.I2: pop esi
pop ebx
pop ebp
ret 4
A sample data file is shown below, this is the one i was using:
4
2 45 16 22
17 21 67 29
45 67 97 35
68 34 90 72
I really dont understand how this is not working. The code to convert to integer was borrowed from WORKING programs, as is the code for output that i used to debug.
First, why are you calling printf with only one parameter? The proto for printf is:
int printf ( const char * format, ... );
Second, your program works almost fine, you are just not exiting the program correctly!! You are linking to the c library and it adds startup code, you need to call exit instead of ret. Actually, just a ret is not the correct way to exit any program in Linux or Windows.
Your exit code should be:
.L4:
popa
call exit
and add extern exit to your list of externs.
Your parseint seems to return an incorrect number
* EDIT *
Since you are still having problems with parseint, from the fgets docs at the c++ site, you are not reading the whole thing:
A newline character makes fgets stop reading, but it is considered a
valid character by the function and included in the string copied to
str.
So, what is happening is you are telling fgets to read in dword buflen number of bytes, which it will or it will stop reading when a newline is found and adds that to the buffer.
This:
; Get first line and pass to ecx
push ebx
push dword buflen
push buffer
call fgets
add esp, 12
should be:
; Get first line and pass to ecx
push ebx
push 1 ; <----- you only want to read 1 byte!
push buffer
call fgets
add esp, 12
extern putchar
extern exit
section .data
section .text
global main
main:
push 's'
mov eax, 2
cmp eax, 2
point:
call putchar
jz point
push 0
call exit
On the console I see only one 's' charcter.
Compile and run:
nasm -f elf ./prog.asm
gcc -m32 -o prog ./prog.o
./prog
The cmp is "equal to 0" (that is, it does set the ZF flag). However, the call putchar in the next line is trashing the flags set by cmp, so your jz does not work (more or less by accident). If you want to save the flags for later comparison, you could use pushf and popf, however this won't really work in your case since putchar will expect the character on the stack, not the flags.
Now, to answer the actual problem which you didn't state. I'll assume you want to print 's' two times. Here's how to do it properly:
mov eax, 2 ; init counter
print_loop:
push eax; save the counter since it will be trashed by putchar
push 's'
call putchar
add esp, 4 ; restore the stack pointer since putchar is cdecl
pop eax ; restore the saved counter
dec eax ; decrement it
jnz print_loop ; if it's not yet zero, do another loop
add esp, 4 can be replaced by another pop eax for slightly shorter code.
The result of doing a cmp is that flags get set, zf for zero, and so on. You can then either branch on whether or not the flag was set or use one of the set? instructions to have a value, e.g. the al register, set based on whether or not the flag was set.
So as part of my Computer Architecture class I need to get comfortable with Assembly, or at least comfortable enough, I'm trying to read the input to the user and then reprint it (for the time being), this is my how I tried to laid this out in pseudo code:
Declare msg variable (this will be printed on screen)
Declare length variable (to be used by the sys_write function) with long enough value
Pop the stack once to get the program name
Pop the stack again to get the first argument
Move the current value of the stack into the msg variable
Move msg to ECX (sys_write argument)
Mov length to EDX (sys_write argument)
Call sys_write using standard output
Kernel call
Call sys_exit and leave
This is my code so far
section .data
msg: db 'placeholder text',0xa;
length: dw 0x123;
section .text
global _start
_start:
pop rbx;
pop rbx;
; this is not working when I leave it in I get this error:
; invalid combination of opcode and operands
;mov msg, rbx;
mov ecx, msg;
mov edx, length;
mov eax, 4;
mov ebx, 1;
int 0x80;
mov ebx, 0;
mov eax, 1;
int 0x80;
When I leave it out (not moving the argument into msg), I get this output
placeholder text
#.shstrtab.text.data
�#�$�`��
We really just begun with NASM so ANY help will be greatly appreciated, I've been looking at this http://www.cin.ufpe.br/~if817/arquivos/asmtut/index.html#stack and http://syscalls.kernelgrok.com/ adapting the examples adapting the registry names to the best of my understanding to match http://www.nasm.us/doc/nasmdo11.html
I'm running Ubuntu 12.04, 64bit compiling (not even sure if this is the right word) NASM under ELF64, I'm sorry to ask such a silly question but I have been unable to find an easy enough tutorial for NASM that uses 64bits.
When the program is called the stack should looks like this:
+----------------+
| ... | <--- rsp + 24
+----------------+
| argument 2 | <--- rsp + 16
+----------------+
| argument 1 | <--- rsp + 8
+----------------+
| argument count | <--- rsp
+----------------+
The first argument is the name of your program and the second is the user input (if the user typed anything as an argument). So the count of the arguments is at least 1.
The arguments for system calls in 64-mode are stored in the following registers:
rax (system call number)
rdi (1st argument)
rsi (2nd argument)
rdx (3rd argument)
rcx (4th argument)
r8 (5th argument)
r9 (6th argument)
And the system call is called with syscall. The numbers of all the system calls can be found here here (yes they are different from the numbers in 32 bit mode).
This is the program which should do your stuff:
section .data
msg: db 'Requesting 1 argument!', 10 ; message + newline
section .text
global _start
_start:
cmp qword [rsp], 2 ; check if argument count is 2
jne fail ; if not jump to the fail lable
mov rax, 1 ; sys_write
mov rdi, 1 ; stdout
mov rsi, [rsp+16] ; get the address of the argument
mov rdx, 1 ; one character (length 1)
loop:
cmp byte [rsi], 0 ; check if current character is 0
je exit ; if 0 then jump to the exit lable
syscall
inc rsi ; jump to the next character
jmp loop ; repeat
fail:
mov rax, 1 ; sys_write
mov rdi, 1 ; stdout
lea rsi, [rel msg] ; move the address of the lable msg in rsi
mov rdx, 23 ; length = 23
syscall
exit:
mov rax, 60 ; sys_exit
mov rdi, 0 ; with code 0
syscall
Since the code isn't prefect in many ways you may want to modify it.
You've followed the instructions quite literally -- and this is expected output.
The stack variable that you write to the message, is just some binary value -- to be exact, it's a pointer to an array of strings containing the command line arguments.
To make sense of that, either you would have to print those strings, or convert the pointer to ascii string eg. "0x12313132".
My OS is Ubuntu 64-bit. Compiling your code produced the error:
nasm print3.asm
print3.asm:12: error: instruction not supported in 16-bit mode
print3.asm:13: error: instruction not supported in 16-bit mode
Exactly where the "pop rbx" is located.
Adding "BITS 64" to the top of the asm file solved the problem:
BITS 64
section .data
msg: db 'placeholder text',0xa;
length: dw 0x123;
...