Skip to content

Commit 35e7d45

Browse files
authored
[BOLT] Add rewriting support for Linux kernel __bug_table (#86908)
Update instruction locations in the __bug_table section after new code is emitted. If an instruction with associated bug ID was deleted, overwrite its location with zero.
1 parent 237572f commit 35e7d45

File tree

2 files changed

+81
-10
lines changed

2 files changed

+81
-10
lines changed

bolt/lib/Rewrite/LinuxKernelRewriter.cpp

Lines changed: 65 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -212,6 +212,11 @@ class LinuxKernelRewriter final : public MetadataRewriter {
212212
/// Size of bug_entry struct.
213213
static constexpr size_t BUG_TABLE_ENTRY_SIZE = 12;
214214

215+
/// List of bug entries per function.
216+
using FunctionBugListType =
217+
DenseMap<BinaryFunction *, SmallVector<uint32_t, 2>>;
218+
FunctionBugListType FunctionBugList;
219+
215220
/// .pci_fixup section.
216221
ErrorOr<BinarySection &> PCIFixupSection = std::errc::bad_address;
217222
static constexpr size_t PCI_FIXUP_ENTRY_SIZE = 16;
@@ -254,7 +259,9 @@ class LinuxKernelRewriter final : public MetadataRewriter {
254259
Error readParaInstructions();
255260
Error rewriteParaInstructions();
256261

262+
/// __bug_table section handling.
257263
Error readBugTable();
264+
Error rewriteBugTable();
258265

259266
/// Do no process functions containing instruction annotated with
260267
/// \p Annotation.
@@ -339,6 +346,9 @@ class LinuxKernelRewriter final : public MetadataRewriter {
339346
if (Error E = rewriteStaticKeysJumpTable())
340347
return E;
341348

349+
if (Error E = rewriteBugTable())
350+
return E;
351+
342352
return Error::success();
343353
}
344354

@@ -1164,15 +1174,17 @@ Error LinuxKernelRewriter::rewriteParaInstructions() {
11641174
}
11651175

11661176
/// Process __bug_table section.
1167-
/// This section contains information useful for kernel debugging.
1177+
/// This section contains information useful for kernel debugging, mostly
1178+
/// utilized by WARN()/WARN_ON() macros and deprecated BUG()/BUG_ON().
1179+
///
11681180
/// Each entry in the section is a struct bug_entry that contains a pointer to
11691181
/// the ud2 instruction corresponding to the bug, corresponding file name (both
11701182
/// pointers use PC relative offset addressing), line number, and flags.
11711183
/// The definition of the struct bug_entry can be found in
1172-
/// `include/asm-generic/bug.h`
1173-
///
1174-
/// NB: find_bug() uses linear search to match an address to an entry in the bug
1175-
/// table. Hence there is no need to sort entries when rewriting the table.
1184+
/// `include/asm-generic/bug.h`. The first entry in the struct is an instruction
1185+
/// address encoded as a PC-relative offset. In theory, it could be an absolute
1186+
/// address if CONFIG_GENERIC_BUG_RELATIVE_POINTERS is not set, but in practice
1187+
/// the kernel code relies on it being a relative offset on x86-64.
11761188
Error LinuxKernelRewriter::readBugTable() {
11771189
BugTableSection = BC.getUniqueSectionByName("__bug_table");
11781190
if (!BugTableSection)
@@ -1215,6 +1227,8 @@ Error LinuxKernelRewriter::readBugTable() {
12151227
" referenced by bug table entry %d",
12161228
InstAddress, EntryID);
12171229
BC.MIB->addAnnotation(*Inst, "BugEntry", EntryID);
1230+
1231+
FunctionBugList[BF].push_back(EntryID);
12181232
}
12191233
}
12201234

@@ -1223,6 +1237,52 @@ Error LinuxKernelRewriter::readBugTable() {
12231237
return Error::success();
12241238
}
12251239

1240+
/// find_bug() uses linear search to match an address to an entry in the bug
1241+
/// table. Hence, there is no need to sort entries when rewriting the table.
1242+
/// When we need to erase an entry, we set its instruction address to zero.
1243+
Error LinuxKernelRewriter::rewriteBugTable() {
1244+
if (!BugTableSection)
1245+
return Error::success();
1246+
1247+
for (BinaryFunction &BF : llvm::make_second_range(BC.getBinaryFunctions())) {
1248+
if (!BC.shouldEmit(BF))
1249+
continue;
1250+
1251+
if (!FunctionBugList.count(&BF))
1252+
continue;
1253+
1254+
// Bugs that will be emitted for this function.
1255+
DenseSet<uint32_t> EmittedIDs;
1256+
for (BinaryBasicBlock &BB : BF) {
1257+
for (MCInst &Inst : BB) {
1258+
if (!BC.MIB->hasAnnotation(Inst, "BugEntry"))
1259+
continue;
1260+
const uint32_t ID = BC.MIB->getAnnotationAs<uint32_t>(Inst, "BugEntry");
1261+
EmittedIDs.insert(ID);
1262+
1263+
// Create a relocation entry for this bug entry.
1264+
MCSymbol *Label =
1265+
BC.MIB->getOrCreateInstLabel(Inst, "__BUG_", BC.Ctx.get());
1266+
const uint64_t EntryOffset = (ID - 1) * BUG_TABLE_ENTRY_SIZE;
1267+
BugTableSection->addRelocation(EntryOffset, Label, ELF::R_X86_64_PC32,
1268+
/*Addend*/ 0);
1269+
}
1270+
}
1271+
1272+
// Clear bug entries that were not emitted for this function, e.g. as a
1273+
// result of DCE, but setting their instruction address to zero.
1274+
for (const uint32_t ID : FunctionBugList[&BF]) {
1275+
if (!EmittedIDs.count(ID)) {
1276+
const uint64_t EntryOffset = (ID - 1) * BUG_TABLE_ENTRY_SIZE;
1277+
BugTableSection->addRelocation(EntryOffset, nullptr, ELF::R_X86_64_PC32,
1278+
/*Addend*/ 0);
1279+
}
1280+
}
1281+
}
1282+
1283+
return Error::success();
1284+
}
1285+
12261286
/// The kernel can replace certain instruction sequences depending on hardware
12271287
/// it is running on and features specified during boot time. The information
12281288
/// about alternative instruction sequences is stored in .altinstructions

bolt/test/X86/linux-bug-table.s

Lines changed: 16 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,21 @@
11
# REQUIRES: system-linux
22

3-
## Check that BOLT correctly parses the Linux kernel __bug_table section.
3+
## Check that BOLT correctly parses and updates the Linux kernel __bug_table
4+
## section.
45

56
# RUN: llvm-mc -filetype=obj -triple x86_64-unknown-unknown %s -o %t.o
67
# RUN: %clang %cflags -nostdlib %t.o -o %t.exe \
78
# RUN: -Wl,--image-base=0xffffffff80000000,--no-dynamic-linker,--no-eh-frame-hdr,--no-pie
89

910
## Verify bug entry bindings to instructions.
1011

11-
# RUN: llvm-bolt %t.exe --print-normalized -o %t.out | FileCheck %s
12+
# RUN: llvm-bolt %t.exe --print-normalized --print-only=_start -o %t.out \
13+
# RUN: --eliminate-unreachable=1 --bolt-info=0 | FileCheck %s
14+
15+
## Verify bug entry bindings again after unreachable code elimination.
16+
17+
# RUN: llvm-bolt %t.out -o %t.out.1 --print-only=_start --print-normalized \
18+
# RUN: |& FileCheck --check-prefix=CHECK-REOPT %s
1219

1320
# CHECK: BOLT-INFO: Linux kernel binary detected
1421
# CHECK: BOLT-INFO: parsed 2 bug table entries
@@ -17,17 +24,21 @@
1724
.globl _start
1825
.type _start, %function
1926
_start:
20-
# CHECK: Binary Function "_start"
21-
nop
27+
jmp .L1
2228
.L0:
2329
ud2
2430
# CHECK: ud2
2531
# CHECK-SAME: BugEntry: 1
26-
nop
2732
.L1:
2833
ud2
2934
# CHECK: ud2
3035
# CHECK-SAME: BugEntry: 2
36+
37+
## Only the second entry should remain after the first pass.
38+
39+
# CHECK-REOPT: ud2
40+
# CHECK-REOPT-SAME: BugEntry: 2
41+
3142
ret
3243
.size _start, .-_start
3344

0 commit comments

Comments
 (0)