Search code examples
macosassemblyx86-64gnu-assembler

Storing keyboard Input in x64 assembly (Mac OS/X)


I have been trying for some time now to get a number from a keyboard and comparing it with a value on the stack. If it is correct it will print "Hello World!" and if incorrect, it should print out "Nope!". However, what happens now is no matter the input "jne" is called, nope is printed, and segfault. Perhaps one of you could lend a hand.

.section __DATA,__data
    str:
      .asciz "Hello world!\n"

sto:
     .asciz "Nope!\n"

.section __TEXT,__text
.globl _main
_main:
    push %rbp
    mov %rsp,%rbp
    sub $0x20, %rsp
    movl $0x0, -0x4(%rbp)
    movl $0x2, -0x8(%rbp)

    movl $0x2000003, %eax
    mov $0, %edi
    subq $0x4, %rsi
    movq %rsi, %rcx
    syscall

    cmp -0x8(%rbp), %edx
    je L1
    jne L2
    xor %rbx, %rbx
    xor %rax, %rax
    movl $0x2000001, %eax          
    syscall

L1:
    xor %rax, %rax
    movl $0x2000004, %eax         
    movl $1, %edi                 
    movq str@GOTPCREL(%rip), %rsi 
    movq $14, %rdx               
    syscall
    ret

L2:
    xor %eax, %eax
    movl $0x2000004, %eax          
    movl $1, %edi                   
    movq sto@GOTPCREL(%rip), %rsi   
    movq $6, %rdx                 
    syscall
    ret

Solution

  • I would start with this OS/X Syscall tutorial (The 64-bit part in your case). It is written for NASM syntax but the important information is the text and links for the SYSCALL calling convention. The SYSCALL table is found on this Apple webpage. Additional information on the standard calling convention for 64-bit OS/X can be found in the System V 64-bit ABI.

    Of importance for SYSCALL convention:

    • arguments are passed in order via these registers rdi, rsi, rdx, r10, r8 and r9
    • syscall number in the rax register
    • the call is done via the syscall instruction
    • what OS X contributes to the mix is that you have to add 0x20000000 to the syscall number (still have to figure out why)

    You have many issues with with your sys_read system call. The SYSCALL table says this:

    3 AUE_NULL    ALL { user_ssize_t read(int fd, user_addr_t cbuf, user_size_t nbyte); } 
    

    So given the calling convention, int fd is in RDI, user_addr_t cbuf (pointer to character buffer to hold return data) is in RSI, and user_size_t nbyte (maximum bytes buffer can contain) is in RDX.

    Your program seg faulted on the ret because you didn't have proper function epilogue to match the function prologue at the top:

    push %rbp                 #
    mov  %rsp,%rbp            # Function prologue
    

    You need to do the reverse at the bottom, set the result code in RAX and then do the ret. Something like:

    mov %rbp,%rsp             # \ Function epilogue
    pop %rbp                  # /
    xor %eax, %eax            # Return value = 0
    ret                       # Return to C runtime which will exit
                              #     gracefully and return to OS
    

    I did other minor cleanup, but tried to keep the structure of the code similar. You will have to learn more assembly to better understand the code that sets up RSI with the address for sys_read SYSCALL . You should try to find a good tutorial/book on x86-64 assembly language programming in general. Writing a primer on that subject is beyond the scope of this answer.

    Code that might be closer to what you were looking for that takes the above into account:

    .section __DATA,__data
    str:
        .asciz "Hello world!\n"
    
    sto:
        .asciz "Nope!\n"
    
    .section __TEXT,__text
    
    .globl _main
    _main:
        push %rbp                 #
        mov  %rsp,%rbp            # Function prologue
    
        sub  $0x20, %rsp          # Allocate 32 bytes of space on stack
                                  #     for temp local variables
    
        movl $0x2, -4(%rbp)       # Number for comparison
    
                                  # 16-bytes from -20(%rbp) to -5(%rbp)
                                  #     for char input buffer
        movl $0x2000003, %eax
        mov $0, %edi              # 0 for STDIN
        lea -20(%rbp), %rsi       # Address of temporary buffer on stack
        mov $16, %edx             # Read 16 character maximum
        syscall
    
        movb (%rsi), %r10b        # RSI = pointer to buffer on stack
                                  # get first byte
        subb $48, %r10b           # Convert first character to number 0-9
        cmpb -4(%rbp), %r10b      # Did we find magic number (2)?
        jne L2                    #     If No exit with error message
    
    L1:                           # If the magic number matched print
                                  #     Hello World
        xor %rax, %rax
        movl $0x2000004, %eax
        movl $1, %edi
        movq str@GOTPCREL(%rip), %rsi
        movq $14, %rdx
        syscall
        jmp L0                    # Jump to exit code
    
    L2:                           # Print "Nope"
        xor %eax, %eax
        movl $0x2000004, %eax
        movl $1, %edi
        movq sto@GOTPCREL(%rip), %rsi
        movq $6, %rdx
        syscall
    
    L0:                           # Code to exit main
        mov %rbp,%rsp             # \ Function epilogue
        pop %rbp                  # /
        xor %eax, %eax            # Return value = 0
        ret                       # Return to C runtime which will exit
                                  #     gracefully and return to OS