Skip to content

Commit 0262979

Browse files
authored
[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.
1 parent eae4f56 commit 0262979

File tree

2 files changed

+116
-35
lines changed

2 files changed

+116
-35
lines changed

bolt/lib/Rewrite/LinuxKernelRewriter.cpp

Lines changed: 70 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -157,6 +157,12 @@ class LinuxKernelRewriter final : public MetadataRewriter {
157157
/// Alignment of paravirtual patch structures.
158158
static constexpr size_t PARA_PATCH_ALIGN = 8;
159159

160+
/// Section containing Linux bug table.
161+
ErrorOr<BinarySection &> BugTableSection = std::errc::bad_address;
162+
163+
/// Size of bug_entry struct.
164+
static constexpr size_t BUG_TABLE_ENTRY_SIZE = 12;
165+
160166
/// Insert an LKMarker for a given code pointer \p PC from a non-code section
161167
/// \p SectionName.
162168
void insertLKMarker(uint64_t PC, uint64_t SectionOffset,
@@ -172,9 +178,6 @@ class LinuxKernelRewriter final : public MetadataRewriter {
172178
/// Process __ksymtab and __ksymtab_gpl.
173179
void processLKKSymtab(bool IsGPL = false);
174180

175-
/// Process special linux kernel section, __bug_table.
176-
void processLKBugTable();
177-
178181
/// Process special linux kernel section, .smp_locks.
179182
void processLKSMPLocks();
180183

@@ -200,6 +203,8 @@ class LinuxKernelRewriter final : public MetadataRewriter {
200203
/// Paravirtual instruction patch sites.
201204
Error readParaInstructions();
202205

206+
Error readBugTable();
207+
203208
/// Mark instructions referenced by kernel metadata.
204209
Error markInstructions();
205210

@@ -224,6 +229,9 @@ class LinuxKernelRewriter final : public MetadataRewriter {
224229
if (Error E = readParaInstructions())
225230
return E;
226231

232+
if (Error E = readBugTable())
233+
return E;
234+
227235
return Error::success();
228236
}
229237

@@ -289,7 +297,6 @@ void LinuxKernelRewriter::processLKSections() {
289297
processLKPCIFixup();
290298
processLKKSymtab();
291299
processLKKSymtab(true);
292-
processLKBugTable();
293300
processLKSMPLocks();
294301
}
295302

@@ -356,37 +363,6 @@ void LinuxKernelRewriter::processLKKSymtab(bool IsGPL) {
356363
}
357364
}
358365

359-
/// Process __bug_table section.
360-
/// This section contains information useful for kernel debugging.
361-
/// Each entry in the section is a struct bug_entry that contains a pointer to
362-
/// the ud2 instruction corresponding to the bug, corresponding file name (both
363-
/// pointers use PC relative offset addressing), line number, and flags.
364-
/// The definition of the struct bug_entry can be found in
365-
/// `include/asm-generic/bug.h`
366-
void LinuxKernelRewriter::processLKBugTable() {
367-
ErrorOr<BinarySection &> SectionOrError =
368-
BC.getUniqueSectionByName("__bug_table");
369-
if (!SectionOrError)
370-
return;
371-
372-
const uint64_t SectionSize = SectionOrError->getSize();
373-
const uint64_t SectionAddress = SectionOrError->getAddress();
374-
assert((SectionSize % 12) == 0 &&
375-
"The size of the __bug_table section should be a multiple of 12");
376-
for (uint64_t I = 0; I < SectionSize; I += 12) {
377-
const uint64_t EntryAddress = SectionAddress + I;
378-
ErrorOr<uint64_t> Offset = BC.getSignedValueAtAddress(EntryAddress, 4);
379-
assert(Offset &&
380-
"Reading valid PC-relative offset for a __bug_table entry");
381-
const int32_t SignedOffset = *Offset;
382-
const uint64_t RefAddress = EntryAddress + SignedOffset;
383-
assert(BC.getBinaryFunctionContainingAddress(RefAddress) &&
384-
"__bug_table entries should point to a function");
385-
386-
insertLKMarker(RefAddress, I, SignedOffset, true, "__bug_table");
387-
}
388-
}
389-
390366
/// .smp_locks section contains PC-relative references to instructions with LOCK
391367
/// prefix. The prefix can be converted to NOP at boot time on non-SMP systems.
392368
void LinuxKernelRewriter::processLKSMPLocks() {
@@ -1097,6 +1073,65 @@ Error LinuxKernelRewriter::readParaInstructions() {
10971073
return Error::success();
10981074
}
10991075

1076+
/// Process __bug_table section.
1077+
/// This section contains information useful for kernel debugging.
1078+
/// Each entry in the section is a struct bug_entry that contains a pointer to
1079+
/// the ud2 instruction corresponding to the bug, corresponding file name (both
1080+
/// pointers use PC relative offset addressing), line number, and flags.
1081+
/// The definition of the struct bug_entry can be found in
1082+
/// `include/asm-generic/bug.h`
1083+
///
1084+
/// NB: find_bug() uses linear search to match an address to an entry in the bug
1085+
/// table. Hence there is no need to sort entries when rewriting the table.
1086+
Error LinuxKernelRewriter::readBugTable() {
1087+
BugTableSection = BC.getUniqueSectionByName("__bug_table");
1088+
if (!BugTableSection)
1089+
return Error::success();
1090+
1091+
if (BugTableSection->getSize() % BUG_TABLE_ENTRY_SIZE)
1092+
return createStringError(errc::executable_format_error,
1093+
"bug table size error");
1094+
1095+
const uint64_t SectionAddress = BugTableSection->getAddress();
1096+
DataExtractor DE(BugTableSection->getContents(), BC.AsmInfo->isLittleEndian(),
1097+
BC.AsmInfo->getCodePointerSize());
1098+
DataExtractor::Cursor Cursor(0);
1099+
uint32_t EntryID = 0;
1100+
while (Cursor && Cursor.tell() < BugTableSection->getSize()) {
1101+
const uint64_t Pos = Cursor.tell();
1102+
const uint64_t InstAddress =
1103+
SectionAddress + Pos + (int32_t)DE.getU32(Cursor);
1104+
Cursor.seek(Pos + BUG_TABLE_ENTRY_SIZE);
1105+
1106+
if (!Cursor)
1107+
return createStringError(errc::executable_format_error,
1108+
"out of bounds while reading __bug_table");
1109+
1110+
++EntryID;
1111+
1112+
BinaryFunction *BF = BC.getBinaryFunctionContainingAddress(InstAddress);
1113+
if (!BF && opts::Verbosity) {
1114+
BC.outs() << "BOLT-INFO: no function matches address 0x"
1115+
<< Twine::utohexstr(InstAddress)
1116+
<< " referenced by bug table\n";
1117+
}
1118+
1119+
if (BF && BC.shouldEmit(*BF)) {
1120+
MCInst *Inst = BF->getInstructionAtOffset(InstAddress - BF->getAddress());
1121+
if (!Inst)
1122+
return createStringError(errc::executable_format_error,
1123+
"no instruction at address 0x%" PRIx64
1124+
" referenced by bug table entry %d",
1125+
InstAddress, EntryID);
1126+
BC.MIB->addAnnotation(*Inst, "BugEntry", EntryID);
1127+
}
1128+
}
1129+
1130+
BC.outs() << "BOLT-INFO: parsed " << EntryID << " bug table entries\n";
1131+
1132+
return Error::success();
1133+
}
1134+
11001135
} // namespace
11011136

11021137
std::unique_ptr<MetadataRewriter>

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

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
# REQUIRES: system-linux
2+
3+
## Check that BOLT correctly parses the Linux kernel __bug_table section.
4+
5+
# RUN: llvm-mc -filetype=obj -triple x86_64-unknown-unknown %s -o %t.o
6+
# RUN: %clang %cflags -nostdlib %t.o -o %t.exe \
7+
# RUN: -Wl,--image-base=0xffffffff80000000,--no-dynamic-linker,--no-eh-frame-hdr,--no-pie
8+
9+
## Verify bug entry bindings to instructions.
10+
11+
# RUN: llvm-bolt %t.exe --print-normalized -o %t.out | FileCheck %s
12+
13+
# CHECK: BOLT-INFO: Linux kernel binary detected
14+
# CHECK: BOLT-INFO: parsed 2 bug table entries
15+
16+
.text
17+
.globl _start
18+
.type _start, %function
19+
_start:
20+
# CHECK: Binary Function "_start"
21+
nop
22+
.L0:
23+
ud2
24+
# CHECK: ud2
25+
# CHECK-SAME: BugEntry: 1
26+
nop
27+
.L1:
28+
ud2
29+
# CHECK: ud2
30+
# CHECK-SAME: BugEntry: 2
31+
ret
32+
.size _start, .-_start
33+
34+
35+
## Bug table.
36+
.section __bug_table,"a",@progbits
37+
1:
38+
.long .L0 - . # instruction
39+
.org 1b + 12
40+
2:
41+
.long .L1 - . # instruction
42+
.org 2b + 12
43+
44+
## Fake Linux Kernel sections.
45+
.section __ksymtab,"a",@progbits
46+
.section __ksymtab_gpl,"a",@progbits

0 commit comments

Comments
 (0)