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