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