Skip to content

Commit 143afb4

Browse files
authored
[BOLT] Add reading support for Linux kernel .altinstructions section (llvm#84283)
Read .altinstructions and annotate instructions that have alternative sequences with "AltInst" annotation. Note that some instructions may have more than one alternatives, in which case they will have multiple annotations in the form "AltInst", "AltInst2", "AltInst3", etc.
1 parent 641b98a commit 143afb4

File tree

2 files changed

+233
-0
lines changed

2 files changed

+233
-0
lines changed

bolt/lib/Rewrite/LinuxKernelRewriter.cpp

Lines changed: 141 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,21 @@ using namespace bolt;
2727

2828
namespace opts {
2929

30+
static cl::opt<bool>
31+
AltInstHasPadLen("alt-inst-has-padlen",
32+
cl::desc("specify that .altinstructions has padlen field"),
33+
cl::init(false), cl::Hidden, cl::cat(BoltCategory));
34+
35+
static cl::opt<uint32_t>
36+
AltInstFeatureSize("alt-inst-feature-size",
37+
cl::desc("size of feature field in .altinstructions"),
38+
cl::init(2), cl::Hidden, cl::cat(BoltCategory));
39+
40+
static cl::opt<bool>
41+
DumpAltInstructions("dump-alt-instructions",
42+
cl::desc("dump Linux alternative instructions info"),
43+
cl::init(false), cl::Hidden, cl::cat(BoltCategory));
44+
3045
static cl::opt<bool>
3146
DumpExceptions("dump-linux-exceptions",
3247
cl::desc("dump Linux kernel exception table"),
@@ -157,6 +172,9 @@ class LinuxKernelRewriter final : public MetadataRewriter {
157172
/// Alignment of paravirtual patch structures.
158173
static constexpr size_t PARA_PATCH_ALIGN = 8;
159174

175+
/// .altinstructions section.
176+
ErrorOr<BinarySection &> AltInstrSection = std::errc::bad_address;
177+
160178
/// Section containing Linux bug table.
161179
ErrorOr<BinarySection &> BugTableSection = std::errc::bad_address;
162180

@@ -205,6 +223,9 @@ class LinuxKernelRewriter final : public MetadataRewriter {
205223

206224
Error readBugTable();
207225

226+
/// Read alternative instruction info from .altinstructions.
227+
Error readAltInstructions();
228+
208229
/// Mark instructions referenced by kernel metadata.
209230
Error markInstructions();
210231

@@ -232,6 +253,9 @@ class LinuxKernelRewriter final : public MetadataRewriter {
232253
if (Error E = readBugTable())
233254
return E;
234255

256+
if (Error E = readAltInstructions())
257+
return E;
258+
235259
return Error::success();
236260
}
237261

@@ -1132,6 +1156,123 @@ Error LinuxKernelRewriter::readBugTable() {
11321156
return Error::success();
11331157
}
11341158

1159+
/// The kernel can replace certain instruction sequences depending on hardware
1160+
/// it is running on and features specified during boot time. The information
1161+
/// about alternative instruction sequences is stored in .altinstructions
1162+
/// section. The format of entries in this section is defined in
1163+
/// arch/x86/include/asm/alternative.h:
1164+
///
1165+
/// struct alt_instr {
1166+
/// s32 instr_offset;
1167+
/// s32 repl_offset;
1168+
/// uXX feature;
1169+
/// u8 instrlen;
1170+
/// u8 replacementlen;
1171+
/// u8 padlen; // present in older kernels
1172+
/// } __packed;
1173+
///
1174+
/// Note the structures is packed.
1175+
Error LinuxKernelRewriter::readAltInstructions() {
1176+
AltInstrSection = BC.getUniqueSectionByName(".altinstructions");
1177+
if (!AltInstrSection)
1178+
return Error::success();
1179+
1180+
const uint64_t Address = AltInstrSection->getAddress();
1181+
DataExtractor DE = DataExtractor(AltInstrSection->getContents(),
1182+
BC.AsmInfo->isLittleEndian(),
1183+
BC.AsmInfo->getCodePointerSize());
1184+
uint64_t EntryID = 0;
1185+
DataExtractor::Cursor Cursor(0);
1186+
while (Cursor && !DE.eof(Cursor)) {
1187+
const uint64_t OrgInstAddress =
1188+
Address + Cursor.tell() + (int32_t)DE.getU32(Cursor);
1189+
const uint64_t AltInstAddress =
1190+
Address + Cursor.tell() + (int32_t)DE.getU32(Cursor);
1191+
const uint64_t Feature = DE.getUnsigned(Cursor, opts::AltInstFeatureSize);
1192+
const uint8_t OrgSize = DE.getU8(Cursor);
1193+
const uint8_t AltSize = DE.getU8(Cursor);
1194+
1195+
// Older kernels may have the padlen field.
1196+
const uint8_t PadLen = opts::AltInstHasPadLen ? DE.getU8(Cursor) : 0;
1197+
1198+
if (!Cursor)
1199+
return createStringError(errc::executable_format_error,
1200+
"out of bounds while reading .altinstructions");
1201+
1202+
++EntryID;
1203+
1204+
if (opts::DumpAltInstructions) {
1205+
BC.outs() << "Alternative instruction entry: " << EntryID
1206+
<< "\n\tOrg: 0x" << Twine::utohexstr(OrgInstAddress)
1207+
<< "\n\tAlt: 0x" << Twine::utohexstr(AltInstAddress)
1208+
<< "\n\tFeature: 0x" << Twine::utohexstr(Feature)
1209+
<< "\n\tOrgSize: " << (int)OrgSize
1210+
<< "\n\tAltSize: " << (int)AltSize << '\n';
1211+
if (opts::AltInstHasPadLen)
1212+
BC.outs() << "\tPadLen: " << (int)PadLen << '\n';
1213+
}
1214+
1215+
if (AltSize > OrgSize)
1216+
return createStringError(errc::executable_format_error,
1217+
"error reading .altinstructions");
1218+
1219+
BinaryFunction *BF = BC.getBinaryFunctionContainingAddress(OrgInstAddress);
1220+
if (!BF && opts::Verbosity) {
1221+
BC.outs() << "BOLT-INFO: no function matches address 0x"
1222+
<< Twine::utohexstr(OrgInstAddress)
1223+
<< " of instruction from .altinstructions\n";
1224+
}
1225+
1226+
BinaryFunction *AltBF =
1227+
BC.getBinaryFunctionContainingAddress(AltInstAddress);
1228+
if (AltBF && BC.shouldEmit(*AltBF)) {
1229+
BC.errs()
1230+
<< "BOLT-WARNING: alternative instruction sequence found in function "
1231+
<< *AltBF << '\n';
1232+
AltBF->setIgnored();
1233+
}
1234+
1235+
if (!BF || !BC.shouldEmit(*BF))
1236+
continue;
1237+
1238+
if (OrgInstAddress + OrgSize > BF->getAddress() + BF->getSize())
1239+
return createStringError(errc::executable_format_error,
1240+
"error reading .altinstructions");
1241+
1242+
MCInst *Inst =
1243+
BF->getInstructionAtOffset(OrgInstAddress - BF->getAddress());
1244+
if (!Inst)
1245+
return createStringError(errc::executable_format_error,
1246+
"no instruction at address 0x%" PRIx64
1247+
" referenced by .altinstructions entry %d",
1248+
OrgInstAddress, EntryID);
1249+
1250+
// There could be more than one alternative instruction sequences for the
1251+
// same original instruction. Annotate each alternative separately.
1252+
std::string AnnotationName = "AltInst";
1253+
unsigned N = 2;
1254+
while (BC.MIB->hasAnnotation(*Inst, AnnotationName))
1255+
AnnotationName = "AltInst" + std::to_string(N++);
1256+
1257+
BC.MIB->addAnnotation(*Inst, AnnotationName, EntryID);
1258+
1259+
// Annotate all instructions from the original sequence. Note that it's not
1260+
// the most efficient way to look for instructions in the address range,
1261+
// but since alternative instructions are uncommon, it will do for now.
1262+
for (uint32_t Offset = 1; Offset < OrgSize; ++Offset) {
1263+
Inst = BF->getInstructionAtOffset(OrgInstAddress + Offset -
1264+
BF->getAddress());
1265+
if (Inst)
1266+
BC.MIB->addAnnotation(*Inst, AnnotationName, EntryID);
1267+
}
1268+
}
1269+
1270+
BC.outs() << "BOLT-INFO: parsed " << EntryID
1271+
<< " alternative instruction entries\n";
1272+
1273+
return Error::success();
1274+
}
1275+
11351276
} // namespace
11361277

11371278
std::unique_ptr<MetadataRewriter>

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

Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
# REQUIRES: system-linux
2+
3+
## Check that BOLT correctly parses the Linux kernel .altinstructions section
4+
## and annotates alternative instructions.
5+
6+
# RUN: llvm-mc -filetype=obj -triple x86_64-unknown-unknown %s -o %t.o
7+
# RUN: %clang %cflags -nostdlib %t.o -o %t.exe \
8+
# RUN: -Wl,--image-base=0xffffffff80000000,--no-dynamic-linker,--no-eh-frame-hdr,--no-pie
9+
# RUN: llvm-bolt %t.exe --print-normalized --keep-nops -o %t.out \
10+
# RUN: --alt-inst-feature-size=2 | FileCheck %s
11+
12+
## Older kernels used to have padlen field in alt_instr. Check compatibility.
13+
14+
# RUN: llvm-mc -filetype=obj -triple x86_64-unknown-unknown --defsym PADLEN=1 \
15+
# RUN: %s -o %t.o
16+
# RUN: %clang %cflags -nostdlib %t.o -o %t.exe \
17+
# RUN: -Wl,--image-base=0xffffffff80000000,--no-dynamic-linker,--no-eh-frame-hdr,--no-pie
18+
# RUN: llvm-bolt %t.exe --print-normalized --keep-nops --alt-inst-has-padlen \
19+
# RUN: -o %t.out | FileCheck %s
20+
21+
## Check with a larger size of "feature" field in alt_instr.
22+
23+
# RUN: llvm-mc -filetype=obj -triple x86_64-unknown-unknown \
24+
# RUN: --defsym FEATURE_SIZE_4=1 %s -o %t.o
25+
# RUN: %clang %cflags -nostdlib %t.o -o %t.exe \
26+
# RUN: -Wl,--image-base=0xffffffff80000000,--no-dynamic-linker,--no-eh-frame-hdr,--no-pie
27+
# RUN: llvm-bolt %t.exe --print-normalized --keep-nops \
28+
# RUN: --alt-inst-feature-size=4 -o %t.out | FileCheck %s
29+
30+
# CHECK: BOLT-INFO: Linux kernel binary detected
31+
# CHECK: BOLT-INFO: parsed 2 alternative instruction entries
32+
33+
.text
34+
.globl _start
35+
.type _start, %function
36+
_start:
37+
# CHECK: Binary Function "_start"
38+
.L0:
39+
rdtsc
40+
# CHECK: rdtsc
41+
# CHECK-SAME: AltInst: 1
42+
# CHECK-SAME: AltInst2: 2
43+
nop
44+
# CHECK-NEXT: nop
45+
# CHECK-SAME: AltInst: 1
46+
# CHECK-SAME: AltInst2: 2
47+
nop
48+
nop
49+
.L1:
50+
ret
51+
.size _start, .-_start
52+
53+
.section .altinstr_replacement,"ax",@progbits
54+
.A0:
55+
lfence
56+
rdtsc
57+
.A1:
58+
rdtscp
59+
.Ae:
60+
61+
## Alternative instruction info.
62+
.section .altinstructions,"a",@progbits
63+
64+
.long .L0 - . # org instruction
65+
.long .A0 - . # alt instruction
66+
.ifdef FEATURE_SIZE_4
67+
.long 0x72 # feature flags
68+
.else
69+
.word 0x72 # feature flags
70+
.endif
71+
.byte .L1 - .L0 # org size
72+
.byte .A1 - .A0 # alt size
73+
.ifdef PADLEN
74+
.byte 0
75+
.endif
76+
77+
.long .L0 - . # org instruction
78+
.long .A1 - . # alt instruction
79+
.ifdef FEATURE_SIZE_4
80+
.long 0x3b # feature flags
81+
.else
82+
.word 0x3b # feature flags
83+
.endif
84+
.byte .L1 - .L0 # org size
85+
.byte .Ae - .A1 # alt size
86+
.ifdef PADLEN
87+
.byte 0
88+
.endif
89+
90+
## Fake Linux Kernel sections.
91+
.section __ksymtab,"a",@progbits
92+
.section __ksymtab_gpl,"a",@progbits

0 commit comments

Comments
 (0)