1 /* 2 Copyright (C) 2015 Volker Krause <vkrause@kde.org> 3 4 This program is free software; you can redistribute it and/or modify it 5 under the terms of the GNU Library General Public License as published by 6 the Free Software Foundation; either version 2 of the License, or (at your 7 option) any later version. 8 9 This program is distributed in the hope that it will be useful, but WITHOUT 10 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 11 FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public 12 License for more details. 13 14 You should have received a copy of the GNU General Public License 15 along with this program. If not, see <https://www.gnu.org/licenses/>. 16 */ 17 18 #include <elf/elffile.h> 19 #include <elf/elffileset.h> 20 #include <elf/elfsymboltablesection.h> 21 #include <elf/elfgnusymbolversiontable.h> 22 #include <elf/elfgnusymbolversiondefinitionssection.h> 23 #include <elf/elfgnusymbolversiondefinition.h> 24 #include <elf/elfgnusymbolversionrequirementssection.h> 25 #include <elf/elfgnusymbolversionrequirement.h> 26 #include <elf/elfgnusymbolversiondefinitionauxiliaryentry.h> 27 28 #include <QDebug> 29 #include <QtTest/qtest.h> 30 #include <QObject> 31 32 #include <elf.h> 33 34 class ElfGNUSymbolVersioningTest: public QObject 35 { 36 Q_OBJECT 37 private slots: testSymbolVersioning()38 void testSymbolVersioning() 39 { 40 ElfFileSet set; 41 set.addFile(QStringLiteral(BINDIR "elf-dissector")); 42 QVERIFY(set.size() > 1); 43 44 // we need a full library for this, not just an executable 45 ElfFile *f = nullptr; 46 for (int i = 0; i < set.size(); ++i) { 47 if (set.file(i)->dynamicSection()->soName() == "libQt5Core.so.5") 48 f = set.file(i); 49 } 50 QVERIFY(f); 51 52 const auto symVerIndex = f->indexOfSection(".gnu.version"); 53 QVERIFY(symVerIndex > 0); 54 QCOMPARE(symVerIndex, f->indexOfSection(SHT_GNU_versym)); 55 const auto symbolVersionTable = f->section<ElfGNUSymbolVersionTable>(symVerIndex); 56 QVERIFY(symbolVersionTable); 57 QCOMPARE(symbolVersionTable->header()->entryCount(), f->section<ElfSymbolTableSection>(f->indexOfSection(".dynsym"))->header()->entryCount()); 58 59 const auto symDefIndex = f->indexOfSection(".gnu.version_d"); 60 QVERIFY(symDefIndex > 0); 61 QCOMPARE(symDefIndex, f->indexOfSection(SHT_GNU_verdef)); 62 const auto symbolVersionDefs = f->section<ElfGNUSymbolVersionDefinitionsSection>(symDefIndex); 63 QVERIFY(symbolVersionDefs); 64 65 QCOMPARE(f->dynamicSection()->entryWithTag(DT_VERDEFNUM)->value(), (uint64_t)symbolVersionDefs->entryCount()); 66 QVERIFY(symbolVersionDefs->entryCount() > 0); 67 68 const auto verDef = symbolVersionDefs->definition(0); 69 QVERIFY(verDef); 70 QVERIFY(verDef->auxiliarySize() > 0); 71 72 const auto verDefAux = verDef->auxiliaryEntry(0); 73 QVERIFY(verDefAux); 74 75 const auto symNeedIndex = f->indexOfSection(".gnu.version_r"); 76 QVERIFY(symNeedIndex > 0); 77 QCOMPARE(symNeedIndex, f->indexOfSection(SHT_GNU_verneed)); 78 const auto symbolVersionNeeds = f->section<ElfGNUSymbolVersionRequirementsSection>(symNeedIndex); 79 QVERIFY(symbolVersionNeeds); 80 81 QCOMPARE(f->dynamicSection()->entryWithTag(DT_VERNEEDNUM)->value(), (uint64_t)symbolVersionNeeds->entryCount()); 82 QVERIFY(symbolVersionNeeds->entryCount() > 0); 83 84 const auto verNeed = symbolVersionNeeds->requirement(0); 85 QVERIFY(verNeed); 86 QVERIFY(verNeed->auxiliarySize() > 0); 87 } 88 testSymbolVersionDefinitions()89 void testSymbolVersionDefinitions() 90 { 91 ElfFileSet set; 92 set.addFile(QStringLiteral(BINDIR "libversioned-symbols.so")); 93 QVERIFY(set.size() > 1); 94 95 auto f = set.file(0); 96 QVERIFY(f); 97 98 const auto symDefIndex = f->indexOfSection(".gnu.version_d"); 99 const auto symbolVersionDefs = f->section<ElfGNUSymbolVersionDefinitionsSection>(symDefIndex); 100 QVERIFY(symbolVersionDefs); 101 QCOMPARE(symbolVersionDefs->entryCount(), 3u); 102 103 ElfGNUSymbolVersionDefinition *defV1 = nullptr, *defV2 = nullptr; 104 auto def = symbolVersionDefs->definition(1); 105 QVERIFY(def); 106 QCOMPARE(def->versionIndex(), (uint16_t)2); 107 QCOMPARE(symbolVersionDefs->definitionForVersionIndex(2), def); 108 #ifdef Q_OS_FREEBSD 109 // Both entries have auxiliarySize == 1 110 QCOMPARE(def->auxiliarySize(), 1u); 111 QCOMPARE(def->auxiliaryEntry(0)->name(), "VER1"); 112 defV1 = def; 113 #else 114 if (def->auxiliarySize() == 1) 115 defV1 = def; 116 else 117 defV2 = def; 118 #endif 119 120 def = symbolVersionDefs->definition(2); 121 QVERIFY(def); 122 QCOMPARE(def->versionIndex(), (uint16_t)3); 123 QCOMPARE(symbolVersionDefs->definitionForVersionIndex(3), def); 124 #ifdef Q_OS_FREEBSD 125 // Both entries have auxiliarySize == 1 126 QCOMPARE(def->auxiliarySize(), 1u); 127 QCOMPARE(def->auxiliaryEntry(0)->name(), "VER2"); 128 defV2 = def; 129 #else 130 if (def->auxiliarySize() == 1) 131 defV1 = def; 132 else 133 defV2 = def; 134 #endif 135 QVERIFY(defV1); 136 QVERIFY(defV2); 137 138 #ifdef Q_OS_FREEBSD 139 QEXPECT_FAIL("", "FreeBSD only 1 auxiliary", Continue); 140 #endif 141 QCOMPARE(defV2->auxiliarySize(), (uint16_t)2); 142 auto defEntry = defV2->auxiliaryEntry(0); 143 QVERIFY(defEntry); 144 QCOMPARE(defEntry->name(), "VER2"); 145 if (defV2->auxiliarySize() > 1) 146 { 147 defEntry = defV2->auxiliaryEntry(1); 148 QVERIFY(defEntry); 149 QCOMPARE(defEntry->name(), "VER1"); 150 } 151 152 QCOMPARE(defV1->auxiliarySize(), (uint16_t)1); 153 defEntry = defV1->auxiliaryEntry(0); 154 QVERIFY(defEntry); 155 QCOMPARE(defEntry->name(), "VER1"); 156 157 const auto symVerIndex = f->indexOfSection(".gnu.version"); 158 const auto symbolVersionTable = f->section<ElfGNUSymbolVersionTable>(symVerIndex); 159 QVERIFY(symbolVersionTable); 160 161 const auto dynTabIndex = f->indexOfSection(".dynsym"); 162 QVERIFY(dynTabIndex > 0); 163 const auto symTab = f->section<ElfSymbolTableSection>(dynTabIndex); 164 QVERIFY(symTab); 165 QCOMPARE(symTab->header()->entryCount(), symbolVersionTable->header()->entryCount()); 166 167 ElfSymbolTableEntry *f1 = nullptr, *f2 = nullptr, *f_ver1 = nullptr, *f_ver2 = nullptr; 168 for (uint i = 0; i < symTab->header()->entryCount(); ++i) { 169 auto sym = symTab->entry(i); 170 QVERIFY(sym); 171 if (strcmp(sym->name(), "function1") == 0) 172 f1 = sym; 173 else if (strcmp(sym->name(), "function2") == 0) 174 f2 = sym; 175 else if (strcmp(sym->name(), "function") == 0) { 176 qDebug() << symbolVersionTable->versionIndex(i); 177 if (symbolVersionTable->versionIndex(i) == defV2->versionIndex()) 178 f_ver2 = sym; 179 else if (symbolVersionTable->versionIndex(i) == defV1->versionIndex()) 180 f_ver1 = sym; 181 } 182 } 183 184 QVERIFY(f1); 185 QVERIFY(f2); 186 QVERIFY(f_ver1); 187 QVERIFY(f_ver2); 188 QCOMPARE(f1->value(), f_ver1->value()); 189 QCOMPARE(f2->value(), f_ver2->value()); 190 } 191 }; 192 193 QTEST_MAIN(ElfGNUSymbolVersioningTest) 194 195 #include "elfgnusymbolversioningtest.moc" 196