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.
Can't get single character to print in Linux NASM
This line is incorrect:
mov ecx, ebx ; move ebx to print
write
(int 80h / eax=4
) expects ecx
to contain the address of the data to write (see this table). But you're passing the data itself.
In your modified code you're placing the character on the stack and then passing its address in ecx
, so that's correct.
However, you've already incremented ecx
by the time you get to .printchar
. That's why your code doesn't print the first character.
As a side note, your check for even/odd numbers is unncessarily complicated. It could be simplified to just:
test ecx,1 ; set EFLAGS based on ecx AND 1
jnz .printchar
NASM Print one Character at a Time
One of the reasons it's not printing is because ebx
is supposed to hold the value 1 to specify stdin
, and another is because sys_write
takes a pointer (the address of your string) as an argument, not an actual character value.
Anyway, let me show you a simpler way of structuring your program:
section .data
SYS_EXIT equ 1
SYS_WRITE equ 4
STDOUT equ 1
TRAP equ 0x80
NUL equ 0
hello: db "Hello World",0xA,NUL ; 0xA is linefeed, terminate with NUL
section .text
global _start
_start:
nop ; for good old gdb
mov ecx, hello ; ecx is the char* to be passed to sys_write
read:
cmp byte[ecx], NUL ; NUL indicates the end of the string
je exit ; if reached the NUL terminator, exit
; setup the registers for a sys_write call
mov eax, SYS_WRITE ; syscall number for sys_write
mov ebx, STDOUT ; print to stdout
mov edx, 1 ; write 1 char at a time
int TRAP; ; execute the syscall
inc ecx ; increment the pointer to the next char
jmp read ; loop back to read
exit:
mov eax, SYS_EXIT ; load the syscall number for sys_exit
mov ebx, 0 ; return a code of 0
int TRAP ; execute the syscall
It can be simpler to NUL terminate your string as I did, or you could also do $-hello
to get it's length at compile time. I also set the registers up for sys_write
at each iteration in the loop (as you do), since sys_write
doesn't preserve all the registers.
How to print a string to the terminal in x86-64 assembly (NASM) without syscall?
This is a good exercise. You will use syscall
(you cannot access stdout
otherwise), but you can do a "bare-metal" write without any external library providing the output routine (like calling printf
). As an example of the basic "bare-metal" write to stdout
in x86_64, I put together a example without any internal or system function calls:
section .data
string1 db 0xa, " Hello StackOverflow!!!", 0xa, 0xa, 0
section .text
global _start
_start:
; calculate the length of string
mov rdi, string1 ; string1 to destination index
xor rcx, rcx ; zero rcx
not rcx ; set rcx = -1
xor al,al ; zero the al register (initialize to NUL)
cld ; clear the direction flag
repnz scasb ; get the string length (dec rcx through NUL)
not rcx ; rev all bits of negative results in absolute value
dec rcx ; -1 to skip the null-terminator, rcx contains length
mov rdx, rcx ; put length in rdx
; write string to stdout
mov rsi, string1 ; string1 to source index
mov rax, 1 ; set write to command
mov rdi,rax ; set destination index to rax (stdout)
syscall ; call kernel
; exit
xor rdi,rdi ; zero rdi (rdi hold return value)
mov rax, 0x3c ; set syscall number to 60 (0x3c hex)
syscall ; call kernel
; Compile/Link
;
; nasm -f elf64 -o hello-stack_64.o hello-stack_64.asm
; ld -o hello-stack_64 hello-stack_64.o
output:
$ ./hello-stack_64
Hello StackOverflow!!!
For general use, I split the process into two parts (1) getting the length and (2) writing to stdout
. Below the strprn
function will write any string to stdout
. It calls strsz
to get the length while preserving the destination index on the stack. This reduces the task of writing a string to stdout
and prevents a lot of repitition in your code.
; szstr computes the lenght of a string.
; rdi - string address
; rdx - contains string length (returned)
section .text
strsz:
xor rcx, rcx ; zero rcx
not rcx ; set rcx = -1 (uses bitwise id: ~x = -x-1)
xor al,al ; zero the al register (initialize to NUL)
cld ; clear the direction flag
repnz scasb ; get the string length (dec rcx through NUL)
not rcx ; rev all bits of negative -> absolute value
dec rcx ; -1 to skip the null-term, rcx contains length
mov rdx, rcx ; size returned in rdx, ready to call write
ret
; strprn writes a string to the file descriptor.
; rdi - string address
; rdx - contains string length
section .text
strprn:
push rdi ; push string address onto stack
call strsz ; call strsz to get length
pop rsi ; pop string to rsi (source index)
mov rax, 0x1 ; put write/stdout number in rax (both 1)
mov rdi, rax ; set destination index to rax (stdout)
syscall ; call kernel
ret
To further automate general output to stdout
NASM macros provide a convenient solution. Example strn
(short for string_n
). It takes two arguments, the addresses of the string, and the number of characters to write:
%macro strn 2
mov rax, 1
mov rdi, 1
mov rsi, %1
mov rdx, %2
syscall
%endmacro
Useful for indents, newlines or writing complete strings. You could generalize further by passing 3 arguments including the destination for rdi
.
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.
linux nasm print multiple characters
In the first time you enter the Print
section, ecx
is pointing to the start of the string and you use it to copy a single character to the start of the output string. But a few more instructions down, you overwrite ecx
with the pointer to the output string, and never restore it, therefore you never manage to copy and print the rest of the string.
Also, why are you calling write()
with a single character string with the aim to loop over it to print the entire string? Why not just pass num
directly in instead of copying a single character to output
and passing that?
Printing a character to standard output in Assembly x86
Sure, you can use any normal C function. Here's a NASM example that uses printf to print some output:
;
; assemble and link with:
; nasm -f elf test.asm && gcc -m32 -o test test.o
;
section .text
extern printf ; If you need other functions, list them in a similar way
global main
main:
mov eax, 0x21 ; The '!' character
push eax
push message
call printf
add esp, 8 ; Restore stack - 4 bytes for eax, and 4 bytes for 'message'
ret
message db 'The character is: %c', 10, 0
If you only want to print a single character, you could use putchar:
push eax
call putchar
If you want to print out a number, you could do it like this:
mov ebx, 8
push ebx
push message
call printf
...
message db 'The number is: %d', 10, 0
linux nasm assembly endlessly printing character
This is digging waaaay back into the deep dark recesses of my memory, but I think you want
mov eax, [other] ; move other to eax
mov ebx, [data] ; move data to ebx
Note the brackets, which are missing in your code. You are loading the addresses of other
and data
into eax
and ebx
, not the values contained there.
x86 nasm assembly, how to print multiplication result correctly?
The AAM
instruction could be useful for cases where the result would have at most 2 digits. In your case 12 x 12 = 144, so that's a no-go.
The conversion loop that you use is almost correct, but you are mixing sizes in relation to the DIV
instruction. If you give a byte-sized operand to DIV
then the operation will divide AX
by that byte and the remainder will be in AH
. If you give a word-sized operand to DIV
then the operation will divide DX:AX
by that word and the remainder will be in DX
.
Because you're writing 32-bit code, better also write the conversion using 32-bit registers.
Instead of using a digits counter, use a sentinel on the stack. No need to preserve ECX
while displaying.
The display function expects a pointer in ECX
. Just use the stackpointer for this and pop the value post-printing.
mov al, 12
mov bl, 12
mul bl ; answer in AX
movzx eax, ax
mov ebx, 10 ; Divisor constant
push ebx ; Sentinel
.a: xor edx, edx
div ebx ; Divide EDX:EAX with EBX, quotient EAX, remainder EDX
add edx, '0' ; Add '0' to make it into character
push edx
test eax, eax ; If EAX (the quotient) is not zero, loop again.
jnz .a
.b: mov eax, 4
mov ebx, 1
mov ecx, esp
mov edx, 1
int 0x80
pop eax
cmp dword [esp], 10 ; Is it the sentinel ?
jne .b ; No, it's a digit
pop eax ; Remove sentinel
Although not 32-code, Displaying numbers with DOS has more info about converting numbers into text.
Related Topics
Aborting a Shell Script If Any Command Returns a Non-Zero Value
How to Invoke a System Call Via Syscall or Sysenter in Inline Assembly
How to Instruct Cron to Execute a Job Every Second Week
How to Run a Program With a Different Working Directory from Current, from Linux Shell
How to Use the Lines of a File as Arguments of a Command
Cannot Connect to the Docker Daemon At Unix:/Var/Run/Docker.Sock. Is the Docker Daemon Running
How to Parse a CSV File in Bash
How to Preserve Quotes in Printing a Bash Script'S Arguments
Using Awk to Print All Columns from the Nth to the Last
Static Link of Shared Library Function in Gcc
Better Way to Rename Files Based on Multiple Patterns
Difference Between Using 'Sh' and 'Source'
Linux Flock, How to "Just" Lock a File
How to Recursively Grep All Directories and Subdirectories