How to correctly replace printf with sys_write? - linux

I'm making new post about the same program - I'm sorry but I think that my question is much different than the previous one. My program gets 2 parameters at the start - number of repeats and a string. Number of repeats determines how many times should the last word from string be printed. For example:
./a.out 3 "ab cd"
shows in output
cdcdcd
I already made ( with Stack users help :-) ) a working program using call printf. It works for 0-9 number of repeats only but it's not as imporant as the main thing - my question is how to replace this "call printf" with sys_write calling.
I got information that I have to compile this using
-nostdlib
option but it doesn't matter if my code isn't correct. I tried my best and I also found some information about possible methods here but I can't make it work properly.
Printing new line works good but I have no idea how to deal with string from parameter #2 connected with sys_write. It would be great if someone more experienced find some time and point out what I need to change in the code. It took some time to get through the "call printf" version but then I was able to experiment and now I'm totally lost. Here it is:
.intel_syntax noprefix
.globl _start
.text
_start:
push ebp
mov ebp, esp
mov ecx, [ebp + 4] # arg1 int ECX
mov ebx, [ebp + 8] # arg2 string EBX
xor eax, eax
# ARG1 - FROM STRING TO INT
atoi:
movzx edx, byte ptr [ecx]
cmp edx, '0'
jb programend
sub edx, '0'
mov ecx, edx
## =========================== FUNCTION =========================== ##
# SEARCH FOR END OF STRING
findend:
mov dl, byte ptr [ebx + eax] # move through next letters
cmp dl, 0
jz findword
inc eax
jmp findend
# SEARCH FOR LAST SPACE
findword:
dec eax
mov dl, byte ptr [ebx + eax]
cmp dl, ' '
jz foundwordstart
jmp findword
# REMEMBER SPACE POSITION, CHECK COUNTER >0
foundwordstart:
push eax # remember space position
cmp ecx, 0 # check if counter > 0
jz theend
jmp foundword
# PRINT LAST WORD
foundword:
inc eax
mov dl, byte ptr [ebx + eax]
cmp dl, 0
jz checkcount
push ecx
push eax # save current position in word
push edx
push ebx
lea ecx, [ebx+eax] # char * to string
mov eax, 4 # sys_write
mov edx, 1; # how many chars will be printed
mov ebx, 1 # stdout
int 0x80
pop ebx
pop edx
pop eax
pop ecx
jmp foundword
# decrease counter and restore beginning of last word
checkcount:
dec ecx # count = count-1
pop eax # restore beginning of last word
jmp foundwordstart
theend:
pop eax # pop the space position from stack
jmp programend
# END OF PROGRAM
programend:
pop ebp
# new line
mov eax,4
mov ebx,1
mov ecx,offset msgn
mov edx,1
int 0x80
# return 0
mov eax, 1
mov ebx, 0
int 0x80
.data
msgn: .ascii "\n"
It's also really strange for me that I can run it with:
mov ecx, [ebp + 12]
mov ecx, [ecx + 8]
add ecx, eax # char * to string
mov eax, 4 # sys_write
mov edx, 1; # how many chars will be printed
mov ebx, 1 # stdout
int 0x80
and it works well - but only if I don't use -nostdlib (and of course I have to change _start to main)...

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.

Getting digit instead of Ascii in nasm assembly intel x86

I'm trying to learn the basics of assembly but can't get across on how to display results stored in memory.
section .data
num1 db 1,2,3,4,5
num2 db 1,2,3,4,5
output: db 'The dot product is "'
outputLen1 : equ $-output
output2: db '" in Hex!', 10
output2Len : equ $-output2
section .bss
dotProd resw 1 ; store dot product in here
section .text
global _start
_start:
mov eax, 0
mov ecx, 5
mov edi, 0
mov esi, 0
looper: mov ax, [edi + num1]
mov dx, [esi + num2]
mul dx
add [dotProd], ax
cmp cx, 1
je printOutput
inc edi
inc esi
dec cx
jmp looper ; go back to looper
printOutput:
mov eax,4 ; The system call for write (sys_write)
mov ebx,1 ; File descriptor 1 - standard output
mov ecx, output ;
mov edx, outputLen1 ;
int 80h ; Call the kernel
mov eax, 4
mov ebx, 1
mov ecx, dotProd,
mov edx, 1
int 80h
mov eax, 4
mov ebx, 1
mov ecx, output2,
mov edx, output2Len
int 80h
jmp done
done:
mov eax,1 ; The system call for exit (sys_exit)
mov ebx,0 ; Exit with return code of 0 (no error)
int 80h
What I'm trying to do is get the dot product of the two list of numbers and display it on the screen. However, I keep getting random letters which I believe are hex representations of the real decimal value. How can I convert it to decimal? The current value display is 7, which should is the equivalent ASCII char for 55, which in this case is the dot product of both list of numbers.
esi and edi must be increased such that it points to next element of array.(in this particular example, only one of them is sufficient).
declare mun1 andnum2 as dd, instead of db (see here).
Also, you have to have method for printing number.(see this and this).
Below is a complete code which uses printf.
;file_name:test.asm
;assemble and link with:
;nasm -f elf test.asm && gcc -m32 -o test test.o
extern printf
%macro push_reg 0
push eax
push ebx
push ecx
push edx
%endmacro
%macro pop_reg 0
pop edx
pop ecx
pop ebx
pop eax
%endmacro
section .data
num1: dd 1,2,3,4,5
num2: dd 1,2,3,4,5
msg: db "Dot product is %d",10,0
section .bss
dotProd resd 1 ; store dot product in here
section .text
global main
main:
mov eax, 0
mov ecx, 5
mov edx, 0
mov esi, 0
mov dword[dotProd], 0h
looper: mov eax, dword[esi + num1]
mov edx, dword[esi + num2]
mul edx
add [dotProd], eax
cmp cx, 1
je printOutput
add esi,4
dec cx
jmp looper ; go back to looper
printOutput:
push_reg
push dword[dotProd]
push dword msg
call printf
add esp,8
pop_reg
jmp done
done:
mov eax,1 ; The system call for exit (sys_exit)
mov ebx,0 ; Exit with return code of 0 (no error)
int 80h

write number to file using NASM

How do I write a variable to a file using NASM?
For example, if I execute some mathematical operation - how do I write the result of the operation to write a file?
My file results have remained empty.
My code:
%include "io.inc"
section .bss
result db 2
section .data
filename db "Downloads/output.txt", 0
section .text
global CMAIN
CMAIN:
mov eax,5
add eax,17
mov [result],eax
PRINT_DEC 2,[result]
jmp write
write:
mov EAX, 8
mov EBX, filename
mov ECX, 0700
int 0x80
mov EBX, EAX
mov EAX, 4
mov ECX, [result]
int 0x80
mov EAX, 6
int 0x80
mov eax, 1
int 0x80
jmp exit
exit:
xor eax, eax
ret
You have to implement ito (integer to ascii) subsequently len for this manner. This code tested and works properly in Ubuntu.
section .bss
answer resb 64
section .data
filename db "./output.txt", 0
section .text
global main
main:
mov eax,5
add eax,44412
push eax ; Push the new calculated number onto the stack
call itoa
mov EAX, 8
mov EBX, filename
mov ECX, 0x0700
int 0x80
push answer
call len
mov EBX, EAX
mov EAX, 4
mov ECX, answer
movzx EDX, di ; move with extended zero edi. length of the string
int 0x80
mov EAX, 6
int 0x80
mov eax, 1
int 0x80
jmp exit
exit:
xor eax, eax
ret
itoa:
; Recursive function. This is going to convert the integer to the character.
push ebp ; Setup a new stack frame
mov ebp, esp
push eax ; Save the registers
push ebx
push ecx
push edx
mov eax, [ebp + 8] ; eax is going to contain the integer
mov ebx, dword 10 ; This is our "stop" value as well as our value to divide with
mov ecx, answer ; Put a pointer to answer into ecx
push ebx ; Push ebx on the field for our "stop" value
itoa_loop:
cmp eax, ebx ; Compare eax, and ebx
jl itoa_unroll ; Jump if eax is less than ebx (which is 10)
xor edx, edx ; Clear edx
div ebx ; Divide by ebx (10)
push edx ; Push the remainder onto the stack
jmp itoa_loop ; Jump back to the top of the loop
itoa_unroll:
add al, 0x30 ; Add 0x30 to the bottom part of eax to make it an ASCII char
mov [ecx], byte al ; Move the ASCII char into the memory references by ecx
inc ecx ; Increment ecx
pop eax ; Pop the next variable from the stack
cmp eax, ebx ; Compare if eax is ebx
jne itoa_unroll ; If they are not equal, we jump back to the unroll loop
; else we are done, and we execute the next few commands
mov [ecx], byte 0xa ; Add a newline character to the end of the character array
inc ecx ; Increment ecx
mov [ecx], byte 0 ; Add a null byte to ecx, so that when we pass it to our
; len function it will properly give us a length
pop edx ; Restore registers
pop ecx
pop ebx
pop eax
mov esp, ebp
pop ebp
ret
len:
; Returns the length of a string. The string has to be null terminated. Otherwise this function
; will fail miserably.
; Upon return. edi will contain the length of the string.
push ebp ; Save the previous stack pointer. We restore it on return
mov ebp, esp ; We setup a new stack frame
push eax ; Save registers we are going to use. edi returns the length of the string
push ecx
mov ecx, [ebp + 8] ; Move the pointer to eax; we want an offset of one, to jump over the return address
mov edi, 0 ; Set the counter to 0. We are going to increment this each loop
len_loop: ; Just a quick label to jump to
movzx eax, byte [ecx + edi] ; Move the character to eax.
movsx eax, al ; Move al to eax. al is part of eax.
inc di ; Increase di.
cmp eax, 0 ; Compare eax to 0.
jnz len_loop ; If it is not zero, we jump back to len_loop and repeat.
dec di ; Remove one from the count
pop ecx ; Restore registers
pop eax
mov esp, ebp ; Set esp back to what ebp used to be.
pop ebp ; Restore the stack frame
ret ; Return to caller

Linux nasm assembly append character/s to a string

On NASM in Arch Linux, how can I append the character zero ('0') to a 32 bit variable? My reason for wanting to do this is so that I can output the number 10 by setting a single-digit input to 1 and appending a zero. I need to figure out how to append the zero.
The desirable situation:
Please enter a number: 9
10
Using this method, I want to be able to do this:
Please enter a number: 9999999
10000000
How can I do this?
Thanks in advance,
RileyH
Well, as Bo says... but I was bored. You seem resistant to doing this the easy way (convert your input to a number, add 1, and convert it back to text) so I tried it using characters. This is what I came up with. It's horrid, but "seems to work".
; enter a number and add 1 - the hard way!
; nasm -f elf32 myprog.asm
; ld -o myprog myprog.o -melf_i386
global _start
; you may have these in an ".inc" file
sys_exit equ 1
sys_read equ 3
sys_write equ 4
stdin equ 0
stdout equ 1
stderr equ 2
LF equ 10
section .data
prompt db "Enter a number - not more than 10 digits - no nondigits.", LF
prompt_size equ $ - prompt
errmsg db "Idiot human! Follow instructions next time!", LF
errmsg_size equ $ - errmsg
section .bss
buffer resb 16
fakecarry resb 1
section .text
_start:
nop
mov eax, sys_write
mov ebx, stdout
mov ecx, prompt
mov edx, prompt_size
int 80h
mov eax, sys_read
mov ebx, stdin
mov ecx, buffer + 1 ; leave a space for an extra digit in front
mov edx, 11
int 80h
cmp byte [buffer + 1 + eax - 1], LF
jz goodinput
; pesky user has tried to overflow us!
; flush the buffer, yell at him, and kick him out!
sub esp, 4 ; temporary "buffer"
flush:
mov eax, sys_read
; ebx still okay
mov ecx, esp ; buffer is on the stack
mov edx, 1
int 80h
cmp byte [ecx], LF
jnz flush
add esp, 4 ; "free" our "buffer"
jmp errexit
goodinput:
lea esi, [buffer + eax - 1] ; end of input characters
mov byte [fakecarry], 1 ; only because we want to add 1
xor edx, edx ; count length as we go
next:
; check for valid decimal digit
mov al, [esi]
cmp al, '0'
jb errexit
cmp al, '9'
ja errexit
add al, [fakecarry] ; from previous digit, or first... to add 1
mov byte [fakecarry], 0 ; reset it for next time
cmp al, '9' ; still good digit?
jna nocarry
; fake a "carry" for next digit
mov byte [fakecarry], 1
mov al, '0'
cmp esi, buffer + 1
jnz nocarry
; if first digit entered, we're done
; save last digit and add one ('1') into the space we left
mov [esi], al
inc edx
dec esi
mov byte [esi], '1'
inc edx
dec esi
jmp done
nocarry:
mov [esi], al
inc edx
dec esi
cmp esi, buffer
jnz next
done:
inc edx
inc edx
mov ecx, esi ; should be either buffer + 1, or buffer
mov ebx, stdout
mov eax, sys_write
int 80h
xor eax, eax ; claim "no error"
exit:
mov ebx, eax
mov eax, sys_exit
int 80h
errexit:
mov edx, errmsg_size
mov ecx, errmsg
mov ebx, stderr
mov eax, sys_write
int 80h
mov ebx, -1
jmp exit
;-----------------------------
Is that what you had in mind?

x86 ASM Linux - Creating a loop

I am working on a program - it should be simple - on a Linux OS using NASM and x86 Intel Assembly Syntax.
The problem I am having is that I cannot create a working loop for my program:
section .data
hello: db 'Loop started.', 0Ah ;string tells the user of start
sLength: equ $-hello ;length of string
notDone: db 'Loop not finished.', 0Ah ;string to tell user of continue
nDLength: equ $-notDone ;length of string
done: db 'The loop has finished', 0Ah ;string tells user of end
dLength: equ $-done ;length of string
section .text
global _start:
_start:
jmp welcome ;jump to label "welcome"
mov ecx, 0 ;number used for loop index
jmp loop ;jump to label "loop"
jmp theend ;jump to the last label
welcome:
mov eax, 4
mov ebx, 1
mov ecx, hello
mov edx, sLength
int 80 ;prints out the string in "hello"
loop:
push ecx ;put ecx on the stack so its value isn't lost
mov eax, 4
mov ebx, 1
mov ecx, notDone
mov edx, nDLength
int 80 ;prints out that the loop isn't finished
pop ecx ;restore value
add ecx, 1 ;add one to ecx's value
cmp ecx, 10
jl loop ;if the value is not ten or more, repeat
theend:
;loop for printing out the "done" string
I am getting the first string printed, one "Not done" and the last string printed; I am missing nine more "Not Done"s! Does anyone have any idea as to why I am losing my value for the ecx register?
Thank you.
_start:
jmp welcome
This means all the code below the JMP is not executed, especially the mov ecx,0 (which should be xor ecx,ecx for a shorter instruction)
Don't start with a jump, start with some code. A JMP is a jump, it's not going back after you've jumped, it just continues the execution.
So after jumping to Welcome:, you go directly to Loop:, thus missing the ecx=0 code.
cmp ecx, 10
jl loop
ECX is not 0, it definitely is greater than 10h, so the loop is not taken.
Try this:
_start:
mov eax, 4
mov ebx, 1
mov ecx, hello
mov edx, sLength
int 80 ;prints out the string in "hello"
xor ecx,ecx ;ecx = 0
loop:
push ecx ;save loop index
mov eax, 4
mov ebx, 1
mov ecx, notDone
mov edx, nDLength
int 80 ;prints out that the loop isn't finished
pop ecx ;get loop index back in ECX
add ecx, 1 ;add one to ecx's value
cmp ecx, 10
jl loop ;if the value is not ten or more, repeat
theend:
You are setting the loop register ecx initial value to the address of "hello", and not 0:
jmp welcome
(mov ecx, 0) ;number used for loop index <- jumped over
...
welcome:
...
mov ecx, hello <- setting
int 80 <- ecx
...
loop:
push ecx ;put ecx on the stack so its value isn't lost

Resources