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