Hello,
The following code:
#include <iostream> #include <omp.h> class Base { public: int memb_base; virtual int foo() { return 0; } }; class Derived : public Base { public: int memb_derived; virtual int foo() { return 1; } }; class Container { public: Derived* Get_d() { return &_d; } void print() { #pragma omp critical { Derived* p = Get_d(); int* vtable = *(int**)p; std::cout << "thread id "<< omp_get_thread_num() << std::endl; std::cout << "Get_d() = "<< p << " vtable = "<< std::hex << vtable << std::endl; std::cout << "foo: "<< p->foo() << std::endl; } } private: static Derived _d; #pragma omp threadprivate(_d) }; Derived Container::_d; int main() { Container cont; #pragma omp parallel for for (int i = 0; i < omp_get_num_threads(); ++i) cont.print(); return 0; }
crashes when compiled as such:
icpc -v
icpc version 18.0.3 (gcc version 6.4.1 compatibility)
icpc -qopenmp -g -O0 crash.C -o crash
using Intel Compiler 2018 Update 3 (GCC 6.4 backend):
OMP_NUM_THREADS=2 ./crash
thread id 0
Get_d() = 0x603710 vtable = 0x6035a8
foo: 1
thread id 1
Get_d() = 0x14e4b1afbf00 vtable = 0
zsh: segmentation fault (core dumped) OMP_NUM_THREADS=2 ./crash
The same code runs fine when compiled with -O2. The reason is that the threadprivate instance of _d on threads other than the main thread are not correctly initialized, the vtable ptr is 0x0 when compiled at -O0. This causes a crash when the program tries to add the offset of the virtual function foo to the start of the vtable, in order to invoke it. This is the corresponding piece of assembly:
(gdb) bt
#0 0x0000000000401966 in Container::print (this=0x7fffffffaea0) at crash.C:26
#1 0x00000000004014c2 in L_main_40__par_region0_2_2 () at crash.C:42
#2 0x00007ffff73c27d3 in __kmp_invoke_microtask () from /opt/intel/lib/libiomp5.so
#3 0x00007ffff738954a in ?? () from /opt/intel/lib/libiomp5.so
#4 0x00007ffff7388c0b in ?? () from /opt/intel/lib/libiomp5.so
#5 0x00007ffff73c2c30 in ?? () from /opt/intel/lib/libiomp5.so
#6 0x00007ffff6eb0075 in start_thread () from /usr/lib/libpthread.so.0
#7 0x00007ffff6be553f in clone () from /usr/lib/libc.so.6
(gdb) disassemble
...
0x0000000000401957 <+371>: mov $0x0,%edx
0x000000000040195c <+376>: movslq %edx,%rdx
0x000000000040195f <+379>: imul $0x8,%rdx,%rdx (offset of function foo is stored in rdx)
0x0000000000401963 <+383>: add (%rax),%rdx (add vtable which is (%rax) to offset)
=> 0x0000000000401966 <+386>: mov (%rdx),%rax (load (%rdx), causes segfault)
%rax contains the pointer to _d, (%rax) dereferences it in order to obtain the address of the vtable, $rdx is the offset of foo. Since (%rax) is 0 the last line is trying to access an invalid address which causes a segmentation violation.