Which Linux system calls should I use to read raw characters from stdin? - node.js

I'm trying to port my stdrepl library to FASM for learning purposes. I know that the GNU readline library already does what I'm trying to do, but I want to learn how to write non-trivial programs in assembly.
In node.js I can easily create a tty by writing:
var stdin = process.stdin;
stdin.setEncoding("utf8");
stdin.setRawMode(true);
stdin.resume();
How do I achieve the same results in pure assembly. I tried reading one byte from stdin at a time in a loop as follows, but it doesn't return the byte right after I hit a key:
oct db ?
mov eax, 3
xor ebx, ebx
mov ecx, oct
mov edx, 1
Note that the data definition oct is not a part of the loop, so please don't smite me for that. I know how to structure an assembly program.

Sorry for the delay (I really should "register" here - that'll get me "notifications", right?). As I said, it's rudimentary and imperfect. Some of the "usual" stuff may be defined elsewhere, but I think you can figure out how to get it to assemble. Just call it - no parameters - and the key is returned in al. Hope it's some use to you!
;-----------------------------
; ioctl subfunctions
%define TCGETS 0x5401 ; tty-"magic"
%define TCSETS 0x5402
; flags for 'em
%define ICANON 2 ;.Do erase and kill processing.
%define ECHO 8 ;.Enable echo.
struc termios
alignb 4
.c_iflag: resd 1 ; input mode flags
.c_oflag: resd 1 ; output mode flags
.c_cflag: resd 1 ; control mode flags
.c_lflag: resd 1 ; local mode flags
.c_line: resb 1 ; line discipline
.c_cc: resb 19 ; control characters
endstruc
;---------------------------------
getc:
push ebp
mov ebp, esp
sub esp, termios_size ; make a place for current kbd mode
push edx
push ecx
push ebx
mov eax, __NR_ioctl ; get current mode
mov ebx, STDIN
mov ecx, TCGETS
lea edx, [ebp - termios_size]
int 80h
; monkey with it
and dword [ebp - termios_size + termios.c_lflag], ~(ICANON | ECHO)
mov eax, __NR_ioctl
mov ebx, STDIN
mov ecx, TCSETS
lea edx, [ebp - termios_size]
int 80h
xor eax, eax
push eax ; this is the buffer to read into
mov eax, __NR_read
mov ebx, STDIN
mov ecx, esp ; character goes on the stack
mov edx, 1 ; just one
int 80h ; do it
; restore normal kbd mode
or dword [ebp - termios_size + termios.c_lflag], ICANON | ECHO
mov eax, __NR_ioctl
mov ebx, STDIN
mov ecx, TCSETS
lea edx, [ebp - termios_size]
int 80h
pop eax ; get character into al
pop ebx ; restore caller's regs
pop ecx
pop edx
mov esp, ebp ; leave
pop ebp
ret
;-------------------------

Related

To display characters in reverse order using nasm [infinite loop running]

THE PROGRAM IS USED TO ACCEPT CHARACTERS AND DISPLAY THEM IN REVERSE ORDER
The code is included here:
section .bss
num resb 1
section .text
global _start
_start:
call inputkey
call outputkey
;Output the number entered
mov eax, 1
mov ebx, 0
int 80h
inputkey:
;Read and store the user input
mov eax, 3
mov ebx, 2
mov ecx, num
mov edx, 1
int 80h
cmp ecx, 1Ch
je .sub2
push ecx
jmp inputkey
.sub2:
push ecx
ret
outputkey:
pop ecx
;Output the message
mov eax, 4
mov ebx, 1
;mov ecx, num
mov edx, 1
int 80h
cmp ecx, 1Ch
je .sub1
jmp outputkey
.sub1:
ret
The code to compile and run the program
logic.asm
is given here:
nasm -f elf logic.asm
ld -m elf_i386 -s -o logic logic.o
./logic
There are a few problems with the code. Firstly, for the sys_read syscall (eax = 3) you supplied 2 as the file descriptor, however 2 refers to stderr, but in this case you'd want stdin, which is 0 (I like to remember it as the non-zero numbers 1 and 2 being the output).
Next, an important thing to realize about the ret instruction is that it pops the value off the top of the stack and returns to it (treating it as an address). Meaning that even if you got to the .sub2 label, you'd likely get a segfault. With this in mind, the stack also tends to not be permanent storage, as in it is not preserved throughout procedures, so I'd recommend just making your buffer larger to e.g. 256 bytes and increment a value to point to an index in the buffer. (Using a fixed-size buffer will keep you from getting into the complications of memory allocation early, though if you want to go down that route you could do an external malloc call or just an mmap syscall.)
To demonstrate what I mean by an index into the reserved buffer:
section .bss
buf resb 256
; ...
inputkey:
xor esi, esi ; clear esi register, we'll use it as the index
mov eax, 3
mov ebx, 0 ; stdin file descriptor
mov edx, 1 ; read one byte
.l1: ; loop can start here instead of earlier, since the values eax, ebx and edx remain unchanged
lea ecx, [buf+esi] ; load the address of buf + esi
int 80h
cmp [buf+esi], 0x0a ; check for a \n character, meaning the user hit enter
je .e1
inc esi
jmp .l1
.e1:
ret
In this case, we also get to preserve esi up until the output, meaning that to reverse the input, we just print in descending order.
outputkey:
mov eax, 4
mov ebx, 1 ; stdout
mov edx, 1
.l2:
lea ecx, [buf+esi]
int 80h
test esi, esi ; if esi is zero it will set the ZF flag
jz .e2:
jmp .l2
.e2:
ret
Note: I haven't tested this code, so if there are any issues with it let me know.

How do you bind a socket in x64? [duplicate]

Hello all.
So I'm learning assembly.And as per my usual learning steps with any new language I pick up I've arrived at networking with assembly.
Which, sadly isn't going that well as I've pretty much failed at step 0, which would be getting a socket through which communication can begin.
The assembly code should be roughly equal to the following C code:
#include <stdio.h>
#include <sys/socket.h>
int main(){
int sock;
sock = socket(AF_INET, SOCK_STREAM, 0);
}
(Let's ignore the fact that it's not closing the socket for now.)
So here's what I did thus far:
Checked the manual. Which would imply that I need to make a socketcall() this is all good and well. The problem starts with that it would need an int that describes what sort of socketcall it should make. The calls manpage isn't helping much with this either as it only describes that:
On a some architectures—for example, x86-64 and ARM—there is no
socketcall() system call; instead socket(2), accept(2), bind(2), and
so on really are implemented as separate system calls.
Yet there are no such calls in the original list of syscalls - and as far as I know the socket(), accept(), bind(), listen(), etc. are calls from libnet and not from the kernel. This got me utterly confused so I've decided to compile the above C code and check up on it with strace. This yielded the following:
socket(PF_INET, SOCK_STREAM, IPPROTO_IP) = 3
While that didn't got me any closer to knowing what socket() is it did explain it's arguments. For witch I don't seem to find the proper documentation (again). I thought that PF_INET, SOCK_STREAM, IPPROTO_IP would be defined in <sys/socket.h> but my grep-ing for them didn't seem to find anything of use. So I decided to just wing it by using gdb in tandem with disass main to find the values. This gave the following output:
Dump of assembler code for function main:
0x00000000004004fd <+0>: push rbp
0x00000000004004fe <+1>: mov rbp,rsp
0x0000000000400501 <+4>: sub rsp,0x10
0x0000000000400505 <+8>: mov edx,0x0
0x000000000040050a <+13>: mov esi,0x1
0x000000000040050f <+18>: mov edi,0x2
0x0000000000400514 <+23>: call 0x400400
0x0000000000400519 <+28>: mov DWORD PTR [rbp-0x4],eax
0x000000000040051c <+31>: leave
0x000000000040051d <+32>: ret
End of assembler dump.
In my experience this would imply that socket() gets it's parameters from EDX (PF_INET), ESI (SOCK_STREAM), and EDI (IPPROTO_IP). Which would be odd for a syscall (as the convention with linux syscalls would be to use EAX/RAX for the call number and other registers for the parameters in increasing order, eg. RBX, RCX, RDX ...). The fact that this is beaing CALL-ed and not INT 0x80'd would also imply that this is not in fact a system call but rather something thats being called from a shared object. Or something.
But then again. Passing arguments in registers is very odd for something that's CALL-ed. Normally as far as I know argument's for called things should be PUSH-ed onto the stack, as the compiler can't know what registers they would try to use.
This behavior becomes even more curious when checking the produced binary with ldd:
linux-vdso.so.1 (0x00007fff4a7fc000)
libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f56b0c61000)
/lib64/ld-linux-x86-64.so.2 (0x00007f56b1037000)
There appears to be no networking library's linked.
And that's the point where I've ran out of ideas.
So I'm asking for the following:
A documentation that describes the x86-64 linux kernel's actual syscalls and their associated numbers. (Preferably as a header file for C.)
The header files that define PF_INET, SOCK_STREAM, IPPROTO_IP as it really bugs me that I wasn't able to find them on my own system.
Maybe a tutorial for networking in assembly on x86-64 linux. (For x86-32 it's easy to find material but for some reason I came up empty with the 64 bits stuff.)
Thanks!
The 64 bit calling convention does use registers to pass arguments, both in user space and to system calls. As you have seen, the user space convention is rdi,rsi, rdx, rcx, r8, r9. For system calls, r10 is used instead of rcx which is clobbered by the syscall instruction. See wikipedia or the ABI documentation for more details.
The definitions of the various constants are hidden in header files, which are nevertheless easily found via a file system search assuming you have the necessary development packages installed. You should look in /usr/include/x86_64-linux-gnu/bits/socket.h and /usr/include/linux/in.h.
As for a system call list, it's trivial to google one, such as this. You can also always look in the kernel source of course.
socket.asm
; Socket
; Compile with: nasm -f elf socket.asm
; Link with (64 bit systems require elf_i386 option): ld -m elf_i386 socket.o -o socket
; Run with: ./socket
%include 'functions.asm'
SECTION .text
global _start
_start:
xor eax, eax ; init eax 0
xor ebx, ebx ; init ebx 0
xor edi, edi ; init edi 0
xor esi, esi ; init esi 0
_socket:
push byte 6 ; push 6 onto the stack (IPPROTO_TCP)
push byte 1 ; push 1 onto the stack (SOCK_STREAM)
push byte 2 ; push 2 onto the stack (PF_INET)
mov ecx, esp ; move address of arguments into ecx
mov ebx, 1 ; invoke subroutine SOCKET (1)
mov eax, 102 ; invoke SYS_SOCKETCALL (kernel opcode 102)
int 80h ; call the kernel
call iprintLF ; call our integer printing function (print the file descriptor in EAX or -1 on error)
_exit:
call quit ; call our quit function
more docs...
this is for x86 system. if you want use for x86_64 system change x86 register to x86_64. for example change 'eax' to 'rax' or 'esp' to 'rsp'. and change syscall value in eax(rax), see https://chromium.googlesource.com/chromiumos/docs/+/master/constants/syscalls.md
[bits 32]
global _start
section .data
msg: db "Socket Failed To Create!",0xa,0
len: equ $-msg
msg1: db "Socket Created",0xa,0
len1: equ $-msg1
msg2: db "Recv Or Send Failed",0xa,0
len2: equ $-msg2
msg3: db "Shutdown Socket Failed",0xa,0
len3: equ $-msg3
DATASIZE: equ 5
SOCK_STREAM: equ 1
AF_INET: equ 2
AF_INET: equ 2
INADDR_ANY: equ 0
MSG_WAITALL: equ 0x100
MSG_DONTWAIT: equ 0x40
SHUT_RDWR: equ 2
SYS_SOCKET: equ 1 ; sys_socket(2)
SYS_BIND: equ 2 ; sys_bind(2)
SYS_CONNECT: equ 3 ; sys_connect(2)
SYS_LISTEN: equ 4 ; sys_listen(2)
SYS_ACCEPT: equ 5 ; sys_accept(2)
SYS_GETSOCKNAME:equ 6 ; sys_getsockname(2)
SYS_GETPEERNAME:equ 7 ; sys_getpeername(2)
SYS_SOCKETPAIR: equ 8 ; sys_socketpair(2)
SYS_SEND: equ 9 ; sys_send(2)
SYS_RECV: equ 10 ; sys_recv(2)
SYS_SENDTO: equ 11 ; sys_sendto(2)
SYS_RECVFROM: equ 12 ; sys_recvfrom(2)
SYS_SHUTDOWN: equ 13 ; sys_shutdown(2)
SYS_SETSOCKOPT: equ 14 ; sys_setsockopt(2)
SYS_GETSOCKOPT: equ 15 ; sys_getsockopt(2)
SYS_SENDMSG: equ 16 ; sys_sendmsg(2)
SYS_RECVMSG: equ 17 ; sys_recvmsg(2)
SYS_ACCEPT4: equ 18 ; sys_accept4(2)
SYS_RECVMMSG: equ 19 ; sys_recvmmsg(2)
SYS_SENDMMSG: equ 20 ; sys_sendmmsg(2)
struc sockaddr_in, -0x30
.sin_family: resb 2 ;2bytes
.sin_port: resb 2 ;2bytes
.sin_addr: resb 4 ;4bytes
.sin_zero: resb 8 ;8bytes
endstruc
struc socket, -0x40
.socketfd resb 4
.connectionfd resb 4
.count resb 4
.data resb DATASIZE
endstruc
section .text
_start:
push ebp
mov ebp, esp
sub esp, 0x400 ;1024byte
xor edx, edx ;or use cdq
;
; int socket(int domain, int type, int protocol);
; domain: The domain argument specifies a communication domain
;
push edx ; Push protocol
push dword SOCK_STREAM ; Push type
push dword AF_INET ; Push domain
mov ecx, esp ; ECX points to args
mov ebx, SYS_SOCKET ;
mov eax, 0x66 ; socketcall()
int 0x80
cmp eax, 0
jl .socket_failed
mov [ebp + socket.socketfd], eax
;
; fill struct sockaddr_in serv_addr;
;
mov word [ebp + sockaddr_in.sin_family], AF_INET
mov word [ebp + sockaddr_in.sin_port], 0x3905
mov dword [ebp + sockaddr_in.sin_addr], INADDR_ANY
push dword [ebp + sockaddr_in.sin_addr]
push word [ebp + sockaddr_in.sin_port]
push word [ebp + sockaddr_in.sin_family]
mov ecx, esp
;
; int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
;
push byte 0x10 ; sizeof(struct sockaddr)
push ecx ; pointer struct sockaddr
push dword [ebp + socket.socketfd]
mov ecx, esp ; ECX points to args
mov ebx, SYS_BIND ;
mov eax, 0x66
int 0x80
cmp eax, 0
jne .socket_failed
;
; int listen(int sockfd, int backlog);
;
push dword 0x10
push dword [ebp + socket.socketfd]
mov ecx, esp
mov ebx, SYS_LISTEN
mov eax, 0x66
int 0x80
cmp eax, 0
jne .socket_failed
;
; int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
;
xor ebx, ebx
push ebx
push ebx
push dword [ebp + socket.socketfd]
mov ecx, esp
mov ebx, SYS_ACCEPT
mov eax, 0x66
int 0x80
cmp eax, -1
je .socket_failed
mov [ebp + socket.connectionfd], eax
mov dword [ebp + socket.count], 0
.again:
lea edi, [ebp + socket.data]
mov ecx, DATASIZE
mov eax, 0
rep stosd
lea eax, [ebp + socket.data]
;
; ssize_t recv(int sockfd, const void *buf, size_t len, int flags);
;
push dword MSG_WAITALL
push dword DATASIZE
push eax
push dword [ebp + socket.connectionfd]
mov ecx, esp
mov ebx, SYS_RECV
mov eax, 0x66
int 0x80
cmp eax, 0
jle .recv_or_send_failed
mov edx, eax
lea ecx, [ebp + socket.data]
call printk
inc dword [ebp + socket.count]
cmp dword [ebp + socket.count], 5
jle .again
.break:
;
; int shutdown(int sockfd, int how);
;
push dword SHUT_RDWR
push dword [ebp + socket.socketfd]
mov ecx, esp
mov ebx, SYS_SHUTDOWN
mov eax, 0x66
int 0x80
cmp eax, 0
jne .shutdown_failed
;
; int close(int fd)
;
mov ebx, [ebp + socket.connectionfd]
mov eax, 0x06
int 0x80
cmp eax, 0
jne .shutdown_failed
jmp .success
.shutdown_failed:
mov edx, len3
mov ecx, msg3
call printk
jmp .end
.recv_or_send_failed:
mov edx, len2
mov ecx, msg2
call printk
jmp .end
.socket_failed:
mov edx, len
mov ecx, msg
call printk
jmp .end
.success:
mov edx, len1
mov ecx, msg1
call printk
jmp .end
.end:
leave
mov ebx,0 ;first syscall argument: exit code
mov eax,1 ;system call number (sys_exit)
int 0x80 ;call kernel
ret
; EDX: message length
; ECX: pointer to message to write
printk:
pusha
mov ebx,1 ;first argument: file handle (stdout)
mov eax,4 ;system call number (sys_write)
int 0x80 ;call kernel
popa
ret

Create/Write to file x86 Linux

I have been trying to teach myself some 32-bit x86 (NASM). I am trying to have a user input a file name, open/create the file, then take a user's message and write that message to the file. I have gone through it in GDB and all syscalls returned correctly. After the program runs the file appears to be created improperly and nothing is written to it. I ahev seen some of the other question that are similar but my code seems to be nearly the same as their's so I can't seem to figure out what the heck is going on.
Here is my noob code:
global _start
section .data
fmsg: db "Enter Filename: ", 0
.len: equ $ - fmsg
umsg: db "Enter message: ", 0
.len: equ $ - umsg
buff: times 50 db 0 ;array for user string
.blen: equ $ - buff
fname: times 50 db 0 ;array for filename
.flen: equ $ - fname
;modes
O_RDONLY: db 0 ;read-only
O_WRONLY: db 1 ;wirte-only
O_RDWR: db 2 ;read and write
;flags
O_CREAT: dw 100o ;create file if file doesnt exists
O_TRUNC: dw 1000o ;truncate file
O_APPEND: dw 2000o ;append to file
section .bss
fd: resd 1 ;file descriptor
bret: resd 1 ;read buffer return value
fret: resd 1 ;read filename return value
tmp: resd 1 ;temp 4 byte variable
section .text
_start:
fprompt: ;Print prompt
mov eax, 0x4 ;syscall 4 - write()
mov ebx, 0x1 ;file desc 1 - stdout
mov ecx, fmsg ;print message
mov edx, fmsg.len ;length of message
int 80h ;syscall interupt
filein:
mov eax, 0x3 ;syscall 3 - read()
mov ebx, 0x0 ;file desc 0 - stdin
mov ecx, fname ;dst buffer
mov edx, fname.flen ;length of buffer
int 80h ;syscall interupt
mov [fret], eax ;save return value to file return variable
cmp eax, edx ;read max bytes or more?
jb fileopen ;jmp is bytes read < max
mov bl, [ecx+eax-1] ;grab last byte # last index before '\0'
cmp bl, 10 ;does it = '\n' ?
je clean1
inc DWORD [fret] ;len++
clean1: ;loop to clear excess input, if any
mov eax, 0x3 ;syscall 3 - read()
mov ebx, 0x0 ;file desc 0 - stdin
mov ecx, tmp ;temp buffer
mov edx, 0x1 ;read only 1 byte
int 80h ;;syscall interupt
test eax, eax ;EOF?
jz fileopen ;Yes, jump to pback
mov al, [tmp] ;put character into lower 8 bits of EAX
cmp al, 10 ;is it = to lf ?
jne clean1 ;no, jump to begining of loop
fileopen:
mov eax, 0x05
mov ebx, fname ;filename
or ecx, O_CREAT ;if it doesn't exist create the file
or ecx, O_TRUNC ;truncate
mov edx, O_WRONLY ;write only
int 80h ;syscall interupt
mov [fd], eax ;save file descripor
prompt2:
mov eax, 0x4 ;syscall 4 - write()
mov ebx, 0x1 ;file desc 1 - stdout
mov ecx, umsg ;print message
mov edx, umsg.len ;length of message
int 80h
userin:
mov eax, 0x3 ;syscall 3 - read()
mov ebx, 0x0 ;file desc 0 - stdin
mov ecx, buff ;dst buffer
mov edx, buff.blen ;length of buffer
int 80h ;syscall interupt
mov [bret], eax ;save return value to buff return variable
cmp eax, edx ;read max bytes or more?
jb writetofile ;jmp is bytes read < max
mov bl, [ecx+eax-1] ;grab last byte # last index before '\0'
cmp bl, 10 ;does it = '\n' ?
je clean2
inc DWORD [bret] ;len++
clean2: ;loop to clear excess input, if any
mov eax, 0x3 ;syscall 3 - read()
mov ebx, 0x0 ;file desc 0 - stdin
mov ecx, tmp ;temp buffer
mov edx, 0x1 ;read only 1 byte
int 80h ;syscall
test eax, eax ;EOF?
jz writetofile ;Yes, jump to pback
mov al, [tmp] ;put character into lower 8 bits of EAX
cmp al, 10 ;is it = to lf ?
jne clean2 ;no, jump to begining of loop
writetofile:
mov eax, 0x4 ;syscall 4 - write()
mov ebx, [fd] ;file desc 1 - stdout
mov ecx, buff ;print message
mov edx, [bret] ;length of message
int 80h ;syscall interupt
closefile:
mov eax, 0x6 ;syscall 6 - close()
mov ebx, [fd] ;file desc
int 80h ;syscall interupt
exit: ;return 0
mov eax, 1 ;syscall 1 - exit()
mov ebx, 0 ;return val
int 80h ;syscall interupt
Here is an example of what I get after running it:
The file "test.txt?" shows up and also shows up as an executable even though I set only read/write for the file. Even when I try to open it there is nothing there. Any thoughts? Also As I mentioned, i am new and teaching myself so if you have any good tips on improvement for other areas of the program please let me know! :)
We have multiple errors (or one big one) in the following three lines of code:
or ecx, O_CREAT ;if it doesn't exist create the file
or ecx, O_TRUNC ;truncate
mov edx, O_WRONLY ;write only
The problem:
What values do the registers ecx and edx have after these lines?
You perform two or operations with the ecx register but obviously it is not initialized at that moment!
This means that you can be sure that the bits representing O_CREAT and O_TRUNC (whatever these values mean - see below) are set but you don't know which values the other bits have.
The O_WRONLY bit should be set in ecx, not in edx. edx should contain the desired file mode instead.
Unfortunately there are two different types of assembler - I don't know which type NASM is of:
One type of assembler would interpret the first instruction as: or ecx, [O_CREAT]
The other type would interpret it as: or ecx, address_of(O_CREAT)
In the first case the instruction mov edx, O_WRONLY will read four bytes starting with the O_WRONLY byte into the edx register so edx will have the value 0x400201 (O_CREAT*0x10000+O_RDWR*0x100+O_WRONLY).
In the second case edx will contain the address of O_WRONLY rather than the value.
The value will be wrong in any case.
Storing your mode constants in memory is really inefficient, even if you were doing it right (which you aren't).
You could trace what system calls you're actually making by running strace ./writefile.
You use O_RDONLY: db 0 to store a byte of static data (in the read/write section instead of .rodata for some reason). Instead, you should define assembler constants with equ:
O_RDONLY equ 0
O_WRONLY equ 1
O_RDWR equ 2
O_CREAT equ 100o ;create file if file doesnt exists
O_TRUNC equ 1000o ;truncate file
O_APPEND equ 2000o ;append to file
Then you can write
mov ecx, O_CREAT | O_TRUNC | O_WRONLY
mov edx, 0777o ; mode is the permission bits if open() creates the file
See the open(2) man page to learn how its args work.
The assembler will do the OR for you at assemble time, instead of having the CPU do 2 loads from memory.
What you actually wrote:
or ecx, O_CREAT ;if it doesn't exist create the file
or ecx, O_TRUNC ;truncate
assembles to two or ecx, imm32 instructions, which OR the addresses of the two dw locations into the original value of ecx. If you had written
movzx ecx, word [O_CREAT]
or cx, [O_TRUNC]
or cx, [O_WRONLY]
your code would have worked, but that would be really silly. (And on some CPUs cause a partial-register stall when something reads the full ecx after you wrote only the low 16 bits.)
If you'd written or ecx, [O_TRUNC], it would do a 32-bit load, so you'd effectively be doing ecx |= (2000o << 16) | 1000o. i.e. the word stored at O_APPEND would be ORed into the high 16 bits of ECX, where it may have a different meaning.
Similarly, mov edx, O_WRONLY assembles to mov edx, imm32, where the address is the immediate. This is why you ended up with weird garbage for the file mode (including the sticky bit set).
Use a debugger.

Register and variables not saving state after jump

I'm trying to learn NASM. I want to write a procedure to get one character at a time from the console until a newline (0xA) is encountered using only kernel calls. So far I have
global _start
section .data
sys_read equ 3
sys_write equ 4
stdin equ 0
stdout equ 1
section .bss
line resb 11
index resb 4
section .text
_start:
push ebp
mov ebp, esp
call _readLine
afterReadLine:
call _printLine
mov esp, ebp
pop ebp
jmp exit
_readLine:
; Reads into line until new line (0xA)
; Number of bytes read will be stored in index when _readLine returns
mov eax, sys_read ; syscall to read
mov ebx, stdin ; stdin
mov edx, [index] ; put index into edx
mov ecx, dword line ; put line addr in ecx
add ecx, edx ; add index to addr in ecx
mov edx, 1 ; read one char
int 0x80 ; call kernel to read char
mov ecx, [index] ; put index into ecx
cmp dword [line + ecx], 0xA ; compare value at line + ecx to new line char
inc byte [index] ; increment index
je afterReadLine ; if last char is newline return
jne _readLine ; if last char is not new line, loop
_printLine:
mov eax, sys_write
mov ebx, stdout
mov ecx, line
mov edx, [index]
int 0x80
ret
exit:
mov eax, 01h ; exit()
xor ebx, ebx ; errno
int 80h
When I test against the value stored in the index variable at the end, it always equals 0. I tried moving the value of index into eax, but it's also zero after the jump. I tried using the ret keyword, but that also seemed to overwrite my values. What is the best practice way to return the value of characters read from this procedure?
EDIT:
I tried the following and I'm still not getting any output. With input "abcd[newline]" the program outputs "abcd" if I hardcode the value of 4 in edx in the _printLine procedure, but as written won't output anything.
global _start
section .data
sys_read equ 3
sys_write equ 4
stdin equ 0
stdout equ 1
bytesRead dd 0
termios: times 36 db 0
ICANON: equ 1<<1
ECHO: equ 1<<3
section .bss
line resb 11
index resb 4
section .text
_start:
push ebp
mov ebp, esp
call canonical_off
call echo_off
call _readLine
call _printLine
call canonical_on
call echo_on
mov esp, ebp
pop ebp
jmp exit
_readLine:
; Reads into line until new line (0xA)
; Number of bytes read will be stored in bytesRead when _readLine returns
mov eax, sys_read ; syscall to read
mov ebx, stdin ; stdin
mov edx, [index] ; put index into edx
mov ecx, dword line ; put line addr in ecx
add ecx, edx ; add index to addr in ecx
mov edx, 1 ; read one char
int 0x80 ; call kernel to read char
mov ecx, [index] ; put index into ecx
cmp dword [line + ecx], 0xA ; compare value at line + ecx to new line char
inc byte [index] ; increment index
jne _readLine ; if last char is not new line, loop
ret
_printLine:
mov eax, sys_write
mov ebx, stdout
mov ecx, line
mov edx, [index] ; Works if hardcoded 4 here!
int 0x80
ret
canonical_off:
call read_stdin_termios
; clear canonical bit in local mode flags
;push rax
mov eax, ICANON
not eax
and [termios+12], eax
;pop rax
call write_stdin_termios
ret
echo_off:
call read_stdin_termios
; clear echo bit in local mode flags
;push rax
mov eax, ECHO
not eax
and [termios+12], eax
;pop rax
call write_stdin_termios
ret
canonical_on:
call read_stdin_termios
; set canonical bit in local mode flags
or dword [termios+12], ICANON
call write_stdin_termios
ret
echo_on:
call read_stdin_termios
; set echo bit in local mode flags
or dword [termios+12], ECHO
call write_stdin_termios
ret
read_stdin_termios:
; push rax
; push rbx
; push rcx
;push rdx
mov eax, 36h
mov ebx, stdin
mov ecx, 5401h
mov edx, termios
int 80h
;pop rdx
; pop rcx
;pop rbx
;pop rax
ret
write_stdin_termios:
; push rax
;push rbx
;push rcx
; push rdx
mov eax, 36h
mov ebx, stdin
mov ecx, 5402h
mov edx, termios
int 80h
;pop rdx
;pop rcx
; pop rbx
;pop rax
ret
exit:
mov eax, 01h ; exit()
xor ebx, ebx ; errno
int 80h
link here http://ideone.com/Lw3fyV
First off, you are calling _readLine but not returning from it, instead you are jumping to the label after call _readLine conveniently called afterReadLine. You should use ret to exit from your function.
Now to your question. By default, the terminal is in canonical mode (Cooked mode), meaning all input is buffered. The system will fill your buffer until the return key is pressed and adds 0xA to the end of the buffer.
What you want is non-canonical mode (raw mode). This means the system does not process the key presses but passes it on to you.
How do i read single character input from keyboard using nasm (assembly) under ubuntu?
By switching to raw mode, you can get each character pressed until the return key and do what you will with it.

NASM Linux Assembly Printing Integers

I am trying to print a single digit integer in nasm assembly on linux. What I currently have compiles fine, but nothing is being written to the screen. Can anyone explain to me what I am doing wrong here?
section .text
global _start
_start:
mov ecx, 1 ; stores 1 in rcx
add edx, ecx ; stores ecx in edx
add edx, 30h ; gets the ascii value in edx
mov ecx, edx ; ascii value is now in ecx
jmp write ; jumps to write
write:
mov eax, ecx ; moves ecx to eax for writing
mov eax, 4 ; sys call for write
mov ebx, 1 ; stdout
int 80h ; call kernel
mov eax,1 ; system exit
mov ebx,0 ; exit 0
int 80h ; call the kernel again
This is adding, not storing:
add edx, ecx ; stores ecx in edx
This copies ecx to eax and then overwrites it with 4:
mov eax, ecx ; moves ecx to eax for writing
mov eax, 4 ; sys call for write
EDIT:
For a 'write' system call:
eax = 4
ebx = file descriptor (1 = screen)
ecx = address of string
edx = length of string
After reviewing the other two answers this is what I finally came up with.
sys_exit equ 1
sys_write equ 4
stdout equ 1
section .bss
outputBuffer resb 4
section .text
global _start
_start:
mov ecx, 1 ; Number 1
add ecx, 0x30 ; Add 30 hex for ascii
mov [outputBuffer], ecx ; Save number in buffer
mov ecx, outputBuffer ; Store address of outputBuffer in ecx
mov eax, sys_write ; sys_write
mov ebx, stdout ; to STDOUT
mov edx, 1 ; length = one byte
int 0x80 ; Call the kernel
mov eax, sys_exit ; system exit
mov ebx, 0 ; exit 0
int 0x80 ; call the kernel again
From man 2 write
ssize_t write(int fd, const void *buf, size_t count);
In addition to the other errors that have been pointed out, write() takes a pointer to the data and a length, not an actual byte itself in a register as you are trying to provide.
So you will have to store your data from a register to memory and use that address (or if it's constant as it currently is, don't load the data into a register but load its address instead).

Resources