Crash when destructors for static objects are executing

夙愿已清 提交于 2019-12-09 03:44:23

The cause of these mysterious & difficult to reproduce bugs: a subtle violation of the One Definition Rule.

A little background ...

Since I was a little tike this software was being linked using the -zmuldefs linker flag, instructing the linker to ignore situations like the following. It's then forced to pick the 1st definition it encounters (and of course the linker warning was being ignored as well):

$ cat /tmp/file1.cc
int x;
int main( int argc, char *argv[] ) { return x; }

$ cat /tmp/file2.cc
double x = 3.14159265358979;

$ gcc /tmp/file{2,1}.cc -o /tmp/test
/tmp/ccuTgbRy.o:(.bss+0x0): multiple definition of 'x'
/tmp/cchvHEav.o:(.data+0x0): first defined here
/usr/bin/ld: Warning: size of symbol 'x' changed from 8 in /tmp/ccYCIypE.o to 4 in /tmp/ccuTgbRy.o
collect2: error: ld returned 1 exit status

$ gcc /tmp/file{2,1}.cc -Wl,-zmuldefs -o /tmp/test
/usr/bin/ld: Warning: size of symbol 'x' changed from 8 in /tmp/ccWaeBBi.o to 4 in /tmp/ccSc9IiE.o

$ /tmp/test; echo $?
68

How this relates back to the problem

There's four basic situations I've encountered where this problem will show up:

$ cat /tmp/file1.cc
double x;  // (1) If file2.cc is linked first it may end up on
           // a dword boundary causing misaligned accesses
           // when used as a double.

std::string mystring; // (2) If file2.cc is linked first, the actual size
                      // of the object is sizeof(char*) so
                      // std::string::string() will clobber memory
                      // after the pointer.

std::string another; // (3)
                     // file1.cc is compiled with -fPIC & put into a
                     // shared library
                     // file2.cc is NOT compiled with -fPIC & is put
                     // into an executable
                     // 
                     // This will cause a very subtle problem: the two
                     // strings share the same piece of memory, but
                     // the constructor will execute once during the executable's
                     // _init() and once for each shared library with its own
                     // variable "another" when their _init() executes.
                     // The destructor will also execute multiple times

$ cat /tmp/file2.cc
int x;
char *mystring;       // (4) Modifying through this ptr will cause undefined
                      // behavior when the other file's "mystring" is used
std::string another;

The ones that result in a size or alignment change should be reported as linker warnings, so someone might be inclined to just fix the problem by renaming the offending variables (or whatever).

However, there's no way to see that the problem exists for the following situations:

  • The object sizes are the same (x is defined as float/int & sizeof(float) == sizeof(int))
  • The the offending variables (with the same size & type) exist in multiple libraries and/or executables

The only solution that will ensure you've eliminated all of these problems:

  • get rid of -zmuldefs
  • ensure all declarations come from headers / include the header where it's defined
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!