Nasm Linux Assembly Printing Integers

NASM Linux Assembly Printing Integers

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

How to print a number in assembly NASM?

If you're already on Linux, there's no need to do the conversion yourself. Just use printf instead:

;
; assemble and link with:
; nasm -f elf printf-test.asm && gcc -m32 -o printf-test printf-test.o
;
section .text
global main
extern printf

main:

mov eax, 0xDEADBEEF
push eax
push message
call printf
add esp, 8
ret

message db "Register = %08X", 10, 0

Note that printf uses the cdecl calling convention so we need to restore the stack pointer afterwards, i.e. add 4 bytes per parameter passed to the function.

printing numbers in nasm

Answer rewritten after some experimentation.

There two errors in your code, and a few inefficiencies.

First, you add 0x30 to the number (to turn it from the number 1 to the ASCII 1). However, you do that increment inside the loop. As a result, your first iteration cx is 0x31, second 0x62 ("b"), third 0x93 (invalid UTf-8 sequence) etc.

Just initialize cx to 0x30 and remove the add from inside the loop.

But there's another problem. RCX is clobbered during system calls. Replacing cx with r12 causes the program to work.

In addition to that, you pass the buffer's length to write, but it only has one character. The program so far:

section .bss

lena equ 1024
outbuff resb lena

section .data

section .text

global _start
_start:
nop
mov r12,30h

incre:
inc r12
mov [outbuff],r12

cmp r12,39h
jg done

cmp r12,39h
jl print


print:
mov rax,1 ;sys_write
mov rdi,1
mov rsi,outbuff
mov rdx,1
syscall
jmp incre

done:
mov rax,60 ;sys_exit
mov rdi,0
syscall

Except even now, the code is extremely inefficient. You have two compares on the same condition, one of them branches to the very next instruction.

Also, your code would be much much much faster and smaller if you moved the breaking condition to the end of the code. Also, cx is a 16 bit register. r12 is a 64 bit register. We actually only need 8 bits. Using larger registers than needed means all of our immediates waste up space in memory and the cache. We therefor switch to the 8 bit variant of r12. After these changes, we get:

section .bss

lena equ 1024
outbuff resb lena

section .data

section .text

global _start
_start:
nop
mov r12b,30h

incre:
inc r12b
mov [outbuff],r12b

mov rax,1 ;sys_write
mov rdi,1
mov rsi,outbuff
mov rdx,1
syscall

cmp r12b,39h
jl incre

mov rax,60 ;sys_exit
mov rdi,0
syscall

There's still lots more you can do. For example, you call the write system call 9 times, instead of filling the buffer and then calling it once (despite the fact that you've allocated a 1024 bytes buffer). It will probably be faster to initialize r12 with zero (xor r12, r12) and then add 0x30. (not relevant for the 8 bit version of the register).

Printing an integer with x86 32-bit Linux sys_write (NASM)

Your numbers will quickly grow larger than just a single digit. What you ought to be doing is have an integer in num rather than a character, and then convert that integer into a string that you can print with sys_write.

Here's one way of doing the conversion: repeated division by 10, getting the lowest digit first as the remainder:

; Input:
; eax = integer value to convert
; esi = pointer to buffer to store the string in (must have room for at least 10 bytes)
; Output:
; eax = pointer to the first character of the generated string
; ecx = length of the generated string
int_to_string:
add esi,9
mov byte [esi],0 ; String terminator

mov ebx,10
.next_digit:
xor edx,edx ; Clear edx prior to dividing edx:eax by ebx
div ebx ; eax /= 10
add dl,'0' ; Convert the remainder to ASCII
dec esi ; store characters in reverse order
mov [esi],dl
test eax,eax
jnz .next_digit ; Repeat until eax==0

; return a pointer to the first digit (not necessarily the start of the provided buffer)
mov eax,esi
ret

Which you can use like this:

    mov    dword [num],1
...
mov eax,[num] ; function args using our own private calling convention
mov esi,buffer
call int_to_string
; eax now holds the address that you pass to sys_write
...

section .bss
num resd 1
buffer resb 10

Your number-doubling can be simplified to shl dword [num],1. Or better, double it at some point while it's still in a register with add eax,eax.

Scan an integer and print the interval (1, integer) in NASM

There are several problems:

1. The parameters to printf, as discussed in the comments. In x86-64, the first few parameters are passed in registers.

2. printf does not preserve the value of eax.

3. The stack is misaligned.

4. rbx is used without saving the caller's value.

5. The address of integer is being loaded instead of its value.

6. Since printf is a varargs function, eax needs to be set to 0 before the call.

7. Spurious int 80h after the call to scanf.

I'll repeat the entire function in order to show the necessary changes in context.

main:
push rbx ; This fixes problems 3 and 4.

mov eax, 4
mov ebx, 1
mov ecx, message1
mov edx, message1Len
int 80h

mov rdi, formatin
mov rsi, integer
mov al, 0
call scanf

mov ebx, [integer] ; fix problems 2 and 5
loop:
mov rdi, formatout ; fix problem 1
mov esi, ebx
xor eax, eax ; fix problem 6
call printf
dec ebx
jnz loop

pop rbx ; restore caller's value
mov rax,0

ret

P.S. To make it count up instead of down, change the loop like this:

    mov ebx, 1
loop:
<call printf>
inc ebx
cmp ebx, [integer]
jle loop

How to print a character in Linux x86 NASM?

ecx should contain a pointer to the start of your char buffer. So you have to have your buffer in memory. You can do the following:

; Print 'A' character 
mov eax, 4 ; __NR_write from asm/unistd_32.h (32-bit int 0x80 ABI)
mov ebx, 1 ; stdout fileno

push 'A'
mov ecx, esp ; esp now points to your char
mov edx, 1 ; edx should contain how many characters to print
int 80h ; sys_write(1, "A", 1)

; return value in EAX = 1 (byte written), or error (-errno)

add esp, 4 ; restore esp if necessary

You can mov byte [esp], 'A' or whatever other address if it's OK to overwrite whatever is on the stack.

Or you can have a character array in section .rodata instead of storing on the fly.


Making a write() system call with the const void *buf arg being some small number (like 'A') will make it return -EFAULT without printing anything. The kernel has to check the pointer anyway, and system calls return an error instead of raising SIGSEGV on bad pointers.

Use strace ./my_program to trace the system calls you actually made, including decoding the return values.



Related Topics



Leave a reply



Submit