Index: lld/ELF/Config.h =================================================================== --- lld/ELF/Config.h +++ lld/ELF/Config.h @@ -115,6 +115,7 @@ bool CheckSections; bool CompressDebugSections; bool Cref; + bool CtorsInInitArray; bool DefineCommon; bool Demangle = true; bool DisableVerify; Index: lld/ELF/Driver.cpp =================================================================== --- lld/ELF/Driver.cpp +++ lld/ELF/Driver.cpp @@ -630,6 +630,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->Demangle = Args.hasFlag(OPT_demangle, OPT_no_demangle, true); Index: lld/ELF/Options.td =================================================================== --- lld/ELF/Options.td +++ lld/ELF/Options.td @@ -79,6 +79,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">; @@ -440,7 +444,6 @@ def no_add_needed: F<"no-add-needed">; def no_allow_shlib_undefined: F<"no-allow-shlib-undefined">; def no_copy_dt_needed_entries: F<"no-copy-dt-needed-entries">; -def no_ctors_in_init_array: F<"no-ctors-in-init-array">; def no_keep_memory: F<"no-keep-memory">; def no_mmap_output_file: F<"no-mmap-output-file">; def no_warn_mismatch: F<"no-warn-mismatch">; Index: lld/ELF/OutputSections.h =================================================================== --- lld/ELF/OutputSections.h +++ lld/ELF/OutputSections.h @@ -120,6 +120,8 @@ }; int getPriority(StringRef S); +bool isCrtbegin(StringRef S); +bool isCrtend(StringRef S); std::vector getInputSections(OutputSection* OS); Index: lld/ELF/OutputSections.cpp =================================================================== --- lld/ELF/OutputSections.cpp +++ lld/ELF/OutputSections.cpp @@ -328,8 +328,8 @@ return !S.empty() && S.drop_back().endswith(Filename); } -static bool isCrtbegin(StringRef S) { return isCrtBeginEnd(S, "crtbegin"); } -static bool isCrtend(StringRef S) { return isCrtBeginEnd(S, "crtend"); } +bool elf::isCrtbegin(StringRef S) { return isCrtBeginEnd(S, "crtbegin"); } +bool elf::isCrtend(StringRef S) { return isCrtBeginEnd(S, "crtend"); } // .ctors and .dtors are sorted by this priority from highest to lowest. // Index: lld/ELF/SyntheticSections.h =================================================================== --- lld/ELF/SyntheticSections.h +++ lld/ELF/SyntheticSections.h @@ -833,6 +833,7 @@ InputSection *createInterpSection(); MergeInputSection *createCommentSection(); +InputSection *createInitFiniSection(InputSection *Sec); void decompressSections(); void mergeSections(); Index: lld/ELF/SyntheticSections.cpp =================================================================== --- lld/ELF/SyntheticSections.cpp +++ lld/ELF/SyntheticSections.cpp @@ -84,6 +84,61 @@ 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; + return Saver.save(Prefix + Twine(65535 - N)); +} + +// 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) { + auto *Contents = make>(Sec->Data.size()); + + // 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 (Config->Is64) + llvm::copy(llvm::reverse(Sec->getDataAs()), + reinterpret_cast(Contents->data())); + else + llvm::copy(llvm::reverse(Sec->getDataAs()), + reinterpret_cast(Contents->data())); + + uint32_t Type = + Sec->Name.startswith(".ctors") ? SHT_INIT_ARRAY : SHT_FINI_ARRAY; + + auto *Ret = make(nullptr, SHF_ALLOC, Type, Config->Wordsize, + *Contents, toInitFiniName(Sec->Name)); + Ret->Live = true; + return Ret; +} + // .MIPS.abiflags section. template MipsAbiFlagsSection::MipsAbiFlagsSection(Elf_Mips_ABIFlags Flags) Index: lld/ELF/Writer.cpp =================================================================== --- lld/ELF/Writer.cpp +++ lld/ELF/Writer.cpp @@ -398,6 +398,31 @@ Add(make()); } +// Replace .[cd]tors sections with .{init,fini}_array sections. +static void createInitFiniSections() { + for (size_t I = 0; I < InputSections.size(); ++I) { + InputSection *Sec = dyn_cast(InputSections[I]); + if (!Sec || !Sec->Live) + 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); + } +} + // The main function of the writer. template void Writer::run() { // Create linker-synthesized sections such as .got or .plt. @@ -407,6 +432,10 @@ if (!Config->Relocatable) combineEhFrameSections(); + // Convert .[cd]tors to .{init,fini}_array if needed. + if (Config->CtorsInInitArray) + createInitFiniSections(); + // We want to process linker script commands. When SECTIONS command // is given we let it create sections. Script->processSectionCommands(); 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: 202000 01000000 00000000 02000000 00000000 +# CHECK-NEXT: 202010 03000000 00000000 04000000 00000000 +# CHECK-NEXT: 202020 05000000 00000000 06000000 00000000 +# CHECK-NEXT: 202030 07000000 00000000 08000000 00000000 + +# CHECK: Contents of section .fini_array: +# CHECK-NEXT: 202040 11000000 00000000 12000000 00000000 +# CHECK-NEXT: 202050 13000000 00000000 14000000 00000000 +# CHECK-NEXT: 202060 15000000 00000000 16000000 00000000 +# CHECK-NEXT: 202070 17000000 00000000 18000000 00000000 + +# CHECK: Contents of section .ctors: +# CHECK-NEXT: 202080 a1000000 00000000 + +# CHECK: Contents of section .dtors: +# CHECK-NEXT: 202088 a2000000 00000000 + +# CTORS: Contents of section .ctors: +# CTORS-NEXT: 202000 a1000000 00000000 08000000 00000000 +# CTORS-NEXT: 202010 07000000 00000000 06000000 00000000 +# CTORS-NEXT: 202020 03000000 00000000 02000000 00000000 + +# CTORS: Contents of section .init_array: +# CTORS-NEXT: 202030 01000000 00000000 04000000 00000000 +# CTORS-NEXT: 202040 05000000 00000000 + +# CTORS: Contents of section .dtors: +# CTORS-NEXT: 202048 a2000000 00000000 18000000 00000000 +# CTORS-NEXT: 202058 17000000 00000000 16000000 00000000 +# CTORS-NEXT: 202068 13000000 00000000 12000000 00000000 + +# CTORS: Contents of section .fini_array: +# CTORS-NEXT: 202078 11000000 00000000 14000000 00000000 +# CTORS-NEXT: 202088 15000000 00000000 Index: lld/test/ELF/ctors_dtors_priority.s =================================================================== --- lld/test/ELF/ctors_dtors_priority.s +++ lld/test/ELF/ctors_dtors_priority.s @@ -5,7 +5,7 @@ // 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 // REQUIRES: x86 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