Skip to content

Commit

Permalink
[BOLT] Add reading support for Linux kernel .parainstructions section (
Browse files Browse the repository at this point in the history
…llvm#83965)

Read .parainstruction section and mark call instructions with ParaSite
annotations.
  • Loading branch information
maksfb authored Mar 5, 2024
1 parent 201572e commit f51ade2
Show file tree
Hide file tree
Showing 2 changed files with 138 additions and 0 deletions.
84 changes: 84 additions & 0 deletions bolt/lib/Rewrite/LinuxKernelRewriter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,10 @@ static cl::opt<bool>
DumpORC("dump-orc", cl::desc("dump raw ORC unwind information (sorted)"),
cl::init(false), cl::Hidden, cl::cat(BoltCategory));

static cl::opt<bool> DumpParavirtualPatchSites(
"dump-para-sites", cl::desc("dump Linux kernel paravitual patch sites"),
cl::init(false), cl::Hidden, cl::cat(BoltCategory));

static cl::opt<bool> DumpStaticCalls("dump-static-calls",
cl::desc("dump Linux kernel static calls"),
cl::init(false), cl::Hidden,
Expand Down Expand Up @@ -147,6 +151,12 @@ class LinuxKernelRewriter final : public MetadataRewriter {
/// Functions with exception handling code.
DenseSet<BinaryFunction *> FunctionsWithExceptions;

/// Section with paravirtual patch sites.
ErrorOr<BinarySection &> 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,
Expand Down Expand Up @@ -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();

Expand All @@ -208,6 +221,9 @@ class LinuxKernelRewriter final : public MetadataRewriter {
if (Error E = readExceptionTable())
return E;

if (Error E = readParaInstructions())
return E;

return Error::success();
}

Expand Down Expand Up @@ -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<MetadataRewriter>
Expand Down
54 changes: 54 additions & 0 deletions bolt/test/X86/linux-parainstructions.s
Original file line number Diff line number Diff line change
@@ -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

0 comments on commit f51ade2

Please sign in to comment.