diff options
Diffstat (limited to 'bolt/lib/Rewrite/LinuxKernelRewriter.cpp')
| -rw-r--r-- | bolt/lib/Rewrite/LinuxKernelRewriter.cpp | 251 |
1 files changed, 156 insertions, 95 deletions
diff --git a/bolt/lib/Rewrite/LinuxKernelRewriter.cpp b/bolt/lib/Rewrite/LinuxKernelRewriter.cpp index 03b414b71cac..5a5e044184d0 100644 --- a/bolt/lib/Rewrite/LinuxKernelRewriter.cpp +++ b/bolt/lib/Rewrite/LinuxKernelRewriter.cpp @@ -21,6 +21,8 @@ #include "llvm/Support/CommandLine.h" #include "llvm/Support/Debug.h" #include "llvm/Support/Errc.h" +#include "llvm/Support/ErrorOr.h" +#include <regex> #define DEBUG_TYPE "bolt-linux" @@ -89,6 +91,34 @@ static cl::opt<bool> } // namespace opts +/// Linux kernel version +struct LKVersion { + LKVersion() {} + LKVersion(unsigned Major, unsigned Minor, unsigned Rev) + : Major(Major), Minor(Minor), Rev(Rev) {} + + bool operator<(const LKVersion &Other) const { + return std::make_tuple(Major, Minor, Rev) < + std::make_tuple(Other.Major, Other.Minor, Other.Rev); + } + + bool operator>(const LKVersion &Other) const { return Other < *this; } + + bool operator<=(const LKVersion &Other) const { return !(*this > Other); } + + bool operator>=(const LKVersion &Other) const { return !(*this < Other); } + + bool operator==(const LKVersion &Other) const { + return Major == Other.Major && Minor == Other.Minor && Rev == Other.Rev; + } + + bool operator!=(const LKVersion &Other) const { return !(*this == Other); } + + unsigned Major{0}; + unsigned Minor{0}; + unsigned Rev{0}; +}; + /// Linux Kernel supports stack unwinding using ORC (oops rewind capability). /// ORC state at every IP can be described by the following data structure. struct ORCState { @@ -123,7 +153,33 @@ inline raw_ostream &operator<<(raw_ostream &OS, const ORCState &E) { namespace { +/// Extension to DataExtractor that supports reading addresses stored in +/// PC-relative format. +class AddressExtractor : public DataExtractor { + uint64_t DataAddress; + +public: + AddressExtractor(StringRef Data, uint64_t DataAddress, bool IsLittleEndian, + uint8_t AddressSize) + : DataExtractor(Data, IsLittleEndian, AddressSize), + DataAddress(DataAddress) {} + + /// Extract 32-bit PC-relative address/pointer. + uint64_t getPCRelAddress32(Cursor &C) { + const uint64_t Base = DataAddress + C.tell(); + return Base + (int32_t)getU32(C); + } + + /// Extract 64-bit PC-relative address/pointer. + uint64_t getPCRelAddress64(Cursor &C) { + const uint64_t Base = DataAddress + C.tell(); + return Base + (int64_t)getU64(C); + } +}; + class LinuxKernelRewriter final : public MetadataRewriter { + LKVersion LinuxKernelVersion; + /// Information required for updating metadata referencing an instruction. struct InstructionFixup { BinarySection &Section; // Section referencing the instruction. @@ -225,6 +281,8 @@ class LinuxKernelRewriter final : public MetadataRewriter { ErrorOr<BinarySection &> PCIFixupSection = std::errc::bad_address; static constexpr size_t PCI_FIXUP_ENTRY_SIZE = 16; + Error detectLinuxKernelVersion(); + /// Process linux kernel special sections and their relocations. void processLKSections(); @@ -290,6 +348,9 @@ public: : MetadataRewriter("linux-kernel-rewriter", BC) {} Error preCFGInitializer() override { + if (Error E = detectLinuxKernelVersion()) + return E; + processLKSections(); if (Error E = processSMPLocks()) @@ -370,6 +431,28 @@ public: } }; +Error LinuxKernelRewriter::detectLinuxKernelVersion() { + if (BinaryData *BD = BC.getBinaryDataByName("linux_banner")) { + const BinarySection &Section = BD->getSection(); + const std::string S = + Section.getContents().substr(BD->getOffset(), BD->getSize()).str(); + + const std::regex Re(R"---(Linux version ((\d+)\.(\d+)(\.(\d+))?))---"); + std::smatch Match; + if (std::regex_search(S, Match, Re)) { + const unsigned Major = std::stoi(Match[2].str()); + const unsigned Minor = std::stoi(Match[3].str()); + const unsigned Rev = Match[5].matched ? std::stoi(Match[5].str()) : 0; + LinuxKernelVersion = LKVersion(Major, Minor, Rev); + BC.outs() << "BOLT-INFO: Linux kernel version is " << Match[1].str() + << "\n"; + return Error::success(); + } + } + return createStringError(errc::executable_format_error, + "Linux kernel version is unknown"); +} + void LinuxKernelRewriter::processLKSections() { processLKKSymtab(); processLKKSymtab(true); @@ -423,13 +506,13 @@ Error LinuxKernelRewriter::processSMPLocks() { return createStringError(errc::executable_format_error, "bad size of .smp_locks section"); - DataExtractor DE = DataExtractor(SMPLocksSection->getContents(), - BC.AsmInfo->isLittleEndian(), - BC.AsmInfo->getCodePointerSize()); - DataExtractor::Cursor Cursor(0); + AddressExtractor AE(SMPLocksSection->getContents(), SectionAddress, + BC.AsmInfo->isLittleEndian(), + BC.AsmInfo->getCodePointerSize()); + AddressExtractor::Cursor Cursor(0); while (Cursor && Cursor.tell() < SectionSize) { const uint64_t Offset = Cursor.tell(); - const uint64_t IP = SectionAddress + Offset + (int32_t)DE.getU32(Cursor); + const uint64_t IP = AE.getPCRelAddress32(Cursor); // Consume the status of the cursor. if (!Cursor) @@ -499,20 +582,17 @@ Error LinuxKernelRewriter::readORCTables() { return createStringError(errc::executable_format_error, "ORC entries number mismatch detected"); - const uint64_t IPSectionAddress = ORCUnwindIPSection->getAddress(); - DataExtractor OrcDE = DataExtractor(ORCUnwindSection->getContents(), - BC.AsmInfo->isLittleEndian(), - BC.AsmInfo->getCodePointerSize()); - DataExtractor IPDE = DataExtractor(ORCUnwindIPSection->getContents(), - BC.AsmInfo->isLittleEndian(), - BC.AsmInfo->getCodePointerSize()); + DataExtractor OrcDE(ORCUnwindSection->getContents(), + BC.AsmInfo->isLittleEndian(), + BC.AsmInfo->getCodePointerSize()); + AddressExtractor IPAE( + ORCUnwindIPSection->getContents(), ORCUnwindIPSection->getAddress(), + BC.AsmInfo->isLittleEndian(), BC.AsmInfo->getCodePointerSize()); DataExtractor::Cursor ORCCursor(0); DataExtractor::Cursor IPCursor(0); uint64_t PrevIP = 0; for (uint32_t Index = 0; Index < NumORCEntries; ++Index) { - const uint64_t IP = - IPSectionAddress + IPCursor.tell() + (int32_t)IPDE.getU32(IPCursor); - + const uint64_t IP = IPAE.getPCRelAddress32(IPCursor); // Consume the status of the cursor. if (!IPCursor) return createStringError(errc::executable_format_error, @@ -856,15 +936,13 @@ Error LinuxKernelRewriter::validateORCTables() { if (!ORCUnwindIPSection) return Error::success(); - const uint64_t IPSectionAddress = ORCUnwindIPSection->getAddress(); - DataExtractor IPDE = DataExtractor(ORCUnwindIPSection->getOutputContents(), - BC.AsmInfo->isLittleEndian(), - BC.AsmInfo->getCodePointerSize()); - DataExtractor::Cursor IPCursor(0); + AddressExtractor IPAE( + ORCUnwindIPSection->getOutputContents(), ORCUnwindIPSection->getAddress(), + BC.AsmInfo->isLittleEndian(), BC.AsmInfo->getCodePointerSize()); + AddressExtractor::Cursor IPCursor(0); uint64_t PrevIP = 0; for (uint32_t Index = 0; Index < NumORCEntries; ++Index) { - const uint64_t IP = - IPSectionAddress + IPCursor.tell() + (int32_t)IPDE.getU32(IPCursor); + const uint64_t IP = IPAE.getPCRelAddress32(IPCursor); if (!IPCursor) return createStringError(errc::executable_format_error, "out of bounds while reading ORC IP table: %s", @@ -916,16 +994,14 @@ Error LinuxKernelRewriter::readStaticCalls() { "static call table size error"); const uint64_t SectionAddress = StaticCallSection->getAddress(); - DataExtractor DE(StaticCallSection->getContents(), - BC.AsmInfo->isLittleEndian(), - BC.AsmInfo->getCodePointerSize()); - DataExtractor::Cursor Cursor(StaticCallTableAddress - SectionAddress); + AddressExtractor AE(StaticCallSection->getContents(), SectionAddress, + BC.AsmInfo->isLittleEndian(), + BC.AsmInfo->getCodePointerSize()); + AddressExtractor::Cursor Cursor(StaticCallTableAddress - SectionAddress); uint32_t EntryID = 0; while (Cursor && Cursor.tell() < Stop->getAddress() - SectionAddress) { - const uint64_t CallAddress = - SectionAddress + Cursor.tell() + (int32_t)DE.getU32(Cursor); - const uint64_t KeyAddress = - SectionAddress + Cursor.tell() + (int32_t)DE.getU32(Cursor); + const uint64_t CallAddress = AE.getPCRelAddress32(Cursor); + const uint64_t KeyAddress = AE.getPCRelAddress32(Cursor); // Consume the status of the cursor. if (!Cursor) @@ -1027,18 +1103,15 @@ Error LinuxKernelRewriter::readExceptionTable() { return createStringError(errc::executable_format_error, "exception table size error"); - const uint64_t SectionAddress = ExceptionsSection->getAddress(); - DataExtractor DE(ExceptionsSection->getContents(), - BC.AsmInfo->isLittleEndian(), - BC.AsmInfo->getCodePointerSize()); - DataExtractor::Cursor Cursor(0); + AddressExtractor AE( + ExceptionsSection->getContents(), ExceptionsSection->getAddress(), + BC.AsmInfo->isLittleEndian(), BC.AsmInfo->getCodePointerSize()); + AddressExtractor::Cursor Cursor(0); uint32_t EntryID = 0; while (Cursor && Cursor.tell() < ExceptionsSection->getSize()) { - const uint64_t InstAddress = - SectionAddress + Cursor.tell() + (int32_t)DE.getU32(Cursor); - const uint64_t FixupAddress = - SectionAddress + Cursor.tell() + (int32_t)DE.getU32(Cursor); - const uint64_t Data = DE.getU32(Cursor); + const uint64_t InstAddress = AE.getPCRelAddress32(Cursor); + const uint64_t FixupAddress = AE.getPCRelAddress32(Cursor); + const uint64_t Data = AE.getU32(Cursor); // Consume the status of the cursor. if (!Cursor) @@ -1134,9 +1207,9 @@ Error LinuxKernelRewriter::readParaInstructions() { if (!ParavirtualPatchSection) return Error::success(); - DataExtractor DE = DataExtractor(ParavirtualPatchSection->getContents(), - BC.AsmInfo->isLittleEndian(), - BC.AsmInfo->getCodePointerSize()); + DataExtractor DE(ParavirtualPatchSection->getContents(), + BC.AsmInfo->isLittleEndian(), + BC.AsmInfo->getCodePointerSize()); uint32_t EntryID = 0; DataExtractor::Cursor Cursor(0); while (Cursor && !DE.eof(Cursor)) { @@ -1235,15 +1308,14 @@ Error LinuxKernelRewriter::readBugTable() { return createStringError(errc::executable_format_error, "bug table size error"); - const uint64_t SectionAddress = BugTableSection->getAddress(); - DataExtractor DE(BugTableSection->getContents(), BC.AsmInfo->isLittleEndian(), - BC.AsmInfo->getCodePointerSize()); - DataExtractor::Cursor Cursor(0); + AddressExtractor AE( + BugTableSection->getContents(), BugTableSection->getAddress(), + BC.AsmInfo->isLittleEndian(), BC.AsmInfo->getCodePointerSize()); + AddressExtractor::Cursor Cursor(0); uint32_t EntryID = 0; while (Cursor && Cursor.tell() < BugTableSection->getSize()) { const uint64_t Pos = Cursor.tell(); - const uint64_t InstAddress = - SectionAddress + Pos + (int32_t)DE.getU32(Cursor); + const uint64_t InstAddress = AE.getPCRelAddress32(Cursor); Cursor.seek(Pos + BUG_TABLE_ENTRY_SIZE); if (!Cursor) @@ -1402,23 +1474,20 @@ Error LinuxKernelRewriter::readAltInstructions() { Error LinuxKernelRewriter::tryReadAltInstructions(uint32_t AltInstFeatureSize, bool AltInstHasPadLen, bool ParseOnly) { - const uint64_t Address = AltInstrSection->getAddress(); - DataExtractor DE = DataExtractor(AltInstrSection->getContents(), - BC.AsmInfo->isLittleEndian(), - BC.AsmInfo->getCodePointerSize()); + AddressExtractor AE( + AltInstrSection->getContents(), AltInstrSection->getAddress(), + BC.AsmInfo->isLittleEndian(), BC.AsmInfo->getCodePointerSize()); + AddressExtractor::Cursor Cursor(0); uint64_t EntryID = 0; - DataExtractor::Cursor Cursor(0); - while (Cursor && !DE.eof(Cursor)) { - const uint64_t OrgInstAddress = - Address + Cursor.tell() + (int32_t)DE.getU32(Cursor); - const uint64_t AltInstAddress = - Address + Cursor.tell() + (int32_t)DE.getU32(Cursor); - const uint64_t Feature = DE.getUnsigned(Cursor, AltInstFeatureSize); - const uint8_t OrgSize = DE.getU8(Cursor); - const uint8_t AltSize = DE.getU8(Cursor); + while (Cursor && !AE.eof(Cursor)) { + const uint64_t OrgInstAddress = AE.getPCRelAddress32(Cursor); + const uint64_t AltInstAddress = AE.getPCRelAddress32(Cursor); + const uint64_t Feature = AE.getUnsigned(Cursor, AltInstFeatureSize); + const uint8_t OrgSize = AE.getU8(Cursor); + const uint8_t AltSize = AE.getU8(Cursor); // Older kernels may have the padlen field. - const uint8_t PadLen = AltInstHasPadLen ? DE.getU8(Cursor) : 0; + const uint8_t PadLen = AltInstHasPadLen ? AE.getU8(Cursor) : 0; if (!Cursor) return createStringError( @@ -1537,19 +1606,17 @@ Error LinuxKernelRewriter::readPCIFixupTable() { return createStringError(errc::executable_format_error, "PCI fixup table size error"); - const uint64_t Address = PCIFixupSection->getAddress(); - DataExtractor DE = DataExtractor(PCIFixupSection->getContents(), - BC.AsmInfo->isLittleEndian(), - BC.AsmInfo->getCodePointerSize()); + AddressExtractor AE( + PCIFixupSection->getContents(), PCIFixupSection->getAddress(), + BC.AsmInfo->isLittleEndian(), BC.AsmInfo->getCodePointerSize()); + AddressExtractor::Cursor Cursor(0); uint64_t EntryID = 0; - DataExtractor::Cursor Cursor(0); - while (Cursor && !DE.eof(Cursor)) { - const uint16_t Vendor = DE.getU16(Cursor); - const uint16_t Device = DE.getU16(Cursor); - const uint32_t Class = DE.getU32(Cursor); - const uint32_t ClassShift = DE.getU32(Cursor); - const uint64_t HookAddress = - Address + Cursor.tell() + (int32_t)DE.getU32(Cursor); + while (Cursor && !AE.eof(Cursor)) { + const uint16_t Vendor = AE.getU16(Cursor); + const uint16_t Device = AE.getU16(Cursor); + const uint32_t Class = AE.getU32(Cursor); + const uint32_t ClassShift = AE.getU32(Cursor); + const uint64_t HookAddress = AE.getPCRelAddress32(Cursor); if (!Cursor) return createStringError(errc::executable_format_error, @@ -1654,18 +1721,15 @@ Error LinuxKernelRewriter::readStaticKeysJumpTable() { "static keys jump table size error"); const uint64_t SectionAddress = StaticKeysJumpSection->getAddress(); - DataExtractor DE(StaticKeysJumpSection->getContents(), - BC.AsmInfo->isLittleEndian(), - BC.AsmInfo->getCodePointerSize()); - DataExtractor::Cursor Cursor(StaticKeysJumpTableAddress - SectionAddress); + AddressExtractor AE(StaticKeysJumpSection->getContents(), SectionAddress, + BC.AsmInfo->isLittleEndian(), + BC.AsmInfo->getCodePointerSize()); + AddressExtractor::Cursor Cursor(StaticKeysJumpTableAddress - SectionAddress); uint32_t EntryID = 0; while (Cursor && Cursor.tell() < Stop->getAddress() - SectionAddress) { - const uint64_t JumpAddress = - SectionAddress + Cursor.tell() + (int32_t)DE.getU32(Cursor); - const uint64_t TargetAddress = - SectionAddress + Cursor.tell() + (int32_t)DE.getU32(Cursor); - const uint64_t KeyAddress = - SectionAddress + Cursor.tell() + (int64_t)DE.getU64(Cursor); + const uint64_t JumpAddress = AE.getPCRelAddress32(Cursor); + const uint64_t TargetAddress = AE.getPCRelAddress32(Cursor); + const uint64_t KeyAddress = AE.getPCRelAddress64(Cursor); // Consume the status of the cursor. if (!Cursor) @@ -1859,21 +1923,18 @@ Error LinuxKernelRewriter::updateStaticKeysJumpTablePostEmit() { return Error::success(); const uint64_t SectionAddress = StaticKeysJumpSection->getAddress(); - DataExtractor DE(StaticKeysJumpSection->getOutputContents(), - BC.AsmInfo->isLittleEndian(), - BC.AsmInfo->getCodePointerSize()); - DataExtractor::Cursor Cursor(StaticKeysJumpTableAddress - SectionAddress); + AddressExtractor AE(StaticKeysJumpSection->getOutputContents(), + SectionAddress, BC.AsmInfo->isLittleEndian(), + BC.AsmInfo->getCodePointerSize()); + AddressExtractor::Cursor Cursor(StaticKeysJumpTableAddress - SectionAddress); const BinaryData *Stop = BC.getBinaryDataByName("__stop___jump_table"); uint32_t EntryID = 0; uint64_t NumShort = 0; uint64_t NumLong = 0; while (Cursor && Cursor.tell() < Stop->getAddress() - SectionAddress) { - const uint64_t JumpAddress = - SectionAddress + Cursor.tell() + (int32_t)DE.getU32(Cursor); - const uint64_t TargetAddress = - SectionAddress + Cursor.tell() + (int32_t)DE.getU32(Cursor); - const uint64_t KeyAddress = - SectionAddress + Cursor.tell() + (int64_t)DE.getU64(Cursor); + const uint64_t JumpAddress = AE.getPCRelAddress32(Cursor); + const uint64_t TargetAddress = AE.getPCRelAddress32(Cursor); + const uint64_t KeyAddress = AE.getPCRelAddress64(Cursor); // Consume the status of the cursor. if (!Cursor) |
