diff --git a/bolt/lib/Rewrite/LinuxKernelRewriter.cpp b/bolt/lib/Rewrite/LinuxKernelRewriter.cpp index 145acd32356cd1..a78397199972c0 100644 --- a/bolt/lib/Rewrite/LinuxKernelRewriter.cpp +++ b/bolt/lib/Rewrite/LinuxKernelRewriter.cpp @@ -36,6 +36,10 @@ static cl::opt DumpORC("dump-orc", cl::desc("dump raw ORC unwind information (sorted)"), cl::init(false), cl::Hidden, cl::cat(BoltCategory)); +static cl::opt DumpParavirtualPatchSites( + "dump-para-sites", cl::desc("dump Linux kernel paravitual patch sites"), + cl::init(false), cl::Hidden, cl::cat(BoltCategory)); + static cl::opt DumpStaticCalls("dump-static-calls", cl::desc("dump Linux kernel static calls"), cl::init(false), cl::Hidden, @@ -147,6 +151,12 @@ class LinuxKernelRewriter final : public MetadataRewriter { /// Functions with exception handling code. DenseSet FunctionsWithExceptions; + /// Section with paravirtual patch sites. + ErrorOr ParavirtualPatchSection = std::errc::bad_address; + + /// Alignment of paravirtual patch structures. + static constexpr size_t PARA_PATCH_ALIGN = 8; + /// Insert an LKMarker for a given code pointer \p PC from a non-code section /// \p SectionName. void insertLKMarker(uint64_t PC, uint64_t SectionOffset, @@ -187,6 +197,9 @@ class LinuxKernelRewriter final : public MetadataRewriter { Error readExceptionTable(); Error rewriteExceptionTable(); + /// Paravirtual instruction patch sites. + Error readParaInstructions(); + /// Mark instructions referenced by kernel metadata. Error markInstructions(); @@ -208,6 +221,9 @@ class LinuxKernelRewriter final : public MetadataRewriter { if (Error E = readExceptionTable()) return E; + if (Error E = readParaInstructions()) + return E; + return Error::success(); } @@ -1013,6 +1029,74 @@ Error LinuxKernelRewriter::rewriteExceptionTable() { return Error::success(); } +/// .parainsrtuctions section contains information for patching parvirtual call +/// instructions during runtime. The entries in the section are in the form: +/// +/// struct paravirt_patch_site { +/// u8 *instr; /* original instructions */ +/// u8 type; /* type of this instruction */ +/// u8 len; /* length of original instruction */ +/// }; +/// +/// Note that the structures are aligned at 8-byte boundary. +Error LinuxKernelRewriter::readParaInstructions() { + ParavirtualPatchSection = BC.getUniqueSectionByName(".parainstructions"); + if (!ParavirtualPatchSection) + return Error::success(); + + DataExtractor DE = DataExtractor(ParavirtualPatchSection->getContents(), + BC.AsmInfo->isLittleEndian(), + BC.AsmInfo->getCodePointerSize()); + uint32_t EntryID = 0; + DataExtractor::Cursor Cursor(0); + while (Cursor && !DE.eof(Cursor)) { + const uint64_t NextOffset = alignTo(Cursor.tell(), Align(PARA_PATCH_ALIGN)); + if (!DE.isValidOffset(NextOffset)) + break; + + Cursor.seek(NextOffset); + + const uint64_t InstrLocation = DE.getU64(Cursor); + const uint8_t Type = DE.getU8(Cursor); + const uint8_t Len = DE.getU8(Cursor); + + if (!Cursor) + return createStringError(errc::executable_format_error, + "out of bounds while reading .parainstructions"); + + ++EntryID; + + if (opts::DumpParavirtualPatchSites) { + BC.outs() << "Paravirtual patch site: " << EntryID << '\n'; + BC.outs() << "\tInstr: 0x" << Twine::utohexstr(InstrLocation) + << "\n\tType: 0x" << Twine::utohexstr(Type) << "\n\tLen: 0x" + << Twine::utohexstr(Len) << '\n'; + } + + BinaryFunction *BF = BC.getBinaryFunctionContainingAddress(InstrLocation); + if (!BF && opts::Verbosity) { + BC.outs() << "BOLT-INFO: no function matches address 0x" + << Twine::utohexstr(InstrLocation) + << " referenced by paravirutal patch site\n"; + } + + if (BF && BC.shouldEmit(*BF)) { + MCInst *Inst = + BF->getInstructionAtOffset(InstrLocation - BF->getAddress()); + if (!Inst) + return createStringError(errc::executable_format_error, + "no instruction at address 0x%" PRIx64 + " in paravirtual call site %d", + InstrLocation, EntryID); + BC.MIB->addAnnotation(*Inst, "ParaSite", EntryID); + } + } + + BC.outs() << "BOLT-INFO: parsed " << EntryID << " paravirtual patch sites\n"; + + return Error::success(); +} + } // namespace std::unique_ptr diff --git a/bolt/test/X86/linux-parainstructions.s b/bolt/test/X86/linux-parainstructions.s new file mode 100644 index 00000000000000..4bdfde5fb7f24b --- /dev/null +++ b/bolt/test/X86/linux-parainstructions.s @@ -0,0 +1,54 @@ +# REQUIRES: system-linux + +## Check that BOLT correctly parses the Linux kernel .parainstructions section. + +# RUN: llvm-mc -filetype=obj -triple x86_64-unknown-unknown %s -o %t.o +# RUN: %clang %cflags -nostdlib %t.o -o %t.exe \ +# RUN: -Wl,--image-base=0xffffffff80000000,--no-dynamic-linker,--no-eh-frame-hdr,--no-pie + +## Verify paravirtual bindings to instructions. + +# RUN: llvm-bolt %t.exe --print-normalized -o %t.out | FileCheck %s + +# CHECK: BOLT-INFO: Linux kernel binary detected +# CHECK: BOLT-INFO: parsed 2 paravirtual patch sites + + .rodata +fptr: + .quad 0 + + .text + .globl _start + .type _start, %function +_start: +# CHECK: Binary Function "_start" + nop +.L1: + call *fptr(%rip) +# CHECK: call +# CHECK-SAME: ParaSite: 1 + nop +.L2: + call *fptr(%rip) +# CHECK: call +# CHECK-SAME: ParaSite: 2 + ret + .size _start, .-_start + + +## Paravirtual patch sites. + .section .parainstructions,"a",@progbits + + .balign 8 + .quad .L1 # instruction + .byte 1 # type + .byte 7 # length + + .balign 8 + .quad .L2 # instruction + .byte 1 # type + .byte 7 # length + +## Fake Linux Kernel sections. + .section __ksymtab,"a",@progbits + .section __ksymtab_gpl,"a",@progbits