164 lines
5.3 KiB
Diff
164 lines
5.3 KiB
Diff
From 610042a8e927106cb2d852fba2a90faa0d0e050f Mon Sep 17 00:00:00 2001
|
|
From: Rui Ueyama <ruiu@bluewhale.systems>
|
|
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<E>::copy_buf(Context<E> &ctx) {
|
|
}
|
|
}
|
|
|
|
-template <typename E>
|
|
-static OutputSection<E> *find_exidx_section(Context<E> &ctx) {
|
|
- for (std::unique_ptr<OutputSection<E>> &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<E> *find_exidx_section(Context<E> &ctx) {
|
|
// likely that it's due to some historical reason.
|
|
//
|
|
// This function sorts .ARM.exidx records.
|
|
-void sort_arm_exidx(Context<E> &ctx) {
|
|
- Timer t(ctx, "sort_arm_exidx");
|
|
-
|
|
- OutputSection<E> *osec = find_exidx_section(ctx);
|
|
- if (!osec)
|
|
- return;
|
|
-
|
|
+static void sort_exidx(Context<E> &ctx, OutputSection<E> &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<E> &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<E> &ctx) {
|
|
});
|
|
}
|
|
|
|
+void fixup_arm_exidx_section(Context<E> &ctx) {
|
|
+ Timer t(ctx, "fixup_arm_exidx_section");
|
|
+
|
|
+ auto find_exidx = [&]() -> OutputSection<E> * {
|
|
+ for (std::unique_ptr<OutputSection<E>> &osec : ctx.output_sections)
|
|
+ if (osec->shdr.sh_type == SHT_ARM_EXIDX)
|
|
+ return osec.get();
|
|
+ return nullptr;
|
|
+ };
|
|
+
|
|
+ OutputSection<E> *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<E> * {
|
|
+ for (std::unique_ptr<OutputSection<E>> &osec : ctx.output_sections)
|
|
+ if (osec->name == ".text")
|
|
+ return osec.get();
|
|
+ return nullptr;
|
|
+ };
|
|
+
|
|
+ if (OutputSection<E> *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 <typename E> void write_dependency_file(Context<E> &);
|
|
// arch-arm32.cc
|
|
//
|
|
|
|
-void sort_arm_exidx(Context<ARM32> &ctx);
|
|
+void fixup_arm_exidx_section(Context<ARM32> &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<E> &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<E> &ctx) {
|
|
report_undef_errors(ctx);
|
|
|
|
if constexpr (std::is_same_v<E, ARM32>)
|
|
- sort_arm_exidx(ctx);
|
|
+ fixup_arm_exidx_section(ctx);
|
|
}
|
|
|
|
template <typename E>
|
|
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
|