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

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 "deadcodefinder.h"
0019 #include <elf/elffileset.h>
0020 #include <elf/elfsymboltablesection.h>
0021 #include <elf/elfhashsection.h>
0022 #include <elf/elfheader.h>
0023 
0024 #include <demangle/demangler.h>
0025 
0026 #include <elf.h>
0027 
0028 #include <iostream>
0029 
0030 DeadCodeFinder::DeadCodeFinder() = default;
0031 DeadCodeFinder::DeadCodeFinder(const DeadCodeFinder&) = default;
0032 DeadCodeFinder::~DeadCodeFinder() = default;
0033 DeadCodeFinder& DeadCodeFinder::operator=(const DeadCodeFinder&) = default;
0034 
0035 void DeadCodeFinder::findUnusedSymbols(ElfFileSet* fileSet)
0036 {
0037     m_fileSet = fileSet;
0038 
0039     for (int i = 0; i < m_fileSet->size(); ++i) {
0040         scanUsage(m_fileSet->file(i));
0041     }
0042 }
0043 
0044 void DeadCodeFinder::scanUsage(ElfFile* file)
0045 {
0046     std::cerr << "Scanning " << qPrintable(file->displayName()) << "..." << std::endl;
0047     for (int i = 0; i < m_fileSet->size(); ++i) {
0048         auto otherFile = m_fileSet->file(i);
0049         auto symTab = file->hash()->linkedSection<ElfSymbolTableSection>();
0050         if (!symTab)
0051             continue;
0052         for (uint j = 0; j < symTab->header()->entryCount(); ++j) {
0053             auto localSym = symTab->entry(j);
0054             if (localSym->size() > 0)
0055                 continue;
0056             auto sym = otherFile->hash()->lookup(localSym->name());
0057             if (!sym)
0058                 continue;
0059             m_usedSymbols[otherFile].insert(sym);
0060         }
0061     }
0062 }
0063 
0064 void DeadCodeFinder::setExcludePrefixes(const QStringList& excludePrefixes)
0065 {
0066     m_excludePrefixes = excludePrefixes;
0067 }
0068 
0069 void DeadCodeFinder::dumpResults()
0070 {
0071     for (int i = 0; i < m_fileSet->size(); ++i) {
0072         auto file = m_fileSet->file(i);
0073         // this only makes sense for libraries
0074         if (file->header()->type() == ET_EXEC)
0075             continue;
0076 
0077         bool skip = false;
0078         foreach (const auto &excludePrefix, m_excludePrefixes) {
0079             if (file->fileName().startsWith(excludePrefix)) {
0080                 skip = true;
0081                 break;
0082             }
0083         }
0084         if (skip)
0085             continue;
0086 
0087         std::cout << "Unreferenced exported symbols in " << qPrintable(file->displayName()) << ":" << std::endl;
0088         dumpResultsForFile(file);
0089         std::cout << std::endl;
0090     }
0091 }
0092 
0093 void DeadCodeFinder::dumpResultsForFile(ElfFile* file)
0094 {
0095     const auto usedSyms = m_usedSymbols.value(file);
0096     const auto symTab = file->hash()->linkedSection<ElfSymbolTableSection>();
0097     if (!symTab)
0098         return;
0099 
0100     QVector<QByteArray> unusedSyms;
0101     for (uint i = 0; i < symTab->header()->entryCount(); ++i) {
0102         auto sym = symTab->entry(i);
0103         if (sym->size() == 0 || sym->bindType() != STB_GLOBAL || sym->visibility() != STV_DEFAULT)
0104             continue;
0105         if (usedSyms.contains(sym))
0106             continue;
0107         unusedSyms.push_back(Demangler::demangleFull(sym->name()).constData());
0108     }
0109 
0110     std::sort(unusedSyms.begin(), unusedSyms.end());
0111     std::for_each(unusedSyms.constBegin(), unusedSyms.constEnd(), [](const QByteArray& sym) { std::cout << sym.constData() << std::endl; });
0112 }