[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
This commit is contained in:
Fangrui Song 2022-03-10 19:54:20 -08:00
parent e970d2823c
commit 4a8de2832a
6 changed files with 99 additions and 8 deletions

View File

@ -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<std::pair<llvm::GlobPattern, uint32_t>> shuffleSections;
bool singleRoRx;

View File

@ -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))

View File

@ -3178,15 +3178,24 @@ template <class ELFT> void VersionNeedSection<ELFT>::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<const typename ELFT::Verdef *>(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)});
}
}

View File

@ -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 <https://reviews.llvm.org/D120701>`_)
Breaking changes
----------------

View File

@ -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

View File

@ -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;
};