[BOLT] Add reading support for Linux kernel __bug_table section (#84082)

Read __bug_table section and annotate ud2 instructions with a
corresponding bug entry ID.
This commit is contained in:
Maksim Panchenko
2024-03-06 23:34:03 -08:00
committed by GitHub
parent eae4f56cb4
commit 02629793a4
2 changed files with 116 additions and 35 deletions

View File

@@ -157,6 +157,12 @@ class LinuxKernelRewriter final : public MetadataRewriter {
/// Alignment of paravirtual patch structures.
static constexpr size_t PARA_PATCH_ALIGN = 8;
/// Section containing Linux bug table.
ErrorOr<BinarySection &> BugTableSection = std::errc::bad_address;
/// Size of bug_entry struct.
static constexpr size_t BUG_TABLE_ENTRY_SIZE = 12;
/// 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,
@@ -172,9 +178,6 @@ class LinuxKernelRewriter final : public MetadataRewriter {
/// Process __ksymtab and __ksymtab_gpl.
void processLKKSymtab(bool IsGPL = false);
/// Process special linux kernel section, __bug_table.
void processLKBugTable();
/// Process special linux kernel section, .smp_locks.
void processLKSMPLocks();
@@ -200,6 +203,8 @@ class LinuxKernelRewriter final : public MetadataRewriter {
/// Paravirtual instruction patch sites.
Error readParaInstructions();
Error readBugTable();
/// Mark instructions referenced by kernel metadata.
Error markInstructions();
@@ -224,6 +229,9 @@ public:
if (Error E = readParaInstructions())
return E;
if (Error E = readBugTable())
return E;
return Error::success();
}
@@ -289,7 +297,6 @@ void LinuxKernelRewriter::processLKSections() {
processLKPCIFixup();
processLKKSymtab();
processLKKSymtab(true);
processLKBugTable();
processLKSMPLocks();
}
@@ -356,37 +363,6 @@ void LinuxKernelRewriter::processLKKSymtab(bool IsGPL) {
}
}
/// Process __bug_table section.
/// This section contains information useful for kernel debugging.
/// Each entry in the section is a struct bug_entry that contains a pointer to
/// the ud2 instruction corresponding to the bug, corresponding file name (both
/// pointers use PC relative offset addressing), line number, and flags.
/// The definition of the struct bug_entry can be found in
/// `include/asm-generic/bug.h`
void LinuxKernelRewriter::processLKBugTable() {
ErrorOr<BinarySection &> SectionOrError =
BC.getUniqueSectionByName("__bug_table");
if (!SectionOrError)
return;
const uint64_t SectionSize = SectionOrError->getSize();
const uint64_t SectionAddress = SectionOrError->getAddress();
assert((SectionSize % 12) == 0 &&
"The size of the __bug_table section should be a multiple of 12");
for (uint64_t I = 0; I < SectionSize; I += 12) {
const uint64_t EntryAddress = SectionAddress + I;
ErrorOr<uint64_t> Offset = BC.getSignedValueAtAddress(EntryAddress, 4);
assert(Offset &&
"Reading valid PC-relative offset for a __bug_table entry");
const int32_t SignedOffset = *Offset;
const uint64_t RefAddress = EntryAddress + SignedOffset;
assert(BC.getBinaryFunctionContainingAddress(RefAddress) &&
"__bug_table entries should point to a function");
insertLKMarker(RefAddress, I, SignedOffset, true, "__bug_table");
}
}
/// .smp_locks section contains PC-relative references to instructions with LOCK
/// prefix. The prefix can be converted to NOP at boot time on non-SMP systems.
void LinuxKernelRewriter::processLKSMPLocks() {
@@ -1097,6 +1073,65 @@ Error LinuxKernelRewriter::readParaInstructions() {
return Error::success();
}
/// Process __bug_table section.
/// This section contains information useful for kernel debugging.
/// Each entry in the section is a struct bug_entry that contains a pointer to
/// the ud2 instruction corresponding to the bug, corresponding file name (both
/// pointers use PC relative offset addressing), line number, and flags.
/// The definition of the struct bug_entry can be found in
/// `include/asm-generic/bug.h`
///
/// NB: find_bug() uses linear search to match an address to an entry in the bug
/// table. Hence there is no need to sort entries when rewriting the table.
Error LinuxKernelRewriter::readBugTable() {
BugTableSection = BC.getUniqueSectionByName("__bug_table");
if (!BugTableSection)
return Error::success();
if (BugTableSection->getSize() % BUG_TABLE_ENTRY_SIZE)
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);
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);
Cursor.seek(Pos + BUG_TABLE_ENTRY_SIZE);
if (!Cursor)
return createStringError(errc::executable_format_error,
"out of bounds while reading __bug_table");
++EntryID;
BinaryFunction *BF = BC.getBinaryFunctionContainingAddress(InstAddress);
if (!BF && opts::Verbosity) {
BC.outs() << "BOLT-INFO: no function matches address 0x"
<< Twine::utohexstr(InstAddress)
<< " referenced by bug table\n";
}
if (BF && BC.shouldEmit(*BF)) {
MCInst *Inst = BF->getInstructionAtOffset(InstAddress - BF->getAddress());
if (!Inst)
return createStringError(errc::executable_format_error,
"no instruction at address 0x%" PRIx64
" referenced by bug table entry %d",
InstAddress, EntryID);
BC.MIB->addAnnotation(*Inst, "BugEntry", EntryID);
}
}
BC.outs() << "BOLT-INFO: parsed " << EntryID << " bug table entries\n";
return Error::success();
}
} // namespace
std::unique_ptr<MetadataRewriter>

View File

@@ -0,0 +1,46 @@
# REQUIRES: system-linux
## Check that BOLT correctly parses the Linux kernel __bug_table 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 bug entry 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 bug table entries
.text
.globl _start
.type _start, %function
_start:
# CHECK: Binary Function "_start"
nop
.L0:
ud2
# CHECK: ud2
# CHECK-SAME: BugEntry: 1
nop
.L1:
ud2
# CHECK: ud2
# CHECK-SAME: BugEntry: 2
ret
.size _start, .-_start
## Bug table.
.section __bug_table,"a",@progbits
1:
.long .L0 - . # instruction
.org 1b + 12
2:
.long .L1 - . # instruction
.org 2b + 12
## Fake Linux Kernel sections.
.section __ksymtab,"a",@progbits
.section __ksymtab_gpl,"a",@progbits