Skip to content

Commit 433e5f0

Browse files
committed
[BOLT] Add reading support for Linux kernel .altinstructions section
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 0262979 commit 433e5f0

File tree

1 file changed

+141
-0
lines changed

1 file changed

+141
-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 alernative 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>

0 commit comments

Comments
 (0)