14
14
#include " bolt/Rewrite/MetadataRewriter.h"
15
15
#include " bolt/Rewrite/MetadataRewriters.h"
16
16
#include " bolt/Utils/CommandLineOpts.h"
17
+ #include " llvm/Support/BinaryStreamWriter.h"
17
18
#include " llvm/Support/CommandLine.h"
19
+ #include " llvm/Support/Debug.h"
18
20
#include " llvm/Support/Errc.h"
19
21
22
+ #define DEBUG_TYPE " bolt-linux"
23
+
20
24
using namespace llvm ;
21
25
using namespace bolt ;
22
26
@@ -48,20 +52,25 @@ struct ORCState {
48
52
bool operator !=(const ORCState &Other) const { return !(*this == Other); }
49
53
};
50
54
55
+ // / Section terminator ORC entry.
56
+ static ORCState NullORC = {0 , 0 , 0 };
57
+
51
58
// / Basic printer for ORC entry. It does not provide the same level of
52
59
// / information as objtool (for now).
53
60
inline raw_ostream &operator <<(raw_ostream &OS, const ORCState &E) {
54
- if (opts::PrintORC)
61
+ if (!opts::PrintORC)
62
+ return OS;
63
+ if (E != NullORC)
55
64
OS << format (" {sp: %d, bp: %d, info: 0x%x}" , E.SPOffset , E.BPOffset ,
56
65
E.Info );
66
+ else
67
+ OS << " {terminator}" ;
68
+
57
69
return OS;
58
70
}
59
71
60
72
namespace {
61
73
62
- // / Section terminator ORC entry.
63
- static ORCState NullORC = {0 , 0 , 0 };
64
-
65
74
class LinuxKernelRewriter final : public MetadataRewriter {
66
75
// / Linux Kernel special sections point to a specific instruction in many
67
76
// / cases. Unlike SDTMarkerInfo, these markers can come from different
@@ -90,6 +99,8 @@ class LinuxKernelRewriter final : public MetadataRewriter {
90
99
BinaryFunction *BF; // / Binary function corresponding to the entry.
91
100
ORCState ORC; // / Stack unwind info in ORC format.
92
101
102
+ // / ORC entries are sorted by their IPs. Terminator entries (NullORC)
103
+ // / should precede other entries with the same address.
93
104
bool operator <(const ORCListEntry &Other) const {
94
105
if (IP < Other.IP )
95
106
return 1 ;
@@ -102,6 +113,9 @@ class LinuxKernelRewriter final : public MetadataRewriter {
102
113
using ORCListType = std::vector<ORCListEntry>;
103
114
ORCListType ORCEntries;
104
115
116
+ // / Number of entries in the input file ORC sections.
117
+ uint64_t NumORCEntries = 0 ;
118
+
105
119
// / Insert an LKMarker for a given code pointer \p PC from a non-code section
106
120
// / \p SectionName.
107
121
void insertLKMarker (uint64_t PC, uint64_t SectionOffset,
@@ -464,10 +478,9 @@ Error LinuxKernelRewriter::readORCTables() {
464
478
return createStringError (errc::executable_format_error,
465
479
" missing ORC section" );
466
480
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)
481
+ NumORCEntries = ORCUnwindIPSection->getSize () / ORC_UNWIND_IP_ENTRY_SIZE;
482
+ if (ORCUnwindSection->getSize () != NumORCEntries * ORC_UNWIND_ENTRY_SIZE ||
483
+ ORCUnwindIPSection->getSize () != NumORCEntries * ORC_UNWIND_IP_ENTRY_SIZE)
471
484
return createStringError (errc::executable_format_error,
472
485
" ORC entries number mismatch detected" );
473
486
@@ -481,7 +494,7 @@ Error LinuxKernelRewriter::readORCTables() {
481
494
DataExtractor::Cursor ORCCursor (0 );
482
495
DataExtractor::Cursor IPCursor (0 );
483
496
uint64_t PrevIP = 0 ;
484
- for (uint32_t Index = 0 ; Index < NumEntries ; ++Index) {
497
+ for (uint32_t Index = 0 ; Index < NumORCEntries ; ++Index) {
485
498
const uint64_t IP =
486
499
IPSectionAddress + IPCursor.tell () + (int32_t )IPDE.getU32 (IPCursor);
487
500
@@ -505,35 +518,31 @@ Error LinuxKernelRewriter::readORCTables() {
505
518
Entry.ORC .SPOffset = (int16_t )OrcDE.getU16 (ORCCursor);
506
519
Entry.ORC .BPOffset = (int16_t )OrcDE.getU16 (ORCCursor);
507
520
Entry.ORC .Info = (int16_t )OrcDE.getU16 (ORCCursor);
521
+ Entry.BF = nullptr ;
508
522
509
523
// Consume the status of the cursor.
510
524
if (!ORCCursor)
511
525
return createStringError (errc::executable_format_error,
512
526
" out of bounds while reading ORC" );
513
527
528
+ if (Entry.ORC == NullORC)
529
+ continue ;
530
+
514
531
BinaryFunction *&BF = Entry.BF ;
515
532
BF = BC.getBinaryFunctionContainingAddress (IP, /* CheckPastEnd*/ true );
516
533
517
534
// If the entry immediately pointing past the end of the function is not
518
535
// the terminator entry, then it does not belong to this function.
519
- if (BF && BF->getAddress () + BF->getSize () == IP && Entry. ORC != NullORC )
536
+ if (BF && BF->getAddress () + BF->getSize () == IP)
520
537
BF = 0 ;
521
538
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
-
527
539
if (!BF) {
528
540
if (opts::Verbosity)
529
541
errs () << " BOLT-WARNING: no binary function found matching ORC 0x"
530
542
<< Twine::utohexstr (IP) << " : " << Entry.ORC << ' \n ' ;
531
543
continue ;
532
544
}
533
545
534
- if (Entry.ORC == NullORC)
535
- continue ;
536
-
537
546
BF->setHasORC (true );
538
547
539
548
if (!BF->hasInstructions ())
@@ -556,9 +565,7 @@ Error LinuxKernelRewriter::readORCTables() {
556
565
BC.MIB ->addAnnotation (*Inst, " ORC" , Entry.ORC );
557
566
}
558
567
559
- // Older kernels could contain unsorted tables in the file as the tables were
560
- // sorted during boot time.
561
- llvm::sort (ORCEntries);
568
+ outs () << " BOLT-INFO: parsed " << NumORCEntries << " ORC entries\n " ;
562
569
563
570
if (opts::DumpORC) {
564
571
outs () << " BOLT-INFO: ORC unwind information:\n " ;
@@ -570,10 +577,51 @@ Error LinuxKernelRewriter::readORCTables() {
570
577
}
571
578
}
572
579
580
+ // Add entries for functions that don't have explicit ORC info at the start.
581
+ // We'll have the correct info for them even if ORC for the preceding function
582
+ // changes.
583
+ ORCListType NewEntries;
584
+ for (BinaryFunction &BF : llvm::make_second_range (BC.getBinaryFunctions ())) {
585
+ auto It = llvm::partition_point (ORCEntries, [&](const ORCListEntry &E) {
586
+ return E.IP <= BF.getAddress ();
587
+ });
588
+ if (It != ORCEntries.begin ())
589
+ --It;
590
+
591
+ if (It->BF == &BF)
592
+ continue ;
593
+
594
+ if (It->ORC == NullORC && It->IP == BF.getAddress ()) {
595
+ assert (!It->BF );
596
+ It->BF = &BF;
597
+ continue ;
598
+ }
599
+
600
+ NewEntries.push_back ({BF.getAddress (), &BF, It->ORC });
601
+ if (It->ORC != NullORC)
602
+ BF.setHasORC (true );
603
+ }
604
+
605
+ llvm::copy (NewEntries, std::back_inserter (ORCEntries));
606
+ llvm::sort (ORCEntries);
607
+
608
+ if (opts::DumpORC) {
609
+ outs () << " BOLT-INFO: amended ORC unwind information:\n " ;
610
+ for (const ORCListEntry &E : ORCEntries) {
611
+ outs () << " 0x" << Twine::utohexstr (E.IP ) << " : " << E.ORC ;
612
+ if (E.BF )
613
+ outs () << " : " << *E.BF ;
614
+ outs () << ' \n ' ;
615
+ }
616
+ }
617
+
573
618
return Error::success ();
574
619
}
575
620
576
621
Error LinuxKernelRewriter::processORCPostCFG () {
622
+ if (!NumORCEntries)
623
+ return Error::success ();
624
+
577
625
// Propagate ORC to the rest of the function. We can annotate every
578
626
// instruction in every function, but to minimize the overhead, we annotate
579
627
// the first instruction in every basic block to reflect the state at the
@@ -593,19 +641,28 @@ Error LinuxKernelRewriter::processORCPostCFG() {
593
641
continue ;
594
642
}
595
643
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.
644
+ // Get state for the start of the function.
598
645
if (!CurrentState) {
646
+ // A terminator entry (NullORC) can match the function address. If
647
+ // there's also a non-terminator entry, it will be placed after the
648
+ // terminator. Hence, we are looking for the last ORC entry that
649
+ // matches the address.
599
650
auto It =
600
651
llvm::partition_point (ORCEntries, [&](const ORCListEntry &E) {
601
- return E.IP < BF.getAddress ();
652
+ return E.IP <= BF.getAddress ();
602
653
});
603
654
if (It != ORCEntries.begin ())
604
- It = std::prev (It);
655
+ --It;
656
+
657
+ assert (It->IP == BF.getAddress () && (!It->BF || It->BF == &BF) &&
658
+ " ORC info at function entry expected." );
605
659
606
- if (It->ORC == NullORC && BF.hasORC ())
660
+ if (It->ORC == NullORC && BF.hasORC ()) {
607
661
errs () << " BOLT-WARNING: ORC unwind info excludes prologue for "
608
662
<< BF << ' \n ' ;
663
+ }
664
+
665
+ It->BF = &BF;
609
666
610
667
CurrentState = It->ORC ;
611
668
if (It->ORC != NullORC)
@@ -623,9 +680,121 @@ Error LinuxKernelRewriter::processORCPostCFG() {
623
680
}
624
681
625
682
Error LinuxKernelRewriter::rewriteORCTables () {
626
- // TODO:
683
+ if (!NumORCEntries)
684
+ return Error::success ();
685
+
686
+ // Update ORC sections in-place. As we change the code, the number of ORC
687
+ // entries may increase for some functions. However, as we remove terminator
688
+ // redundancy (see below), more space is freed up and we should always be able
689
+ // to fit new ORC tables in the reserved space.
690
+ auto createInPlaceWriter = [&](BinarySection &Section) -> BinaryStreamWriter {
691
+ const size_t Size = Section.getSize ();
692
+ uint8_t *NewContents = new uint8_t [Size ];
693
+ Section.updateContents (NewContents, Size );
694
+ Section.setOutputFileOffset (Section.getInputFileOffset ());
695
+ return BinaryStreamWriter ({NewContents, Size }, BC.AsmInfo ->isLittleEndian ()
696
+ ? endianness::little
697
+ : endianness::big);
698
+ };
699
+ BinaryStreamWriter UnwindWriter = createInPlaceWriter (*ORCUnwindSection);
700
+ BinaryStreamWriter UnwindIPWriter = createInPlaceWriter (*ORCUnwindIPSection);
701
+
702
+ uint64_t NumEmitted = 0 ;
703
+ std::optional<ORCState> LastEmittedORC;
704
+ auto emitORCEntry = [&](const uint64_t IP, const ORCState &ORC,
705
+ MCSymbol *Label = 0 , bool Force = false ) -> Error {
706
+ if (LastEmittedORC && ORC == *LastEmittedORC && !Force)
707
+ return Error::success ();
708
+
709
+ LastEmittedORC = ORC;
710
+
711
+ if (++NumEmitted > NumORCEntries)
712
+ return createStringError (errc::executable_format_error,
713
+ " exceeded the number of allocated ORC entries" );
714
+
715
+ if (Label)
716
+ ORCUnwindIPSection->addRelocation (UnwindIPWriter.getOffset (), Label,
717
+ Relocation::getPC32 (), /* Addend*/ 0 );
718
+
719
+ const int32_t IPValue =
720
+ IP - ORCUnwindIPSection->getAddress () - UnwindIPWriter.getOffset ();
721
+ if (Error E = UnwindIPWriter.writeInteger (IPValue))
722
+ return E;
723
+
724
+ if (Error E = UnwindWriter.writeInteger (ORC.SPOffset ))
725
+ return E;
726
+ if (Error E = UnwindWriter.writeInteger (ORC.BPOffset ))
727
+ return E;
728
+ if (Error E = UnwindWriter.writeInteger (ORC.Info ))
729
+ return E;
730
+
731
+ return Error::success ();
732
+ };
733
+
734
+ // Emit new ORC entries for the emitted function.
735
+ auto emitORC = [&](const BinaryFunction &BF) -> Error {
736
+ assert (!BF.isSplit () && " Split functions not supported by ORC writer yet." );
737
+
738
+ ORCState CurrentState = NullORC;
739
+ for (BinaryBasicBlock *BB : BF.getLayout ().blocks ()) {
740
+ for (MCInst &Inst : *BB) {
741
+ ErrorOr<ORCState> ErrorOrState =
742
+ BC.MIB ->tryGetAnnotationAs <ORCState>(Inst, " ORC" );
743
+ if (!ErrorOrState || *ErrorOrState == CurrentState)
744
+ continue ;
745
+
746
+ // Issue label for the instruction.
747
+ MCSymbol *Label = BC.MIB ->getLabel (Inst);
748
+ if (!Label) {
749
+ Label = BC.Ctx ->createTempSymbol (" __ORC_" );
750
+ BC.MIB ->setLabel (Inst, Label);
751
+ }
752
+
753
+ if (Error E = emitORCEntry (0 , *ErrorOrState, Label))
754
+ return E;
755
+
756
+ CurrentState = *ErrorOrState;
757
+ }
758
+ }
759
+
760
+ return Error::success ();
761
+ };
762
+
763
+ for (ORCListEntry &Entry : ORCEntries) {
764
+ // Emit original entries for functions that we haven't modified.
765
+ if (!Entry.BF || !BC.shouldEmit (*Entry.BF )) {
766
+ // Emit terminator only if it marks the start of a function.
767
+ if (Entry.ORC == NullORC && !Entry.BF )
768
+ continue ;
769
+ if (Error E = emitORCEntry (Entry.IP , Entry.ORC ))
770
+ return E;
771
+ continue ;
772
+ }
773
+
774
+ // Emit all ORC entries for a function referenced by an entry and skip over
775
+ // the rest of entries for this function by resetting its ORC attribute.
776
+ if (Entry.BF ->hasORC ()) {
777
+ if (Error E = emitORC (*Entry.BF ))
778
+ return E;
779
+ Entry.BF ->setHasORC (false );
780
+ }
781
+ }
782
+
783
+ LLVM_DEBUG (dbgs () << " BOLT-DEBUG: emitted " << NumEmitted
784
+ << " ORC entries\n " );
785
+
786
+ // Replicate terminator entry at the end of sections to match the original
787
+ // table sizes.
788
+ const BinaryFunction &LastBF = BC.getBinaryFunctions ().rbegin ()->second ;
789
+ const uint64_t LastIP = LastBF.getAddress () + LastBF.getMaxSize ();
790
+ while (UnwindWriter.bytesRemaining ()) {
791
+ if (Error E = emitORCEntry (LastIP, NullORC, nullptr , /* Force*/ true ))
792
+ return E;
793
+ }
794
+
627
795
return Error::success ();
628
796
}
797
+
629
798
} // namespace
630
799
631
800
std::unique_ptr<MetadataRewriter>
0 commit comments