1 //===--- JumpTable.h - Representation of a jump table ---------------------===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8 //
9 //===----------------------------------------------------------------------===//
10 
11 #include "bolt/Core/JumpTable.h"
12 #include "bolt/Core/BinaryFunction.h"
13 #include "bolt/Core/BinarySection.h"
14 #include "llvm/Support/CommandLine.h"
15 #include "llvm/Support/Debug.h"
16 
17 #define DEBUG_TYPE "bolt"
18 
19 using namespace llvm;
20 using namespace bolt;
21 
22 using JumpTable = bolt::JumpTable;
23 
24 namespace opts {
25 extern cl::opt<JumpTableSupportLevel> JumpTables;
26 extern cl::opt<unsigned> Verbosity;
27 } // namespace opts
28 
29 bolt::JumpTable::JumpTable(MCSymbol &Symbol, uint64_t Address, size_t EntrySize,
30                            JumpTableType Type, LabelMapType &&Labels,
31                            BinaryFunction &BF, BinarySection &Section)
32     : BinaryData(Symbol, Address, 0, EntrySize, Section), EntrySize(EntrySize),
33       OutputEntrySize(EntrySize), Type(Type), Labels(Labels), Parent(&BF) {}
34 
35 std::pair<size_t, size_t>
36 bolt::JumpTable::getEntriesForAddress(const uint64_t Addr) const {
37   // Check if this is not an address, but a cloned JT id
38   if ((int64_t)Addr < 0ll)
39     return std::make_pair(0, Entries.size());
40 
41   const uint64_t InstOffset = Addr - getAddress();
42   size_t StartIndex = 0, EndIndex = 0;
43   uint64_t Offset = 0;
44 
45   for (size_t I = 0; I < Entries.size(); ++I) {
46     auto LI = Labels.find(Offset);
47     if (LI != Labels.end()) {
48       const auto NextLI = std::next(LI);
49       const uint64_t NextOffset =
50           NextLI == Labels.end() ? getSize() : NextLI->first;
51       if (InstOffset >= LI->first && InstOffset < NextOffset) {
52         StartIndex = I;
53         EndIndex = I;
54         while (Offset < NextOffset) {
55           ++EndIndex;
56           Offset += EntrySize;
57         }
58         break;
59       }
60     }
61     Offset += EntrySize;
62   }
63 
64   return std::make_pair(StartIndex, EndIndex);
65 }
66 
67 bool bolt::JumpTable::replaceDestination(uint64_t JTAddress,
68                                          const MCSymbol *OldDest,
69                                          MCSymbol *NewDest) {
70   bool Patched = false;
71   const std::pair<size_t, size_t> Range = getEntriesForAddress(JTAddress);
72   for (auto I = &Entries[Range.first], E = &Entries[Range.second]; I != E;
73        ++I) {
74     MCSymbol *&Entry = *I;
75     if (Entry == OldDest) {
76       Patched = true;
77       Entry = NewDest;
78     }
79   }
80   return Patched;
81 }
82 
83 void bolt::JumpTable::updateOriginal() {
84   BinaryContext &BC = getSection().getBinaryContext();
85   const uint64_t BaseOffset = getAddress() - getSection().getAddress();
86   uint64_t EntryOffset = BaseOffset;
87   for (MCSymbol *Entry : Entries) {
88     const uint64_t RelType =
89         Type == JTT_NORMAL ? ELF::R_X86_64_64 : ELF::R_X86_64_PC32;
90     const uint64_t RelAddend =
91         Type == JTT_NORMAL ? 0 : EntryOffset - BaseOffset;
92     // Replace existing relocation with the new one to allow any modifications
93     // to the original jump table.
94     if (BC.HasRelocations)
95       getOutputSection().removeRelocationAt(EntryOffset);
96     getOutputSection().addRelocation(EntryOffset, Entry, RelType, RelAddend);
97     EntryOffset += EntrySize;
98   }
99 }
100 
101 void bolt::JumpTable::print(raw_ostream &OS) const {
102   uint64_t Offset = 0;
103   if (Type == JTT_PIC)
104     OS << "PIC ";
105   OS << "Jump table " << getName() << " for function " << *Parent << " at 0x"
106      << Twine::utohexstr(getAddress()) << " with a total count of " << Count
107      << ":\n";
108   for (const uint64_t EntryOffset : OffsetEntries) {
109     OS << "  0x" << Twine::utohexstr(EntryOffset) << '\n';
110   }
111   for (const MCSymbol *Entry : Entries) {
112     auto LI = Labels.find(Offset);
113     if (Offset && LI != Labels.end()) {
114       OS << "Jump Table " << LI->second->getName() << " at 0x"
115          << Twine::utohexstr(getAddress() + Offset)
116          << " (possibly part of larger jump table):\n";
117     }
118     OS << format("  0x%04" PRIx64 " : ", Offset) << Entry->getName();
119     if (!Counts.empty()) {
120       OS << " : " << Counts[Offset / EntrySize].Mispreds << "/"
121          << Counts[Offset / EntrySize].Count;
122     }
123     OS << '\n';
124     Offset += EntrySize;
125   }
126   OS << "\n\n";
127 }
128