Skip to content

Commit

Permalink
[BOLT] Detect Linux kernel version if the binary is a Linux kernel (#…
Browse files Browse the repository at this point in the history
…119088)

This makes it easier to handle differences (e.g. of exception table
entry size) between versions of Linux kernel
  • Loading branch information
FLZ101 authored Dec 26, 2024
1 parent 8906b7b commit 6e8a1a4
Show file tree
Hide file tree
Showing 13 changed files with 199 additions and 0 deletions.
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"
#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

0 comments on commit 6e8a1a4

Please sign in to comment.