Skip to content

Commit d16b21b

Browse files
authored
[BOLT][Linux] Support ORC for alternative instructions (llvm#96709)
Alternative instruction sequences in the Linux kernel can modify the stack and thus they need their own ORC unwind entries. Since there's only one ORC table, it has to be "shared" among multiple instruction sequences. The kernel achieves this by putting a restriction on instruction boundaries. If ORC state changes at a given IP, only one of the alternative sequences can have an instruction starting/ending at this IP. Then, developers can insert NOPs to guarantee the above requirement is met. The most common use of ORC with alternatives is "pushf; pop %rax" sequence used for paravirtualization. Note that newer kernel versions no longer use .parainstructions; instead, they utilize alternatives for the same purpose. Before we implement a better support for alternatives, we can safely skip ORC entries associated with them. Fixes llvm#87052.
1 parent 7423bf7 commit d16b21b

File tree

4 files changed

+88
-8
lines changed

4 files changed

+88
-8
lines changed

bolt/include/bolt/Core/BinaryFunction.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -930,6 +930,10 @@ class BinaryFunction {
930930
return const_cast<BinaryFunction *>(this)->getInstructionAtOffset(Offset);
931931
}
932932

933+
/// When the function is in disassembled state, return an instruction that
934+
/// contains the \p Offset.
935+
MCInst *getInstructionContainingOffset(uint64_t Offset);
936+
933937
std::optional<MCInst> disassembleInstructionAtOffset(uint64_t Offset) const;
934938

935939
/// Return offset for the first instruction. If there is data at the

bolt/lib/Core/BinaryFunction.cpp

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4472,6 +4472,18 @@ MCInst *BinaryFunction::getInstructionAtOffset(uint64_t Offset) {
44724472
}
44734473
}
44744474

4475+
MCInst *BinaryFunction::getInstructionContainingOffset(uint64_t Offset) {
4476+
assert(CurrentState == State::Disassembled && "Wrong function state");
4477+
4478+
if (Offset > Size)
4479+
return nullptr;
4480+
4481+
auto II = Instructions.upper_bound(Offset);
4482+
assert(II != Instructions.begin() && "First instruction not at offset 0");
4483+
--II;
4484+
return &II->second;
4485+
}
4486+
44754487
void BinaryFunction::printLoopInfo(raw_ostream &OS) const {
44764488
if (!opts::shouldPrint(*this))
44774489
return;

bolt/lib/Rewrite/LinuxKernelRewriter.cpp

Lines changed: 25 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -295,9 +295,6 @@ class LinuxKernelRewriter final : public MetadataRewriter {
295295
if (Error E = processSMPLocks())
296296
return E;
297297

298-
if (Error E = readORCTables())
299-
return E;
300-
301298
if (Error E = readStaticCalls())
302299
return E;
303300

@@ -313,6 +310,11 @@ class LinuxKernelRewriter final : public MetadataRewriter {
313310
if (Error E = readAltInstructions())
314311
return E;
315312

313+
// Some ORC entries could be linked to alternative instruction
314+
// sequences. Hence, we read ORC after .altinstructions.
315+
if (Error E = readORCTables())
316+
return E;
317+
316318
if (Error E = readPCIFixupTable())
317319
return E;
318320

@@ -563,11 +565,28 @@ Error LinuxKernelRewriter::readORCTables() {
563565
if (!BF->hasInstructions())
564566
continue;
565567

566-
MCInst *Inst = BF->getInstructionAtOffset(IP - BF->getAddress());
567-
if (!Inst)
568+
const uint64_t Offset = IP - BF->getAddress();
569+
MCInst *Inst = BF->getInstructionAtOffset(Offset);
570+
if (!Inst) {
571+
// Check if there is an alternative instruction(s) at this IP. Multiple
572+
// alternative instructions can take a place of a single original
573+
// instruction and each alternative can have a separate ORC entry.
574+
// Since ORC table is shared between all alternative sequences, there's
575+
// a requirement that only one (out of many) sequences can have an
576+
// instruction from the ORC table to avoid ambiguities/conflicts.
577+
//
578+
// For now, we have limited support for alternatives. I.e. we still print
579+
// functions with them, but will not change the code in the output binary.
580+
// As such, we can ignore alternative ORC entries. They will be preserved
581+
// in the binary, but will not get printed in the instruction stream.
582+
Inst = BF->getInstructionContainingOffset(Offset);
583+
if (Inst || BC.MIB->hasAnnotation(*Inst, "AltInst"))
584+
continue;
585+
568586
return createStringError(
569587
errc::executable_format_error,
570588
"no instruction at address 0x%" PRIx64 " in .orc_unwind_ip", IP);
589+
}
571590

572591
// Some addresses will have two entries associated with them. The first
573592
// one being a "weak" section terminator. Since we ignore the terminator,
@@ -1440,7 +1459,7 @@ Error LinuxKernelRewriter::tryReadAltInstructions(uint32_t AltInstFeatureSize,
14401459
AltBF->setIgnored();
14411460
}
14421461

1443-
if (!BF || !BC.shouldEmit(*BF))
1462+
if (!BF || !BF->hasInstructions())
14441463
continue;
14451464

14461465
if (OrgInstAddress + OrgSize > BF->getAddress() + BF->getSize())

bolt/test/X86/linux-alt-instruction.s

Lines changed: 47 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@
3838
# RUN: llvm-bolt %t.exe --print-cfg -o %t.fs4.out | FileCheck %s
3939

4040
# CHECK: BOLT-INFO: Linux kernel binary detected
41-
# CHECK: BOLT-INFO: parsed 2 alternative instruction entries
41+
# CHECK: BOLT-INFO: parsed 3 alternative instruction entries
4242

4343
.text
4444
.globl _start
@@ -50,10 +50,12 @@ _start:
5050
# CHECK: rdtsc
5151
# CHECK-SAME: AltInst: 1
5252
# CHECK-SAME: AltInst2: 2
53+
# CHECK-SAME: AltInst3: 3
5354
nop
5455
# CHECK-NEXT: nop
5556
# CHECK-SAME: AltInst: 1
5657
# CHECK-SAME: AltInst2: 2
58+
# CHECK-SAME: AltInst3: 3
5759
nop
5860
nop
5961
.L1:
@@ -66,6 +68,9 @@ _start:
6668
rdtsc
6769
.A1:
6870
rdtscp
71+
.A2:
72+
pushf
73+
pop %rax
6974
.Ae:
7075

7176
## Alternative instruction info.
@@ -92,11 +97,51 @@ _start:
9297
.word 0x3b # feature flags
9398
.endif
9499
.byte .L1 - .L0 # org size
95-
.byte .Ae - .A1 # alt size
100+
.byte .A2 - .A1 # alt size
96101
.ifdef PADLEN
97102
.byte 0
98103
.endif
99104

105+
.long .L0 - . # org instruction
106+
.long .A2 - . # alt instruction
107+
.ifdef FEATURE_SIZE_4
108+
.long 0x110 # feature flags
109+
.else
110+
.word 0x110 # feature flags
111+
.endif
112+
.byte .L1 - .L0 # org size
113+
.byte .Ae - .A2 # alt size
114+
.ifdef PADLEN
115+
.byte 0
116+
.endif
117+
118+
## ORC unwind for "pushf; pop %rax" alternative sequence.
119+
.section .orc_unwind,"a",@progbits
120+
.align 4
121+
.section .orc_unwind_ip,"a",@progbits
122+
.align 4
123+
124+
.section .orc_unwind
125+
.2byte 8
126+
.2byte 0
127+
.2byte 0x205
128+
.section .orc_unwind_ip
129+
.long _start - .
130+
131+
.section .orc_unwind
132+
.2byte 16
133+
.2byte 0
134+
.2byte 0x205
135+
.section .orc_unwind_ip
136+
.long .L0 + 1 - .
137+
138+
.section .orc_unwind
139+
.2byte 8
140+
.2byte 0
141+
.2byte 0x205
142+
.section .orc_unwind_ip
143+
.long .L0 + 2 - .
144+
100145
## Fake Linux Kernel sections.
101146
.section __ksymtab,"a",@progbits
102147
.section __ksymtab_gpl,"a",@progbits

0 commit comments

Comments
 (0)