0

I wrote this sample program in assembly to train with syscall and function. The first function printString works correctly, it prints a given string. The problem is in the readString function, it should read STRLEN characters and then returns:

section .data
; Define standard constants.
LF equ 10 ; line feed
NULL equ 0 ; end of string
EXIT_SUCCESS equ 0 ; success code
STDIN equ 0 ; standard input
STDOUT equ 1 ; standard output
SYS_read equ 0 ; read
SYS_write equ 1 ; write
SYS_exit equ 60 ; terminate
; Define some strings.
STRLEN equ 10
pmpt db "Enter Text: ", NULL
newLine db LF, NULL
section .bss
chr resb 1
inLine resb STRLEN+2 ; total of 52
section .text
global _start
_start:
; Display prompt.
mov rdi, pmpt
call printString

mov rdi, inLine
mov rsi, STRLEN
mov rdx, chr
call readString

; Output the line to verify successful read
mov rdi, rax
call printString

; Example done.
exampleDone:
mov rax, SYS_exit
mov rdi, EXIT_SUCCESS
syscall

; ******************************************************
; Generic procedure to display a string to the screen.
; String must be NULL terminated.
; Algorithm:
; Count characters in string (excluding NULL)
; Use syscall to output characters
; Arguments:
; 1) address, string
; Returns:
; nothing
global printString
printString:
push rbx
; Count characters in string.
mov rbx, rdi
mov rdx, 0
strCountLoop:
cmp byte [rbx], NULL
je strCountDone
inc rdx
inc rbx
jmp strCountLoop
strCountDone:
cmp rdx, 0
je prtDone
; Call OS to output string.
mov rax, SYS_write ; system code for write()
mov rsi, rdi ; address of char's to write
mov rdi, STDOUT ; standard out
; RDX=count to write, set above
syscall ; system call
; String printed, return to calling routine.
prtDone:
pop rbx
ret

global readString
readString:
push rbx
push r12
push r11

mov rcx, rsi
; Read characters from user (one at a time)
mov rbx, rdi ; inLine addr
mov r11, rdx
mov r12, 0 ; char count
readCharacters:
    mov rax, SYS_read; system code for read
    mov rdi, STDIN; standard in
    lea rsi, byte[r11] ; address of chr
    mov rdx, 1 ; count (how many to read)
    syscall ; syscall
    mov al, byte [r11] ; get character just read
    cmp al, 10 ; if linefeed, input done
    je readDone
    inc r12 ; count++
    cmp r12, rcx ; if # chars > STRLEN
    jae readCharacters ; stop placing in buffer
    mov byte [rbx], al ; inLine[i] = chr
    inc rbx ; update tmpStr addr
    jmp readCharacters
readDone:
mov al, bl

pop r11
pop r12
pop rbx
ret

In the last sixth line mov al, bl so I return the result to the main function. But then when I pass to the printString it goes in segmentation fault. I debugged in gdb the rbx register with x/s $rbx but effectively it doesn't contain the string, but why? If I write the code in the readString in the main string, it correctly works. I think that I lost something when I pass the arguments and with pushing and popping registers in the function.

EDIT This is the registers content:

(gdb) info register
rax            0x1  1
rbx            0x402011 4202513
rcx            0x4010a0 4198560
rdx            0x1  1
rsi            0x402010 4202512
rdi            0x0  0
rbp            0x0  0x0
rsp            0x7fffffffe1b0   0x7fffffffe1b0
r8             0x0  0
r9             0x0  0
r10            0x0  0
r11            0x206    518
r12            0x0  0
r13            0x0  0
r14            0x0  0
r15            0x0  0
rip            0x4010a0 0x4010a0 <readCharacters+26>
eflags         0x206    [ PF IF ]
cs             0x33 51
ss             0x2b 43
ds             0x0  0
es             0x0  0
fs             0x0  0
gs             0x0  0

It goes in segmentation fault on line 93 that is the syscall to STDIN.

Could you kindly help me? Thanks in advance

DarkSkull
  • 1,041
  • 3
  • 13
  • 23
  • My NASM doesn't want to assemble `lea rsi, byte[r11] `. Did you realize that SYS_READ doesn't return immediately, but waits until the ENTER key is pressed? – rkhb Sep 10 '18 at 19:58
  • Yes, I know. I waits until `\n` character. – DarkSkull Sep 10 '18 at 19:59
  • Not exactly. It waits until the file descriptor has any data available. When stdin is a TTY in canonical mode, one way to "submit" what you type is with enter; another is with control-D. (which sets an end-of-file condition if you press it on an empty line, but not on a non-empty line.) – Peter Cordes Sep 10 '18 at 20:02
  • 1
    This is really hard to read. Intent your instructions more than your labels. I think your problem might be you only reserve 1 byte for `chr`, but you appear to be using multi-byte strings. What instruction segfaults, and what values are in registers when it does? – Peter Cordes Sep 10 '18 at 20:03
  • Yes, the byte for `chr` is 1 because I read one charatcter at time so I can check the length of the string is equal to `STRLEN`. I'll update soon with registers content. – DarkSkull Sep 10 '18 at 20:09
  • `syscall` clobbers `r11`, using it to save RFLAGS. It's not `syscall` that faults, it's the `mov al, byte [r11]`. – Peter Cordes Sep 10 '18 at 20:28
  • So, please tell me how can I fix it. – DarkSkull Sep 10 '18 at 20:32
  • 1
    Use any register other than RCX or R11 to keep values across `syscall`. BTW, in the normal x86-64 System V calling convention, R11 isn't call-preserved. You don't have to push/pop it to save/restore the caller's value. But RBX is. – Peter Cordes Sep 10 '18 at 20:32
  • If you write as an answer I'll accept it. Thank you very much! – DarkSkull Sep 10 '18 at 20:34

0 Answers0