File indexing completed on 2024-05-12 05:43:30

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 "dependencysorter.h"
0019 #include <checks/dependenciescheck.h>
0020 #include <elf/elffileset.h>
0021 
0022 #include <QDebug>
0023 #include <QHash>
0024 
0025 #include <cassert>
0026 #include <elf.h>
0027 
0028 void DependencySorter::sortDtNeeded(ElfFileSet* fileSet)
0029 {
0030     assert(fileSet->size() > 0);
0031 
0032     auto file = fileSet->file(0);
0033     if (!file->dynamicSection())
0034         return;
0035 
0036     // TODO index SO_NAME, this probably should be moved to ElfFileSet, we have that in a bunch of places now
0037     QHash<QByteArray, int> nameIndex;
0038     for (int i = 0; i < fileSet->size(); ++i) {
0039         const auto f = fileSet->file(i);
0040         if (!f->dynamicSection())
0041             continue;
0042         const auto soName = f->dynamicSection()->soName();
0043         if (nameIndex.contains(soName)) {
0044             qWarning() << "Suspicious DT_NEEDED entry '" << soName << "' in " << f->fileName() << ", aborting.";
0045             return;
0046         }
0047 
0048         if (!soName.isEmpty())
0049             nameIndex.insert(soName, i);
0050     }
0051 
0052     // count usages
0053     QVector<int> usageCounts;
0054     const auto needed = file->dynamicSection()->neededLibraries();
0055     usageCounts.resize(needed.size());
0056     for (int i = 0; i < needed.size(); ++i) {
0057         auto depFile = fileSet->file(nameIndex.value(needed.at(i)));
0058         assert(depFile);
0059         assert(file != depFile);
0060 
0061         usageCounts[i] = DependenciesCheck::usedSymbolCount(file, depFile);
0062     }
0063     qDebug() << usageCounts;
0064 
0065     // sort, check if change is needed
0066     QVector<int> sortedNeededIndex;
0067     sortedNeededIndex.resize(needed.size());
0068     std::iota(sortedNeededIndex.begin(), sortedNeededIndex.end(), 0);
0069     assert(sortedNeededIndex.size() == usageCounts.size());
0070     qDebug() << sortedNeededIndex;
0071 
0072     std::sort(sortedNeededIndex.begin(), sortedNeededIndex.end(), [usageCounts](int lhs, int rhs) {
0073         return usageCounts.at(lhs) > usageCounts.at(rhs);
0074     });
0075 
0076     qDebug() << sortedNeededIndex;
0077 
0078     // since we modify the file in-place, get the necessary string table values before we do that
0079     QVector<uint64_t> neededValues;
0080     neededValues.resize(needed.size());
0081     int neededIndex = 0;
0082     for (uint i = 0; i < file->dynamicSection()->header()->entryCount(); ++i) {
0083         auto dynEntry = file->dynamicSection()->entry(i);
0084         if (dynEntry->tag() != DT_NEEDED)
0085             continue;
0086         neededValues[neededIndex++] = dynEntry->value();
0087     }
0088 
0089     // open target file
0090     ElfFile newFile(file->fileName());
0091     if (!newFile.open(QFile::ReadWrite) || !newFile.isValid()) {
0092         qWarning() << "Can't open" << file->fileName() << "for writing.";
0093         return;
0094     }
0095 
0096     // write change
0097     neededIndex = 0;
0098     auto newDynSection = newFile.dynamicSection();
0099     for (uint i = 0; i < newDynSection->header()->entryCount(); ++i) {
0100         auto dynEntry = newDynSection->entry(i);
0101         if (dynEntry->tag() != DT_NEEDED)
0102             continue;
0103 
0104         dynEntry->setValue(neededValues.at(sortedNeededIndex.at(neededIndex)));
0105 
0106         qDebug() << needed.at(sortedNeededIndex.at(neededIndex));
0107         ++neededIndex;
0108     }
0109 
0110 }