-2

I wonder why the assembly version is not showing any output when piped into hexdump?

Source code (printf.c):

#include <stdio.h>

// gcc -o printf printf.c && ./printf

int main() {
    const char *frmt = "%s\n";
    char *msg = "TEST";

    printf(frmt, msg);

    return 0;
}

Output to STDOUT:

$ ./printf
TEST

Pipe to hexdump:

$ ./printf | hexdump -v -C
00000000  54 45 53 54 0a                                    |TEST.|
00000005

Strace:

$ strace -ttT -f -e trace="write" ./printf
17:44:33.841789 write(1, "TEST\n", 5TEST
)   = 5 <0.000050>
17:44:33.842320 +++ exited with 0 +++

Source code (printf.nasm):

; nasm -g -f elf64 printf.nasm && ld printf.o -o printf && ./printf

extern printf

global main

section .text
main:
    mov rdi, frmt
    mov rsi, msg
    xor rax, rax
    call printf

    jmp exit

exit:
    mov rax, 60
    xor rdi, rdi
    syscall

section .data
    frmt db "%s", 0xa, 0x0
    msg: db "TEST"
    len:  equ $ - msg

Output to STDOUT:

$ ./printf
TEST

Pipe to hexdump (no output):

$ ./printf | hexdump -v -C

Strace:

$ strace -ttT -f -e trace="write" ./printf
17:46:00.828887 write(1, "TEST\n", 5TEST
)   = 5 <0.000050>
17:46:00.829282 +++ exited with 0 +++
HTF
  • 6,632
  • 6
  • 30
  • 49
  • 1
    You're missing the trailing null on `msg`? – Barmar Jan 19 '21 at 18:08
  • @Barmar no, it doesn't seem to be the case. I've tried to match the `C` code so there is one in `frmt`, unless this is not how it should be done. – HTF Jan 19 '21 at 18:13
  • 1
    All C strings end with `0x0`. You have it in `frmt` but not `msg` in the assembly. – Barmar Jan 19 '21 at 18:15
  • In C all `char` strings are really called ***null-terminated** byte strings*. That *null-terminator* must exist for *all* strings. If it doesn't exist for `"TEST"` then the `printf` function would simply not know when and where the string ended. – Some programmer dude Jan 19 '21 at 18:18
  • 3
    Probably because your call to printf is being buffered and because you use syscall to exit the program the buffer didn't get flushed and the output lost. Rather than using syscall to exit, call `exit` to ensure the buffers are properly flushed and to terminate the process. – Michael Petch Jan 19 '21 at 18:34
  • 1
    @Michael Petch: I agree, I also think that the output is buffered. However, I think returning from `main` (much like the C example does) will likewise end up flushing the buffers as intended. – ecm Jan 19 '21 at 18:45
  • 2
    @ecm: yes a simple `ret` will do as well as the buffers will be flushed and process will be terminated that way as well. Both ways the C runtime will flush any buffers and do any necessary tear down before terminating the process. – Michael Petch Jan 19 '21 at 18:47

1 Answers1

1

%s format is used for printing C strings, which are required to end with a zero byte. You're missing the null terminator for msg.

section .data
    frmt db "%s", 0xa, 0x0
    msg: db "TEST", 0x0
    len:  equ $ - msg

There also doesn't seem to be any need for the len variable, which you never use.

Barmar
  • 741,623
  • 53
  • 500
  • 612
  • Thanks, this still didn't solve the issue. – HTF Jan 19 '21 at 18:24
  • 3
    The issue here is that the exit syscall is preventing any data the C library buffered (ie via `printf`) to be properly flushed before the process terminates. Rather than use syscall to exit, use the C library `exit` function. Had they used the sys_write system call to write then the string wouldn't have to be nul terminated. Almost wonder if they had a version at one point that did just that. – Michael Petch Jan 19 '21 at 18:41
  • 1
    The alternative to `exit` is simply using `ret` from `main` in this case. – Michael Petch Jan 19 '21 at 18:49