From 3f88964527a38d4881ccebce27b23fa19b209507 Mon Sep 17 00:00:00 2001 From: Rui Ueyama Date: Wed, 8 Nov 2023 20:53:14 +0900 Subject: [PATCH] Map .ctors/.dtors into .init_array/.fini_array .init_array/.fini_array haven't completely replace .ctors/.dtors, so we need to convert .ctors/.dtors to .init_array/.fini_array. Fixes https://github.com/rui314/mold/issues/1113 --- elf/input-files.cc | 9 +++++ elf/main.cc | 5 +++ elf/mold.h | 3 ++ elf/passes.cc | 59 +++++++++++++++++++++++++++++++-- test/elf/ctors-in-init-array.sh | 55 ++++++++++++++++++++++++++++++ 5 files changed, 129 insertions(+), 2 deletions(-) create mode 100755 test/elf/ctors-in-init-array.sh diff --git a/elf/input-files.cc b/elf/input-files.cc index 4141db91f8..622d6777dd 100644 --- a/elf/input-files.cc +++ b/elf/input-files.cc @@ -314,6 +314,15 @@ void ObjectFile::initialize_sections(Context &ctx) { continue; } + if (shdr.sh_type == SHT_INIT_ARRAY || + shdr.sh_type == SHT_FINI_ARRAY || + shdr.sh_type == SHT_PREINIT_ARRAY) + ctx.has_init_array = true; + + if (name == ".ctors" || name.starts_with(".ctors.") || + name == ".dtors" || name.starts_with(".dtors.")) + ctx.has_ctors = true; + if (name == ".eh_frame") eh_frame_section = this->sections[i].get(); diff --git a/elf/main.cc b/elf/main.cc index 7847d14d1d..d0aacd9808 100644 --- a/elf/main.cc +++ b/elf/main.cc @@ -535,6 +535,11 @@ int elf_main(int argc, char **argv) { // because they are superceded by .init_array/.fini_array, though. sort_ctor_dtor(ctx); + // If .ctors/.dtors are to be placed to .init_array/.fini_array, + // we need to reverse their contents. + if (ctx.has_init_array && ctx.has_ctors) + reverse_ctors_in_init_array(ctx); + // Handle --shuffle-sections if (ctx.arg.shuffle_sections != SHUFFLE_SECTIONS_NONE) shuffle_sections(ctx); diff --git a/elf/mold.h b/elf/mold.h index 117bb38987..c186cebf2f 100644 --- a/elf/mold.h +++ b/elf/mold.h @@ -1372,6 +1372,7 @@ template void check_duplicate_symbols(Context &); template void check_symbol_types(Context &); template void sort_init_fini(Context &); template void sort_ctor_dtor(Context &); +template void reverse_ctors_in_init_array(Context &); template void shuffle_sections(Context &); template void compute_section_sizes(Context &); template void sort_output_sections(Context &); @@ -1750,6 +1751,8 @@ struct Context { bool has_error = false; bool has_lto_object = false; + Atomic has_init_array = false; + Atomic has_ctors = false; // Symbol table tbb::concurrent_hash_map, HashCmp> symbol_map; diff --git a/elf/passes.cc b/elf/passes.cc index 1a74abc5cf..1efded1b39 100644 --- a/elf/passes.cc +++ b/elf/passes.cc @@ -513,6 +513,14 @@ get_output_name(Context &ctx, std::string_view name, u64 flags) { template static OutputSectionKey get_output_section_key(Context &ctx, InputSection &isec) { + if (ctx.has_init_array) { + std::string_view name = isec.name(); + if (name == ".ctors" || name.starts_with(".ctors.")) + return {".init_array", SHT_INIT_ARRAY, SHF_ALLOC | SHF_WRITE}; + if (name == ".dtors" || name.starts_with(".dtors.")) + return {".fini_array", SHT_FINI_ARRAY, SHF_ALLOC | SHF_WRITE}; + } + const ElfShdr &shdr = isec.shdr(); std::string_view name = get_output_name(ctx, isec.name(), shdr.sh_flags); u64 type = canonicalize_type(name, shdr.sh_type); @@ -523,6 +531,7 @@ get_output_section_key(Context &ctx, InputSection &isec) { // So is .fini_array. if (type == SHT_INIT_ARRAY || type == SHT_FINI_ARRAY) flags |= SHF_WRITE; + return {name, type, flags}; } @@ -1079,8 +1088,14 @@ void sort_init_fini(Context &ctx) { std::reverse(osec->members.begin(), osec->members.end()); std::unordered_map *, i64> map; - for (InputSection *isec : osec->members) - map.insert({isec, get_init_fini_priority(isec)}); + + for (InputSection *isec : osec->members) { + std::string_view name = isec->name(); + if (name.starts_with(".ctors") || name.starts_with(".dtors")) + map.insert({isec, 65535 - get_ctor_dtor_priority(isec)}); + else + map.insert({isec, get_init_fini_priority(isec)}); + } sort(osec->members, [&](InputSection *a, InputSection *b) { return map[a] < map[b]; @@ -1112,6 +1127,45 @@ void sort_ctor_dtor(Context &ctx) { } } +// .ctors/.dtors serves the purpose as .init_array/.fini_array, albeit +// with very subtly differences. Both contain pointers to +// initializer/finalizer functions. The runtime executes them one by one +// but in the exact opposite order to one another. Therefore, if we are to +// place the contents of .ctors/.dtors into .init_array/.fini_array, we +// need to reverse them. +// +// It's unfortunate that we have both .ctors/.dtors and +// .init_array/.fini_array in ELF for historical reasons, but that's +// the reality we need to deal with. +template +void reverse_ctors_in_init_array(Context &ctx) { + Timer t(ctx, "reverse_ctors_in_init_array"); + + for (Chunk *chunk : ctx.chunks) { + if (OutputSection *osec = chunk->to_osec()) { + if (osec->name == ".init_array" || osec->name == ".fini_array") { + for (InputSection *isec : osec->members) { + if (isec->name().starts_with(".ctors") || + isec->name().starts_with(".dtors")) { + if (isec->sh_size % sizeof(Word)) { + Error(ctx) << isec << ": section corrupted"; + continue; + } + + u8 *buf = (u8 *)isec->contents.data(); + std::reverse((Word *)buf, (Word *)(buf + isec->sh_size)); + + std::span> rels = isec->get_rels(ctx); + for (ElfRel &r : rels) + r.r_offset = isec->sh_size - r.r_offset - sizeof(Word); + std::reverse(rels.begin(), rels.end()); + } + } + } + } + } +} + template static void shuffle(std::vector &vec, u64 seed) { if (vec.empty()) @@ -2746,6 +2800,7 @@ template void check_duplicate_symbols(Context &); template void check_symbol_types(Context &); template void sort_init_fini(Context &); template void sort_ctor_dtor(Context &); +template void reverse_ctors_in_init_array(Context &); template void shuffle_sections(Context &); template void compute_section_sizes(Context &); template void sort_output_sections(Context &); diff --git a/test/elf/ctors-in-init-array.sh b/test/elf/ctors-in-init-array.sh new file mode 100755 index 0000000000..ab048df616 --- /dev/null +++ b/test/elf/ctors-in-init-array.sh @@ -0,0 +1,55 @@ +#!/bin/bash +. $(dirname $0)/common.inc + +cat < + +static void ctor1() { printf("ctor1 "); } +static void ctor2() { printf("ctor2 "); } +static void ctor3() { printf("ctor3 "); } +static void ctor4() { printf("ctor4 "); } + +static void dtor1() { printf("dtor1 "); } +static void dtor2() { printf("dtor2 "); } +static void dtor3() { printf("dtor3 "); } +static void dtor4() { printf("dtor4 "); } + +__attribute__((aligned(sizeof(void *)), section(".ctors.65435"))) +void (*ctors65435[])() = { ctor1 }; + +__attribute__((aligned(sizeof(void *)), section(".ctors.65433"))) +void (*ctors65433[])() = { ctor2 }; + +__attribute__((aligned(sizeof(void *)), section(".ctors"))) +void (*ctors[])() = { ctor4, ctor3 }; + +__attribute__((aligned(sizeof(void *)), section(".dtors"))) +void (*dtors[])() = { dtor1, dtor2 }; + +__attribute__((aligned(sizeof(void *)), section(".dtors.65433"))) +void (*dtors65433[])() = { dtor3 }; + +__attribute__((aligned(sizeof(void *)), section(".dtors.65435"))) +void (*dtors65435[])() = { dtor4 }; +EOF + +cat < + +__attribute__((constructor(101))) +static void init1() { printf("init1 "); } + +__attribute__((constructor)) +static void init2() { printf("init2 "); } + +__attribute__((destructor(101))) +static void fini1() { printf("fini1 "); } + +__attribute__((destructor)) +static void fini2() { printf("fini2 "); } + +int main() {} +EOF + +$CC -B. -o $t/exe $t/a.o $t/b.o +$QEMU $t/exe | grep -q 'ctor1 init1 ctor2 ctor3 ctor4 init2 fini2 dtor1 dtor2 dtor3 fini1 dtor4'