The branch main has been updated by jhb:

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

commit 9d4104b214963bb3371ada05cae8006940121634
Author:     John Baldwin <j...@freebsd.org>
AuthorDate: 2021-01-29 18:53:50 +0000
Commit:     John Baldwin <j...@freebsd.org>
CommitDate: 2021-01-29 18:53:50 +0000

    Fix ldd to work with more ELF files.
    
    - Use libelf to parse ELF data structures and remove code duplication
      for ELF32.
    
    - Don't require the OSABI field to be set to the FreeBSD OSABI for
      shared libraries.  Both AArch64 and RISC-V leave it set to "none"
      and instead depend on the ABI tag note.  For ldd, this means falling
      back to walking the notes in PT_NOTE segments to find the ABI tag
      note to determine if an ELF shared library without OSABI set in the
      header file is a FreeBSD shared library.
    
    Reviewed by:    kib
    MFC after:      5 days
    Sponsored by:   DARPA
    Differential Revision:  https://reviews.freebsd.org/D28342
---
 usr.bin/ldd/Makefile |   2 +
 usr.bin/ldd/ldd.c    | 335 ++++++++++++++++++++++++++++-----------------------
 2 files changed, 187 insertions(+), 150 deletions(-)

diff --git a/usr.bin/ldd/Makefile b/usr.bin/ldd/Makefile
index 5d2beb7bb6c0..78e551d80a33 100644
--- a/usr.bin/ldd/Makefile
+++ b/usr.bin/ldd/Makefile
@@ -3,4 +3,6 @@
 PROG?=         ldd
 SRCS=          ldd.c
 
+LIBADD=                elf
+
 .include <bsd.prog.mk>
diff --git a/usr.bin/ldd/ldd.c b/usr.bin/ldd/ldd.c
index 08c91d829bd7..f948b7312863 100644
--- a/usr.bin/ldd/ldd.c
+++ b/usr.bin/ldd/ldd.c
@@ -33,6 +33,7 @@
 #include <sys/cdefs.h>
 __FBSDID("$FreeBSD$");
 
+#include <sys/param.h>
 #include <sys/wait.h>
 
 #include <machine/elf.h>
@@ -43,6 +44,9 @@ __FBSDID("$FreeBSD$");
 #include <err.h>
 #include <errno.h>
 #include <fcntl.h>
+#include <gelf.h>
+#include <libelf.h>
+#include <stdbool.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
@@ -259,179 +263,210 @@ usage(void)
        exit(1);
 }
 
-static int
-is_executable(const char *fname, int fd, int *is_shlib, int *type)
+static bool
+has_freebsd_abi_tag(const char *fname, Elf *elf, GElf_Ehdr *ehdr, off_t offset,
+    size_t len)
 {
-       union {
-#if __ELF_WORD_SIZE > 32 && defined(ELF32_SUPPORTED)
-               Elf32_Ehdr elf32;
-#endif
-               Elf_Ehdr elf;
-       } hdr;
-       Elf_Phdr phdr, dynphdr;
-       Elf_Dyn *dynp;
-       void *dyndata;
-#if __ELF_WORD_SIZE > 32 && defined(ELF32_SUPPORTED)
-       Elf32_Phdr phdr32, dynphdr32;
-       Elf32_Dyn *dynp32;
-#endif
-       int df1pie, dynamic, i, n;
-
-       *is_shlib = 0;
-       *type = TYPE_UNKNOWN;
-       df1pie = 0;
+       Elf_Data dst, src;
+       const Elf_Note *note;
+       char *buf;
+       const char *name;
+       void *copy;
+       size_t namesz, descsz;
+       bool has_abi_tag;
+
+       buf = elf_rawfile(elf, NULL);
+       if (buf == NULL) {
+               warnx("%s: %s", fname, elf_errmsg(0));
+               return (false);
+       }
 
-       if ((n = read(fd, &hdr, sizeof(hdr))) == -1) {
-               warn("%s: can't read program header", fname);
-               return (0);
+       memset(&src, 0, sizeof(src));
+       src.d_buf = buf + offset;
+       src.d_size = len;
+       src.d_type = ELF_T_NOTE;
+       src.d_version = EV_CURRENT;
+
+       memset(&dst, 0, sizeof(dst));
+       dst.d_buf = copy = malloc(len);
+       dst.d_size = len;
+       dst.d_type = ELF_T_NOTE;
+       dst.d_version = EV_CURRENT;
+
+       if (gelf_xlatetom(elf, &dst, &src, ehdr->e_ident[EI_DATA]) == NULL) {
+               warnx("%s: failed to parse notes: %s", fname, elf_errmsg(0));
+               free(copy);
+               return (false);
        }
 
-#if __ELF_WORD_SIZE > 32 && defined(ELF32_SUPPORTED)
-       if ((size_t)n >= sizeof(hdr.elf32) && IS_ELF(hdr.elf32) &&
-           hdr.elf32.e_ident[EI_CLASS] == ELFCLASS32) {
-               /* Handle 32 bit ELF objects */
+       buf = copy;
+       has_abi_tag = false;
+       for (;;) {
+               if (len < sizeof(*note))
+                       break;
 
-               dynamic = 0;
-               *type = TYPE_ELF32;
+               note = (const void *)buf;
+               buf += sizeof(*note);
+               len -= sizeof(*note);
 
-               if (lseek(fd, hdr.elf32.e_phoff, SEEK_SET) == -1) {
-                       warnx("%s: header too short", fname);
-                       return (0);
-               }
-               for (i = 0; i < hdr.elf32.e_phnum; i++) {
-                       if (read(fd, &phdr32, hdr.elf32.e_phentsize) !=
-                           sizeof(phdr32)) {
-                               warnx("%s: can't read program header", fname);
-                               return (0);
-                       }
-                       if (phdr32.p_type == PT_DYNAMIC) {
-                               dynamic = 1;
-                               dynphdr32 = phdr32;
-                               break;
-                       }
-               }
+               namesz = roundup2(note->n_namesz, sizeof(uint32_t));
+               descsz = roundup2(note->n_descsz, sizeof(uint32_t));
+               if (len < namesz + descsz)
+                       break;
 
-               if (!dynamic) {
-                       warnx("%s: not a dynamic ELF executable", fname);
-                       return (0);
+               name = buf;
+               if (note->n_namesz == sizeof(ELF_NOTE_FREEBSD) &&
+                   strncmp(name, ELF_NOTE_FREEBSD, note->n_namesz) == 0 &&
+                   note->n_type == NT_FREEBSD_ABI_TAG &&
+                   note->n_descsz == sizeof(uint32_t)) {
+                       has_abi_tag = true;
+                       break;
                }
 
-               if (hdr.elf32.e_type == ET_DYN) {
-                       if (lseek(fd, dynphdr32.p_offset, SEEK_SET) == -1) {
-                               warnx("%s: dynamic segment out of range",
-                                   fname);
-                               return (0);
-                       }
-                       dyndata = malloc(dynphdr32.p_filesz);
-                       if (dyndata == NULL) {
-                               warn("malloc");
-                               return (0);
-                       }
-                       if (read(fd, dyndata, dynphdr32.p_filesz) !=
-                           (ssize_t)dynphdr32.p_filesz) {
-                               free(dyndata);
-                               warnx("%s: can't read dynamic segment", fname);
-                               return (0);
-                       }
-                       for (dynp32 = dyndata; dynp32->d_tag != DT_NULL;
-                           dynp32++) {
-                               if (dynp32->d_tag != DT_FLAGS_1)
-                                       continue;
-                               df1pie = (dynp32->d_un.d_val & DF_1_PIE) != 0;
-                               break;
-                       }
-                       free(dyndata);
+               buf += namesz + descsz;
+               len -= namesz + descsz;
+       }
 
-                       if (hdr.elf32.e_ident[EI_OSABI] == ELFOSABI_FREEBSD) {
-                               if (!df1pie)
-                                       *is_shlib = 1;
-                               return (1);
-                       }
-                       warnx("%s: not a FreeBSD ELF shared object", fname);
-                       return (0);
-               }
+       free(copy);
+       return (has_abi_tag);
+}
 
-               return (1);
+static bool
+is_pie(const char *fname, Elf *elf, GElf_Ehdr *ehdr, off_t offset, size_t len)
+{
+       Elf_Data dst, src;
+       char *buf;
+       void *copy;
+       const GElf_Dyn *dyn;
+       size_t dynsize;
+       u_int count, i;
+       bool pie;
+
+       buf = elf_rawfile(elf, NULL);
+       if (buf == NULL) {
+               warnx("%s: %s", fname, elf_errmsg(0));
+               return (false);
        }
-#endif
 
-       if ((size_t)n >= sizeof(hdr.elf) && IS_ELF(hdr.elf) &&
-           hdr.elf.e_ident[EI_CLASS] == ELF_TARG_CLASS) {
-               /* Handle default ELF objects on this architecture */
+       dynsize = gelf_fsize(elf, ELF_T_DYN, 1, EV_CURRENT);
+       if (dynsize == 0) {
+               warnx("%s: %s", fname, elf_errmsg(0));
+               return (false);
+       }
+       count = len / dynsize;
+
+       memset(&src, 0, sizeof(src));
+       src.d_buf = buf + offset;
+       src.d_size = len;
+       src.d_type = ELF_T_DYN;
+       src.d_version = EV_CURRENT;
+
+       memset(&dst, 0, sizeof(dst));
+       dst.d_buf = copy = malloc(count * sizeof(*dyn));
+       dst.d_size = count * sizeof(*dyn);
+       dst.d_type = ELF_T_DYN;
+       dst.d_version = EV_CURRENT;
+
+       if (gelf_xlatetom(elf, &dst, &src, ehdr->e_ident[EI_DATA]) == NULL) {
+               warnx("%s: failed to parse .dynamic: %s", fname, elf_errmsg(0));
+               free(copy);
+               return (false);
+       }
+
+       dyn = copy;
+       pie = false;
+       for (i = 0; i < count; i++) {
+               if (dyn[i].d_tag != DT_FLAGS_1)
+                       continue;
+
+               pie = (dyn[i].d_un.d_val & DF_1_PIE) != 0;
+               break;
+       }
+
+       free(copy);
+       return (pie);
+}
+
+static int
+is_executable(const char *fname, int fd, int *is_shlib, int *type)
+{
+       Elf *elf;
+       GElf_Ehdr ehdr;
+       GElf_Phdr phdr;
+       bool dynamic, freebsd, pie;
+       int i;
+
+       *is_shlib = 0;
+       *type = TYPE_UNKNOWN;
+       dynamic = false;
+       freebsd = false;
+       pie = false;
+
+       if (elf_version(EV_CURRENT) == EV_NONE) {
+               warnx("unsupported libelf");
+               return (0);
+       }
+       elf = elf_begin(fd, ELF_C_READ, NULL);
+       if (elf == NULL) {
+               warnx("%s: %s", fname, elf_errmsg(0));
+               return (0);
+       }
+       if (elf_kind(elf) != ELF_K_ELF) {
+               elf_end(elf);
+               warnx("%s: not a dynamic ELF executable", fname);
+               return (0);
+       }
+       if (gelf_getehdr(elf, &ehdr) == NULL) {
+               warnx("%s: %s", fname, elf_errmsg(0));
+               elf_end(elf);
+               return (0);
+       }
 
-               dynamic = 0;
-               *type = TYPE_ELF;
+       *type = TYPE_ELF;
+#if __ELF_WORD_SIZE > 32 && defined(ELF32_SUPPORTED)
+       if (gelf_getclass(elf) == ELFCLASS32) {
+               *type = TYPE_ELF32;
+       }
+#endif
 
-               if (lseek(fd, hdr.elf.e_phoff, SEEK_SET) == -1) {
-                       warnx("%s: header too short", fname);
+       freebsd = ehdr.e_ident[EI_OSABI] == ELFOSABI_FREEBSD;
+       for (i = 0; i < ehdr.e_phnum; i++) {
+               if (gelf_getphdr(elf, i, &phdr) == NULL) {
+                       warnx("%s: %s", fname, elf_errmsg(0));
+                       elf_end(elf);
                        return (0);
                }
-               for (i = 0; i < hdr.elf.e_phnum; i++) {
-                       if (read(fd, &phdr, hdr.elf.e_phentsize)
-                          != sizeof(phdr)) {
-                               warnx("%s: can't read program header", fname);
-                               return (0);
-                       }
-                       if (phdr.p_type == PT_DYNAMIC) {
-                               dynamic = 1;
-                               dynphdr = phdr;
-                               break;
-                       }
+               switch (phdr.p_type) {
+               case PT_NOTE:
+                       if (ehdr.e_ident[EI_OSABI] == ELFOSABI_NONE && !freebsd)
+                               freebsd = has_freebsd_abi_tag(fname, elf, &ehdr,
+                                   phdr.p_offset, phdr.p_filesz);
+                       break;
+               case PT_DYNAMIC:
+                       dynamic = true;
+                       if (ehdr.e_type == ET_DYN)
+                               pie = is_pie(fname, elf, &ehdr, phdr.p_offset,
+                                   phdr.p_filesz);
+                       break;
                }
+       }
 
-               if (!dynamic) {
-                       warnx("%s: not a dynamic ELF executable", fname);
-                       return (0);
-               }
+       if (!dynamic) {
+               elf_end(elf);
+               warnx("%s: not a dynamic ELF executable", fname);
+               return (0);
+       }
 
-               if (hdr.elf.e_type == ET_DYN) {
-                       if (lseek(fd, dynphdr.p_offset, SEEK_SET) == -1) {
-                               warnx("%s: dynamic segment out of range",
-                                   fname);
-                               return (0);
-                       }
-                       dyndata = malloc(dynphdr.p_filesz);
-                       if (dyndata == NULL) {
-                               warn("malloc");
-                               return (0);
-                       }
-                       if (read(fd, dyndata, dynphdr.p_filesz) !=
-                           (ssize_t)dynphdr.p_filesz) {
-                               free(dyndata);
-                               warnx("%s: can't read dynamic segment", fname);
-                               return (0);
-                       }
-                       for (dynp = dyndata; dynp->d_tag != DT_NULL; dynp++) {
-                               if (dynp->d_tag != DT_FLAGS_1)
-                                       continue;
-                               df1pie = (dynp->d_un.d_val & DF_1_PIE) != 0;
-                               break;
-                       }
-                       free(dyndata);
-
-                       switch (hdr.elf.e_ident[EI_OSABI]) {
-                       case ELFOSABI_FREEBSD:
-                               if (!df1pie)
-                                       *is_shlib = 1;
-                               return (1);
-#ifdef __ARM_EABI__
-                       case ELFOSABI_NONE:
-                               if (hdr.elf.e_machine != EM_ARM)
-                                       break;
-                               if (EF_ARM_EABI_VERSION(hdr.elf.e_flags) <
-                                   EF_ARM_EABI_FREEBSD_MIN)
-                                       break;
-                               *is_shlib = 1;
-                               return (1);
-#endif
-                       }
+       if (ehdr.e_type == ET_DYN && !pie) {
+               *is_shlib = 1;
+
+               if (!freebsd) {
+                       elf_end(elf);
                        warnx("%s: not a FreeBSD ELF shared object", fname);
                        return (0);
                }
-
-               return (1);
        }
 
-       warnx("%s: not a dynamic executable", fname);
-       return (0);
+       elf_end(elf);
+       return (1);
 }
_______________________________________________
dev-commits-src-main@freebsd.org mailing list
https://lists.freebsd.org/mailman/listinfo/dev-commits-src-main
To unsubscribe, send any mail to "dev-commits-src-main-unsubscr...@freebsd.org"

Reply via email to