diff options
-rw-r--r-- | usr.sbin/pkg/config.c | 214 |
1 files changed, 202 insertions, 12 deletions
diff --git a/usr.sbin/pkg/config.c b/usr.sbin/pkg/config.c index 142fd1b..01eaa9f 100644 --- a/usr.sbin/pkg/config.c +++ b/usr.sbin/pkg/config.c @@ -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 *m, int e) 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(§_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: |