[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:
@@ -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) {
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
53
bolt/test/X86/linux-version.S
Normal file
53
bolt/test/X86/linux-version.S
Normal 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
|
||||
Reference in New Issue
Block a user