Skip to content

Commit

Permalink
Map .ctors/.dtors into .init_array/.fini_array
Browse files Browse the repository at this point in the history
.init_array/.fini_array haven't completely replace .ctors/.dtors, so
we need to convert .ctors/.dtors to .init_array/.fini_array.

Fixes #1113
  • Loading branch information
rui314 committed Nov 9, 2023
1 parent e18dc70 commit 3f88964
Show file tree
Hide file tree
Showing 5 changed files with 129 additions and 2 deletions.
9 changes: 9 additions & 0 deletions elf/input-files.cc
Original file line number Diff line number Diff line change
Expand Up @@ -314,6 +314,15 @@ void ObjectFile<E>::initialize_sections(Context<E> &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();

Expand Down
5 changes: 5 additions & 0 deletions elf/main.cc
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
3 changes: 3 additions & 0 deletions elf/mold.h
Original file line number Diff line number Diff line change
Expand Up @@ -1372,6 +1372,7 @@ template <typename E> void check_duplicate_symbols(Context<E> &);
template <typename E> void check_symbol_types(Context<E> &);
template <typename E> void sort_init_fini(Context<E> &);
template <typename E> void sort_ctor_dtor(Context<E> &);
template <typename E> void reverse_ctors_in_init_array(Context<E> &);
template <typename E> void shuffle_sections(Context<E> &);
template <typename E> void compute_section_sizes(Context<E> &);
template <typename E> void sort_output_sections(Context<E> &);
Expand Down Expand Up @@ -1750,6 +1751,8 @@ struct Context {

bool has_error = false;
bool has_lto_object = false;
Atomic<bool> has_init_array = false;
Atomic<bool> has_ctors = false;

// Symbol table
tbb::concurrent_hash_map<std::string_view, Symbol<E>, HashCmp> symbol_map;
Expand Down
59 changes: 57 additions & 2 deletions elf/passes.cc
Original file line number Diff line number Diff line change
Expand Up @@ -513,6 +513,14 @@ get_output_name(Context<E> &ctx, std::string_view name, u64 flags) {
template <typename E>
static OutputSectionKey
get_output_section_key(Context<E> &ctx, InputSection<E> &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<E> &shdr = isec.shdr();
std::string_view name = get_output_name(ctx, isec.name(), shdr.sh_flags);
u64 type = canonicalize_type<E>(name, shdr.sh_type);
Expand All @@ -523,6 +531,7 @@ get_output_section_key(Context<E> &ctx, InputSection<E> &isec) {
// So is .fini_array.
if (type == SHT_INIT_ARRAY || type == SHT_FINI_ARRAY)
flags |= SHF_WRITE;

return {name, type, flags};
}

Expand Down Expand Up @@ -1079,8 +1088,14 @@ void sort_init_fini(Context<E> &ctx) {
std::reverse(osec->members.begin(), osec->members.end());

std::unordered_map<InputSection<E> *, i64> map;
for (InputSection<E> *isec : osec->members)
map.insert({isec, get_init_fini_priority(isec)});

for (InputSection<E> *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<E> *a, InputSection<E> *b) {
return map[a] < map[b];
Expand Down Expand Up @@ -1112,6 +1127,45 @@ void sort_ctor_dtor(Context<E> &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 <typename E>
void reverse_ctors_in_init_array(Context<E> &ctx) {
Timer t(ctx, "reverse_ctors_in_init_array");

for (Chunk<E> *chunk : ctx.chunks) {
if (OutputSection<E> *osec = chunk->to_osec()) {
if (osec->name == ".init_array" || osec->name == ".fini_array") {
for (InputSection<E> *isec : osec->members) {
if (isec->name().starts_with(".ctors") ||
isec->name().starts_with(".dtors")) {
if (isec->sh_size % sizeof(Word<E>)) {
Error(ctx) << isec << ": section corrupted";
continue;
}

u8 *buf = (u8 *)isec->contents.data();
std::reverse((Word<E> *)buf, (Word<E> *)(buf + isec->sh_size));

std::span<ElfRel<E>> rels = isec->get_rels(ctx);
for (ElfRel<E> &r : rels)
r.r_offset = isec->sh_size - r.r_offset - sizeof(Word<E>);
std::reverse(rels.begin(), rels.end());
}
}
}
}
}
}

template <typename T>
static void shuffle(std::vector<T> &vec, u64 seed) {
if (vec.empty())
Expand Down Expand Up @@ -2746,6 +2800,7 @@ template void check_duplicate_symbols(Context<E> &);
template void check_symbol_types(Context<E> &);
template void sort_init_fini(Context<E> &);
template void sort_ctor_dtor(Context<E> &);
template void reverse_ctors_in_init_array(Context<E> &);
template void shuffle_sections(Context<E> &);
template void compute_section_sizes(Context<E> &);
template void sort_output_sections(Context<E> &);
Expand Down
55 changes: 55 additions & 0 deletions test/elf/ctors-in-init-array.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
#!/bin/bash
. $(dirname $0)/common.inc

cat <<EOF | $CC -o $t/a.o -c -xc -
#include <stdio.h>
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 <<EOF | $CC -o $t/b.o -c -xc -
#include <stdio.h>
__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'

0 comments on commit 3f88964

Please sign in to comment.