Skip site navigation (1)Skip section navigation (2)
Date:      Tue, 10 Sep 2013 20:56:01 +0000 (UTC)
From:      Baptiste Daroussin <bapt@FreeBSD.org>
To:        src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-head@freebsd.org
Subject:   svn commit: r255457 - head/usr.sbin/pkg
Message-ID:  <201309102056.r8AKu1rQ000442@svn.freebsd.org>

next in thread | raw e-mail | index | archive | help
Author: bapt
Date: Tue Sep 10 20:56:01 2013
New Revision: 255457
URL: http://svnweb.freebsd.org/changeset/base/255457

Log:
  Add support to detect arm vs armv6
  
  There are two different versions of the ARM ABI depending on the
  TARGET_ARCH. As these are sligntly different a package built for
  one may not work on another. We need to detect which one we are on
  by parsing the .ARM.attributes section.
  
  This will only work on the ARM EABI as this section is part of the
  ABI definition. As armv6 only supports the ARM EABI this is not a
  problem for the oabi.
  
  Older versions of libelf in FreeBSD fail to read the
  .ARM.attributes section needed. As armv6 is unsupported on these
  versions we can assume we are running on arm.
  
  Submitted by:	andrew
  Approved by:	re (delphij)
  Obtained from:	pkgng git

Modified:
  head/usr.sbin/pkg/config.c

Modified: head/usr.sbin/pkg/config.c
==============================================================================
--- head/usr.sbin/pkg/config.c	Tue Sep 10 19:00:32 2013	(r255456)
+++ head/usr.sbin/pkg/config.c	Tue Sep 10 20:56:01 2013	(r255457)
@@ -32,6 +32,7 @@ __FBSDID("$FreeBSD$");
 #include <sys/elf_common.h>
 #include <sys/endian.h>
 
+#include <assert.h>
 #include <bsdyml.h>
 #include <ctype.h>
 #include <err.h>
@@ -100,6 +101,138 @@ elf_corres_to_string(struct _elf_corres 
 	return ("unknown");
 }
 
+static const char *
+aeabi_parse_arm_attributes(void *data, size_t length) 
+{
+	uint32_t sect_len;
+	uint8_t *section = data;
+
+#define	MOVE(len) do {            \
+	assert(length >= (len));  \
+	section += (len);         \
+	length -= (len);          \
+} while (0)
+
+	if (length == 0 || *section != 'A')
+		return (NULL);
+
+	MOVE(1);
+
+	/* Read the section length */
+	if (length < sizeof(sect_len))
+		return (NULL);
+
+	memcpy(&sect_len, section, sizeof(sect_len));
+
+	/*
+	 * The section length should be no longer than the section it is within
+	 */
+	if (sect_len > length)
+		return (NULL);
+
+	MOVE(sizeof(sect_len));
+
+	/* Skip the vendor name */
+	while (length != 0) {
+		if (*section == '\0')
+			break;
+		MOVE(1);
+	}
+	if (length == 0)
+		return (NULL);
+	MOVE(1);
+
+	while (length != 0) {
+		uint32_t tag_length;
+
+	switch(*section) {
+	case 1: /* Tag_File */
+		MOVE(1);
+		if (length < sizeof(tag_length))
+			return (NULL);
+		memcpy(&tag_length, section, sizeof(tag_length));
+		break;
+	case 2: /* Tag_Section */
+	case 3: /* Tag_Symbol */
+	default:
+		return (NULL);
+	}
+	/* At least space for the tag and size */
+	if (tag_length <= 5)
+		return (NULL);
+	tag_length--;
+	/* Check the tag fits */
+	if (tag_length > length)
+		return (NULL);
+
+#define  MOVE_TAG(len) do {           \
+	assert(tag_length >= (len));  \
+	MOVE(len);                    \
+	tag_length -= (len);          \
+} while(0)
+
+		MOVE(sizeof(tag_length));
+		tag_length -= sizeof(tag_length);
+
+		while (tag_length != 0) {
+			uint8_t tag;
+
+			assert(tag_length >= length);
+
+			tag = *section;
+			MOVE_TAG(1);
+
+			/*
+			 * These tag values come from:
+			 * 
+			 * Addenda to, and Errata in, the ABI for the
+			 * ARM Architecture. Release 2.08, section 2.3.
+			 */
+			if (tag == 6) { /* == Tag_CPU_arch */
+				uint8_t val;
+
+				val = *section;
+				/*
+				 * We don't support values that require
+				 * more than one byte.
+				 */
+				if (val & (1 << 7))
+					return (NULL);
+
+				/* We have an ARMv4 or ARMv5 */
+				if (val <= 5)
+					return ("arm");
+				else /* We have an ARMv6+ */
+					return ("armv6");
+			} else if (tag == 4 || tag == 5 || tag == 32 ||
+			    tag == 65 || tag == 67) {
+				while (*section != '\0' && length != 0)
+					MOVE_TAG(1);
+				if (tag_length == 0)
+					return (NULL);
+				/* Skip the last byte */
+				MOVE_TAG(1);
+			} else if ((tag >= 7 && tag <= 31) || tag == 34 ||
+			    tag == 36 || tag == 38 || tag == 42 || tag == 44 ||
+			    tag == 64 || tag == 66 || tag == 68 || tag == 70) {
+				/* Skip the uleb128 data */
+				while (*section & (1 << 7) && length != 0)
+					MOVE_TAG(1);
+				if (tag_length == 0)
+					return (NULL);
+				/* Skip the last byte */
+				MOVE_TAG(1);
+			} else
+				return (NULL);
+#undef MOVE_TAG
+		}
+
+		break;
+	}
+	return (NULL);
+#undef MOVE
+}
+
 static int
 pkg_get_myabi(char *dest, size_t sz)
 {
@@ -108,7 +241,8 @@ pkg_get_myabi(char *dest, size_t sz)
 	Elf_Note note;
 	Elf_Scn *scn;
 	char *src, *osname;
-	const char *abi, *fpu;
+	const char *arch, *abi, *fpu, *endian_corres_str;
+	const char *wordsize_corres_str;
 	GElf_Ehdr elfhdr;
 	GElf_Shdr shdr;
 	int fd, i, ret;
@@ -177,21 +311,72 @@ pkg_get_myabi(char *dest, size_t sz)
 	for (i = 0; osname[i] != '\0'; i++)
 		osname[i] = (char)tolower(osname[i]);
 
-	snprintf(dest, sz, "%s:%d:%s:%s",
-	    osname, version / 100000,
-	    elf_corres_to_string(mach_corres, (int)elfhdr.e_machine),
-	    elf_corres_to_string(wordsize_corres,
-	    (int)elfhdr.e_ident[EI_CLASS]));
+	wordsize_corres_str = elf_corres_to_string(wordsize_corres,
+	    (int)elfhdr.e_ident[EI_CLASS]);
+
+	arch = elf_corres_to_string(mach_corres, (int) elfhdr.e_machine);
+
+	snprintf(dest, sz, "%s:%d",
+	    osname, version / 100000);
 
 	ret = 0;
 
 	switch (elfhdr.e_machine) {
 	case EM_ARM:
+		endian_corres_str = elf_corres_to_string(endian_corres,
+		    (int)elfhdr.e_ident[EI_DATA]);
+
 		/* FreeBSD doesn't support the hard-float ABI yet */
 		fpu = "softfp";
 		if ((elfhdr.e_flags & 0xFF000000) != 0) {
+			const char *sh_name = NULL;
+			size_t shstrndx;
+
 			/* This is an EABI file, the conformance level is set */
 			abi = "eabi";
+			/* Find which TARGET_ARCH we are building for. */
+			elf_getshdrstrndx(elf, &shstrndx);
+			while ((scn = elf_nextscn(elf, scn)) != NULL) {
+				sh_name = NULL;
+				if (gelf_getshdr(scn, &shdr) != &shdr) {
+					scn = NULL;
+					break;
+				}
+
+				sh_name = elf_strptr(elf, shstrndx,
+				    shdr.sh_name);
+				if (sh_name == NULL)
+					continue;
+				if (strcmp(".ARM.attributes", sh_name) == 0)
+					break;
+			}
+			if (scn != NULL && sh_name != NULL) {
+				data = elf_getdata(scn, NULL);
+				/*
+				 * Prior to FreeBSD 10.0 libelf would return
+				 * NULL from elf_getdata on the .ARM.attributes
+				 * section. As this was the first release to
+				 * get armv6 support assume a NULL value means
+				 * arm.
+				 *
+				 * This assumption can be removed when 9.x
+				 * is unsupported.
+				 */
+				if (data != NULL) {
+					arch = aeabi_parse_arm_attributes(
+					    data->d_buf, data->d_size);
+					if (arch == NULL) {
+						ret = 1;
+						warn("unknown ARM ARCH");
+						goto cleanup;
+					}
+				}
+			} else {
+				ret = 1;
+				warn("Unable to find the .ARM.attributes "
+				    "section");
+				goto cleanup;
+			}
 		} else if (elfhdr.e_ident[EI_OSABI] != ELFOSABI_NONE) {
 			/*
 			 * EABI executables all have this field set to
@@ -200,12 +385,12 @@ pkg_get_myabi(char *dest, size_t sz)
 			abi = "oabi";
 		} else {
 			ret = 1;
+			warn("unknown ARM ABI");
 			goto cleanup;
 		}
 		snprintf(dest + strlen(dest), sz - strlen(dest),
-		    ":%s:%s:%s", elf_corres_to_string(endian_corres,
-		    (int)elfhdr.e_ident[EI_DATA]),
-		    abi, fpu);
+		    ":%s:%s:%s:%s:%s", arch, wordsize_corres_str,
+		    endian_corres_str, abi, fpu);
 		break;
 	case EM_MIPS:
 		/*
@@ -230,10 +415,15 @@ pkg_get_myabi(char *dest, size_t sz)
 				abi = "n64";
 			break;
 		}
-		snprintf(dest + strlen(dest), sz - strlen(dest),
-		    ":%s:%s", elf_corres_to_string(endian_corres,
-		    (int)elfhdr.e_ident[EI_DATA]), abi);
+		endian_corres_str = elf_corres_to_string(endian_corres,
+		    (int)elfhdr.e_ident[EI_DATA]);
+
+		snprintf(dest + strlen(dest), sz - strlen(dest), ":%s:%s:%s:%s",
+		    arch, wordsize_corres_str, endian_corres_str, abi);
 		break;
+	default:
+		snprintf(dest + strlen(dest), sz - strlen(dest), ":%s:%s",
+		    arch, wordsize_corres_str);
 	}
 
 cleanup:



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