mirror of
https://github.com/intel/llvm.git
synced 2026-01-13 19:08:21 +08:00
[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:
@@ -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>
|
||||
|
||||
46
bolt/test/X86/linux-bug-table.s
Normal file
46
bolt/test/X86/linux-bug-table.s
Normal 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
|
||||
Reference in New Issue
Block a user