From 610042a8e927106cb2d852fba2a90faa0d0e050f Mon Sep 17 00:00:00 2001 From: Rui Ueyama Date: Fri, 21 Oct 2022 07:08:09 +0800 Subject: [PATCH] [ELF] Set .ARM.exidx's sh_link to .text GNU binutil's strip command doesn't like an .ARM.exidx section with sh_link set to 0. arm-linux-gnueabihf-strip actually crashes on my machine when the command is run on such a file. https://github.com/rui314/mold/issues/801 --- elf/arch-arm32.cc | 56 +++++++++++++++++++++++++++++-------------- elf/mold.h | 2 +- elf/output-chunks.cc | 4 ++-- elf/passes.cc | 2 +- test/elf/exception.sh | 8 +++++++ 5 files changed, 50 insertions(+), 22 deletions(-) diff --git a/elf/arch-arm32.cc b/elf/arch-arm32.cc index 46e45d43d..bea510763 100644 --- a/elf/arch-arm32.cc +++ b/elf/arch-arm32.cc @@ -552,14 +552,6 @@ void RangeExtensionThunk::copy_buf(Context &ctx) { } } -template -static OutputSection *find_exidx_section(Context &ctx) { - for (std::unique_ptr> &osec : ctx.output_sections) - if (osec->shdr.sh_type == SHT_ARM_EXIDX) - return osec.get(); - return nullptr; -} - // ARM executables use an .ARM.exidx section to look up an exception // handling record for the current instruction pointer. The table needs // to be sorted by their addresses. @@ -569,13 +561,7 @@ static OutputSection *find_exidx_section(Context &ctx) { // likely that it's due to some historical reason. // // This function sorts .ARM.exidx records. -void sort_arm_exidx(Context &ctx) { - Timer t(ctx, "sort_arm_exidx"); - - OutputSection *osec = find_exidx_section(ctx); - if (!osec) - return; - +static void sort_exidx(Context &ctx, OutputSection &osec) { // .ARM.exidx records consists of a signed 31-bit relative address // and a 32-bit value. The relative address indicates the start // address of a function that the record covers. The value is one of @@ -595,11 +581,11 @@ void sort_arm_exidx(Context &ctx) { ul32 val; }; - if (osec->shdr.sh_size % sizeof(Entry)) + if (osec.shdr.sh_size % sizeof(Entry)) Fatal(ctx) << "invalid .ARM.exidx section size"; - Entry *ent = (Entry *)(ctx.buf + osec->shdr.sh_offset); - i64 num_entries = osec->shdr.sh_size / sizeof(Entry); + Entry *ent = (Entry *)(ctx.buf + osec.shdr.sh_offset); + i64 num_entries = osec.shdr.sh_size / sizeof(Entry); // Entry's addresses are relative to themselves. In order to sort // records by addresses, we first translate them so that the addresses @@ -628,4 +614,38 @@ void sort_arm_exidx(Context &ctx) { }); } +void fixup_arm_exidx_section(Context &ctx) { + Timer t(ctx, "fixup_arm_exidx_section"); + + auto find_exidx = [&]() -> OutputSection * { + for (std::unique_ptr> &osec : ctx.output_sections) + if (osec->shdr.sh_type == SHT_ARM_EXIDX) + return osec.get(); + return nullptr; + }; + + OutputSection *exidx = find_exidx(); + if (!exidx) + return; + + // Sort .ARM.exidx contents + sort_exidx(ctx, *exidx); + + // .ARM.exidx's sh_link should be set to the .text section index. + // Runtime doesn't care about it, but the binutils's strip command does. + if (ctx.shdr) { + auto find_text = [&]() -> OutputSection * { + for (std::unique_ptr> &osec : ctx.output_sections) + if (osec->name == ".text") + return osec.get(); + return nullptr; + }; + + if (OutputSection *text = find_exidx()) { + exidx->shdr.sh_link = text->shndx; + ctx.shdr->copy_buf(ctx); + } + } +} + } // namespace mold::elf diff --git a/elf/mold.h b/elf/mold.h index 11938270e..c8bac2b4e 100644 --- a/elf/mold.h +++ b/elf/mold.h @@ -1434,7 +1434,7 @@ template void write_dependency_file(Context &); // arch-arm32.cc // -void sort_arm_exidx(Context &ctx); +void fixup_arm_exidx_section(Context &ctx); // // arch-riscv64.cc diff --git a/elf/output-chunks.cc b/elf/output-chunks.cc index 54562feb2..0d1d0dcfd 100644 --- a/elf/output-chunks.cc +++ b/elf/output-chunks.cc @@ -847,9 +847,9 @@ get_output_name(Context &ctx, std::string_view name, u64 flags) { if ((name == ".rodata" || name.starts_with(".rodata.")) && (flags & SHF_MERGE)) return (flags & SHF_STRINGS) ? ".rodata.str" : ".rodata.cst"; - if (name == ".ARM.exidx" || name.starts_with(".ARM.exidx.")) + if (name.starts_with(".ARM.exidx")) return ".ARM.exidx"; - if (name == ".ARM.extab" || name.starts_with(".ARM.extab.")) + if (name.starts_with(".ARM.extab")) return ".ARM.extab"; if (ctx.arg.z_keep_text_section_prefix) { diff --git a/elf/passes.cc b/elf/passes.cc index 713a06a84..4bffc3dbd 100644 --- a/elf/passes.cc +++ b/elf/passes.cc @@ -1102,7 +1102,7 @@ void copy_chunks(Context &ctx) { report_undef_errors(ctx); if constexpr (std::is_same_v) - sort_arm_exidx(ctx); + fixup_arm_exidx_section(ctx); } template diff --git a/test/elf/exception.sh b/test/elf/exception.sh index 6832966f0..c584c99fa 100755 --- a/test/elf/exception.sh +++ b/test/elf/exception.sh @@ -65,3 +65,11 @@ if [ $MACHINE = x86_64 -o $MACHINE = aarch64 ]; then $CXX -B. -o $t/exe10 $t/e.o -no-pie $QEMU $t/exe10 fi + +$CXX -B. -o $t/exe11 $t/b.o -pie +$STRIP $t/exe11 +$QEMU $t/exe11 + +$CXX -B. -o $t/exe12 $t/c.o -no-pie +$STRIP $t/exe12 +$QEMU $t/exe12