Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 99b4532

Browse files
authored
[BOLT] Add support for Linux kernel .smp_locks section (#90798)
Parse .smp_locks section entries and create fixups that are going to be used to update the section before the binary emission.
1 parent 11f76b8 commit 99b4532

2 files changed

Lines changed: 119 additions & 116 deletions

File tree

bolt/lib/Rewrite/LinuxKernelRewriter.cpp

Lines changed: 79 additions & 116 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,11 @@ static cl::opt<bool>
6262
cl::desc("dump Linux kernel PCI fixup table"),
6363
cl::init(false), cl::Hidden, cl::cat(BoltCategory));
6464

65+
static cl::opt<bool> DumpSMPLocks("dump-smp-locks",
66+
cl::desc("dump Linux kernel SMP locks"),
67+
cl::init(false), cl::Hidden,
68+
cl::cat(BoltCategory));
69+
6570
static cl::opt<bool> DumpStaticCalls("dump-static-calls",
6671
cl::desc("dump Linux kernel static calls"),
6772
cl::init(false), cl::Hidden,
@@ -119,19 +124,18 @@ inline raw_ostream &operator<<(raw_ostream &OS, const ORCState &E) {
119124
namespace {
120125

121126
class LinuxKernelRewriter final : public MetadataRewriter {
122-
/// Linux Kernel special sections point to a specific instruction in many
123-
/// cases. Unlike SDTMarkerInfo, these markers can come from different
124-
/// sections.
125-
struct LKInstructionMarkerInfo {
126-
uint64_t SectionOffset;
127-
int32_t PCRelativeOffset;
128-
bool IsPCRelative;
129-
StringRef SectionName;
127+
/// Information required for updating metadata referencing an instruction.
128+
struct InstructionFixup {
129+
BinarySection &Section; // Section referencing the instruction.
130+
uint64_t Offset; // Offset in the section above.
131+
BinaryFunction &BF; // Function containing the instruction.
132+
MCSymbol &Label; // Label marking the instruction.
133+
bool IsPCRelative; // If the reference type is relative.
130134
};
135+
std::vector<InstructionFixup> Fixups;
131136

132-
/// Map linux kernel program locations/instructions to their pointers in
133-
/// special linux kernel sections
134-
std::unordered_map<uint64_t, std::vector<LKInstructionMarkerInfo>> LKMarkers;
137+
/// Size of an entry in .smp_locks section.
138+
static constexpr size_t SMP_LOCKS_ENTRY_SIZE = 4;
135139

136140
/// Linux ORC sections.
137141
ErrorOr<BinarySection &> ORCUnwindSection = std::errc::bad_address;
@@ -221,23 +225,20 @@ class LinuxKernelRewriter final : public MetadataRewriter {
221225
ErrorOr<BinarySection &> PCIFixupSection = std::errc::bad_address;
222226
static constexpr size_t PCI_FIXUP_ENTRY_SIZE = 16;
223227

224-
/// Insert an LKMarker for a given code pointer \p PC from a non-code section
225-
/// \p SectionName.
226-
void insertLKMarker(uint64_t PC, uint64_t SectionOffset,
227-
int32_t PCRelativeOffset, bool IsPCRelative,
228-
StringRef SectionName);
229-
230228
/// Process linux kernel special sections and their relocations.
231229
void processLKSections();
232230

233231
/// Process __ksymtab and __ksymtab_gpl.
234232
void processLKKSymtab(bool IsGPL = false);
235233

236-
/// Process special linux kernel section, .smp_locks.
237-
void processLKSMPLocks();
234+
// Create relocations in sections requiring fixups.
235+
//
236+
// Make sure functions that will not be emitted are marked as such before this
237+
// function is executed.
238+
void processInstructionFixups();
238239

239-
/// Update LKMarkers' locations for the output binary.
240-
void updateLKMarkers();
240+
/// Process .smp_locks section.
241+
Error processSMPLocks();
241242

242243
/// Read ORC unwind information and annotate instructions.
243244
Error readORCTables();
@@ -282,16 +283,14 @@ class LinuxKernelRewriter final : public MetadataRewriter {
282283
Error rewriteStaticKeysJumpTable();
283284
Error updateStaticKeysJumpTablePostEmit();
284285

285-
/// Mark instructions referenced by kernel metadata.
286-
Error markInstructions();
287-
288286
public:
289287
LinuxKernelRewriter(BinaryContext &BC)
290288
: MetadataRewriter("linux-kernel-rewriter", BC) {}
291289

292290
Error preCFGInitializer() override {
293291
processLKSections();
294-
if (Error E = markInstructions())
292+
293+
if (Error E = processSMPLocks())
295294
return E;
296295

297296
if (Error E = readORCTables())
@@ -352,12 +351,12 @@ class LinuxKernelRewriter final : public MetadataRewriter {
352351
if (Error E = rewriteBugTable())
353352
return E;
354353

354+
processInstructionFixups();
355+
355356
return Error::success();
356357
}
357358

358359
Error postEmitFinalizer() override {
359-
updateLKMarkers();
360-
361360
if (Error E = updateStaticKeysJumpTablePostEmit())
362361
return E;
363362

@@ -368,39 +367,9 @@ class LinuxKernelRewriter final : public MetadataRewriter {
368367
}
369368
};
370369

371-
Error LinuxKernelRewriter::markInstructions() {
372-
for (const uint64_t PC : llvm::make_first_range(LKMarkers)) {
373-
BinaryFunction *BF = BC.getBinaryFunctionContainingAddress(PC);
374-
375-
if (!BF || !BC.shouldEmit(*BF))
376-
continue;
377-
378-
const uint64_t Offset = PC - BF->getAddress();
379-
MCInst *Inst = BF->getInstructionAtOffset(Offset);
380-
if (!Inst)
381-
return createStringError(errc::executable_format_error,
382-
"no instruction matches kernel marker offset");
383-
384-
BC.MIB->setOffset(*Inst, static_cast<uint32_t>(Offset));
385-
386-
BF->setHasSDTMarker(true);
387-
}
388-
389-
return Error::success();
390-
}
391-
392-
void LinuxKernelRewriter::insertLKMarker(uint64_t PC, uint64_t SectionOffset,
393-
int32_t PCRelativeOffset,
394-
bool IsPCRelative,
395-
StringRef SectionName) {
396-
LKMarkers[PC].emplace_back(LKInstructionMarkerInfo{
397-
SectionOffset, PCRelativeOffset, IsPCRelative, SectionName});
398-
}
399-
400370
void LinuxKernelRewriter::processLKSections() {
401371
processLKKSymtab();
402372
processLKKSymtab(true);
403-
processLKSMPLocks();
404373
}
405374

406375
/// Process __ksymtab[_gpl] sections of Linux Kernel.
@@ -439,79 +408,73 @@ void LinuxKernelRewriter::processLKKSymtab(bool IsGPL) {
439408

440409
/// .smp_locks section contains PC-relative references to instructions with LOCK
441410
/// prefix. The prefix can be converted to NOP at boot time on non-SMP systems.
442-
void LinuxKernelRewriter::processLKSMPLocks() {
443-
ErrorOr<BinarySection &> SectionOrError =
411+
Error LinuxKernelRewriter::processSMPLocks() {
412+
ErrorOr<BinarySection &> SMPLocksSection =
444413
BC.getUniqueSectionByName(".smp_locks");
445-
if (!SectionOrError)
446-
return;
414+
if (!SMPLocksSection)
415+
return Error::success();
447416

448-
uint64_t SectionSize = SectionOrError->getSize();
449-
const uint64_t SectionAddress = SectionOrError->getAddress();
450-
assert((SectionSize % 4) == 0 &&
451-
"The size of the .smp_locks section should be a multiple of 4");
417+
const uint64_t SectionSize = SMPLocksSection->getSize();
418+
const uint64_t SectionAddress = SMPLocksSection->getAddress();
419+
if (SectionSize % SMP_LOCKS_ENTRY_SIZE)
420+
return createStringError(errc::executable_format_error,
421+
"bad size of .smp_locks section");
452422

453-
for (uint64_t I = 0; I < SectionSize; I += 4) {
454-
const uint64_t EntryAddress = SectionAddress + I;
455-
ErrorOr<uint64_t> Offset = BC.getSignedValueAtAddress(EntryAddress, 4);
456-
assert(Offset && "Reading valid PC-relative offset for a .smp_locks entry");
457-
int32_t SignedOffset = *Offset;
458-
uint64_t RefAddress = EntryAddress + SignedOffset;
423+
DataExtractor DE = DataExtractor(SMPLocksSection->getContents(),
424+
BC.AsmInfo->isLittleEndian(),
425+
BC.AsmInfo->getCodePointerSize());
426+
DataExtractor::Cursor Cursor(0);
427+
while (Cursor && Cursor.tell() < SectionSize) {
428+
const uint64_t Offset = Cursor.tell();
429+
const uint64_t IP = SectionAddress + Offset + (int32_t)DE.getU32(Cursor);
430+
431+
// Consume the status of the cursor.
432+
if (!Cursor)
433+
return createStringError(errc::executable_format_error,
434+
"error while reading .smp_locks: %s",
435+
toString(Cursor.takeError()).c_str());
459436

460-
BinaryFunction *ContainingBF =
461-
BC.getBinaryFunctionContainingAddress(RefAddress);
462-
if (!ContainingBF)
437+
if (opts::DumpSMPLocks)
438+
BC.outs() << "SMP lock at 0x: " << Twine::utohexstr(IP) << '\n';
439+
440+
BinaryFunction *BF = BC.getBinaryFunctionContainingAddress(IP);
441+
if (!BF || !BC.shouldEmit(*BF))
463442
continue;
464443

465-
insertLKMarker(RefAddress, I, SignedOffset, true, ".smp_locks");
466-
}
467-
}
444+
MCInst *Inst = BF->getInstructionAtOffset(IP - BF->getAddress());
445+
if (!Inst)
446+
return createStringError(errc::executable_format_error,
447+
"no instruction matches lock at 0x%" PRIx64, IP);
468448

469-
void LinuxKernelRewriter::updateLKMarkers() {
470-
if (LKMarkers.size() == 0)
471-
return;
449+
// Check for duplicate entries.
450+
if (BC.MIB->hasAnnotation(*Inst, "SMPLock"))
451+
return createStringError(errc::executable_format_error,
452+
"duplicate SMP lock at 0x%" PRIx64, IP);
472453

473-
std::unordered_map<std::string, uint64_t> PatchCounts;
474-
for (std::pair<const uint64_t, std::vector<LKInstructionMarkerInfo>>
475-
&LKMarkerInfoKV : LKMarkers) {
476-
const uint64_t OriginalAddress = LKMarkerInfoKV.first;
477-
const BinaryFunction *BF =
478-
BC.getBinaryFunctionContainingAddress(OriginalAddress, false, true);
479-
if (!BF)
480-
continue;
454+
BC.MIB->addAnnotation(*Inst, "SMPLock", true);
455+
MCSymbol *Label =
456+
BC.MIB->getOrCreateInstLabel(*Inst, "__SMPLock_", BC.Ctx.get());
481457

482-
uint64_t NewAddress = BF->translateInputToOutputAddress(OriginalAddress);
483-
if (NewAddress == 0)
484-
continue;
458+
Fixups.push_back({*SMPLocksSection, Offset, *BF, *Label,
459+
/*IsPCRelative*/ true});
460+
}
461+
462+
const uint64_t NumEntries = SectionSize / SMP_LOCKS_ENTRY_SIZE;
463+
BC.outs() << "BOLT-INFO: parsed " << NumEntries << " SMP lock entries\n";
485464

486-
// Apply base address.
487-
if (OriginalAddress >= 0xffffffff00000000 && NewAddress < 0xffffffff)
488-
NewAddress = NewAddress + 0xffffffff00000000;
465+
return Error::success();
466+
}
489467

490-
if (OriginalAddress == NewAddress)
468+
void LinuxKernelRewriter::processInstructionFixups() {
469+
for (InstructionFixup &Fixup : Fixups) {
470+
if (!BC.shouldEmit(Fixup.BF))
491471
continue;
492472

493-
for (LKInstructionMarkerInfo &LKMarkerInfo : LKMarkerInfoKV.second) {
494-
StringRef SectionName = LKMarkerInfo.SectionName;
495-
SimpleBinaryPatcher *LKPatcher;
496-
ErrorOr<BinarySection &> BSec = BC.getUniqueSectionByName(SectionName);
497-
assert(BSec && "missing section info for kernel section");
498-
if (!BSec->getPatcher())
499-
BSec->registerPatcher(std::make_unique<SimpleBinaryPatcher>());
500-
LKPatcher = static_cast<SimpleBinaryPatcher *>(BSec->getPatcher());
501-
PatchCounts[std::string(SectionName)]++;
502-
if (LKMarkerInfo.IsPCRelative)
503-
LKPatcher->addLE32Patch(LKMarkerInfo.SectionOffset,
504-
NewAddress - OriginalAddress +
505-
LKMarkerInfo.PCRelativeOffset);
506-
else
507-
LKPatcher->addLE64Patch(LKMarkerInfo.SectionOffset, NewAddress);
508-
}
473+
Fixup.Section.addRelocation(Fixup.Offset, &Fixup.Label,
474+
Fixup.IsPCRelative ? ELF::R_X86_64_PC32
475+
: ELF::R_X86_64_64,
476+
/*Addend*/ 0);
509477
}
510-
BC.outs() << "BOLT-INFO: patching linux kernel sections. Total patches per "
511-
"section are as follows:\n";
512-
for (const std::pair<const std::string, uint64_t> &KV : PatchCounts)
513-
BC.outs() << " Section: " << KV.first << ", patch-counts: " << KV.second
514-
<< '\n';
515478
}
516479

517480
Error LinuxKernelRewriter::readORCTables() {

bolt/test/X86/linux-smp-locks.s

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
# REQUIRES: system-linux
2+
3+
## Check that BOLT correctly parses and updates the Linux kernel .smp_locks
4+
## section.
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=0 --bolt-info=0 -o %t.out \
10+
# RUN: |& FileCheck %s
11+
12+
## Check the output of BOLT with NOPs removed.
13+
14+
# RUN: llvm-bolt %t.out -o %t.out.1 --print-normalized |& FileCheck %s
15+
16+
# CHECK: BOLT-INFO: Linux kernel binary detected
17+
# CHECK: BOLT-INFO: parsed 2 SMP lock entries
18+
19+
.text
20+
.globl _start
21+
.type _start, %function
22+
_start:
23+
nop
24+
nop
25+
.L0:
26+
lock incl (%rdi)
27+
# CHECK: lock {{.*}} SMPLock
28+
.L1:
29+
lock orb $0x40, 0x4(%rsi)
30+
# CHECK: lock {{.*}} SMPLock
31+
ret
32+
.size _start, .-_start
33+
34+
.section .smp_locks,"a",@progbits
35+
.long .L0 - .
36+
.long .L1 - .
37+
38+
## Fake Linux Kernel sections.
39+
.section __ksymtab,"a",@progbits
40+
.section __ksymtab_gpl,"a",@progbits

0 commit comments

Comments
 (0)