Skip to content

Commit 481f849

Browse files
committed
[BOLT] Add writing support for Linux kernel ORC
Update ORC information based on the new code layout and emit corresponding ORC sections for the Linux kernel.
1 parent 7fe97f0 commit 481f849

File tree

2 files changed

+226
-40
lines changed

2 files changed

+226
-40
lines changed

bolt/lib/Rewrite/LinuxKernelRewriter.cpp

Lines changed: 184 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -14,9 +14,12 @@
1414
#include "bolt/Rewrite/MetadataRewriter.h"
1515
#include "bolt/Rewrite/MetadataRewriters.h"
1616
#include "bolt/Utils/CommandLineOpts.h"
17+
#include "llvm/Support/BinaryStreamWriter.h"
1718
#include "llvm/Support/CommandLine.h"
1819
#include "llvm/Support/Errc.h"
1920

21+
#define DEBUG_TYPE "bolt-linux"
22+
2023
using namespace llvm;
2124
using namespace bolt;
2225

@@ -48,20 +51,25 @@ struct ORCState {
4851
bool operator!=(const ORCState &Other) const { return !(*this == Other); }
4952
};
5053

54+
/// Section terminator ORC entry.
55+
static ORCState NullORC = {0, 0, 0};
56+
5157
/// Basic printer for ORC entry. It does not provide the same level of
5258
/// information as objtool (for now).
5359
inline raw_ostream &operator<<(raw_ostream &OS, const ORCState &E) {
54-
if (opts::PrintORC)
60+
if (!opts::PrintORC)
61+
return OS;
62+
if (E != NullORC)
5563
OS << format("{sp: %d, bp: %d, info: 0x%x}", E.SPOffset, E.BPOffset,
5664
E.Info);
65+
else
66+
OS << "{terminator}";
67+
5768
return OS;
5869
}
5970

6071
namespace {
6172

62-
/// Section terminator ORC entry.
63-
static ORCState NullORC = {0, 0, 0};
64-
6573
class LinuxKernelRewriter final : public MetadataRewriter {
6674
/// Linux Kernel special sections point to a specific instruction in many
6775
/// cases. Unlike SDTMarkerInfo, these markers can come from different
@@ -102,6 +110,9 @@ class LinuxKernelRewriter final : public MetadataRewriter {
102110
using ORCListType = std::vector<ORCListEntry>;
103111
ORCListType ORCEntries;
104112

113+
/// Number of entries in the input file ORC sections.
114+
uint64_t NumORCEntries = 0;
115+
105116
/// Insert an LKMarker for a given code pointer \p PC from a non-code section
106117
/// \p SectionName.
107118
void insertLKMarker(uint64_t PC, uint64_t SectionOffset,
@@ -207,8 +218,6 @@ void LinuxKernelRewriter::insertLKMarker(uint64_t PC, uint64_t SectionOffset,
207218
}
208219

209220
void LinuxKernelRewriter::processLKSections() {
210-
assert(BC.IsLinuxKernel && "Linux kernel binary expected.");
211-
212221
processLKExTable();
213222
processLKPCIFixup();
214223
processLKKSymtab();
@@ -464,10 +473,9 @@ Error LinuxKernelRewriter::readORCTables() {
464473
return createStringError(errc::executable_format_error,
465474
"missing ORC section");
466475

467-
const uint64_t NumEntries =
468-
ORCUnwindIPSection->getSize() / ORC_UNWIND_IP_ENTRY_SIZE;
469-
if (ORCUnwindSection->getSize() != NumEntries * ORC_UNWIND_ENTRY_SIZE ||
470-
ORCUnwindIPSection->getSize() != NumEntries * ORC_UNWIND_IP_ENTRY_SIZE)
476+
NumORCEntries = ORCUnwindIPSection->getSize() / ORC_UNWIND_IP_ENTRY_SIZE;
477+
if (ORCUnwindSection->getSize() != NumORCEntries * ORC_UNWIND_ENTRY_SIZE ||
478+
ORCUnwindIPSection->getSize() != NumORCEntries * ORC_UNWIND_IP_ENTRY_SIZE)
471479
return createStringError(errc::executable_format_error,
472480
"ORC entries number mismatch detected");
473481

@@ -481,7 +489,7 @@ Error LinuxKernelRewriter::readORCTables() {
481489
DataExtractor::Cursor ORCCursor(0);
482490
DataExtractor::Cursor IPCursor(0);
483491
uint64_t PrevIP = 0;
484-
for (uint32_t Index = 0; Index < NumEntries; ++Index) {
492+
for (uint32_t Index = 0; Index < NumORCEntries; ++Index) {
485493
const uint64_t IP =
486494
IPSectionAddress + IPCursor.tell() + (int32_t)IPDE.getU32(IPCursor);
487495

@@ -505,35 +513,31 @@ Error LinuxKernelRewriter::readORCTables() {
505513
Entry.ORC.SPOffset = (int16_t)OrcDE.getU16(ORCCursor);
506514
Entry.ORC.BPOffset = (int16_t)OrcDE.getU16(ORCCursor);
507515
Entry.ORC.Info = (int16_t)OrcDE.getU16(ORCCursor);
516+
Entry.BF = nullptr;
508517

509518
// Consume the status of the cursor.
510519
if (!ORCCursor)
511520
return createStringError(errc::executable_format_error,
512521
"out of bounds while reading ORC");
513522

523+
if (Entry.ORC == NullORC)
524+
continue;
525+
514526
BinaryFunction *&BF = Entry.BF;
515527
BF = BC.getBinaryFunctionContainingAddress(IP, /*CheckPastEnd*/ true);
516528

517529
// If the entry immediately pointing past the end of the function is not
518530
// the terminator entry, then it does not belong to this function.
519-
if (BF && BF->getAddress() + BF->getSize() == IP && Entry.ORC != NullORC)
531+
if (BF && BF->getAddress() + BF->getSize() == IP)
520532
BF = 0;
521533

522-
// If terminator entry points to the start of the function, then it belongs
523-
// to a different function that contains the previous IP.
524-
if (BF && BF->getAddress() == IP && Entry.ORC == NullORC)
525-
BF = BC.getBinaryFunctionContainingAddress(IP - 1);
526-
527534
if (!BF) {
528535
if (opts::Verbosity)
529536
errs() << "BOLT-WARNING: no binary function found matching ORC 0x"
530537
<< Twine::utohexstr(IP) << ": " << Entry.ORC << '\n';
531538
continue;
532539
}
533540

534-
if (Entry.ORC == NullORC)
535-
continue;
536-
537541
BF->setHasORC(true);
538542

539543
if (!BF->hasInstructions())
@@ -556,12 +560,46 @@ Error LinuxKernelRewriter::readORCTables() {
556560
BC.MIB->addAnnotation(*Inst, "ORC", Entry.ORC);
557561
}
558562

559-
// Older kernels could contain unsorted tables in the file as the tables were
560-
// sorted during boot time.
563+
if (opts::DumpORC) {
564+
outs() << "BOLT-INFO: ORC unwind information:\n";
565+
for (const ORCListEntry &E : ORCEntries) {
566+
outs() << "0x" << Twine::utohexstr(E.IP) << ": " << E.ORC;
567+
if (E.BF)
568+
outs() << ": " << *E.BF;
569+
outs() << '\n';
570+
}
571+
}
572+
573+
// Add entries for functions that don't have explicit ORC info at the start.
574+
// We'll have the correct info for them even if ORC for the preceding function
575+
// changes.
576+
ORCListType NewEntries;
577+
for (BinaryFunction &BF : llvm::make_second_range(BC.getBinaryFunctions())) {
578+
auto It = llvm::partition_point(ORCEntries, [&](const ORCListEntry &E) {
579+
return E.IP <= BF.getAddress();
580+
});
581+
if (It != ORCEntries.begin())
582+
--It;
583+
584+
if (It->BF == &BF)
585+
continue;
586+
587+
if (It->ORC == NullORC && It->IP == BF.getAddress()) {
588+
assert(!It->BF);
589+
It->BF = &BF;
590+
continue;
591+
}
592+
593+
NewEntries.push_back({BF.getAddress(), &BF, It->ORC});
594+
if (It->ORC != NullORC)
595+
BF.setHasORC(true);
596+
}
597+
598+
llvm::copy(NewEntries, std::back_inserter(ORCEntries));
561599
llvm::sort(ORCEntries);
562600

563601
if (opts::DumpORC) {
564-
outs() << "BOLT-INFO: ORC unwind information:\n";
602+
outs() << "BOLT-INFO: amended ORC unwind information:\n";
565603
for (const ORCListEntry &E : ORCEntries) {
566604
outs() << "0x" << Twine::utohexstr(E.IP) << ": " << E.ORC;
567605
if (E.BF)
@@ -593,20 +631,26 @@ Error LinuxKernelRewriter::processORCPostCFG() {
593631
continue;
594632
}
595633

596-
// In case there was no ORC entry that matched the function start
597-
// address, we need to propagate ORC state from the previous entry.
634+
// Get state for the start of the function.
598635
if (!CurrentState) {
599636
auto It =
600637
llvm::partition_point(ORCEntries, [&](const ORCListEntry &E) {
601-
return E.IP < BF.getAddress();
638+
return E.IP <= BF.getAddress();
602639
});
603640
if (It != ORCEntries.begin())
604-
It = std::prev(It);
641+
--It;
642+
643+
if (It->IP != BF.getAddress() || It->BF != &BF)
644+
dbgs() << "0x" << Twine::utohexstr(It->IP) << " : " << BF << '\n';
645+
assert(It->IP == BF.getAddress() && (!It->BF || It->BF == &BF) &&
646+
"Function entry expected.");
605647

606648
if (It->ORC == NullORC && BF.hasORC())
607649
errs() << "BOLT-WARNING: ORC unwind info excludes prologue for "
608650
<< BF << '\n';
609651

652+
It->BF = &BF;
653+
610654
CurrentState = It->ORC;
611655
if (It->ORC != NullORC)
612656
BF.setHasORC(true);
@@ -623,9 +667,121 @@ Error LinuxKernelRewriter::processORCPostCFG() {
623667
}
624668

625669
Error LinuxKernelRewriter::rewriteORCTables() {
626-
// TODO:
670+
if (!ORCUnwindSection || !ORCUnwindIPSection)
671+
return Error::success();
672+
673+
// Update ORC sections in-place. As we change the code, the number of ORC
674+
// entries may increase for some functions. However, as we remove terminator
675+
// redundancy (see below), more space is freed up and we should always be able
676+
// to fit new ORC tables in the reserved space.
677+
auto createInPlaceWriter = [&](BinarySection &Section) -> BinaryStreamWriter {
678+
const size_t Size = Section.getSize();
679+
uint8_t *NewContents = new uint8_t[Size];
680+
Section.updateContents(NewContents, Size);
681+
Section.setOutputFileOffset(Section.getInputFileOffset());
682+
return BinaryStreamWriter({NewContents, Size}, BC.AsmInfo->isLittleEndian()
683+
? endianness::little
684+
: endianness::big);
685+
};
686+
BinaryStreamWriter UnwindWriter = createInPlaceWriter(*ORCUnwindSection);
687+
BinaryStreamWriter UnwindIPWriter = createInPlaceWriter(*ORCUnwindIPSection);
688+
689+
uint64_t NumEmitted = 0;
690+
std::optional<ORCState> LastEmittedORC;
691+
auto emitORCEntry = [&](const uint64_t IP, const ORCState &ORC,
692+
MCSymbol *Label = 0, bool Force = false) -> Error {
693+
if (LastEmittedORC && ORC == *LastEmittedORC && !Force)
694+
return Error::success();
695+
696+
LastEmittedORC = ORC;
697+
698+
if (++NumEmitted > NumORCEntries)
699+
return createStringError(errc::executable_format_error,
700+
"exceeded the number of allocated ORC entries");
701+
702+
if (Label)
703+
ORCUnwindIPSection->addRelocation(UnwindIPWriter.getOffset(), Label,
704+
Relocation::getPC32(), /*Addend*/ 0);
705+
706+
const int32_t IPValue =
707+
IP - ORCUnwindIPSection->getAddress() - UnwindIPWriter.getOffset();
708+
if (Error E = UnwindIPWriter.writeInteger(IPValue))
709+
return E;
710+
711+
if (Error E = UnwindWriter.writeInteger(ORC.SPOffset))
712+
return E;
713+
if (Error E = UnwindWriter.writeInteger(ORC.BPOffset))
714+
return E;
715+
if (Error E = UnwindWriter.writeInteger(ORC.Info))
716+
return E;
717+
718+
return Error::success();
719+
};
720+
721+
// Emit new ORC entries for an emitted function.
722+
auto emitORC = [&](const BinaryFunction &BF) -> Error {
723+
assert(!BF.isSplit() && "Split functions not supported by ORC writer yet.");
724+
725+
ORCState CurrentState = NullORC;
726+
for (BinaryBasicBlock *BB : BF.getLayout().blocks()) {
727+
for (MCInst &Inst : *BB) {
728+
ErrorOr<ORCState> ErrorOrState =
729+
BC.MIB->tryGetAnnotationAs<ORCState>(Inst, "ORC");
730+
if (!ErrorOrState || *ErrorOrState == CurrentState)
731+
continue;
732+
733+
// Issue label for the instruction.
734+
MCSymbol *Label = BC.MIB->getLabel(Inst);
735+
if (!Label) {
736+
Label = BC.Ctx->createTempSymbol("__ORC_");
737+
BC.MIB->setLabel(Inst, Label);
738+
}
739+
740+
if (Error E = emitORCEntry(0, *ErrorOrState, Label))
741+
return E;
742+
743+
CurrentState = *ErrorOrState;
744+
}
745+
}
746+
747+
return Error::success();
748+
};
749+
750+
for (ORCListEntry &Entry : ORCEntries) {
751+
// Emit original entries for functions that we haven't modified.
752+
if (!Entry.BF || !BC.shouldEmit(*Entry.BF)) {
753+
// Emit terminator only if it marks the start of a function.
754+
if (Entry.ORC == NullORC && !Entry.BF)
755+
continue;
756+
if (Error E = emitORCEntry(Entry.IP, Entry.ORC))
757+
return E;
758+
continue;
759+
}
760+
761+
// Emit all ORC entries for a function referenced by an entry and skip over
762+
// the rest of entries for this function by resetting its ORC attribute.
763+
if (Entry.BF->hasORC()) {
764+
if (Error E = emitORC(*Entry.BF))
765+
return E;
766+
Entry.BF->setHasORC(false);
767+
}
768+
}
769+
770+
LLVM_DEBUG(dbgs() << "BOLT-DEBUG: emitted " << NumEmitted
771+
<< " ORC entries\n");
772+
773+
// Replicate terminator entry at the end of sections to match the original
774+
// table sizes.
775+
const BinaryFunction &LastBF = BC.getBinaryFunctions().rbegin()->second;
776+
const uint64_t LastIP = LastBF.getAddress() + LastBF.getMaxSize();
777+
while (UnwindWriter.bytesRemaining()) {
778+
if (Error E = emitORCEntry(LastIP, NullORC, nullptr, /*Force*/ true))
779+
return E;
780+
}
781+
627782
return Error::success();
628783
}
784+
629785
} // namespace
630786

631787
std::unique_ptr<MetadataRewriter>

0 commit comments

Comments
 (0)