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