I wrote a simple C program which just calls the exit() function, however strace says that the binary is actually calling exit_group, is exit() a exit_group() wrapper? Are these two functions equivalent? If so why would the compiler choose exit_group() over exit()?
-
2So you load `1` in `eax` and called the kernel interrupt and `strace` shows an `exit_group` call (`252` `0xfc`)? Which compiler? What were your compile and link strings? (it may also be a `strace` issue, though you would think they would have the lookup table right) – David C. Rankin Oct 24 '17 at 06:02
-
1@DavidC.Rankin As far as I understand the question correctly the user is calling the `exit()` function in the `libc` library. – Martin Rosenau Oct 24 '17 at 06:05
-
2IIRC, that comes from standard C (and/or POSIX?) becoming thread-aware, and mandating that `exit()` should terminate all the running threads; on Linux the plain `exit`syscall would just terminate the current thread, so at some point the `exit()` libc function was remapped to the `exit_group` syscall, which does what is required by the standard. – Matteo Italia Oct 24 '17 at 06:06
-
@DavidC.Rankin I just fixed the question, – Trey Oct 24 '17 at 06:07
-
1@MartinRosenau, you may be correct, the `assembly` tag threw me off. (since assembly does have both and `exit` and `exit_group` syscall. – David C. Rankin Oct 24 '17 at 06:07
-
@DavidC.Rankin, More interestingly I also tried to use the _exit function from unistd.h, but nothing changed the program is still calling exit_group() – Trey Oct 24 '17 at 06:11
-
1I just did a short C file and used `gcc` to dump to assembler (e.g. `gcc -S -masm=intel -o exitcall.asm exitcall.c` and the assembler instruction is `call exit@PLT`, Running `trace` through, you are correct, I find `exit_group(0)`. I then wrote the file in assembly `nasm` and ran `strace` on the executable and it calls `exit(0) = ?; +++ exited with 0 +++` Strange, I don't have the answer, but I now fully understand the question. Are you using `gcc` as the compiler? – David C. Rankin Oct 24 '17 at 06:35
-
@DavidC.Rankin, yep, version 7.2 – Trey Oct 24 '17 at 06:55
-
1@Trey -- did you compile to object with `gas` and then link with `ld`? ... or did you let `gcc` handle the link? If so, it would be interesting to know what options `gcc` used by default. For my x86 example, I compiled with `nasm`, e.g. `nasm -f elf -o ./obj/exitcall32.o exitcall32.asm` and linked with `ld -m elf_i386 -o ./bin/exitcall32 ./obj/exitcall32.o`. There are differences in entry point (`main` v. `_start`) which may pull in the libc `exit_group` if you compiled and let `gcc` do the link. – David C. Rankin Oct 24 '17 at 06:56
-
@DavidC.Rankin I used **as** and then **ld**, without any flags, and i'm using a 64-bit machine – Trey Oct 24 '17 at 06:57
-
1@Trey: Did you use `eax=1` / `int $0x80` in 64-bit code? If so, strace decodes it wrong, but it really is `sys_exit()`. See https://stackoverflow.com/questions/46087730/what-happens-if-you-use-the-32-bit-int-0x80-linux-abi-in-64-bit-code. Works for me with `cat > foo.S` / copy-paste your code / `gcc -m32 foo.S -nostdlib -static` && `strace a.out`. I get `exit(0)`. In 64-bit a executable, strace decodes it as `write(0, NULL, 0)` and then says `+++ exited with 0 +++`. So I can't reproduce your `exit_group` from eax=1 / `int $0x80` – Peter Cordes Oct 24 '17 at 06:57
-
Either change your question back to the old one, or update it with a [mcve] for strace decoding eax=1 / `int $0x80` as `exit(0)`. It's just weird now, because that's a pretty different question from asking about what the C library wrapper functions do (and why). – Peter Cordes Oct 24 '17 at 07:42
1 Answers
The Linux and glibc man pages document all of this (See especially the "C library/kernel differences" in the NOTES section).
_exit(2)
: In glibc 2.3 and later, this wrapper function actually uses the LinuxSYS_exit_group
system call to exit all threads. Before glibc2.3, it was a wrapper forSYS_exit
to exit just the current thread.exit_group(2)
: glibc wrapper forSYS_exit_group
, which exits all threads.exit(3)
: The ISO C89 function which flushes buffers and then exits the whole process. (It always usesexit_group()
because there's no benefit to checking if the process was single-threaded and deciding to useSYS_exit
vs.SYS_exit_group
). As @Matteo points out, recent ISO C / POSIX standards are thread-aware and one or both probably require this behaviour.But apparently
exit(3)
itself is not thread-safe (in the C library cleanup parts), so I guess don't call it from multiple threads at once.syscall
/int 0x80
withSYS_exit
: terminates just the current thread, leaving others running. AFAIK, modern glibc has no thin wrapper function for this Linux system call, but I thinkpthread_exit()
uses it if this isn't the last thread. (Otherwise exit(3) -> exit_group(2).)
Only exit()
, not _exit()
or exit_group()
, flushes stdout
, leading to "printf
doesn't print anything" problems in newbie asm programs if writing to a pipe (which makes stdout
full-buffered instead of line-buffered), or if you forgot the \n
in the format string. For example, How come _exit(0) (exiting by syscall) prevents me from receiving any stdout content?. If you use any buffered I/O functions, or at_exit
, or anything like that, it's usually a good idea to call the libc exit(3)
function instead of the system call directly. But of course you can call fflush
before SYS_exit_group
.
(Also related: On x64 Linux, what is the difference between syscall, int 0x80 and ret to exit a program? - ret
from main is equivalent to calling exit(3)
)
It's not of course the compiler that chose anything, it's libc. When you include headers and write read(fd, buf, 123)
or exit(1)
, the C compiler just sees an ordinary function call.
Some C libraries (e.g. musl, but not glibc) may use inline asm to inline a syscall
instruction into your binary, but still the headers are part of the C library, not the compiler.

- 328,167
- 45
- 605
- 847
-
To be honest, you beat me to the man pages.... and again I learn how much I've still to learn. Thanks Peter. – David C. Rankin Oct 24 '17 at 06:44
-
4@DavidC.Rankin: I didn't have to look this up just now; I was curious about it years ago. :P I learned a *lot* of systems programming stuff just from reading Linux man pages and seeing what various programs did using `strace`. – Peter Cordes Oct 24 '17 at 06:49
-
@PeterCordes: This is the way. The complexity is reflected by the evolution of threading on Linux **systems** over time and the fact that the syscall mechanics remain in the API even after a better mechanism is found. I say **system** to emphasize the interplay of kernel, libc and compilers. – artless noise Dec 18 '20 at 15:05