14
14
#include " bolt/Rewrite/MetadataRewriter.h"
15
15
#include " bolt/Rewrite/MetadataRewriters.h"
16
16
#include " bolt/Utils/CommandLineOpts.h"
17
+ #include " llvm/ADT/DenseSet.h"
17
18
#include " llvm/Support/BinaryStreamWriter.h"
18
19
#include " llvm/Support/CommandLine.h"
19
20
#include " llvm/Support/Debug.h"
@@ -27,9 +28,9 @@ using namespace bolt;
27
28
namespace opts {
28
29
29
30
static cl::opt<bool >
30
- PrintORC ( " print-orc " ,
31
- cl::desc (" print ORC unwind information for instructions " ),
32
- cl::init(true ), cl::Hidden, cl::cat(BoltCategory));
31
+ DumpExceptions ( " dump-linux-exceptions " ,
32
+ cl::desc (" dump Linux kernel exception table " ),
33
+ cl::init(false ), cl::Hidden, cl::cat(BoltCategory));
33
34
34
35
static cl::opt<bool >
35
36
DumpORC (" dump-orc" , cl::desc(" dump raw ORC unwind information (sorted)" ),
@@ -40,6 +41,11 @@ static cl::opt<bool> DumpStaticCalls("dump-static-calls",
40
41
cl::init(false ), cl::Hidden,
41
42
cl::cat(BoltCategory));
42
43
44
+ static cl::opt<bool >
45
+ PrintORC (" print-orc" ,
46
+ cl::desc (" print ORC unwind information for instructions" ),
47
+ cl::init(true ), cl::Hidden, cl::cat(BoltCategory));
48
+
43
49
} // namespace opts
44
50
45
51
// / Linux Kernel supports stack unwinding using ORC (oops rewind capability).
@@ -134,6 +140,13 @@ class LinuxKernelRewriter final : public MetadataRewriter {
134
140
using StaticCallListType = std::vector<StaticCallInfo>;
135
141
StaticCallListType StaticCallEntries;
136
142
143
+ // / Section containing the Linux exception table.
144
+ ErrorOr<BinarySection &> ExceptionsSection = std::errc::bad_address;
145
+ static constexpr size_t EXCEPTION_TABLE_ENTRY_SIZE = 12 ;
146
+
147
+ // / Functions with exception handling code.
148
+ DenseSet<BinaryFunction *> FunctionsWithExceptions;
149
+
137
150
// / Insert an LKMarker for a given code pointer \p PC from a non-code section
138
151
// / \p SectionName.
139
152
void insertLKMarker (uint64_t PC, uint64_t SectionOffset,
@@ -143,9 +156,6 @@ class LinuxKernelRewriter final : public MetadataRewriter {
143
156
// / Process linux kernel special sections and their relocations.
144
157
void processLKSections ();
145
158
146
- // / Process special linux kernel section, __ex_table.
147
- void processLKExTable ();
148
-
149
159
// / Process special linux kernel section, .pci_fixup.
150
160
void processLKPCIFixup ();
151
161
@@ -174,6 +184,9 @@ class LinuxKernelRewriter final : public MetadataRewriter {
174
184
Error readStaticCalls ();
175
185
Error rewriteStaticCalls ();
176
186
187
+ Error readExceptionTable ();
188
+ Error rewriteExceptionTable ();
189
+
177
190
// / Mark instructions referenced by kernel metadata.
178
191
Error markInstructions ();
179
192
@@ -192,6 +205,9 @@ class LinuxKernelRewriter final : public MetadataRewriter {
192
205
if (Error E = readStaticCalls ())
193
206
return E;
194
207
208
+ if (Error E = readExceptionTable ())
209
+ return E;
210
+
195
211
return Error::success ();
196
212
}
197
213
@@ -203,6 +219,11 @@ class LinuxKernelRewriter final : public MetadataRewriter {
203
219
}
204
220
205
221
Error preEmitFinalizer () override {
222
+ // Since rewriteExceptionTable() can mark functions as non-simple, run it
223
+ // before other rewriters that depend on simple/emit status.
224
+ if (Error E = rewriteExceptionTable ())
225
+ return E;
226
+
206
227
if (Error E = rewriteORCTables ())
207
228
return E;
208
229
@@ -249,77 +270,13 @@ void LinuxKernelRewriter::insertLKMarker(uint64_t PC, uint64_t SectionOffset,
249
270
}
250
271
251
272
void LinuxKernelRewriter::processLKSections () {
252
- processLKExTable ();
253
273
processLKPCIFixup ();
254
274
processLKKSymtab ();
255
275
processLKKSymtab (true );
256
276
processLKBugTable ();
257
277
processLKSMPLocks ();
258
278
}
259
279
260
- // / Process __ex_table section of Linux Kernel.
261
- // / This section contains information regarding kernel level exception
262
- // / handling (https://www.kernel.org/doc/html/latest/x86/exception-tables.html).
263
- // / More documentation is in arch/x86/include/asm/extable.h.
264
- // /
265
- // / The section is the list of the following structures:
266
- // /
267
- // / struct exception_table_entry {
268
- // / int insn;
269
- // / int fixup;
270
- // / int handler;
271
- // / };
272
- // /
273
- void LinuxKernelRewriter::processLKExTable () {
274
- ErrorOr<BinarySection &> SectionOrError =
275
- BC.getUniqueSectionByName (" __ex_table" );
276
- if (!SectionOrError)
277
- return ;
278
-
279
- const uint64_t SectionSize = SectionOrError->getSize ();
280
- const uint64_t SectionAddress = SectionOrError->getAddress ();
281
- assert ((SectionSize % 12 ) == 0 &&
282
- " The size of the __ex_table section should be a multiple of 12" );
283
- for (uint64_t I = 0 ; I < SectionSize; I += 4 ) {
284
- const uint64_t EntryAddress = SectionAddress + I;
285
- ErrorOr<uint64_t > Offset = BC.getSignedValueAtAddress (EntryAddress, 4 );
286
- assert (Offset && " failed reading PC-relative offset for __ex_table" );
287
- int32_t SignedOffset = *Offset;
288
- const uint64_t RefAddress = EntryAddress + SignedOffset;
289
-
290
- BinaryFunction *ContainingBF =
291
- BC.getBinaryFunctionContainingAddress (RefAddress);
292
- if (!ContainingBF)
293
- continue ;
294
-
295
- MCSymbol *ReferencedSymbol = ContainingBF->getSymbol ();
296
- const uint64_t FunctionOffset = RefAddress - ContainingBF->getAddress ();
297
- switch (I % 12 ) {
298
- default :
299
- llvm_unreachable (" bad alignment of __ex_table" );
300
- break ;
301
- case 0 :
302
- // insn
303
- insertLKMarker (RefAddress, I, SignedOffset, true , " __ex_table" );
304
- break ;
305
- case 4 :
306
- // fixup
307
- if (FunctionOffset)
308
- ReferencedSymbol = ContainingBF->addEntryPointAtOffset (FunctionOffset);
309
- BC.addRelocation (EntryAddress, ReferencedSymbol, Relocation::getPC32 (), 0 ,
310
- *Offset);
311
- break ;
312
- case 8 :
313
- // handler
314
- assert (!FunctionOffset &&
315
- " __ex_table handler entry should point to function start" );
316
- BC.addRelocation (EntryAddress, ReferencedSymbol, Relocation::getPC32 (), 0 ,
317
- *Offset);
318
- break ;
319
- }
320
- }
321
- }
322
-
323
280
// / Process .pci_fixup section of Linux Kernel.
324
281
// / This section contains a list of entries for different PCI devices and their
325
282
// / corresponding hook handler (code pointer where the fixup
@@ -943,6 +900,119 @@ Error LinuxKernelRewriter::rewriteStaticCalls() {
943
900
return Error::success ();
944
901
}
945
902
903
+ // / Instructions that access user-space memory can cause page faults. These
904
+ // / faults will be handled by the kernel and execution will resume at the fixup
905
+ // / code location if the address was invalid. The kernel uses the exception
906
+ // / table to match the faulting instruction to its fixup. The table consists of
907
+ // / the following entries:
908
+ // /
909
+ // / struct exception_table_entry {
910
+ // / int insn;
911
+ // / int fixup;
912
+ // / int data;
913
+ // / };
914
+ // /
915
+ // / More info at:
916
+ // / https://www.kernel.org/doc/Documentation/x86/exception-tables.txt
917
+ Error LinuxKernelRewriter::readExceptionTable () {
918
+ ExceptionsSection = BC.getUniqueSectionByName (" __ex_table" );
919
+ if (!ExceptionsSection)
920
+ return Error::success ();
921
+
922
+ if (ExceptionsSection->getSize () % EXCEPTION_TABLE_ENTRY_SIZE)
923
+ return createStringError (errc::executable_format_error,
924
+ " exception table size error" );
925
+
926
+ const uint64_t SectionAddress = ExceptionsSection->getAddress ();
927
+ DataExtractor DE (ExceptionsSection->getContents (),
928
+ BC.AsmInfo ->isLittleEndian (),
929
+ BC.AsmInfo ->getCodePointerSize ());
930
+ DataExtractor::Cursor Cursor (0 );
931
+ uint32_t EntryID = 0 ;
932
+ while (Cursor && Cursor.tell () < ExceptionsSection->getSize ()) {
933
+ const uint64_t InstAddress =
934
+ SectionAddress + Cursor.tell () + (int32_t )DE.getU32 (Cursor);
935
+ const uint64_t FixupAddress =
936
+ SectionAddress + Cursor.tell () + (int32_t )DE.getU32 (Cursor);
937
+ const uint64_t Data = DE.getU32 (Cursor);
938
+
939
+ // Consume the status of the cursor.
940
+ if (!Cursor)
941
+ return createStringError (errc::executable_format_error,
942
+ " out of bounds while reading exception table" );
943
+
944
+ ++EntryID;
945
+
946
+ if (opts::DumpExceptions) {
947
+ BC.outs () << " Exception Entry: " << EntryID << ' \n ' ;
948
+ BC.outs () << " \t Insn: 0x" << Twine::utohexstr (InstAddress) << ' \n '
949
+ << " \t Fixup: 0x" << Twine::utohexstr (FixupAddress) << ' \n '
950
+ << " \t Data: 0x" << Twine::utohexstr (Data) << ' \n ' ;
951
+ }
952
+
953
+ MCInst *Inst = nullptr ;
954
+ MCSymbol *FixupLabel = nullptr ;
955
+
956
+ BinaryFunction *InstBF = BC.getBinaryFunctionContainingAddress (InstAddress);
957
+ if (InstBF && BC.shouldEmit (*InstBF)) {
958
+ Inst = InstBF->getInstructionAtOffset (InstAddress - InstBF->getAddress ());
959
+ if (!Inst)
960
+ return createStringError (errc::executable_format_error,
961
+ " no instruction at address 0x%" PRIx64
962
+ " in exception table" ,
963
+ InstAddress);
964
+ BC.MIB ->addAnnotation (*Inst, " ExceptionEntry" , EntryID);
965
+ FunctionsWithExceptions.insert (InstBF);
966
+ }
967
+
968
+ if (!InstBF && opts::Verbosity) {
969
+ BC.outs () << " BOLT-INFO: no function matches instruction at 0x"
970
+ << Twine::utohexstr (InstAddress)
971
+ << " referenced by Linux exception table\n " ;
972
+ }
973
+
974
+ BinaryFunction *FixupBF =
975
+ BC.getBinaryFunctionContainingAddress (FixupAddress);
976
+ if (FixupBF && BC.shouldEmit (*FixupBF)) {
977
+ const uint64_t Offset = FixupAddress - FixupBF->getAddress ();
978
+ if (!FixupBF->getInstructionAtOffset (Offset))
979
+ return createStringError (errc::executable_format_error,
980
+ " no instruction at fixup address 0x%" PRIx64
981
+ " in exception table" ,
982
+ FixupAddress);
983
+ FixupLabel = Offset ? FixupBF->addEntryPointAtOffset (Offset)
984
+ : FixupBF->getSymbol ();
985
+ if (Inst)
986
+ BC.MIB ->addAnnotation (*Inst, " Fixup" , FixupLabel->getName ());
987
+ FunctionsWithExceptions.insert (FixupBF);
988
+ }
989
+
990
+ if (!FixupBF && opts::Verbosity) {
991
+ BC.outs () << " BOLT-INFO: no function matches fixup code at 0x"
992
+ << Twine::utohexstr (FixupAddress)
993
+ << " referenced by Linux exception table\n " ;
994
+ }
995
+ }
996
+
997
+ BC.outs () << " BOLT-INFO: parsed "
998
+ << ExceptionsSection->getSize () / EXCEPTION_TABLE_ENTRY_SIZE
999
+ << " exception table entries\n " ;
1000
+
1001
+ return Error::success ();
1002
+ }
1003
+
1004
+ // / Depending on the value of CONFIG_BUILDTIME_TABLE_SORT, the kernel expects
1005
+ // / the exception table to be sorted. Hence we have to sort it after code
1006
+ // / reordering.
1007
+ Error LinuxKernelRewriter::rewriteExceptionTable () {
1008
+ // Disable output of functions with exceptions before rewrite support is
1009
+ // added.
1010
+ for (BinaryFunction *BF : FunctionsWithExceptions)
1011
+ BF->setSimple (false );
1012
+
1013
+ return Error::success ();
1014
+ }
1015
+
946
1016
} // namespace
947
1017
948
1018
std::unique_ptr<MetadataRewriter>
0 commit comments