Skip to content

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

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 8 commits into from
Dec 26, 2024
5 changes: 5 additions & 0 deletions bolt/include/bolt/Core/BinaryData.h
Original file line number Diff line number Diff line change
Expand Up @@ -169,6 +169,11 @@ class BinaryData {
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) {
Expand Down
1 change: 1 addition & 0 deletions bolt/lib/Core/BinaryContext.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
}
Expand Down
59 changes: 59 additions & 0 deletions bolt/lib/Rewrite/LinuxKernelRewriter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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"

Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -148,6 +178,8 @@ class AddressExtractor : public DataExtractor {
};

class LinuxKernelRewriter final : public MetadataRewriter {
LKVersion LinuxKernelVersion;

/// Information required for updating metadata referencing an instruction.
struct InstructionFixup {
BinarySection &Section; // Section referencing the instruction.
Expand Down Expand Up @@ -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();

Expand Down Expand Up @@ -314,6 +348,9 @@ class LinuxKernelRewriter final : public MetadataRewriter {
: MetadataRewriter("linux-kernel-rewriter", BC) {}

Error preCFGInitializer() override {
if (Error E = detectLinuxKernelVersion())
return E;

processLKSections();

if (Error E = processSMPLocks())
Expand Down Expand Up @@ -394,6 +431,28 @@ class LinuxKernelRewriter final : public MetadataRewriter {
}
};

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);
Expand Down
9 changes: 9 additions & 0 deletions bolt/test/X86/linux-alt-instruction.s
Original file line number Diff line number Diff line change
Expand Up @@ -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
9 changes: 9 additions & 0 deletions bolt/test/X86/linux-bug-table.s
Original file line number Diff line number Diff line change
Expand Up @@ -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
9 changes: 9 additions & 0 deletions bolt/test/X86/linux-exceptions.s
Original file line number Diff line number Diff line change
Expand Up @@ -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
9 changes: 9 additions & 0 deletions bolt/test/X86/linux-orc.s
Original file line number Diff line number Diff line change
Expand Up @@ -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
9 changes: 9 additions & 0 deletions bolt/test/X86/linux-parainstructions.s
Original file line number Diff line number Diff line change
Expand Up @@ -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
9 changes: 9 additions & 0 deletions bolt/test/X86/linux-pci-fixup.s
Original file line number Diff line number Diff line change
Expand Up @@ -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
9 changes: 9 additions & 0 deletions bolt/test/X86/linux-smp-locks.s
Original file line number Diff line number Diff line change
Expand Up @@ -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
9 changes: 9 additions & 0 deletions bolt/test/X86/linux-static-calls.s
Original file line number Diff line number Diff line change
Expand Up @@ -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
9 changes: 9 additions & 0 deletions bolt/test/X86/linux-static-keys.s
Original file line number Diff line number Diff line change
Expand Up @@ -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 changes: 53 additions & 0 deletions bolt/test/X86/linux-version.S
Original file line number Diff line number Diff line change
@@ -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"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: could you please place CHECKs next to the object you are checking against?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

#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
Loading