[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