1

Whenever the user inputs s, the expected value in the rax register that the buffer is moved to would be 73, but instead it is a73. Why is this? I need these two values to be equal in order to perform the jumps I need for the user input menu.

On any user input, the information in the register is always preceded by an a, while the register that I use to check for the value is not. This makes it impossible to compare them for a jump.

Any suggestions?

different values in rax

section .data
prompt: db 'Enter a command: '

section .bss
buffer: resb 100; "reserve" 32 bytes

section .text ; code
    global _start

_start:
     mov rax, 4 ; write
     mov rbx, 1 ; stdout
     mov rcx, prompt ; where characters start
     mov rdx, 0x10 ; 16 characters
     int 0x80

     mov rax, 3 ; read
     mov rbx, 0 ; from stdin
     mov rcx, buffer ; start of storage
     mov rdx, 0x10; no more than 64 (?) chars
     int 0x80

     mov rax, [buffer]
     mov rbx, "s"
     cmp rax, rbx
     je _s

     ; return to Linux
     mov rax, 1
     mov rbx, 0
     int 0x80
_s:
     add r8, [buffer]

     ; dump buffer that was read
     mov rdx, rax ; # chars read
     mov rax, 4 ; write
     mov rbx, 1 ; to stdout
     mov rcx, buffer; starting point
     int 0x80

     jmp _start
Barmar
  • 741,623
  • 53
  • 500
  • 612

1 Answers1

2

If the user types s, followed by <enter>, the memory starting at the address of buffer will contain bytes ['s', '\n', '\0', '\0', ...] (where the newline byte '\n' is from pressing <enter> and the null bytes '\0' are from the .bss section being initialized to 0). As integers, represented in hex, the corresponding values in memory are [0x73, 0x0A, 0x00, 0x00, ...].

The mov rax, [buffer] instruction will copy 8 bytes of memory starting at the address of buffer to the rax register. The byte ordering is little endian on x86, so the 8 bytes will be loaded from memory in reversed order, resulting in rax having 0x0000000000000A73.

Workarounds

This workaround is based on Peter Cordes's comment below. The idea is to compare 1) the first byte starting at the address of buffer with 2) the byte 's'. This would replace the three lines in your question that 1) move [buffer] to rax, 2) move 's' to rbx, and 3) cmp rax, rbx.

     cmp byte [buffer], 's'
     je _s

This would check that the first character entered is 's', even if followed by other characters. If your intent is to check that only a single character 's' is entered (optionally followed by '\n' in the case that <enter> was pressed to end the input, as opposed to <ctrl-d>), a more thorough approach could utilize the value returned by the read system call, which indicates how many bytes were read.

Without checking how many characters are read, you might want to clear the buffer on each iteration. As is, a user could enter 's' on one iteration, followed by <ctrl-d> on the next iteration, and the buffer would still start with an 's'.

Band-aid Workarounds

(I had originally proposed the following two ideas as workarounds, but they have their own problems that Peter Cordes's identifies in the comments below)

To work around the issue, one option could be to add the newline to your target for comparison.

     mov rax, [buffer]
     mov rbx, `s\n`  ; the second operand was formerly "s"
     cmp rax, rbx
     je _s

Alternatively, specifying that the read system call only consume 1 byte could be another approach to address the issue.

     mov rax, 3  ; read
     mov rbx, 0  ; from stdin
     mov rcx, buffer  ; start of storage
     mov rdx, 0x01  ; the second operand was formerly 0x10
     int 0x80
dannyadam
  • 3,950
  • 2
  • 22
  • 19
  • 1
    Generally it's good to mention a solution as well as describe the problem. Your description is great, but if someone is having this problem in the first place they probably don't know about `movzx eax, byte [buffer]` or `cmp byte [buffer], 's'` – Peter Cordes Dec 01 '20 at 07:54
  • Thanks for the feedback @PeterCordes! I've updated the reply to propose possible solutions. – dannyadam Dec 01 '20 at 21:09
  • 1
    Your methods would only work if `s\n` was the first input. If the first line of input was `abcdef...`, then the high bytes would be non-zero after you do type `s\n`. You definitely should not be encouraging the OP to use an 8-byte load when they only want to compare 1 or 2. 4-byte `mov` and `cmp ax,bx` could be usable, or just movzx, or `cmp word [buffer], \`s\n\``. Also, doing 1-byte reads would mean you'd stop at any `s`, even if not at the beginning of a line. If you want that, no reason to read 1 byte at a time, just loop over the buffer after doing a normal-sized read. – Peter Cordes Dec 02 '20 at 03:17
  • 1
    Also, requiring a `\n` would mean it doesn't work if you "submit" the input buffer by hitting control-D once (if typing on a terminal). Not handling that kind of shenanigans is probably fine for a toy program, though. But try `strace cat > /dev/null` and type stuff, hitting control-d on non-empty lines, to see what happens. – Peter Cordes Dec 02 '20 at 03:19
  • 1
    Also, since you're showing code making a system call in your answer, you shouldn't be using the 32-bit ABI in 64-bit mode. [What happens if you use the 32-bit int 0x80 Linux ABI in 64-bit code?](https://stackoverflow.com/q/46087730). Not mentioning that problem with the code in the question is one thing, but using the wrong way in your answer kind of endorses it, which you shouldn't do. – Peter Cordes Dec 02 '20 at 03:44
  • Thanks again @PeterCordes! I've updated the answer to include an idea from your first comment above, using `cmp byte [buffer], 's'`. – dannyadam Dec 02 '20 at 07:02
  • *Without checking how many characters are read* - But you *can* and should check that, instead of clearing the buffer. `read` returns a length (or -errno) in RAX. (Or in EAX if you use the 32-bit int 0x80 version.) And BTW, there are advantages for beginners to loading into registers: it lets them see with a debugger. So I'd suggest showing `movzx eax, word [buffer]`, or byte if you want only 1. (Then `cmp`-immediate with the character / string and either byte or dword register size). Writing a 32-bit register implicitly zero-extends to 64-bit, there's nothing better about using RAX. – Peter Cordes Dec 02 '20 at 07:43