summaryrefslogtreecommitdiffstats
path: root/tests/auto/symbols
diff options
context:
space:
mode:
authorLars Knoll <lars.knoll@nokia.com>2009-03-23 09:18:55 (GMT)
committerSimon Hausmann <simon.hausmann@nokia.com>2009-03-23 09:18:55 (GMT)
commite5fcad302d86d316390c6b0f62759a067313e8a9 (patch)
treec2afbf6f1066b6ce261f14341cf6d310e5595bc1 /tests/auto/symbols
downloadQt-e5fcad302d86d316390c6b0f62759a067313e8a9.zip
Qt-e5fcad302d86d316390c6b0f62759a067313e8a9.tar.gz
Qt-e5fcad302d86d316390c6b0f62759a067313e8a9.tar.bz2
Long live Qt 4.5!
Diffstat (limited to 'tests/auto/symbols')
-rw-r--r--tests/auto/symbols/.gitignore1
-rw-r--r--tests/auto/symbols/symbols.pro7
-rw-r--r--tests/auto/symbols/tst_symbols.cpp433
3 files changed, 441 insertions, 0 deletions
diff --git a/tests/auto/symbols/.gitignore b/tests/auto/symbols/.gitignore
new file mode 100644
index 0000000..c9d6e70
--- /dev/null
+++ b/tests/auto/symbols/.gitignore
@@ -0,0 +1 @@
+tst_symbols
diff --git a/tests/auto/symbols/symbols.pro b/tests/auto/symbols/symbols.pro
new file mode 100644
index 0000000..2b772f4
--- /dev/null
+++ b/tests/auto/symbols/symbols.pro
@@ -0,0 +1,7 @@
+load(qttest_p4)
+
+cross_compile: DEFINES += QT_CROSS_COMPILED
+SOURCES += tst_symbols.cpp
+QT = core
+
+
diff --git a/tests/auto/symbols/tst_symbols.cpp b/tests/auto/symbols/tst_symbols.cpp
new file mode 100644
index 0000000..fb148a9
--- /dev/null
+++ b/tests/auto/symbols/tst_symbols.cpp
@@ -0,0 +1,433 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the test suite of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain
+** additional rights. These rights are described in the Nokia Qt LGPL
+** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+
+#include <QtCore/QtCore>
+#include <QtTest/QtTest>
+
+#ifdef QT_NAMESPACE
+#define STRINGIFY_HELPER(s) #s
+#define STRINGIFY(s) STRINGIFY_HELPER(s)
+QString ns = STRINGIFY(QT_NAMESPACE) + QString("::");
+#else
+QString ns;
+#endif
+
+class tst_Symbols: public QObject
+{
+ Q_OBJECT
+private slots:
+ void prefix();
+ void globalObjects();
+};
+
+/* Computes the line number from a symbol */
+static QString symbolToLine(const QString &symbol, const QString &lib)
+{
+ // nm outputs the symbol name, the type, the address and the size
+ QRegExp re("global constructors keyed to ([a-zA-Z_0-9.]*) (.) ([0-f]*) ([0-f]*)");
+ if (re.indexIn(symbol) == -1)
+ return QString();
+
+ // address and symbolSize are in hex. Convert to integers
+ bool ok;
+ int symbolAddress = re.cap(3).toInt(&ok, 16);
+ if (!ok)
+ return QString();
+ int symbolSize = re.cap(4).toInt(&ok, 16);
+ if (!ok)
+ return QString();
+
+ // now, find the start address, which is the address - size
+ QString startAddress = QString::number(symbolAddress - symbolSize, 16);
+
+ QProcess proc;
+ proc.start("addr2line", QStringList() << "-e" << lib << startAddress);
+ if (!proc.waitForFinished())
+ return QString();
+
+ QString result = QString::fromLocal8Bit(proc.readLine());
+ result.chop(1); // chop tailing newline
+ return result;
+}
+
+/* This test searches through all Qt libraries and searches for symbols
+ starting with "global constructors keyed to "
+
+ These indicate static global objects, which should not be used in shared
+ libraries - use Q_GLOBAL_STATIC instead.
+*/
+void tst_Symbols::globalObjects()
+{
+#ifndef Q_OS_LINUX
+ QSKIP("Linux-specific test", SkipAll);
+#endif
+
+ // these are regexps for global objects that are allowed in Qt
+ QStringList whitelist = QStringList()
+ // ignore qInitResources - they are safe to use
+ << "^_Z[0-9]*qInitResources_"
+ << "qrc_.*\\.cpp"
+ // ignore qRegisterGuiVariant - it's a safe fallback to register GUI Variants
+ << "qRegisterGuiVariant";
+
+ bool isFailed = false;
+
+ QDir dir(qgetenv("QTDIR") + "/lib", "*.so");
+ QStringList files = dir.entryList();
+ QVERIFY(!files.isEmpty());
+
+ foreach (QString lib, files) {
+
+ QProcess proc;
+ proc.start("nm",
+ QStringList() << "-C" << "--format=posix"
+ << dir.absolutePath() + "/" + lib);
+ QVERIFY(proc.waitForFinished());
+ QCOMPARE(proc.exitCode(), 0);
+ QCOMPARE(QString::fromLocal8Bit(proc.readAllStandardError()), QString());
+
+ QStringList symbols = QString::fromLocal8Bit(proc.readAll()).split("\n");
+ QVERIFY(!symbols.isEmpty());
+ foreach (QString symbol, symbols) {
+ if (symbol.isEmpty())
+ continue;
+
+ if (!symbol.startsWith("global constructors keyed to "))
+ continue;
+
+ QRegExp re("global constructors keyed to ([a-zA-Z_0-9.]*)");
+ QVERIFY(re.indexIn(symbol) != -1);
+
+ QString cap = re.cap(1);
+
+ bool whitelisted = false;
+ foreach (QString white, whitelist) {
+ if (cap.indexOf(QRegExp(white)) != -1) {
+ whitelisted = true;
+ break;
+ }
+ }
+ if (whitelisted)
+ continue;
+
+ QString line = symbolToLine(symbol, dir.absolutePath() + "/" + lib);
+
+ if (cap.contains('.'))
+ QWARN(qPrintable("Static global object(s) found in " + lib + " in file " + cap + " (" + line + ")"));
+ else
+ QWARN(qPrintable("Static global object found in " + lib + " near symbol " + cap + " (" + line + ")"));
+
+ isFailed = true;
+ }
+ }
+
+ if (isFailed) {
+#if QT_VERSION >= 0x040600
+ QVERIFY2(!isFailed, "Libraries contain static global objects. See Debug output above.");
+#else
+ QSKIP("Libraries contains static global objects. See Debug output above. [These errors cannot be fixed in 4.5 in time]", SkipSingle);
+#endif
+ }
+}
+
+void tst_Symbols::prefix()
+{
+#if defined(QT_CROSS_COMPILED)
+ QSKIP("Probably no compiler on the target", SkipAll);
+#elif defined(Q_OS_LINUX)
+ QStringList qtTypes;
+ qtTypes << "QString" << "QChar" << "QWidget" << "QObject" << "QVariant" << "QList"
+ << "QMap" << "QHash" << "QVector" << "QRect" << "QSize" << "QPoint"
+ << "QTextFormat" << "QTextLength" << "QPen" << "QFont" << "QIcon"
+ << "QPixmap" << "QImage" << "QRegion" << "QPolygon";
+ QStringList qAlgorithmFunctions;
+ qAlgorithmFunctions << "qBinaryFind" << "qLowerBound" << "qUpperBound"
+ << "qAbs" << "qMin" << "qMax" << "qBound" << "qSwap"
+ << "qHash" << "qDeleteAll" << "qCopy" << "qSort";
+
+ QStringList exceptionalSymbols;
+ exceptionalSymbols << "XRectangle::~XRectangle()"
+ << "XChar2b::~XChar2b()"
+ << "XPoint::~XPoint()"
+ << "glyph_metrics_t::"; // #### Qt 4.2
+
+ QStringList stupidCSymbols;
+ stupidCSymbols << "Add_Glyph_Property"
+ << "Check_Property"
+ << "Coverage_Index"
+ << "Get_Class"
+ << "Get_Device"
+ << "rcsid3"
+ << "sfnt_module_class"
+ << "t1cid_driver_class"
+ << "t42_driver_class"
+ << "winfnt_driver_class"
+ << "pshinter_module_class"
+ << "psnames_module_class"
+ ;
+
+ QHash<QString,QStringList> excusedPrefixes;
+ excusedPrefixes[QString()] =
+ QStringList() << "Ui_Q"; // uic generated, like Ui_QPrintDialog
+
+ excusedPrefixes["QtCore"] =
+ QStringList() << "hb_"
+ << "HB_"
+ // zlib symbols, for -qt-zlib ;(
+ << "deflate"
+ << "compress"
+ << "uncompress"
+ << "adler32"
+ << "gz"
+ << "inflate"
+ << "zlib"
+ << "zError"
+ << "get_crc_table"
+ << "crc32";
+
+ excusedPrefixes["QtGui"] =
+ QStringList() << "ftglue_"
+ << "Load_"
+ << "otl_"
+ << "TT_"
+ << "tt_"
+ << "t1_"
+ << "Free_"
+ << "FT_"
+ << "FTC_"
+ << "ft_"
+ << "ftc_"
+ << "af_autofitter"
+ << "af_dummy"
+ << "af_latin"
+ << "autofit_"
+ << "XPanorami"
+ << "Xinerama"
+ << "bdf_"
+ << "ccf_"
+ << "gray_raster"
+ << "pcf_"
+ << "cff_"
+ << "otv_"
+ << "pfr_"
+ << "ps_"
+ << "psaux"
+ << "png_";
+
+ excusedPrefixes["QtSql"] =
+ QStringList() << "sqlite3";
+
+ excusedPrefixes["QtWebKit"] =
+ QStringList() << "WebCore::"
+ << "KJS::"
+ << "kjs"
+ << "kJS"
+ << "JS"
+// << "OpaqueJS"
+ << "WTF"
+ << "wtf_"
+ << "SVG::"
+ << "NPN_"
+ << "cti" // ctiTrampoline and ctiVMThrowTrampoline from the JIT
+#ifdef QT_NAMESPACE
+ << "QWeb" // Webkit is only 'namespace aware'
+#endif
+ ;
+
+ excusedPrefixes["phonon"] =
+ QStringList() << ns + "Phonon";
+
+ QDir dir(qgetenv("QTDIR") + "/lib", "*.so");
+ QStringList files = dir.entryList();
+ QVERIFY(!files.isEmpty());
+
+ bool isFailed = false;
+ foreach (QString lib, files) {
+ if (lib.contains("Designer") || lib.contains("QtCLucene") || lib.contains("XmlPatternsSDK"))
+ continue;
+
+ bool isPhonon = lib.contains("phonon");
+
+ QProcess proc;
+ proc.start("nm",
+ QStringList() << "-g" << "-C" << "-D" << "--format=posix"
+ << "--defined-only" << dir.absolutePath() + "/" + lib);
+ QVERIFY(proc.waitForFinished());
+ QCOMPARE(proc.exitCode(), 0);
+ QCOMPARE(QString::fromLocal8Bit(proc.readAllStandardError()), QString());
+
+ QStringList symbols = QString::fromLocal8Bit(proc.readAll()).split("\n");
+ QVERIFY(!symbols.isEmpty());
+ foreach (QString symbol, symbols) {
+ if (symbol.isEmpty())
+ continue;
+
+ if (symbol.startsWith("unsigned "))
+ // strip modifiers
+ symbol = symbol.mid(symbol.indexOf(' ') + 1);
+ if (symbol.startsWith("long long ")) {
+ symbol = symbol.mid(10);
+ } else if (symbol.startsWith("bool ") || symbol.startsWith("bool* ")
+ || symbol.startsWith("char ") || symbol.startsWith("char* ")
+ || symbol.startsWith("int ") || symbol.startsWith("int* ") || symbol.startsWith("int*&")
+ || symbol.startsWith("short") || symbol.startsWith("long ")
+ || symbol.startsWith("void ") || symbol.startsWith("void* ")
+ || symbol.startsWith("double ") || symbol.startsWith("double* ")
+ || symbol.startsWith("float ") || symbol.startsWith("float* ")) {
+ // partial templates have the return type in their demangled name, strip it
+ symbol = symbol.mid(symbol.indexOf(' ') + 1);
+ }
+ if (symbol.startsWith("const ") || symbol.startsWith("const* ") ||
+ symbol.startsWith("const& ")) {
+ // strip modifiers
+ symbol = symbol.mid(symbol.indexOf(' ') + 1);
+ }
+
+ if (symbol.startsWith("_") || symbol.startsWith("std::"))
+ continue;
+ if (symbol.startsWith("vtable ") || symbol.startsWith("VTT for ") ||
+ symbol.startsWith("construction vtable for"))
+ continue;
+ if (symbol.startsWith("typeinfo "))
+ continue;
+ if (symbol.startsWith("non-virtual thunk ") || symbol.startsWith("virtual thunk"))
+ continue;
+ if (symbol.startsWith(ns + "operator"))
+ continue;
+ if (symbol.startsWith("guard variable for "))
+ continue;
+ if (symbol.contains("(" + ns + "QTextStream"))
+ // QTextStream is excused.
+ continue;
+ if (symbol.contains("(" + ns + "Q3TextStream"))
+ // Q3TextStream is excused.
+ continue;
+ if (symbol.startsWith(ns + "bitBlt") || symbol.startsWith(ns + "copyBlt"))
+ // you're excused, too
+ continue;
+
+ bool symbolOk = false;
+
+ QHash<QString,QStringList>::ConstIterator it = excusedPrefixes.constBegin();
+ for ( ; it != excusedPrefixes.constEnd(); ++it) {
+ if (!lib.contains(it.key()))
+ continue;
+ foreach (QString prefix, it.value())
+ if (symbol.startsWith(prefix)) {
+ symbolOk = true;
+ break;
+ }
+ }
+
+ if (symbolOk)
+ continue;
+
+ foreach (QString cSymbolPattern, stupidCSymbols)
+ if (symbol.contains(cSymbolPattern)) {
+ symbolOk = true;
+ break;
+ }
+
+ if (symbolOk)
+ continue;
+
+ QStringList fields = symbol.split(' ');
+ // the last two fields are address and size and the third last field is the symbol type
+ QVERIFY(fields.count() > 3);
+ QString type = fields.at(fields.count() - 3);
+ // weak symbol
+ if (type == QLatin1String("W")) {
+ if (symbol.contains("qAtomic"))
+ continue;
+
+ if (symbol.contains("fstat")
+ || symbol.contains("lstat")
+ || symbol.contains("stat64")
+ )
+ continue;
+
+ foreach (QString acceptedPattern, qAlgorithmFunctions + exceptionalSymbols)
+ if (symbol.contains(acceptedPattern)) {
+ symbolOk = true;
+ break;
+ }
+
+ if (symbolOk)
+ continue;
+
+ QString plainSymbol;
+ for (int i = 0; i < fields.count() - 3; ++i) {
+ if (i > 0)
+ plainSymbol += QLatin1Char(' ');
+ plainSymbol += fields.at(i);
+ }
+ foreach (QString qtType, qtTypes)
+ if (plainSymbol.contains(qtType)) {
+ symbolOk = true;
+ break;
+ }
+
+ if (symbolOk)
+ continue;
+ }
+
+ QString prefix = ns + "q";
+ if (!symbol.startsWith(prefix, Qt::CaseInsensitive)
+ && !(isPhonon && symbol.startsWith("Phonon"))) {
+ qDebug("symbol in '%s' does not start with prefix '%s': '%s'",
+ qPrintable(lib), qPrintable(prefix), qPrintable(symbol));
+ isFailed = true;
+ }
+ }
+ }
+
+# if defined(Q_OS_LINUX) && defined(Q_CC_INTEL)
+ QEXPECT_FAIL("", "linux-icc* incorrectly exports some QtWebkit symbols, waiting for a fix from Intel.", Continue);
+# endif
+ QVERIFY2(!isFailed, "Libraries contain non-prefixed symbols. See Debug output :)");
+#else
+ QSKIP("Linux-specific test", SkipAll);
+#endif
+}
+
+QTEST_MAIN(tst_Symbols)
+#include "tst_symbols.moc"