Reported here: http://permalink.gmane.org/gmane.comp.compilers.clang.devel/38557
$ cat tmp/tsan/atexit.cc
#include <cstdio>
#include <cstdlib>
class Logger {
public:
Logger() {
std::printf("Logger ctor\n");
}
void log(const char* msg) {
std::printf("%s", msg);
}
~Logger() {
std::printf("Logger dtor\n");
}
};
Logger logger;
void log_from_atexit() {
logger.log("In log_from_atexit\n");
}
int main(int argc, char* argv[]) {
std::atexit(log_from_atexit);
return EXIT_SUCCESS;
}
$ ./bin/clang++ -fsanitize=thread tmp/tsan/atexit.cc && ./a.out
Logger ctor
Logger dtor
In log_from_atexit
This is wrong according to standard:
3.6.3 [basic.start.term] / 3:
If the completion of the initialization of an object with static storage
duration is sequenced before a call to std::atexit (see <cstdlib>, 18.5), the
call to the function passed to std::atexit is sequenced before the call to the
destructor for the object
The problem is in custom stack of atexit handlers maintained by TSan and its
atexit/__cxa_atexit interceptors:
1) TSan registers its own __tsan::finalize function, which is supposed to run
all atexit callbacks. This function is registered very early in initialization,
and thus is run very late in termination.
2) Logger::~Logger is registered with the __cxa_atexit call generated by the
compiler. But because the binary is linked as PIE, "dso_handle" argument of
__cxa_atexit is non-zero, and TSan just redirects this call to __cxa_atexit
from libc
3) log_from_atexit is registered with regular "atexit" call and is added to the
TSan stack of destructors (which are invoked after all the destructors
registered by libc version of __cxa_atexit).
Original issue reported on code.google.com by samso...@google.com on 21 Aug 2014 at 10:17
Original issue reported on code.google.com by
samso...@google.com
on 21 Aug 2014 at 10:17