Assembly: Printf Not Printing New Line

Assembly: printf not printing new line

When you use quotes or double quotes around a string in NASM, it doesn't accept C style escape sequences. On Linux you can encode \n as ASCII 10 like this:

fmt db "Number of parameters: %d", 10, 0 

There is an alternative. NASM supports backquotes (backticks) which will allow NASM to process the characters between them as C style escape sequences. This should work as well:

fmt db `Number of parameters: %d \n`, 0

Please note: Those are not single quotes, but backticks. This is described in the NASM documentation:

3.4.2 Character Strings

A character string consists of up to eight characters enclosed in either single quotes ('...'), double quotes ("...") or backquotes (...). Single or double quotes are equivalent to NASM (except of course that surrounding the constant with single quotes allows double quotes to appear within it and vice versa); the contents of those are represented verbatim. Strings enclosed in backquotes support C-style -escapes for special characters.

Printf without newline in assembly

fflush() flushes buffered output in line or full-buffered stdio streams:

extern fflush
...
xor edi, edi ; RDI = 0
call fflush ; fflush(NULL) flushes all streams
...

Alternatively, mov rdi, [stdout] / call fflush also works to flush only that stream. (Use default rel for efficient RIP-relative addressing, and you'll need extern stdout as well.)

printing new lines with printf assembly

NASM accepts strings in single quotes ('...') or double quotes ("..."), which are equivalent, and do not provide any escapes; or in backquotes (`...`), which provide support for C-style escapes, which is what you want.

(See section 3.4.2, "Character Strings", in the documentation.)

To get actual ASCII newlines in your data in memory, rather than literal backslash n:

sentence0: db `Hello\nWorld\n`, 0

Or do it manually:

sentence0: db 'Hello', 10, 'World`, 10, 0

YASM (another NASM-syntax assembler) doesn't accept backticks, so the manual option is your only choice there.

And BTW, you can call puts instead of printf if you don't have any actual formatting in your format string (leave out the trailing newline).

x86 Assembly - printf doesn't print without \n

The problem is that printf by default just prints stuff into the stdout buffer. Things won't actually be printed until the buffer is flushed. The depends on the buffering mode of stdout, but, by default, it is line-buffered, which means it gets flushed every time you print a newline character.

To flush explicitly in C, you call fflush; you can do that in asm code with

pushl stdout
call fflush
addl $4, %esp

Alternately, you can call the stdlib exit function (which flushes all I/O buffers before actually exiting), instead of using the _exit system call, which does not.

Printf call from assembly do not print to stdout

If you want to use C library functions you shouldn't use _start, but good old main. If you steal _start from libc, it won't be able to perform several setup and cleanup operations, such as flushing the open C files.

This is visible in this case only when writing to file, as when writing to a tty stdout is line-buffered by default, so it's flushed immediately. When redirecting to file, instead, the data is just copied in a temporary buffer, to be flushed when it's full enough, or at termination - but the C runtime cleanup functions aren't ever called in your case, so the data is never actually passed to the OS.

Another possibility is to exit using the exit libc function (extern exit at the beginning and call exit at the end of the execution), which should handle the cleanup, but I'm not sure if stuff from libc is even required to work fine if it doesn't get the chance to be initialized in first place should be safe on Linux, see @PeterCordes comment and thanks to @Kirill_Zaitsev for trying it.

Using printf in assembly leads to empty output when piping, but works on the terminal

Use call exit instead of a raw _exit syscall after using stdio functions like printf. This flushes stdio buffers (write system call) before making an exit_group system call).

(Or if your program defines a main instead of _start, returning from main is equivalent to calling exit. You can't ret from _start.) Calling fflush(NULL) should also work.


As Michael explained, it is OK to link the C-library dynamically. This is also how it is introduced in the "Programming bottom up" book (see chapter 8).

However it is important to call exit from the C-library in order to end the program and not to bypass it, which was what I wrongly did by calling exit-syscall. As hinted by Michael, exit does a lot of clean up like flushing streams.

That is what happened: As explained here, the C-library buffers the the standard streams as follows:

  1. No buffering for standard error.
  2. If standard out/in is a terminal, it is line-buffered.
  3. If standard out/in is a not a terminal, it is fully-buffered and thus flush is needed before a raw exit system call.

Which case applies is decided when printf is called for the first time for a stream.

So if printf_try is called directly in the terminal, the output of the program can be seen because hello has \n at the end (which triggers the flush in the line-buffered mode) and it is a terminal, also the 2. case.

Calling printf_try via $(./printf_try) means that the stdout is no longer a terminal (actually I don't know whether is is a temp file or a memory file) and thus the 3. case is in effect - there is need for an explicit flush i.e. call to C-exit.

MIPS printing new line methods

We can print one character at a time — one per syscall, using syscall #11.  The character to print goes in $a0.  To print a newline character without using a data declaration:

li $a0, 10   # $a0 == character to print: newline '\n' (linefeed, 0xA/10)
li $v0, 11 # syscall function code #11 -- print one character
syscall

Also, often we can include the newline character, '\n' at the beginning or end of another string that we already need to printed, so the newline doesn't necessarily have to be printed separately in its own syscall.

For example, let's do the equivalent of printf("sum: %d\n, avg: %d\n", sum, avg);  This printf prints 2 lines of output, i.e. uses two newline characters:

Decomposing this we get:

la $a0, sum     # "sum: "
li $v0, 4 # syscall function code #4 for print string
syscall # prints "sum: "

move $a0, $s0 # assume sum in $s0, move to $a0 for syscall
li $v0, 1 # syscall function code #1 for print integer
syscall

li $a0, avg # "\n, avg: "
li $v0, 4 # syscall function code #4 for print string
syscall

move $a0, $s1 # assume avg in $s1
li $v0, 1 # syscall function code #1 for print integer
syscall

li $a0, 10
li $v0, 11
syscall

.data
sum: .asciz "sum: "
avg: .asciz "\n, avg: "

This sequence prints 2 lines of output.  The first '\n' newline is appended to the beginning of the prompt/introducing string "avg: ".  The second newline is printed using a syscall #11, so the whole sequence doesn't require a separate "\n" data declaration.

The printf format string is decomposed into string literals as follows:

printf("sum: %d\n, avg: %d\n", sum, avg);
^---^ ^-------^ ^
lit1 lit2 lit3

lit1 is "sum: "; lit2 is "\n, avg: "; lit3 is one character so doesn't need data.  You can see that lit2 contains the end of one line and the beginnig of another, together printed in one syscall.  Between lit1 and lit2 and lit3 are syscall #1's to print integers.



Related Topics



Leave a reply



Submit