7

I've recently read this article on using printf and scanf in assembly:

Meaning of intfmt: db "%d", 10, 0 in assembly

In particular it says "In printf, the newline prints a newline and then (if the output is in line buffered mode, which it probably is), flushes the internal output buffer so you can actually see the result. So when you remove the 10, there's no flush and you don't see the output."

However I do not know what to do if I do not want a newline after my output in my assembly file. Here's a simple test file I've written to try printing without a newline:

extern printf


LINUX        equ     80H      ; interupt number for entering Linux kernel
EXIT         equ     60       ; Linux system call 1 i.e. exit ()




section .data
    int_output_format: db "%ld", 0


segment .text
    global  main


main:
    mov r8, 10
    push rdi
    push rsi
    push r10
    push r9
    mov rsi, r8
    mov rdi, int_output_format
    xor rax, rax
    call printf
    pop r9
    pop r10
    pop rsi
    pop rdi
    call os_return      ; return to operating system


os_return:
    mov  rax, EXIT      ; Linux system call 1 i.e. exit ()
    mov  rdi, 0     ; Error code 0 i.e. no errors
    syscall     ; Interrupt Linux kernel 64-bit

but as the article I've read suggests stdout isn't being flushed. I was thinking perhaps I need to somehow flush after I output the number? But I'm really not sure.

I am using the NASM assembly language.

Thanks in advance!

Community
  • 1
  • 1
Sarah Tattersall
  • 1,275
  • 2
  • 21
  • 32

5 Answers5

8

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.)

Peter Cordes
  • 328,167
  • 45
  • 605
  • 847
Sarah Tattersall
  • 1,275
  • 2
  • 21
  • 32
  • 1
    `fflush` takes a FILE* arg (in RDI). You need to zero RDI, not RAX, to pass it a NULL pointer (which means flush all open streams), or you need to `mov rdi, [rel stdout]` to load that specific global variable's value. fflush isn't variadic so it doesn't care about the value of AL. (edit: I'll just fix that myself) – Peter Cordes Feb 02 '21 at 20:07
  • Generally if you're using stdio calls from libc, you should exit with `call exit`, rather than a raw _exit system call. – Peter Cordes Apr 24 '23 at 04:13
3

Call fflush(stdout); to display what's currently sitting in the buffers.

Alexey Frunze
  • 61,140
  • 12
  • 83
  • 180
3

For Windows 32-bit mode (FASM):

push [_iob]
call [fflush]   ; call into DLL.  Callee-pops calling convention

GNU/Linux 32-bit mode (NASM)

extern fflush
extern stdout
...
push dword [stdout]
call fflush            ; linker takes care of PLT stub for dynamic linking (in a non-PIE executable)
add  esp, 4            ; caller-pops calling convention
etc...
Peter Cordes
  • 328,167
  • 45
  • 605
  • 847
Mikhail
  • 7,749
  • 11
  • 62
  • 136
1

The other possibility would be to remove the default line buffering of the stdoutstream. Here the C call to do that. Translation to assembly let as exercice, as I don't think it makes even sense to do file/stream I/O in ASM, the cost/benefit is tremendously wrong.

setvbuf(stdout, NULL, _IONBF, 0);

This way every printf (and fputs, putc, puts etc...) would have an implicit fflush

Patrick Schlüter
  • 11,394
  • 1
  • 43
  • 48
0

My answer is for those searching a fast bypass to the problem, not an actual fix.

I was attempting to output the number 1234 one digit at a time and encountered the same issue as those here. After having no success with the mentioned solutions, and not wanting to spend more than a few minutes on this, I have found an easy way to display the number.

In your output string formats, simply have an output string that is an empty line (with the newline ofcourse).

Digit_out: db "%u", 0
Number_end: db "", 10, 0

Output as you would; in my case I had 4 pushes of digit_out (with 4 pushes of the digits themselves) and 4 calls to printf.

Once this is done, push Number_end and do a final call to printf. The entire number will then show :)

Dharman
  • 30,962
  • 25
  • 85
  • 135
Remi Jones
  • 45
  • 4
  • TL:DR: just print a newline eventually, before exiting, with any stdio function. Yes, this works as long as your stdout is a terminal, not redirected to a file or pipe (which would make it default to full-buffered). Normally you want your output to end with a newline anyway, but you should *also* exit with `call exit` (or `ret` from `main`) if you've been using stdio functions, not a raw `_exit` syscall. Then your program will work consistently even when outputting to a regular file or other non-terminal. – Peter Cordes Mar 20 '22 at 01:34