0

I am writing a basic ASM code using MASM & Irvine32. The code is that it takes input character wise, user enters how many characters he wants to enter & the loop runs that many times. The Procedure only accepts Alphabets. Displays rejected messages if num or something else pressed. Returns to Main (calling function) if ecx == 0 (number entered by user). Now my Problem is that the ret keyword gives back the control to the start of the same Procedure(called procedure) instead of going back to main(caller).

include Irvine32.inc
.data
Input_Prompt BYTE "Enter String:", 0
Max_Length_input BYTE "Enter max length to read:", 0
Rejected_mess BYTE "Rejected !", 0
ret_mess BYTE "returing mess !", 0
USER_STR BYTE ?
.code
main PROC
    mov edx, offset Max_Length_input    ;ask for max length
    call WriteString
    call Readint
    mov ecx, eax
    
    mov esi, offset USER_STR ;passing offset of storage
    call String_Input
main endp

String_Input PROC
    mov ebx, 0
    mov edx, offset Input_Prompt ;ask for input
    l1:
        call WriteString
        call Readchar
        call writechar
        call crlf
        cmp al, 'a'
        JA L2
        cmp al, 'z'
        JB L2
        L2:
        cmp al, 'A'
        JA break
        JB Reject
        cmp al, 'Z'
        JB break
        JA Reject
        break:
        mov [esi+ebx], al   ;filling chars in the storage
        inc ebx
    loop l1
    mov edx, offset USER_STR    ;displaying final data in storage
    call writestring
    
    mov edx, offset ret_mess
    call crlf
    call writestring
    call crlf
    ret 

    reject:
    mov edx, offset Rejected_mess
    call crlf
    call writestring
String_Input endp
exit
end main

There is a similar question on Stack overflow RET function returning to beginning of code instead of CALL point But that has Push and pop whereas I haven't touched the Stack. What am I missing. Thanks for ur help

  • 1
    Debug by single stepping line by line. Observe the value of the `esp` register upon entry to the function in question, and the return address that is on the top of the stack. Then single step through the function line by line, validating both the `esp` register value and the top of stack value (the return address) haven't changed. If either has changed, that's where to start looking. – Erik Eidt Dec 21 '22 at 18:33
  • 2
    It seems `main` is missing a `ret` and under reject a `ret` is missing – Michael Petch Dec 21 '22 at 18:35
  • Yes I did step by step debugging and that's how I came to know that as soon as ret is hit the control goes to start of fucntion. But the call stack is understandable. – Wakeel Furqan Ahmed Dec 21 '22 at 18:36
  • yeah I included ret in reject label but same happens once that ret is hit. – Wakeel Furqan Ahmed Dec 21 '22 at 18:36
  • 2
    Ok it is returning properly, but there is missing assembly code at `main` for it to return to. Since the function is after `main`, that's where it goes. What instruction in `main` do you expect it to return to (as there aren't any left)? – Erik Eidt Dec 21 '22 at 18:37
  • thanks ret in main made it work. What's the logical explanation to this. I never use to put ret in main – Wakeel Furqan Ahmed Dec 21 '22 at 18:39
  • 2
    `main endp` doesn't do a `ret` for you. In this case it will start executing the code in `string_input` because it fell right below `main`. Whether it worked or not may be on whether there were functions after `main` and what code they contained. – Michael Petch Dec 21 '22 at 18:51
  • Related: [What if there is no return statement in a CALLed block of code in assembly programs](https://stackoverflow.com/q/41205054) / [Why is no value returned if a function does not explicity use 'ret'](https://stackoverflow.com/q/20578122) . But not exact duplicates because your `String_Input` has a `ret` along one path but an `exit` along the error path of execution. (After the `endp` but it's still the next instruction in memory). Not sure if that `exit` was supposed to be part of `main` or what, in which case you're putting one function's machine code in the middle of another. – Peter Cordes Dec 22 '22 at 20:38

1 Answers1

3

Assembly's control flow is a lot like BASIC's, in the sense that once the program begins it will keep going to the next "line" unless redirected. This is the hardcoded behavior of the eip register; it's set to a starting address and reads the bytes at that address to determine what instruction to run, then adds the size of that instruction to its own internal value and repeats. If your code has no control flow instructions like jmp,call, and ret, the CPU will just run all the code in your program in the order it was written. Labels and procs aren't seen by the CPU; they exist solely for your convenience. The assembler translates them into a memory address that is relative to the starting location (i.e. the .code label.)

.code
main PROC
    mov edx, offset Max_Length_input    ;ask for max length
    call WriteString
    call Readint
    mov ecx, eax
    
    mov esi, offset USER_STR ;passing offset of storage
    call String_Input
main endp

Once your code gets to call String_Input, the eip register is redirected to the instruction mov ebx, 0 (because it's the instruction directly underneath String_Input PROC and continues forward from there. Inside your String_Input PROC is a ret, and when the ret is executed, the eip register is set to the instruction immediately after call String_Input. And that is where the problem lies, as main endp isn't an instruction, it just exists for your convenience. The next instruction is mov ebx,0, which is the first instruction of your String_Input routine. Which is why the ret appears to just give control flow back to the same function again- because it comes directly after the call!

The main takeaway is that labels and proc declarations do not alter control flow, unlike curly braces in C and other high level languages.

Easiest way to fix this problem is to have a ret at the end of your proc. main is a notable exception to this rule, as the way you properly exit main is platform-specific. In your case it's most likely not ret but I don't know the exact method. Maybe it's ret, maybe not. You'll have to read the documentation for your OS and environment.

puppydrum64
  • 1,598
  • 2
  • 15
  • 1
    While it is best to not us `ret` to return from `main` it often will work as it is likely returning to the runtime which will exit (but there can be issues with closing multiple threads etc). If you notice in his code there is an `exit` after `string_input` near the bottom. That `exit` is an Irvine32 macro that does `ExitProcess`. That `exit` should be moved to the end of `main` PROC and replace a `ret`. – Michael Petch Dec 22 '22 at 13:14
  • @MichaelPetch: Why would it be better not to `ret` from `main`? If you're calling it `main`, it should be a real function that's called with a valid return address. If it's a process entry point that isn't a function, normally that should get named `_start` or some other special name. But yeah, calling an exit function is good, too, and in this case they put their `string_input` in the middle of their `main`! – Peter Cordes Dec 22 '22 at 20:34
  • 1
    @PeterCordes : Because in some circumstances returning from `main` on Windows doesn't properly handle multiple threads (and in some cases another thread could be created as part of process creation). If you are using the C runtime then it should work because that will eventually call `ExitProcess`. `ret` from `main` will cause the current thread to terminate if `main` is called directly from Windows (which is usually the case with assembly code). To make sure that isn't a problem it is better to call `ExitProcess`, or `exit` in Irvine32 or use `.exit` in 32-bit MASM code. – Michael Petch Dec 22 '22 at 20:42
  • Yes as u said to bring exit after main endp, and remove ret from main. That's also working – Wakeel Furqan Ahmed Dec 23 '22 at 14:10