|
21 | 21 | #include "llvm/Support/CommandLine.h"
|
22 | 22 | #include "llvm/Support/Debug.h"
|
23 | 23 | #include "llvm/Support/Errc.h"
|
| 24 | +#include "llvm/Support/ErrorOr.h" |
| 25 | +#include <regex> |
24 | 26 |
|
25 | 27 | #define DEBUG_TYPE "bolt-linux"
|
26 | 28 |
|
@@ -89,6 +91,34 @@ static cl::opt<bool>
|
89 | 91 |
|
90 | 92 | } // namespace opts
|
91 | 93 |
|
| 94 | +/// Linux kernel version |
| 95 | +struct LKVersion { |
| 96 | + LKVersion() {} |
| 97 | + LKVersion(unsigned Major, unsigned Minor, unsigned Rev) |
| 98 | + : Major(Major), Minor(Minor), Rev(Rev) {} |
| 99 | + |
| 100 | + bool operator<(const LKVersion &Other) const { |
| 101 | + return std::make_tuple(Major, Minor, Rev) < |
| 102 | + std::make_tuple(Other.Major, Other.Minor, Other.Rev); |
| 103 | + } |
| 104 | + |
| 105 | + bool operator>(const LKVersion &Other) const { return Other < *this; } |
| 106 | + |
| 107 | + bool operator<=(const LKVersion &Other) const { return !(*this > Other); } |
| 108 | + |
| 109 | + bool operator>=(const LKVersion &Other) const { return !(*this < Other); } |
| 110 | + |
| 111 | + bool operator==(const LKVersion &Other) const { |
| 112 | + return Major == Other.Major && Minor == Other.Minor && Rev == Other.Rev; |
| 113 | + } |
| 114 | + |
| 115 | + bool operator!=(const LKVersion &Other) const { return !(*this == Other); } |
| 116 | + |
| 117 | + unsigned Major{0}; |
| 118 | + unsigned Minor{0}; |
| 119 | + unsigned Rev{0}; |
| 120 | +}; |
| 121 | + |
92 | 122 | /// Linux Kernel supports stack unwinding using ORC (oops rewind capability).
|
93 | 123 | /// ORC state at every IP can be described by the following data structure.
|
94 | 124 | struct ORCState {
|
@@ -124,6 +154,8 @@ inline raw_ostream &operator<<(raw_ostream &OS, const ORCState &E) {
|
124 | 154 | namespace {
|
125 | 155 |
|
126 | 156 | class LinuxKernelRewriter final : public MetadataRewriter {
|
| 157 | + LKVersion LinuxKernelVersion; |
| 158 | + |
127 | 159 | /// Information required for updating metadata referencing an instruction.
|
128 | 160 | struct InstructionFixup {
|
129 | 161 | BinarySection &Section; // Section referencing the instruction.
|
@@ -225,6 +257,8 @@ class LinuxKernelRewriter final : public MetadataRewriter {
|
225 | 257 | ErrorOr<BinarySection &> PCIFixupSection = std::errc::bad_address;
|
226 | 258 | static constexpr size_t PCI_FIXUP_ENTRY_SIZE = 16;
|
227 | 259 |
|
| 260 | + Error detectLinuxKernelVersion(); |
| 261 | + |
228 | 262 | /// Process linux kernel special sections and their relocations.
|
229 | 263 | void processLKSections();
|
230 | 264 |
|
@@ -290,6 +324,9 @@ class LinuxKernelRewriter final : public MetadataRewriter {
|
290 | 324 | : MetadataRewriter("linux-kernel-rewriter", BC) {}
|
291 | 325 |
|
292 | 326 | Error preCFGInitializer() override {
|
| 327 | + if (Error E = detectLinuxKernelVersion()) |
| 328 | + return E; |
| 329 | + |
293 | 330 | processLKSections();
|
294 | 331 |
|
295 | 332 | if (Error E = processSMPLocks())
|
@@ -370,6 +407,28 @@ class LinuxKernelRewriter final : public MetadataRewriter {
|
370 | 407 | }
|
371 | 408 | };
|
372 | 409 |
|
| 410 | +Error LinuxKernelRewriter::detectLinuxKernelVersion() { |
| 411 | + if (BinaryData *BD = BC.getBinaryDataByName("linux_banner")) { |
| 412 | + const BinarySection &Section = BD->getSection(); |
| 413 | + const std::string S = |
| 414 | + Section.getContents().substr(BD->getOffset(), BD->getSize()).str(); |
| 415 | + |
| 416 | + const std::regex Re(R"---(Linux version ((\d+)\.(\d+)(\.(\d+))?))---"); |
| 417 | + std::smatch Match; |
| 418 | + if (std::regex_search(S, Match, Re)) { |
| 419 | + const unsigned Major = std::stoi(Match[2].str()); |
| 420 | + const unsigned Minor = std::stoi(Match[3].str()); |
| 421 | + const unsigned Rev = Match.size() > 5 ? std::stoi(Match[5].str()) : 0; |
| 422 | + LinuxKernelVersion = LKVersion(Major, Minor, Rev); |
| 423 | + BC.outs() << "BOLT-INFO: Linux kernel version is " << Match[1].str() |
| 424 | + << "\n"; |
| 425 | + return Error::success(); |
| 426 | + } |
| 427 | + } |
| 428 | + return createStringError(errc::executable_format_error, |
| 429 | + "Linux kernel version is unknown"); |
| 430 | +} |
| 431 | + |
373 | 432 | void LinuxKernelRewriter::processLKSections() {
|
374 | 433 | processLKKSymtab();
|
375 | 434 | processLKKSymtab(true);
|
|
0 commit comments