Index: lld/ELF/SyntheticSections.cpp =================================================================== --- lld/ELF/SyntheticSections.cpp +++ lld/ELF/SyntheticSections.cpp @@ -1679,6 +1679,56 @@ relativeGroups.emplace_back(std::move(group)); } + // For non-relative relocations, we would like to: + // 1. Have relocations with the same symbol offset to be consecutive, so + // that the runtime linker can speed-up symbol lookup by implementing an + // 1-entry cache. + // 2. Group relocations by r_info to reduce the size of the relocation + // section. + // Since the symbol offset is the high bits in r_info, sorting by r_info + // allows us to do both. + // + // For Rela, we also want to sort by r_addend when r_info is the same. This + // enables us to group by r_addend as well. + llvm::stable_sort(nonRelatives, [](const Elf_Rela &a, const Elf_Rela &b) { + if (a.r_info != b.r_info) + return a.r_info < b.r_info; + if (config->isRela) + return a.r_addend < b.r_addend; + return false; + }); + + // Group relocations with the same r_info. Note that each group emits a group + // header and that may make the relocation section larger. It is hard to + // estimate the size of a group header as the encoded size of that varies + // based on r_info. However, we can approximate this trade-off by the number + // of values encoded. Each group header contains 3 values, and each + // relocation in a group encodes one less value, as compared to when it is + // not grouped. Therefore, we only group relocations if there are 3 or more + // of them with the same r_info. + // + // For Rela, the addend for most non-relative relocations is zero, and thus + // we can usually get a smaller relocation section if we group by addend + // as well. + std::vector ungroupedNonRelatives; + std::vector> nonRelativeGroups; + for (auto i = nonRelatives.begin(), e = nonRelatives.end(); i != e;) { + auto j = i + 1; + while (j != e && i->r_info == j->r_info && + (!config->isRela || i->r_addend == j->r_addend)) + ++j; + if (j - i < 3) + ungroupedNonRelatives.insert(ungroupedNonRelatives.end(), i, j); + else + nonRelativeGroups.emplace_back(i, j); + i = j; + } + + // Sort ungrouped relocations by offset to minimize the encoded length. + llvm::sort(ungroupedNonRelatives, [](const Elf_Rela &a, const Elf_Rela &b) { + return a.r_offset < b.r_offset; + }); + unsigned hasAddendIfRela = config->isRela ? RELOCATION_GROUP_HAS_ADDEND_FLAG : 0; @@ -1733,14 +1783,30 @@ } } - // Finally the non-relative relocations. - llvm::sort(nonRelatives, [](const Elf_Rela &a, const Elf_Rela &b) { - return a.r_offset < b.r_offset; - }); - if (!nonRelatives.empty()) { - add(nonRelatives.size()); + const unsigned groupedByAddendIfRela = + config->isRela ? RELOCATION_GROUPED_BY_ADDEND_FLAG : 0; + + // Grouped non-relatives. + for (std::vector &g : nonRelativeGroups) { + add(g.size()); + add(RELOCATION_GROUPED_BY_INFO_FLAG | hasAddendIfRela | + groupedByAddendIfRela); + add(g[0].r_info); + if (config->isRela) { + add(g[0].r_addend - addend); + addend = g[0].r_addend; + } + for (Elf_Rela &r : g) { + add(r.r_offset - offset); + offset = r.r_offset; + } + } + + // Finally the ungrouped non-relative relocations. + if (!ungroupedNonRelatives.empty()) { + add(ungroupedNonRelatives.size()); add(hasAddendIfRela); - for (Elf_Rela &r : nonRelatives) { + for (Elf_Rela &r : ungroupedNonRelatives) { add(r.r_offset - offset); offset = r.r_offset; add(r.r_info);