2021-12-21 10:21:41 -08:00
|
|
|
//===- bolt/Core/JumpTable.cpp - Jump table at low-level IR ---------------===//
|
2017-11-14 20:05:11 -08:00
|
|
|
//
|
2021-03-15 18:04:18 -07:00
|
|
|
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
|
|
|
// See https://llvm.org/LICENSE.txt for license information.
|
|
|
|
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
2017-11-14 20:05:11 -08:00
|
|
|
//
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
//
|
2021-12-21 10:21:41 -08:00
|
|
|
// This file implements the JumpTable class.
|
|
|
|
//
|
2017-11-14 20:05:11 -08:00
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
|
2021-10-08 11:47:10 -07:00
|
|
|
#include "bolt/Core/JumpTable.h"
|
|
|
|
#include "bolt/Core/BinaryFunction.h"
|
|
|
|
#include "bolt/Core/BinarySection.h"
|
2017-11-14 20:05:11 -08:00
|
|
|
#include "llvm/Support/CommandLine.h"
|
|
|
|
|
|
|
|
#define DEBUG_TYPE "bolt"
|
|
|
|
|
|
|
|
using namespace llvm;
|
|
|
|
using namespace bolt;
|
|
|
|
|
2021-06-02 22:46:57 -07:00
|
|
|
using JumpTable = bolt::JumpTable;
|
|
|
|
|
2017-11-14 20:05:11 -08:00
|
|
|
namespace opts {
|
|
|
|
extern cl::opt<JumpTableSupportLevel> JumpTables;
|
|
|
|
extern cl::opt<unsigned> Verbosity;
|
2021-12-14 16:52:51 -08:00
|
|
|
} // namespace opts
|
2017-11-14 20:05:11 -08:00
|
|
|
|
2021-06-02 22:46:57 -07:00
|
|
|
bolt::JumpTable::JumpTable(MCSymbol &Symbol, uint64_t Address, size_t EntrySize,
|
|
|
|
JumpTableType Type, LabelMapType &&Labels,
|
[BOLT] Support multiple parents for split jump table
There are two assumptions regarding jump table:
(a) It is accessed by only one fragment, say, Parent
(b) All entries target instructions in Parent
For (a), BOLT stores jump table entries as relative offset to Parent.
For (b), BOLT treats jump table entries target somewhere out of Parent
as INVALID_OFFSET, including fragment of same split function.
In this update, we extend (a) and (b) to include fragment of same split
functinon. For (a), we store jump table entries in absolute offset
instead. In addition, jump table will store all fragments that access
it. A fragment uses this information to only create label for jump table
entries that target to that fragment.
For (b), using absolute offset allows jump table entries to target
fragments of same split function, i.e., extend support for split jump
table. This can be done using relocation (fragment start/size) and
fragment detection heuristics (e.g., using symbol name pattern for
non-stripped binaries).
For jump table targets that can only be reached by one fragment, we
mark them as local label; otherwise, they would be the secondary
function entry to the target fragment.
Test Plan
```
ninja check-bolt
```
Reviewed By: Amir
Differential Revision: https://reviews.llvm.org/D128474
2022-07-13 23:35:51 -07:00
|
|
|
BinarySection &Section)
|
2021-06-02 22:46:57 -07:00
|
|
|
: BinaryData(Symbol, Address, 0, EntrySize, Section), EntrySize(EntrySize),
|
[BOLT] Support multiple parents for split jump table
There are two assumptions regarding jump table:
(a) It is accessed by only one fragment, say, Parent
(b) All entries target instructions in Parent
For (a), BOLT stores jump table entries as relative offset to Parent.
For (b), BOLT treats jump table entries target somewhere out of Parent
as INVALID_OFFSET, including fragment of same split function.
In this update, we extend (a) and (b) to include fragment of same split
functinon. For (a), we store jump table entries in absolute offset
instead. In addition, jump table will store all fragments that access
it. A fragment uses this information to only create label for jump table
entries that target to that fragment.
For (b), using absolute offset allows jump table entries to target
fragments of same split function, i.e., extend support for split jump
table. This can be done using relocation (fragment start/size) and
fragment detection heuristics (e.g., using symbol name pattern for
non-stripped binaries).
For jump table targets that can only be reached by one fragment, we
mark them as local label; otherwise, they would be the secondary
function entry to the target fragment.
Test Plan
```
ninja check-bolt
```
Reviewed By: Amir
Differential Revision: https://reviews.llvm.org/D128474
2022-07-13 23:35:51 -07:00
|
|
|
OutputEntrySize(EntrySize), Type(Type), Labels(Labels) {}
|
2019-05-02 17:42:06 -07:00
|
|
|
|
2017-11-14 20:05:11 -08:00
|
|
|
std::pair<size_t, size_t>
|
2021-06-02 22:46:57 -07:00
|
|
|
bolt::JumpTable::getEntriesForAddress(const uint64_t Addr) const {
|
2019-07-02 16:56:41 -07:00
|
|
|
// Check if this is not an address, but a cloned JT id
|
|
|
|
if ((int64_t)Addr < 0ll)
|
|
|
|
return std::make_pair(0, Entries.size());
|
|
|
|
|
2017-11-14 20:05:11 -08:00
|
|
|
const uint64_t InstOffset = Addr - getAddress();
|
|
|
|
size_t StartIndex = 0, EndIndex = 0;
|
|
|
|
uint64_t Offset = 0;
|
|
|
|
|
|
|
|
for (size_t I = 0; I < Entries.size(); ++I) {
|
|
|
|
auto LI = Labels.find(Offset);
|
|
|
|
if (LI != Labels.end()) {
|
|
|
|
const auto NextLI = std::next(LI);
|
2021-04-08 00:19:26 -07:00
|
|
|
const uint64_t NextOffset =
|
|
|
|
NextLI == Labels.end() ? getSize() : NextLI->first;
|
2017-11-14 20:05:11 -08:00
|
|
|
if (InstOffset >= LI->first && InstOffset < NextOffset) {
|
|
|
|
StartIndex = I;
|
|
|
|
EndIndex = I;
|
|
|
|
while (Offset < NextOffset) {
|
|
|
|
++EndIndex;
|
|
|
|
Offset += EntrySize;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
Offset += EntrySize;
|
|
|
|
}
|
|
|
|
|
|
|
|
return std::make_pair(StartIndex, EndIndex);
|
|
|
|
}
|
|
|
|
|
2021-06-02 22:46:57 -07:00
|
|
|
bool bolt::JumpTable::replaceDestination(uint64_t JTAddress,
|
|
|
|
const MCSymbol *OldDest,
|
|
|
|
MCSymbol *NewDest) {
|
2021-05-13 10:50:47 -07:00
|
|
|
bool Patched = false;
|
2021-04-08 00:19:26 -07:00
|
|
|
const std::pair<size_t, size_t> Range = getEntriesForAddress(JTAddress);
|
2023-01-25 11:38:07 -08:00
|
|
|
for (auto I = Range.first; I != Range.second; ++I) {
|
|
|
|
if (Entries[I] == OldDest) {
|
2017-11-14 20:05:11 -08:00
|
|
|
Patched = true;
|
2023-01-25 11:38:07 -08:00
|
|
|
Entries[I] = NewDest;
|
2017-11-14 20:05:11 -08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
return Patched;
|
|
|
|
}
|
|
|
|
|
2021-06-02 22:46:57 -07:00
|
|
|
void bolt::JumpTable::updateOriginal() {
|
2021-05-12 23:35:10 -07:00
|
|
|
BinaryContext &BC = getSection().getBinaryContext();
|
2017-11-14 20:05:11 -08:00
|
|
|
const uint64_t BaseOffset = getAddress() - getSection().getAddress();
|
2021-05-12 23:35:10 -07:00
|
|
|
uint64_t EntryOffset = BaseOffset;
|
2021-04-08 00:19:26 -07:00
|
|
|
for (MCSymbol *Entry : Entries) {
|
2021-05-12 23:35:10 -07:00
|
|
|
const uint64_t RelType =
|
|
|
|
Type == JTT_NORMAL ? ELF::R_X86_64_64 : ELF::R_X86_64_PC32;
|
|
|
|
const uint64_t RelAddend =
|
|
|
|
Type == JTT_NORMAL ? 0 : EntryOffset - BaseOffset;
|
|
|
|
// Replace existing relocation with the new one to allow any modifications
|
|
|
|
// to the original jump table.
|
|
|
|
if (BC.HasRelocations)
|
|
|
|
getOutputSection().removeRelocationAt(EntryOffset);
|
|
|
|
getOutputSection().addRelocation(EntryOffset, Entry, RelType, RelAddend);
|
|
|
|
EntryOffset += EntrySize;
|
2017-11-14 20:05:11 -08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-06-02 22:46:57 -07:00
|
|
|
void bolt::JumpTable::print(raw_ostream &OS) const {
|
2017-11-14 20:05:11 -08:00
|
|
|
uint64_t Offset = 0;
|
2019-06-28 09:21:27 -07:00
|
|
|
if (Type == JTT_PIC)
|
|
|
|
OS << "PIC ";
|
[BOLT] Support multiple parents for split jump table
There are two assumptions regarding jump table:
(a) It is accessed by only one fragment, say, Parent
(b) All entries target instructions in Parent
For (a), BOLT stores jump table entries as relative offset to Parent.
For (b), BOLT treats jump table entries target somewhere out of Parent
as INVALID_OFFSET, including fragment of same split function.
In this update, we extend (a) and (b) to include fragment of same split
functinon. For (a), we store jump table entries in absolute offset
instead. In addition, jump table will store all fragments that access
it. A fragment uses this information to only create label for jump table
entries that target to that fragment.
For (b), using absolute offset allows jump table entries to target
fragments of same split function, i.e., extend support for split jump
table. This can be done using relocation (fragment start/size) and
fragment detection heuristics (e.g., using symbol name pattern for
non-stripped binaries).
For jump table targets that can only be reached by one fragment, we
mark them as local label; otherwise, they would be the secondary
function entry to the target fragment.
Test Plan
```
ninja check-bolt
```
Reviewed By: Amir
Differential Revision: https://reviews.llvm.org/D128474
2022-07-13 23:35:51 -07:00
|
|
|
ListSeparator LS;
|
|
|
|
|
|
|
|
OS << "Jump table " << getName() << " for function ";
|
|
|
|
for (BinaryFunction *Frag : Parents)
|
|
|
|
OS << LS << *Frag;
|
|
|
|
OS << " at 0x" << Twine::utohexstr(getAddress()) << " with a total count of "
|
|
|
|
<< Count << ":\n";
|
|
|
|
for (const uint64_t EntryAddress : EntriesAsAddress)
|
|
|
|
OS << " absolute offset: 0x" << Twine::utohexstr(EntryAddress) << '\n';
|
2021-04-08 00:19:26 -07:00
|
|
|
for (const MCSymbol *Entry : Entries) {
|
2017-11-14 20:05:11 -08:00
|
|
|
auto LI = Labels.find(Offset);
|
2019-06-28 09:21:27 -07:00
|
|
|
if (Offset && LI != Labels.end()) {
|
|
|
|
OS << "Jump Table " << LI->second->getName() << " at 0x"
|
|
|
|
<< Twine::utohexstr(getAddress() + Offset)
|
2021-12-14 16:52:51 -08:00
|
|
|
<< " (possibly part of larger jump table):\n";
|
2017-11-14 20:05:11 -08:00
|
|
|
}
|
|
|
|
OS << format(" 0x%04" PRIx64 " : ", Offset) << Entry->getName();
|
|
|
|
if (!Counts.empty()) {
|
2021-12-14 16:52:51 -08:00
|
|
|
OS << " : " << Counts[Offset / EntrySize].Mispreds << "/"
|
|
|
|
<< Counts[Offset / EntrySize].Count;
|
2017-11-14 20:05:11 -08:00
|
|
|
}
|
|
|
|
OS << '\n';
|
|
|
|
Offset += EntrySize;
|
|
|
|
}
|
|
|
|
OS << "\n\n";
|
|
|
|
}
|