diff --git a/sbin/ldconfig/elfhints.c b/sbin/ldconfig/elfhints.c --- a/sbin/ldconfig/elfhints.c +++ b/sbin/ldconfig/elfhints.c @@ -29,6 +29,7 @@ */ #include +#include #include #include @@ -50,8 +51,8 @@ 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 write_elf_hints(const char *); +static bool read_elf_hints(const char *, bool, bool, bool); +static void write_elf_hints(const char *, bool); static const char *dirs[MAXDIRS]; static int ndirs; @@ -97,7 +98,7 @@ int i; int nlibs; - read_elf_hints(hintsfile, 1); + (void)read_elf_hints(hintsfile, 1, false, false); printf("%s:\n", hintsfile); printf("\tsearch directories:"); for (i = 0; i < ndirs; i++) @@ -185,8 +186,12 @@ fclose(fp); } -static void -read_elf_hints(const char *hintsfile, bool must_exist) +/* Convert between native byte order and forced little resp. big endian. */ +#define COND_SWAP(n) (is_le ? le32toh(n) : be32toh(n)) + +static bool +read_elf_hints(const char *hintsfile, bool must_exist, bool force_be, + bool force_le) { int fd; struct stat s; @@ -195,10 +200,11 @@ char *strtab; char *dirlist; char *p; + bool is_le; if ((fd = open(hintsfile, O_RDONLY)) == -1) { if (errno == ENOENT && !must_exist) - return; + return (((htole32(1) == 1) || force_le) && !force_be); err(1, "Cannot open \"%s\"", hintsfile); } if (fstat(fd, &s) == -1) @@ -216,28 +222,36 @@ close(fd); hdr = (struct elfhints_hdr *)mapbase; - if (hdr->magic != ELFHINTS_MAGIC) + is_le = (le32toh(hdr->magic) == ELFHINTS_MAGIC); + if (!is_le && be32toh(hdr->magic) != ELFHINTS_MAGIC) errx(1, "\"%s\": invalid file format", hintsfile); - if (hdr->version != 1) + if ((force_be && is_le) || (force_le && !is_le)) + errx(1, "\"%s\": incompatible endianness requested", hintsfile); + if (COND_SWAP(hdr->version) != 1) errx(1, "\"%s\": unrecognized file version (%d)", hintsfile, - hdr->version); + COND_SWAP(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) add_dir(hintsfile, p, 1); + return (is_le); } 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, bool force_le) { struct stat s; int i; + bool is_le; if (merge) - read_elf_hints(hintsfile, false); + is_le = read_elf_hints(hintsfile, false, force_be, force_le); + else + is_le = ((htole32(1) == 1) || force_le) && !force_be; for (i = 0; i < argc; i++) { if (stat(argv[i], &s) == -1) warn("warning: %s", argv[i]); @@ -246,11 +260,11 @@ else add_dir(hintsfile, argv[i], 0); } - write_elf_hints(hintsfile); + write_elf_hints(hintsfile, is_le); } static void -write_elf_hints(const char *hintsfile) +write_elf_hints(const char *hintsfile, bool is_le) { struct elfhints_hdr hdr; char *tempname; @@ -267,9 +281,9 @@ 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); @@ -280,8 +294,10 @@ 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.h b/sbin/ldconfig/ldconfig.h --- a/sbin/ldconfig/ldconfig.h +++ b/sbin/ldconfig/ldconfig.h @@ -38,7 +38,7 @@ __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, bool); __END_DECLS #endif diff --git a/sbin/ldconfig/ldconfig.8 b/sbin/ldconfig/ldconfig.8 --- a/sbin/ldconfig/ldconfig.8 +++ b/sbin/ldconfig/ldconfig.8 @@ -105,16 +105,29 @@ .It Fl 32 Generate the hints for 32-bit ABI shared libraries on 64-bit systems that support running 32-bit binaries. +.It Fl 32b +Generate the hints for 32-bit ABI shared libraries +on 64-bit systems that support running 32-bit binaries +for use by a big-endian CPU. +.It Fl 32l +Generate the hints for 32-bit ABI shared libraries +on 64-bit systems that support running 32-bit binaries +for use by a little-endian CPU. +.It Fl 64b +Generate the hints for for use by a big-endian 64-bit CPU. +.It Fl 64l +Generate the hints for for use by a little-endian 64-bit CPU. .It Fl elf Ignored for backwards compatibility. .It Fl R Appends pathnames on the command line to the directory list from the hints file. .Pp -This is the default action when no options are given. +This is the default action when no file or directory arguments are given. .It Fl f Ar hints_file Read and/or update the specified hints file, instead of the standard file. -This option is provided primarily for testing. +This option is provided for testing and to e.g. allow updating a hints +file in a file system prepared for a CPU with different endianness. .It Fl i Run in insecure mode. The security checks will not be performed. diff --git a/sbin/ldconfig/ldconfig.c b/sbin/ldconfig/ldconfig.c --- a/sbin/ldconfig/ldconfig.c +++ b/sbin/ldconfig/ldconfig.c @@ -57,7 +57,7 @@ { const char *hints_file; int c; - bool is_32, justread, merge, rescan, verbose; + bool is_32, justread, merge, rescan, verbose, force_be, force_le; is_32 = justread = merge = rescan = verbose = false; @@ -67,8 +67,16 @@ } else if (strcmp(argv[1], "-elf") == 0) { argc--; argv++; - } else if (strcmp(argv[1], "-32") == 0) { + } else if (strncmp(argv[1], "-32", 3) == 0) { is_32 = true; + force_be = (argv[1][3] == 'b'); + force_le = (argv[1][3] == 'l'); + argc--; + argv++; + } else if (strncmp(argv[1], "-64", 3) == 0) { + is_32 = false; + force_be = (argv[1][3] == 'b'); + force_le = (argv[1][3] == 'l'); argc--; argv++; } else { @@ -115,7 +123,7 @@ if (argc == optind) rescan = true; update_elf_hints(hints_file, argc - optind, - argv + optind, merge || rescan); + argv + optind, merge || rescan, force_be, force_le); } exit(0); } @@ -124,7 +132,7 @@ usage(void) { fprintf(stderr, - "usage: ldconfig [-32] [-elf] [-Rimrv] [-f hints_file] " - "[directory | file ...]\n"); + "usage: ldconfig [-32|-32b|-32l|-64b|-64l] [-elf] [-Rimrv] " + "[-f hints_file] [directory | file ...]\n"); exit(1); }