0

I'm trying to get the following to jump back to main, which prints a menu. In another function (that works correctly) I used the jmp main command. However, that function only prints a message indicating that it got to that function before printing the menu again.

If I use just one of the last three lines, I get a seg fault error. Same if I use leave and return together. I'm assuming it has something to do with calling scanf.

read_int:
    mov    edi, enterintmsg
    mov    eax,0
    call   printf           
    lea    rdi, [LC5]
    lea    rsi, [value]
    xor    eax,eax
    call   scanf
    mov    cl, [value]
    jmp    main
    ;leave
    ;ret    

Current code:

bits 64
global  main
    extern  puts
extern  printf
extern  scanf   
extern  get_kb



section.data
 ;LC0
errormsg:   db  'Invalid Input. Enter N,F, or X',0x0D,0x0a,0

;LC1
numequalsmsg:   db  'Number equals: '

LC2:    db  "%d",0


menuprompt: db  0x0D,0x0a,'Enter N to enter an integer from 0 to 20',0x0D,0x0a,'Enter F to display the first N+1 numbers (beginning with zero) on the console',0x0D,0x0a,'Enter X to quit the program',0x0D,0x0a,0


choicemsg:  db  "Your Choice: ",0

LC5:    db  "%d",0

;LC6
enterintmsg:    db  "Enter and integer 0-20: ",0

;LC7
enternummsg:    db  'Enter a valid number between 0 and 20',0x0D,0x0a,0


LC8:    db  " , ",0
LC9:    db  'Success!',0x0D,0x0a,0
LC10:   db  'In L10!',0x0D,0x0a,0       
LC11:   db  'In L12!',0x0D,0x0a,0 
LC13:   db  'In compare to zero section',0x0D, 
value:  db  0
choice: db  1


.code
main:
    ;function setup 
    push    rbp
    mov rbp, rsp
    sub rsp, 16

menu:
    ;print menu 
    mov edi, menuprompt
    call    puts            ;display menu
    mov edi,choicemsg
    ;mov    eax, 0
    ;call   printf          ;display "Your choice:" 
    call puts
    ;call   getn    
    ;push choice
    ;push LC5       ;string format
    ;call scanf     ;stores input in choice
    ;GetLInt     [choice]
    ;mov    ebx, choice
    ;lea    rdi, [LC5]
    ;lea    rsi, [choice]
    ;xor    eax,eax
    ;call scanf
    ;mov    bl, [choice]
    call    get_kb
    mov bl, al
    cmp bl, 'N' ;N
    je  entered_n
    cmp bl, 'F' ;F
    je  correct
    cmp bl, 'X' ;X
    je  correct
    ;leave
    ;ret

    jmp menu
    ;ret

correct:
    mov edi, LC9
    mov eax,0
    call    printf
    jmp     menu
    ;leave  
    ;ret 

entered_n:
    call    read_int
    ;jmp    menu
    ;leave
    jmp menu

read_int:
    mov edi, enterintmsg
    mov eax,0
    call    printf          

    lea rdi, [LC5]
    lea rsi, [value]
    xor eax,eax

    ;add    esp,4       ;remove parameters
    push    rsi
    push    rdi 
    call scanf
    mov cl, [value]
    ;jmp    menu
    ;leave
    ret


     ;leave 
user3866044
  • 181
  • 6
  • 20
  • Why would you jump to `main` and abandon the current stack frame etc? Does `main` reset the stack pointer? – Weather Vane Jul 21 '15 at 17:24
  • It does indeed. I had it set up to where main only set the stack frame and then called menu, but couldn't get it to leave either function to return to menu. – user3866044 Jul 21 '15 at 17:26
  • Changed it back to where main only sets up the stack. The menu function is called after printing the success function, but if I cal the read_int function above I still get the seg fault error. changed jmp main to jmp menu. – user3866044 Jul 21 '15 at 17:29
  • 1
    How are you calling the fuction? – Ross Ridge Jul 21 '15 at 17:33
  • 1
    I never coded with 64 bit but I understand the calling conventions are different from 32 bit. I'm curious as to why you use 32-bit registers for `printf` but 64-bit for `scanf`. – Weather Vane Jul 21 '15 at 17:33
  • calling it from the menu function with cmp bl, 'N' je read_int – user3866044 Jul 21 '15 at 17:43
  • It's definitely something with scanf. works when commenting that out. – user3866044 Jul 21 '15 at 18:02
  • Note that `LC13` won't have a string terminator after you have written to `value`, which was previously a default terminator. I don't see you writing to `value` but I do wonder what is meant by `lea rsi, [value]` when it's a byte field, followed by another byte field `choice`. – Weather Vane Jul 21 '15 at 19:16
  • FYI, this followup(?) has the same bugs I fixed on [How to use data stored in register when calling scanf in nasm assembly](https://stackoverflow.com/q/31523170) - missing space in `section .data`, and using `.code` (which just declares a label) instead of a `section .text` directive. Eventually a followup [Any ideas why this is crashing after calling scanf in the readint function (Nasm Assembly)?](https://stackoverflow.com/q/31644669) got to that bug. – Peter Cordes Mar 12 '20 at 20:58

1 Answers1

3

To return a function you need to use the RET instruction. For the RET instruction to work the the return address, the location the instruction should jump to, needs to be at the top of the stack. Normally this return address is pushed on the stack by the CALL instruction, however you didn't use the CALL instruction to call the function. Instead you used JE read_int. This means your function has no idea where it was called from and so a RET instruction can't resume executing the previous function where it left off.

The solution is to change the code that calls read_int to something like this:

     cmp bl, 'N'
     jne not_n

     call read_int
     ; read_int returns here so put whatever code 
     ; that needs to be executed afterwards here

not_n:
     ; code handling the case when BL != 'N'

You can then change read_int to use RET, and only RET, to return from the function.

Using JMP main to return the main function doesn't work, because it does exactly what it says it does. It jumps to the address of main which is the first instruction in of the main function. This causes your main function to be executed again from the start.

You don't want to use the LEAVE instruction unless you set up a stack frame. Your read_int function doesn't set up stack frame, so using it will likely causes your code to crash.

Ross Ridge
  • 38,414
  • 7
  • 81
  • 112
  • Since I'll have three options, N,F, and X, could I have it cmp to N, jump to "equals n", which would call read_int? – user3866044 Jul 21 '15 at 18:13
  • I'm ready to rip my hair out!Added my current code to the main question. This was super easy converting from C, but hard to write without it. Still getting a seg fault. Tried several things. – user3866044 Jul 21 '15 at 18:40
  • @user3866044 time for a walk in the woods? BTW you said earlier that `main` resets the stack pointer. Your recent added code shows it does not. It consumes more stack, equivalent to a memory leak. – Weather Vane Jul 21 '15 at 18:50
  • @user3866044 a jump to `main` would only be done in embedded code where there is no master to rescue the computer. It's a last resort to be used only when there is a critical failure that can't be handled by the procedural system. – Weather Vane Jul 21 '15 at 18:59
  • @WeatherVane I took the main jmp out. it's supposed to jump to menu now. The other few programs I've written in assembly used that function setup in main, figured it reset the stack after another user mentioned it. – user3866044 Jul 21 '15 at 19:02