Index: lld/ELF/Config.h =================================================================== --- lld/ELF/Config.h +++ lld/ELF/Config.h @@ -40,6 +40,9 @@ // For --discard-{all,locals,none}. enum class DiscardPolicy { Default, All, Locals, None }; +// For --icf={none,safe,all}. +enum class ICFLevel { None, Safe, All }; + // For --strip-{all,debug}. enum class StripPolicy { None, All, Debug }; @@ -137,7 +140,6 @@ bool GnuUnique; bool HasDynamicList = false; bool HasDynSymTab; - bool ICF; bool IgnoreDataAddressEquality; bool IgnoreFunctionAddressEquality; bool LTODebugPassManager; @@ -183,6 +185,7 @@ bool ZRetpolineplt; bool ZWxneeded; DiscardPolicy Discard; + ICFLevel ICF; OrphanHandlingPolicy OrphanHandling; SortSectionPolicy SortSection; StripPolicy Strip; Index: lld/ELF/Driver.cpp =================================================================== --- lld/ELF/Driver.cpp +++ lld/ELF/Driver.cpp @@ -51,6 +51,7 @@ #include "llvm/ADT/StringSwitch.h" #include "llvm/Support/CommandLine.h" #include "llvm/Support/Compression.h" +#include "llvm/Support/LEB128.h" #include "llvm/Support/Path.h" #include "llvm/Support/TarWriter.h" #include "llvm/Support/TargetSelect.h" @@ -294,7 +295,7 @@ error("-r and -shared may not be used together"); if (Config->GcSections) error("-r and --gc-sections may not be used together"); - if (Config->ICF) + if (Config->ICF != ICFLevel::None) error("-r and --icf may not be used together"); if (Config->Pie) error("-r and -pie may not be used together"); @@ -497,6 +498,15 @@ return Arg->getValue(); } +static ICFLevel getICF(opt::InputArgList &Args) { + auto *Arg = Args.getLastArg(OPT_icf_none, OPT_icf_safe, OPT_icf_all); + if (!Arg || Arg->getOption().getID() == OPT_icf_none) + return ICFLevel::None; + if (Arg->getOption().getID() == OPT_icf_safe) + return ICFLevel::Safe; + return ICFLevel::All; +} + static StripPolicy getStrip(opt::InputArgList &Args) { if (Args.hasArg(OPT_relocatable)) return StripPolicy::None; @@ -710,7 +720,7 @@ Config->GcSections = Args.hasFlag(OPT_gc_sections, OPT_no_gc_sections, false); Config->GnuUnique = Args.hasFlag(OPT_gnu_unique, OPT_no_gnu_unique, true); Config->GdbIndex = Args.hasFlag(OPT_gdb_index, OPT_no_gdb_index, false); - Config->ICF = Args.hasFlag(OPT_icf_all, OPT_icf_none, false); + Config->ICF = getICF(Args); Config->IgnoreDataAddressEquality = Args.hasArg(OPT_ignore_data_address_equality); Config->IgnoreFunctionAddressEquality = @@ -1187,14 +1197,51 @@ // Record sections that define symbols mentioned in --keep-unique // these sections are inelligible for ICF. +template static void findKeepUniqueSections(opt::InputArgList &Args) { + auto KeepUnique = [](Symbol *S) -> bool { + if (auto *D = dyn_cast_or_null(S)) { + if (D->Section) { + D->Section->KeepUnique = true; + return true; + } + } + return false; + }; + for (auto *Arg : Args.filtered(OPT_keep_unique)) { StringRef Name = Arg->getValue(); - if (auto *Sym = dyn_cast_or_null(Symtab->find(Name))) - Sym->Section->KeepUnique = true; - else + if (!KeepUnique(Symtab->find(Name))) warn("could not find symbol " + Name + " to keep unique"); } + + if (Config->ICF == ICFLevel::Safe) { + for (Symbol *S : Symtab->getSymbols()) + if (S->includeInDynsym()) + KeepUnique(S); + + for (InputFile *F : ObjectFiles) { + auto *Obj = cast>(F); + ArrayRef Syms = Obj->getSymbols(); + if (Obj->AddrsigSec) { + ArrayRef Contents = + check(Obj->getObj().getSectionContents(Obj->AddrsigSec)); + const uint8_t *Cur = Contents.begin(); + while (Cur != Contents.end()) { + unsigned Size; + const char *Err; + uint64_t SymIndex = decodeULEB128(Cur, &Size, Contents.end(), &Err); + if (Err) + fatal(F->getName() + ": could not decode addrsig section: " + Err); + KeepUnique(Syms[SymIndex]); + Cur += Size; + } + } else { + for (Symbol *S : Syms) + KeepUnique(S); + } + } + } } // Do actual linking. Note that when this function is called, @@ -1369,8 +1416,8 @@ markLive(); demoteSymbols(); mergeSections(); - if (Config->ICF) { - findKeepUniqueSections(Args); + if (Config->ICF != ICFLevel::None) { + findKeepUniqueSections(Args); doIcf(); } Index: lld/ELF/ICF.cpp =================================================================== --- lld/ELF/ICF.cpp +++ lld/ELF/ICF.cpp @@ -174,7 +174,8 @@ // Don't merge read only data sections unless // --ignore-data-address-equality was passed. - if (!(S->Flags & SHF_EXECINSTR) && !Config->IgnoreDataAddressEquality) + if (!(S->Flags & SHF_EXECINSTR) && + !(Config->IgnoreDataAddressEquality || Config->ICF == ICFLevel::Safe)) return false; // Don't merge synthetic sections as their Data member is not valid and empty. Index: lld/ELF/InputFiles.h =================================================================== --- lld/ELF/InputFiles.h +++ lld/ELF/InputFiles.h @@ -210,6 +210,9 @@ // symbol table. StringRef SourceFile; + // Pointer to this input file's .llvm_addrsig section, if it has one. + const Elf_Shdr *AddrsigSec = nullptr; + private: void initializeSections(llvm::DenseSet &ComdatGroups); Index: lld/ELF/InputFiles.cpp =================================================================== --- lld/ELF/InputFiles.cpp +++ lld/ELF/InputFiles.cpp @@ -428,6 +428,13 @@ // if -r is given, we'll let the final link discard such sections. // This is compatible with GNU. if ((Sec.sh_flags & SHF_EXCLUDE) && !Config->Relocatable) { + if (Sec.sh_type == SHT_LLVM_ADDRSIG) { + if (Sec.sh_link != 0) + this->AddrsigSec = &Sec; + else if (Config->ICF == ICFLevel::Safe) + warn(toString(this) + ": --icf=safe is incompatible with object " + "files created using objcopy or ld -r"); + } this->Sections[I] = &InputSection::Discarded; continue; } Index: lld/ELF/Options.td =================================================================== --- lld/ELF/Options.td +++ lld/ELF/Options.td @@ -170,6 +170,8 @@ def icf_all: F<"icf=all">, HelpText<"Enable identical code folding">; +def icf_safe: F<"icf=safe">, HelpText<"Enable safe identical code folding">; + def icf_none: F<"icf=none">, HelpText<"Disable identical code folding (default)">; def ignore_function_address_equality: F<"ignore-function-address-equality">,