diff --git a/lld/ELF/Config.h b/lld/ELF/Config.h --- a/lld/ELF/Config.h +++ b/lld/ELF/Config.h @@ -148,7 +148,7 @@ uint64_t> callGraphProfile; bool allowMultipleDefinition; - bool androidPackDynRelocs; + bool androidPackDynRelocs = false; bool armHasBlx = false; bool armHasMovtMovw = false; bool armJ1J2BranchEncoding = false; @@ -206,7 +206,8 @@ 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 --- a/lld/ELF/Driver.cpp +++ b/lld/ELF/Driver.cpp @@ -465,6 +465,7 @@ "noexecstack", "nognustack", "nokeep-text-section-prefix", + "nopack-relative-relocs", "norelro", "noseparate-code", "nostart-stop-gc", @@ -472,6 +473,7 @@ "now", "origin", "pac-plt", + "pack-relative-relocs", "rel", "rela", "relro", @@ -1352,8 +1354,13 @@ 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 --- a/lld/ELF/SyntheticSections.cpp +++ b/lld/ELF/SyntheticSections.cpp @@ -3178,15 +3178,23 @@ verneeds.emplace_back(); Verneed &vn = verneeds.back(); vn.nameStrTab = getPartition().dynStrTab->addString(f->soName); + 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 (config->relrGlibc && 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 --- a/lld/docs/ReleaseNotes.rst +++ b/lld/docs/ReleaseNotes.rst @@ -26,6 +26,9 @@ 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 --- a/lld/docs/ld.lld.1 +++ b/lld/docs/ld.lld.1 @@ -807,6 +807,12 @@ .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 --- /dev/null +++ b/lld/test/ELF/pack-dyn-relocs-glibc.s @@ -0,0 +1,63 @@ +# 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 + +## 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=OTHER + +# OTHER: Relocation section '.relr.dyn' at offset {{.*}} contains 1 entries: +# OTHER: Version needs section '.gnu.version_r' contains 1 entries: +# OTHER-NEXT: Addr: {{.*}} +# OTHER-NEXT: 0x0000: Version: 1 File: libc.so.6 Cnt: 1 +# OTHER-NEXT: 0x0010: Name: GLIBC_3 Flags: none Version: 2 +# OTHER-EMPTY: + +#--- 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; +};