2

I have already asked a question and people pointed me to a thread of interger to string conv. and vice versa. I just copied it for now so I can see if my program works, and I'll try and write my own later. But there's a problem.I can't find a mistake I've done. The program always exits with Segmentation Fault after the last input. I tried removing the int to string conversion and just display the inputted values and it worked. So I must've done something wrong with the conversion. It's one of my first programs and I really need to understand why it won't work If I want to progress any further. Thank you. Here is my code:

section .text
global _start       



_start:                     

mov edx, lenask
mov ecx, ask
mov ebx, 1
mov eax, 4
int 0x80

mov edx, 5
mov ecx, input
mov ebx, 0
mov eax, 3
int 0x80

mov edx, lenask2
mov ecx, ask2
mov ebx, 1
mov eax, 4
int 0x80

mov edx, 5
mov ecx, input2
mov ebx, 0
mov eax, 3
int 0x80

lea esi, [input]
mov ecx, 2
call string_to_int
push eax

lea esi, [input2]
mov ecx, 4
call string_to_int
mov ebx, eax
pop eax



neg eax

add ebx, eax


mov [buffer], ebx
mov eax, [buffer]
lea esi, [result]
call int_to_string


mov edx, lenanswer
mov ecx, answer
mov ebx, 1
mov eax, 4
int 0x80


mov edx, 5
mov ecx, result
mov ebx, 1
mov eax, 4
int 0x80




mov eax, 1
mov ebx, 0
int 80h





;code taken from another thread
; Input:
; EAX = integer value to convert
; ESI = pointer to buffer to store the string in (must have room for at      least 10 bytes)
; Output:
; EAX = pointer to the first character of the generated string
int_to_string:
add esi,9
mov byte [esi], 0

mov ebx,10         
.next_digit:
xor edx,edx         ; Clear edx prior to dividing edx:eax by ebx
div ebx             ; eax /= 10
add dl,'0'          ; Convert the remainder to ASCII 
dec esi             ; store characters in reverse order
mov [esi],dl
test eax,eax            
jnz .next_digit     ; Repeat until eax==0
mov eax,esi
push eax
ret



;code taken from another thread
; Input:
; ESI = pointer to the string to convert
; ECX = number of digits in the string (must be > 0)
; Output:
; EAX = integer value
string_to_int:
xor ebx,ebx    ; clear ebx
.next_digit:
movzx eax,byte[esi]
inc esi
sub al,'0'    ; convert from ASCII to number
imul ebx,10
add ebx,eax   ; ebx = ebx*10 + eax
loop .next_digit  ; while (--ecx)
mov eax,ebx
ret


section .data

ask db "What is your age?"
lenask equ $-ask
ask2 db "What is today's year?"
lenask2 equ $-ask2
answer db "The age you were born was: "
lenanswer equ $-answer

section .bss 

input resw 5
input2 resw 5
buffer resw 5
result resw 10

An example of what happens:

What is your age?35
What is today's year?2015
The age you were born was: Segmentation fault(core dumped)

It should have done:

What is your age?35
What is today's year?2015
The age you were born was: 1980

Thank you!

Jester
  • 56,577
  • 4
  • 81
  • 125
  • Reading fixed bytes might be wrong. You may be supposed to read only one byte per `int 0x80` and check for `\n', which stands for end of the data. – MikeCAT Nov 21 '15 at 13:53
  • The end of `int_to_string` certainly looks bad: `push eax; ret` That is probably the place of the fault. You should learn to use a debugger and also not copy code blindly without understanding it. – Jester Nov 21 '15 at 13:55
  • Try adding a blank line at the end of the code. Some compilers want that. – sentientmachine Nov 21 '15 at 13:56
  • @Jester, I am partially responsible. This person asked a question yesterday that I commented on and directed them to this [SO Answer](http://stackoverflow.com/a/19312503/3857942) but I didn't follow up with them because I was busy. The original code doesn't have the _push_ – Michael Petch Nov 21 '15 at 16:44

1 Answers1

2

Since I directed you to this SO Answer, I'll followup and point out your two problems in the code you wrote. First of all you have added a push eax to the original int_to_string function. This line should be removed to restore the original function to proper order:

jnz .next_digit     ; Repeat until eax==0
mov eax,esi
push eax   ; REMOVE this
ret

It should be:

jnz .next_digit     ; Repeat until eax==0
mov eax,esi
ret

The primary bug that prevents you from displaying the integer you converted to the string is a misunderstanding of how the string_to_int code is written. In the comments it says:

;code taken from another thread
; Input:
; EAX = integer value to convert
; ESI = pointer to buffer to store the string in (must have room for at least 10 bytes)
; Output:
; EAX = pointer to the first character of the generated string

You properly set up the inputs, but you may not have noticed that EAX returns a pointer to the first character of the string (as an output). You need to use the return address in EAX as the pointer to the string when you write it out to the console.

After you call string_to_int you should temporarily push the value on the stack (or save it in a temporary memory location) and then put that pointer in ECX when you are ready to write out the integer that was converted to a string. If you use push eax then you can amend your code to look like this:

lea esi, [result]
call int_to_string
push eax          ; Save the pointer to the beginning of the string containing the number

mov edx, lenanswer
mov ecx, answer
mov ebx, 1
mov eax, 4
int 0x80

pop ecx           ; Restore the pointer to beginning of number to display
mov edx, 5
mov ebx, 1
mov eax, 4
int 0x80

Making these changes to your code should resolve your problems. I highly recommend learning to use a debugger. It is likely the ddd graphical front end to the GNU Debugger (gdb) is available on your platform. This is a reasonable debugger if you are new to GDB. The best way to maximize usage with assembly language programming is to compile your assembler code with debug information. I recommend doing it something like:

nasm -f elf32 -g -F dwarf filename.asm -o filename.o
ld -melf_i386 filename.o -o filename

This assembles your assembly program with dwarf debugging information (dwarf format can make stepping through assembler easier in GDB/DDD). I use LD to link it to a final executable (use whatever way you normally do for linking). An executable called filename will be produced. You can change the executable name to whatever you wish, I just use filename as an example.

If you have DDD you can launch your program in the debugger with a command like this:

ddd ./filename

The DDD manual can be found here

Community
  • 1
  • 1
Michael Petch
  • 46,082
  • 8
  • 107
  • 198
  • Thank you again. I am highly against using a code blindly and copy pasting it, but I did it just to see if it works. I'll make sure to understand it. Now, why do I have to use eax string pointer if the function stored the full string in the result buffer? why can't I just mov ecx, result , wouldn't it be the same? – user3348294 Nov 21 '15 at 17:45
  • @user3348294 It is was the way the code was written. You should look into general conversion of integers to strings. If you use the divide by 10 method to get access to all the digits to convert to characters you end up processing the characters in reverse order. 1970 would be processed 0791. This code accounts for the reversed digits by building the number in your array from the end of the buffer moving backwards. He returns the position of the beginning of the number in _EAX_ since it likely won't be at the beginning of the buffer you passed (unless you were dealing with a large number) – Michael Petch Nov 21 '15 at 17:54
  • @user3348294 Were you able to get your program functioning with the changes suggested? – Michael Petch Nov 21 '15 at 17:55
  • Yes I did.I understand now. It worked after I compiled it. But what about windows, i tried compiling it with -f win32, but after executing the exe it stops working. Sorry for the inconvenience. I've read somwhere that start and main functions aren't present on windows and need to be replaced with winmain, is that it? – user3348294 Nov 21 '15 at 18:04
  • @user3348294 Your code as written won't run on Windows. `int 0x80` system calls is Linux specific and will likely crash or not do anything useful on Windows. To read and write from input and output devices on Windows is very different. You aren't running your program on Linux? – Michael Petch Nov 21 '15 at 18:05
  • Yes I have but I was just curious to try and compile it for win32. – user3348294 Nov 21 '15 at 18:09
  • @user3348294 ; You basically have to rewrite all the input and output parts of your code to work with Win32 APIs. You may be able to assemble and link this on Windows, but running it as is won't do as you expect. – Michael Petch Nov 21 '15 at 18:10
  • @user3348294 If my answer did actually resolve your issues on Linux you might want to consider accepting it as an answer. More on the hows and whys of accepting an answer can be found [here](http://meta.stackexchange.com/a/5235/271768) . Doing this also marks a question as being solved if there is an answer that actually solves your problem to your satisfaction. – Michael Petch Nov 21 '15 at 18:12