1

I am trying to use the putchar function from the C standard library. It seems pretty straightforward--you pass in an int, and it writes the character to stdout. However, when I compile and run this program, nothing happens.

    # This doesn't work:
    .section .text
    .globl _start

_start:
    mov $0x41, %dil
    call putchar

    mov $60, %rax
    syscall
extremeaxe5
  • 723
  • 3
  • 13
  • 3
    That's because your program neither correctly initialises the libc nor correctly deinitialises it, thus not flushing buffers on exit. To fix this, start your program from `main` (instead of `_start`), link through the C compiler and terminate your program by returning from `main` or calling `exit`. – fuz Mar 24 '19 at 18:33
  • @fuz Ahh, ok. So changing the exiting behavior from the system call to `call exit` seems to have made it work. Why does this happen? – extremeaxe5 Mar 24 '19 at 18:37
  • 3
    It's because the libc does buffered I/O. On exit, output buffers are flushed by the function `exit`. If you don't call `exit` or manually flush the output buffers, they won't be flushed. That said, merely calling `exit` is insufficient! Unless you also initialise the libc correctly (by having your program start from `main` and letting the C compiler provide `_start` to initialise the libc), weird things may happen if you use any libc function. And if you use the libc, you should try not to call any system calls directly. Use the libc functions instead! – fuz Mar 24 '19 at 18:45
  • 1
    On the other hand, if you *do* forgo the use of `putchar` you can use `write` and it doesn't need any initialization – Antti Haapala -- Слава Україні Mar 24 '19 at 18:50
  • @AnttiHaapala wait is `write` just a wrapper around the sys_write system call? – extremeaxe5 Mar 24 '19 at 19:00
  • @fuz Thanks. Could you write this as the accepted answer? – extremeaxe5 Mar 24 '19 at 19:01
  • I mean especially the `write` system call – Antti Haapala -- Слава Україні Mar 24 '19 at 19:01
  • @extremeaxe5 Yes, exactly! `write` is just a wrapper around the `write` system call. You are going to find such wrappers for almost all system calls, those that don't have one have special reasons why there is no wrapper. Let me write an answer. – fuz Mar 24 '19 at 19:06

1 Answers1

3

You don't see any output because the libc's IO package performs buffered IO and you do not flush the buffer of stdout before terminating your program. This issue is a symptom of a bigger underlying problem and that is you not initialising and deinitialising the libc correctly. In every program that uses the libc, you should:

  1. let the libc initialise itself on startup, letting its initialisation code be _start and call your main function
  2. not perform raw system calls through the syscall instruction, instead use the wrappers provided by the libc
  3. terminate the program correctly by calling exit or returning from main
  4. link in all the libc code correctly by using the C compiler driver cc to link your program instead of directly invoking the link editor

Once you are more experienced, you may diverge from these suggestions, but doing so can have subtle consequences you may not be aware of, so for now it's best to just follow these rules.

fuz
  • 88,405
  • 25
  • 200
  • 352