How to read and display a value in Linux assembly? - linux

I'm a beginner in Linux assembler and I have some questions. I'd like to read some characters from keyboard, convert it to value (I understand that this convertion should be from ASCII to decimal, right?), do some math (add, sub, multiply, whatever) and display the result in Terminal. How should I do that? I wrote some code but it probably doesn't make sense:
SYSEXIT = 1
EXIT_SUCC = 0
SYSWRITE = 4
SYSCALL = 0x80
SYSREAD = 3
.data
value: .space 5, 0
value_len: .long .-value
result: .long
result_len: .long .-result
.text
.global _start
_start:
movl $SYSREAD, %eax
movl $EXIT_SUCC, %ebx
movl $value, %ecx
movl value_len, %edx
int $SYSCALL
movl $0, %edx
movl value_len, %ecx
for:
movb value(, %edx, 1), %al
subb $48, %al
movb %al, result(, %edx, 1)
inc %edx
loop for
add $10, result
movl $0, %edx
movl result_len, %ecx
for1:
movb result(, %edx, 1), %al
add $48, %al
movb %al, result(, %edx, 1)
inc %edx
loop for1
movl $SYSWRITE, %eax
movl $SYSEXIT, %ebx
movl $result, %ecx
movl result_len, %edx
int $SYSCALL
movl $SYSEXIT, %eax
movl $EXIT_SUCC, %ebx
int $SYSCALL
I don't know if I should reserve memory by spaces? Or reading characters in loop?
How to convert it, to be able to make some math operation and then convert it to be able to display it?
I know that to get the value of ASCII char I should subtract 48, but what next?
I had an idea to multiply each bits by 2^k where k is 0,1,2...n it's good idea? If so, how to implement something like this?
As you can see I had a lot of questions, but I only need to someone show me how to do, what I am asking about. I saw some similar problems, but nothing like this in Linux.
Thank you in advance for the all information.
All the best.

At first reading and writing the console in Linux is by using the file functions with special console handles, that have always the same values: STDIN=0, STDOUT=1 and STDERR=2.
At second you will need some decent documentation about Linux system calls. Notice that the C-centric one (like "man") are not suitable, because C language does not use the system calls directly, but has wrappers that often change the arguments and the result values.
On the following site you can download an assembly-centric SDK for Linux, that contains the needed documentation and many examples and include files.
If you only need the help files, you can browse them online: Here
If the problem is the conversion from ASCII string to number and then back to string, here are two simple procedures that can do the job, if the requirements are not so big. The StrToNum is not so advanced, it simply convert decimal unsigned number:
; Arguments:
; esi - pointer to the string
; Return:
; CF=0
; eax - converted number
; edx - offset to the byte where convertion ended.
;
; CF=1 - the string contains invalid number.
;
StrToNum:
push ebx esi edi
xor ebx,ebx ; ebx will store our number
xor eax,eax
mov al,[esi]
cmp al,'0'
jb .error
cmp al,'9'
jbe .digit
jmp .error
.digit:
sub al,'0'
add ebx,eax
inc esi
mov al,[esi]
cmp al,'0'
jb .finish
cmp al,'9'
ja .finish
mov edx,ebx ; multiply ebx by 10
shl ebx,3
add ebx,edx
add ebx,edx
jmp .digit
.finish:
mov eax, ebx
mov edx, esi
clc
pop edi esi ebx
ret
.error:
stc
pop edi esi ebx
ret
NumToStr is pretty flexible. It converts number to a string in any radix and with sign:
;**********************************************************************************
; NumToStr converts the number in eax to the string in any radix approx. [2..26]
; Arguments:
; edi - pointer to the string buffer
; ecx - radix
; eax - number to convert.
; There is no parameter check, so be careful.
; returns: edi points to the end of a converted number
;**********************************************************************************
NumToStr:
test eax,eax
jns NumToStrU
neg eax
mov byte [edi],"-"
inc edi
NumToStrU:
cmp eax,ecx
jb .lessA
xor edx,edx
div ecx
push edx
call NumToStrU
pop eax
.lessA:
cmp al, 10
sbb al, 69h
das
stosb
ret

Related

Loading value at address into register

As a learning exercise, I've been handwriting assembly. I can't seem to figure out how to load the value of an address into a register.
Semantically, I want to do the following:
_start:
# read(0, buffer, 1)
mov $3, %eax # System call 3 is read
mov $0, %ebx # File handle 0 is stdin
mov $buffer, %ecx # Buffer to write to
mov $1, %edx # Length of buffer
int $0x80 # Invoke system call
lea (%ecx, %ecx), %edi # Pull the value at address into %edi
cmp $97, %edi # Compare to 'a'
je done
I've written a higher-level implementation in C:
char buffer[1];
int main()
{
read(0, buffer, 1);
char a = buffer[0];
return (a == 'a') ? 1 : 0;
}
But compiling with gcc -S produces assembly that doesn't port well into my implementation above.
I think lea is the right instruction I should be using to load the value at the given address stored in %ecx into %edi, but upon inspection in gdb, %edi contains a garbage value after this instruction is executed. Is this approach correct?
Instead of the lea instruction, what you need is:
movzbl (%ecx), %edi
That is, zero extending into the edi register the byte at the memory address contained in ecx.
_start:
# read(0, buffer, 1)
mov $3, %eax # System call 3 is read
mov $0, %ebx # File handle 0 is stdin
mov $buffer, %ecx # Buffer to write to
mov $1, %edx # Length of buffer
int $0x80 # Invoke system call
movzbl (%ecx), %edi # Pull the value at address ecx into edi
cmp $97, %edi # Compare to 'a'
je done
Some advice
You don't really need the movz instruction: you don't need a separate load operation, since you can compare the byte in memory pointed by ecx directly with cmp:
cmpb $97, (%ecx)
You may want to specify the character to be compared against (i.e., 'a') as $'a' instead of $97 in order to improve readability:
cmpb $'a', (%ecx)
Avoiding conditional branches is usually a good idea. Immediately after performing the system call, you could use the following code that uses cmov for determining the return value, which is stored in eax, instead of performing a conditional jump (i.e., the je instruction):
xor %eax, %eax # set eax to zero
cmpb $'a', (%ecx) # compare to 'a'
cmovz %edx, %eax # conditionally move edx(=1) into eax
ret # eax is either 0 or 1 at this point
edx was set to 1 prior to the system call. Therefore, this approach above relies on the fact that edx is preserved across the system call (i.e., the int 0x80 instruction).
Even better, you could use sete on al after the comparison instead of the cmov:
xor %eax, %eax # set eax to zero
cmpb $'a', (%ecx) # compare to 'a'
sete %al # conditionally set al
ret # eax is either 0 or 1 at this point
The register al, which was set to zero by means of xor %eax, %eax, will be set to 1 if the ZF flag was set by the cmp (i.e., if the byte pointed by ecx is 'a'). With this approach you don't need to care about thinking whether the syscall preserves edx or not, since the outcome doesn't depend on edx.

Assembler - adding big (128b) numbers (AT&T assembly syntax) - where to store results?

I am trying to add two 128 bits numbers using ATT assembly syntax in linux ubuntu 64b and I am debugging it in gdb so I know that after every loop the result of adding two parts is correct but how to store all 4 results together?? I was considering adding every result to the stack but I can't add the register content to the stack, right? Am I even doing it correctly? I am a real beginner in assembler but I need it for uni :/
EXIT_SUCCESS = 0
SYSEXIT = 1
number1:
.long 0x10304008, 0x701100FF, 0x45100020, 0x08570030
number2:
.long 0xF040500C, 0x00220026, 0x321000CB, 0x04520031
.global _start
_start:
movl $4, %edx
clc
pushf
_loop:
dec %edx
movl number1(,%edx,4), %eax
movl number2(,%edx,4), %ebx
popf
adcl %ebx, %eax
cmp $0, %edx
jne _loop
popf
jnc _end
push $1
_end:
mov $SYSEXIT, %eax
mov $EXIT_SUCCESS, %ebx
int $0x80

Printing floating point numbers in assembler

I'm trying to print a floating-point value from assemler calling a printf function. It works fine with strings and integer values but fails printing floats. Here is an example of working code:
global main
extern printf
section .data
message: db "String is: %d %x %s", 10, 0
end_message: db ".. end of string", 0
section .text
main:
mov eax, 0xff
mov edi, message
movsxd rsi, eax
mov rdx, 0xff
mov rcx, end_message
xor rax, rax
call printf
ret
String is: 255 ff .. end of string
So, the parameters are passed through registers: edi contains address of a formatting string, rsi and rdx contain the same number to print in decimal and hex styles, rcx contains end of a string, rax contains 0 as we do not have a float to print.
This code works fine but something changes while trying to print float:
global main
extern printf
section .data
val: dq 123.456
msg: db "Result is: %fl",10, 0
section .text
main:
mov rdi,msg
movsd xmm0,[val]
mov eax,1
call printf
mov rax, 0
ret
This code snipped can be compiled but returns segmentation fault being executed. It seems that the problem is in wrong value of xmm0 but trying to change movsd xmm0,[val] to movsd xmm0,val gives an
error: invalid combination of opcode and operands
message.
The compiler is NASM running on openSuSe 12.3
Update. I tried to make a c program and produce a .S assembly. It gives a very weird solution:
main:
.LFB2:
.cfi_startproc
pushq %rbp
.cfi_def_cfa_offset 16
.cfi_offset 6, -16
movq %rsp, %rbp
.cfi_def_cfa_register 6
subq $32, %rsp
movl %edi, -4(%rbp)
movq %rsi, -16(%rbp)
movq val(%rip), %rax
movq %rax, -24(%rbp)
movsd -24(%rbp), %xmm0
movl $.LC0, %edi
movl $1, %eax
call printf
movl $0, %eax
leave
.cfi_def_cfa 7, 8
ret
Is it possible to write a simple printf example?
for your assembler problem:
you need to align the stack before your main program starts.
insert
sub rsp, 8
right after main:
then add it again before ret:
add rsp, 8

How to read input string from keyboard using assembly language

I'm trying to write a simple program that takes string from the keyboard, then prints it to the screen. So far I couldn't make it work.
Here's the code:
.section .rodata
output: .string "you entered %s\n"
input: .string "%s"
.text
.globl main
.type main, #function
main:
pushl %ebp
movl %esp, %ebp
subl $100, %esp
pushl $input
call scanf
movl %ebp, %esp
subl $100, %esp
pushl $output
call printf
xorl %eax, %eax
movl %ebp, %esp
popl %ebp
ret
When I execute it, the output is you entered (null) for any given input.
when I set the offset of subl $100, %esp command (the one before call print) to subl $104, %esp I get you entered %s, and when the offset is set to 108 I get you entered *gibberish*.
I feel like it's a game where I need to guess where scanf saved the string on the stack (why isn't it where it should be?).
I am using IA32 instruction set.
Any help would be greatly appreciated.
Basically there are 3 problems in your program:
subl $100, %esp
pushl $input
# Error 1:
# As Frank Kotler already wrote at this point
# only $input is stored on the stack; however
# the address of the buffer must also be on
# the stack (see Frank Kotler's comment)
call scanf
movl %ebp, %esp
# Error 2:
# Now the buffer is below ESP.
# Because interrupts use the kernel stack they
# will not overwrite the memory below ESP.
# However signals will destroy the memory below
# ESP!!
#
# Instead of the lines:
# movl %ebp, %esp
# subl $100, %esp
#
# You should use something like this:
# add $8, %esp
# or:
# lea -100(%ebp), %esp
#
subl $100, %esp
# Error 3:
# As for "scanf" the second argument
# is missing on the stack
pushl $output
call printf
[org 0x0100]
jmp start
;=======Data===================================
s1: db 'Enter String: $'
s2: db 'Reversed String:$'
linefeed: db 10, '$'
;=======Read string and store into stack=======
start: mov dx, s1
mov ah, 09h
int 21h
mov dx, linefeed
mov ah, 09h
int 21h
mov si,0
mov bx,0
again: mov ah, 01h
int 21h
cmp al,byte 0dh
je endread
Page 8 of 10
mov bl,al
push bx
inc si
cmp si,15
je endread
jmp again
endread: Push si
;=======Read from stack and print to screen=====
mov dx, linefeed
mov ah, 09h
int 21h
mov dx, s2
mov ah, 09h
int 21h
mov dx, linefeed
mov ah, 09h
int 21h
pop si
r1: pop bx
mov dl,bl
mov ah, 02h
int 21h
dec si
cmp si,0
jne r1
mov dx, linefeed
mov ah, 09h
int 21h
mov ax, 0x4c00 ;terminate program
int 21h

x86 Assembly 2-Digit Keyboard Input

It needs to allow a two-digit number for input that will be used to indicate how many times the name is printed. I can't figure out how to separate the second digit though and have it checked to make sure it is between 0x30 and 0x39. I also keep getting this weird box after the name that has 0017 inside it.
.data
input_msg_len: .long 26
input_msg: .ascii "Enter a two-digit number: "
name: .ascii "Michael Chabon\n"
name_len: .long 16
max: .long 0
count: .long 0
tmp: .long 0
input_str: .ascii "??"
.text
.global _start
_start:
mov $4, %eax
mov $1, %ebx
mov $input_msg, %ecx
mov input_msg_len, %edx
int $0x80
mov $3, %eax
mov $0, %ebx
mov $input_str, %ecx
mov $2, %edx
int $0x80
mov $input_str, %eax
add count, %eax
mov $input_str, %eax
mov (%eax), %bl
cmp $0x30, %bl
jl _start
cmp $0x39, %bl
jg _start
mov count, %eax
inc %eax
mov %eax, count
sub $0x30, %bl
mov %bl, max
mov $10, %bl
imul %bl
mov %bl, max
#Not sure how to check second char in input_str.
#Want to check it then subtract $0x30 and move to tmp before adding tmp to max.
mov $0, %edi
again:
cmp max, %edi
je end
mov $4, %eax
mov $1, %ebx
mov $name, %ecx
mov name_len, %edx
int $0x80
inc %edi
jmp again
end:
mov $1, %eax
int $0x80
Thanks in advance!
There are some bugs in your code.
Below, 2 first lines of that block are redundant, as mov $input_str, %eax overwrites eax anyway.
mov $input_str, %eax
add count, %eax
mov $input_str, %eax
Then here, it makes no sense to load count into eax here:
mov count, %eax
inc %eax
mov %eax, count
You can do this in a lot shorter and clearer way with:
incl count
Then, next bug is that you recently loaded count into eax, and then multiply the lowest 8 bits of count loaded into al with 10, in this piece of code:
mov (%eax), %bl // bl = first character
cmp $0x30, %bl
jl _start
cmp $0x39, %bl
jg _start
mov count, %eax // eax = count
inc %eax // eax++
mov %eax, count // count = eax
sub $0x30, %bl // 0 <= bl <= 9
mov %bl, max // max = bl <- you lose this value in the next mov %bl, max
mov $10, %bl // bl = 10
imul %bl // ax = 10 * and(count, 0xff) // ax = al*bl (signed multiply)
mov %bl, max // max = 10 <- here you overwrite the value of max with 10
So, according to my intuition you don't want to do ax = 10 * and(count, 0xff), but 10 * (first number). imul %bl does o signed multiply between al and bl, and stores the result in ax. So the code above could be changed to something like this:
mov (%eax), %bl // bl = first character
cmp $0x30, %bl
jl _start
cmp $0x39, %bl
jg _start
incl count
pushl %eax // push eax to stack
sub $0x30, %bl // 0 <= bl <= 9
mov $10, %al // al = 10
imul %bl // ax = 10 * bl (signed multiply)
mov %al, max // 0 <= max <= 90
Then, you can check the second character similarly to the first character:
pop %eax // pop eax from stack
incl %eax
mov (%eax), %bl // bl = second character
cmp $0x30, %bl
jl _start
cmp $0x39, %bl
jg _start
sub $0x30, %bl // 0 <= bl <= 9
add %bl, max // 0 <= max <= 99
I strongly recommend you to learn to use some debugger. gdb has several frontends, of which I think ddd works best according to my experience. gdbtui is also convenient.

Resources