[llvm] [BOLT] Add reading support for Linux kernel __bug_table section (PR #84082)
Maksim Panchenko via llvm-commits
llvm-commits at lists.llvm.org
Tue Mar 5 14:34:43 PST 2024
https://github.com/maksfb created https://github.com/llvm/llvm-project/pull/84082
Read __bug_table section and annotate ud2 instructions with a corresponding bug entry ID.
>From 6c3c515028404b9d886188e83110726fabda1ca9 Mon Sep 17 00:00:00 2001
From: Maksim Panchenko <maks at fb.com>
Date: Tue, 5 Mar 2024 14:29:52 -0800
Subject: [PATCH] [BOLT] Add reading support for Linux kernel __bug_table
section
Read __bug_table section and annotate ud2 instructions with a
corresponding bug entry ID.
---
bolt/lib/Rewrite/LinuxKernelRewriter.cpp | 105 +++++++++++++++--------
bolt/test/X86/linux-bug-table.s | 46 ++++++++++
2 files changed, 116 insertions(+), 35 deletions(-)
create mode 100644 bolt/test/X86/linux-bug-table.s
diff --git a/bolt/lib/Rewrite/LinuxKernelRewriter.cpp b/bolt/lib/Rewrite/LinuxKernelRewriter.cpp
index a78397199972c0..964a47346592fc 100644
--- a/bolt/lib/Rewrite/LinuxKernelRewriter.cpp
+++ b/bolt/lib/Rewrite/LinuxKernelRewriter.cpp
@@ -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 @@ class LinuxKernelRewriter final : public MetadataRewriter {
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>
diff --git a/bolt/test/X86/linux-bug-table.s b/bolt/test/X86/linux-bug-table.s
new file mode 100644
index 00000000000000..e8de2fb6cba79d
--- /dev/null
+++ b/bolt/test/X86/linux-bug-table.s
@@ -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", at progbits
+1:
+ .long .L0 - . # instruction
+ .org 1b + 12
+2:
+ .long .L1 - . # instruction
+ .org 2b + 12
+
+## Fake Linux Kernel sections.
+ .section __ksymtab,"a", at progbits
+ .section __ksymtab_gpl,"a", at progbits
More information about the llvm-commits
mailing list