5

I noticed that the FreeBSD code in /bin and /usr/bin have some fix to use exit instead of return, what does that mean?

All I have in my thought is that the return statement may cause vfork(2) to corrupt the stack frame, is that the only reason for this? If that was true, then why just a portion of the commands in /bin and /usr/bin got fixes, not all of them?

3442
  • 8,248
  • 2
  • 19
  • 41
oxnz
  • 835
  • 6
  • 16
  • 3
    Can you point to something more concrete? Like a commit, a list of source files, or something? This question is very hard to understand without more context. – unwind Nov 12 '15 at 09:35
  • 1
    freeBSD `/bin/df` has a commit 'Use exit() instead of return in `main()`' recently, but /bin/cat still use `return` in `main()`. – oxnz Nov 12 '15 at 10:40
  • 2
    @oxnz: maybe something like this? http://stackoverflow.com/a/5856935/214671 – Matteo Italia Nov 12 '15 at 10:48
  • 2
    Sorry, but no. This is the commit which confused me. https://github.com/freebsd/freebsd/commit/5ec82107b61b159ab874773644b3df4880e4874e – oxnz Nov 12 '15 at 10:53
  • found a similar question: http://stackoverflow.com/questions/461449/return-statement-vs-exit-in-main – oxnz Nov 18 '15 at 02:49
  • It's simply good style and robust practice to use `exit()` wherever the intention is to terminate the program, even within `main()`. The commit author's reasoning and justification was confused and vastly over-thought, and full of unsubstantiated assumptions. There is a difference between `return` and `exit()` from `main()`, but it is totally irrelevant in this specific example. – Greg A. Woods Dec 09 '15 at 19:56

3 Answers3

1

According to 5.1.2.2.3p1

If the return type of the main function is a type compatible with int, a return from the initial call to the main function is equivalent to calling the exit function with the value returned by the main function as its argument

This rules out your corrupt the stack frame theory; return 0; is functionally identical to exit(0); for conforming C implementations (at least for non-recursive main entry points).

I would suggest that this change was merely stylistic, or perhaps guided by ignorance. Another possibility is that the author has the desire to transform main into a recursive function (or main has already been turned into a recursive function; idk, I just did a quick grep and it doesn't seem immediately recursive). Finally, as the last grim alternative, perhaps the C implementation that FreeBSD uses is non-conforming (I sure hope not!)...

edit: It just occurred to me by reading through this answer that this could have been a work-around for a compiler bug, but alas I checked the source code for usage of atexit and could find no reason to pursue this line of reasoning any further.

Community
  • 1
  • 1
autistic
  • 1
  • 3
  • 35
  • 80
1

Let's see if I can amalgamate the various links provided by others into an appropriate answer.


As far as the C language is concerned, there is no difference.

A call to exit() does

  1. call the functions registered with atexit();
  2. flushes and closes the output streams;
  3. removes files opened with tmpfile();
  4. returns control to the environment.

A return <n> from main is equivalent to exit( <n> ).


Some flawed static code analyzers think different.

As far as the C language is concerned, memory allocated by main() and not free()d is leaked. Unix cleans up at process termination, but not all operating systems do (!).

Apparently, some static code analyzers consider memory still allocated at the point of exit() to be not leaked (while they do for return from main()), which is why that commit was made (to get rid of the warning).

This is a workaround for a bug in the code analyzer.


As far as the C++ language is concerned, there is a lot of difference.

When you return from main(), you are leaving the scope of the function, which means local objects get destroyed.

Since C++ programmers enjoy the benefits of deterministic destruction (as opposed to e.g. Java which might or might not execute your destructor even on VM termination...), they tend to make their destructors do more than just freeing memory. Network connections, temporary files, terminal windows locked by ncurses, all that kind of goodness, which C programmers would have to care for manually or use atexit() for.

When you call std::exit() from main(), that function directly turns over control to the runtime. The main() function never returns, the process gets terminated without calling the destructors of main()-local objects.

Moreover, while std::exit() is defined to flush and close the C output streams, there is no such provision for the C++ output streams.

Community
  • 1
  • 1
DevSolar
  • 67,862
  • 21
  • 134
  • 209
  • You're over-thinking things too much and making assumptions. (1) There *is* a difference between `return` and `exit()` from `main()`, and that is to do with the lifetime of objects defined in `main()` which have automatic-storage duration (and the rare possibility they might be referenced by a callback registered with `atexit()`). (2) Any memory allocated anywhere and not freed before `exit()` is leaked. There is nothing special about `main()`. There is no actual static analyzer that will be confused (the author was also making assumptions and was confused). (3) C++ is irrelevant here. – Greg A. Woods Dec 09 '15 at 19:49
0

There's not really a very good reason for doing that, in my opinion.

This particular change is discussed in this post on the freebsd mailing list.

The short story is that the commit log should be

"Use exit() instead of return in main() to work around a broken static analyzer"

So it's to help static analyzers, and memory analyzers that would otherwise report stuff allocated in main() as a memory leak - which most simple utilities doesn't bother to de-allocate since the process is anyway going to exit.

nos
  • 223,662
  • 58
  • 417
  • 506