git: fa7b31166ddb - stable/14 - ldconfig: support hints files of either byte-order

From: Stefan Eßer <se_at_FreeBSD.org>
Date: Fri, 29 Mar 2024 06:50:08 UTC
The branch stable/14 has been updated by se:

URL: https://cgit.FreeBSD.org/src/commit/?id=fa7b31166ddb93eddead4c814c5f5e4c0bd38dd7

commit fa7b31166ddb93eddead4c814c5f5e4c0bd38dd7
Author:     Stefan Eßer <se@FreeBSD.org>
AuthorDate: 2024-02-28 17:49:29 +0000
Commit:     Stefan Eßer <se@FreeBSD.org>
CommitDate: 2024-03-29 06:49:02 +0000

    ldconfig: support hints files of either byte-order
    
    Make the ldconfig program accept hints files in little-endian and
    big-endian format on all architectures.
    
    The default format is the native byte-order of the respective host.
    This is expected to change when a version of the pkg command is
    available that implements support for either byte-order in its
    internal ldconfig function. (Already committed in the development
    tree of the pkg utility, a release is expected at the end of Q1/2024).
    
    This update adds the -B option to the ldconfig program. It enforces
    the creation of a big-endian hints file on a little-endian host.
    The main purpose to is support of tests with non-native byte-order
    files on little-endian hosts. It will be removed when all supported
    FreeBSD releases use little-endian hints files by default.
    
    When little-endian hints files are generally used, support of
    either byte-order in libexec/rtld can also be removed.
    
    When support for big-endian hints files is no longer required,
    the COND_SWAP macro in ldconfig and rtld shall be replaced by
    le32toh(), which just return their argument on little-endian
    architectures.
    
    Approved by:    kib
    Relnotes:       yes
    Differential Revision:  https://reviews.freebsd.org/D44093
    
    (cherry picked from commit e0dfecadf5946b6a8ad423be4eb1c14017b3a8a7)
---
 sbin/ldconfig/elfhints.c | 46 ++++++++++++++++++++++++++++++++--------------
 sbin/ldconfig/ldconfig.8 | 11 ++++++++---
 sbin/ldconfig/ldconfig.c | 13 ++++++++-----
 sbin/ldconfig/ldconfig.h |  2 +-
 4 files changed, 49 insertions(+), 23 deletions(-)

diff --git a/sbin/ldconfig/elfhints.c b/sbin/ldconfig/elfhints.c
index 72b9273ab93a..d6ee5e0918d6 100644
--- a/sbin/ldconfig/elfhints.c
+++ b/sbin/ldconfig/elfhints.c
@@ -27,6 +27,7 @@
  */
 
 #include <sys/param.h>
+#include <sys/endian.h>
 #include <sys/mman.h>
 #include <sys/stat.h>
 
@@ -48,11 +49,12 @@
 
 static void	add_dir(const char *, const char *, bool);
 static void	read_dirs_from_file(const char *, const char *);
-static void	read_elf_hints(const char *, bool);
+static void	read_elf_hints(const char *, bool, bool);
 static void	write_elf_hints(const char *);
 
 static const char	*dirs[MAXDIRS];
 static int		 ndirs;
+static bool		 is_be;
 bool			 insecure;
 
 static void
@@ -95,7 +97,7 @@ list_elf_hints(const char *hintsfile)
 	int	i;
 	int	nlibs;
 
-	read_elf_hints(hintsfile, 1);
+	read_elf_hints(hintsfile, true, false);
 	printf("%s:\n", hintsfile);
 	printf("\tsearch directories:");
 	for (i = 0;  i < ndirs;  i++)
@@ -183,8 +185,11 @@ read_dirs_from_file(const char *hintsfile, const char *listfile)
 	fclose(fp);
 }
 
+/* Convert between native byte order and forced little resp. big endian. */
+#define COND_SWAP(n) (is_be ? be32toh(n) : le32toh(n))
+
 static void
-read_elf_hints(const char *hintsfile, bool must_exist)
+read_elf_hints(const char *hintsfile, bool must_exist, bool force_be)
 {
 	int	 		 fd;
 	struct stat		 s;
@@ -193,6 +198,7 @@ read_elf_hints(const char *hintsfile, bool must_exist)
 	char			*strtab;
 	char			*dirlist;
 	char			*p;
+	int			 hdr_version;
 
 	if ((fd = open(hintsfile, O_RDONLY)) == -1) {
 		if (errno == ENOENT && !must_exist)
@@ -214,14 +220,18 @@ read_elf_hints(const char *hintsfile, bool must_exist)
 	close(fd);
 
 	hdr = (struct elfhints_hdr *)mapbase;
-	if (hdr->magic != ELFHINTS_MAGIC)
+	is_be = be32toh(hdr->magic) == ELFHINTS_MAGIC;
+	if (COND_SWAP(hdr->magic) != ELFHINTS_MAGIC)
 		errx(1, "\"%s\": invalid file format", hintsfile);
-	if (hdr->version != 1)
+	if (force_be && !is_be)
+		errx(1, "\"%s\": incompatible endianness requested", hintsfile);
+	hdr_version = COND_SWAP(hdr->version);
+	if (hdr_version != 1)
 		errx(1, "\"%s\": unrecognized file version (%d)", hintsfile,
-		    hdr->version);
+		    hdr_version);
 
-	strtab = (char *)mapbase + hdr->strtab;
-	dirlist = strtab + hdr->dirlist;
+	strtab = (char *)mapbase + COND_SWAP(hdr->strtab);
+	dirlist = strtab + COND_SWAP(hdr->dirlist);
 
 	if (*dirlist != '\0')
 		while ((p = strsep(&dirlist, ":")) != NULL)
@@ -229,13 +239,19 @@ read_elf_hints(const char *hintsfile, bool must_exist)
 }
 
 void
-update_elf_hints(const char *hintsfile, int argc, char **argv, bool merge)
+update_elf_hints(const char *hintsfile, int argc, char **argv, bool merge,
+    bool force_be)
 {
 	struct stat s;
 	int i;
 
+	/*
+	 * Remove "be32toh(1) == 1" from this condition to create
+	 * little-endian hints files on all architectures by default.
+	 */
+	is_be = be32toh(1) == 1 || force_be;
 	if (merge)
-		read_elf_hints(hintsfile, false);
+		read_elf_hints(hintsfile, false, force_be);
 	for (i = 0;  i < argc;  i++) {
 		if (stat(argv[i], &s) == -1)
 			warn("warning: %s", argv[i]);
@@ -265,9 +281,9 @@ write_elf_hints(const char *hintsfile)
 	if ((fp = fdopen(fd, "wb")) == NULL)
 		err(1, "fdopen(%s)", tempname);
 
-	hdr.magic = ELFHINTS_MAGIC;
-	hdr.version = 1;
-	hdr.strtab = sizeof hdr;
+	hdr.magic = COND_SWAP(ELFHINTS_MAGIC);
+	hdr.version = COND_SWAP(1);
+	hdr.strtab = COND_SWAP(sizeof hdr);
 	hdr.strsize = 0;
 	hdr.dirlist = 0;
 	memset(hdr.spare, 0, sizeof hdr.spare);
@@ -278,8 +294,10 @@ write_elf_hints(const char *hintsfile)
 		for (i = 1;  i < ndirs;  i++)
 			hdr.strsize += 1 + strlen(dirs[i]);
 	}
-	hdr.dirlistlen = hdr.strsize;
+	hdr.dirlistlen = COND_SWAP(hdr.strsize);
 	hdr.strsize++;	/* For the null terminator */
+	/* convert in-place from native to target endianness */
+	hdr.strsize = COND_SWAP(hdr.strsize);
 
 	/* Write the header. */
 	if (fwrite(&hdr, 1, sizeof hdr, fp) != sizeof hdr)
diff --git a/sbin/ldconfig/ldconfig.8 b/sbin/ldconfig/ldconfig.8
index 47e0dfa99b50..88d99567912e 100644
--- a/sbin/ldconfig/ldconfig.8
+++ b/sbin/ldconfig/ldconfig.8
@@ -32,7 +32,7 @@
 .\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
 .\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 .\"
-.Dd May 15, 2021
+.Dd February 28, 2024
 .Dt LDCONFIG 8
 .Os
 .Sh NAME
@@ -41,7 +41,7 @@
 .Sh SYNOPSIS
 .Nm
 .Op Fl 32
-.Op Fl Rimrv
+.Op Fl BRimrv
 .Op Fl f Ar hints_file
 .Op Ar directory | Ar
 .Sh DESCRIPTION
@@ -105,6 +105,11 @@ Generate the hints for 32-bit ABI shared libraries
 on 64-bit systems that support running 32-bit binaries.
 .It Fl elf
 Ignored for backwards compatibility.
+.It Fl B
+Force writing big-endian binary data´to the hints file.
+The default is to create little-endian hints files on all architectures.
+Reading of and merging into hints files preserves the endianness of the
+existing hints file.
 .It Fl R
 Appends pathnames on the command line to the directory list from
 the hints file.
@@ -159,7 +164,7 @@ invocations with
 .El
 .Sh SEE ALSO
 .Xr ld 1 ,
-.Xr ld-elf.so.1 ,
+.Xr ld-elf.so.1 1 ,
 .Xr link 5
 .Sh HISTORY
 A
diff --git a/sbin/ldconfig/ldconfig.c b/sbin/ldconfig/ldconfig.c
index 2f5cdbd6505e..0eb9cb801ac2 100644
--- a/sbin/ldconfig/ldconfig.c
+++ b/sbin/ldconfig/ldconfig.c
@@ -57,9 +57,9 @@ main(int argc, char **argv)
 {
 	const char *hints_file;
 	int c;
-	bool is_32, justread, merge, rescan, verbose;
+	bool is_32, justread, merge, rescan, force_be, verbose;
 
-	is_32 = justread = merge = rescan = verbose = false;
+	force_be = is_32 = justread = merge = rescan = verbose = false;
 
 	while (argc > 1) {
 		if (strcmp(argv[1], "-aout") == 0) {
@@ -80,8 +80,11 @@ main(int argc, char **argv)
 		hints_file = __PATH_ELF_HINTS("32");
 	else
 		hints_file = _PATH_ELF_HINTS;
-	while((c = getopt(argc, argv, "Rf:imrsv")) != -1) {
+	while((c = getopt(argc, argv, "BRf:imrsv")) != -1) {
 		switch (c) {
+		case 'B':
+			force_be = true;
+			break;
 		case 'R':
 			rescan = true;
 			break;
@@ -115,7 +118,7 @@ main(int argc, char **argv)
 		if (argc == optind)
 			rescan = true;
 		update_elf_hints(hints_file, argc - optind,
-		    argv + optind, merge || rescan);
+		    argv + optind, merge || rescan, force_be);
 	}
 	exit(0);
 }
@@ -124,7 +127,7 @@ static void
 usage(void)
 {
 	fprintf(stderr,
-	    "usage: ldconfig [-32] [-elf] [-Rimrv] [-f hints_file] "
+	    "usage: ldconfig [-32] [-elf] [-BRimrv] [-f hints_file]"
 	    "[directory | file ...]\n");
 	exit(1);
 }
diff --git a/sbin/ldconfig/ldconfig.h b/sbin/ldconfig/ldconfig.h
index e03ba928be7d..aa7ad810bacd 100644
--- a/sbin/ldconfig/ldconfig.h
+++ b/sbin/ldconfig/ldconfig.h
@@ -36,7 +36,7 @@ extern bool insecure;		/* -i flag, needed here for elfhints.c */
 
 __BEGIN_DECLS
 void	list_elf_hints(const char *);
-void	update_elf_hints(const char *, int, char **, bool);
+void	update_elf_hints(const char *, int, char **, bool, bool);
 __END_DECLS
 
 #endif