12

The following test-case, reduced from a real-world application, fails to link with -fsanitize=undefined (using GCC 6.1.1) but links fine without it. Can anyone tell me why?

It seems to have something to do with the combination of Qt/QObject, -fvisibility=hidden, and -fsanitize=undefined, but exactly where the problem lies is beyond me.

lib1.h:

#include <QObject>

class MyObject : public QObject
{
public:
    MyObject (QObject * parent = nullptr);
    ~MyObject ();

    void myMethod ();
};

lib1.cc:

#include "lib1.h"

#define EXPORT __attribute__((visibility("default")))

EXPORT MyObject::MyObject (QObject * parent) : QObject (parent)
{
}

EXPORT MyObject::~MyObject ()
{
}

EXPORT void MyObject::myMethod ()
{
}

lib2.cc:

#include "lib1.h"

void test (MyObject * object)
{
    object->myMethod ();
}

Build steps:

LIBFLAGS="-fPIC -shared -Wall -Wl,-z,defs"
QTFLAGS="-I/usr/include/qt -I/usr/include/qt/QtCore -lQt5Core"

g++ -fsanitize=undefined -fvisibility=hidden \
 ${QTFLAGS} ${LIBFLAGS} lib1.cc -o lib1.so

g++ -fsanitize=undefined \
 ${QTFLAGS} ${LIBFLAGS} lib1.so lib2.cc -o lib2.so

Build output (from the final step):

/tmp/ccY7PHv4.o:(.data.rel+0x18): undefined reference to `typeinfo for MyObject'
collect2: error: ld returned 1 exit status
John Lindgren
  • 777
  • 5
  • 14

2 Answers2

14

The answer to the actual question as asked is that -fsanitize=undefined is actually a collection of sanitizers including the vptr sanitizer.

https://clang.llvm.org/docs/UndefinedBehaviorSanitizer.html

The vptr sanitizer is clearly marked as requiring RTTI, which other answers have described why it's not available.

To run all the tests except vptr, you can say

-fsanitize=undefined -fno-sanitize=vptr
xaxxon
  • 19,189
  • 5
  • 50
  • 80
2

I think the -fsanitize=undefined is a red herring.

You are only exporting the member functions of that class. In order to also export its metadata (like its typeinfo and potential v-table pointer) you need to export the class.

Try this

class EXPORT MyObject : public QObject
{
public:
    MyObject (QObject * parent = nullptr);
    ~MyObject ();

    void myMethod ();
};

Then you should not need to mark up the individual member functions.

Arvid
  • 10,915
  • 1
  • 32
  • 40
  • ... which means a little more trouble, since the EXPORT now has to be defined in the public header, and needs to switch between dllimport and dllexport for the Windows builds. But I've been doing some more reading, and this seems to be the recommended way to do it. – John Lindgren May 26 '16 at 02:19
  • I believe you would need that regardless on windows, as it decorate symbol names. see http://stackoverflow.com/questions/3704374/linking-error-lnk2019-in-msvc-unresolved-symbols-with-imp-prefix-but-shoul – Arvid May 26 '16 at 17:45
  • Define "need". :) Correct or not, we've been building this application on Windows for years without any sort of dllimport/dllexport in the public headers. Marking individual function implementations, as in the example, was enough to make everything compile and run happily. – John Lindgren May 30 '16 at 03:07
  • msvc has a much longer tradition of not exporting everything by default (i.e. requiring dllexport) and probably worked out all these edge cases in the late 90's. Perhaps to automaticall export class metadata if you export some parts of it. that's just my assumption though. – Arvid May 30 '16 at 17:31
  • @Arvid How can it be a red herring if it links fine without it? I'm running into the exact same problem with code (third party compiled without RTTI) that compiles fine without ubsan but gives this error with ubsan. – xaxxon Jan 18 '18 at 12:52
  • @Arvid found the real answer and posted it - sanitize=undefined includes the vptr sanitizer which causes a dependency on the RTTI data. – xaxxon Jan 18 '18 at 13:01