File indexing completed on 2024-04-28 05:41:06

0001 /*
0002     Copyright (C) 2015 Volker Krause <vkrause@kde.org>
0003 
0004     This program is free software; you can redistribute it and/or modify it
0005     under the terms of the GNU Library General Public License as published by
0006     the Free Software Foundation; either version 2 of the License, or (at your
0007     option) any later version.
0008 
0009     This program is distributed in the hope that it will be useful, but WITHOUT
0010     ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
0011     FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Library General Public
0012     License for more details.
0013 
0014     You should have received a copy of the GNU General Public License
0015     along with this program.  If not, see <https://www.gnu.org/licenses/>.
0016 */
0017 
0018 #include <elf/elffile.h>
0019 #include <elf/elffileset.h>
0020 #include <elf/elfsymboltablesection.h>
0021 #include <elf/elfgnusymbolversiontable.h>
0022 #include <elf/elfgnusymbolversiondefinitionssection.h>
0023 #include <elf/elfgnusymbolversiondefinition.h>
0024 #include <elf/elfgnusymbolversionrequirementssection.h>
0025 #include <elf/elfgnusymbolversionrequirement.h>
0026 #include <elf/elfgnusymbolversiondefinitionauxiliaryentry.h>
0027 
0028 #include <QDebug>
0029 #include <QtTest/qtest.h>
0030 #include <QObject>
0031 
0032 #include <elf.h>
0033 
0034 class ElfGNUSymbolVersioningTest: public QObject
0035 {
0036     Q_OBJECT
0037 private slots:
0038     void testSymbolVersioning()
0039     {
0040         ElfFileSet set;
0041         set.addFile(QStringLiteral(BINDIR "elf-dissector"));
0042         QVERIFY(set.size() > 1);
0043 
0044         // we need a full library for this, not just an executable
0045         ElfFile *f = nullptr;
0046         for (int i = 0; i < set.size(); ++i) {
0047             if (set.file(i)->dynamicSection()->soName() == "libQt5Core.so.5")
0048                 f = set.file(i);
0049         }
0050         QVERIFY(f);
0051 
0052         const auto symVerIndex = f->indexOfSection(".gnu.version");
0053         QVERIFY(symVerIndex > 0);
0054         QCOMPARE(symVerIndex, f->indexOfSection(SHT_GNU_versym));
0055         const auto symbolVersionTable = f->section<ElfGNUSymbolVersionTable>(symVerIndex);
0056         QVERIFY(symbolVersionTable);
0057         QCOMPARE(symbolVersionTable->header()->entryCount(), f->section<ElfSymbolTableSection>(f->indexOfSection(".dynsym"))->header()->entryCount());
0058 
0059         const auto symDefIndex = f->indexOfSection(".gnu.version_d");
0060         QVERIFY(symDefIndex > 0);
0061         QCOMPARE(symDefIndex, f->indexOfSection(SHT_GNU_verdef));
0062         const auto symbolVersionDefs = f->section<ElfGNUSymbolVersionDefinitionsSection>(symDefIndex);
0063         QVERIFY(symbolVersionDefs);
0064 
0065         QCOMPARE(f->dynamicSection()->entryWithTag(DT_VERDEFNUM)->value(), (uint64_t)symbolVersionDefs->entryCount());
0066         QVERIFY(symbolVersionDefs->entryCount() > 0);
0067 
0068         const auto verDef = symbolVersionDefs->definition(0);
0069         QVERIFY(verDef);
0070         QVERIFY(verDef->auxiliarySize() > 0);
0071 
0072         const auto verDefAux = verDef->auxiliaryEntry(0);
0073         QVERIFY(verDefAux);
0074 
0075         const auto symNeedIndex = f->indexOfSection(".gnu.version_r");
0076         QVERIFY(symNeedIndex > 0);
0077         QCOMPARE(symNeedIndex, f->indexOfSection(SHT_GNU_verneed));
0078         const auto symbolVersionNeeds = f->section<ElfGNUSymbolVersionRequirementsSection>(symNeedIndex);
0079         QVERIFY(symbolVersionNeeds);
0080 
0081         QCOMPARE(f->dynamicSection()->entryWithTag(DT_VERNEEDNUM)->value(), (uint64_t)symbolVersionNeeds->entryCount());
0082         QVERIFY(symbolVersionNeeds->entryCount() > 0);
0083 
0084         const auto verNeed = symbolVersionNeeds->requirement(0);
0085         QVERIFY(verNeed);
0086         QVERIFY(verNeed->auxiliarySize() > 0);
0087     }
0088 
0089     void testSymbolVersionDefinitions()
0090     {
0091         ElfFileSet set;
0092         set.addFile(QStringLiteral(BINDIR "libversioned-symbols.so"));
0093         QVERIFY(set.size() > 1);
0094 
0095         auto f = set.file(0);
0096         QVERIFY(f);
0097 
0098         const auto symDefIndex = f->indexOfSection(".gnu.version_d");
0099         const auto symbolVersionDefs = f->section<ElfGNUSymbolVersionDefinitionsSection>(symDefIndex);
0100         QVERIFY(symbolVersionDefs);
0101         QCOMPARE(symbolVersionDefs->entryCount(), 3u);
0102 
0103         ElfGNUSymbolVersionDefinition *defV1 = nullptr, *defV2 = nullptr;
0104         auto def = symbolVersionDefs->definition(1);
0105         QVERIFY(def);
0106         QCOMPARE(def->versionIndex(), (uint16_t)2);
0107         QCOMPARE(symbolVersionDefs->definitionForVersionIndex(2), def);
0108 #ifdef Q_OS_FREEBSD
0109         // Both entries have auxiliarySize == 1
0110         QCOMPARE(def->auxiliarySize(), 1u);
0111         QCOMPARE(def->auxiliaryEntry(0)->name(), "VER1");
0112         defV1 = def;
0113 #else
0114         if (def->auxiliarySize() == 1)
0115             defV1 = def;
0116         else
0117             defV2 = def;
0118 #endif
0119 
0120         def = symbolVersionDefs->definition(2);
0121         QVERIFY(def);
0122         QCOMPARE(def->versionIndex(), (uint16_t)3);
0123         QCOMPARE(symbolVersionDefs->definitionForVersionIndex(3), def);
0124 #ifdef Q_OS_FREEBSD
0125         // Both entries have auxiliarySize == 1
0126         QCOMPARE(def->auxiliarySize(), 1u);
0127         QCOMPARE(def->auxiliaryEntry(0)->name(), "VER2");
0128         defV2 = def;
0129 #else
0130         if (def->auxiliarySize() == 1)
0131             defV1 = def;
0132         else
0133             defV2 = def;
0134 #endif
0135         QVERIFY(defV1);
0136         QVERIFY(defV2);
0137 
0138 #ifdef Q_OS_FREEBSD
0139         QEXPECT_FAIL("", "FreeBSD only 1 auxiliary", Continue);
0140 #endif
0141         QCOMPARE(defV2->auxiliarySize(), (uint16_t)2);
0142         auto defEntry = defV2->auxiliaryEntry(0);
0143         QVERIFY(defEntry);
0144         QCOMPARE(defEntry->name(), "VER2");
0145         if (defV2->auxiliarySize() > 1)
0146         {
0147             defEntry = defV2->auxiliaryEntry(1);
0148             QVERIFY(defEntry);
0149             QCOMPARE(defEntry->name(), "VER1");
0150         }
0151 
0152         QCOMPARE(defV1->auxiliarySize(), (uint16_t)1);
0153         defEntry = defV1->auxiliaryEntry(0);
0154         QVERIFY(defEntry);
0155         QCOMPARE(defEntry->name(), "VER1");
0156 
0157         const auto symVerIndex = f->indexOfSection(".gnu.version");
0158         const auto symbolVersionTable = f->section<ElfGNUSymbolVersionTable>(symVerIndex);
0159         QVERIFY(symbolVersionTable);
0160 
0161         const auto dynTabIndex = f->indexOfSection(".dynsym");
0162         QVERIFY(dynTabIndex > 0);
0163         const auto symTab = f->section<ElfSymbolTableSection>(dynTabIndex);
0164         QVERIFY(symTab);
0165         QCOMPARE(symTab->header()->entryCount(), symbolVersionTable->header()->entryCount());
0166 
0167         ElfSymbolTableEntry *f1 = nullptr, *f2 = nullptr, *f_ver1 = nullptr, *f_ver2 = nullptr;
0168         for (uint i = 0; i < symTab->header()->entryCount(); ++i) {
0169             auto sym = symTab->entry(i);
0170             QVERIFY(sym);
0171             if (strcmp(sym->name(), "function1") == 0)
0172                 f1 = sym;
0173             else if (strcmp(sym->name(), "function2") == 0)
0174                 f2 = sym;
0175             else if (strcmp(sym->name(), "function") == 0) {
0176                 qDebug() << symbolVersionTable->versionIndex(i);
0177                 if (symbolVersionTable->versionIndex(i) == defV2->versionIndex())
0178                     f_ver2 = sym;
0179                 else if (symbolVersionTable->versionIndex(i) == defV1->versionIndex())
0180                     f_ver1 = sym;
0181             }
0182         }
0183 
0184         QVERIFY(f1);
0185         QVERIFY(f2);
0186         QVERIFY(f_ver1);
0187         QVERIFY(f_ver2);
0188         QCOMPARE(f1->value(), f_ver1->value());
0189         QCOMPARE(f2->value(), f_ver2->value());
0190     }
0191 };
0192 
0193 QTEST_MAIN(ElfGNUSymbolVersioningTest)
0194 
0195 #include "elfgnusymbolversioningtest.moc"