[BOLT] Detect Linux kernel version if the binary is a Linux kernel (#119088)

This makes it easier to handle differences (e.g. of exception table
entry size) between versions of Linux kernel
This commit is contained in:
Franklin
2024-12-27 01:54:23 +08:00
committed by GitHub
parent 8906b7be91
commit 6e8a1a45a7
13 changed files with 199 additions and 0 deletions

View File

@@ -169,6 +169,11 @@ public:
return Parent && (Parent == BD || Parent->isAncestorOf(BD));
}
void updateSize(uint64_t N) {
if (N > Size)
Size = N;
}
void setIsMoveable(bool Flag) { IsMoveable = Flag; }
void setSection(BinarySection &NewSection);
void setOutputSection(BinarySection &NewSection) {

View File

@@ -1076,6 +1076,7 @@ MCSymbol *BinaryContext::registerNameAtAddress(StringRef Name, uint64_t Address,
BD = GAI->second;
if (!BD->hasName(Name)) {
GlobalSymbols[Name] = BD;
BD->updateSize(Size);
BD->Symbols.push_back(Symbol);
}
}

View File

@@ -21,6 +21,8 @@
#include "llvm/Support/CommandLine.h"
#include "llvm/Support/Debug.h"
#include "llvm/Support/Errc.h"
#include "llvm/Support/ErrorOr.h"
#include <regex>
#define DEBUG_TYPE "bolt-linux"
@@ -89,6 +91,34 @@ static cl::opt<bool>
} // namespace opts
/// Linux kernel version
struct LKVersion {
LKVersion() {}
LKVersion(unsigned Major, unsigned Minor, unsigned Rev)
: Major(Major), Minor(Minor), Rev(Rev) {}
bool operator<(const LKVersion &Other) const {
return std::make_tuple(Major, Minor, Rev) <
std::make_tuple(Other.Major, Other.Minor, Other.Rev);
}
bool operator>(const LKVersion &Other) const { return Other < *this; }
bool operator<=(const LKVersion &Other) const { return !(*this > Other); }
bool operator>=(const LKVersion &Other) const { return !(*this < Other); }
bool operator==(const LKVersion &Other) const {
return Major == Other.Major && Minor == Other.Minor && Rev == Other.Rev;
}
bool operator!=(const LKVersion &Other) const { return !(*this == Other); }
unsigned Major{0};
unsigned Minor{0};
unsigned Rev{0};
};
/// Linux Kernel supports stack unwinding using ORC (oops rewind capability).
/// ORC state at every IP can be described by the following data structure.
struct ORCState {
@@ -148,6 +178,8 @@ public:
};
class LinuxKernelRewriter final : public MetadataRewriter {
LKVersion LinuxKernelVersion;
/// Information required for updating metadata referencing an instruction.
struct InstructionFixup {
BinarySection &Section; // Section referencing the instruction.
@@ -249,6 +281,8 @@ class LinuxKernelRewriter final : public MetadataRewriter {
ErrorOr<BinarySection &> PCIFixupSection = std::errc::bad_address;
static constexpr size_t PCI_FIXUP_ENTRY_SIZE = 16;
Error detectLinuxKernelVersion();
/// Process linux kernel special sections and their relocations.
void processLKSections();
@@ -314,6 +348,9 @@ public:
: MetadataRewriter("linux-kernel-rewriter", BC) {}
Error preCFGInitializer() override {
if (Error E = detectLinuxKernelVersion())
return E;
processLKSections();
if (Error E = processSMPLocks())
@@ -394,6 +431,28 @@ public:
}
};
Error LinuxKernelRewriter::detectLinuxKernelVersion() {
if (BinaryData *BD = BC.getBinaryDataByName("linux_banner")) {
const BinarySection &Section = BD->getSection();
const std::string S =
Section.getContents().substr(BD->getOffset(), BD->getSize()).str();
const std::regex Re(R"---(Linux version ((\d+)\.(\d+)(\.(\d+))?))---");
std::smatch Match;
if (std::regex_search(S, Match, Re)) {
const unsigned Major = std::stoi(Match[2].str());
const unsigned Minor = std::stoi(Match[3].str());
const unsigned Rev = Match[5].matched ? std::stoi(Match[5].str()) : 0;
LinuxKernelVersion = LKVersion(Major, Minor, Rev);
BC.outs() << "BOLT-INFO: Linux kernel version is " << Match[1].str()
<< "\n";
return Error::success();
}
}
return createStringError(errc::executable_format_error,
"Linux kernel version is unknown");
}
void LinuxKernelRewriter::processLKSections() {
processLKKSymtab();
processLKKSymtab(true);

View File

@@ -142,6 +142,15 @@ _start:
.section .orc_unwind_ip
.long .L0 + 2 - .
## Linux kernel version
.rodata
.align 16
.globl linux_banner
.type linux_banner, @object
linux_banner:
.string "Linux version 6.6.61\n"
.size linux_banner, . - linux_banner
## Fake Linux Kernel sections.
.section __ksymtab,"a",@progbits
.section __ksymtab_gpl,"a",@progbits

View File

@@ -56,6 +56,15 @@ _start:
.long .L1 - . # instruction
.org 2b + 12
## Linux kernel version
.rodata
.align 16
.globl linux_banner
.type linux_banner, @object
linux_banner:
.string "Linux version 6.6.61\n"
.size linux_banner, . - linux_banner
## Fake Linux Kernel sections.
.section __ksymtab,"a",@progbits
.section __ksymtab_gpl,"a",@progbits

View File

@@ -59,6 +59,15 @@ foo:
.long .LF0 - . # fixup
.long 0 # data
## Linux kernel version
.rodata
.align 16
.globl linux_banner
.type linux_banner, @object
linux_banner:
.string "Linux version 6.6.61\n"
.size linux_banner, . - linux_banner
## Fake Linux Kernel sections.
.section __ksymtab,"a",@progbits
.section __ksymtab_gpl,"a",@progbits

View File

@@ -157,6 +157,15 @@ bar:
.section .orc_unwind_ip
.long .L4 - .
## Linux kernel version
.rodata
.align 16
.globl linux_banner
.type linux_banner, @object
linux_banner:
.string "Linux version 6.6.61\n"
.size linux_banner, . - linux_banner
## Fake Linux Kernel sections.
.section __ksymtab,"a",@progbits
.section __ksymtab_gpl,"a",@progbits

View File

@@ -49,6 +49,15 @@ _start:
.byte 1 # type
.byte 7 # length
## Linux kernel version
.rodata
.align 16
.globl linux_banner
.type linux_banner, @object
linux_banner:
.string "Linux version 6.6.61\n"
.size linux_banner, . - linux_banner
## Fake Linux Kernel sections.
.section __ksymtab,"a",@progbits
.section __ksymtab_gpl,"a",@progbits

View File

@@ -36,6 +36,15 @@ _start:
.long 0x0 # class shift
.long .L0 - . # fixup
## Linux kernel version
.rodata
.align 16
.globl linux_banner
.type linux_banner, @object
linux_banner:
.string "Linux version 6.6.61\n"
.size linux_banner, . - linux_banner
## Fake Linux Kernel sections.
.section __ksymtab,"a",@progbits
.section __ksymtab_gpl,"a",@progbits

View File

@@ -35,6 +35,15 @@ _start:
.long .L0 - .
.long .L1 - .
## Linux kernel version
.rodata
.align 16
.globl linux_banner
.type linux_banner, @object
linux_banner:
.string "Linux version 6.6.61\n"
.size linux_banner, . - linux_banner
## Fake Linux Kernel sections.
.section __ksymtab,"a",@progbits
.section __ksymtab_gpl,"a",@progbits

View File

@@ -54,6 +54,15 @@ __start_static_call_sites:
.type __stop_static_call_sites, %object
__stop_static_call_sites:
## Linux kernel version
.rodata
.align 16
.globl linux_banner
.type linux_banner, @object
linux_banner:
.string "Linux version 6.6.61\n"
.size linux_banner, . - linux_banner
## Fake Linux Kernel sections.
.section __ksymtab,"a",@progbits
.section __ksymtab_gpl,"a",@progbits

View File

@@ -85,6 +85,15 @@ __stop___jump_table:
fake_static_key:
.quad 0
## Linux kernel version
.rodata
.align 16
.globl linux_banner
.type linux_banner, @object
linux_banner:
.string "Linux version 6.6.61\n"
.size linux_banner, . - linux_banner
## Fake Linux Kernel sections.
.section __ksymtab,"a",@progbits
.section __ksymtab_gpl,"a",@progbits

View File

@@ -0,0 +1,53 @@
# REQUIRES: system-linux
## Check that BOLT correctly detects the Linux kernel version
# RUN: %clang -DA -target x86_64-unknown-unknown \
# RUN: %cflags -nostdlib %s -o %t.exe \
# RUN: -Wl,--image-base=0xffffffff80000000,--no-dynamic-linker,--no-eh-frame-hdr
# RUN: llvm-bolt %t.exe -o %t.out 2>&1 | FileCheck --check-prefix=CHECK-A %s
# RUN: %clang -DB -target x86_64-unknown-unknown \
# RUN: %cflags -nostdlib %s -o %t.exe \
# RUN: -Wl,--image-base=0xffffffff80000000,--no-dynamic-linker,--no-eh-frame-hdr
# RUN: llvm-bolt %t.exe -o %t.out 2>&1 | FileCheck --check-prefix=CHECK-B %s
# RUN: %clang -DC -target x86_64-unknown-unknown \
# RUN: %cflags -nostdlib %s -o %t.exe \
# RUN: -Wl,--image-base=0xffffffff80000000,--no-dynamic-linker,--no-eh-frame-hdr
# RUN: llvm-bolt %t.exe -o %t.out 2>&1 | FileCheck --check-prefix=CHECK-C %s
.text
.globl foo
.type foo, %function
foo:
ret
.size foo, .-foo
## Linux kernel version
.rodata
.align 16
.globl linux_banner
.type linux_banner, @object
linux_banner:
#ifdef A
.string "Linux version 6.6.61\n"
#endif
# CHECK-A: BOLT-INFO: Linux kernel version is 6.6.61
#ifdef B
.string "Linux version 6.6.50-rc4\n"
#endif
# CHECK-B: BOLT-INFO: Linux kernel version is 6.6.50
#ifdef C
.string "Linux version 6.6\n"
#endif
# CHECK-C: BOLT-INFO: Linux kernel version is 6.6
.size linux_banner, . - linux_banner
## Fake Linux Kernel sections.
.section __ksymtab,"a",@progbits
.section __ksymtab_gpl,"a",@progbits