Index: lld/ELF/Config.h =================================================================== --- lld/ELF/Config.h +++ lld/ELF/Config.h @@ -149,6 +149,7 @@ bool checkSections; bool compressDebugSections; bool cref; + bool CtorsInInitArray; std::vector> deadRelocInNonAlloc; bool defineCommon; bool demangle = true; Index: lld/ELF/Driver.cpp =================================================================== --- lld/ELF/Driver.cpp +++ lld/ELF/Driver.cpp @@ -946,6 +946,8 @@ config->chroot = args.getLastArgValue(OPT_chroot); config->compressDebugSections = getCompressDebugSections(args); config->cref = args.hasFlag(OPT_cref, OPT_no_cref, false); + config->CtorsInInitArray = + args.hasFlag(OPT_ctors_in_init_array, OPT_no_ctors_in_init_array, true); config->defineCommon = args.hasFlag(OPT_define_common, OPT_no_define_common, !args.hasArg(OPT_relocatable)); config->optimizeBBJumps = @@ -2336,6 +2338,10 @@ { llvm::TimeTraceScope timeScope("Assign sections"); + // Convert .[cd]tors to .{init,fini}_array if needed. + if (config->CtorsInInitArray) + createInitFiniSections(); + // Create output sections described by SECTIONS commands. script->processSectionCommands(); Index: lld/ELF/InputSection.h =================================================================== --- lld/ELF/InputSection.h +++ lld/ELF/InputSection.h @@ -403,6 +403,7 @@ // The list of all input sections. extern std::vector inputSections; +extern llvm::DenseSet ctorsInInitArraySection; // The set of TOC entries (.toc + addend) for which we should not apply // toc-indirect to toc-relative relaxation. const Symbol * refers to the Index: lld/ELF/InputSection.cpp =================================================================== --- lld/ELF/InputSection.cpp +++ lld/ELF/InputSection.cpp @@ -41,6 +41,7 @@ using namespace lld::elf; std::vector elf::inputSections; +llvm::DenseSet elf::ctorsInInitArraySection; DenseSet> elf::ppc64noTocRelax; // Returns a string to construct an error message. @@ -1259,14 +1260,31 @@ ": uncompress failed: " + llvm::toString(std::move(e))); uint8_t *bufEnd = buf + outSecOff + size; relocate(buf + outSecOff, bufEnd); - return; + } else { + // Copy section contents from source object file to output file + // and then apply relocations. + memcpy(buf + outSecOff, data().data(), data().size()); + uint8_t *bufEnd = buf + outSecOff + data().size(); + relocate(buf + outSecOff, bufEnd); } - // Copy section contents from source object file to output file - // and then apply relocations. - memcpy(buf + outSecOff, data().data(), data().size()); - uint8_t *bufEnd = buf + outSecOff + data().size(); - relocate(buf + outSecOff, bufEnd); + // A .[cd]tors section contains function pointers that are executed at + // runtime from the last one to the first one. .{init,fini}_array also + // contains function pointers, but the order the runtime calls them is the + // opposite. So, when we convert a .[cd]tors to .{init,fini}_array, we need + // to reverse the contents. + if (ctorsInInitArraySection.find(this) != ctorsInInitArraySection.end()) { + auto size = data().size(); + if (config->is64) { + MutableArrayRef data = makeMutableArrayRef( + (uint64_t*)(buf + outSecOff), size/sizeof(uint64_t)); + std::reverse(data.begin(), data.end()); + } else { + MutableArrayRef data = makeMutableArrayRef( + (uint32_t*)(buf + outSecOff), size /sizeof(uint32_t)); + std::reverse(data.begin(), data.end()); + } + } } void InputSection::replace(InputSection *other) { Index: lld/ELF/Options.td =================================================================== --- lld/ELF/Options.td +++ lld/ELF/Options.td @@ -128,6 +128,10 @@ "Output cross reference table", "Do not output cross reference table">; +defm ctors_in_init_array: B<"ctors-in-init-array", + "Merge .ctors/.dtors with .init_array/.fini_array", + "Do not merge .ctors/.dtors with .init_array/.fini_array">; + defm define_common: B<"define-common", "Assign space to common symbols", "Do not assign space to common symbols">; @@ -676,7 +680,6 @@ def: F<"long-plt">; def: F<"no-add-needed">; def: F<"no-copy-dt-needed-entries">; -def: F<"no-ctors-in-init-array">; def: F<"no-keep-memory">; def: F<"no-pipeline-knowledge">; def: F<"no-relax">; Index: lld/ELF/OutputSections.h =================================================================== --- lld/ELF/OutputSections.h +++ lld/ELF/OutputSections.h @@ -108,7 +108,7 @@ void sortInitFini(); void sortCtorsDtors(); -private: +public: // Used for implementation of --compress-debug-sections option. std::vector zDebugHeader; llvm::SmallVector compressedData; @@ -117,6 +117,8 @@ }; int getPriority(StringRef s); +bool isCrtbegin(StringRef s); +bool isCrtend(StringRef s); InputSection *getFirstInputSection(const OutputSection *os); std::vector getInputSections(const OutputSection *os); Index: lld/ELF/OutputSections.cpp =================================================================== --- lld/ELF/OutputSections.cpp +++ lld/ELF/OutputSections.cpp @@ -439,13 +439,13 @@ // Gcc uses any of crtbegin[|S|T].o. // Clang uses Gcc's plus clang_rt.crtbegin[|S|T][-|].o. -static bool isCrtbegin(StringRef s) { +bool elf::isCrtbegin(StringRef s) { static std::regex re(R"((clang_rt\.)?crtbegin[ST]?(-.*)?\.o)"); s = sys::path::filename(s); return std::regex_match(s.begin(), s.end(), re); } -static bool isCrtend(StringRef s) { +bool elf::isCrtend(StringRef s) { static std::regex re(R"((clang_rt\.)?crtend[ST]?(-.*)?\.o)"); s = sys::path::filename(s); return std::regex_match(s.begin(), s.end(), re); Index: lld/ELF/SyntheticSections.h =================================================================== --- lld/ELF/SyntheticSections.h +++ lld/ELF/SyntheticSections.h @@ -1148,6 +1148,7 @@ MergeInputSection *createCommentSection(); MergeSyntheticSection *createMergeSynthetic(StringRef name, uint32_t type, uint64_t flags, uint32_t alignment); +InputSection *createInitFiniSection(InputSection *Sec); template void splitSections(); template void writeEhdr(uint8_t *buf, Partition &part); Index: lld/ELF/SyntheticSections.cpp =================================================================== --- lld/ELF/SyntheticSections.cpp +++ lld/ELF/SyntheticSections.cpp @@ -88,6 +88,46 @@ getVersion(), ".comment"); } +// Returns an .{init,fini}_array section name for a given .ctors/.dtors +// section name. For example, it returns ".init_array.10000" for +// ".ctors.55535". +// +// Section names may include priorities, e.g. .ctors.30 or .init_array.101. +// .ctors.65535 is the highest priority while .init_array.0 is the highest. +// So we need to translate the number. +static StringRef toInitFiniName(StringRef S) { + // .ctors without any priority should have the lowest priority (i.e. the + // greatest number) than any .init_array, so priority 65537 is assigned. + if (S == ".ctors") + return ".init_array.65537"; + if (S == ".dtors") + return ".fini_array.65537"; + + assert(S.startswith(".ctors.") || S.startswith(".dtors.")); + StringRef Prefix = S.startswith(".c") ? ".init_array." : ".fini_array."; + + uint16_t N = 0; + if (!to_integer(S.substr(7), N, 10)) + return S; + auto *Outs = make>(); + return Twine(Prefix + Twine(65535 - N)).toStringRef(*Outs); +} + +// Create an .{init,fini}_array section from a given .ctors/.dtors section. +// +// Both types of sections are to call constructors or destructors of +// global objects. .{init,fini}_array are newer, and in most systems, +// they are used exclusively. However, there are still old object files +// out there that contain .ctors/.dtors, so we need to handle them by +// converting them to .init/fini. +InputSection *elf::createInitFiniSection(InputSection *Sec) { + uint32_t Type = + Sec->name.startswith(".ctors") ? SHT_INIT_ARRAY : SHT_FINI_ARRAY; + Sec->type = Type; + Sec->name = toInitFiniName(Sec->name); + return Sec; +} + // .MIPS.abiflags section. template MipsAbiFlagsSection::MipsAbiFlagsSection(Elf_Mips_ABIFlags flags) Index: lld/ELF/Writer.h =================================================================== --- lld/ELF/Writer.h +++ lld/ELF/Writer.h @@ -22,6 +22,7 @@ class InputSectionBase; void copySectionsIntoPartitions(); template void createSyntheticSections(); +template void createInitFiniSections(); void combineEhSections(); template void writeResult(); Index: lld/ELF/Writer.cpp =================================================================== --- lld/ELF/Writer.cpp +++ lld/ELF/Writer.cpp @@ -578,6 +578,35 @@ add(in.strTab); } +// Replace .[cd]tors sections with .{init,fini}_array sections. +template +void elf::createInitFiniSections() { + for (size_t I = 0; I < inputSections.size(); ++I) { + InputSection *Sec = dyn_cast(inputSections[I]); + if (!Sec || !Sec->isLive()) + continue; + + if (Sec->name != ".ctors" && !Sec->name.startswith(".ctors.") && + Sec->name != ".dtors" && !Sec->name.startswith(".dtors.")) { + continue; + } + + // As a special rule, .[cd]tors sections in crtbeginS.o or crtendS.o + // shouldn't be converted because the runtime expect them to be in the + // original section. This is ugly, but without this, the resulting + // exectuable would crash on startup. + if (Sec->name == ".ctors" || Sec->name == ".dtors") { + StringRef S = Sec->file->getName(); + if (isCrtbegin(S) || isCrtend(S)) { + continue; + } + } + + inputSections[I] = createInitFiniSection(Sec); + ctorsInInitArraySection.insert(inputSections[I]); + } +} + // The main function of the writer. template void Writer::run() { copyLocalSymbols(); @@ -2202,8 +2231,9 @@ // Fill other section headers. The dynamic table is finalized // at the end because some tags like RELSZ depend on result // of finalizing other sections. - for (OutputSection *sec : outputSections) + for (OutputSection *sec : outputSections) { sec->finalize(); + } } // Ensure data sections are not mixed with executable sections when @@ -3035,6 +3065,11 @@ template void elf::createSyntheticSections(); template void elf::createSyntheticSections(); +template void elf::createInitFiniSections(); +template void elf::createInitFiniSections(); +template void elf::createInitFiniSections(); +template void elf::createInitFiniSections(); + template void elf::writeResult(); template void elf::writeResult(); template void elf::writeResult(); Index: lld/test/ELF/ctors-in-init-array.s =================================================================== --- /dev/null +++ lld/test/ELF/ctors-in-init-array.s @@ -0,0 +1,89 @@ +# REQUIRES: x86 + +# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %s -o %t.o + +# RUN: mkdir -p %t.dir +# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux \ +# RUN: %p/Inputs/ctors_dtors_priority1.s -o %t.dir/crtbeginS.o + +# RUN: ld.lld %t.o %t.dir/crtbeginS.o -o %t1.exe +# RUN: llvm-objdump -s %t1.exe | FileCheck %s + +# RUN: ld.lld %t.o %t.dir/crtbeginS.o -o %t2.exe --no-ctors-in-init-array +# RUN: llvm-objdump -s %t2.exe | FileCheck -check-prefix=CTORS %s + +.globl _start +_start: + nop + +.section .ctors, "aw", @progbits + .quad 8 +.section .ctors.100, "aw", @progbits + .quad 2 +.section .ctors.005, "aw", @progbits + .quad 3 +.section .ctors, "aw", @progbits + .quad 7 +.section .ctors, "aw", @progbits + .quad 6 + +.section .init_array.1, "aw", @init_array + .quad 1 +.section .init_array.65535, "aw", @init_array + .quad 4 +.section .init_array, "aw", @init_array + .quad 5 + +.section .dtors, "aw", @progbits + .quad 0x18 +.section .dtors.100, "aw", @progbits + .quad 0x12 +.section .dtors.005, "aw", @progbits + .quad 0x13 +.section .dtors, "aw", @progbits + .quad 0x17 +.section .dtors, "aw", @progbits + .quad 0x16 + +.section .fini_array.1, "aw", @fini_array + .quad 0x11 +.section .fini_array.65535, "aw", @fini_array + .quad 0x14 +.section .fini_array, "aw", @fini_array + .quad 0x15 + +# CHECK: Contents of section .init_array: +# CHECK-NEXT: 202194 01000000 00000000 02000000 00000000 +# CHECK-NEXT: 2021a4 03000000 00000000 04000000 00000000 +# CHECK-NEXT: 2021b4 05000000 00000000 06000000 00000000 +# CHECK-NEXT: 2021c4 07000000 00000000 08000000 00000000 + +# CHECK: Contents of section .fini_array: +# CHECK-NEXT: 2021d4 11000000 00000000 12000000 00000000 +# CHECK-NEXT: 2021e4 13000000 00000000 14000000 00000000 +# CHECK-NEXT: 2021f4 15000000 00000000 16000000 00000000 +# CHECK-NEXT: 202204 17000000 00000000 18000000 00000000 + +# CHECK: Contents of section .ctors: +# CHECK-NEXT: 202214 a1000000 00000000 + +# CHECK: Contents of section .dtors: +# CHECK-NEXT: 20221c a2000000 00000000 + +# CTORS: Contents of section .ctors: +# CTORS-NEXT: 202194 a1000000 00000000 08000000 00000000 +# CTORS-NEXT: 2021a4 07000000 00000000 06000000 00000000 +# CTORS-NEXT: 2021b4 03000000 00000000 02000000 00000000 + +# CTORS: Contents of section .init_array: +# CTORS-NEXT: 2021c4 01000000 00000000 04000000 00000000 +# CTORS-NEXT: 2021d4 05000000 00000000 + +# CTORS: Contents of section .dtors: +# CTORS-NEXT: 2021dc a2000000 00000000 18000000 00000000 +# CTORS-NEXT: 2021ec 17000000 00000000 16000000 00000000 +# CTORS-NEXT: 2021fc 13000000 00000000 12000000 00000000 + +# CTORS: Contents of section .fini_array: +# CTORS-NEXT: 20220c 11000000 00000000 14000000 00000000 +# CTORS-NEXT: 20221c 15000000 00000000 \ No newline at end of file Index: lld/test/ELF/ctors_dtors_priority.s =================================================================== --- lld/test/ELF/ctors_dtors_priority.s +++ lld/test/ELF/ctors_dtors_priority.s @@ -10,17 +10,17 @@ // RUN: %p/Inputs/ctors_dtors_priority2.s -o %t2 // RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux \ // RUN: %p/Inputs/ctors_dtors_priority3.s -o %t/crtend.o -// RUN: ld.lld %t1 %t2 %t/crtend.o %t/crtbegin.o -o %t.exe +// RUN: ld.lld --no-ctors-in-init-array %t1 %t2 %t/crtend.o %t/crtbegin.o -o %t.exe // RUN: llvm-objdump -s %t.exe | FileCheck %s // RUN: cp %t/crtbegin.o %t/clang_rt.crtbegin.o // RUN: cp %t/crtend.o %t/clang_rt.crtend.o -// RUN: ld.lld %t1 %t2 %t/clang_rt.crtend.o %t/clang_rt.crtbegin.o -o %t.clang_rt.exe +// RUN: ld.lld --no-ctors-in-init-array %t1 %t2 %t/clang_rt.crtend.o %t/clang_rt.crtbegin.o -o %t.clang_rt.exe // RUN: llvm-objdump -s %t.clang_rt.exe | FileCheck %s // RUN: cp %t/crtbegin.o %t/clang_rt.crtbegin-x86_64.o // RUN: cp %t/crtend.o %t/clang_rt.crtend-x86_64.o -// RUN: ld.lld %t1 %t2 %t/clang_rt.crtend-x86_64.o %t/clang_rt.crtbegin-x86_64.o -o %t.clang_rt-arch.exe +// RUN: ld.lld --no-ctors-in-init-array %t1 %t2 %t/clang_rt.crtend-x86_64.o %t/clang_rt.crtbegin-x86_64.o -o %t.clang_rt-arch.exe // RUN: llvm-objdump -s %t.clang_rt-arch.exe | FileCheck %s Index: lld/test/ELF/gc-sections.s =================================================================== --- lld/test/ELF/gc-sections.s +++ lld/test/ELF/gc-sections.s @@ -1,11 +1,11 @@ # REQUIRES: x86 # RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %s -o %t -# RUN: ld.lld %t -o %t2 +# RUN: ld.lld --no-ctors-in-init-array %t -o %t2 # RUN: llvm-readobj --sections --symbols %t2 | FileCheck -check-prefix=NOGC %s -# RUN: ld.lld --gc-sections %t -o %t2 +# RUN: ld.lld --no-ctors-in-init-array --gc-sections %t -o %t2 # RUN: llvm-readobj --sections --symbols %t2 | FileCheck -check-prefix=GC1 %s -# RUN: ld.lld --export-dynamic --gc-sections %t -o %t2 +# RUN: ld.lld --no-ctors-in-init-array --export-dynamic --gc-sections %t -o %t2 # RUN: llvm-readobj --sections --symbols %t2 | FileCheck -check-prefix=GC2 %s # NOGC: Name: .eh_frame Index: lld/test/ELF/linkerscript/sort-init.s =================================================================== --- lld/test/ELF/linkerscript/sort-init.s +++ lld/test/ELF/linkerscript/sort-init.s @@ -3,7 +3,7 @@ # RUN: split-file %s %t # RUN: llvm-mc -filetype=obj -triple=x86_64 %t/asm -o %t.o -# RUN: ld.lld -T %t/lds %t.o -o %t.out +# RUN: ld.lld --no-ctors-in-init-array -T %t/lds %t.o -o %t.out # RUN: llvm-readelf -x .init_array %t.out | FileCheck %s # CHECK: Hex dump of section '.init_array':