tech-toolchain archive

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index][Old Index]

RELR: compressed R_*_RELATIVE reloc section



[bcc tech-toolchain, followups to tech-userlevel cc me]

The attached patch teaches ld.elf_so(1) to understand RELR, a
compressed format for R_*_RELATIVE-type relocations that often
substantially reduces the sizes of position-independent executables,
much closer to the non-PIE versions.

The patch adds support when relocating shared objects and dynamic PIEs
(but not static PIEs or ld.elf_so itself).  Later, we might consider
flipping this on in bsd.*.mk on ports for which the toolchain supports
it.

OK?


Experiments conducted by other people show savings of many megabytes
in large executables like Chromium and Firefox when built as PIEs:

Rahul Chaudhry, `Re: Proposal for a new section type SHT_RELR',
generic-abi mailing list, 2018-02-07.
http://groups.google.com/g/generic-abi/c/bX460iggiKg/m/Jnz1lgLJAgAJ
http://web.archive.org/web/20241213012330/http://groups.google.com/g/generic-abi/c/bX460iggiKg/m/Jnz1lgLJAgAJ

The code to implement RELR in ld.elf_so(1) is small (560 bytes on
amd64) -- likely dwarfed by the space saved by using RELR.  E.g., on
amd64 in current, the ntpd executable has a 59448-byte .rela.dyn
dominated by R_X86_64_RELATIVE; with -Wl,-z,pack-relative-relocs, that
becomes a 360-byte .rela.dyn and a 744-byte .relr.dyn.

GNU binutils has supported RELR, via the -Wl,-z,pack-relative-relocs
option, on amd64, i386, and powerpc since 2.38 -- we are on 2.42 in
current, so the toolchain support for these ports is already there.
(Since then, GNU binutils has added support on aarch64 and loongson.)

Other operating systems have adopted the same format:

- FreeBSD rtld since 13.1 in 2021
- OpenBSD ld.so since 7.1 2002
- glibc since 2.36 in 2022
- musl libc since 2022

Background reading:

Fangrui Song, `Relative relocations and RELR', MaskRay, 2021-10-31.
http://maskray.me/blog/2021-10-31-relative-relocations-and-relr

Change attached in two formats:
- pr59360-ldelfsorelr.patch -- piecemeal hg changeset series
- pr59360-ldelfsorelr.diff -- giant end-to-end diff

PR bin/59360: ld.elf_so(8): missing RELR support
http://gnats.NetBSD.org/59360
# HG changeset patch
# User Taylor R Campbell <riastradh%NetBSD.org@localhost>
# Date 1745702701 0
#      Sat Apr 26 21:25:01 2025 +0000
# Branch trunk
# Node ID f673072924a94e661ab1235cc1911b82ec4d43ad
# Parent  13492505e6407eebf7935dde7c7d2668a01b2055
# EXP-Topic riastradh-pr59360-ldelfsorelr
ld.elf_so: Add a trivial test for R_*_RELATIVE relocations.

PR bin/59360: ld.elf_so(8): missing RELR support

diff -r 13492505e640 -r f673072924a9 distrib/sets/lists/debug/mi
--- a/distrib/sets/lists/debug/mi	Sat Apr 26 20:28:14 2025 +0000
+++ b/distrib/sets/lists/debug/mi	Sat Apr 26 21:25:01 2025 +0000
@@ -2455,6 +2455,8 @@
 ./usr/libdata/debug/usr/tests/libexec/ld.elf_so/h_dl_symver_v2.debug	tests-libexec-debug	debug,atf,pic,compattestfile
 ./usr/libdata/debug/usr/tests/libexec/ld.elf_so/h_ifunc.debug	tests-libexec-debug	debug,atf,pic,compattestfile
 ./usr/libdata/debug/usr/tests/libexec/ld.elf_so/h_locking.debug	tests-libexec-debug	debug,atf,pic,compattestfile
+./usr/libdata/debug/usr/tests/libexec/ld.elf_so/h_r_rel_nopack.debug	tests-libexec-debug	debug,atf,pic,compattestfile
+./usr/libdata/debug/usr/tests/libexec/ld.elf_so/h_r_rel_pack.debug	tests-libexec-debug	debug,atf,pic,compattestfile
 ./usr/libdata/debug/usr/tests/libexec/ld.elf_so/t_dlerror-cleared.debug	tests-libexec-debug	debug,atf,pic,compattestfile
 ./usr/libdata/debug/usr/tests/libexec/ld.elf_so/t_dlerror-false.debug	tests-libexec-debug	debug,atf,pic,compattestfile
 ./usr/libdata/debug/usr/tests/libexec/ld.elf_so/t_dlinfo.debug		tests-libexec-debug	debug,atf,pic,compattestfile
diff -r 13492505e640 -r f673072924a9 distrib/sets/lists/tests/mi
--- a/distrib/sets/lists/tests/mi	Sat Apr 26 20:28:14 2025 +0000
+++ b/distrib/sets/lists/tests/mi	Sat Apr 26 21:25:01 2025 +0000
@@ -4264,6 +4264,8 @@
 ./usr/tests/libexec/ld.elf_so/h_helper_symver_dso2	tests-libexec-tests	compattestfile,atf
 ./usr/tests/libexec/ld.elf_so/h_ifunc			tests-libexec-tests	compattestfile,atf,pic
 ./usr/tests/libexec/ld.elf_so/h_locking			tests-libexec-tests	compattestfile,atf,pic
+./usr/tests/libexec/ld.elf_so/h_r_rel_nopack		tests-libexec-tests	compattestfile,atf,pic
+./usr/tests/libexec/ld.elf_so/h_r_rel_pack		tests-libexec-tests	compattestfile,atf,pic
 ./usr/tests/libexec/ld.elf_so/h_thread_local_dtor	tests-libexec-tests	compattestfile,atf,pic
 ./usr/tests/libexec/ld.elf_so/t_df_1_noopen		tests-libexec-tests	compattestfile,atf,pic
 ./usr/tests/libexec/ld.elf_so/t_dl_symver		tests-libexec-tests	compattestfile,atf,pic
@@ -4276,6 +4278,7 @@
 ./usr/tests/libexec/ld.elf_so/t_ifunc_norelro		tests-libexec-tests	compattestfile,atf,pic
 ./usr/tests/libexec/ld.elf_so/t_ifunc_norelro_now	tests-libexec-tests	compattestfile,atf,pic
 ./usr/tests/libexec/ld.elf_so/t_ifunc_now		tests-libexec-tests	compattestfile,atf,pic
+./usr/tests/libexec/ld.elf_so/t_r_rel			tests-libexec-tests	compattestfile,atf,pic
 ./usr/tests/libexec/ld.elf_so/t_rtld_r_debug		tests-libexec-tests	compattestfile,atf,pic
 ./usr/tests/libexec/ld.elf_so/t_rtld_r_debug_nopie	tests-libexec-tests	compattestfile,atf,pic
 ./usr/tests/libexec/ld.elf_so/t_thread_local_dtor	tests-libexec-tests	compattestfile,atf,pic
diff -r 13492505e640 -r f673072924a9 tests/libexec/ld.elf_so/Makefile
--- a/tests/libexec/ld.elf_so/Makefile	Sat Apr 26 20:28:14 2025 +0000
+++ b/tests/libexec/ld.elf_so/Makefile	Sat Apr 26 21:25:01 2025 +0000
@@ -80,6 +80,7 @@ LDADD.t_tls_extern+=	-Wl,-rpath,${TESTSD
 
 TESTS_SH+=		t_df_1_noopen
 TESTS_SH+=		t_dl_symver
+TESTS_SH+=		t_r_rel
 TESTS_SH+=		t_thread_local_dtor
 
 BINDIR=			${TESTSDIR}
@@ -117,6 +118,16 @@ SRCS.h_dl_symver_v2=	h_dl_symver.c
 V2ODIR!=		cd ${.CURDIR}/helper_symver_dso2 && ${PRINTOBJDIR}
 LDADD.h_dl_symver_v2=	-L${V2ODIR} -lh_helper_symver_dso
 
+PROGS+=			h_r_rel_pack
+PROGS+=			h_r_rel_nopack
+
+SRCS.h_r_rel_pack=	h_r_rel.c
+SRCS.h_r_rel_nopack=	h_r_rel.c
+
+h_r_rel_pack: CTFMERGE=:	# PR toolchain/59364: ctf tools needs update
+LDFLAGS.h_r_rel_pack=	-Wl,-z,pack-relative-relocs
+LDFLAGS.h_r_rel_nopack=	-Wl,-z,nopack-relative-relocs
+
 .include <bsd.test.mk>
 
 .else
diff -r 13492505e640 -r f673072924a9 tests/libexec/ld.elf_so/h_r_rel.c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/libexec/ld.elf_so/h_r_rel.c	Sat Apr 26 21:25:01 2025 +0000
@@ -0,0 +1,146 @@
+/*	$NetBSD$	*/
+
+/*-
+ * Copyright (c) 2025 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__RCSID("$NetBSD$");
+
+#include <stdio.h>
+
+/*
+ * When built as position-independent executable, the value of foop and
+ * foopp should be computed either via R_*_RELATIVE or R_*_REL32 or
+ * similar, which -- ports that support it -- may be compressed into a
+ * SHT_RELR section.
+ *
+ * One pointer indirection is enough to produce this effect, but we use
+ * two pointer indirections to increase the probability of a crash in
+ * case the relocations are done wrong.
+ */
+static int foo = 0x5f4d7635;
+static int *volatile foop = &foo;
+static int *volatile *volatile foopp = &foop;
+
+/*
+ * The RELR section compresses relocations for adjacent addresses into
+ * bitmaps of 31 or 63 bits apiece.  Create a bunch of consecutive
+ * addresses to relocate, punctuated by the occasional non-relocated
+ * address (null), to check for fencepost errors in the bitmap
+ * iteration.
+ */
+static int bar[] = {
+	0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+	0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+	0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
+	0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,
+	0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27,
+	0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f,
+	0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37,
+	0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f,
+	0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47,
+	0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f,
+	0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57,
+	0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f,
+	0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67,
+	0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f,
+	0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77,
+	0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f,
+};
+
+static int *volatile barp[] = {
+	&bar[0x00], &bar[0x01], &bar[0x02], &bar[0x03],
+	&bar[0x04], &bar[0x05], &bar[0x06], &bar[0x07],
+	&bar[0x08], &bar[0x09], &bar[0x0a], &bar[0x0b],
+	&bar[0x0c], &bar[0x0d], &bar[0x0e], &bar[0x0f],
+	&bar[0x10], &bar[0x11], &bar[0x12], &bar[0x13],
+	&bar[0x14], &bar[0x15], &bar[0x16], &bar[0x17],
+	&bar[0x18], &bar[0x19], &bar[0x1a], &bar[0x1b],
+	&bar[0x1c], &bar[0x1d], &bar[0x1e], &bar[0x1f],
+	&bar[0x20], &bar[0x21], &bar[0x22], &bar[0x23],
+	&bar[0x24], &bar[0x25], &bar[0x26], &bar[0x27],
+	&bar[0x28], &bar[0x29], &bar[0x2a], &bar[0x2b],
+	&bar[0x2c], &bar[0x2d], &bar[0x2e], &bar[0x2f],
+	&bar[0x30], &bar[0x31], &bar[0x32], &bar[0x33],
+	&bar[0x34], &bar[0x35], &bar[0x36], &bar[0x37],
+	&bar[0x38], &bar[0x39], &bar[0x3a], &bar[0x3b],
+	&bar[0x3c], &bar[0x3d], &bar[0x3e], &bar[0x3f],
+	NULL,       &bar[0x41], &bar[0x42], &bar[0x43], /* test a clear bit */
+	&bar[0x44], &bar[0x45], &bar[0x46], &bar[0x47],
+	&bar[0x48], &bar[0x49], &bar[0x4a], &bar[0x4b],
+	&bar[0x4c], &bar[0x4d], &bar[0x4e], &bar[0x4f],
+	&bar[0x50], &bar[0x51], &bar[0x52], &bar[0x53],
+	&bar[0x54], &bar[0x55], &bar[0x56], &bar[0x57],
+	&bar[0x58], &bar[0x59], &bar[0x5a], &bar[0x5b],
+	&bar[0x5c], &bar[0x5d], &bar[0x5e], &bar[0x5f],
+	&bar[0x60], &bar[0x61], &bar[0x62], &bar[0x63],
+	&bar[0x64], &bar[0x65], &bar[0x66], &bar[0x67],
+	&bar[0x68], &bar[0x69], &bar[0x6a], &bar[0x6b],
+	&bar[0x6c], &bar[0x6d], &bar[0x6e], &bar[0x6f],
+	&bar[0x70], &bar[0x71], &bar[0x72], &bar[0x73],
+	&bar[0x74], &bar[0x75], &bar[0x76], &bar[0x77],
+	&bar[0x78], &bar[0x79], &bar[0x7a], &bar[0x7b],
+	&bar[0x7c], &bar[0x7d], &bar[0x7e], &bar[0x7f],
+	NULL,		/* confirm we stop at the end */
+};
+
+static int baz = -0x1adbd477;
+static int *volatile bazp = &baz;
+
+int
+main(void)
+{
+	int i, result = 0;
+
+	if (**foopp != 0x5f4d7635) {
+		fprintf(stderr, "foo @ %p, foop = %p, *foop = %p,"
+		    " **foopp = 0x%x\n",
+		    &foo, foop, *foopp, **foopp);
+		result |= 1;
+	}
+	for (i = 0; i < (int)__arraycount(barp); i++) {
+		if (i == 0x40 || i == 0x80) {
+			if (barp[i] != NULL) {
+				fprintf(stderr, "barp[%u] = %p\n",
+				    i, barp[i]);
+			}
+		} else {
+			if (*barp[i] != i) {
+				fprintf(stderr, "bar[%u] @ %p, barp[%u] = %p,"
+				    " *barp[%u] = %u\n",
+				    i, &bar[i], i, barp[i], i, *barp[i]);
+				result |= 1;
+			}
+		}
+	}
+	if (*bazp != -0x1adbd477) {
+		fprintf(stderr, "baz @ %p, bazp = %p, *bazp = 0x%x\n",
+		    &baz, bazp, *bazp);
+		result |= 1;
+	}
+
+	return result;
+}
diff -r 13492505e640 -r f673072924a9 tests/libexec/ld.elf_so/t_r_rel.sh
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/libexec/ld.elf_so/t_r_rel.sh	Sat Apr 26 21:25:01 2025 +0000
@@ -0,0 +1,115 @@
+#	$NetBSD$
+#
+# Copyright (c) 2025 The NetBSD Foundation, Inc.
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+#    notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+#    notice, this list of conditions and the following disclaimer in the
+#    documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+# ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+# TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+# PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+# BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+# POSSIBILITY OF SUCH DAMAGE.
+#
+
+cleanup_core()
+{
+	local prog
+
+	prog=$1
+	test -f "${prog}.core" || return
+	readelf -rs "$(atf_get_srcdir)/${prog}"
+	gdb -batch -ex bt -ex 'info registers' -ex disas \
+	    "$(atf_get_srcdir)/${prog}" "${prog}.core"
+}
+
+atf_test_case readelf_relative_nopack
+readelf_relative_nopack_head()
+{
+	atf_set "descr" "readelf R_*_RELATIVE with -z nopack-relative-relocs"
+	atf_set "require.progs" "readelf"
+}
+readelf_relative_nopack_body()
+{
+	atf_check -o match:'R_.*_REL' \
+	    readelf -r "$(atf_get_srcdir)"/h_r_rel_nopack
+}
+
+atf_test_case readelf_relative_pack
+readelf_relative_pack_head()
+{
+	atf_set "descr" "readelf R_*_RELATIVE with -z pack-relative-relocs"
+	atf_set "require.progs" "readelf"
+}
+readelf_relative_pack_body()
+{
+	case `uname -p` in
+	i386|powerpc64*|x86_64)
+		;;
+	*)	# Actually missing GNU binutils ld(1) support.
+		atf_expect_fail "PR bin/59360: ld.elf_so(8):" \
+		    " missing RELR support"
+		;;
+	esac
+	atf_check -o not-match:'R_.*_REL' \
+	    readelf -r "$(atf_get_srcdir)"/h_r_rel_pack
+}
+
+atf_test_case run_relative_nopack cleanup
+run_relative_nopack_head()
+{
+	atf_set "descr" "run R_*_RELATIVE with -z nopack-relative-relocs"
+}
+run_relative_nopack_body()
+{
+	atf_check "$(atf_get_srcdir)"/h_r_rel_nopack
+}
+run_relative_nopack_cleanup()
+{
+	cleanup_core h_r_rel_nopack
+}
+
+atf_test_case run_relative_pack cleanup
+run_relative_pack_head()
+{
+	atf_set "descr" "run R_*_RELATIVE with -z pack-relative-relocs"
+}
+run_relative_pack_body()
+{
+	case `uname -p` in
+	i386|powerpc64*|x86_64)
+		atf_expect_fail "PR bin/59360: ld.elf_so(8):"
+		    " missing RELR support"
+		;;
+	*)	# Missing GNU binutils ld(1) support to generate RELR
+		# sections, so the program should run just fine because
+		# it just uses traditional REL/RELA instead.
+		;;
+	esac
+	atf_check "$(atf_get_srcdir)"/h_r_rel_pack
+}
+run_relative_pack_cleanup()
+{
+	cleanup_core h_r_rel_pack
+}
+
+atf_init_test_cases()
+{
+	atf_add_test_case readelf_relative_nopack
+	atf_add_test_case readelf_relative_pack
+	atf_add_test_case run_relative_nopack
+	atf_add_test_case run_relative_pack
+}
# HG changeset patch
# User Taylor R Campbell <riastradh%NetBSD.org@localhost>
# Date 1745709336 0
#      Sat Apr 26 23:15:36 2025 +0000
# Branch trunk
# Node ID 508beec7e3dd6d44e78a570a662e6edd40e87c7a
# Parent  f673072924a94e661ab1235cc1911b82ec4d43ad
# EXP-Topic riastradh-pr59360-ldelfsorelr
sys/exec_elf.h: Add RELR definitions.

The SHT_RELR .relr.dyn section, identified in DT_RELR dynamic tag,
holds compressed R_*_RELATIVE-type relocations, substantially
reducing the disk space occupied by many programs.

Reference:

Rahul Chaudhry, `Re: Proposal for a new section type SHT_RELR',
generic-abi mailing list, 2018-02-07.

http://groups.google.com/g/generic-abi/c/bX460iggiKg/m/Jnz1lgLJAgAJ
http://web.archive.org/web/20241213012330/http://groups.google.com/g/generic-abi/c/bX460iggiKg/m/Jnz1lgLJAgAJ

PR bin/59360: ld.elf_so(8): missing RELR support

diff -r f673072924a9 -r 508beec7e3dd sys/sys/exec_elf.h
--- a/sys/sys/exec_elf.h	Sat Apr 26 21:25:01 2025 +0000
+++ b/sys/sys/exec_elf.h	Sat Apr 26 23:15:36 2025 +0000
@@ -517,7 +517,8 @@ typedef struct {
 #define SHT_PREINIT_ARRAY    16		/* Pre-initialization function ptrs */
 #define SHT_GROUP	     17		/* Section group */
 #define SHT_SYMTAB_SHNDX     18		/* Section indexes (see SHN_XINDEX) */
-#define SHT_NUM		     19
+#define SHT_RELR	     19		/* Relative relocation information */
+#define SHT_NUM		     20
 
 #define SHT_LOOS	     0x60000000 /* Operating system specific range */
 #define SHT_GNU_INCREMENTAL_INPUTS 0x6fff4700   /* GNU incremental build data */
@@ -681,6 +682,9 @@ typedef struct {
 #define ELF32_R_TYPE(info)	((info) & 0xff)
 #define ELF32_R_INFO(sym, type) (((sym) << 8) + (unsigned char)(type))
 
+/* Relative relocations (DT_RELR, SHT_RELR, .relr.dyn) */
+typedef Elf32_Word	Elf32_Relr;
+
 typedef struct {
 	Elf64_Addr	r_offset;	/* where to do it */
 	Elf64_Xword	r_info;		/* index & type of relocation */
@@ -697,6 +701,9 @@ typedef struct {
 #define ELF64_R_TYPE(info)	((info) & 0xffffffff)
 #define ELF64_R_INFO(sym,type)	(((sym) << 32) + (type))
 
+/* Relative relocations (DT_RELR, SHT_RELR, .relr.dyn) */
+typedef Elf64_Xword	Elf64_Relr;
+
 /*
  * Move entries
  */
@@ -798,7 +805,10 @@ typedef struct {
 #define DT_PREINIT_ARRAY 32	/* Address of pre-init function array */
 #define DT_PREINIT_ARRAYSZ 33	/* Size, in bytes, of DT_PREINIT_ARRAY array */
 #define DT_SYMTAB_SHNDX 34	/* Addr. of SHT_SYMTAB_SHNDX § of DT_SYMTAB */
-#define DT_NUM		35
+#define DT_RELRSZ	35	/* Size, in bytes, of DT_RELR table */
+#define DT_RELR		36	/* Address of Relr relocation table */
+#define DT_RELRENT	37	/* Size, in bytes, of one DT_RELR entry */
+#define DT_NUM		38
 
 #define DT_LOOS		0x60000000	/* Operating system specific range */
 #define DT_GNU_HASH	0x6ffffef5	/* GNU-style hash table */
@@ -1206,6 +1216,7 @@ struct netbsd_elfcore_procinfo {
 #define Elf_Sym		Elf32_Sym
 #define Elf_Rel		Elf32_Rel
 #define Elf_Rela	Elf32_Rela
+#define Elf_Relr	Elf32_Relr
 #define Elf_Dyn		Elf32_Dyn
 #define Elf_Word	Elf32_Word
 #define Elf_Sword	Elf32_Sword
@@ -1232,6 +1243,7 @@ struct netbsd_elfcore_procinfo {
 #define Elf_Sym		Elf64_Sym
 #define Elf_Rel		Elf64_Rel
 #define Elf_Rela	Elf64_Rela
+#define Elf_Relr	Elf64_Relr
 #define Elf_Dyn		Elf64_Dyn
 #define Elf_Word	Elf64_Word
 #define Elf_Sword	Elf64_Sword
# HG changeset patch
# User Taylor R Campbell <riastradh%NetBSD.org@localhost>
# Date 1745710299 0
#      Sat Apr 26 23:31:39 2025 +0000
# Branch trunk
# Node ID 40467c0f0c9bd9a70414873f87a0ffd17da67dfd
# Parent  508beec7e3dd6d44e78a570a662e6edd40e87c7a
# EXP-Topic riastradh-pr59360-ldelfsorelr
ld.elf_so: Implement RELR relocations.

The SHT_RELR .relr.dyn section, identified in DT_RELR dynamic tag,
holds compressed R_*_RELATIVE-type relocations, substantially
reducing the disk space occupied by many programs.

Reference:

Rahul Chaudhry, `Re: Proposal for a new section type SHT_RELR',
generic-abi mailing list, 2018-02-07.

http://groups.google.com/g/generic-abi/c/bX460iggiKg/m/Jnz1lgLJAgAJ
http://web.archive.org/web/20241213012330/http://groups.google.com/g/generic-abi/c/bX460iggiKg/m/Jnz1lgLJAgAJ

PR bin/59360: ld.elf_so(8): missing RELR support

diff -r 508beec7e3dd -r 40467c0f0c9b libexec/ld.elf_so/headers.c
--- a/libexec/ld.elf_so/headers.c	Sat Apr 26 23:15:36 2025 +0000
+++ b/libexec/ld.elf_so/headers.c	Sat Apr 26 23:31:39 2025 +0000
@@ -72,7 +72,7 @@ void
 	const Elf_Dyn  *dyn_rpath = NULL;
 	bool		use_pltrel = false;
 	bool		use_pltrela = false;
-	Elf_Addr        relsz = 0, relasz = 0;
+	Elf_Addr        relsz = 0, relasz = 0, relrsz = 0;
 	Elf_Addr	pltrel = 0, pltrelsz = 0;
 #ifdef RTLD_LOADER
 	Elf_Addr	init = 0, fini = 0;
@@ -117,6 +117,19 @@ void
 			assert(dynp->d_un.d_val == sizeof(Elf_Rela));
 			break;
 
+		case DT_RELR:
+			obj->relr = (const Elf_Relr *)(obj->relocbase +
+			    dynp->d_un.d_ptr);
+			break;
+
+		case DT_RELRSZ:
+			relrsz = dynp->d_un.d_val;
+			break;
+
+		case DT_RELRENT:
+			assert(dynp->d_un.d_val == sizeof(Elf_Relr));
+			break;
+
 		case DT_PLTREL:
 			use_pltrel = dynp->d_un.d_val == DT_REL;
 			use_pltrela = dynp->d_un.d_val == DT_RELA;
@@ -426,6 +439,7 @@ void
 
 	obj->rellim = (const Elf_Rel *)((const uint8_t *)obj->rel + relsz);
 	obj->relalim = (const Elf_Rela *)((const uint8_t *)obj->rela + relasz);
+	obj->relrlim = (const Elf_Relr *)((const uint8_t *)obj->relr + relrsz);
 	if (use_pltrel) {
 		obj->pltrel = (const Elf_Rel *)(obj->relocbase + pltrel);
 		obj->pltrellim = (const Elf_Rel *)(obj->relocbase + pltrel + pltrelsz);
diff -r 508beec7e3dd -r 40467c0f0c9b libexec/ld.elf_so/reloc.c
--- a/libexec/ld.elf_so/reloc.c	Sat Apr 26 23:15:36 2025 +0000
+++ b/libexec/ld.elf_so/reloc.c	Sat Apr 26 23:31:39 2025 +0000
@@ -178,6 +178,80 @@ int
 }
 
 /*
+ * _rtld_relocate_relr(obj)
+ *
+ *	Relocate the RELR entries of obj.  The RELR table is encoded as
+ *	a sequence of alternating addresses and bitmaps.  Each address
+ *	entry has the low-order bit clear, and each bitmap has the
+ *	low-order bit set:
+ *
+ *		AAAAAAA0
+ *		BBBBBBB1
+ *		BBBBBBB1
+ *		BBBBBBB1
+ *		AAAAAAA0
+ *		BBBBBBB1
+ *		...
+ *
+ *	Each address A is taken relative to obj->relocbase, and has
+ *	obj->relocbase added to the Elf_Addr it points at.  For each
+ *	bit i in the following bitmaps concatenated starting at 1,
+ *	excluding the low-order bit used to distinguish bitmaps from
+ *	addresses, the Elf_Addr at the address
+ *
+ *		A + sizeof(Elf_Addr)*i
+ *
+ *	(again, relative to obj->relocbase) has obj->relocbase added
+ *	too.
+ *
+ *	DT_RELR relocations are processed before any DT_REL or DT_RELA
+ *	relocations.
+ *
+ *	References:
+ *
+ *	Rahul Chaudhry, `Re: Proposal for a new section type SHT_RELR',
+ *	generic-abi mailing list, 2018-02-07.
+ *
+ *	http://groups.google.com/g/generic-abi/c/bX460iggiKg/m/Jnz1lgLJAgAJ
+ *	http://web.archive.org/web/20241213012330/http://groups.google.com/g/generic-abi/c/bX460iggiKg/m/Jnz1lgLJAgAJ
+ */
+static void
+_rtld_relocate_relr(Obj_Entry *obj)
+{
+	const Elf_Relr *relr;
+	Elf_Addr *where;
+	unsigned i;
+
+	if (obj->relr == obj->relrlim)
+		return;
+
+	for (relr = obj->relr; relr < obj->relrlim;) {
+		/*
+		 * At an address entry.  Relocate the address.
+		 */
+		assert((*relr & 1) == 0);
+		where = (Elf_Addr *)(obj->relocbase + *relr);
+		*where++ += (Elf_Addr)obj->relocbase;
+
+		/*
+		 * Process every bitmap entry after the address.
+		 */
+		while (++relr < obj->relrlim && *relr & 1) {
+			/*
+			 * Process every set bit in the bitmap.  Note
+			 * that the first bit (i=0) is not processed
+			 * here -- it's just metadata to mark a bitmap
+			 * entry.
+			 */
+			for (i = 1; i < CHAR_BIT*sizeof(*relr); i++, where++) {
+				if (*relr & ((Elf_Relr)1 << i))
+					*where += (Elf_Addr)obj->relocbase;
+			}
+		}
+	}
+}
+
+/*
  * Relocate newly-loaded shared objects.  The argument is a pointer to
  * the Obj_Entry for the first such object.  All objects from the first
  * to the end of the list of objects are relocated.  Returns 0 on success,
@@ -220,6 +294,8 @@ int
 				return -1;
 			}
 		}
+		dbg(("doing relative relocations"));
+		_rtld_relocate_relr(obj);
 		dbg(("doing non-PLT relocations"));
 		if (_rtld_relocate_nonplt_objects(obj) < 0)
 			ok = 0;
diff -r 508beec7e3dd -r 40467c0f0c9b libexec/ld.elf_so/rtld.h
--- a/libexec/ld.elf_so/rtld.h	Sat Apr 26 23:15:36 2025 +0000
+++ b/libexec/ld.elf_so/rtld.h	Sat Apr 26 23:31:39 2025 +0000
@@ -168,6 +168,8 @@ typedef struct Struct_Obj_Entry {
 	const Elf_Rel  *rellim;		/* Limit of Relocation entries */
 	const Elf_Rela *rela;		/* Relocation entries */
 	const Elf_Rela *relalim;	/* Limit of Relocation entries */
+	const Elf_Relr *relr;		/* Relative relocations */
+	const Elf_Relr *relrlim;	/* Limit of relative relocations */
 	const Elf_Rel  *pltrel;		/* PLT relocation entries */
 	const Elf_Rel  *pltrellim;	/* Limit of PLT relocation entries */
 	const Elf_Rela *pltrela;	/* PLT relocation entries */
diff -r 508beec7e3dd -r 40467c0f0c9b tests/libexec/ld.elf_so/t_r_rel.sh
--- a/tests/libexec/ld.elf_so/t_r_rel.sh	Sat Apr 26 23:15:36 2025 +0000
+++ b/tests/libexec/ld.elf_so/t_r_rel.sh	Sat Apr 26 23:31:39 2025 +0000
@@ -91,8 +91,6 @@ run_relative_pack_body()
 {
 	case `uname -p` in
 	i386|powerpc64*|x86_64)
-		atf_expect_fail "PR bin/59360: ld.elf_so(8):"
-		    " missing RELR support"
 		;;
 	*)	# Missing GNU binutils ld(1) support to generate RELR
 		# sections, so the program should run just fine because
diff -r 13492505e640 -r 40467c0f0c9b distrib/sets/lists/debug/mi
--- a/distrib/sets/lists/debug/mi	Sat Apr 26 20:28:14 2025 +0000
+++ b/distrib/sets/lists/debug/mi	Sat Apr 26 23:31:39 2025 +0000
@@ -2455,6 +2455,8 @@
 ./usr/libdata/debug/usr/tests/libexec/ld.elf_so/h_dl_symver_v2.debug	tests-libexec-debug	debug,atf,pic,compattestfile
 ./usr/libdata/debug/usr/tests/libexec/ld.elf_so/h_ifunc.debug	tests-libexec-debug	debug,atf,pic,compattestfile
 ./usr/libdata/debug/usr/tests/libexec/ld.elf_so/h_locking.debug	tests-libexec-debug	debug,atf,pic,compattestfile
+./usr/libdata/debug/usr/tests/libexec/ld.elf_so/h_r_rel_nopack.debug	tests-libexec-debug	debug,atf,pic,compattestfile
+./usr/libdata/debug/usr/tests/libexec/ld.elf_so/h_r_rel_pack.debug	tests-libexec-debug	debug,atf,pic,compattestfile
 ./usr/libdata/debug/usr/tests/libexec/ld.elf_so/t_dlerror-cleared.debug	tests-libexec-debug	debug,atf,pic,compattestfile
 ./usr/libdata/debug/usr/tests/libexec/ld.elf_so/t_dlerror-false.debug	tests-libexec-debug	debug,atf,pic,compattestfile
 ./usr/libdata/debug/usr/tests/libexec/ld.elf_so/t_dlinfo.debug		tests-libexec-debug	debug,atf,pic,compattestfile
diff -r 13492505e640 -r 40467c0f0c9b distrib/sets/lists/tests/mi
--- a/distrib/sets/lists/tests/mi	Sat Apr 26 20:28:14 2025 +0000
+++ b/distrib/sets/lists/tests/mi	Sat Apr 26 23:31:39 2025 +0000
@@ -4264,6 +4264,8 @@
 ./usr/tests/libexec/ld.elf_so/h_helper_symver_dso2	tests-libexec-tests	compattestfile,atf
 ./usr/tests/libexec/ld.elf_so/h_ifunc			tests-libexec-tests	compattestfile,atf,pic
 ./usr/tests/libexec/ld.elf_so/h_locking			tests-libexec-tests	compattestfile,atf,pic
+./usr/tests/libexec/ld.elf_so/h_r_rel_nopack		tests-libexec-tests	compattestfile,atf,pic
+./usr/tests/libexec/ld.elf_so/h_r_rel_pack		tests-libexec-tests	compattestfile,atf,pic
 ./usr/tests/libexec/ld.elf_so/h_thread_local_dtor	tests-libexec-tests	compattestfile,atf,pic
 ./usr/tests/libexec/ld.elf_so/t_df_1_noopen		tests-libexec-tests	compattestfile,atf,pic
 ./usr/tests/libexec/ld.elf_so/t_dl_symver		tests-libexec-tests	compattestfile,atf,pic
@@ -4276,6 +4278,7 @@
 ./usr/tests/libexec/ld.elf_so/t_ifunc_norelro		tests-libexec-tests	compattestfile,atf,pic
 ./usr/tests/libexec/ld.elf_so/t_ifunc_norelro_now	tests-libexec-tests	compattestfile,atf,pic
 ./usr/tests/libexec/ld.elf_so/t_ifunc_now		tests-libexec-tests	compattestfile,atf,pic
+./usr/tests/libexec/ld.elf_so/t_r_rel			tests-libexec-tests	compattestfile,atf,pic
 ./usr/tests/libexec/ld.elf_so/t_rtld_r_debug		tests-libexec-tests	compattestfile,atf,pic
 ./usr/tests/libexec/ld.elf_so/t_rtld_r_debug_nopie	tests-libexec-tests	compattestfile,atf,pic
 ./usr/tests/libexec/ld.elf_so/t_thread_local_dtor	tests-libexec-tests	compattestfile,atf,pic
diff -r 13492505e640 -r 40467c0f0c9b libexec/ld.elf_so/headers.c
--- a/libexec/ld.elf_so/headers.c	Sat Apr 26 20:28:14 2025 +0000
+++ b/libexec/ld.elf_so/headers.c	Sat Apr 26 23:31:39 2025 +0000
@@ -72,7 +72,7 @@ void
 	const Elf_Dyn  *dyn_rpath = NULL;
 	bool		use_pltrel = false;
 	bool		use_pltrela = false;
-	Elf_Addr        relsz = 0, relasz = 0;
+	Elf_Addr        relsz = 0, relasz = 0, relrsz = 0;
 	Elf_Addr	pltrel = 0, pltrelsz = 0;
 #ifdef RTLD_LOADER
 	Elf_Addr	init = 0, fini = 0;
@@ -117,6 +117,19 @@ void
 			assert(dynp->d_un.d_val == sizeof(Elf_Rela));
 			break;
 
+		case DT_RELR:
+			obj->relr = (const Elf_Relr *)(obj->relocbase +
+			    dynp->d_un.d_ptr);
+			break;
+
+		case DT_RELRSZ:
+			relrsz = dynp->d_un.d_val;
+			break;
+
+		case DT_RELRENT:
+			assert(dynp->d_un.d_val == sizeof(Elf_Relr));
+			break;
+
 		case DT_PLTREL:
 			use_pltrel = dynp->d_un.d_val == DT_REL;
 			use_pltrela = dynp->d_un.d_val == DT_RELA;
@@ -426,6 +439,7 @@ void
 
 	obj->rellim = (const Elf_Rel *)((const uint8_t *)obj->rel + relsz);
 	obj->relalim = (const Elf_Rela *)((const uint8_t *)obj->rela + relasz);
+	obj->relrlim = (const Elf_Relr *)((const uint8_t *)obj->relr + relrsz);
 	if (use_pltrel) {
 		obj->pltrel = (const Elf_Rel *)(obj->relocbase + pltrel);
 		obj->pltrellim = (const Elf_Rel *)(obj->relocbase + pltrel + pltrelsz);
diff -r 13492505e640 -r 40467c0f0c9b libexec/ld.elf_so/reloc.c
--- a/libexec/ld.elf_so/reloc.c	Sat Apr 26 20:28:14 2025 +0000
+++ b/libexec/ld.elf_so/reloc.c	Sat Apr 26 23:31:39 2025 +0000
@@ -178,6 +178,80 @@ int
 }
 
 /*
+ * _rtld_relocate_relr(obj)
+ *
+ *	Relocate the RELR entries of obj.  The RELR table is encoded as
+ *	a sequence of alternating addresses and bitmaps.  Each address
+ *	entry has the low-order bit clear, and each bitmap has the
+ *	low-order bit set:
+ *
+ *		AAAAAAA0
+ *		BBBBBBB1
+ *		BBBBBBB1
+ *		BBBBBBB1
+ *		AAAAAAA0
+ *		BBBBBBB1
+ *		...
+ *
+ *	Each address A is taken relative to obj->relocbase, and has
+ *	obj->relocbase added to the Elf_Addr it points at.  For each
+ *	bit i in the following bitmaps concatenated starting at 1,
+ *	excluding the low-order bit used to distinguish bitmaps from
+ *	addresses, the Elf_Addr at the address
+ *
+ *		A + sizeof(Elf_Addr)*i
+ *
+ *	(again, relative to obj->relocbase) has obj->relocbase added
+ *	too.
+ *
+ *	DT_RELR relocations are processed before any DT_REL or DT_RELA
+ *	relocations.
+ *
+ *	References:
+ *
+ *	Rahul Chaudhry, `Re: Proposal for a new section type SHT_RELR',
+ *	generic-abi mailing list, 2018-02-07.
+ *
+ *	http://groups.google.com/g/generic-abi/c/bX460iggiKg/m/Jnz1lgLJAgAJ
+ *	http://web.archive.org/web/20241213012330/http://groups.google.com/g/generic-abi/c/bX460iggiKg/m/Jnz1lgLJAgAJ
+ */
+static void
+_rtld_relocate_relr(Obj_Entry *obj)
+{
+	const Elf_Relr *relr;
+	Elf_Addr *where;
+	unsigned i;
+
+	if (obj->relr == obj->relrlim)
+		return;
+
+	for (relr = obj->relr; relr < obj->relrlim;) {
+		/*
+		 * At an address entry.  Relocate the address.
+		 */
+		assert((*relr & 1) == 0);
+		where = (Elf_Addr *)(obj->relocbase + *relr);
+		*where++ += (Elf_Addr)obj->relocbase;
+
+		/*
+		 * Process every bitmap entry after the address.
+		 */
+		while (++relr < obj->relrlim && *relr & 1) {
+			/*
+			 * Process every set bit in the bitmap.  Note
+			 * that the first bit (i=0) is not processed
+			 * here -- it's just metadata to mark a bitmap
+			 * entry.
+			 */
+			for (i = 1; i < CHAR_BIT*sizeof(*relr); i++, where++) {
+				if (*relr & ((Elf_Relr)1 << i))
+					*where += (Elf_Addr)obj->relocbase;
+			}
+		}
+	}
+}
+
+/*
  * Relocate newly-loaded shared objects.  The argument is a pointer to
  * the Obj_Entry for the first such object.  All objects from the first
  * to the end of the list of objects are relocated.  Returns 0 on success,
@@ -220,6 +294,8 @@ int
 				return -1;
 			}
 		}
+		dbg(("doing relative relocations"));
+		_rtld_relocate_relr(obj);
 		dbg(("doing non-PLT relocations"));
 		if (_rtld_relocate_nonplt_objects(obj) < 0)
 			ok = 0;
diff -r 13492505e640 -r 40467c0f0c9b libexec/ld.elf_so/rtld.h
--- a/libexec/ld.elf_so/rtld.h	Sat Apr 26 20:28:14 2025 +0000
+++ b/libexec/ld.elf_so/rtld.h	Sat Apr 26 23:31:39 2025 +0000
@@ -168,6 +168,8 @@ typedef struct Struct_Obj_Entry {
 	const Elf_Rel  *rellim;		/* Limit of Relocation entries */
 	const Elf_Rela *rela;		/* Relocation entries */
 	const Elf_Rela *relalim;	/* Limit of Relocation entries */
+	const Elf_Relr *relr;		/* Relative relocations */
+	const Elf_Relr *relrlim;	/* Limit of relative relocations */
 	const Elf_Rel  *pltrel;		/* PLT relocation entries */
 	const Elf_Rel  *pltrellim;	/* Limit of PLT relocation entries */
 	const Elf_Rela *pltrela;	/* PLT relocation entries */
diff -r 13492505e640 -r 40467c0f0c9b sys/sys/exec_elf.h
--- a/sys/sys/exec_elf.h	Sat Apr 26 20:28:14 2025 +0000
+++ b/sys/sys/exec_elf.h	Sat Apr 26 23:31:39 2025 +0000
@@ -517,7 +517,8 @@ typedef struct {
 #define SHT_PREINIT_ARRAY    16		/* Pre-initialization function ptrs */
 #define SHT_GROUP	     17		/* Section group */
 #define SHT_SYMTAB_SHNDX     18		/* Section indexes (see SHN_XINDEX) */
-#define SHT_NUM		     19
+#define SHT_RELR	     19		/* Relative relocation information */
+#define SHT_NUM		     20
 
 #define SHT_LOOS	     0x60000000 /* Operating system specific range */
 #define SHT_GNU_INCREMENTAL_INPUTS 0x6fff4700   /* GNU incremental build data */
@@ -681,6 +682,9 @@ typedef struct {
 #define ELF32_R_TYPE(info)	((info) & 0xff)
 #define ELF32_R_INFO(sym, type) (((sym) << 8) + (unsigned char)(type))
 
+/* Relative relocations (DT_RELR, SHT_RELR, .relr.dyn) */
+typedef Elf32_Word	Elf32_Relr;
+
 typedef struct {
 	Elf64_Addr	r_offset;	/* where to do it */
 	Elf64_Xword	r_info;		/* index & type of relocation */
@@ -697,6 +701,9 @@ typedef struct {
 #define ELF64_R_TYPE(info)	((info) & 0xffffffff)
 #define ELF64_R_INFO(sym,type)	(((sym) << 32) + (type))
 
+/* Relative relocations (DT_RELR, SHT_RELR, .relr.dyn) */
+typedef Elf64_Xword	Elf64_Relr;
+
 /*
  * Move entries
  */
@@ -798,7 +805,10 @@ typedef struct {
 #define DT_PREINIT_ARRAY 32	/* Address of pre-init function array */
 #define DT_PREINIT_ARRAYSZ 33	/* Size, in bytes, of DT_PREINIT_ARRAY array */
 #define DT_SYMTAB_SHNDX 34	/* Addr. of SHT_SYMTAB_SHNDX § of DT_SYMTAB */
-#define DT_NUM		35
+#define DT_RELRSZ	35	/* Size, in bytes, of DT_RELR table */
+#define DT_RELR		36	/* Address of Relr relocation table */
+#define DT_RELRENT	37	/* Size, in bytes, of one DT_RELR entry */
+#define DT_NUM		38
 
 #define DT_LOOS		0x60000000	/* Operating system specific range */
 #define DT_GNU_HASH	0x6ffffef5	/* GNU-style hash table */
@@ -1206,6 +1216,7 @@ struct netbsd_elfcore_procinfo {
 #define Elf_Sym		Elf32_Sym
 #define Elf_Rel		Elf32_Rel
 #define Elf_Rela	Elf32_Rela
+#define Elf_Relr	Elf32_Relr
 #define Elf_Dyn		Elf32_Dyn
 #define Elf_Word	Elf32_Word
 #define Elf_Sword	Elf32_Sword
@@ -1232,6 +1243,7 @@ struct netbsd_elfcore_procinfo {
 #define Elf_Sym		Elf64_Sym
 #define Elf_Rel		Elf64_Rel
 #define Elf_Rela	Elf64_Rela
+#define Elf_Relr	Elf64_Relr
 #define Elf_Dyn		Elf64_Dyn
 #define Elf_Word	Elf64_Word
 #define Elf_Sword	Elf64_Sword
diff -r 13492505e640 -r 40467c0f0c9b tests/libexec/ld.elf_so/Makefile
--- a/tests/libexec/ld.elf_so/Makefile	Sat Apr 26 20:28:14 2025 +0000
+++ b/tests/libexec/ld.elf_so/Makefile	Sat Apr 26 23:31:39 2025 +0000
@@ -80,6 +80,7 @@ LDADD.t_tls_extern+=	-Wl,-rpath,${TESTSD
 
 TESTS_SH+=		t_df_1_noopen
 TESTS_SH+=		t_dl_symver
+TESTS_SH+=		t_r_rel
 TESTS_SH+=		t_thread_local_dtor
 
 BINDIR=			${TESTSDIR}
@@ -117,6 +118,16 @@ SRCS.h_dl_symver_v2=	h_dl_symver.c
 V2ODIR!=		cd ${.CURDIR}/helper_symver_dso2 && ${PRINTOBJDIR}
 LDADD.h_dl_symver_v2=	-L${V2ODIR} -lh_helper_symver_dso
 
+PROGS+=			h_r_rel_pack
+PROGS+=			h_r_rel_nopack
+
+SRCS.h_r_rel_pack=	h_r_rel.c
+SRCS.h_r_rel_nopack=	h_r_rel.c
+
+h_r_rel_pack: CTFMERGE=:	# PR toolchain/59364: ctf tools needs update
+LDFLAGS.h_r_rel_pack=	-Wl,-z,pack-relative-relocs
+LDFLAGS.h_r_rel_nopack=	-Wl,-z,nopack-relative-relocs
+
 .include <bsd.test.mk>
 
 .else
diff -r 13492505e640 -r 40467c0f0c9b tests/libexec/ld.elf_so/h_r_rel.c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/libexec/ld.elf_so/h_r_rel.c	Sat Apr 26 23:31:39 2025 +0000
@@ -0,0 +1,146 @@
+/*	$NetBSD$	*/
+
+/*-
+ * Copyright (c) 2025 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__RCSID("$NetBSD$");
+
+#include <stdio.h>
+
+/*
+ * When built as position-independent executable, the value of foop and
+ * foopp should be computed either via R_*_RELATIVE or R_*_REL32 or
+ * similar, which -- ports that support it -- may be compressed into a
+ * SHT_RELR section.
+ *
+ * One pointer indirection is enough to produce this effect, but we use
+ * two pointer indirections to increase the probability of a crash in
+ * case the relocations are done wrong.
+ */
+static int foo = 0x5f4d7635;
+static int *volatile foop = &foo;
+static int *volatile *volatile foopp = &foop;
+
+/*
+ * The RELR section compresses relocations for adjacent addresses into
+ * bitmaps of 31 or 63 bits apiece.  Create a bunch of consecutive
+ * addresses to relocate, punctuated by the occasional non-relocated
+ * address (null), to check for fencepost errors in the bitmap
+ * iteration.
+ */
+static int bar[] = {
+	0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+	0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+	0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
+	0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,
+	0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27,
+	0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f,
+	0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37,
+	0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f,
+	0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47,
+	0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f,
+	0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57,
+	0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f,
+	0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67,
+	0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f,
+	0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77,
+	0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f,
+};
+
+static int *volatile barp[] = {
+	&bar[0x00], &bar[0x01], &bar[0x02], &bar[0x03],
+	&bar[0x04], &bar[0x05], &bar[0x06], &bar[0x07],
+	&bar[0x08], &bar[0x09], &bar[0x0a], &bar[0x0b],
+	&bar[0x0c], &bar[0x0d], &bar[0x0e], &bar[0x0f],
+	&bar[0x10], &bar[0x11], &bar[0x12], &bar[0x13],
+	&bar[0x14], &bar[0x15], &bar[0x16], &bar[0x17],
+	&bar[0x18], &bar[0x19], &bar[0x1a], &bar[0x1b],
+	&bar[0x1c], &bar[0x1d], &bar[0x1e], &bar[0x1f],
+	&bar[0x20], &bar[0x21], &bar[0x22], &bar[0x23],
+	&bar[0x24], &bar[0x25], &bar[0x26], &bar[0x27],
+	&bar[0x28], &bar[0x29], &bar[0x2a], &bar[0x2b],
+	&bar[0x2c], &bar[0x2d], &bar[0x2e], &bar[0x2f],
+	&bar[0x30], &bar[0x31], &bar[0x32], &bar[0x33],
+	&bar[0x34], &bar[0x35], &bar[0x36], &bar[0x37],
+	&bar[0x38], &bar[0x39], &bar[0x3a], &bar[0x3b],
+	&bar[0x3c], &bar[0x3d], &bar[0x3e], &bar[0x3f],
+	NULL,       &bar[0x41], &bar[0x42], &bar[0x43], /* test a clear bit */
+	&bar[0x44], &bar[0x45], &bar[0x46], &bar[0x47],
+	&bar[0x48], &bar[0x49], &bar[0x4a], &bar[0x4b],
+	&bar[0x4c], &bar[0x4d], &bar[0x4e], &bar[0x4f],
+	&bar[0x50], &bar[0x51], &bar[0x52], &bar[0x53],
+	&bar[0x54], &bar[0x55], &bar[0x56], &bar[0x57],
+	&bar[0x58], &bar[0x59], &bar[0x5a], &bar[0x5b],
+	&bar[0x5c], &bar[0x5d], &bar[0x5e], &bar[0x5f],
+	&bar[0x60], &bar[0x61], &bar[0x62], &bar[0x63],
+	&bar[0x64], &bar[0x65], &bar[0x66], &bar[0x67],
+	&bar[0x68], &bar[0x69], &bar[0x6a], &bar[0x6b],
+	&bar[0x6c], &bar[0x6d], &bar[0x6e], &bar[0x6f],
+	&bar[0x70], &bar[0x71], &bar[0x72], &bar[0x73],
+	&bar[0x74], &bar[0x75], &bar[0x76], &bar[0x77],
+	&bar[0x78], &bar[0x79], &bar[0x7a], &bar[0x7b],
+	&bar[0x7c], &bar[0x7d], &bar[0x7e], &bar[0x7f],
+	NULL,		/* confirm we stop at the end */
+};
+
+static int baz = -0x1adbd477;
+static int *volatile bazp = &baz;
+
+int
+main(void)
+{
+	int i, result = 0;
+
+	if (**foopp != 0x5f4d7635) {
+		fprintf(stderr, "foo @ %p, foop = %p, *foop = %p,"
+		    " **foopp = 0x%x\n",
+		    &foo, foop, *foopp, **foopp);
+		result |= 1;
+	}
+	for (i = 0; i < (int)__arraycount(barp); i++) {
+		if (i == 0x40 || i == 0x80) {
+			if (barp[i] != NULL) {
+				fprintf(stderr, "barp[%u] = %p\n",
+				    i, barp[i]);
+			}
+		} else {
+			if (*barp[i] != i) {
+				fprintf(stderr, "bar[%u] @ %p, barp[%u] = %p,"
+				    " *barp[%u] = %u\n",
+				    i, &bar[i], i, barp[i], i, *barp[i]);
+				result |= 1;
+			}
+		}
+	}
+	if (*bazp != -0x1adbd477) {
+		fprintf(stderr, "baz @ %p, bazp = %p, *bazp = 0x%x\n",
+		    &baz, bazp, *bazp);
+		result |= 1;
+	}
+
+	return result;
+}
diff -r 13492505e640 -r 40467c0f0c9b tests/libexec/ld.elf_so/t_r_rel.sh
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/libexec/ld.elf_so/t_r_rel.sh	Sat Apr 26 23:31:39 2025 +0000
@@ -0,0 +1,113 @@
+#	$NetBSD$
+#
+# Copyright (c) 2025 The NetBSD Foundation, Inc.
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+#    notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+#    notice, this list of conditions and the following disclaimer in the
+#    documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+# ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+# TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+# PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+# BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+# POSSIBILITY OF SUCH DAMAGE.
+#
+
+cleanup_core()
+{
+	local prog
+
+	prog=$1
+	test -f "${prog}.core" || return
+	readelf -rs "$(atf_get_srcdir)/${prog}"
+	gdb -batch -ex bt -ex 'info registers' -ex disas \
+	    "$(atf_get_srcdir)/${prog}" "${prog}.core"
+}
+
+atf_test_case readelf_relative_nopack
+readelf_relative_nopack_head()
+{
+	atf_set "descr" "readelf R_*_RELATIVE with -z nopack-relative-relocs"
+	atf_set "require.progs" "readelf"
+}
+readelf_relative_nopack_body()
+{
+	atf_check -o match:'R_.*_REL' \
+	    readelf -r "$(atf_get_srcdir)"/h_r_rel_nopack
+}
+
+atf_test_case readelf_relative_pack
+readelf_relative_pack_head()
+{
+	atf_set "descr" "readelf R_*_RELATIVE with -z pack-relative-relocs"
+	atf_set "require.progs" "readelf"
+}
+readelf_relative_pack_body()
+{
+	case `uname -p` in
+	i386|powerpc64*|x86_64)
+		;;
+	*)	# Actually missing GNU binutils ld(1) support.
+		atf_expect_fail "PR bin/59360: ld.elf_so(8):" \
+		    " missing RELR support"
+		;;
+	esac
+	atf_check -o not-match:'R_.*_REL' \
+	    readelf -r "$(atf_get_srcdir)"/h_r_rel_pack
+}
+
+atf_test_case run_relative_nopack cleanup
+run_relative_nopack_head()
+{
+	atf_set "descr" "run R_*_RELATIVE with -z nopack-relative-relocs"
+}
+run_relative_nopack_body()
+{
+	atf_check "$(atf_get_srcdir)"/h_r_rel_nopack
+}
+run_relative_nopack_cleanup()
+{
+	cleanup_core h_r_rel_nopack
+}
+
+atf_test_case run_relative_pack cleanup
+run_relative_pack_head()
+{
+	atf_set "descr" "run R_*_RELATIVE with -z pack-relative-relocs"
+}
+run_relative_pack_body()
+{
+	case `uname -p` in
+	i386|powerpc64*|x86_64)
+		;;
+	*)	# Missing GNU binutils ld(1) support to generate RELR
+		# sections, so the program should run just fine because
+		# it just uses traditional REL/RELA instead.
+		;;
+	esac
+	atf_check "$(atf_get_srcdir)"/h_r_rel_pack
+}
+run_relative_pack_cleanup()
+{
+	cleanup_core h_r_rel_pack
+}
+
+atf_init_test_cases()
+{
+	atf_add_test_case readelf_relative_nopack
+	atf_add_test_case readelf_relative_pack
+	atf_add_test_case run_relative_nopack
+	atf_add_test_case run_relative_pack
+}


Home | Main Index | Thread Index | Old Index