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

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 "elfgnuhashsection.h"
0019 #include "elfsymboltablesection.h"
0020 #include "elffile.h"
0021 
0022 
0023 #include <cassert>
0024 
0025 ElfGnuHashSection::ElfGnuHashSection(ElfFile* file, ElfSectionHeader* shdr):
0026     ElfHashSection(file, shdr)
0027 {
0028     // must be a power of two
0029     assert ((maskWordsCount() & (maskWordsCount() - 1)) == 0);
0030 }
0031 
0032 ElfGnuHashSection::~ElfGnuHashSection() = default;
0033 
0034 uint32_t ElfGnuHashSection::bucketCount() const
0035 {
0036     return *reinterpret_cast<const uint32_t*>(rawData());
0037 }
0038 
0039 uint32_t ElfGnuHashSection::bucket(uint32_t index) const
0040 {
0041     return *(reinterpret_cast<const uint32_t*>(rawData() + maskWordsCount() * file()->addressSize()) + 4 + index);
0042 }
0043 
0044 uint32_t ElfGnuHashSection::chainCount() const
0045 {
0046     const auto symTab = linkedSection<ElfSymbolTableSection>();
0047     assert(symTab);
0048     return symTab->header()->entryCount() - symbolIndex();
0049 }
0050 
0051 uint32_t* ElfGnuHashSection::value(uint32_t index) const
0052 {
0053     return const_cast<uint32_t*>(reinterpret_cast<const uint32_t*>(rawData() + maskWordsCount() * file()->addressSize()) + 4 + bucketCount() + index - symbolIndex());
0054 }
0055 
0056 uint32_t ElfGnuHashSection::symbolIndex() const
0057 {
0058     return *(reinterpret_cast<const uint32_t*>(rawData()) + 1);
0059 }
0060 
0061 uint32_t ElfGnuHashSection::maskWordsCount() const
0062 {
0063     return *(reinterpret_cast<const uint32_t*>(rawData()) + 2);
0064 }
0065 
0066 uint32_t ElfGnuHashSection::shift2() const
0067 {
0068     return *(reinterpret_cast<const uint32_t*>(rawData()) + 3);
0069 }
0070 
0071 uint32_t ElfGnuHashSection::hash(const char* name)
0072 {
0073     uint32_t h = 5381;
0074 
0075     for (unsigned char c = *name; c != '\0'; c = *++name)
0076         h = h * 33 + c;
0077 
0078     return h;
0079 }
0080 
0081 uint64_t ElfGnuHashSection::filterMask(uint32_t index) const
0082 {
0083     if (file()->addressSize() == 8)
0084         return *(reinterpret_cast<const uint64_t*>(rawData()) + 2 + index);
0085     return *(reinterpret_cast<const uint32_t*>(rawData()) + 4 + index);
0086 }
0087 
0088 ElfSymbolTableEntry* ElfGnuHashSection::lookup(const char* name) const
0089 {
0090     auto h1 = hash(name);
0091 
0092     {
0093         const uint32_t h2 = h1 >> shift2();
0094         const uint32_t c = file()->addressSize() * 8;
0095         const uint32_t n = (h1 / c) & (maskWordsCount() - 1);
0096 
0097         const uint32_t hashbit1 = h1 & (c - 1);
0098         const uint32_t hashbit2 = h2 & (c - 1);
0099 
0100         const auto bitmask = filterMask(n);
0101         if (((bitmask >> hashbit1) & (bitmask >> hashbit2) & 1) == 0)
0102             return nullptr;
0103     }
0104 
0105     auto n = bucket(h1 % bucketCount());
0106     if (n == 0)
0107         return nullptr;
0108 
0109     const auto symTab = linkedSection<ElfSymbolTableSection>();
0110     assert(symTab);
0111     auto hashValue = value(n);
0112 
0113     for (h1 &= ~1; true; ++n) {
0114         const auto entry = symTab->entry(n);
0115         const auto h2 = *hashValue++;
0116         if ((h1 == (h2 & ~1)) && strcmp(name, entry->name()) == 0)
0117             return entry;
0118         if (h2 & 1)
0119             break;
0120     }
0121 
0122     return nullptr;
0123 }
0124 
0125 QVector<uint32_t> ElfGnuHashSection::histogram() const
0126 {
0127     QVector<uint32_t> hist;
0128     for (uint i = 0; i < bucketCount(); ++i) {
0129         int count = 0;
0130 
0131         const auto n = bucket(i);
0132         auto hashValue = value(n);
0133 
0134         while (!(*hashValue++ & 1))
0135             ++count;
0136 
0137         hist.resize(std::max(hist.size(), count + 1));
0138         hist[count]++;
0139     }
0140     return hist;
0141 }
0142 
0143 double ElfGnuHashSection::averagePrefixLength() const
0144 {
0145     int sum = 0;
0146     int count = 0;
0147 
0148     const auto symTab = linkedSection<ElfSymbolTableSection>();
0149     assert(symTab);
0150 
0151     for (uint i = 0; i < bucketCount(); ++i) {
0152         auto n1 = bucket(i);
0153         auto hashValue1 = value(n1);
0154         while (!(*hashValue1 & 1)) {
0155             const auto entry1 = symTab->entry(n1);
0156 
0157             auto n2 = n1 + 1;
0158             auto hashValue2 = hashValue1 + 1;
0159             while (!(*hashValue2 & 1)) {
0160                 const auto entry2 = symTab->entry(n2);
0161                 sum += commonPrefixLength(entry1->name(), entry2->name());
0162                 ++count;
0163                 ++hashValue2;
0164             }
0165             ++hashValue1;
0166             ++n1;
0167         }
0168     }
0169 
0170     return count > 0 ? (double)sum / (double)count : 0.0;
0171 }