0

i am building and running a code in NASM on a x86_64 Linux.

The program calls the GNU libc printf function from my program.

The program only must print a sentence to stdout.

; comment
section .data
fmt: db "Hello %s %c", 0
name: db "Jane Doe", 0

section .text
global _start
extern printf

func:
lea rdi, [fmt]
lea rsi, [name]
mov rcx, 0x0A
xor rax, rax
call printf
ret

_start:
call func
; exit
mov rax, 1
mov rbx, 0
int 80h
ret

Here is the way i compile it :

nasm -f elf64 Program.s -o Program.o -Werror 
ld -m elf_x86_64 Program.o -o Program -lc -dynamic-linker /lib64/ld-linux-x86-64.so.2

When i run the program, it outputs to the terminal Hello Jane Doe. Ok, that is what i am expecting.

But when i redirect the out as following :

./Program > output.txt

The file output.txt is empty.

-rw-rw-r-- 1 me me    0 output.txt

Any idea ? It seems that the libc printf is in this case printing into another file descriptor than stdout but maybe i am wrong.

SOLUTION

The solution has been found by a user in the comments.

Switching

; exit
mov rax, 1
mov rbx, 0
int 80h

by call exit did the trick.

user7364588
  • 1,014
  • 2
  • 12
  • 32
  • 1
    Could be an issue with data not being flushed by printf if it detects that it's writing to a file? Try calling exit instead of `int 80h` (which doesn't call stuff registered by `atexit`) – Andrew Sun Feb 03 '19 at 22:04
  • Semi-related (not the problem here): the 32-bit `int 0x80` ABI won't work for system calls that need 64-bit pointers. Use the 64-bit `syscall` ABI. [What happens if you use the 32-bit int 0x80 Linux ABI in 64-bit code?](//stackoverflow.com/q/46087730). It's perfectly safe for `sys_exit` unless you're on a kernel built without CONFIG_IA32_EMULATION, or Windows Subsystem for Linux. – Peter Cordes Feb 03 '19 at 22:30

1 Answers1

4

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.

Matteo Italia
  • 123,740
  • 17
  • 206
  • 299
  • glibc startup code runs before `_start` in a *dynamically* linked executable on Linux (using `.init` hooks); that's not the problem. (It's generally not recommended to use libc from _start, but it's actually fully safe in a dynamic Linux executable, as far as I know. It doesn't work on Cygwin, which is maybe why some people recommend against it?). Only exiting without flushing stdio when it's full-buffered is the problem. – Peter Cordes Feb 03 '19 at 22:27
  • @PeterCordes @MatteoItalia Is there a way to use the good old main without compiling with `gcc` but only with `ld` ? – user7364588 Feb 04 '19 at 09:49
  • 1
    @Alrick: yes, if you pass all the same options to `ld` that gcc does, including the `.o` files that define `_start`. Or write your own `_start` that calls your `main` and then passes the return value to libc `exit(3)`. (e.g. [How Get arguments value using inline assembly in C without Glibc?](//stackoverflow.com/q/50260855) where Matteo posted a truly minimal `_start` which uses sys_exit, you could modify that to `call exit` instead.) But IDK why you would, just use gcc's front-end to run `as` and `ld` and link in the CRT startup code. – Peter Cordes Feb 04 '19 at 09:57
  • 2
    @Alrick Not possible in a portable manner. Try to avoid using `ld` directly unless you know exactly what you are doing. – fuz Feb 04 '19 at 22:04