https://bugs.gentoo.org/911591 https://bugzilla.mozilla.org/show_bug.cgi?id=1847697 https://github.com/rui314/mold/issues/653#event-10041847648 https://github.com/rui314/mold/commit/f467ad1add2ab6e381e0e458f026df197e63d487 From f467ad1add2ab6e381e0e458f026df197e63d487 Mon Sep 17 00:00:00 2001 From: Rui Ueyama Date: Wed, 9 Aug 2023 11:40:09 +0900 Subject: [PATCH] Create a symbol version dependency to GLIBC_ABI_DT_RELR Fixes https://github.com/rui314/mold/issues/653 --- elf/cmdline.cc | 2 ++ elf/mold.h | 1 + elf/output-chunks.cc | 44 +++++++++++++++++++++++++----- test/elf/z-pack-relative-relocs.sh | 16 +++++++++++ 4 files changed, 56 insertions(+), 7 deletions(-) create mode 100755 test/elf/z-pack-relative-relocs.sh diff --git a/elf/cmdline.cc b/elf/cmdline.cc index c568ce086..82a0e6869 100644 --- a/elf/cmdline.cc +++ b/elf/cmdline.cc @@ -875,8 +875,10 @@ std::vector parse_nonpositional_args(Context &ctx) { ctx.arg.z_nodefaultlib = true; } else if (read_z_flag("pack-relative-relocs")) { ctx.arg.pack_dyn_relocs_relr = true; + ctx.arg.z_pack_relative_relocs = true; } else if (read_z_flag("nopack-relative-relocs")) { ctx.arg.pack_dyn_relocs_relr = false; + ctx.arg.z_pack_relative_relocs = false; } else if (read_z_flag("separate-loadable-segments")) { z_separate_code = SEPARATE_LOADABLE_SEGMENTS; } else if (read_z_flag("separate-code")) { diff --git a/elf/mold.h b/elf/mold.h index e5532211c..3a027f1e9 100644 --- a/elf/mold.h +++ b/elf/mold.h @@ -1831,6 +1831,7 @@ struct Context { bool z_nodefaultlib = false; bool z_now = false; bool z_origin = false; + bool z_pack_relative_relocs = false; bool z_relro = true; bool z_sectionheader = true; bool z_shstk = false; diff --git a/elf/output-chunks.cc b/elf/output-chunks.cc index 726a4da2b..3896a2991 100644 --- a/elf/output-chunks.cc +++ b/elf/output-chunks.cc @@ -2373,12 +2373,13 @@ void VerneedSection::construct(Context &ctx) { std::tuple(((SharedFile *)b->file)->soname, b->ver_idx); }); - // Resize of .gnu.version + // Resize .gnu.version ctx.versym->contents.resize(ctx.dynsym->symbols.size(), 1); ctx.versym->contents[0] = 0; // Allocate a large enough buffer for .gnu.version_r. - contents.resize((sizeof(ElfVerneed) + sizeof(ElfVernaux)) * syms.size()); + contents.resize((sizeof(ElfVerneed) + sizeof(ElfVernaux)) * + (syms.size() + 1)); // Fill .gnu.version_r. u8 *buf = (u8 *)&contents[0]; @@ -2394,14 +2395,14 @@ void VerneedSection::construct(Context &ctx) { verneed->vn_next = ptr - (u8 *)verneed; verneed = (ElfVerneed *)ptr; - ptr += sizeof(*verneed); + ptr += sizeof(ElfVerneed); verneed->vn_version = 1; verneed->vn_file = ctx.dynstr->find_string(((SharedFile *)file)->soname); verneed->vn_aux = sizeof(ElfVerneed); aux = nullptr; }; - auto add_entry = [&](Symbol *sym) { + auto add_entry = [&](std::string_view verstr) { verneed->vn_cnt++; if (aux) @@ -2409,23 +2410,52 @@ void VerneedSection::construct(Context &ctx) { aux = (ElfVernaux *)ptr; ptr += sizeof(*aux); - std::string_view verstr = sym->get_version(); aux->vna_hash = elf_hash(verstr); aux->vna_other = ++veridx; aux->vna_name = ctx.dynstr->add_string(verstr); }; + // Create version entries. for (i64 i = 0; i < syms.size(); i++) { if (i == 0 || syms[i - 1]->file != syms[i]->file) { start_group(syms[i]->file); - add_entry(syms[i]); + add_entry(syms[i]->get_version()); } else if (syms[i - 1]->ver_idx != syms[i]->ver_idx) { - add_entry(syms[i]); + add_entry(syms[i]->get_version()); } ctx.versym->contents[syms[i]->get_dynsym_idx(ctx)] = veridx; } + if (ctx.arg.z_pack_relative_relocs) { + // If `-z pack-relative-relocs` is specified, we'll create a .relr.dyn + // section and store base relocation records to that section instead of + // to the usual .rela.dyn section. + // + // .relr.dyn is relatively new feature and not supported by glibc until + // 2.38 which was released in 2022. Executables built with `-z + // pack-relative-relocs` don't work and usually crash immediately on + // startup if libc doesn't support it. + // + // In the following code, we'll add a dependency to a dummy version name + // "GLIBC_ABI_DT_RELR" so that executables built with the option failed + // with a more friendly "version `GLIBC_ABI_DT_RELR' not found" error + // message. glibc 2.38 or later knows about this dummy version name and + // simply ignores it. + auto find_glibc2 = [&]() -> InputFile * { + for (Symbol *sym : syms) + if (((SharedFile *)sym->file)->soname.starts_with("libc.so.") && + sym->get_version().starts_with("GLIBC_2.")) + return sym->file; + return nullptr; + }; + + if (InputFile *file = find_glibc2()) { + start_group(file); + add_entry("GLIBC_ABI_DT_RELR"); + } + } + // Resize .gnu.version_r to fit to its contents. contents.resize(ptr - buf); } diff --git a/test/elf/z-pack-relative-relocs.sh b/test/elf/z-pack-relative-relocs.sh new file mode 100755 index 000000000..e09d441e7 --- /dev/null +++ b/test/elf/z-pack-relative-relocs.sh @@ -0,0 +1,16 @@ +#!/bin/bash +. $(dirname $0)/common.inc + +cat < +int main() { + printf("Hello world\n"); +} +EOF + +$CC -B. -o $t/exe $t/a.o -pie -Wl,-z,pack-relative-relocs + +readelf -W -V $t/exe > $t/log +grep -Fq GLIBC_2. $t/log || skip + +grep -q GLIBC_ABI_DT_RELR $t/log