@@ -50,6 +50,7 @@ class X86_64 : public TargetInfo {
5050 bool deleteFallThruJmpInsn (InputSection &is, InputFile *file,
5151 InputSection *nextIS) const override ;
5252 bool relaxOnce (int pass) const override ;
53+ void relaxCFIJumpTables () const override ;
5354 void applyBranchToBranchOpt () const override ;
5455
5556private:
@@ -317,6 +318,177 @@ bool X86_64::deleteFallThruJmpInsn(InputSection &is, InputFile *file,
317318 return true ;
318319}
319320
321+ void X86_64::relaxCFIJumpTables () const {
322+ // Relax CFI jump tables.
323+ // - Split jump table into pieces and place target functions inside the jump
324+ // table if small enough.
325+ // - Move jump table before last called function and delete last branch
326+ // instruction.
327+ std::map<InputSection *, std::vector<InputSection *>> sectionReplacements;
328+ SmallVector<InputSection *, 0 > storage;
329+ for (OutputSection *osec : ctx.outputSections ) {
330+ if (!(osec->flags & SHF_EXECINSTR))
331+ continue ;
332+ for (InputSection *sec : getInputSections (*osec, storage)) {
333+ if (sec->type != SHT_LLVM_CFI_JUMP_TABLE || sec->entsize == 0 ||
334+ sec->size % sec->entsize != 0 )
335+ continue ;
336+
337+ // We're going to replace the jump table with this list of sections. This
338+ // list will be made up of slices of the original section and function
339+ // bodies that were moved into the jump table.
340+ std::vector<InputSection *> replacements;
341+
342+ // First, push the original jump table section. This is only so that it
343+ // can act as a relocation target. Later on, we will set the size of the
344+ // jump table section to 0 so that the slices and moved function bodies
345+ // become the actual relocation targets.
346+ replacements.push_back (sec);
347+
348+ // Add the slice [begin, end) of the original section to the replacement
349+ // list. [rbegin, rend) is the slice of the relocation list that covers
350+ // [begin, end).
351+ auto addSectionSlice = [&](size_t begin, size_t end, Relocation *rbegin,
352+ Relocation *rend) {
353+ auto *slice = make<InputSection>(
354+ sec->file , sec->name , sec->type , sec->flags , sec->entsize ,
355+ sec->entsize ,
356+ sec->contentMaybeDecompress ().slice (begin, end - begin));
357+ // Ensure that --preferred-function-alignment does not mess with the
358+ // placement of this section.
359+ slice->retainAlignment = true ;
360+ for (const Relocation &r : ArrayRef<Relocation>(rbegin, rend)) {
361+ slice->relocations .push_back (
362+ Relocation{r.expr , r.type , r.offset - begin, r.addend , r.sym });
363+ }
364+ replacements.push_back (slice);
365+ };
366+
367+ // r is the only relocation in a jump table entry. Figure out whether it
368+ // is a branch pointing to the start of a statically known section that
369+ // hasn't already been moved while processing a different jump table
370+ // section, and if so return it.
371+ auto getMovableSection = [&](Relocation &r) -> InputSection * {
372+ if (r.type != R_X86_64_PC32 && r.type != R_X86_64_PLT32)
373+ return nullptr ;
374+ auto *sym = dyn_cast_or_null<Defined>(r.sym );
375+ if (!sym || sym->isPreemptible || sym->isGnuIFunc () ||
376+ sym->value + r.addend != -4ull )
377+ return nullptr ;
378+ auto *target = dyn_cast_or_null<InputSection>(sym->section );
379+ if (!target || target->addralign > sec->entsize ||
380+ sectionReplacements.count (target))
381+ return nullptr ;
382+ return target;
383+ };
384+
385+ // Figure out the movable section for the last entry. We do this first
386+ // because the last entry controls which output section the jump table is
387+ // placed into, which affects move eligibility for other sections.
388+ auto *lastSec = [&]() -> InputSection * {
389+ Relocation *lastReloc = sec->relocs ().end ();
390+ while (lastReloc != sec->relocs ().begin () &&
391+ (lastReloc - 1 )->offset >= sec->size - sec->entsize )
392+ --lastReloc;
393+ if (lastReloc + 1 != sec->relocs ().end ())
394+ return nullptr ;
395+ return getMovableSection (*lastReloc);
396+ }();
397+ OutputSection *targetOutputSec;
398+ if (lastSec) {
399+ // We've already decided to move the output section so make sure that we
400+ // don't try to move it again.
401+ sectionReplacements[lastSec] = replacements;
402+ targetOutputSec = lastSec->getParent ();
403+ } else {
404+ targetOutputSec = sec->getParent ();
405+ }
406+
407+ // Walk the jump table entries other than the last one looking for sections
408+ // that are small enough to be moved into the jump table and in the same
409+ // section as the jump table's destination.
410+ size_t begin = 0 ;
411+ Relocation *rbegin = sec->relocs ().begin ();
412+ size_t cur = begin;
413+ Relocation *rcur = rbegin;
414+ while (cur != sec->size - sec->entsize ) {
415+ size_t next = cur + sec->entsize ;
416+ Relocation *rnext = rcur;
417+ while (rnext != sec->relocs ().end () && rnext->offset < next)
418+ ++rnext;
419+ if (rcur + 1 == rnext) {
420+ InputSection *target = getMovableSection (*rcur);
421+ if (target && target->size <= sec->entsize &&
422+ target->getParent () == targetOutputSec) {
423+ // Okay, we found a small enough section. Move it into the jump
424+ // table. First add a slice for the unmodified jump table entries
425+ // before this one.
426+ addSectionSlice (begin, cur, rbegin, rcur);
427+ // Ensure that --preferred-function-alignment does not mess with the
428+ // placement of this section.
429+ target->retainAlignment = true ;
430+ // Add the target to our replacement list, and set the target's
431+ // replacement list to the empty list. This removes it from its
432+ // original position and adds it here, as well as causing
433+ // future getMovableSection() queries to return nullptr.
434+ replacements.push_back (target);
435+ sectionReplacements[target] = {};
436+ begin = next;
437+ rbegin = rnext;
438+ }
439+ }
440+ cur = next;
441+ rcur = rnext;
442+ }
443+
444+ // Finally, process the last entry. If it is movable, move the entire
445+ // jump table behind it and delete the last entry (so that the last
446+ // function's body acts as the last jump table entry), otherwise leave the
447+ // jump table where it is and keep the last entry.
448+ if (lastSec) {
449+ addSectionSlice (begin, cur, rbegin, rcur);
450+ lastSec->retainAlignment = true ;
451+ replacements.push_back (lastSec);
452+ sectionReplacements[sec] = {};
453+ sectionReplacements[lastSec] = replacements;
454+ for (auto *s : replacements)
455+ s->parent = lastSec->parent ;
456+ } else {
457+ addSectionSlice (begin, sec->size , rbegin, sec->relocs ().end ());
458+ sectionReplacements[sec] = replacements;
459+ for (auto *s : replacements)
460+ s->parent = sec->parent ;
461+ }
462+
463+ // Everything from the original section has been recreated, so delete the
464+ // original contents.
465+ sec->relocations .clear ();
466+ sec->size = 0 ;
467+ }
468+ }
469+
470+ // Now that we have the complete mapping of replacements, go through the input
471+ // section lists and apply the replacements.
472+ for (OutputSection *osec : ctx.outputSections ) {
473+ if (!(osec->flags & SHF_EXECINSTR))
474+ continue ;
475+ for (SectionCommand *cmd : osec->commands ) {
476+ auto *isd = dyn_cast<InputSectionDescription>(cmd);
477+ if (!isd)
478+ continue ;
479+ SmallVector<InputSection *> newSections;
480+ for (auto *sec : isd->sections ) {
481+ auto i = sectionReplacements.find (sec);
482+ if (i == sectionReplacements.end ())
483+ newSections.push_back (sec);
484+ else
485+ newSections.append (i->second .begin (), i->second .end ());
486+ }
487+ isd->sections = std::move (newSections);
488+ }
489+ }
490+ }
491+
320492bool X86_64::relaxOnce (int pass) const {
321493 uint64_t minVA = UINT64_MAX, maxVA = 0 ;
322494 for (OutputSection *osec : ctx.outputSections ) {
0 commit comments