From 4a8de2832a2a730f63b71bdf1c1b446285ec5b6f Mon Sep 17 00:00:00 2001 From: Fangrui Song Date: Thu, 10 Mar 2022 19:54:20 -0800 Subject: [PATCH] [ELF] Add -z pack-relative-relocs GNU ld 2.38 added -z pack-relative-relocs which is similar to --pack-dyn-relocs=relr but synthesizes the `GLIBC_ABI_DT_RELR` version dependency if a shared object named `libc.so.*` has a `GLIBC_2.*` version dependency. This is used to implement the (as some glibc folks call) version lockout mechanism. Add this option, because glibc does not want to support --pack-dyn-relocs=relr which does not add `GLIBC_ABI_DT_RELR`. See https://maskray.me/blog/2021-10-31-relative-relocations-and-relr for detail. Close https://github.com/llvm/llvm-project/issues/53775 Reviewed By: peter.smith Differential Revision: https://reviews.llvm.org/D120701 --- lld/ELF/Config.h | 5 +-- lld/ELF/Driver.cpp | 11 ++++-- lld/ELF/SyntheticSections.cpp | 17 +++++++--- lld/docs/ReleaseNotes.rst | 3 ++ lld/docs/ld.lld.1 | 6 ++++ lld/test/ELF/pack-dyn-relocs-glibc.s | 65 ++++++++++++++++++++++++++++++++++++ 6 files changed, 99 insertions(+), 8 deletions(-) create mode 100644 lld/test/ELF/pack-dyn-relocs-glibc.s diff --git a/lld/ELF/Config.h b/lld/ELF/Config.h index 4b2ad7e..7b7265c 100644 --- a/lld/ELF/Config.h +++ b/lld/ELF/Config.h @@ -148,7 +148,7 @@ struct Configuration { uint64_t> callGraphProfile; bool allowMultipleDefinition; - bool androidPackDynRelocs; + bool androidPackDynRelocs = false; bool armHasBlx = false; bool armHasMovtMovw = false; bool armJ1J2BranchEncoding = false; @@ -206,7 +206,8 @@ struct Configuration { bool printIcfSections; bool relax; bool relocatable; - bool relrPackDynRelocs; + bool relrGlibc = false; + bool relrPackDynRelocs = false; bool saveTemps; std::vector> shuffleSections; bool singleRoRx; diff --git a/lld/ELF/Driver.cpp b/lld/ELF/Driver.cpp index 2a1fe19..9f5d550 100644 --- a/lld/ELF/Driver.cpp +++ b/lld/ELF/Driver.cpp @@ -465,6 +465,7 @@ constexpr const char *knownZFlags[] = { "noexecstack", "nognustack", "nokeep-text-section-prefix", + "nopack-relative-relocs", "norelro", "noseparate-code", "nostart-stop-gc", @@ -472,6 +473,7 @@ constexpr const char *knownZFlags[] = { "now", "origin", "pac-plt", + "pack-relative-relocs", "rel", "rela", "relro", @@ -1352,8 +1354,13 @@ static void readConfigs(opt::InputArgList &args) { std::tie(config->buildId, config->buildIdVector) = getBuildId(args); - std::tie(config->androidPackDynRelocs, config->relrPackDynRelocs) = - getPackDynRelocs(args); + if (getZFlag(args, "pack-relative-relocs", "nopack-relative-relocs", false)) { + config->relrGlibc = true; + config->relrPackDynRelocs = true; + } else { + std::tie(config->androidPackDynRelocs, config->relrPackDynRelocs) = + getPackDynRelocs(args); + } if (auto *arg = args.getLastArg(OPT_symbol_ordering_file)){ if (args.hasArg(OPT_call_graph_ordering_file)) diff --git a/lld/ELF/SyntheticSections.cpp b/lld/ELF/SyntheticSections.cpp index 41c890b..7b8de80 100644 --- a/lld/ELF/SyntheticSections.cpp +++ b/lld/ELF/SyntheticSections.cpp @@ -3178,15 +3178,24 @@ template void VersionNeedSection::finalizeContents() { verneeds.emplace_back(); Verneed &vn = verneeds.back(); vn.nameStrTab = getPartition().dynStrTab->addString(f->soName); + bool isLibc = config->relrGlibc && f->soName.startswith("libc.so."); + bool isGlibc2 = false; for (unsigned i = 0; i != f->vernauxs.size(); ++i) { if (f->vernauxs[i] == 0) continue; auto *verdef = reinterpret_cast(f->verdefs[i]); - vn.vernauxs.push_back( - {verdef->vd_hash, f->vernauxs[i], - getPartition().dynStrTab->addString(f->getStringTable().data() + - verdef->getAux()->vda_name)}); + StringRef ver(f->getStringTable().data() + verdef->getAux()->vda_name); + if (isLibc && ver.startswith("GLIBC_2.")) + isGlibc2 = true; + vn.vernauxs.push_back({verdef->vd_hash, f->vernauxs[i], + getPartition().dynStrTab->addString(ver)}); + } + if (isGlibc2) { + const char *ver = "GLIBC_ABI_DT_RELR"; + vn.vernauxs.push_back({hashSysV(ver), + ++SharedFile::vernauxNum + getVerDefNum(), + getPartition().dynStrTab->addString(ver)}); } } diff --git a/lld/docs/ReleaseNotes.rst b/lld/docs/ReleaseNotes.rst index af09e1e5..5878cdd 100644 --- a/lld/docs/ReleaseNotes.rst +++ b/lld/docs/ReleaseNotes.rst @@ -26,6 +26,9 @@ Non-comprehensive list of changes in this release ELF Improvements ---------------- +* ``-z pack-relative-relocs`` is now available to support ``DT_RELR`` for glibc 2.36+. + (`D120701 `_) + Breaking changes ---------------- diff --git a/lld/docs/ld.lld.1 b/lld/docs/ld.lld.1 index acd2670..b81eeb2 100644 --- a/lld/docs/ld.lld.1 +++ b/lld/docs/ld.lld.1 @@ -807,6 +807,12 @@ processing. .It Cm pac-plt AArch64 only, use pointer authentication in PLT. .Pp +.It Cm pack-relative-relocs +Similar to +.Cm -pack-dyn-relocs=relr +, but synthesizes the GLIBC_ABI_DT_RELR version dependency if there is a GLIBC_2.* version dependency. +glibc ld.so rejects loading a dynamically linked object without the GLIBC_ABI_DT_RELR version dependency. +.Pp .It Cm rel Use REL format for dynamic relocations. .Pp diff --git a/lld/test/ELF/pack-dyn-relocs-glibc.s b/lld/test/ELF/pack-dyn-relocs-glibc.s new file mode 100644 index 0000000..32b6ba3 --- /dev/null +++ b/lld/test/ELF/pack-dyn-relocs-glibc.s @@ -0,0 +1,65 @@ +# REQUIRES: x86 +## -z pack-relative-relocs is a variant of --pack-dyn-relocs=relr: add +## GLIBC_ABI_DT_RELR verneed if there is a verneed named "GLIBC_2.*". + +# RUN: rm -rf %t && split-file %s %t +# RUN: llvm-mc -filetype=obj -triple=x86_64 %t/a.s -o %t/a.o +# RUN: llvm-mc -filetype=obj -triple=x86_64 %t/libc.s -o %t/libc.o +# RUN: ld.lld -shared --soname=libc.so.6 --version-script=%t/glibc.ver %t/libc.o -o %t/libc.so.6 + +# RUN: ld.lld -pie %t/a.o %t/libc.so.6 -z pack-relative-relocs -o %t/glibc 2>&1 | count 0 +# RUN: llvm-readelf -r -V %t/glibc | FileCheck %s --check-prefix=GLIBC +## Arbitrarily let -z pack-relative-relocs win. +# RUN: ld.lld -pie %t/a.o %t/libc.so.6 -z pack-relative-relocs --pack-dyn-relocs=relr -o %t/glibc2 +# RUN: cmp %t/glibc %t/glibc2 + +# GLIBC: Relocation section '.relr.dyn' at offset {{.*}} contains 1 entries: +# GLIBC: Version needs section '.gnu.version_r' contains 1 entries: +# GLIBC-NEXT: Addr: {{.*}} +# GLIBC-NEXT: 0x0000: Version: 1 File: libc.so.6 Cnt: 2 +# GLIBC-NEXT: 0x0010: Name: GLIBC_2.33 Flags: none Version: 2 +# GLIBC-NEXT: 0x0020: Name: GLIBC_ABI_DT_RELR Flags: none Version: 3 +# GLIBC-EMPTY: + +# RUN: ld.lld -pie %t/a.o %t/libc.so.6 -z pack-relative-relocs -z nopack-relative-relocs -o %t/notrelr 2>&1 | count 0 +# RUN: llvm-readelf -r -V %t/notrelr | FileCheck %s --check-prefix=REGULAR + +# REGULAR-NOT: Relocation section '.relr.dyn' +# REGULAR-NOT: Name: GLIBC_ABI_DT_RELR + +## soname is not "libc.so.*". Don't synthesize GLIBC_ABI_DT_RELR. In glibc, ld.so +## doesn't define GLIBC_ABI_DT_RELR. libc.so itself should not reference GLIBC_ABI_DT_RELR. +# RUN: ld.lld -shared --soname=ld-linux-x86-64.so.2 --version-script=%t/glibc.ver %t/libc.o -o %t/ld.so +# RUN: ld.lld -pie %t/a.o %t/ld.so -z pack-relative-relocs -o %t/other 2>&1 | count 0 +# RUN: llvm-readelf -r -V %t/other | FileCheck %s --check-prefix=NOTLIBC + +# NOTLIBC: Relocation section '.relr.dyn' at offset {{.*}} contains 1 entries: +# NOTLIBC-NOT: Name: GLIBC_ABI_DT_RELR + +## There is no GLIBC_2.* verneed. Don't add GLIBC_ABI_DT_RELR verneed. +# RUN: ld.lld -shared --soname=libc.so.6 --version-script=%t/other.ver %t/libc.o -o %t/libc.so.6 +# RUN: ld.lld -pie %t/a.o %t/libc.so.6 -z pack-relative-relocs -o %t/other +# RUN: llvm-readelf -r -V %t/other | FileCheck %s --check-prefix=NOTLIBC + +#--- a.s +.globl _start +_start: + call stat + +.data +.balign 8 +.dc.a .data + +#--- libc.s +.weak stat +stat: + +#--- glibc.ver +GLIBC_2.33 { + stat; +}; + +#--- other.ver +GLIBC_3 { + stat; +}; -- 2.7.4