Skip to content

Commit f633f32

Browse files
authored
[BOLT] Fix NOP instruction emission on x86 (#72186)
Use MCAsmBackend::writeNopData() interface to emit NOP instructions on x86. There are multiple forms of NOP instruction on x86 with different sizes. Currently, LLVM's assembly/disassembly does not support all forms correctly which can lead to a breakage of input code semantics, e.g. if the program relies on NOP instructions for reserving a patch space. Add "--keep-nops" option to preserve NOP instructions.
1 parent d29d4cb commit f633f32

File tree

6 files changed

+95
-1
lines changed

6 files changed

+95
-1
lines changed

bolt/include/bolt/Core/BinaryFunction.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1296,7 +1296,7 @@ class BinaryFunction {
12961296
/// Return true if the function body is non-contiguous.
12971297
bool isSplit() const { return isSimple() && getLayout().isSplit(); }
12981298

1299-
bool shouldPreserveNops() const { return PreserveNops; }
1299+
bool shouldPreserveNops() const;
13001300

13011301
/// Return true if the function has exception handling tables.
13021302
bool hasEHRanges() const { return HasEHRanges; }

bolt/lib/Core/BinaryEmitter.cpp

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -507,6 +507,18 @@ void BinaryEmitter::emitFunctionBody(BinaryFunction &BF, FunctionFragment &FF,
507507
Streamer.emitLabel(InstrLabel);
508508
}
509509

510+
// Emit sized NOPs via MCAsmBackend::writeNopData() interface on x86.
511+
// This is a workaround for invalid NOPs handling by asm/disasm layer.
512+
if (BC.MIB->isNoop(Instr) && BC.isX86()) {
513+
if (std::optional<uint32_t> Size = BC.MIB->getSize(Instr)) {
514+
SmallString<15> Code;
515+
raw_svector_ostream VecOS(Code);
516+
BC.MAB->writeNopData(VecOS, *Size, BC.STI.get());
517+
Streamer.emitBytes(Code);
518+
continue;
519+
}
520+
}
521+
510522
Streamer.emitInstruction(Instr, *BC.STI);
511523
LastIsPrefix = BC.MIB->isPrefix(Instr);
512524
}

bolt/lib/Core/BinaryFunction.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@ extern cl::OptionCategory BoltRelocCategory;
5858

5959
extern cl::opt<bool> EnableBAT;
6060
extern cl::opt<bool> Instrument;
61+
extern cl::opt<bool> KeepNops;
6162
extern cl::opt<bool> StrictMode;
6263
extern cl::opt<bool> UpdateDebugSections;
6364
extern cl::opt<unsigned> Verbosity;
@@ -4366,6 +4367,10 @@ MCInst *BinaryFunction::getInstructionAtOffset(uint64_t Offset) {
43664367
}
43674368
}
43684369

4370+
bool BinaryFunction::shouldPreserveNops() const {
4371+
return PreserveNops || opts::KeepNops;
4372+
}
4373+
43694374
void BinaryFunction::printLoopInfo(raw_ostream &OS) const {
43704375
if (!opts::shouldPrint(*this))
43714376
return;

bolt/lib/Passes/BinaryPasses.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -608,12 +608,15 @@ void LowerAnnotations::runOnFunctions(BinaryContext &BC) {
608608
std::optional<uint32_t> Offset = BF->requiresAddressTranslation()
609609
? BC.MIB->getOffset(*II)
610610
: std::nullopt;
611+
std::optional<uint32_t> Size = BC.MIB->getSize(*II);
611612
MCSymbol *Label = BC.MIB->getLabel(*II);
612613

613614
BC.MIB->stripAnnotations(*II);
614615

615616
if (Offset)
616617
BC.MIB->setOffset(*II, *Offset);
618+
if (Size)
619+
BC.MIB->setSize(*II, *Size);
617620
if (Label)
618621
BC.MIB->setLabel(*II, Label);
619622
}

bolt/lib/Utils/CommandLineOpts.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,11 @@ cl::opt<bool>
129129
cl::desc("instrument code to generate accurate profile data"),
130130
cl::cat(BoltOptCategory));
131131

132+
cl::opt<bool>
133+
KeepNops("keep-nops",
134+
cl::desc("keep no-op instructions. By default they are removed."),
135+
cl::Hidden, cl::cat(BoltOptCategory));
136+
132137
cl::opt<std::string>
133138
OutputFilename("o",
134139
cl::desc("<output file>"),

bolt/test/X86/keep-nops.s

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
## Check that BOLT preserves NOP instructions of different sizes correctly.
2+
3+
# REQUIRES: system-linux
4+
5+
# RUN: llvm-mc -filetype=obj -triple x86_64-unknown-linux %s -o %t.o
6+
# RUN: ld.lld %t.o -o %t.exe -q
7+
# RUN: llvm-bolt %t.exe -o %t.bolt.exe --keep-nops --relocs --print-finalized \
8+
# RUN: |& FileCheck --check-prefix=CHECK-BOLT %s
9+
# RUN: llvm-objdump -d %t.bolt.exe | FileCheck %s
10+
11+
.text
12+
.globl _start
13+
.type _start,@function
14+
_start:
15+
.cfi_startproc
16+
.nops 1
17+
.nops 2
18+
.nops 3
19+
.nops 4
20+
.nops 5
21+
.nops 6
22+
.nops 7
23+
.nops 8
24+
.nops 9
25+
.nops 10
26+
.nops 11
27+
.nops 12
28+
.nops 13
29+
.nops 14
30+
.nops 15
31+
32+
# CHECK: <_start>:
33+
# CHECK-NEXT: 90
34+
# CHECK-NEXT: 66 90
35+
# CHECK-NEXT: 0f 1f 00
36+
# CHECK-NEXT: 0f 1f 40 00
37+
# CHECK-NEXT: 0f 1f 44 00 00
38+
# CHECK-NEXT: 66 0f 1f 44 00 00
39+
# CHECK-NEXT: 0f 1f 80 00 00 00 00
40+
# CHECK-NEXT: 0f 1f 84 00 00 00 00 00
41+
# CHECK-NEXT: 66 0f 1f 84 00 00 00 00 00
42+
# CHECK-NEXT: 66 2e 0f 1f 84 00 00 00 00 00
43+
# CHECK-NEXT: 66 66 2e 0f 1f 84 00 00 00 00 00
44+
# CHECK-NEXT: 66 66 66 2e 0f 1f 84 00 00 00 00 00
45+
# CHECK-NEXT: 66 66 66 66 2e 0f 1f 84 00 00 00 00 00
46+
# CHECK-NEXT: 66 66 66 66 66 2e 0f 1f 84 00 00 00 00 00
47+
# CHECK-NEXT: 66 66 66 66 66 66 2e 0f 1f 84 00 00 00 00 00
48+
49+
# CHECK-BOLT: Size: 1
50+
# CHECK-BOLT-NEXT: Size: 2
51+
# CHECK-BOLT-NEXT: Size: 3
52+
# CHECK-BOLT-NEXT: Size: 4
53+
# CHECK-BOLT-NEXT: Size: 5
54+
# CHECK-BOLT-NEXT: Size: 6
55+
# CHECK-BOLT-NEXT: Size: 7
56+
# CHECK-BOLT-NEXT: Size: 8
57+
# CHECK-BOLT-NEXT: Size: 9
58+
# CHECK-BOLT-NEXT: Size: 10
59+
# CHECK-BOLT-NEXT: Size: 11
60+
# CHECK-BOLT-NEXT: Size: 12
61+
# CHECK-BOLT-NEXT: Size: 13
62+
# CHECK-BOLT-NEXT: Size: 14
63+
# CHECK-BOLT-NEXT: Size: 15
64+
65+
# Needed for relocation mode.
66+
.reloc 0, R_X86_64_NONE
67+
68+
.size _start, .-_start
69+
.cfi_endproc

0 commit comments

Comments
 (0)