Skip site navigation (1)Skip section navigation (2)
Date:      Fri, 5 Feb 2021 00:04:31 GMT
From:      John Baldwin <jhb@FreeBSD.org>
To:        src-committers@FreeBSD.org, dev-commits-src-all@FreeBSD.org, dev-commits-src-branches@FreeBSD.org
Subject:   git: fa3b2ad265d3 - stable/13 - Fix ldd to work with more ELF files.
Message-ID:  <202102050004.11504VeU026815@gitrepo.freebsd.org>

next in thread | raw e-mail | index | archive | help
The branch stable/13 has been updated by jhb:

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

commit fa3b2ad265d311191c1c8337bf3aa356f28480cb
Author:     John Baldwin <jhb@FreeBSD.org>
AuthorDate: 2021-01-29 18:53:50 +0000
Commit:     John Baldwin <jhb@FreeBSD.org>
CommitDate: 2021-02-05 00:03:20 +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.
    
    (cherry picked from commit 9d4104b214963bb3371ada05cae8006940121634)
---
 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);
 }



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?202102050004.11504VeU026815>