Skip to content

[BOLT] Add reading support for Linux kernel .parainstructions section #83965

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Mar 5, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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