Index: head/contrib/binutils/binutils/readelf.c =================================================================== --- head/contrib/binutils/binutils/readelf.c (revision 274816) +++ head/contrib/binutils/binutils/readelf.c (revision 274817) @@ -1,9996 +1,9998 @@ /* readelf.c -- display contents of an ELF format file Copyright 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007 Free Software Foundation, Inc. Originally developed by Eric Youngdale Modifications by Nick Clifton This file is part of GNU Binutils. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA 02110-1301, USA. */ /* The difference between readelf and objdump: Both programs are capable of displaying the contents of ELF format files, so why does the binutils project have two file dumpers ? The reason is that objdump sees an ELF file through a BFD filter of the world; if BFD has a bug where, say, it disagrees about a machine constant in e_flags, then the odds are good that it will remain internally consistent. The linker sees it the BFD way, objdump sees it the BFD way, GAS sees it the BFD way. There was need for a tool to go find out what the file actually says. This is why the readelf program does not link against the BFD library - it exists as an independent program to help verify the correct working of BFD. There is also the case that readelf can provide more information about an ELF file than is provided by objdump. In particular it can display DWARF debugging information which (at the moment) objdump cannot. */ #include "sysdep.h" #include #include #include /* for PATH_MAX */ #ifdef HAVE_LIMITS_H #include #endif #ifndef PATH_MAX /* for MAXPATHLEN */ # ifdef HAVE_SYS_PARAM_H # include # endif # ifndef PATH_MAX # ifdef MAXPATHLEN # define PATH_MAX MAXPATHLEN # else # define PATH_MAX 1024 # endif # endif #endif #if __GNUC__ >= 2 /* Define BFD64 here, even if our default architecture is 32 bit ELF as this will allow us to read in and parse 64bit and 32bit ELF files. Only do this if we believe that the compiler can support a 64 bit data type. For now we only rely on GCC being able to do this. */ #define BFD64 #endif #include "bfd.h" #include "bucomm.h" #include "dwarf.h" #include "elf/common.h" #include "elf/external.h" #include "elf/internal.h" /* Included here, before RELOC_MACROS_GEN_FUNC is defined, so that we can obtain the H8 reloc numbers. We need these for the get_reloc_size() function. We include h8.h again after defining RELOC_MACROS_GEN_FUNC so that we get the naming function as well. */ #include "elf/h8.h" #undef _ELF_H8_H /* Undo the effects of #including reloc-macros.h. */ #undef START_RELOC_NUMBERS #undef RELOC_NUMBER #undef FAKE_RELOC #undef EMPTY_RELOC #undef END_RELOC_NUMBERS #undef _RELOC_MACROS_H /* The following headers use the elf/reloc-macros.h file to automatically generate relocation recognition functions such as elf_mips_reloc_type() */ #define RELOC_MACROS_GEN_FUNC #include "elf/alpha.h" #include "elf/arc.h" #include "elf/arm.h" #include "elf/avr.h" #include "elf/bfin.h" #include "elf/cris.h" #include "elf/crx.h" #include "elf/d10v.h" #include "elf/d30v.h" #include "elf/dlx.h" #include "elf/fr30.h" #include "elf/frv.h" #include "elf/h8.h" #include "elf/hppa.h" #include "elf/i386.h" #include "elf/i370.h" #include "elf/i860.h" #include "elf/i960.h" #include "elf/ia64.h" #include "elf/ip2k.h" #include "elf/iq2000.h" #include "elf/m32c.h" #include "elf/m32r.h" #include "elf/m68k.h" #include "elf/m68hc11.h" #include "elf/mcore.h" #include "elf/mep.h" #include "elf/mips.h" #include "elf/mmix.h" #include "elf/mn10200.h" #include "elf/mn10300.h" #include "elf/mt.h" #include "elf/msp430.h" #include "elf/or32.h" #include "elf/pj.h" #include "elf/ppc.h" #include "elf/ppc64.h" #include "elf/s390.h" #include "elf/score.h" #include "elf/sh.h" #include "elf/sparc.h" #include "elf/spu.h" #include "elf/v850.h" #include "elf/vax.h" #include "elf/x86-64.h" #include "elf/xstormy16.h" #include "elf/xtensa.h" #include "aout/ar.h" #include "getopt.h" #include "libiberty.h" char *program_name = "readelf"; static long archive_file_offset; static unsigned long archive_file_size; static unsigned long dynamic_addr; static bfd_size_type dynamic_size; static unsigned int dynamic_nent; static char *dynamic_strings; static unsigned long dynamic_strings_length; static char *string_table; static unsigned long string_table_length; static unsigned long num_dynamic_syms; static Elf_Internal_Sym *dynamic_symbols; static Elf_Internal_Syminfo *dynamic_syminfo; static unsigned long dynamic_syminfo_offset; static unsigned int dynamic_syminfo_nent; static char program_interpreter[PATH_MAX]; static bfd_vma dynamic_info[DT_ENCODING]; static bfd_vma dynamic_info_DT_GNU_HASH; static bfd_vma version_info[16]; static Elf_Internal_Ehdr elf_header; static Elf_Internal_Shdr *section_headers; static Elf_Internal_Phdr *program_headers; static Elf_Internal_Dyn *dynamic_section; static Elf_Internal_Shdr *symtab_shndx_hdr; static int show_name; static int do_dynamic; static int do_syms; static int do_reloc; static int do_sections; static int do_section_groups; static int do_section_details; static int do_segments; static int do_unwind; static int do_using_dynamic; static int do_header; static int do_dump; static int do_version; static int do_wide; static int do_histogram; static int do_debugging; static int do_arch; static int do_notes; static int is_32bit_elf; struct group_list { struct group_list *next; unsigned int section_index; }; struct group { struct group_list *root; unsigned int group_index; }; static size_t group_count; static struct group *section_groups; static struct group **section_headers_groups; /* A linked list of the section names for which dumps were requested by name. */ struct dump_list_entry { char *name; int type; struct dump_list_entry *next; }; static struct dump_list_entry *dump_sects_byname; /* A dynamic array of flags indicating for which sections a hex dump has been requested (via the -x switch) and/or a disassembly dump (via the -i switch). */ char *cmdline_dump_sects = NULL; unsigned num_cmdline_dump_sects = 0; /* A dynamic array of flags indicating for which sections a dump of some kind has been requested. It is reset on a per-object file basis and then initialised from the cmdline_dump_sects array, the results of interpreting the -w switch, and the dump_sects_byname list. */ char *dump_sects = NULL; unsigned int num_dump_sects = 0; #define HEX_DUMP (1 << 0) #define DISASS_DUMP (1 << 1) #define DEBUG_DUMP (1 << 2) /* How to print a vma value. */ typedef enum print_mode { HEX, DEC, DEC_5, UNSIGNED, PREFIX_HEX, FULL_HEX, LONG_HEX } print_mode; static void (*byte_put) (unsigned char *, bfd_vma, int); #define UNKNOWN -1 #define SECTION_NAME(X) \ ((X) == NULL ? "" \ : string_table == NULL ? "" \ : ((X)->sh_name >= string_table_length ? "" \ : string_table + (X)->sh_name)) /* Given st_shndx I, map to section_headers index. */ #define SECTION_HEADER_INDEX(I) \ ((I) < SHN_LORESERVE \ ? (I) \ : ((I) <= SHN_HIRESERVE \ ? 0 \ : (I) - (SHN_HIRESERVE + 1 - SHN_LORESERVE))) /* Reverse of the above. */ #define SECTION_HEADER_NUM(N) \ ((N) < SHN_LORESERVE \ ? (N) \ : (N) + (SHN_HIRESERVE + 1 - SHN_LORESERVE)) #define SECTION_HEADER(I) (section_headers + SECTION_HEADER_INDEX (I)) #define DT_VERSIONTAGIDX(tag) (DT_VERNEEDNUM - (tag)) /* Reverse order! */ #define BYTE_GET(field) byte_get (field, sizeof (field)) #define NUM_ELEM(array) (sizeof (array) / sizeof ((array)[0])) #define GET_ELF_SYMBOLS(file, section) \ (is_32bit_elf ? get_32bit_elf_symbols (file, section) \ : get_64bit_elf_symbols (file, section)) #define VALID_DYNAMIC_NAME(offset) ((dynamic_strings != NULL) && (offset < dynamic_strings_length)) /* GET_DYNAMIC_NAME asssumes that VALID_DYNAMIC_NAME has already been called and verified that the string exists. */ #define GET_DYNAMIC_NAME(offset) (dynamic_strings + offset) /* This is just a bit of syntatic sugar. */ #define streq(a,b) (strcmp ((a), (b)) == 0) #define strneq(a,b,n) (strncmp ((a), (b), (n)) == 0) #define const_strneq(a,b) (strncmp ((a), (b), sizeof (b) - 1) == 0) static void * get_data (void *var, FILE *file, long offset, size_t size, size_t nmemb, const char *reason) { void *mvar; if (size == 0 || nmemb == 0) return NULL; if (fseek (file, archive_file_offset + offset, SEEK_SET)) { error (_("Unable to seek to 0x%lx for %s\n"), archive_file_offset + offset, reason); return NULL; } mvar = var; if (mvar == NULL) { /* Check for overflow. */ if (nmemb < (~(size_t) 0 - 1) / size) /* + 1 so that we can '\0' terminate invalid string table sections. */ mvar = malloc (size * nmemb + 1); if (mvar == NULL) { error (_("Out of memory allocating 0x%lx bytes for %s\n"), (unsigned long)(size * nmemb), reason); return NULL; } ((char *) mvar)[size * nmemb] = '\0'; } if (fread (mvar, size, nmemb, file) != nmemb) { error (_("Unable to read in 0x%lx bytes of %s\n"), (unsigned long)(size * nmemb), reason); if (mvar != var) free (mvar); return NULL; } return mvar; } static void byte_put_little_endian (unsigned char *field, bfd_vma value, int size) { switch (size) { case 8: field[7] = (((value >> 24) >> 24) >> 8) & 0xff; field[6] = ((value >> 24) >> 24) & 0xff; field[5] = ((value >> 24) >> 16) & 0xff; field[4] = ((value >> 24) >> 8) & 0xff; /* Fall through. */ case 4: field[3] = (value >> 24) & 0xff; field[2] = (value >> 16) & 0xff; /* Fall through. */ case 2: field[1] = (value >> 8) & 0xff; /* Fall through. */ case 1: field[0] = value & 0xff; break; default: error (_("Unhandled data length: %d\n"), size); abort (); } } #if defined BFD64 && !BFD_HOST_64BIT_LONG static int print_dec_vma (bfd_vma vma, int is_signed) { char buf[40]; char *bufp = buf; int nc = 0; if (is_signed && (bfd_signed_vma) vma < 0) { vma = -vma; putchar ('-'); nc = 1; } do { *bufp++ = '0' + vma % 10; vma /= 10; } while (vma != 0); nc += bufp - buf; while (bufp > buf) putchar (*--bufp); return nc; } static int print_hex_vma (bfd_vma vma) { char buf[32]; char *bufp = buf; int nc; do { char digit = '0' + (vma & 0x0f); if (digit > '9') digit += 'a' - '0' - 10; *bufp++ = digit; vma >>= 4; } while (vma != 0); nc = bufp - buf; while (bufp > buf) putchar (*--bufp); return nc; } #endif /* Print a VMA value. */ static int print_vma (bfd_vma vma, print_mode mode) { #ifdef BFD64 if (is_32bit_elf) #endif { switch (mode) { case FULL_HEX: return printf ("0x%8.8lx", (unsigned long) vma); case LONG_HEX: return printf ("%8.8lx", (unsigned long) vma); case DEC_5: if (vma <= 99999) return printf ("%5ld", (long) vma); /* Drop through. */ case PREFIX_HEX: return printf ("0x%lx", (unsigned long) vma); case HEX: return printf ("%lx", (unsigned long) vma); case DEC: return printf ("%ld", (unsigned long) vma); case UNSIGNED: return printf ("%lu", (unsigned long) vma); } } #ifdef BFD64 else { int nc = 0; switch (mode) { case FULL_HEX: nc = printf ("0x"); /* Drop through. */ case LONG_HEX: printf_vma (vma); return nc + 16; case PREFIX_HEX: nc = printf ("0x"); /* Drop through. */ case HEX: #if BFD_HOST_64BIT_LONG return nc + printf ("%lx", vma); #else return nc + print_hex_vma (vma); #endif case DEC: #if BFD_HOST_64BIT_LONG return printf ("%ld", vma); #else return print_dec_vma (vma, 1); #endif case DEC_5: #if BFD_HOST_64BIT_LONG if (vma <= 99999) return printf ("%5ld", vma); else return printf ("%#lx", vma); #else if (vma <= 99999) return printf ("%5ld", _bfd_int64_low (vma)); else return print_hex_vma (vma); #endif case UNSIGNED: #if BFD_HOST_64BIT_LONG return printf ("%lu", vma); #else return print_dec_vma (vma, 0); #endif } } #endif return 0; } /* Display a symbol on stdout. If do_wide is not true then format the symbol to be at most WIDTH characters, truncating as necessary. If WIDTH is negative then format the string to be exactly - WIDTH characters, truncating or padding as necessary. */ static void print_symbol (int width, const char *symbol) { if (do_wide) printf ("%s", symbol); else if (width < 0) printf ("%-*.*s", width, width, symbol); else printf ("%-.*s", width, symbol); } static void byte_put_big_endian (unsigned char *field, bfd_vma value, int size) { switch (size) { case 8: field[7] = value & 0xff; field[6] = (value >> 8) & 0xff; field[5] = (value >> 16) & 0xff; field[4] = (value >> 24) & 0xff; value >>= 16; value >>= 16; /* Fall through. */ case 4: field[3] = value & 0xff; field[2] = (value >> 8) & 0xff; value >>= 16; /* Fall through. */ case 2: field[1] = value & 0xff; value >>= 8; /* Fall through. */ case 1: field[0] = value & 0xff; break; default: error (_("Unhandled data length: %d\n"), size); abort (); } } /* Return a pointer to section NAME, or NULL if no such section exists. */ static Elf_Internal_Shdr * find_section (const char *name) { unsigned int i; for (i = 0; i < elf_header.e_shnum; i++) if (streq (SECTION_NAME (section_headers + i), name)) return section_headers + i; return NULL; } /* Guess the relocation size commonly used by the specific machines. */ static int guess_is_rela (unsigned long e_machine) { switch (e_machine) { /* Targets that use REL relocations. */ case EM_386: case EM_486: case EM_960: case EM_ARM: case EM_D10V: case EM_CYGNUS_D10V: case EM_DLX: case EM_MIPS: case EM_MIPS_RS3_LE: case EM_CYGNUS_M32R: case EM_OPENRISC: case EM_OR32: case EM_SCORE: return FALSE; /* Targets that use RELA relocations. */ case EM_68K: case EM_860: case EM_ALPHA: case EM_ALTERA_NIOS2: case EM_AVR: case EM_AVR_OLD: case EM_BLACKFIN: case EM_CRIS: case EM_CRX: case EM_D30V: case EM_CYGNUS_D30V: case EM_FR30: case EM_CYGNUS_FR30: case EM_CYGNUS_FRV: case EM_H8S: case EM_H8_300: case EM_H8_300H: case EM_IA_64: case EM_IP2K: case EM_IP2K_OLD: case EM_IQ2000: case EM_M32C: case EM_M32R: case EM_MCORE: case EM_CYGNUS_MEP: case EM_MMIX: case EM_MN10200: case EM_CYGNUS_MN10200: case EM_MN10300: case EM_CYGNUS_MN10300: case EM_MSP430: case EM_MSP430_OLD: case EM_MT: case EM_NIOS32: case EM_PPC64: case EM_PPC: case EM_S390: case EM_S390_OLD: case EM_SH: case EM_SPARC: case EM_SPARC32PLUS: case EM_SPARCV9: case EM_SPU: case EM_V850: case EM_CYGNUS_V850: case EM_VAX: case EM_X86_64: case EM_XSTORMY16: case EM_XTENSA: case EM_XTENSA_OLD: return TRUE; case EM_68HC05: case EM_68HC08: case EM_68HC11: case EM_68HC16: case EM_FX66: case EM_ME16: case EM_MMA: case EM_NCPU: case EM_NDR1: case EM_PCP: case EM_ST100: case EM_ST19: case EM_ST7: case EM_ST9PLUS: case EM_STARCORE: case EM_SVX: case EM_TINYJ: default: warn (_("Don't know about relocations on this machine architecture\n")); return FALSE; } } static int slurp_rela_relocs (FILE *file, unsigned long rel_offset, unsigned long rel_size, Elf_Internal_Rela **relasp, unsigned long *nrelasp) { Elf_Internal_Rela *relas; unsigned long nrelas; unsigned int i; if (is_32bit_elf) { Elf32_External_Rela *erelas; erelas = get_data (NULL, file, rel_offset, 1, rel_size, _("relocs")); if (!erelas) return 0; nrelas = rel_size / sizeof (Elf32_External_Rela); relas = cmalloc (nrelas, sizeof (Elf_Internal_Rela)); if (relas == NULL) { free (erelas); error (_("out of memory parsing relocs\n")); return 0; } for (i = 0; i < nrelas; i++) { relas[i].r_offset = BYTE_GET (erelas[i].r_offset); relas[i].r_info = BYTE_GET (erelas[i].r_info); relas[i].r_addend = BYTE_GET (erelas[i].r_addend); } free (erelas); } else { Elf64_External_Rela *erelas; erelas = get_data (NULL, file, rel_offset, 1, rel_size, _("relocs")); if (!erelas) return 0; nrelas = rel_size / sizeof (Elf64_External_Rela); relas = cmalloc (nrelas, sizeof (Elf_Internal_Rela)); if (relas == NULL) { free (erelas); error (_("out of memory parsing relocs\n")); return 0; } for (i = 0; i < nrelas; i++) { relas[i].r_offset = BYTE_GET (erelas[i].r_offset); relas[i].r_info = BYTE_GET (erelas[i].r_info); relas[i].r_addend = BYTE_GET (erelas[i].r_addend); } free (erelas); } *relasp = relas; *nrelasp = nrelas; return 1; } static int slurp_rel_relocs (FILE *file, unsigned long rel_offset, unsigned long rel_size, Elf_Internal_Rela **relsp, unsigned long *nrelsp) { Elf_Internal_Rela *rels; unsigned long nrels; unsigned int i; if (is_32bit_elf) { Elf32_External_Rel *erels; erels = get_data (NULL, file, rel_offset, 1, rel_size, _("relocs")); if (!erels) return 0; nrels = rel_size / sizeof (Elf32_External_Rel); rels = cmalloc (nrels, sizeof (Elf_Internal_Rela)); if (rels == NULL) { free (erels); error (_("out of memory parsing relocs\n")); return 0; } for (i = 0; i < nrels; i++) { rels[i].r_offset = BYTE_GET (erels[i].r_offset); rels[i].r_info = BYTE_GET (erels[i].r_info); rels[i].r_addend = 0; } free (erels); } else { Elf64_External_Rel *erels; erels = get_data (NULL, file, rel_offset, 1, rel_size, _("relocs")); if (!erels) return 0; nrels = rel_size / sizeof (Elf64_External_Rel); rels = cmalloc (nrels, sizeof (Elf_Internal_Rela)); if (rels == NULL) { free (erels); error (_("out of memory parsing relocs\n")); return 0; } for (i = 0; i < nrels; i++) { rels[i].r_offset = BYTE_GET (erels[i].r_offset); rels[i].r_info = BYTE_GET (erels[i].r_info); rels[i].r_addend = 0; } free (erels); } *relsp = rels; *nrelsp = nrels; return 1; } /* Display the contents of the relocation data found at the specified offset. */ static int dump_relocations (FILE *file, unsigned long rel_offset, unsigned long rel_size, Elf_Internal_Sym *symtab, unsigned long nsyms, char *strtab, unsigned long strtablen, int is_rela) { unsigned int i; Elf_Internal_Rela *rels; if (is_rela == UNKNOWN) is_rela = guess_is_rela (elf_header.e_machine); if (is_rela) { if (!slurp_rela_relocs (file, rel_offset, rel_size, &rels, &rel_size)) return 0; } else { if (!slurp_rel_relocs (file, rel_offset, rel_size, &rels, &rel_size)) return 0; } if (is_32bit_elf) { if (is_rela) { if (do_wide) printf (_(" Offset Info Type Sym. Value Symbol's Name + Addend\n")); else printf (_(" Offset Info Type Sym.Value Sym. Name + Addend\n")); } else { if (do_wide) printf (_(" Offset Info Type Sym. Value Symbol's Name\n")); else printf (_(" Offset Info Type Sym.Value Sym. Name\n")); } } else { if (is_rela) { if (do_wide) printf (_(" Offset Info Type Symbol's Value Symbol's Name + Addend\n")); else printf (_(" Offset Info Type Sym. Value Sym. Name + Addend\n")); } else { if (do_wide) printf (_(" Offset Info Type Symbol's Value Symbol's Name\n")); else printf (_(" Offset Info Type Sym. Value Sym. Name\n")); } } for (i = 0; i < rel_size; i++) { const char *rtype; const char *rtype2 = NULL; const char *rtype3 = NULL; bfd_vma offset; bfd_vma info; bfd_vma symtab_index; bfd_vma type; bfd_vma type2 = 0; bfd_vma type3 = 0; offset = rels[i].r_offset; info = rels[i].r_info; if (is_32bit_elf) { type = ELF32_R_TYPE (info); symtab_index = ELF32_R_SYM (info); } else { /* The #ifdef BFD64 below is to prevent a compile time warning. We know that if we do not have a 64 bit data type that we will never execute this code anyway. */ #ifdef BFD64 if (elf_header.e_machine == EM_MIPS) { /* In little-endian objects, r_info isn't really a 64-bit little-endian value: it has a 32-bit little-endian symbol index followed by four individual byte fields. Reorder INFO accordingly. */ if (elf_header.e_ident[EI_DATA] != ELFDATA2MSB) info = (((info & 0xffffffff) << 32) | ((info >> 56) & 0xff) | ((info >> 40) & 0xff00) | ((info >> 24) & 0xff0000) | ((info >> 8) & 0xff000000)); type = ELF64_MIPS_R_TYPE (info); type2 = ELF64_MIPS_R_TYPE2 (info); type3 = ELF64_MIPS_R_TYPE3 (info); } else if (elf_header.e_machine == EM_SPARCV9) type = ELF64_R_TYPE_ID (info); else type = ELF64_R_TYPE (info); symtab_index = ELF64_R_SYM (info); #endif } if (is_32bit_elf) { #ifdef _bfd_int64_low printf ("%8.8lx %8.8lx ", _bfd_int64_low (offset), _bfd_int64_low (info)); #else printf ("%8.8lx %8.8lx ", offset, info); #endif } else { #ifdef _bfd_int64_low printf (do_wide ? "%8.8lx%8.8lx %8.8lx%8.8lx " : "%4.4lx%8.8lx %4.4lx%8.8lx ", _bfd_int64_high (offset), _bfd_int64_low (offset), _bfd_int64_high (info), _bfd_int64_low (info)); #else printf (do_wide ? "%16.16lx %16.16lx " : "%12.12lx %12.12lx ", offset, info); #endif } switch (elf_header.e_machine) { default: rtype = NULL; break; case EM_M32R: case EM_CYGNUS_M32R: rtype = elf_m32r_reloc_type (type); break; case EM_386: case EM_486: rtype = elf_i386_reloc_type (type); break; case EM_68HC11: case EM_68HC12: rtype = elf_m68hc11_reloc_type (type); break; case EM_68K: rtype = elf_m68k_reloc_type (type); break; case EM_960: rtype = elf_i960_reloc_type (type); break; case EM_AVR: case EM_AVR_OLD: rtype = elf_avr_reloc_type (type); break; case EM_OLD_SPARCV9: case EM_SPARC32PLUS: case EM_SPARCV9: case EM_SPARC: rtype = elf_sparc_reloc_type (type); break; case EM_SPU: rtype = elf_spu_reloc_type (type); break; case EM_V850: case EM_CYGNUS_V850: rtype = v850_reloc_type (type); break; case EM_D10V: case EM_CYGNUS_D10V: rtype = elf_d10v_reloc_type (type); break; case EM_D30V: case EM_CYGNUS_D30V: rtype = elf_d30v_reloc_type (type); break; case EM_DLX: rtype = elf_dlx_reloc_type (type); break; case EM_SH: rtype = elf_sh_reloc_type (type); break; case EM_MN10300: case EM_CYGNUS_MN10300: rtype = elf_mn10300_reloc_type (type); break; case EM_MN10200: case EM_CYGNUS_MN10200: rtype = elf_mn10200_reloc_type (type); break; case EM_FR30: case EM_CYGNUS_FR30: rtype = elf_fr30_reloc_type (type); break; case EM_CYGNUS_FRV: rtype = elf_frv_reloc_type (type); break; case EM_MCORE: rtype = elf_mcore_reloc_type (type); break; case EM_MMIX: rtype = elf_mmix_reloc_type (type); break; case EM_MSP430: case EM_MSP430_OLD: rtype = elf_msp430_reloc_type (type); break; case EM_PPC: rtype = elf_ppc_reloc_type (type); break; case EM_PPC64: rtype = elf_ppc64_reloc_type (type); break; case EM_MIPS: case EM_MIPS_RS3_LE: rtype = elf_mips_reloc_type (type); if (!is_32bit_elf) { rtype2 = elf_mips_reloc_type (type2); rtype3 = elf_mips_reloc_type (type3); } break; case EM_ALPHA: rtype = elf_alpha_reloc_type (type); break; case EM_ARM: rtype = elf_arm_reloc_type (type); break; case EM_ARC: rtype = elf_arc_reloc_type (type); break; case EM_PARISC: rtype = elf_hppa_reloc_type (type); break; case EM_H8_300: case EM_H8_300H: case EM_H8S: rtype = elf_h8_reloc_type (type); break; case EM_OPENRISC: case EM_OR32: rtype = elf_or32_reloc_type (type); break; case EM_PJ: case EM_PJ_OLD: rtype = elf_pj_reloc_type (type); break; case EM_IA_64: rtype = elf_ia64_reloc_type (type); break; case EM_CRIS: rtype = elf_cris_reloc_type (type); break; case EM_860: rtype = elf_i860_reloc_type (type); break; case EM_X86_64: rtype = elf_x86_64_reloc_type (type); break; case EM_S370: rtype = i370_reloc_type (type); break; case EM_S390_OLD: case EM_S390: rtype = elf_s390_reloc_type (type); break; case EM_SCORE: rtype = elf_score_reloc_type (type); break; case EM_XSTORMY16: rtype = elf_xstormy16_reloc_type (type); break; case EM_CRX: rtype = elf_crx_reloc_type (type); break; case EM_VAX: rtype = elf_vax_reloc_type (type); break; case EM_IP2K: case EM_IP2K_OLD: rtype = elf_ip2k_reloc_type (type); break; case EM_IQ2000: rtype = elf_iq2000_reloc_type (type); break; case EM_XTENSA_OLD: case EM_XTENSA: rtype = elf_xtensa_reloc_type (type); break; case EM_M32C: rtype = elf_m32c_reloc_type (type); break; case EM_MT: rtype = elf_mt_reloc_type (type); break; case EM_BLACKFIN: rtype = elf_bfin_reloc_type (type); break; case EM_CYGNUS_MEP: rtype = elf_mep_reloc_type (type); break; } if (rtype == NULL) #ifdef _bfd_int64_low printf (_("unrecognized: %-7lx"), _bfd_int64_low (type)); #else printf (_("unrecognized: %-7lx"), type); #endif else printf (do_wide ? "%-22.22s" : "%-17.17s", rtype); if (elf_header.e_machine == EM_ALPHA && rtype != NULL && streq (rtype, "R_ALPHA_LITUSE") && is_rela) { switch (rels[i].r_addend) { case LITUSE_ALPHA_ADDR: rtype = "ADDR"; break; case LITUSE_ALPHA_BASE: rtype = "BASE"; break; case LITUSE_ALPHA_BYTOFF: rtype = "BYTOFF"; break; case LITUSE_ALPHA_JSR: rtype = "JSR"; break; case LITUSE_ALPHA_TLSGD: rtype = "TLSGD"; break; case LITUSE_ALPHA_TLSLDM: rtype = "TLSLDM"; break; case LITUSE_ALPHA_JSRDIRECT: rtype = "JSRDIRECT"; break; default: rtype = NULL; } if (rtype) printf (" (%s)", rtype); else { putchar (' '); printf (_(""), (unsigned long) rels[i].r_addend); } } else if (symtab_index) { if (symtab == NULL || symtab_index >= nsyms) printf (" bad symbol index: %08lx", (unsigned long) symtab_index); else { Elf_Internal_Sym *psym; psym = symtab + symtab_index; printf (" "); print_vma (psym->st_value, LONG_HEX); printf (is_32bit_elf ? " " : " "); if (psym->st_name == 0) { const char *sec_name = ""; char name_buf[40]; if (ELF_ST_TYPE (psym->st_info) == STT_SECTION) { bfd_vma sec_index = (bfd_vma) -1; if (psym->st_shndx < SHN_LORESERVE) sec_index = psym->st_shndx; else if (psym->st_shndx > SHN_HIRESERVE) sec_index = psym->st_shndx - (SHN_HIRESERVE + 1 - SHN_LORESERVE); if (sec_index != (bfd_vma) -1) sec_name = SECTION_NAME (section_headers + sec_index); else if (psym->st_shndx == SHN_ABS) sec_name = "ABS"; else if (psym->st_shndx == SHN_COMMON) sec_name = "COMMON"; else if (elf_header.e_machine == EM_MIPS && psym->st_shndx == SHN_MIPS_SCOMMON) sec_name = "SCOMMON"; else if (elf_header.e_machine == EM_MIPS && psym->st_shndx == SHN_MIPS_SUNDEFINED) sec_name = "SUNDEF"; else if (elf_header.e_machine == EM_X86_64 && psym->st_shndx == SHN_X86_64_LCOMMON) sec_name = "LARGE_COMMON"; else if (elf_header.e_machine == EM_IA_64 && elf_header.e_ident[EI_OSABI] == ELFOSABI_HPUX && psym->st_shndx == SHN_IA_64_ANSI_COMMON) sec_name = "ANSI_COM"; else { sprintf (name_buf, "
", (unsigned int) psym->st_shndx); sec_name = name_buf; } } print_symbol (22, sec_name); } else if (strtab == NULL) printf (_(""), psym->st_name); else if (psym->st_name >= strtablen) printf (_(""), psym->st_name); else print_symbol (22, strtab + psym->st_name); if (is_rela) printf (" + %lx", (unsigned long) rels[i].r_addend); } } else if (is_rela) { printf ("%*c", is_32bit_elf ? (do_wide ? 34 : 28) : (do_wide ? 26 : 20), ' '); print_vma (rels[i].r_addend, LONG_HEX); } if (elf_header.e_machine == EM_SPARCV9 && rtype != NULL && streq (rtype, "R_SPARC_OLO10")) printf (" + %lx", (unsigned long) ELF64_R_TYPE_DATA (info)); putchar ('\n'); if (! is_32bit_elf && elf_header.e_machine == EM_MIPS) { printf (" Type2: "); if (rtype2 == NULL) #ifdef _bfd_int64_low printf (_("unrecognized: %-7lx"), _bfd_int64_low (type2)); #else printf (_("unrecognized: %-7lx"), type2); #endif else printf ("%-17.17s", rtype2); printf ("\n Type3: "); if (rtype3 == NULL) #ifdef _bfd_int64_low printf (_("unrecognized: %-7lx"), _bfd_int64_low (type3)); #else printf (_("unrecognized: %-7lx"), type3); #endif else printf ("%-17.17s", rtype3); putchar ('\n'); } } free (rels); return 1; } static const char * get_mips_dynamic_type (unsigned long type) { switch (type) { case DT_MIPS_RLD_VERSION: return "MIPS_RLD_VERSION"; case DT_MIPS_TIME_STAMP: return "MIPS_TIME_STAMP"; case DT_MIPS_ICHECKSUM: return "MIPS_ICHECKSUM"; case DT_MIPS_IVERSION: return "MIPS_IVERSION"; case DT_MIPS_FLAGS: return "MIPS_FLAGS"; case DT_MIPS_BASE_ADDRESS: return "MIPS_BASE_ADDRESS"; case DT_MIPS_MSYM: return "MIPS_MSYM"; case DT_MIPS_CONFLICT: return "MIPS_CONFLICT"; case DT_MIPS_LIBLIST: return "MIPS_LIBLIST"; case DT_MIPS_LOCAL_GOTNO: return "MIPS_LOCAL_GOTNO"; case DT_MIPS_CONFLICTNO: return "MIPS_CONFLICTNO"; case DT_MIPS_LIBLISTNO: return "MIPS_LIBLISTNO"; case DT_MIPS_SYMTABNO: return "MIPS_SYMTABNO"; case DT_MIPS_UNREFEXTNO: return "MIPS_UNREFEXTNO"; case DT_MIPS_GOTSYM: return "MIPS_GOTSYM"; case DT_MIPS_HIPAGENO: return "MIPS_HIPAGENO"; case DT_MIPS_RLD_MAP: return "MIPS_RLD_MAP"; case DT_MIPS_DELTA_CLASS: return "MIPS_DELTA_CLASS"; case DT_MIPS_DELTA_CLASS_NO: return "MIPS_DELTA_CLASS_NO"; case DT_MIPS_DELTA_INSTANCE: return "MIPS_DELTA_INSTANCE"; case DT_MIPS_DELTA_INSTANCE_NO: return "MIPS_DELTA_INSTANCE_NO"; case DT_MIPS_DELTA_RELOC: return "MIPS_DELTA_RELOC"; case DT_MIPS_DELTA_RELOC_NO: return "MIPS_DELTA_RELOC_NO"; case DT_MIPS_DELTA_SYM: return "MIPS_DELTA_SYM"; case DT_MIPS_DELTA_SYM_NO: return "MIPS_DELTA_SYM_NO"; case DT_MIPS_DELTA_CLASSSYM: return "MIPS_DELTA_CLASSSYM"; case DT_MIPS_DELTA_CLASSSYM_NO: return "MIPS_DELTA_CLASSSYM_NO"; case DT_MIPS_CXX_FLAGS: return "MIPS_CXX_FLAGS"; case DT_MIPS_PIXIE_INIT: return "MIPS_PIXIE_INIT"; case DT_MIPS_SYMBOL_LIB: return "MIPS_SYMBOL_LIB"; case DT_MIPS_LOCALPAGE_GOTIDX: return "MIPS_LOCALPAGE_GOTIDX"; case DT_MIPS_LOCAL_GOTIDX: return "MIPS_LOCAL_GOTIDX"; case DT_MIPS_HIDDEN_GOTIDX: return "MIPS_HIDDEN_GOTIDX"; case DT_MIPS_PROTECTED_GOTIDX: return "MIPS_PROTECTED_GOTIDX"; case DT_MIPS_OPTIONS: return "MIPS_OPTIONS"; case DT_MIPS_INTERFACE: return "MIPS_INTERFACE"; case DT_MIPS_DYNSTR_ALIGN: return "MIPS_DYNSTR_ALIGN"; case DT_MIPS_INTERFACE_SIZE: return "MIPS_INTERFACE_SIZE"; case DT_MIPS_RLD_TEXT_RESOLVE_ADDR: return "MIPS_RLD_TEXT_RESOLVE_ADDR"; case DT_MIPS_PERF_SUFFIX: return "MIPS_PERF_SUFFIX"; case DT_MIPS_COMPACT_SIZE: return "MIPS_COMPACT_SIZE"; case DT_MIPS_GP_VALUE: return "MIPS_GP_VALUE"; case DT_MIPS_AUX_DYNAMIC: return "MIPS_AUX_DYNAMIC"; default: return NULL; } } static const char * get_sparc64_dynamic_type (unsigned long type) { switch (type) { case DT_SPARC_REGISTER: return "SPARC_REGISTER"; default: return NULL; } } static const char * get_ppc_dynamic_type (unsigned long type) { switch (type) { case DT_PPC_GOT: return "PPC_GOT"; default: return NULL; } } static const char * get_ppc64_dynamic_type (unsigned long type) { switch (type) { case DT_PPC64_GLINK: return "PPC64_GLINK"; case DT_PPC64_OPD: return "PPC64_OPD"; case DT_PPC64_OPDSZ: return "PPC64_OPDSZ"; default: return NULL; } } static const char * get_parisc_dynamic_type (unsigned long type) { switch (type) { case DT_HP_LOAD_MAP: return "HP_LOAD_MAP"; case DT_HP_DLD_FLAGS: return "HP_DLD_FLAGS"; case DT_HP_DLD_HOOK: return "HP_DLD_HOOK"; case DT_HP_UX10_INIT: return "HP_UX10_INIT"; case DT_HP_UX10_INITSZ: return "HP_UX10_INITSZ"; case DT_HP_PREINIT: return "HP_PREINIT"; case DT_HP_PREINITSZ: return "HP_PREINITSZ"; case DT_HP_NEEDED: return "HP_NEEDED"; case DT_HP_TIME_STAMP: return "HP_TIME_STAMP"; case DT_HP_CHECKSUM: return "HP_CHECKSUM"; case DT_HP_GST_SIZE: return "HP_GST_SIZE"; case DT_HP_GST_VERSION: return "HP_GST_VERSION"; case DT_HP_GST_HASHVAL: return "HP_GST_HASHVAL"; case DT_HP_EPLTREL: return "HP_GST_EPLTREL"; case DT_HP_EPLTRELSZ: return "HP_GST_EPLTRELSZ"; case DT_HP_FILTERED: return "HP_FILTERED"; case DT_HP_FILTER_TLS: return "HP_FILTER_TLS"; case DT_HP_COMPAT_FILTERED: return "HP_COMPAT_FILTERED"; case DT_HP_LAZYLOAD: return "HP_LAZYLOAD"; case DT_HP_BIND_NOW_COUNT: return "HP_BIND_NOW_COUNT"; case DT_PLT: return "PLT"; case DT_PLT_SIZE: return "PLT_SIZE"; case DT_DLT: return "DLT"; case DT_DLT_SIZE: return "DLT_SIZE"; default: return NULL; } } static const char * get_ia64_dynamic_type (unsigned long type) { switch (type) { case DT_IA_64_PLT_RESERVE: return "IA_64_PLT_RESERVE"; default: return NULL; } } static const char * get_alpha_dynamic_type (unsigned long type) { switch (type) { case DT_ALPHA_PLTRO: return "ALPHA_PLTRO"; default: return NULL; } } static const char * get_score_dynamic_type (unsigned long type) { switch (type) { case DT_SCORE_BASE_ADDRESS: return "SCORE_BASE_ADDRESS"; case DT_SCORE_LOCAL_GOTNO: return "SCORE_LOCAL_GOTNO"; case DT_SCORE_SYMTABNO: return "SCORE_SYMTABNO"; case DT_SCORE_GOTSYM: return "SCORE_GOTSYM"; case DT_SCORE_UNREFEXTNO: return "SCORE_UNREFEXTNO"; case DT_SCORE_HIPAGENO: return "SCORE_HIPAGENO"; default: return NULL; } } static const char * get_dynamic_type (unsigned long type) { static char buff[64]; switch (type) { case DT_NULL: return "NULL"; case DT_NEEDED: return "NEEDED"; case DT_PLTRELSZ: return "PLTRELSZ"; case DT_PLTGOT: return "PLTGOT"; case DT_HASH: return "HASH"; case DT_STRTAB: return "STRTAB"; case DT_SYMTAB: return "SYMTAB"; case DT_RELA: return "RELA"; case DT_RELASZ: return "RELASZ"; case DT_RELAENT: return "RELAENT"; case DT_STRSZ: return "STRSZ"; case DT_SYMENT: return "SYMENT"; case DT_INIT: return "INIT"; case DT_FINI: return "FINI"; case DT_SONAME: return "SONAME"; case DT_RPATH: return "RPATH"; case DT_SYMBOLIC: return "SYMBOLIC"; case DT_REL: return "REL"; case DT_RELSZ: return "RELSZ"; case DT_RELENT: return "RELENT"; case DT_PLTREL: return "PLTREL"; case DT_DEBUG: return "DEBUG"; case DT_TEXTREL: return "TEXTREL"; case DT_JMPREL: return "JMPREL"; case DT_BIND_NOW: return "BIND_NOW"; case DT_INIT_ARRAY: return "INIT_ARRAY"; case DT_FINI_ARRAY: return "FINI_ARRAY"; case DT_INIT_ARRAYSZ: return "INIT_ARRAYSZ"; case DT_FINI_ARRAYSZ: return "FINI_ARRAYSZ"; case DT_RUNPATH: return "RUNPATH"; case DT_FLAGS: return "FLAGS"; case DT_PREINIT_ARRAY: return "PREINIT_ARRAY"; case DT_PREINIT_ARRAYSZ: return "PREINIT_ARRAYSZ"; case DT_CHECKSUM: return "CHECKSUM"; case DT_PLTPADSZ: return "PLTPADSZ"; case DT_MOVEENT: return "MOVEENT"; case DT_MOVESZ: return "MOVESZ"; case DT_FEATURE: return "FEATURE"; case DT_POSFLAG_1: return "POSFLAG_1"; case DT_SYMINSZ: return "SYMINSZ"; case DT_SYMINENT: return "SYMINENT"; /* aka VALRNGHI */ case DT_ADDRRNGLO: return "ADDRRNGLO"; case DT_CONFIG: return "CONFIG"; case DT_DEPAUDIT: return "DEPAUDIT"; case DT_AUDIT: return "AUDIT"; case DT_PLTPAD: return "PLTPAD"; case DT_MOVETAB: return "MOVETAB"; case DT_SYMINFO: return "SYMINFO"; /* aka ADDRRNGHI */ case DT_VERSYM: return "VERSYM"; case DT_TLSDESC_GOT: return "TLSDESC_GOT"; case DT_TLSDESC_PLT: return "TLSDESC_PLT"; case DT_RELACOUNT: return "RELACOUNT"; case DT_RELCOUNT: return "RELCOUNT"; case DT_FLAGS_1: return "FLAGS_1"; case DT_VERDEF: return "VERDEF"; case DT_VERDEFNUM: return "VERDEFNUM"; case DT_VERNEED: return "VERNEED"; case DT_VERNEEDNUM: return "VERNEEDNUM"; case DT_AUXILIARY: return "AUXILIARY"; case DT_USED: return "USED"; case DT_FILTER: return "FILTER"; case DT_GNU_PRELINKED: return "GNU_PRELINKED"; case DT_GNU_CONFLICT: return "GNU_CONFLICT"; case DT_GNU_CONFLICTSZ: return "GNU_CONFLICTSZ"; case DT_GNU_LIBLIST: return "GNU_LIBLIST"; case DT_GNU_LIBLISTSZ: return "GNU_LIBLISTSZ"; case DT_GNU_HASH: return "GNU_HASH"; default: if ((type >= DT_LOPROC) && (type <= DT_HIPROC)) { const char *result; switch (elf_header.e_machine) { case EM_MIPS: case EM_MIPS_RS3_LE: result = get_mips_dynamic_type (type); break; case EM_SPARCV9: result = get_sparc64_dynamic_type (type); break; case EM_PPC: result = get_ppc_dynamic_type (type); break; case EM_PPC64: result = get_ppc64_dynamic_type (type); break; case EM_IA_64: result = get_ia64_dynamic_type (type); break; case EM_ALPHA: result = get_alpha_dynamic_type (type); break; case EM_SCORE: result = get_score_dynamic_type (type); break; default: result = NULL; break; } if (result != NULL) return result; snprintf (buff, sizeof (buff), _("Processor Specific: %lx"), type); } else if (((type >= DT_LOOS) && (type <= DT_HIOS)) || (elf_header.e_machine == EM_PARISC && (type >= OLD_DT_LOOS) && (type <= OLD_DT_HIOS))) { const char *result; switch (elf_header.e_machine) { case EM_PARISC: result = get_parisc_dynamic_type (type); break; default: result = NULL; break; } if (result != NULL) return result; snprintf (buff, sizeof (buff), _("Operating System specific: %lx"), type); } else snprintf (buff, sizeof (buff), _(": %lx"), type); return buff; } } static char * get_file_type (unsigned e_type) { static char buff[32]; switch (e_type) { case ET_NONE: return _("NONE (None)"); case ET_REL: return _("REL (Relocatable file)"); case ET_EXEC: return _("EXEC (Executable file)"); case ET_DYN: return _("DYN (Shared object file)"); case ET_CORE: return _("CORE (Core file)"); default: if ((e_type >= ET_LOPROC) && (e_type <= ET_HIPROC)) snprintf (buff, sizeof (buff), _("Processor Specific: (%x)"), e_type); else if ((e_type >= ET_LOOS) && (e_type <= ET_HIOS)) snprintf (buff, sizeof (buff), _("OS Specific: (%x)"), e_type); else snprintf (buff, sizeof (buff), _(": %x"), e_type); return buff; } } static char * get_machine_name (unsigned e_machine) { static char buff[64]; /* XXX */ switch (e_machine) { case EM_NONE: return _("None"); case EM_M32: return "WE32100"; case EM_SPARC: return "Sparc"; case EM_SPU: return "SPU"; case EM_386: return "Intel 80386"; case EM_68K: return "MC68000"; case EM_88K: return "MC88000"; case EM_486: return "Intel 80486"; case EM_860: return "Intel 80860"; case EM_MIPS: return "MIPS R3000"; case EM_S370: return "IBM System/370"; case EM_MIPS_RS3_LE: return "MIPS R4000 big-endian"; case EM_OLD_SPARCV9: return "Sparc v9 (old)"; case EM_PARISC: return "HPPA"; case EM_PPC_OLD: return "Power PC (old)"; case EM_SPARC32PLUS: return "Sparc v8+" ; case EM_960: return "Intel 90860"; case EM_PPC: return "PowerPC"; case EM_PPC64: return "PowerPC64"; case EM_V800: return "NEC V800"; case EM_FR20: return "Fujitsu FR20"; case EM_RH32: return "TRW RH32"; case EM_MCORE: return "MCORE"; case EM_ARM: return "ARM"; case EM_OLD_ALPHA: return "Digital Alpha (old)"; case EM_SH: return "Renesas / SuperH SH"; case EM_SPARCV9: return "Sparc v9"; case EM_TRICORE: return "Siemens Tricore"; case EM_ARC: return "ARC"; case EM_H8_300: return "Renesas H8/300"; case EM_H8_300H: return "Renesas H8/300H"; case EM_H8S: return "Renesas H8S"; case EM_H8_500: return "Renesas H8/500"; case EM_IA_64: return "Intel IA-64"; case EM_MIPS_X: return "Stanford MIPS-X"; case EM_COLDFIRE: return "Motorola Coldfire"; case EM_68HC12: return "Motorola M68HC12"; case EM_ALPHA: return "Alpha"; case EM_CYGNUS_D10V: case EM_D10V: return "d10v"; case EM_CYGNUS_D30V: case EM_D30V: return "d30v"; case EM_CYGNUS_M32R: case EM_M32R: return "Renesas M32R (formerly Mitsubishi M32r)"; case EM_CYGNUS_V850: case EM_V850: return "NEC v850"; case EM_CYGNUS_MN10300: case EM_MN10300: return "mn10300"; case EM_CYGNUS_MN10200: case EM_MN10200: return "mn10200"; case EM_CYGNUS_FR30: case EM_FR30: return "Fujitsu FR30"; case EM_CYGNUS_FRV: return "Fujitsu FR-V"; case EM_PJ_OLD: case EM_PJ: return "picoJava"; case EM_MMA: return "Fujitsu Multimedia Accelerator"; case EM_PCP: return "Siemens PCP"; case EM_NCPU: return "Sony nCPU embedded RISC processor"; case EM_NDR1: return "Denso NDR1 microprocesspr"; case EM_STARCORE: return "Motorola Star*Core processor"; case EM_ME16: return "Toyota ME16 processor"; case EM_ST100: return "STMicroelectronics ST100 processor"; case EM_TINYJ: return "Advanced Logic Corp. TinyJ embedded processor"; case EM_FX66: return "Siemens FX66 microcontroller"; case EM_ST9PLUS: return "STMicroelectronics ST9+ 8/16 bit microcontroller"; case EM_ST7: return "STMicroelectronics ST7 8-bit microcontroller"; case EM_68HC16: return "Motorola MC68HC16 Microcontroller"; case EM_68HC11: return "Motorola MC68HC11 Microcontroller"; case EM_68HC08: return "Motorola MC68HC08 Microcontroller"; case EM_68HC05: return "Motorola MC68HC05 Microcontroller"; case EM_SVX: return "Silicon Graphics SVx"; case EM_ST19: return "STMicroelectronics ST19 8-bit microcontroller"; case EM_VAX: return "Digital VAX"; case EM_AVR_OLD: case EM_AVR: return "Atmel AVR 8-bit microcontroller"; case EM_CRIS: return "Axis Communications 32-bit embedded processor"; case EM_JAVELIN: return "Infineon Technologies 32-bit embedded cpu"; case EM_FIREPATH: return "Element 14 64-bit DSP processor"; case EM_ZSP: return "LSI Logic's 16-bit DSP processor"; case EM_MMIX: return "Donald Knuth's educational 64-bit processor"; case EM_HUANY: return "Harvard Universitys's machine-independent object format"; case EM_PRISM: return "Vitesse Prism"; case EM_X86_64: return "Advanced Micro Devices X86-64"; case EM_S390_OLD: case EM_S390: return "IBM S/390"; case EM_SCORE: return "SUNPLUS S+Core"; case EM_XSTORMY16: return "Sanyo Xstormy16 CPU core"; case EM_OPENRISC: case EM_OR32: return "OpenRISC"; case EM_CRX: return "National Semiconductor CRX microprocessor"; case EM_DLX: return "OpenDLX"; case EM_IP2K_OLD: case EM_IP2K: return "Ubicom IP2xxx 8-bit microcontrollers"; case EM_IQ2000: return "Vitesse IQ2000"; case EM_XTENSA_OLD: case EM_XTENSA: return "Tensilica Xtensa Processor"; case EM_M32C: return "Renesas M32c"; case EM_MT: return "Morpho Techologies MT processor"; case EM_BLACKFIN: return "Analog Devices Blackfin"; case EM_NIOS32: return "Altera Nios"; case EM_ALTERA_NIOS2: return "Altera Nios II"; case EM_XC16X: return "Infineon Technologies xc16x"; case EM_CYGNUS_MEP: return "Toshiba MeP Media Engine"; default: snprintf (buff, sizeof (buff), _(": 0x%x"), e_machine); return buff; } } static void decode_ARM_machine_flags (unsigned e_flags, char buf[]) { unsigned eabi; int unknown = 0; eabi = EF_ARM_EABI_VERSION (e_flags); e_flags &= ~ EF_ARM_EABIMASK; /* Handle "generic" ARM flags. */ if (e_flags & EF_ARM_RELEXEC) { strcat (buf, ", relocatable executable"); e_flags &= ~ EF_ARM_RELEXEC; } if (e_flags & EF_ARM_HASENTRY) { strcat (buf, ", has entry point"); e_flags &= ~ EF_ARM_HASENTRY; } /* Now handle EABI specific flags. */ switch (eabi) { default: strcat (buf, ", "); if (e_flags) unknown = 1; break; case EF_ARM_EABI_VER1: strcat (buf, ", Version1 EABI"); while (e_flags) { unsigned flag; /* Process flags one bit at a time. */ flag = e_flags & - e_flags; e_flags &= ~ flag; switch (flag) { case EF_ARM_SYMSARESORTED: /* Conflicts with EF_ARM_INTERWORK. */ strcat (buf, ", sorted symbol tables"); break; default: unknown = 1; break; } } break; case EF_ARM_EABI_VER2: strcat (buf, ", Version2 EABI"); while (e_flags) { unsigned flag; /* Process flags one bit at a time. */ flag = e_flags & - e_flags; e_flags &= ~ flag; switch (flag) { case EF_ARM_SYMSARESORTED: /* Conflicts with EF_ARM_INTERWORK. */ strcat (buf, ", sorted symbol tables"); break; case EF_ARM_DYNSYMSUSESEGIDX: strcat (buf, ", dynamic symbols use segment index"); break; case EF_ARM_MAPSYMSFIRST: strcat (buf, ", mapping symbols precede others"); break; default: unknown = 1; break; } } break; case EF_ARM_EABI_VER3: strcat (buf, ", Version3 EABI"); break; case EF_ARM_EABI_VER4: strcat (buf, ", Version4 EABI"); goto eabi; case EF_ARM_EABI_VER5: strcat (buf, ", Version5 EABI"); eabi: while (e_flags) { unsigned flag; /* Process flags one bit at a time. */ flag = e_flags & - e_flags; e_flags &= ~ flag; switch (flag) { case EF_ARM_BE8: strcat (buf, ", BE8"); break; case EF_ARM_LE8: strcat (buf, ", LE8"); break; default: unknown = 1; break; } } break; case EF_ARM_EABI_UNKNOWN: strcat (buf, ", GNU EABI"); while (e_flags) { unsigned flag; /* Process flags one bit at a time. */ flag = e_flags & - e_flags; e_flags &= ~ flag; switch (flag) { case EF_ARM_INTERWORK: strcat (buf, ", interworking enabled"); break; case EF_ARM_APCS_26: strcat (buf, ", uses APCS/26"); break; case EF_ARM_APCS_FLOAT: strcat (buf, ", uses APCS/float"); break; case EF_ARM_PIC: strcat (buf, ", position independent"); break; case EF_ARM_ALIGN8: strcat (buf, ", 8 bit structure alignment"); break; case EF_ARM_NEW_ABI: strcat (buf, ", uses new ABI"); break; case EF_ARM_OLD_ABI: strcat (buf, ", uses old ABI"); break; case EF_ARM_SOFT_FLOAT: strcat (buf, ", software FP"); break; case EF_ARM_VFP_FLOAT: strcat (buf, ", VFP"); break; case EF_ARM_MAVERICK_FLOAT: strcat (buf, ", Maverick FP"); break; default: unknown = 1; break; } } } if (unknown) strcat (buf,", "); } static char * get_machine_flags (unsigned e_flags, unsigned e_machine) { static char buf[1024]; buf[0] = '\0'; if (e_flags) { switch (e_machine) { default: break; case EM_ARM: decode_ARM_machine_flags (e_flags, buf); break; case EM_CYGNUS_FRV: switch (e_flags & EF_FRV_CPU_MASK) { case EF_FRV_CPU_GENERIC: break; default: strcat (buf, ", fr???"); break; case EF_FRV_CPU_FR300: strcat (buf, ", fr300"); break; case EF_FRV_CPU_FR400: strcat (buf, ", fr400"); break; case EF_FRV_CPU_FR405: strcat (buf, ", fr405"); break; case EF_FRV_CPU_FR450: strcat (buf, ", fr450"); break; case EF_FRV_CPU_FR500: strcat (buf, ", fr500"); break; case EF_FRV_CPU_FR550: strcat (buf, ", fr550"); break; case EF_FRV_CPU_SIMPLE: strcat (buf, ", simple"); break; case EF_FRV_CPU_TOMCAT: strcat (buf, ", tomcat"); break; } break; case EM_68K: if ((e_flags & EF_M68K_ARCH_MASK) == EF_M68K_M68000) strcat (buf, ", m68000"); else if ((e_flags & EF_M68K_ARCH_MASK) == EF_M68K_CPU32) strcat (buf, ", cpu32"); else if ((e_flags & EF_M68K_ARCH_MASK) == EF_M68K_FIDO) strcat (buf, ", fido_a"); else { char const *isa = _("unknown"); char const *mac = _("unknown mac"); char const *additional = NULL; switch (e_flags & EF_M68K_CF_ISA_MASK) { case EF_M68K_CF_ISA_A_NODIV: isa = "A"; additional = ", nodiv"; break; case EF_M68K_CF_ISA_A: isa = "A"; break; case EF_M68K_CF_ISA_A_PLUS: isa = "A+"; break; case EF_M68K_CF_ISA_B_NOUSP: isa = "B"; additional = ", nousp"; break; case EF_M68K_CF_ISA_B: isa = "B"; break; } strcat (buf, ", cf, isa "); strcat (buf, isa); if (additional) strcat (buf, additional); if (e_flags & EF_M68K_CF_FLOAT) strcat (buf, ", float"); switch (e_flags & EF_M68K_CF_MAC_MASK) { case 0: mac = NULL; break; case EF_M68K_CF_MAC: mac = "mac"; break; case EF_M68K_CF_EMAC: mac = "emac"; break; } if (mac) { strcat (buf, ", "); strcat (buf, mac); } } break; case EM_PPC: if (e_flags & EF_PPC_EMB) strcat (buf, ", emb"); if (e_flags & EF_PPC_RELOCATABLE) strcat (buf, ", relocatable"); if (e_flags & EF_PPC_RELOCATABLE_LIB) strcat (buf, ", relocatable-lib"); break; case EM_V850: case EM_CYGNUS_V850: switch (e_flags & EF_V850_ARCH) { case E_V850E1_ARCH: strcat (buf, ", v850e1"); break; case E_V850E_ARCH: strcat (buf, ", v850e"); break; case E_V850_ARCH: strcat (buf, ", v850"); break; default: strcat (buf, ", unknown v850 architecture variant"); break; } break; case EM_M32R: case EM_CYGNUS_M32R: if ((e_flags & EF_M32R_ARCH) == E_M32R_ARCH) strcat (buf, ", m32r"); break; case EM_MIPS: case EM_MIPS_RS3_LE: if (e_flags & EF_MIPS_NOREORDER) strcat (buf, ", noreorder"); if (e_flags & EF_MIPS_PIC) strcat (buf, ", pic"); if (e_flags & EF_MIPS_CPIC) strcat (buf, ", cpic"); if (e_flags & EF_MIPS_UCODE) strcat (buf, ", ugen_reserved"); if (e_flags & EF_MIPS_ABI2) strcat (buf, ", abi2"); if (e_flags & EF_MIPS_OPTIONS_FIRST) strcat (buf, ", odk first"); if (e_flags & EF_MIPS_32BITMODE) strcat (buf, ", 32bitmode"); switch ((e_flags & EF_MIPS_MACH)) { case E_MIPS_MACH_3900: strcat (buf, ", 3900"); break; case E_MIPS_MACH_4010: strcat (buf, ", 4010"); break; case E_MIPS_MACH_4100: strcat (buf, ", 4100"); break; case E_MIPS_MACH_4111: strcat (buf, ", 4111"); break; case E_MIPS_MACH_4120: strcat (buf, ", 4120"); break; case E_MIPS_MACH_4650: strcat (buf, ", 4650"); break; case E_MIPS_MACH_5400: strcat (buf, ", 5400"); break; case E_MIPS_MACH_5500: strcat (buf, ", 5500"); break; case E_MIPS_MACH_SB1: strcat (buf, ", sb1"); break; case E_MIPS_MACH_9000: strcat (buf, ", 9000"); break; case 0: /* We simply ignore the field in this case to avoid confusion: MIPS ELF does not specify EF_MIPS_MACH, it is a GNU extension. */ break; default: strcat (buf, ", unknown CPU"); break; } switch ((e_flags & EF_MIPS_ABI)) { case E_MIPS_ABI_O32: strcat (buf, ", o32"); break; case E_MIPS_ABI_O64: strcat (buf, ", o64"); break; case E_MIPS_ABI_EABI32: strcat (buf, ", eabi32"); break; case E_MIPS_ABI_EABI64: strcat (buf, ", eabi64"); break; case 0: /* We simply ignore the field in this case to avoid confusion: MIPS ELF does not specify EF_MIPS_ABI, it is a GNU extension. This means it is likely to be an o32 file, but not for sure. */ break; default: strcat (buf, ", unknown ABI"); break; } if (e_flags & EF_MIPS_ARCH_ASE_MDMX) strcat (buf, ", mdmx"); if (e_flags & EF_MIPS_ARCH_ASE_M16) strcat (buf, ", mips16"); switch ((e_flags & EF_MIPS_ARCH)) { case E_MIPS_ARCH_1: strcat (buf, ", mips1"); break; case E_MIPS_ARCH_2: strcat (buf, ", mips2"); break; case E_MIPS_ARCH_3: strcat (buf, ", mips3"); break; case E_MIPS_ARCH_4: strcat (buf, ", mips4"); break; case E_MIPS_ARCH_5: strcat (buf, ", mips5"); break; case E_MIPS_ARCH_32: strcat (buf, ", mips32"); break; case E_MIPS_ARCH_32R2: strcat (buf, ", mips32r2"); break; case E_MIPS_ARCH_64: strcat (buf, ", mips64"); break; case E_MIPS_ARCH_64R2: strcat (buf, ", mips64r2"); break; default: strcat (buf, ", unknown ISA"); break; } break; case EM_SH: switch ((e_flags & EF_SH_MACH_MASK)) { case EF_SH1: strcat (buf, ", sh1"); break; case EF_SH2: strcat (buf, ", sh2"); break; case EF_SH3: strcat (buf, ", sh3"); break; case EF_SH_DSP: strcat (buf, ", sh-dsp"); break; case EF_SH3_DSP: strcat (buf, ", sh3-dsp"); break; case EF_SH4AL_DSP: strcat (buf, ", sh4al-dsp"); break; case EF_SH3E: strcat (buf, ", sh3e"); break; case EF_SH4: strcat (buf, ", sh4"); break; case EF_SH5: strcat (buf, ", sh5"); break; case EF_SH2E: strcat (buf, ", sh2e"); break; case EF_SH4A: strcat (buf, ", sh4a"); break; case EF_SH2A: strcat (buf, ", sh2a"); break; case EF_SH4_NOFPU: strcat (buf, ", sh4-nofpu"); break; case EF_SH4A_NOFPU: strcat (buf, ", sh4a-nofpu"); break; case EF_SH2A_NOFPU: strcat (buf, ", sh2a-nofpu"); break; case EF_SH3_NOMMU: strcat (buf, ", sh3-nommu"); break; case EF_SH4_NOMMU_NOFPU: strcat (buf, ", sh4-nommu-nofpu"); break; case EF_SH2A_SH4_NOFPU: strcat (buf, ", sh2a-nofpu-or-sh4-nommu-nofpu"); break; case EF_SH2A_SH3_NOFPU: strcat (buf, ", sh2a-nofpu-or-sh3-nommu"); break; case EF_SH2A_SH4: strcat (buf, ", sh2a-or-sh4"); break; case EF_SH2A_SH3E: strcat (buf, ", sh2a-or-sh3e"); break; default: strcat (buf, ", unknown ISA"); break; } break; case EM_SPARCV9: if (e_flags & EF_SPARC_32PLUS) strcat (buf, ", v8+"); if (e_flags & EF_SPARC_SUN_US1) strcat (buf, ", ultrasparcI"); if (e_flags & EF_SPARC_SUN_US3) strcat (buf, ", ultrasparcIII"); if (e_flags & EF_SPARC_HAL_R1) strcat (buf, ", halr1"); if (e_flags & EF_SPARC_LEDATA) strcat (buf, ", ledata"); if ((e_flags & EF_SPARCV9_MM) == EF_SPARCV9_TSO) strcat (buf, ", tso"); if ((e_flags & EF_SPARCV9_MM) == EF_SPARCV9_PSO) strcat (buf, ", pso"); if ((e_flags & EF_SPARCV9_MM) == EF_SPARCV9_RMO) strcat (buf, ", rmo"); break; case EM_PARISC: switch (e_flags & EF_PARISC_ARCH) { case EFA_PARISC_1_0: strcpy (buf, ", PA-RISC 1.0"); break; case EFA_PARISC_1_1: strcpy (buf, ", PA-RISC 1.1"); break; case EFA_PARISC_2_0: strcpy (buf, ", PA-RISC 2.0"); break; default: break; } if (e_flags & EF_PARISC_TRAPNIL) strcat (buf, ", trapnil"); if (e_flags & EF_PARISC_EXT) strcat (buf, ", ext"); if (e_flags & EF_PARISC_LSB) strcat (buf, ", lsb"); if (e_flags & EF_PARISC_WIDE) strcat (buf, ", wide"); if (e_flags & EF_PARISC_NO_KABP) strcat (buf, ", no kabp"); if (e_flags & EF_PARISC_LAZYSWAP) strcat (buf, ", lazyswap"); break; case EM_PJ: case EM_PJ_OLD: if ((e_flags & EF_PICOJAVA_NEWCALLS) == EF_PICOJAVA_NEWCALLS) strcat (buf, ", new calling convention"); if ((e_flags & EF_PICOJAVA_GNUCALLS) == EF_PICOJAVA_GNUCALLS) strcat (buf, ", gnu calling convention"); break; case EM_IA_64: if ((e_flags & EF_IA_64_ABI64)) strcat (buf, ", 64-bit"); else strcat (buf, ", 32-bit"); if ((e_flags & EF_IA_64_REDUCEDFP)) strcat (buf, ", reduced fp model"); if ((e_flags & EF_IA_64_NOFUNCDESC_CONS_GP)) strcat (buf, ", no function descriptors, constant gp"); else if ((e_flags & EF_IA_64_CONS_GP)) strcat (buf, ", constant gp"); if ((e_flags & EF_IA_64_ABSOLUTE)) strcat (buf, ", absolute"); break; case EM_VAX: if ((e_flags & EF_VAX_NONPIC)) strcat (buf, ", non-PIC"); if ((e_flags & EF_VAX_DFLOAT)) strcat (buf, ", D-Float"); if ((e_flags & EF_VAX_GFLOAT)) strcat (buf, ", G-Float"); break; } } return buf; } static const char * get_osabi_name (unsigned int osabi) { static char buff[32]; switch (osabi) { case ELFOSABI_NONE: return "UNIX - System V"; case ELFOSABI_HPUX: return "UNIX - HP-UX"; case ELFOSABI_NETBSD: return "UNIX - NetBSD"; case ELFOSABI_LINUX: return "UNIX - Linux"; case ELFOSABI_HURD: return "GNU/Hurd"; case ELFOSABI_SOLARIS: return "UNIX - Solaris"; case ELFOSABI_AIX: return "UNIX - AIX"; case ELFOSABI_IRIX: return "UNIX - IRIX"; case ELFOSABI_FREEBSD: return "UNIX - FreeBSD"; case ELFOSABI_TRU64: return "UNIX - TRU64"; case ELFOSABI_MODESTO: return "Novell - Modesto"; case ELFOSABI_OPENBSD: return "UNIX - OpenBSD"; case ELFOSABI_OPENVMS: return "VMS - OpenVMS"; case ELFOSABI_NSK: return "HP - Non-Stop Kernel"; case ELFOSABI_AROS: return "Amiga Research OS"; case ELFOSABI_STANDALONE: return _("Standalone App"); case ELFOSABI_ARM: return "ARM"; default: snprintf (buff, sizeof (buff), _(""), osabi); return buff; } } static const char * get_arm_segment_type (unsigned long type) { switch (type) { case PT_ARM_EXIDX: return "EXIDX"; default: break; } return NULL; } static const char * get_mips_segment_type (unsigned long type) { switch (type) { case PT_MIPS_REGINFO: return "REGINFO"; case PT_MIPS_RTPROC: return "RTPROC"; case PT_MIPS_OPTIONS: return "OPTIONS"; default: break; } return NULL; } static const char * get_parisc_segment_type (unsigned long type) { switch (type) { case PT_HP_TLS: return "HP_TLS"; case PT_HP_CORE_NONE: return "HP_CORE_NONE"; case PT_HP_CORE_VERSION: return "HP_CORE_VERSION"; case PT_HP_CORE_KERNEL: return "HP_CORE_KERNEL"; case PT_HP_CORE_COMM: return "HP_CORE_COMM"; case PT_HP_CORE_PROC: return "HP_CORE_PROC"; case PT_HP_CORE_LOADABLE: return "HP_CORE_LOADABLE"; case PT_HP_CORE_STACK: return "HP_CORE_STACK"; case PT_HP_CORE_SHM: return "HP_CORE_SHM"; case PT_HP_CORE_MMF: return "HP_CORE_MMF"; case PT_HP_PARALLEL: return "HP_PARALLEL"; case PT_HP_FASTBIND: return "HP_FASTBIND"; case PT_HP_OPT_ANNOT: return "HP_OPT_ANNOT"; case PT_HP_HSL_ANNOT: return "HP_HSL_ANNOT"; case PT_HP_STACK: return "HP_STACK"; case PT_HP_CORE_UTSNAME: return "HP_CORE_UTSNAME"; case PT_PARISC_ARCHEXT: return "PARISC_ARCHEXT"; case PT_PARISC_UNWIND: return "PARISC_UNWIND"; case PT_PARISC_WEAKORDER: return "PARISC_WEAKORDER"; default: break; } return NULL; } static const char * get_ia64_segment_type (unsigned long type) { switch (type) { case PT_IA_64_ARCHEXT: return "IA_64_ARCHEXT"; case PT_IA_64_UNWIND: return "IA_64_UNWIND"; case PT_HP_TLS: return "HP_TLS"; case PT_IA_64_HP_OPT_ANOT: return "HP_OPT_ANNOT"; case PT_IA_64_HP_HSL_ANOT: return "HP_HSL_ANNOT"; case PT_IA_64_HP_STACK: return "HP_STACK"; default: break; } return NULL; } static const char * get_segment_type (unsigned long p_type) { static char buff[32]; switch (p_type) { case PT_NULL: return "NULL"; case PT_LOAD: return "LOAD"; case PT_DYNAMIC: return "DYNAMIC"; case PT_INTERP: return "INTERP"; case PT_NOTE: return "NOTE"; case PT_SHLIB: return "SHLIB"; case PT_PHDR: return "PHDR"; case PT_TLS: return "TLS"; case PT_GNU_EH_FRAME: return "GNU_EH_FRAME"; case PT_GNU_STACK: return "GNU_STACK"; case PT_GNU_RELRO: return "GNU_RELRO"; default: if ((p_type >= PT_LOPROC) && (p_type <= PT_HIPROC)) { const char *result; switch (elf_header.e_machine) { case EM_ARM: result = get_arm_segment_type (p_type); break; case EM_MIPS: case EM_MIPS_RS3_LE: result = get_mips_segment_type (p_type); break; case EM_PARISC: result = get_parisc_segment_type (p_type); break; case EM_IA_64: result = get_ia64_segment_type (p_type); break; default: result = NULL; break; } if (result != NULL) return result; sprintf (buff, "LOPROC+%lx", p_type - PT_LOPROC); } else if ((p_type >= PT_LOOS) && (p_type <= PT_HIOS)) { const char *result; switch (elf_header.e_machine) { case EM_PARISC: result = get_parisc_segment_type (p_type); break; case EM_IA_64: result = get_ia64_segment_type (p_type); break; default: result = NULL; break; } if (result != NULL) return result; sprintf (buff, "LOOS+%lx", p_type - PT_LOOS); } else snprintf (buff, sizeof (buff), _(": %lx"), p_type); return buff; } } static const char * get_mips_section_type_name (unsigned int sh_type) { switch (sh_type) { case SHT_MIPS_LIBLIST: return "MIPS_LIBLIST"; case SHT_MIPS_MSYM: return "MIPS_MSYM"; case SHT_MIPS_CONFLICT: return "MIPS_CONFLICT"; case SHT_MIPS_GPTAB: return "MIPS_GPTAB"; case SHT_MIPS_UCODE: return "MIPS_UCODE"; case SHT_MIPS_DEBUG: return "MIPS_DEBUG"; case SHT_MIPS_REGINFO: return "MIPS_REGINFO"; case SHT_MIPS_PACKAGE: return "MIPS_PACKAGE"; case SHT_MIPS_PACKSYM: return "MIPS_PACKSYM"; case SHT_MIPS_RELD: return "MIPS_RELD"; case SHT_MIPS_IFACE: return "MIPS_IFACE"; case SHT_MIPS_CONTENT: return "MIPS_CONTENT"; case SHT_MIPS_OPTIONS: return "MIPS_OPTIONS"; case SHT_MIPS_SHDR: return "MIPS_SHDR"; case SHT_MIPS_FDESC: return "MIPS_FDESC"; case SHT_MIPS_EXTSYM: return "MIPS_EXTSYM"; case SHT_MIPS_DENSE: return "MIPS_DENSE"; case SHT_MIPS_PDESC: return "MIPS_PDESC"; case SHT_MIPS_LOCSYM: return "MIPS_LOCSYM"; case SHT_MIPS_AUXSYM: return "MIPS_AUXSYM"; case SHT_MIPS_OPTSYM: return "MIPS_OPTSYM"; case SHT_MIPS_LOCSTR: return "MIPS_LOCSTR"; case SHT_MIPS_LINE: return "MIPS_LINE"; case SHT_MIPS_RFDESC: return "MIPS_RFDESC"; case SHT_MIPS_DELTASYM: return "MIPS_DELTASYM"; case SHT_MIPS_DELTAINST: return "MIPS_DELTAINST"; case SHT_MIPS_DELTACLASS: return "MIPS_DELTACLASS"; case SHT_MIPS_DWARF: return "MIPS_DWARF"; case SHT_MIPS_DELTADECL: return "MIPS_DELTADECL"; case SHT_MIPS_SYMBOL_LIB: return "MIPS_SYMBOL_LIB"; case SHT_MIPS_EVENTS: return "MIPS_EVENTS"; case SHT_MIPS_TRANSLATE: return "MIPS_TRANSLATE"; case SHT_MIPS_PIXIE: return "MIPS_PIXIE"; case SHT_MIPS_XLATE: return "MIPS_XLATE"; case SHT_MIPS_XLATE_DEBUG: return "MIPS_XLATE_DEBUG"; case SHT_MIPS_WHIRL: return "MIPS_WHIRL"; case SHT_MIPS_EH_REGION: return "MIPS_EH_REGION"; case SHT_MIPS_XLATE_OLD: return "MIPS_XLATE_OLD"; case SHT_MIPS_PDR_EXCEPTION: return "MIPS_PDR_EXCEPTION"; default: break; } return NULL; } static const char * get_parisc_section_type_name (unsigned int sh_type) { switch (sh_type) { case SHT_PARISC_EXT: return "PARISC_EXT"; case SHT_PARISC_UNWIND: return "PARISC_UNWIND"; case SHT_PARISC_DOC: return "PARISC_DOC"; case SHT_PARISC_ANNOT: return "PARISC_ANNOT"; case SHT_PARISC_SYMEXTN: return "PARISC_SYMEXTN"; case SHT_PARISC_STUBS: return "PARISC_STUBS"; case SHT_PARISC_DLKM: return "PARISC_DLKM"; default: break; } return NULL; } static const char * get_ia64_section_type_name (unsigned int sh_type) { /* If the top 8 bits are 0x78 the next 8 are the os/abi ID. */ if ((sh_type & 0xFF000000) == SHT_IA_64_LOPSREG) return get_osabi_name ((sh_type & 0x00FF0000) >> 16); switch (sh_type) { case SHT_IA_64_EXT: return "IA_64_EXT"; case SHT_IA_64_UNWIND: return "IA_64_UNWIND"; case SHT_IA_64_PRIORITY_INIT: return "IA_64_PRIORITY_INIT"; default: break; } return NULL; } static const char * get_x86_64_section_type_name (unsigned int sh_type) { switch (sh_type) { case SHT_X86_64_UNWIND: return "X86_64_UNWIND"; default: break; } return NULL; } static const char * get_arm_section_type_name (unsigned int sh_type) { switch (sh_type) { case SHT_ARM_EXIDX: return "ARM_EXIDX"; case SHT_ARM_PREEMPTMAP: return "ARM_PREEMPTMAP"; case SHT_ARM_ATTRIBUTES: return "ARM_ATTRIBUTES"; default: break; } return NULL; } static const char * get_section_type_name (unsigned int sh_type) { static char buff[32]; switch (sh_type) { case SHT_NULL: return "NULL"; case SHT_PROGBITS: return "PROGBITS"; case SHT_SYMTAB: return "SYMTAB"; case SHT_STRTAB: return "STRTAB"; case SHT_RELA: return "RELA"; case SHT_HASH: return "HASH"; case SHT_DYNAMIC: return "DYNAMIC"; case SHT_NOTE: return "NOTE"; case SHT_NOBITS: return "NOBITS"; case SHT_REL: return "REL"; case SHT_SHLIB: return "SHLIB"; case SHT_DYNSYM: return "DYNSYM"; case SHT_INIT_ARRAY: return "INIT_ARRAY"; case SHT_FINI_ARRAY: return "FINI_ARRAY"; case SHT_PREINIT_ARRAY: return "PREINIT_ARRAY"; case SHT_GNU_HASH: return "GNU_HASH"; case SHT_GROUP: return "GROUP"; case SHT_SYMTAB_SHNDX: return "SYMTAB SECTION INDICIES"; case SHT_GNU_verdef: return "VERDEF"; case SHT_GNU_verneed: return "VERNEED"; case SHT_GNU_versym: return "VERSYM"; case 0x6ffffff0: return "VERSYM"; case 0x6ffffffc: return "VERDEF"; case 0x7ffffffd: return "AUXILIARY"; case 0x7fffffff: return "FILTER"; case SHT_GNU_LIBLIST: return "GNU_LIBLIST"; default: if ((sh_type >= SHT_LOPROC) && (sh_type <= SHT_HIPROC)) { const char *result; switch (elf_header.e_machine) { case EM_MIPS: case EM_MIPS_RS3_LE: result = get_mips_section_type_name (sh_type); break; case EM_PARISC: result = get_parisc_section_type_name (sh_type); break; case EM_IA_64: result = get_ia64_section_type_name (sh_type); break; case EM_X86_64: result = get_x86_64_section_type_name (sh_type); break; case EM_ARM: result = get_arm_section_type_name (sh_type); break; default: result = NULL; break; } if (result != NULL) return result; sprintf (buff, "LOPROC+%x", sh_type - SHT_LOPROC); } else if ((sh_type >= SHT_LOOS) && (sh_type <= SHT_HIOS)) sprintf (buff, "LOOS+%x", sh_type - SHT_LOOS); else if ((sh_type >= SHT_LOUSER) && (sh_type <= SHT_HIUSER)) sprintf (buff, "LOUSER+%x", sh_type - SHT_LOUSER); else snprintf (buff, sizeof (buff), _(": %x"), sh_type); return buff; } } #define OPTION_DEBUG_DUMP 512 static struct option options[] = { {"all", no_argument, 0, 'a'}, {"file-header", no_argument, 0, 'h'}, {"program-headers", no_argument, 0, 'l'}, {"headers", no_argument, 0, 'e'}, {"histogram", no_argument, 0, 'I'}, {"segments", no_argument, 0, 'l'}, {"sections", no_argument, 0, 'S'}, {"section-headers", no_argument, 0, 'S'}, {"section-groups", no_argument, 0, 'g'}, {"section-details", no_argument, 0, 't'}, {"full-section-name",no_argument, 0, 'N'}, {"symbols", no_argument, 0, 's'}, {"syms", no_argument, 0, 's'}, {"relocs", no_argument, 0, 'r'}, {"notes", no_argument, 0, 'n'}, {"dynamic", no_argument, 0, 'd'}, {"arch-specific", no_argument, 0, 'A'}, {"version-info", no_argument, 0, 'V'}, {"use-dynamic", no_argument, 0, 'D'}, {"hex-dump", required_argument, 0, 'x'}, {"debug-dump", optional_argument, 0, OPTION_DEBUG_DUMP}, {"unwind", no_argument, 0, 'u'}, #ifdef SUPPORT_DISASSEMBLY {"instruction-dump", required_argument, 0, 'i'}, #endif {"version", no_argument, 0, 'v'}, {"wide", no_argument, 0, 'W'}, {"help", no_argument, 0, 'H'}, {0, no_argument, 0, 0} }; static void usage (FILE *stream) { fprintf (stream, _("Usage: readelf elf-file(s)\n")); fprintf (stream, _(" Display information about the contents of ELF format files\n")); fprintf (stream, _(" Options are:\n\ -a --all Equivalent to: -h -l -S -s -r -d -V -A -I\n\ -h --file-header Display the ELF file header\n\ -l --program-headers Display the program headers\n\ --segments An alias for --program-headers\n\ -S --section-headers Display the sections' header\n\ --sections An alias for --section-headers\n\ -g --section-groups Display the section groups\n\ -t --section-details Display the section details\n\ -e --headers Equivalent to: -h -l -S\n\ -s --syms Display the symbol table\n\ --symbols An alias for --syms\n\ -n --notes Display the core notes (if present)\n\ -r --relocs Display the relocations (if present)\n\ -u --unwind Display the unwind info (if present)\n\ -d --dynamic Display the dynamic section (if present)\n\ -V --version-info Display the version sections (if present)\n\ -A --arch-specific Display architecture specific information (if any).\n\ -D --use-dynamic Use the dynamic section info when displaying symbols\n\ -x --hex-dump= Dump the contents of section \n\ -w[liaprmfFsoR] or\n\ --debug-dump[=line,=info,=abbrev,=pubnames,=aranges,=macro,=frames,=str,=loc,=Ranges]\n\ Display the contents of DWARF2 debug sections\n")); #ifdef SUPPORT_DISASSEMBLY fprintf (stream, _("\ -i --instruction-dump=\n\ Disassemble the contents of section \n")); #endif fprintf (stream, _("\ -I --histogram Display histogram of bucket list lengths\n\ -W --wide Allow output width to exceed 80 characters\n\ @ Read options from \n\ -H --help Display this information\n\ -v --version Display the version number of readelf\n")); if (REPORT_BUGS_TO[0] && stream == stdout) fprintf (stdout, _("Report bugs to %s\n"), REPORT_BUGS_TO); exit (stream == stdout ? 0 : 1); } /* Record the fact that the user wants the contents of section number SECTION to be displayed using the method(s) encoded as flags bits in TYPE. Note, TYPE can be zero if we are creating the array for the first time. */ static void request_dump (unsigned int section, int type) { if (section >= num_dump_sects) { char *new_dump_sects; new_dump_sects = calloc (section + 1, 1); if (new_dump_sects == NULL) error (_("Out of memory allocating dump request table.\n")); else { /* Copy current flag settings. */ memcpy (new_dump_sects, dump_sects, num_dump_sects); free (dump_sects); dump_sects = new_dump_sects; num_dump_sects = section + 1; } } if (dump_sects) dump_sects[section] |= type; return; } /* Request a dump by section name. */ static void request_dump_byname (const char *section, int type) { struct dump_list_entry *new_request; new_request = malloc (sizeof (struct dump_list_entry)); if (!new_request) error (_("Out of memory allocating dump request table.\n")); new_request->name = strdup (section); if (!new_request->name) error (_("Out of memory allocating dump request table.\n")); new_request->type = type; new_request->next = dump_sects_byname; dump_sects_byname = new_request; } static void parse_args (int argc, char **argv) { int c; if (argc < 2) usage (stderr); while ((c = getopt_long (argc, argv, "ersuahnldSDAINtgw::x:i:vVWH", options, NULL)) != EOF) { char *cp; int section; switch (c) { case 0: /* Long options. */ break; case 'H': usage (stdout); break; case 'a': do_syms++; do_reloc++; do_unwind++; do_dynamic++; do_header++; do_sections++; do_section_groups++; do_segments++; do_version++; do_histogram++; do_arch++; do_notes++; break; case 'g': do_section_groups++; break; case 't': case 'N': do_sections++; do_section_details++; break; case 'e': do_header++; do_sections++; do_segments++; break; case 'A': do_arch++; break; case 'D': do_using_dynamic++; break; case 'r': do_reloc++; break; case 'u': do_unwind++; break; case 'h': do_header++; break; case 'l': do_segments++; break; case 's': do_syms++; break; case 'S': do_sections++; break; case 'd': do_dynamic++; break; case 'I': do_histogram++; break; case 'n': do_notes++; break; case 'x': do_dump++; section = strtoul (optarg, & cp, 0); if (! *cp && section >= 0) request_dump (section, HEX_DUMP); else request_dump_byname (optarg, HEX_DUMP); break; case 'w': do_dump++; if (optarg == 0) do_debugging = 1; else { unsigned int index = 0; do_debugging = 0; while (optarg[index]) switch (optarg[index++]) { case 'i': case 'I': do_debug_info = 1; break; case 'a': case 'A': do_debug_abbrevs = 1; break; case 'l': case 'L': do_debug_lines = 1; break; case 'p': case 'P': do_debug_pubnames = 1; break; case 'r': do_debug_aranges = 1; break; case 'R': do_debug_ranges = 1; break; case 'F': do_debug_frames_interp = 1; case 'f': do_debug_frames = 1; break; case 'm': case 'M': do_debug_macinfo = 1; break; case 's': case 'S': do_debug_str = 1; break; case 'o': case 'O': do_debug_loc = 1; break; default: warn (_("Unrecognized debug option '%s'\n"), optarg); break; } } break; case OPTION_DEBUG_DUMP: do_dump++; if (optarg == 0) do_debugging = 1; else { typedef struct { const char * option; int * variable; } debug_dump_long_opts; debug_dump_long_opts opts_table [] = { /* Please keep this table alpha- sorted. */ { "Ranges", & do_debug_ranges }, { "abbrev", & do_debug_abbrevs }, { "aranges", & do_debug_aranges }, { "frames", & do_debug_frames }, { "frames-interp", & do_debug_frames_interp }, { "info", & do_debug_info }, { "line", & do_debug_lines }, { "loc", & do_debug_loc }, { "macro", & do_debug_macinfo }, { "pubnames", & do_debug_pubnames }, /* This entry is for compatability with earlier versions of readelf. */ { "ranges", & do_debug_aranges }, { "str", & do_debug_str }, { NULL, NULL } }; const char *p; do_debugging = 0; p = optarg; while (*p) { debug_dump_long_opts * entry; for (entry = opts_table; entry->option; entry++) { size_t len = strlen (entry->option); if (strneq (p, entry->option, len) && (p[len] == ',' || p[len] == '\0')) { * entry->variable = 1; /* The --debug-dump=frames-interp option also enables the --debug-dump=frames option. */ if (do_debug_frames_interp) do_debug_frames = 1; p += len; break; } } if (entry->option == NULL) { warn (_("Unrecognized debug option '%s'\n"), p); p = strchr (p, ','); if (p == NULL) break; } if (*p == ',') p++; } } break; #ifdef SUPPORT_DISASSEMBLY case 'i': do_dump++; section = strtoul (optarg, & cp, 0); if (! *cp && section >= 0) { request_dump (section, DISASS_DUMP); break; } goto oops; #endif case 'v': print_version (program_name); break; case 'V': do_version++; break; case 'W': do_wide++; break; default: #ifdef SUPPORT_DISASSEMBLY oops: #endif /* xgettext:c-format */ error (_("Invalid option '-%c'\n"), c); /* Drop through. */ case '?': usage (stderr); } } if (!do_dynamic && !do_syms && !do_reloc && !do_unwind && !do_sections && !do_segments && !do_header && !do_dump && !do_version && !do_histogram && !do_debugging && !do_arch && !do_notes && !do_section_groups) usage (stderr); else if (argc < 3) { warn (_("Nothing to do.\n")); usage (stderr); } } static const char * get_elf_class (unsigned int elf_class) { static char buff[32]; switch (elf_class) { case ELFCLASSNONE: return _("none"); case ELFCLASS32: return "ELF32"; case ELFCLASS64: return "ELF64"; default: snprintf (buff, sizeof (buff), _(""), elf_class); return buff; } } static const char * get_data_encoding (unsigned int encoding) { static char buff[32]; switch (encoding) { case ELFDATANONE: return _("none"); case ELFDATA2LSB: return _("2's complement, little endian"); case ELFDATA2MSB: return _("2's complement, big endian"); default: snprintf (buff, sizeof (buff), _(""), encoding); return buff; } } /* Decode the data held in 'elf_header'. */ static int process_file_header (void) { if ( elf_header.e_ident[EI_MAG0] != ELFMAG0 || elf_header.e_ident[EI_MAG1] != ELFMAG1 || elf_header.e_ident[EI_MAG2] != ELFMAG2 || elf_header.e_ident[EI_MAG3] != ELFMAG3) { error (_("Not an ELF file - it has the wrong magic bytes at the start\n")); return 0; } if (do_header) { int i; printf (_("ELF Header:\n")); printf (_(" Magic: ")); for (i = 0; i < EI_NIDENT; i++) printf ("%2.2x ", elf_header.e_ident[i]); printf ("\n"); printf (_(" Class: %s\n"), get_elf_class (elf_header.e_ident[EI_CLASS])); printf (_(" Data: %s\n"), get_data_encoding (elf_header.e_ident[EI_DATA])); printf (_(" Version: %d %s\n"), elf_header.e_ident[EI_VERSION], (elf_header.e_ident[EI_VERSION] == EV_CURRENT ? "(current)" : (elf_header.e_ident[EI_VERSION] != EV_NONE ? "" : ""))); printf (_(" OS/ABI: %s\n"), get_osabi_name (elf_header.e_ident[EI_OSABI])); printf (_(" ABI Version: %d\n"), elf_header.e_ident[EI_ABIVERSION]); printf (_(" Type: %s\n"), get_file_type (elf_header.e_type)); printf (_(" Machine: %s\n"), get_machine_name (elf_header.e_machine)); printf (_(" Version: 0x%lx\n"), (unsigned long) elf_header.e_version); printf (_(" Entry point address: ")); print_vma ((bfd_vma) elf_header.e_entry, PREFIX_HEX); printf (_("\n Start of program headers: ")); print_vma ((bfd_vma) elf_header.e_phoff, DEC); printf (_(" (bytes into file)\n Start of section headers: ")); print_vma ((bfd_vma) elf_header.e_shoff, DEC); printf (_(" (bytes into file)\n")); printf (_(" Flags: 0x%lx%s\n"), (unsigned long) elf_header.e_flags, get_machine_flags (elf_header.e_flags, elf_header.e_machine)); printf (_(" Size of this header: %ld (bytes)\n"), (long) elf_header.e_ehsize); printf (_(" Size of program headers: %ld (bytes)\n"), (long) elf_header.e_phentsize); printf (_(" Number of program headers: %ld\n"), (long) elf_header.e_phnum); printf (_(" Size of section headers: %ld (bytes)\n"), (long) elf_header.e_shentsize); printf (_(" Number of section headers: %ld"), (long) elf_header.e_shnum); if (section_headers != NULL && elf_header.e_shnum == 0) printf (" (%ld)", (long) section_headers[0].sh_size); putc ('\n', stdout); printf (_(" Section header string table index: %ld"), (long) elf_header.e_shstrndx); if (section_headers != NULL && elf_header.e_shstrndx == SHN_XINDEX) printf (" (%ld)", (long) section_headers[0].sh_link); else if (elf_header.e_shstrndx != SHN_UNDEF && (elf_header.e_shstrndx >= elf_header.e_shnum || (elf_header.e_shstrndx >= SHN_LORESERVE && elf_header.e_shstrndx <= SHN_HIRESERVE))) printf (" "); putc ('\n', stdout); } if (section_headers != NULL) { if (elf_header.e_shnum == 0) elf_header.e_shnum = section_headers[0].sh_size; if (elf_header.e_shstrndx == SHN_XINDEX) elf_header.e_shstrndx = section_headers[0].sh_link; else if (elf_header.e_shstrndx != SHN_UNDEF && (elf_header.e_shstrndx >= elf_header.e_shnum || (elf_header.e_shstrndx >= SHN_LORESERVE && elf_header.e_shstrndx <= SHN_HIRESERVE))) elf_header.e_shstrndx = SHN_UNDEF; free (section_headers); section_headers = NULL; } return 1; } static int get_32bit_program_headers (FILE *file, Elf_Internal_Phdr *program_headers) { Elf32_External_Phdr *phdrs; Elf32_External_Phdr *external; Elf_Internal_Phdr *internal; unsigned int i; phdrs = get_data (NULL, file, elf_header.e_phoff, elf_header.e_phentsize, elf_header.e_phnum, _("program headers")); if (!phdrs) return 0; for (i = 0, internal = program_headers, external = phdrs; i < elf_header.e_phnum; i++, internal++, external++) { internal->p_type = BYTE_GET (external->p_type); internal->p_offset = BYTE_GET (external->p_offset); internal->p_vaddr = BYTE_GET (external->p_vaddr); internal->p_paddr = BYTE_GET (external->p_paddr); internal->p_filesz = BYTE_GET (external->p_filesz); internal->p_memsz = BYTE_GET (external->p_memsz); internal->p_flags = BYTE_GET (external->p_flags); internal->p_align = BYTE_GET (external->p_align); } free (phdrs); return 1; } static int get_64bit_program_headers (FILE *file, Elf_Internal_Phdr *program_headers) { Elf64_External_Phdr *phdrs; Elf64_External_Phdr *external; Elf_Internal_Phdr *internal; unsigned int i; phdrs = get_data (NULL, file, elf_header.e_phoff, elf_header.e_phentsize, elf_header.e_phnum, _("program headers")); if (!phdrs) return 0; for (i = 0, internal = program_headers, external = phdrs; i < elf_header.e_phnum; i++, internal++, external++) { internal->p_type = BYTE_GET (external->p_type); internal->p_flags = BYTE_GET (external->p_flags); internal->p_offset = BYTE_GET (external->p_offset); internal->p_vaddr = BYTE_GET (external->p_vaddr); internal->p_paddr = BYTE_GET (external->p_paddr); internal->p_filesz = BYTE_GET (external->p_filesz); internal->p_memsz = BYTE_GET (external->p_memsz); internal->p_align = BYTE_GET (external->p_align); } free (phdrs); return 1; } /* Returns 1 if the program headers were read into `program_headers'. */ static int get_program_headers (FILE *file) { Elf_Internal_Phdr *phdrs; /* Check cache of prior read. */ if (program_headers != NULL) return 1; phdrs = cmalloc (elf_header.e_phnum, sizeof (Elf_Internal_Phdr)); if (phdrs == NULL) { error (_("Out of memory\n")); return 0; } if (is_32bit_elf ? get_32bit_program_headers (file, phdrs) : get_64bit_program_headers (file, phdrs)) { program_headers = phdrs; return 1; } free (phdrs); return 0; } /* Returns 1 if the program headers were loaded. */ static int process_program_headers (FILE *file) { Elf_Internal_Phdr *segment; unsigned int i; if (elf_header.e_phnum == 0) { if (do_segments) printf (_("\nThere are no program headers in this file.\n")); return 0; } if (do_segments && !do_header) { printf (_("\nElf file type is %s\n"), get_file_type (elf_header.e_type)); printf (_("Entry point ")); print_vma ((bfd_vma) elf_header.e_entry, PREFIX_HEX); printf (_("\nThere are %d program headers, starting at offset "), elf_header.e_phnum); print_vma ((bfd_vma) elf_header.e_phoff, DEC); printf ("\n"); } if (! get_program_headers (file)) return 0; if (do_segments) { if (elf_header.e_phnum > 1) printf (_("\nProgram Headers:\n")); else printf (_("\nProgram Headers:\n")); if (is_32bit_elf) printf (_(" Type Offset VirtAddr PhysAddr FileSiz MemSiz Flg Align\n")); else if (do_wide) printf (_(" Type Offset VirtAddr PhysAddr FileSiz MemSiz Flg Align\n")); else { printf (_(" Type Offset VirtAddr PhysAddr\n")); printf (_(" FileSiz MemSiz Flags Align\n")); } } dynamic_addr = 0; dynamic_size = 0; for (i = 0, segment = program_headers; i < elf_header.e_phnum; i++, segment++) { if (do_segments) { printf (" %-14.14s ", get_segment_type (segment->p_type)); if (is_32bit_elf) { printf ("0x%6.6lx ", (unsigned long) segment->p_offset); printf ("0x%8.8lx ", (unsigned long) segment->p_vaddr); printf ("0x%8.8lx ", (unsigned long) segment->p_paddr); printf ("0x%5.5lx ", (unsigned long) segment->p_filesz); printf ("0x%5.5lx ", (unsigned long) segment->p_memsz); printf ("%c%c%c ", (segment->p_flags & PF_R ? 'R' : ' '), (segment->p_flags & PF_W ? 'W' : ' '), (segment->p_flags & PF_X ? 'E' : ' ')); printf ("%#lx", (unsigned long) segment->p_align); } else if (do_wide) { if ((unsigned long) segment->p_offset == segment->p_offset) printf ("0x%6.6lx ", (unsigned long) segment->p_offset); else { print_vma (segment->p_offset, FULL_HEX); putchar (' '); } print_vma (segment->p_vaddr, FULL_HEX); putchar (' '); print_vma (segment->p_paddr, FULL_HEX); putchar (' '); if ((unsigned long) segment->p_filesz == segment->p_filesz) printf ("0x%6.6lx ", (unsigned long) segment->p_filesz); else { print_vma (segment->p_filesz, FULL_HEX); putchar (' '); } if ((unsigned long) segment->p_memsz == segment->p_memsz) printf ("0x%6.6lx", (unsigned long) segment->p_memsz); else { print_vma (segment->p_offset, FULL_HEX); } printf (" %c%c%c ", (segment->p_flags & PF_R ? 'R' : ' '), (segment->p_flags & PF_W ? 'W' : ' '), (segment->p_flags & PF_X ? 'E' : ' ')); if ((unsigned long) segment->p_align == segment->p_align) printf ("%#lx", (unsigned long) segment->p_align); else { print_vma (segment->p_align, PREFIX_HEX); } } else { print_vma (segment->p_offset, FULL_HEX); putchar (' '); print_vma (segment->p_vaddr, FULL_HEX); putchar (' '); print_vma (segment->p_paddr, FULL_HEX); printf ("\n "); print_vma (segment->p_filesz, FULL_HEX); putchar (' '); print_vma (segment->p_memsz, FULL_HEX); printf (" %c%c%c ", (segment->p_flags & PF_R ? 'R' : ' '), (segment->p_flags & PF_W ? 'W' : ' '), (segment->p_flags & PF_X ? 'E' : ' ')); print_vma (segment->p_align, HEX); } } switch (segment->p_type) { case PT_DYNAMIC: if (dynamic_addr) error (_("more than one dynamic segment\n")); /* Try to locate the .dynamic section. If there is a section header table, we can easily locate it. */ if (section_headers != NULL) { Elf_Internal_Shdr *sec; sec = find_section (".dynamic"); if (sec == NULL || sec->sh_size == 0) { error (_("no .dynamic section in the dynamic segment\n")); break; } if (sec->sh_type == SHT_NOBITS) break; dynamic_addr = sec->sh_offset; dynamic_size = sec->sh_size; if (dynamic_addr < segment->p_offset || dynamic_addr > segment->p_offset + segment->p_filesz) warn (_("the .dynamic section is not contained within the dynamic segment\n")); else if (dynamic_addr > segment->p_offset) warn (_("the .dynamic section is not the first section in the dynamic segment.\n")); } else { /* Otherwise, we can only assume that the .dynamic section is the first section in the DYNAMIC segment. */ dynamic_addr = segment->p_offset; dynamic_size = segment->p_filesz; } break; case PT_INTERP: if (fseek (file, archive_file_offset + (long) segment->p_offset, SEEK_SET)) error (_("Unable to find program interpreter name\n")); else { char fmt [32]; int ret = snprintf (fmt, sizeof (fmt), "%%%ds", PATH_MAX); if (ret >= (int) sizeof (fmt) || ret < 0) error (_("Internal error: failed to create format string to display program interpreter\n")); program_interpreter[0] = 0; if (fscanf (file, fmt, program_interpreter) <= 0) error (_("Unable to read program interpreter name\n")); if (do_segments) printf (_("\n [Requesting program interpreter: %s]"), program_interpreter); } break; } if (do_segments) putc ('\n', stdout); } if (do_segments && section_headers != NULL && string_table != NULL) { printf (_("\n Section to Segment mapping:\n")); printf (_(" Segment Sections...\n")); for (i = 0; i < elf_header.e_phnum; i++) { unsigned int j; Elf_Internal_Shdr *section; segment = program_headers + i; section = section_headers; printf (" %2.2d ", i); for (j = 1; j < elf_header.e_shnum; j++, section++) { if (ELF_IS_SECTION_IN_SEGMENT_MEMORY(section, segment)) printf ("%s ", SECTION_NAME (section)); } putc ('\n',stdout); } } return 1; } /* Find the file offset corresponding to VMA by using the program headers. */ static long offset_from_vma (FILE *file, bfd_vma vma, bfd_size_type size) { Elf_Internal_Phdr *seg; if (! get_program_headers (file)) { warn (_("Cannot interpret virtual addresses without program headers.\n")); return (long) vma; } for (seg = program_headers; seg < program_headers + elf_header.e_phnum; ++seg) { if (seg->p_type != PT_LOAD) continue; if (vma >= (seg->p_vaddr & -seg->p_align) && vma + size <= seg->p_vaddr + seg->p_filesz) return vma - seg->p_vaddr + seg->p_offset; } warn (_("Virtual address 0x%lx not located in any PT_LOAD segment.\n"), (long) vma); return (long) vma; } static int get_32bit_section_headers (FILE *file, unsigned int num) { Elf32_External_Shdr *shdrs; Elf_Internal_Shdr *internal; unsigned int i; shdrs = get_data (NULL, file, elf_header.e_shoff, elf_header.e_shentsize, num, _("section headers")); if (!shdrs) return 0; section_headers = cmalloc (num, sizeof (Elf_Internal_Shdr)); if (section_headers == NULL) { error (_("Out of memory\n")); return 0; } for (i = 0, internal = section_headers; i < num; i++, internal++) { internal->sh_name = BYTE_GET (shdrs[i].sh_name); internal->sh_type = BYTE_GET (shdrs[i].sh_type); internal->sh_flags = BYTE_GET (shdrs[i].sh_flags); internal->sh_addr = BYTE_GET (shdrs[i].sh_addr); internal->sh_offset = BYTE_GET (shdrs[i].sh_offset); internal->sh_size = BYTE_GET (shdrs[i].sh_size); internal->sh_link = BYTE_GET (shdrs[i].sh_link); internal->sh_info = BYTE_GET (shdrs[i].sh_info); internal->sh_addralign = BYTE_GET (shdrs[i].sh_addralign); internal->sh_entsize = BYTE_GET (shdrs[i].sh_entsize); } free (shdrs); return 1; } static int get_64bit_section_headers (FILE *file, unsigned int num) { Elf64_External_Shdr *shdrs; Elf_Internal_Shdr *internal; unsigned int i; shdrs = get_data (NULL, file, elf_header.e_shoff, elf_header.e_shentsize, num, _("section headers")); if (!shdrs) return 0; section_headers = cmalloc (num, sizeof (Elf_Internal_Shdr)); if (section_headers == NULL) { error (_("Out of memory\n")); return 0; } for (i = 0, internal = section_headers; i < num; i++, internal++) { internal->sh_name = BYTE_GET (shdrs[i].sh_name); internal->sh_type = BYTE_GET (shdrs[i].sh_type); internal->sh_flags = BYTE_GET (shdrs[i].sh_flags); internal->sh_addr = BYTE_GET (shdrs[i].sh_addr); internal->sh_size = BYTE_GET (shdrs[i].sh_size); internal->sh_entsize = BYTE_GET (shdrs[i].sh_entsize); internal->sh_link = BYTE_GET (shdrs[i].sh_link); internal->sh_info = BYTE_GET (shdrs[i].sh_info); internal->sh_offset = BYTE_GET (shdrs[i].sh_offset); internal->sh_addralign = BYTE_GET (shdrs[i].sh_addralign); } free (shdrs); return 1; } static Elf_Internal_Sym * get_32bit_elf_symbols (FILE *file, Elf_Internal_Shdr *section) { unsigned long number; Elf32_External_Sym *esyms; Elf_External_Sym_Shndx *shndx; Elf_Internal_Sym *isyms; Elf_Internal_Sym *psym; unsigned int j; esyms = get_data (NULL, file, section->sh_offset, 1, section->sh_size, _("symbols")); if (!esyms) return NULL; shndx = NULL; if (symtab_shndx_hdr != NULL && (symtab_shndx_hdr->sh_link == (unsigned long) SECTION_HEADER_NUM (section - section_headers))) { shndx = get_data (NULL, file, symtab_shndx_hdr->sh_offset, 1, symtab_shndx_hdr->sh_size, _("symtab shndx")); if (!shndx) { free (esyms); return NULL; } } number = section->sh_size / section->sh_entsize; isyms = cmalloc (number, sizeof (Elf_Internal_Sym)); if (isyms == NULL) { error (_("Out of memory\n")); if (shndx) free (shndx); free (esyms); return NULL; } for (j = 0, psym = isyms; j < number; j++, psym++) { psym->st_name = BYTE_GET (esyms[j].st_name); psym->st_value = BYTE_GET (esyms[j].st_value); psym->st_size = BYTE_GET (esyms[j].st_size); psym->st_shndx = BYTE_GET (esyms[j].st_shndx); if (psym->st_shndx == SHN_XINDEX && shndx != NULL) psym->st_shndx = byte_get ((unsigned char *) &shndx[j], sizeof (shndx[j])); psym->st_info = BYTE_GET (esyms[j].st_info); psym->st_other = BYTE_GET (esyms[j].st_other); } if (shndx) free (shndx); free (esyms); return isyms; } static Elf_Internal_Sym * get_64bit_elf_symbols (FILE *file, Elf_Internal_Shdr *section) { unsigned long number; Elf64_External_Sym *esyms; Elf_External_Sym_Shndx *shndx; Elf_Internal_Sym *isyms; Elf_Internal_Sym *psym; unsigned int j; esyms = get_data (NULL, file, section->sh_offset, 1, section->sh_size, _("symbols")); if (!esyms) return NULL; shndx = NULL; if (symtab_shndx_hdr != NULL && (symtab_shndx_hdr->sh_link == (unsigned long) SECTION_HEADER_NUM (section - section_headers))) { shndx = get_data (NULL, file, symtab_shndx_hdr->sh_offset, 1, symtab_shndx_hdr->sh_size, _("symtab shndx")); if (!shndx) { free (esyms); return NULL; } } number = section->sh_size / section->sh_entsize; isyms = cmalloc (number, sizeof (Elf_Internal_Sym)); if (isyms == NULL) { error (_("Out of memory\n")); if (shndx) free (shndx); free (esyms); return NULL; } for (j = 0, psym = isyms; j < number; j++, psym++) { psym->st_name = BYTE_GET (esyms[j].st_name); psym->st_info = BYTE_GET (esyms[j].st_info); psym->st_other = BYTE_GET (esyms[j].st_other); psym->st_shndx = BYTE_GET (esyms[j].st_shndx); if (psym->st_shndx == SHN_XINDEX && shndx != NULL) psym->st_shndx = byte_get ((unsigned char *) &shndx[j], sizeof (shndx[j])); psym->st_value = BYTE_GET (esyms[j].st_value); psym->st_size = BYTE_GET (esyms[j].st_size); } if (shndx) free (shndx); free (esyms); return isyms; } static const char * get_elf_section_flags (bfd_vma sh_flags) { static char buff[1024]; char *p = buff; int field_size = is_32bit_elf ? 8 : 16; int index, size = sizeof (buff) - (field_size + 4 + 1); bfd_vma os_flags = 0; bfd_vma proc_flags = 0; bfd_vma unknown_flags = 0; const struct { const char *str; int len; } flags [] = { { "WRITE", 5 }, { "ALLOC", 5 }, { "EXEC", 4 }, { "MERGE", 5 }, { "STRINGS", 7 }, { "INFO LINK", 9 }, { "LINK ORDER", 10 }, { "OS NONCONF", 10 }, { "GROUP", 5 }, { "TLS", 3 } }; if (do_section_details) { sprintf (buff, "[%*.*lx]: ", field_size, field_size, (unsigned long) sh_flags); p += field_size + 4; } while (sh_flags) { bfd_vma flag; flag = sh_flags & - sh_flags; sh_flags &= ~ flag; if (do_section_details) { switch (flag) { case SHF_WRITE: index = 0; break; case SHF_ALLOC: index = 1; break; case SHF_EXECINSTR: index = 2; break; case SHF_MERGE: index = 3; break; case SHF_STRINGS: index = 4; break; case SHF_INFO_LINK: index = 5; break; case SHF_LINK_ORDER: index = 6; break; case SHF_OS_NONCONFORMING: index = 7; break; case SHF_GROUP: index = 8; break; case SHF_TLS: index = 9; break; default: index = -1; break; } if (index != -1) { if (p != buff + field_size + 4) { if (size < (10 + 2)) abort (); size -= 2; *p++ = ','; *p++ = ' '; } size -= flags [index].len; p = stpcpy (p, flags [index].str); } else if (flag & SHF_MASKOS) os_flags |= flag; else if (flag & SHF_MASKPROC) proc_flags |= flag; else unknown_flags |= flag; } else { switch (flag) { case SHF_WRITE: *p = 'W'; break; case SHF_ALLOC: *p = 'A'; break; case SHF_EXECINSTR: *p = 'X'; break; case SHF_MERGE: *p = 'M'; break; case SHF_STRINGS: *p = 'S'; break; case SHF_INFO_LINK: *p = 'I'; break; case SHF_LINK_ORDER: *p = 'L'; break; case SHF_OS_NONCONFORMING: *p = 'O'; break; case SHF_GROUP: *p = 'G'; break; case SHF_TLS: *p = 'T'; break; default: if (elf_header.e_machine == EM_X86_64 && flag == SHF_X86_64_LARGE) *p = 'l'; else if (flag & SHF_MASKOS) { *p = 'o'; sh_flags &= ~ SHF_MASKOS; } else if (flag & SHF_MASKPROC) { *p = 'p'; sh_flags &= ~ SHF_MASKPROC; } else *p = 'x'; break; } p++; } } if (do_section_details) { if (os_flags) { size -= 5 + field_size; if (p != buff + field_size + 4) { if (size < (2 + 1)) abort (); size -= 2; *p++ = ','; *p++ = ' '; } sprintf (p, "OS (%*.*lx)", field_size, field_size, (unsigned long) os_flags); p += 5 + field_size; } if (proc_flags) { size -= 7 + field_size; if (p != buff + field_size + 4) { if (size < (2 + 1)) abort (); size -= 2; *p++ = ','; *p++ = ' '; } sprintf (p, "PROC (%*.*lx)", field_size, field_size, (unsigned long) proc_flags); p += 7 + field_size; } if (unknown_flags) { size -= 10 + field_size; if (p != buff + field_size + 4) { if (size < (2 + 1)) abort (); size -= 2; *p++ = ','; *p++ = ' '; } sprintf (p, "UNKNOWN (%*.*lx)", field_size, field_size, (unsigned long) unknown_flags); p += 10 + field_size; } } *p = '\0'; return buff; } static int process_section_headers (FILE *file) { Elf_Internal_Shdr *section; unsigned int i; section_headers = NULL; if (elf_header.e_shnum == 0) { if (do_sections) printf (_("\nThere are no sections in this file.\n")); return 1; } if (do_sections && !do_header) printf (_("There are %d section headers, starting at offset 0x%lx:\n"), elf_header.e_shnum, (unsigned long) elf_header.e_shoff); if (is_32bit_elf) { if (! get_32bit_section_headers (file, elf_header.e_shnum)) return 0; } else if (! get_64bit_section_headers (file, elf_header.e_shnum)) return 0; /* Read in the string table, so that we have names to display. */ if (elf_header.e_shstrndx != SHN_UNDEF && SECTION_HEADER_INDEX (elf_header.e_shstrndx) < elf_header.e_shnum) { section = SECTION_HEADER (elf_header.e_shstrndx); if (section->sh_size != 0) { string_table = get_data (NULL, file, section->sh_offset, 1, section->sh_size, _("string table")); string_table_length = string_table != NULL ? section->sh_size : 0; } } /* Scan the sections for the dynamic symbol table and dynamic string table and debug sections. */ dynamic_symbols = NULL; dynamic_strings = NULL; dynamic_syminfo = NULL; symtab_shndx_hdr = NULL; eh_addr_size = is_32bit_elf ? 4 : 8; switch (elf_header.e_machine) { case EM_MIPS: case EM_MIPS_RS3_LE: /* The 64-bit MIPS EABI uses a combination of 32-bit ELF and 64-bit FDE addresses. However, the ABI also has a semi-official ILP32 variant for which the normal FDE address size rules apply. GCC 4.0 marks EABI64 objects with a dummy .gcc_compiled_longXX section, where XX is the size of longs in bits. Unfortunately, earlier compilers provided no way of distinguishing ILP32 objects from LP64 objects, so if there's any doubt, we should assume that the official LP64 form is being used. */ if ((elf_header.e_flags & EF_MIPS_ABI) == E_MIPS_ABI_EABI64 && find_section (".gcc_compiled_long32") == NULL) eh_addr_size = 8; break; case EM_H8_300: case EM_H8_300H: switch (elf_header.e_flags & EF_H8_MACH) { case E_H8_MACH_H8300: case E_H8_MACH_H8300HN: case E_H8_MACH_H8300SN: case E_H8_MACH_H8300SXN: eh_addr_size = 2; break; case E_H8_MACH_H8300H: case E_H8_MACH_H8300S: case E_H8_MACH_H8300SX: eh_addr_size = 4; break; } } #define CHECK_ENTSIZE_VALUES(section, i, size32, size64) \ do \ { \ size_t expected_entsize \ = is_32bit_elf ? size32 : size64; \ if (section->sh_entsize != expected_entsize) \ error (_("Section %d has invalid sh_entsize %lx (expected %lx)\n"), \ i, (unsigned long int) section->sh_entsize, \ (unsigned long int) expected_entsize); \ section->sh_entsize = expected_entsize; \ } \ while (0) #define CHECK_ENTSIZE(section, i, type) \ CHECK_ENTSIZE_VALUES (section, i, sizeof (Elf32_External_##type), \ sizeof (Elf64_External_##type)) for (i = 0, section = section_headers; i < elf_header.e_shnum; i++, section++) { char *name = SECTION_NAME (section); if (section->sh_type == SHT_DYNSYM) { if (dynamic_symbols != NULL) { error (_("File contains multiple dynamic symbol tables\n")); continue; } CHECK_ENTSIZE (section, i, Sym); num_dynamic_syms = section->sh_size / section->sh_entsize; dynamic_symbols = GET_ELF_SYMBOLS (file, section); } else if (section->sh_type == SHT_STRTAB && streq (name, ".dynstr")) { if (dynamic_strings != NULL) { error (_("File contains multiple dynamic string tables\n")); continue; } dynamic_strings = get_data (NULL, file, section->sh_offset, 1, section->sh_size, _("dynamic strings")); dynamic_strings_length = section->sh_size; } else if (section->sh_type == SHT_SYMTAB_SHNDX) { if (symtab_shndx_hdr != NULL) { error (_("File contains multiple symtab shndx tables\n")); continue; } symtab_shndx_hdr = section; } else if (section->sh_type == SHT_SYMTAB) CHECK_ENTSIZE (section, i, Sym); else if (section->sh_type == SHT_GROUP) CHECK_ENTSIZE_VALUES (section, i, GRP_ENTRY_SIZE, GRP_ENTRY_SIZE); else if (section->sh_type == SHT_REL) CHECK_ENTSIZE (section, i, Rel); else if (section->sh_type == SHT_RELA) CHECK_ENTSIZE (section, i, Rela); else if ((do_debugging || do_debug_info || do_debug_abbrevs || do_debug_lines || do_debug_pubnames || do_debug_aranges || do_debug_frames || do_debug_macinfo || do_debug_str || do_debug_loc || do_debug_ranges) && const_strneq (name, ".debug_")) { name += 7; if (do_debugging || (do_debug_info && streq (name, "info")) || (do_debug_abbrevs && streq (name, "abbrev")) || (do_debug_lines && streq (name, "line")) || (do_debug_pubnames && streq (name, "pubnames")) || (do_debug_aranges && streq (name, "aranges")) || (do_debug_ranges && streq (name, "ranges")) || (do_debug_frames && streq (name, "frame")) || (do_debug_macinfo && streq (name, "macinfo")) || (do_debug_str && streq (name, "str")) || (do_debug_loc && streq (name, "loc")) ) request_dump (i, DEBUG_DUMP); } /* linkonce section to be combined with .debug_info at link time. */ else if ((do_debugging || do_debug_info) && const_strneq (name, ".gnu.linkonce.wi.")) request_dump (i, DEBUG_DUMP); else if (do_debug_frames && streq (name, ".eh_frame")) request_dump (i, DEBUG_DUMP); } if (! do_sections) return 1; if (elf_header.e_shnum > 1) printf (_("\nSection Headers:\n")); else printf (_("\nSection Header:\n")); if (is_32bit_elf) { if (do_section_details) { printf (_(" [Nr] Name\n")); printf (_(" Type Addr Off Size ES Lk Inf Al\n")); } else printf (_(" [Nr] Name Type Addr Off Size ES Flg Lk Inf Al\n")); } else if (do_wide) { if (do_section_details) { printf (_(" [Nr] Name\n")); printf (_(" Type Address Off Size ES Lk Inf Al\n")); } else printf (_(" [Nr] Name Type Address Off Size ES Flg Lk Inf Al\n")); } else { if (do_section_details) { printf (_(" [Nr] Name\n")); printf (_(" Type Address Offset Link\n")); printf (_(" Size EntSize Info Align\n")); } else { printf (_(" [Nr] Name Type Address Offset\n")); printf (_(" Size EntSize Flags Link Info Align\n")); } } if (do_section_details) printf (_(" Flags\n")); for (i = 0, section = section_headers; i < elf_header.e_shnum; i++, section++) { if (do_section_details) { printf (" [%2u] %s\n", SECTION_HEADER_NUM (i), SECTION_NAME (section)); if (is_32bit_elf || do_wide) printf (" %-15.15s ", get_section_type_name (section->sh_type)); } else printf (" [%2u] %-17.17s %-15.15s ", SECTION_HEADER_NUM (i), SECTION_NAME (section), get_section_type_name (section->sh_type)); if (is_32bit_elf) { print_vma (section->sh_addr, LONG_HEX); printf ( " %6.6lx %6.6lx %2.2lx", (unsigned long) section->sh_offset, (unsigned long) section->sh_size, (unsigned long) section->sh_entsize); if (do_section_details) fputs (" ", stdout); else printf (" %3s ", get_elf_section_flags (section->sh_flags)); printf ("%2ld %3lu %2ld\n", (unsigned long) section->sh_link, (unsigned long) section->sh_info, (unsigned long) section->sh_addralign); } else if (do_wide) { print_vma (section->sh_addr, LONG_HEX); if ((long) section->sh_offset == section->sh_offset) printf (" %6.6lx", (unsigned long) section->sh_offset); else { putchar (' '); print_vma (section->sh_offset, LONG_HEX); } if ((unsigned long) section->sh_size == section->sh_size) printf (" %6.6lx", (unsigned long) section->sh_size); else { putchar (' '); print_vma (section->sh_size, LONG_HEX); } if ((unsigned long) section->sh_entsize == section->sh_entsize) printf (" %2.2lx", (unsigned long) section->sh_entsize); else { putchar (' '); print_vma (section->sh_entsize, LONG_HEX); } if (do_section_details) fputs (" ", stdout); else printf (" %3s ", get_elf_section_flags (section->sh_flags)); printf ("%2ld %3lu ", (unsigned long) section->sh_link, (unsigned long) section->sh_info); if ((unsigned long) section->sh_addralign == section->sh_addralign) printf ("%2ld\n", (unsigned long) section->sh_addralign); else { print_vma (section->sh_addralign, DEC); putchar ('\n'); } } else if (do_section_details) { printf (" %-15.15s ", get_section_type_name (section->sh_type)); print_vma (section->sh_addr, LONG_HEX); if ((long) section->sh_offset == section->sh_offset) printf (" %16.16lx", (unsigned long) section->sh_offset); else { printf (" "); print_vma (section->sh_offset, LONG_HEX); } printf (" %ld\n ", (unsigned long) section->sh_link); print_vma (section->sh_size, LONG_HEX); putchar (' '); print_vma (section->sh_entsize, LONG_HEX); printf (" %-16lu %ld\n", (unsigned long) section->sh_info, (unsigned long) section->sh_addralign); } else { putchar (' '); print_vma (section->sh_addr, LONG_HEX); if ((long) section->sh_offset == section->sh_offset) printf (" %8.8lx", (unsigned long) section->sh_offset); else { printf (" "); print_vma (section->sh_offset, LONG_HEX); } printf ("\n "); print_vma (section->sh_size, LONG_HEX); printf (" "); print_vma (section->sh_entsize, LONG_HEX); printf (" %3s ", get_elf_section_flags (section->sh_flags)); printf (" %2ld %3lu %ld\n", (unsigned long) section->sh_link, (unsigned long) section->sh_info, (unsigned long) section->sh_addralign); } if (do_section_details) printf (" %s\n", get_elf_section_flags (section->sh_flags)); } if (!do_section_details) printf (_("Key to Flags:\n\ W (write), A (alloc), X (execute), M (merge), S (strings)\n\ I (info), L (link order), G (group), x (unknown)\n\ O (extra OS processing required) o (OS specific), p (processor specific)\n")); return 1; } static const char * get_group_flags (unsigned int flags) { static char buff[32]; switch (flags) { case GRP_COMDAT: return "COMDAT"; default: snprintf (buff, sizeof (buff), _("[: 0x%x]"), flags); break; } return buff; } static int process_section_groups (FILE *file) { Elf_Internal_Shdr *section; unsigned int i; struct group *group; Elf_Internal_Shdr *symtab_sec, *strtab_sec; Elf_Internal_Sym *symtab; char *strtab; size_t strtab_size; /* Don't process section groups unless needed. */ if (!do_unwind && !do_section_groups) return 1; if (elf_header.e_shnum == 0) { if (do_section_groups) printf (_("\nThere are no sections in this file.\n")); return 1; } if (section_headers == NULL) { error (_("Section headers are not available!\n")); abort (); } section_headers_groups = calloc (elf_header.e_shnum, sizeof (struct group *)); if (section_headers_groups == NULL) { error (_("Out of memory\n")); return 0; } /* Scan the sections for the group section. */ group_count = 0; for (i = 0, section = section_headers; i < elf_header.e_shnum; i++, section++) if (section->sh_type == SHT_GROUP) group_count++; if (group_count == 0) { if (do_section_groups) printf (_("\nThere are no section groups in this file.\n")); return 1; } section_groups = calloc (group_count, sizeof (struct group)); if (section_groups == NULL) { error (_("Out of memory\n")); return 0; } symtab_sec = NULL; strtab_sec = NULL; symtab = NULL; strtab = NULL; strtab_size = 0; for (i = 0, section = section_headers, group = section_groups; i < elf_header.e_shnum; i++, section++) { if (section->sh_type == SHT_GROUP) { char *name = SECTION_NAME (section); char *group_name; unsigned char *start, *indices; unsigned int entry, j, size; Elf_Internal_Shdr *sec; Elf_Internal_Sym *sym; /* Get the symbol table. */ if (SECTION_HEADER_INDEX (section->sh_link) >= elf_header.e_shnum || ((sec = SECTION_HEADER (section->sh_link))->sh_type != SHT_SYMTAB)) { error (_("Bad sh_link in group section `%s'\n"), name); continue; } if (symtab_sec != sec) { symtab_sec = sec; if (symtab) free (symtab); symtab = GET_ELF_SYMBOLS (file, symtab_sec); } sym = symtab + section->sh_info; if (ELF_ST_TYPE (sym->st_info) == STT_SECTION) { bfd_vma sec_index = SECTION_HEADER_INDEX (sym->st_shndx); if (sec_index == 0) { error (_("Bad sh_info in group section `%s'\n"), name); continue; } group_name = SECTION_NAME (section_headers + sec_index); strtab_sec = NULL; if (strtab) free (strtab); strtab = NULL; strtab_size = 0; } else { /* Get the string table. */ if (SECTION_HEADER_INDEX (symtab_sec->sh_link) >= elf_header.e_shnum) { strtab_sec = NULL; if (strtab) free (strtab); strtab = NULL; strtab_size = 0; } else if (strtab_sec != (sec = SECTION_HEADER (symtab_sec->sh_link))) { strtab_sec = sec; if (strtab) free (strtab); strtab = get_data (NULL, file, strtab_sec->sh_offset, 1, strtab_sec->sh_size, _("string table")); strtab_size = strtab != NULL ? strtab_sec->sh_size : 0; } group_name = sym->st_name < strtab_size ? strtab + sym->st_name : ""; } start = get_data (NULL, file, section->sh_offset, 1, section->sh_size, _("section data")); indices = start; size = (section->sh_size / section->sh_entsize) - 1; entry = byte_get (indices, 4); indices += 4; if (do_section_groups) { printf ("\n%s group section [%5u] `%s' [%s] contains %u sections:\n", get_group_flags (entry), i, name, group_name, size); printf (_(" [Index] Name\n")); } group->group_index = i; for (j = 0; j < size; j++) { struct group_list *g; entry = byte_get (indices, 4); indices += 4; if (SECTION_HEADER_INDEX (entry) >= elf_header.e_shnum) { error (_("section [%5u] in group section [%5u] > maximum section [%5u]\n"), entry, i, elf_header.e_shnum - 1); continue; } else if (entry >= SHN_LORESERVE && entry <= SHN_HIRESERVE) { error (_("invalid section [%5u] in group section [%5u]\n"), entry, i); continue; } if (section_headers_groups [SECTION_HEADER_INDEX (entry)] != NULL) { if (entry) { error (_("section [%5u] in group section [%5u] already in group section [%5u]\n"), entry, i, section_headers_groups [SECTION_HEADER_INDEX (entry)]->group_index); continue; } else { /* Intel C/C++ compiler may put section 0 in a section group. We just warn it the first time and ignore it afterwards. */ static int warned = 0; if (!warned) { error (_("section 0 in group section [%5u]\n"), section_headers_groups [SECTION_HEADER_INDEX (entry)]->group_index); warned++; } } } section_headers_groups [SECTION_HEADER_INDEX (entry)] = group; if (do_section_groups) { sec = SECTION_HEADER (entry); printf (" [%5u] %s\n", entry, SECTION_NAME (sec)); } g = xmalloc (sizeof (struct group_list)); g->section_index = entry; g->next = group->root; group->root = g; } if (start) free (start); group++; } } if (symtab) free (symtab); if (strtab) free (strtab); return 1; } static struct { const char *name; int reloc; int size; int rela; } dynamic_relocations [] = { { "REL", DT_REL, DT_RELSZ, FALSE }, { "RELA", DT_RELA, DT_RELASZ, TRUE }, { "PLT", DT_JMPREL, DT_PLTRELSZ, UNKNOWN } }; /* Process the reloc section. */ static int process_relocs (FILE *file) { unsigned long rel_size; unsigned long rel_offset; if (!do_reloc) return 1; if (do_using_dynamic) { int is_rela; const char *name; int has_dynamic_reloc; unsigned int i; has_dynamic_reloc = 0; for (i = 0; i < ARRAY_SIZE (dynamic_relocations); i++) { is_rela = dynamic_relocations [i].rela; name = dynamic_relocations [i].name; rel_size = dynamic_info [dynamic_relocations [i].size]; rel_offset = dynamic_info [dynamic_relocations [i].reloc]; has_dynamic_reloc |= rel_size; if (is_rela == UNKNOWN) { if (dynamic_relocations [i].reloc == DT_JMPREL) switch (dynamic_info[DT_PLTREL]) { case DT_REL: is_rela = FALSE; break; case DT_RELA: is_rela = TRUE; break; } } if (rel_size) { printf (_("\n'%s' relocation section at offset 0x%lx contains %ld bytes:\n"), name, rel_offset, rel_size); dump_relocations (file, offset_from_vma (file, rel_offset, rel_size), rel_size, dynamic_symbols, num_dynamic_syms, dynamic_strings, dynamic_strings_length, is_rela); } } if (! has_dynamic_reloc) printf (_("\nThere are no dynamic relocations in this file.\n")); } else { Elf_Internal_Shdr *section; unsigned long i; int found = 0; for (i = 0, section = section_headers; i < elf_header.e_shnum; i++, section++) { if ( section->sh_type != SHT_RELA && section->sh_type != SHT_REL) continue; rel_offset = section->sh_offset; rel_size = section->sh_size; if (rel_size) { Elf_Internal_Shdr *strsec; int is_rela; printf (_("\nRelocation section ")); if (string_table == NULL) printf ("%d", section->sh_name); else printf (_("'%s'"), SECTION_NAME (section)); printf (_(" at offset 0x%lx contains %lu entries:\n"), rel_offset, (unsigned long) (rel_size / section->sh_entsize)); is_rela = section->sh_type == SHT_RELA; if (section->sh_link && SECTION_HEADER_INDEX (section->sh_link) < elf_header.e_shnum) { Elf_Internal_Shdr *symsec; Elf_Internal_Sym *symtab; unsigned long nsyms; unsigned long strtablen = 0; char *strtab = NULL; symsec = SECTION_HEADER (section->sh_link); if (symsec->sh_type != SHT_SYMTAB && symsec->sh_type != SHT_DYNSYM) continue; nsyms = symsec->sh_size / symsec->sh_entsize; symtab = GET_ELF_SYMBOLS (file, symsec); if (symtab == NULL) continue; if (SECTION_HEADER_INDEX (symsec->sh_link) < elf_header.e_shnum) { strsec = SECTION_HEADER (symsec->sh_link); strtab = get_data (NULL, file, strsec->sh_offset, 1, strsec->sh_size, _("string table")); strtablen = strtab == NULL ? 0 : strsec->sh_size; } dump_relocations (file, rel_offset, rel_size, symtab, nsyms, strtab, strtablen, is_rela); if (strtab) free (strtab); free (symtab); } else dump_relocations (file, rel_offset, rel_size, NULL, 0, NULL, 0, is_rela); found = 1; } } if (! found) printf (_("\nThere are no relocations in this file.\n")); } return 1; } /* Process the unwind section. */ #include "unwind-ia64.h" /* An absolute address consists of a section and an offset. If the section is NULL, the offset itself is the address, otherwise, the address equals to LOAD_ADDRESS(section) + offset. */ struct absaddr { unsigned short section; bfd_vma offset; }; #define ABSADDR(a) \ ((a).section \ ? section_headers [(a).section].sh_addr + (a).offset \ : (a).offset) struct ia64_unw_aux_info { struct ia64_unw_table_entry { struct absaddr start; struct absaddr end; struct absaddr info; } *table; /* Unwind table. */ unsigned long table_len; /* Length of unwind table. */ unsigned char *info; /* Unwind info. */ unsigned long info_size; /* Size of unwind info. */ bfd_vma info_addr; /* starting address of unwind info. */ bfd_vma seg_base; /* Starting address of segment. */ Elf_Internal_Sym *symtab; /* The symbol table. */ unsigned long nsyms; /* Number of symbols. */ char *strtab; /* The string table. */ unsigned long strtab_size; /* Size of string table. */ }; static void find_symbol_for_address (Elf_Internal_Sym *symtab, unsigned long nsyms, const char *strtab, unsigned long strtab_size, struct absaddr addr, const char **symname, bfd_vma *offset) { bfd_vma dist = 0x100000; Elf_Internal_Sym *sym, *best = NULL; unsigned long i; for (i = 0, sym = symtab; i < nsyms; ++i, ++sym) { if (ELF_ST_TYPE (sym->st_info) == STT_FUNC && sym->st_name != 0 && (addr.section == SHN_UNDEF || addr.section == sym->st_shndx) && addr.offset >= sym->st_value && addr.offset - sym->st_value < dist) { best = sym; dist = addr.offset - sym->st_value; if (!dist) break; } } if (best) { *symname = (best->st_name >= strtab_size ? "" : strtab + best->st_name); *offset = dist; return; } *symname = NULL; *offset = addr.offset; } static void dump_ia64_unwind (struct ia64_unw_aux_info *aux) { struct ia64_unw_table_entry *tp; int in_body; for (tp = aux->table; tp < aux->table + aux->table_len; ++tp) { bfd_vma stamp; bfd_vma offset; const unsigned char *dp; const unsigned char *head; const char *procname; find_symbol_for_address (aux->symtab, aux->nsyms, aux->strtab, aux->strtab_size, tp->start, &procname, &offset); fputs ("\n<", stdout); if (procname) { fputs (procname, stdout); if (offset) printf ("+%lx", (unsigned long) offset); } fputs (">: [", stdout); print_vma (tp->start.offset, PREFIX_HEX); fputc ('-', stdout); print_vma (tp->end.offset, PREFIX_HEX); printf ("], info at +0x%lx\n", (unsigned long) (tp->info.offset - aux->seg_base)); head = aux->info + (ABSADDR (tp->info) - aux->info_addr); stamp = byte_get ((unsigned char *) head, sizeof (stamp)); printf (" v%u, flags=0x%lx (%s%s), len=%lu bytes\n", (unsigned) UNW_VER (stamp), (unsigned long) ((stamp & UNW_FLAG_MASK) >> 32), UNW_FLAG_EHANDLER (stamp) ? " ehandler" : "", UNW_FLAG_UHANDLER (stamp) ? " uhandler" : "", (unsigned long) (eh_addr_size * UNW_LENGTH (stamp))); if (UNW_VER (stamp) != 1) { printf ("\tUnknown version.\n"); continue; } in_body = 0; for (dp = head + 8; dp < head + 8 + eh_addr_size * UNW_LENGTH (stamp);) dp = unw_decode (dp, in_body, & in_body); } } static int slurp_ia64_unwind_table (FILE *file, struct ia64_unw_aux_info *aux, Elf_Internal_Shdr *sec) { unsigned long size, nrelas, i; Elf_Internal_Phdr *seg; struct ia64_unw_table_entry *tep; Elf_Internal_Shdr *relsec; Elf_Internal_Rela *rela, *rp; unsigned char *table, *tp; Elf_Internal_Sym *sym; const char *relname; /* First, find the starting address of the segment that includes this section: */ if (elf_header.e_phnum) { if (! get_program_headers (file)) return 0; for (seg = program_headers; seg < program_headers + elf_header.e_phnum; ++seg) { if (seg->p_type != PT_LOAD) continue; if (sec->sh_addr >= seg->p_vaddr && (sec->sh_addr + sec->sh_size <= seg->p_vaddr + seg->p_memsz)) { aux->seg_base = seg->p_vaddr; break; } } } /* Second, build the unwind table from the contents of the unwind section: */ size = sec->sh_size; table = get_data (NULL, file, sec->sh_offset, 1, size, _("unwind table")); if (!table) return 0; aux->table = xcmalloc (size / (3 * eh_addr_size), sizeof (aux->table[0])); tep = aux->table; for (tp = table; tp < table + size; tp += 3 * eh_addr_size, ++tep) { tep->start.section = SHN_UNDEF; tep->end.section = SHN_UNDEF; tep->info.section = SHN_UNDEF; if (is_32bit_elf) { tep->start.offset = byte_get ((unsigned char *) tp + 0, 4); tep->end.offset = byte_get ((unsigned char *) tp + 4, 4); tep->info.offset = byte_get ((unsigned char *) tp + 8, 4); } else { tep->start.offset = BYTE_GET ((unsigned char *) tp + 0); tep->end.offset = BYTE_GET ((unsigned char *) tp + 8); tep->info.offset = BYTE_GET ((unsigned char *) tp + 16); } tep->start.offset += aux->seg_base; tep->end.offset += aux->seg_base; tep->info.offset += aux->seg_base; } free (table); /* Third, apply any relocations to the unwind table: */ for (relsec = section_headers; relsec < section_headers + elf_header.e_shnum; ++relsec) { if (relsec->sh_type != SHT_RELA || SECTION_HEADER_INDEX (relsec->sh_info) >= elf_header.e_shnum || SECTION_HEADER (relsec->sh_info) != sec) continue; if (!slurp_rela_relocs (file, relsec->sh_offset, relsec->sh_size, & rela, & nrelas)) return 0; for (rp = rela; rp < rela + nrelas; ++rp) { if (is_32bit_elf) { relname = elf_ia64_reloc_type (ELF32_R_TYPE (rp->r_info)); sym = aux->symtab + ELF32_R_SYM (rp->r_info); } else { relname = elf_ia64_reloc_type (ELF64_R_TYPE (rp->r_info)); sym = aux->symtab + ELF64_R_SYM (rp->r_info); } if (! const_strneq (relname, "R_IA64_SEGREL")) { warn (_("Skipping unexpected relocation type %s\n"), relname); continue; } i = rp->r_offset / (3 * eh_addr_size); switch (rp->r_offset/eh_addr_size % 3) { case 0: aux->table[i].start.section = sym->st_shndx; aux->table[i].start.offset += rp->r_addend + sym->st_value; break; case 1: aux->table[i].end.section = sym->st_shndx; aux->table[i].end.offset += rp->r_addend + sym->st_value; break; case 2: aux->table[i].info.section = sym->st_shndx; aux->table[i].info.offset += rp->r_addend + sym->st_value; break; default: break; } } free (rela); } aux->table_len = size / (3 * eh_addr_size); return 1; } static int ia64_process_unwind (FILE *file) { Elf_Internal_Shdr *sec, *unwsec = NULL, *strsec; unsigned long i, unwcount = 0, unwstart = 0; struct ia64_unw_aux_info aux; memset (& aux, 0, sizeof (aux)); for (i = 0, sec = section_headers; i < elf_header.e_shnum; ++i, ++sec) { if (sec->sh_type == SHT_SYMTAB && SECTION_HEADER_INDEX (sec->sh_link) < elf_header.e_shnum) { aux.nsyms = sec->sh_size / sec->sh_entsize; aux.symtab = GET_ELF_SYMBOLS (file, sec); strsec = SECTION_HEADER (sec->sh_link); aux.strtab = get_data (NULL, file, strsec->sh_offset, 1, strsec->sh_size, _("string table")); aux.strtab_size = aux.strtab != NULL ? strsec->sh_size : 0; } else if (sec->sh_type == SHT_IA_64_UNWIND) unwcount++; } if (!unwcount) printf (_("\nThere are no unwind sections in this file.\n")); while (unwcount-- > 0) { char *suffix; size_t len, len2; for (i = unwstart, sec = section_headers + unwstart; i < elf_header.e_shnum; ++i, ++sec) if (sec->sh_type == SHT_IA_64_UNWIND) { unwsec = sec; break; } unwstart = i + 1; len = sizeof (ELF_STRING_ia64_unwind_once) - 1; if ((unwsec->sh_flags & SHF_GROUP) != 0) { /* We need to find which section group it is in. */ struct group_list *g = section_headers_groups [i]->root; for (; g != NULL; g = g->next) { sec = SECTION_HEADER (g->section_index); if (streq (SECTION_NAME (sec), ELF_STRING_ia64_unwind_info)) break; } if (g == NULL) i = elf_header.e_shnum; } else if (strneq (SECTION_NAME (unwsec), ELF_STRING_ia64_unwind_once, len)) { /* .gnu.linkonce.ia64unw.FOO -> .gnu.linkonce.ia64unwi.FOO. */ len2 = sizeof (ELF_STRING_ia64_unwind_info_once) - 1; suffix = SECTION_NAME (unwsec) + len; for (i = 0, sec = section_headers; i < elf_header.e_shnum; ++i, ++sec) if (strneq (SECTION_NAME (sec), ELF_STRING_ia64_unwind_info_once, len2) && streq (SECTION_NAME (sec) + len2, suffix)) break; } else { /* .IA_64.unwindFOO -> .IA_64.unwind_infoFOO .IA_64.unwind or BAR -> .IA_64.unwind_info. */ len = sizeof (ELF_STRING_ia64_unwind) - 1; len2 = sizeof (ELF_STRING_ia64_unwind_info) - 1; suffix = ""; if (strneq (SECTION_NAME (unwsec), ELF_STRING_ia64_unwind, len)) suffix = SECTION_NAME (unwsec) + len; for (i = 0, sec = section_headers; i < elf_header.e_shnum; ++i, ++sec) if (strneq (SECTION_NAME (sec), ELF_STRING_ia64_unwind_info, len2) && streq (SECTION_NAME (sec) + len2, suffix)) break; } if (i == elf_header.e_shnum) { printf (_("\nCould not find unwind info section for ")); if (string_table == NULL) printf ("%d", unwsec->sh_name); else printf (_("'%s'"), SECTION_NAME (unwsec)); } else { aux.info_size = sec->sh_size; aux.info_addr = sec->sh_addr; aux.info = get_data (NULL, file, sec->sh_offset, 1, aux.info_size, _("unwind info")); printf (_("\nUnwind section ")); if (string_table == NULL) printf ("%d", unwsec->sh_name); else printf (_("'%s'"), SECTION_NAME (unwsec)); printf (_(" at offset 0x%lx contains %lu entries:\n"), (unsigned long) unwsec->sh_offset, (unsigned long) (unwsec->sh_size / (3 * eh_addr_size))); (void) slurp_ia64_unwind_table (file, & aux, unwsec); if (aux.table_len > 0) dump_ia64_unwind (& aux); if (aux.table) free ((char *) aux.table); if (aux.info) free ((char *) aux.info); aux.table = NULL; aux.info = NULL; } } if (aux.symtab) free (aux.symtab); if (aux.strtab) free ((char *) aux.strtab); return 1; } struct hppa_unw_aux_info { struct hppa_unw_table_entry { struct absaddr start; struct absaddr end; unsigned int Cannot_unwind:1; /* 0 */ unsigned int Millicode:1; /* 1 */ unsigned int Millicode_save_sr0:1; /* 2 */ unsigned int Region_description:2; /* 3..4 */ unsigned int reserved1:1; /* 5 */ unsigned int Entry_SR:1; /* 6 */ unsigned int Entry_FR:4; /* number saved */ /* 7..10 */ unsigned int Entry_GR:5; /* number saved */ /* 11..15 */ unsigned int Args_stored:1; /* 16 */ unsigned int Variable_Frame:1; /* 17 */ unsigned int Separate_Package_Body:1; /* 18 */ unsigned int Frame_Extension_Millicode:1; /* 19 */ unsigned int Stack_Overflow_Check:1; /* 20 */ unsigned int Two_Instruction_SP_Increment:1; /* 21 */ unsigned int Ada_Region:1; /* 22 */ unsigned int cxx_info:1; /* 23 */ unsigned int cxx_try_catch:1; /* 24 */ unsigned int sched_entry_seq:1; /* 25 */ unsigned int reserved2:1; /* 26 */ unsigned int Save_SP:1; /* 27 */ unsigned int Save_RP:1; /* 28 */ unsigned int Save_MRP_in_frame:1; /* 29 */ unsigned int extn_ptr_defined:1; /* 30 */ unsigned int Cleanup_defined:1; /* 31 */ unsigned int MPE_XL_interrupt_marker:1; /* 0 */ unsigned int HP_UX_interrupt_marker:1; /* 1 */ unsigned int Large_frame:1; /* 2 */ unsigned int Pseudo_SP_Set:1; /* 3 */ unsigned int reserved4:1; /* 4 */ unsigned int Total_frame_size:27; /* 5..31 */ } *table; /* Unwind table. */ unsigned long table_len; /* Length of unwind table. */ bfd_vma seg_base; /* Starting address of segment. */ Elf_Internal_Sym *symtab; /* The symbol table. */ unsigned long nsyms; /* Number of symbols. */ char *strtab; /* The string table. */ unsigned long strtab_size; /* Size of string table. */ }; static void dump_hppa_unwind (struct hppa_unw_aux_info *aux) { struct hppa_unw_table_entry *tp; for (tp = aux->table; tp < aux->table + aux->table_len; ++tp) { bfd_vma offset; const char *procname; find_symbol_for_address (aux->symtab, aux->nsyms, aux->strtab, aux->strtab_size, tp->start, &procname, &offset); fputs ("\n<", stdout); if (procname) { fputs (procname, stdout); if (offset) printf ("+%lx", (unsigned long) offset); } fputs (">: [", stdout); print_vma (tp->start.offset, PREFIX_HEX); fputc ('-', stdout); print_vma (tp->end.offset, PREFIX_HEX); printf ("]\n\t"); #define PF(_m) if (tp->_m) printf (#_m " "); #define PV(_m) if (tp->_m) printf (#_m "=%d ", tp->_m); PF(Cannot_unwind); PF(Millicode); PF(Millicode_save_sr0); /* PV(Region_description); */ PF(Entry_SR); PV(Entry_FR); PV(Entry_GR); PF(Args_stored); PF(Variable_Frame); PF(Separate_Package_Body); PF(Frame_Extension_Millicode); PF(Stack_Overflow_Check); PF(Two_Instruction_SP_Increment); PF(Ada_Region); PF(cxx_info); PF(cxx_try_catch); PF(sched_entry_seq); PF(Save_SP); PF(Save_RP); PF(Save_MRP_in_frame); PF(extn_ptr_defined); PF(Cleanup_defined); PF(MPE_XL_interrupt_marker); PF(HP_UX_interrupt_marker); PF(Large_frame); PF(Pseudo_SP_Set); PV(Total_frame_size); #undef PF #undef PV } printf ("\n"); } static int slurp_hppa_unwind_table (FILE *file, struct hppa_unw_aux_info *aux, Elf_Internal_Shdr *sec) { unsigned long size, unw_ent_size, nentries, nrelas, i; Elf_Internal_Phdr *seg; struct hppa_unw_table_entry *tep; Elf_Internal_Shdr *relsec; Elf_Internal_Rela *rela, *rp; unsigned char *table, *tp; Elf_Internal_Sym *sym; const char *relname; /* First, find the starting address of the segment that includes this section. */ if (elf_header.e_phnum) { if (! get_program_headers (file)) return 0; for (seg = program_headers; seg < program_headers + elf_header.e_phnum; ++seg) { if (seg->p_type != PT_LOAD) continue; if (sec->sh_addr >= seg->p_vaddr && (sec->sh_addr + sec->sh_size <= seg->p_vaddr + seg->p_memsz)) { aux->seg_base = seg->p_vaddr; break; } } } /* Second, build the unwind table from the contents of the unwind section. */ size = sec->sh_size; table = get_data (NULL, file, sec->sh_offset, 1, size, _("unwind table")); if (!table) return 0; unw_ent_size = 16; nentries = size / unw_ent_size; size = unw_ent_size * nentries; tep = aux->table = xcmalloc (nentries, sizeof (aux->table[0])); for (tp = table; tp < table + size; tp += unw_ent_size, ++tep) { unsigned int tmp1, tmp2; tep->start.section = SHN_UNDEF; tep->end.section = SHN_UNDEF; tep->start.offset = byte_get ((unsigned char *) tp + 0, 4); tep->end.offset = byte_get ((unsigned char *) tp + 4, 4); tmp1 = byte_get ((unsigned char *) tp + 8, 4); tmp2 = byte_get ((unsigned char *) tp + 12, 4); tep->start.offset += aux->seg_base; tep->end.offset += aux->seg_base; tep->Cannot_unwind = (tmp1 >> 31) & 0x1; tep->Millicode = (tmp1 >> 30) & 0x1; tep->Millicode_save_sr0 = (tmp1 >> 29) & 0x1; tep->Region_description = (tmp1 >> 27) & 0x3; tep->reserved1 = (tmp1 >> 26) & 0x1; tep->Entry_SR = (tmp1 >> 25) & 0x1; tep->Entry_FR = (tmp1 >> 21) & 0xf; tep->Entry_GR = (tmp1 >> 16) & 0x1f; tep->Args_stored = (tmp1 >> 15) & 0x1; tep->Variable_Frame = (tmp1 >> 14) & 0x1; tep->Separate_Package_Body = (tmp1 >> 13) & 0x1; tep->Frame_Extension_Millicode = (tmp1 >> 12) & 0x1; tep->Stack_Overflow_Check = (tmp1 >> 11) & 0x1; tep->Two_Instruction_SP_Increment = (tmp1 >> 10) & 0x1; tep->Ada_Region = (tmp1 >> 9) & 0x1; tep->cxx_info = (tmp1 >> 8) & 0x1; tep->cxx_try_catch = (tmp1 >> 7) & 0x1; tep->sched_entry_seq = (tmp1 >> 6) & 0x1; tep->reserved2 = (tmp1 >> 5) & 0x1; tep->Save_SP = (tmp1 >> 4) & 0x1; tep->Save_RP = (tmp1 >> 3) & 0x1; tep->Save_MRP_in_frame = (tmp1 >> 2) & 0x1; tep->extn_ptr_defined = (tmp1 >> 1) & 0x1; tep->Cleanup_defined = tmp1 & 0x1; tep->MPE_XL_interrupt_marker = (tmp2 >> 31) & 0x1; tep->HP_UX_interrupt_marker = (tmp2 >> 30) & 0x1; tep->Large_frame = (tmp2 >> 29) & 0x1; tep->Pseudo_SP_Set = (tmp2 >> 28) & 0x1; tep->reserved4 = (tmp2 >> 27) & 0x1; tep->Total_frame_size = tmp2 & 0x7ffffff; } free (table); /* Third, apply any relocations to the unwind table. */ for (relsec = section_headers; relsec < section_headers + elf_header.e_shnum; ++relsec) { if (relsec->sh_type != SHT_RELA || SECTION_HEADER_INDEX (relsec->sh_info) >= elf_header.e_shnum || SECTION_HEADER (relsec->sh_info) != sec) continue; if (!slurp_rela_relocs (file, relsec->sh_offset, relsec->sh_size, & rela, & nrelas)) return 0; for (rp = rela; rp < rela + nrelas; ++rp) { if (is_32bit_elf) { relname = elf_hppa_reloc_type (ELF32_R_TYPE (rp->r_info)); sym = aux->symtab + ELF32_R_SYM (rp->r_info); } else { relname = elf_hppa_reloc_type (ELF64_R_TYPE (rp->r_info)); sym = aux->symtab + ELF64_R_SYM (rp->r_info); } /* R_PARISC_SEGREL32 or R_PARISC_SEGREL64. */ if (! const_strneq (relname, "R_PARISC_SEGREL")) { warn (_("Skipping unexpected relocation type %s\n"), relname); continue; } i = rp->r_offset / unw_ent_size; switch ((rp->r_offset % unw_ent_size) / eh_addr_size) { case 0: aux->table[i].start.section = sym->st_shndx; aux->table[i].start.offset += sym->st_value + rp->r_addend; break; case 1: aux->table[i].end.section = sym->st_shndx; aux->table[i].end.offset += sym->st_value + rp->r_addend; break; default: break; } } free (rela); } aux->table_len = nentries; return 1; } static int hppa_process_unwind (FILE *file) { struct hppa_unw_aux_info aux; Elf_Internal_Shdr *unwsec = NULL; Elf_Internal_Shdr *strsec; Elf_Internal_Shdr *sec; unsigned long i; memset (& aux, 0, sizeof (aux)); if (string_table == NULL) return 1; for (i = 0, sec = section_headers; i < elf_header.e_shnum; ++i, ++sec) { if (sec->sh_type == SHT_SYMTAB && SECTION_HEADER_INDEX (sec->sh_link) < elf_header.e_shnum) { aux.nsyms = sec->sh_size / sec->sh_entsize; aux.symtab = GET_ELF_SYMBOLS (file, sec); strsec = SECTION_HEADER (sec->sh_link); aux.strtab = get_data (NULL, file, strsec->sh_offset, 1, strsec->sh_size, _("string table")); aux.strtab_size = aux.strtab != NULL ? strsec->sh_size : 0; } else if (streq (SECTION_NAME (sec), ".PARISC.unwind")) unwsec = sec; } if (!unwsec) printf (_("\nThere are no unwind sections in this file.\n")); for (i = 0, sec = section_headers; i < elf_header.e_shnum; ++i, ++sec) { if (streq (SECTION_NAME (sec), ".PARISC.unwind")) { printf (_("\nUnwind section ")); printf (_("'%s'"), SECTION_NAME (sec)); printf (_(" at offset 0x%lx contains %lu entries:\n"), (unsigned long) sec->sh_offset, (unsigned long) (sec->sh_size / (2 * eh_addr_size + 8))); slurp_hppa_unwind_table (file, &aux, sec); if (aux.table_len > 0) dump_hppa_unwind (&aux); if (aux.table) free ((char *) aux.table); aux.table = NULL; } } if (aux.symtab) free (aux.symtab); if (aux.strtab) free ((char *) aux.strtab); return 1; } static int process_unwind (FILE *file) { struct unwind_handler { int machtype; int (*handler)(FILE *file); } handlers[] = { { EM_IA_64, ia64_process_unwind }, { EM_PARISC, hppa_process_unwind }, { 0, 0 } }; int i; if (!do_unwind) return 1; for (i = 0; handlers[i].handler != NULL; i++) if (elf_header.e_machine == handlers[i].machtype) return handlers[i].handler (file); printf (_("\nThere are no unwind sections in this file.\n")); return 1; } static void dynamic_section_mips_val (Elf_Internal_Dyn *entry) { switch (entry->d_tag) { case DT_MIPS_FLAGS: if (entry->d_un.d_val == 0) printf ("NONE\n"); else { static const char * opts[] = { "QUICKSTART", "NOTPOT", "NO_LIBRARY_REPLACEMENT", "NO_MOVE", "SGI_ONLY", "GUARANTEE_INIT", "DELTA_C_PLUS_PLUS", "GUARANTEE_START_INIT", "PIXIE", "DEFAULT_DELAY_LOAD", "REQUICKSTART", "REQUICKSTARTED", "CORD", "NO_UNRES_UNDEF", "RLD_ORDER_SAFE" }; unsigned int cnt; int first = 1; for (cnt = 0; cnt < NUM_ELEM (opts); ++cnt) if (entry->d_un.d_val & (1 << cnt)) { printf ("%s%s", first ? "" : " ", opts[cnt]); first = 0; } puts (""); } break; case DT_MIPS_IVERSION: if (VALID_DYNAMIC_NAME (entry->d_un.d_val)) printf ("Interface Version: %s\n", GET_DYNAMIC_NAME (entry->d_un.d_val)); else printf ("\n", (long) entry->d_un.d_ptr); break; case DT_MIPS_TIME_STAMP: { char timebuf[20]; struct tm *tmp; time_t time = entry->d_un.d_val; tmp = gmtime (&time); snprintf (timebuf, sizeof (timebuf), "%04u-%02u-%02uT%02u:%02u:%02u", tmp->tm_year + 1900, tmp->tm_mon + 1, tmp->tm_mday, tmp->tm_hour, tmp->tm_min, tmp->tm_sec); printf ("Time Stamp: %s\n", timebuf); } break; case DT_MIPS_RLD_VERSION: case DT_MIPS_LOCAL_GOTNO: case DT_MIPS_CONFLICTNO: case DT_MIPS_LIBLISTNO: case DT_MIPS_SYMTABNO: case DT_MIPS_UNREFEXTNO: case DT_MIPS_HIPAGENO: case DT_MIPS_DELTA_CLASS_NO: case DT_MIPS_DELTA_INSTANCE_NO: case DT_MIPS_DELTA_RELOC_NO: case DT_MIPS_DELTA_SYM_NO: case DT_MIPS_DELTA_CLASSSYM_NO: case DT_MIPS_COMPACT_SIZE: printf ("%ld\n", (long) entry->d_un.d_ptr); break; default: printf ("%#lx\n", (long) entry->d_un.d_ptr); } } static void dynamic_section_parisc_val (Elf_Internal_Dyn *entry) { switch (entry->d_tag) { case DT_HP_DLD_FLAGS: { static struct { long int bit; const char *str; } flags[] = { { DT_HP_DEBUG_PRIVATE, "HP_DEBUG_PRIVATE" }, { DT_HP_DEBUG_CALLBACK, "HP_DEBUG_CALLBACK" }, { DT_HP_DEBUG_CALLBACK_BOR, "HP_DEBUG_CALLBACK_BOR" }, { DT_HP_NO_ENVVAR, "HP_NO_ENVVAR" }, { DT_HP_BIND_NOW, "HP_BIND_NOW" }, { DT_HP_BIND_NONFATAL, "HP_BIND_NONFATAL" }, { DT_HP_BIND_VERBOSE, "HP_BIND_VERBOSE" }, { DT_HP_BIND_RESTRICTED, "HP_BIND_RESTRICTED" }, { DT_HP_BIND_SYMBOLIC, "HP_BIND_SYMBOLIC" }, { DT_HP_RPATH_FIRST, "HP_RPATH_FIRST" }, { DT_HP_BIND_DEPTH_FIRST, "HP_BIND_DEPTH_FIRST" }, { DT_HP_GST, "HP_GST" }, { DT_HP_SHLIB_FIXED, "HP_SHLIB_FIXED" }, { DT_HP_MERGE_SHLIB_SEG, "HP_MERGE_SHLIB_SEG" }, { DT_HP_NODELETE, "HP_NODELETE" }, { DT_HP_GROUP, "HP_GROUP" }, { DT_HP_PROTECT_LINKAGE_TABLE, "HP_PROTECT_LINKAGE_TABLE" } }; int first = 1; size_t cnt; bfd_vma val = entry->d_un.d_val; for (cnt = 0; cnt < sizeof (flags) / sizeof (flags[0]); ++cnt) if (val & flags[cnt].bit) { if (! first) putchar (' '); fputs (flags[cnt].str, stdout); first = 0; val ^= flags[cnt].bit; } if (val != 0 || first) { if (! first) putchar (' '); print_vma (val, HEX); } } break; default: print_vma (entry->d_un.d_ptr, PREFIX_HEX); break; } putchar ('\n'); } static void dynamic_section_ia64_val (Elf_Internal_Dyn *entry) { switch (entry->d_tag) { case DT_IA_64_PLT_RESERVE: /* First 3 slots reserved. */ print_vma (entry->d_un.d_ptr, PREFIX_HEX); printf (" -- "); print_vma (entry->d_un.d_ptr + (3 * 8), PREFIX_HEX); break; default: print_vma (entry->d_un.d_ptr, PREFIX_HEX); break; } putchar ('\n'); } static int get_32bit_dynamic_section (FILE *file) { Elf32_External_Dyn *edyn, *ext; Elf_Internal_Dyn *entry; edyn = get_data (NULL, file, dynamic_addr, 1, dynamic_size, _("dynamic section")); if (!edyn) return 0; /* SGI's ELF has more than one section in the DYNAMIC segment, and we might not have the luxury of section headers. Look for the DT_NULL terminator to determine the number of entries. */ for (ext = edyn, dynamic_nent = 0; (char *) ext < (char *) edyn + dynamic_size; ext++) { dynamic_nent++; if (BYTE_GET (ext->d_tag) == DT_NULL) break; } dynamic_section = cmalloc (dynamic_nent, sizeof (*entry)); if (dynamic_section == NULL) { error (_("Out of memory\n")); free (edyn); return 0; } for (ext = edyn, entry = dynamic_section; entry < dynamic_section + dynamic_nent; ext++, entry++) { entry->d_tag = BYTE_GET (ext->d_tag); entry->d_un.d_val = BYTE_GET (ext->d_un.d_val); } free (edyn); return 1; } static int get_64bit_dynamic_section (FILE *file) { Elf64_External_Dyn *edyn, *ext; Elf_Internal_Dyn *entry; edyn = get_data (NULL, file, dynamic_addr, 1, dynamic_size, _("dynamic section")); if (!edyn) return 0; /* SGI's ELF has more than one section in the DYNAMIC segment, and we might not have the luxury of section headers. Look for the DT_NULL terminator to determine the number of entries. */ for (ext = edyn, dynamic_nent = 0; (char *) ext < (char *) edyn + dynamic_size; ext++) { dynamic_nent++; if (BYTE_GET (ext->d_tag) == DT_NULL) break; } dynamic_section = cmalloc (dynamic_nent, sizeof (*entry)); if (dynamic_section == NULL) { error (_("Out of memory\n")); free (edyn); return 0; } for (ext = edyn, entry = dynamic_section; entry < dynamic_section + dynamic_nent; ext++, entry++) { entry->d_tag = BYTE_GET (ext->d_tag); entry->d_un.d_val = BYTE_GET (ext->d_un.d_val); } free (edyn); return 1; } static void print_dynamic_flags (bfd_vma flags) { int first = 1; while (flags) { bfd_vma flag; flag = flags & - flags; flags &= ~ flag; if (first) first = 0; else putc (' ', stdout); switch (flag) { case DF_ORIGIN: fputs ("ORIGIN", stdout); break; case DF_SYMBOLIC: fputs ("SYMBOLIC", stdout); break; case DF_TEXTREL: fputs ("TEXTREL", stdout); break; case DF_BIND_NOW: fputs ("BIND_NOW", stdout); break; case DF_STATIC_TLS: fputs ("STATIC_TLS", stdout); break; default: fputs ("unknown", stdout); break; } } puts (""); } /* Parse and display the contents of the dynamic section. */ static int process_dynamic_section (FILE *file) { Elf_Internal_Dyn *entry; if (dynamic_size == 0) { if (do_dynamic) printf (_("\nThere is no dynamic section in this file.\n")); return 1; } if (is_32bit_elf) { if (! get_32bit_dynamic_section (file)) return 0; } else if (! get_64bit_dynamic_section (file)) return 0; /* Find the appropriate symbol table. */ if (dynamic_symbols == NULL) { for (entry = dynamic_section; entry < dynamic_section + dynamic_nent; ++entry) { Elf_Internal_Shdr section; if (entry->d_tag != DT_SYMTAB) continue; dynamic_info[DT_SYMTAB] = entry->d_un.d_val; /* Since we do not know how big the symbol table is, we default to reading in the entire file (!) and processing that. This is overkill, I know, but it should work. */ section.sh_offset = offset_from_vma (file, entry->d_un.d_val, 0); if (archive_file_offset != 0) section.sh_size = archive_file_size - section.sh_offset; else { if (fseek (file, 0, SEEK_END)) error (_("Unable to seek to end of file!\n")); section.sh_size = ftell (file) - section.sh_offset; } if (is_32bit_elf) section.sh_entsize = sizeof (Elf32_External_Sym); else section.sh_entsize = sizeof (Elf64_External_Sym); num_dynamic_syms = section.sh_size / section.sh_entsize; if (num_dynamic_syms < 1) { error (_("Unable to determine the number of symbols to load\n")); continue; } dynamic_symbols = GET_ELF_SYMBOLS (file, §ion); } } /* Similarly find a string table. */ if (dynamic_strings == NULL) { for (entry = dynamic_section; entry < dynamic_section + dynamic_nent; ++entry) { unsigned long offset; long str_tab_len; if (entry->d_tag != DT_STRTAB) continue; dynamic_info[DT_STRTAB] = entry->d_un.d_val; /* Since we do not know how big the string table is, we default to reading in the entire file (!) and processing that. This is overkill, I know, but it should work. */ offset = offset_from_vma (file, entry->d_un.d_val, 0); if (archive_file_offset != 0) str_tab_len = archive_file_size - offset; else { if (fseek (file, 0, SEEK_END)) error (_("Unable to seek to end of file\n")); str_tab_len = ftell (file) - offset; } if (str_tab_len < 1) { error (_("Unable to determine the length of the dynamic string table\n")); continue; } dynamic_strings = get_data (NULL, file, offset, 1, str_tab_len, _("dynamic string table")); dynamic_strings_length = str_tab_len; break; } } /* And find the syminfo section if available. */ if (dynamic_syminfo == NULL) { unsigned long syminsz = 0; for (entry = dynamic_section; entry < dynamic_section + dynamic_nent; ++entry) { if (entry->d_tag == DT_SYMINENT) { /* Note: these braces are necessary to avoid a syntax error from the SunOS4 C compiler. */ assert (sizeof (Elf_External_Syminfo) == entry->d_un.d_val); } else if (entry->d_tag == DT_SYMINSZ) syminsz = entry->d_un.d_val; else if (entry->d_tag == DT_SYMINFO) dynamic_syminfo_offset = offset_from_vma (file, entry->d_un.d_val, syminsz); } if (dynamic_syminfo_offset != 0 && syminsz != 0) { Elf_External_Syminfo *extsyminfo, *extsym; Elf_Internal_Syminfo *syminfo; /* There is a syminfo section. Read the data. */ extsyminfo = get_data (NULL, file, dynamic_syminfo_offset, 1, syminsz, _("symbol information")); if (!extsyminfo) return 0; dynamic_syminfo = malloc (syminsz); if (dynamic_syminfo == NULL) { error (_("Out of memory\n")); return 0; } dynamic_syminfo_nent = syminsz / sizeof (Elf_External_Syminfo); for (syminfo = dynamic_syminfo, extsym = extsyminfo; syminfo < dynamic_syminfo + dynamic_syminfo_nent; ++syminfo, ++extsym) { syminfo->si_boundto = BYTE_GET (extsym->si_boundto); syminfo->si_flags = BYTE_GET (extsym->si_flags); } free (extsyminfo); } } if (do_dynamic && dynamic_addr) printf (_("\nDynamic section at offset 0x%lx contains %u entries:\n"), dynamic_addr, dynamic_nent); if (do_dynamic) printf (_(" Tag Type Name/Value\n")); for (entry = dynamic_section; entry < dynamic_section + dynamic_nent; entry++) { if (do_dynamic) { const char *dtype; putchar (' '); print_vma (entry->d_tag, FULL_HEX); dtype = get_dynamic_type (entry->d_tag); printf (" (%s)%*s", dtype, ((is_32bit_elf ? 27 : 19) - (int) strlen (dtype)), " "); } switch (entry->d_tag) { case DT_FLAGS: if (do_dynamic) print_dynamic_flags (entry->d_un.d_val); break; case DT_AUXILIARY: case DT_FILTER: case DT_CONFIG: case DT_DEPAUDIT: case DT_AUDIT: if (do_dynamic) { switch (entry->d_tag) { case DT_AUXILIARY: printf (_("Auxiliary library")); break; case DT_FILTER: printf (_("Filter library")); break; case DT_CONFIG: printf (_("Configuration file")); break; case DT_DEPAUDIT: printf (_("Dependency audit library")); break; case DT_AUDIT: printf (_("Audit library")); break; } if (VALID_DYNAMIC_NAME (entry->d_un.d_val)) printf (": [%s]\n", GET_DYNAMIC_NAME (entry->d_un.d_val)); else { printf (": "); print_vma (entry->d_un.d_val, PREFIX_HEX); putchar ('\n'); } } break; case DT_FEATURE: if (do_dynamic) { printf (_("Flags:")); if (entry->d_un.d_val == 0) printf (_(" None\n")); else { unsigned long int val = entry->d_un.d_val; if (val & DTF_1_PARINIT) { printf (" PARINIT"); val ^= DTF_1_PARINIT; } if (val & DTF_1_CONFEXP) { printf (" CONFEXP"); val ^= DTF_1_CONFEXP; } if (val != 0) printf (" %lx", val); puts (""); } } break; case DT_POSFLAG_1: if (do_dynamic) { printf (_("Flags:")); if (entry->d_un.d_val == 0) printf (_(" None\n")); else { unsigned long int val = entry->d_un.d_val; if (val & DF_P1_LAZYLOAD) { printf (" LAZYLOAD"); val ^= DF_P1_LAZYLOAD; } if (val & DF_P1_GROUPPERM) { printf (" GROUPPERM"); val ^= DF_P1_GROUPPERM; } if (val != 0) printf (" %lx", val); puts (""); } } break; case DT_FLAGS_1: if (do_dynamic) { printf (_("Flags:")); if (entry->d_un.d_val == 0) printf (_(" None\n")); else { unsigned long int val = entry->d_un.d_val; if (val & DF_1_NOW) { printf (" NOW"); val ^= DF_1_NOW; } if (val & DF_1_GLOBAL) { printf (" GLOBAL"); val ^= DF_1_GLOBAL; } if (val & DF_1_GROUP) { printf (" GROUP"); val ^= DF_1_GROUP; } if (val & DF_1_NODELETE) { printf (" NODELETE"); val ^= DF_1_NODELETE; } if (val & DF_1_LOADFLTR) { printf (" LOADFLTR"); val ^= DF_1_LOADFLTR; } if (val & DF_1_INITFIRST) { printf (" INITFIRST"); val ^= DF_1_INITFIRST; } if (val & DF_1_NOOPEN) { printf (" NOOPEN"); val ^= DF_1_NOOPEN; } if (val & DF_1_ORIGIN) { printf (" ORIGIN"); val ^= DF_1_ORIGIN; } if (val & DF_1_DIRECT) { printf (" DIRECT"); val ^= DF_1_DIRECT; } if (val & DF_1_TRANS) { printf (" TRANS"); val ^= DF_1_TRANS; } if (val & DF_1_INTERPOSE) { printf (" INTERPOSE"); val ^= DF_1_INTERPOSE; } if (val & DF_1_NODEFLIB) { printf (" NODEFLIB"); val ^= DF_1_NODEFLIB; } if (val & DF_1_NODUMP) { printf (" NODUMP"); val ^= DF_1_NODUMP; } if (val & DF_1_CONLFAT) { printf (" CONLFAT"); val ^= DF_1_CONLFAT; } if (val != 0) printf (" %lx", val); puts (""); } } break; case DT_PLTREL: dynamic_info[entry->d_tag] = entry->d_un.d_val; if (do_dynamic) puts (get_dynamic_type (entry->d_un.d_val)); break; case DT_NULL : case DT_NEEDED : case DT_PLTGOT : case DT_HASH : case DT_STRTAB : case DT_SYMTAB : case DT_RELA : case DT_INIT : case DT_FINI : case DT_SONAME : case DT_RPATH : case DT_SYMBOLIC: case DT_REL : case DT_DEBUG : case DT_TEXTREL : case DT_JMPREL : case DT_RUNPATH : dynamic_info[entry->d_tag] = entry->d_un.d_val; if (do_dynamic) { char *name; if (VALID_DYNAMIC_NAME (entry->d_un.d_val)) name = GET_DYNAMIC_NAME (entry->d_un.d_val); else name = NULL; if (name) { switch (entry->d_tag) { case DT_NEEDED: printf (_("Shared library: [%s]"), name); if (streq (name, program_interpreter)) printf (_(" program interpreter")); break; case DT_SONAME: printf (_("Library soname: [%s]"), name); break; case DT_RPATH: printf (_("Library rpath: [%s]"), name); break; case DT_RUNPATH: printf (_("Library runpath: [%s]"), name); break; default: print_vma (entry->d_un.d_val, PREFIX_HEX); break; } } else print_vma (entry->d_un.d_val, PREFIX_HEX); putchar ('\n'); } break; case DT_PLTRELSZ: case DT_RELASZ : case DT_STRSZ : case DT_RELSZ : case DT_RELAENT : case DT_SYMENT : case DT_RELENT : dynamic_info[entry->d_tag] = entry->d_un.d_val; case DT_PLTPADSZ: case DT_MOVEENT : case DT_MOVESZ : case DT_INIT_ARRAYSZ: case DT_FINI_ARRAYSZ: case DT_GNU_CONFLICTSZ: case DT_GNU_LIBLISTSZ: if (do_dynamic) { print_vma (entry->d_un.d_val, UNSIGNED); printf (" (bytes)\n"); } break; case DT_VERDEFNUM: case DT_VERNEEDNUM: case DT_RELACOUNT: case DT_RELCOUNT: if (do_dynamic) { print_vma (entry->d_un.d_val, UNSIGNED); putchar ('\n'); } break; case DT_SYMINSZ: case DT_SYMINENT: case DT_SYMINFO: case DT_USED: case DT_INIT_ARRAY: case DT_FINI_ARRAY: if (do_dynamic) { if (entry->d_tag == DT_USED && VALID_DYNAMIC_NAME (entry->d_un.d_val)) { char *name = GET_DYNAMIC_NAME (entry->d_un.d_val); if (*name) { printf (_("Not needed object: [%s]\n"), name); break; } } print_vma (entry->d_un.d_val, PREFIX_HEX); putchar ('\n'); } break; case DT_BIND_NOW: /* The value of this entry is ignored. */ if (do_dynamic) putchar ('\n'); break; case DT_GNU_PRELINKED: if (do_dynamic) { struct tm *tmp; time_t time = entry->d_un.d_val; tmp = gmtime (&time); printf ("%04u-%02u-%02uT%02u:%02u:%02u\n", tmp->tm_year + 1900, tmp->tm_mon + 1, tmp->tm_mday, tmp->tm_hour, tmp->tm_min, tmp->tm_sec); } break; case DT_GNU_HASH: dynamic_info_DT_GNU_HASH = entry->d_un.d_val; if (do_dynamic) { print_vma (entry->d_un.d_val, PREFIX_HEX); putchar ('\n'); } break; default: if ((entry->d_tag >= DT_VERSYM) && (entry->d_tag <= DT_VERNEEDNUM)) version_info[DT_VERSIONTAGIDX (entry->d_tag)] = entry->d_un.d_val; if (do_dynamic) { switch (elf_header.e_machine) { case EM_MIPS: case EM_MIPS_RS3_LE: dynamic_section_mips_val (entry); break; case EM_PARISC: dynamic_section_parisc_val (entry); break; case EM_IA_64: dynamic_section_ia64_val (entry); break; default: print_vma (entry->d_un.d_val, PREFIX_HEX); putchar ('\n'); } } break; } } return 1; } static char * get_ver_flags (unsigned int flags) { static char buff[32]; buff[0] = 0; if (flags == 0) return _("none"); if (flags & VER_FLG_BASE) strcat (buff, "BASE "); if (flags & VER_FLG_WEAK) { if (flags & VER_FLG_BASE) strcat (buff, "| "); strcat (buff, "WEAK "); } if (flags & ~(VER_FLG_BASE | VER_FLG_WEAK)) strcat (buff, "| "); return buff; } /* Display the contents of the version sections. */ static int process_version_sections (FILE *file) { Elf_Internal_Shdr *section; unsigned i; int found = 0; if (! do_version) return 1; for (i = 0, section = section_headers; i < elf_header.e_shnum; i++, section++) { switch (section->sh_type) { case SHT_GNU_verdef: { Elf_External_Verdef *edefs; unsigned int idx; unsigned int cnt; found = 1; printf (_("\nVersion definition section '%s' contains %ld entries:\n"), SECTION_NAME (section), section->sh_info); printf (_(" Addr: 0x")); printf_vma (section->sh_addr); printf (_(" Offset: %#08lx Link: %lx (%s)\n"), (unsigned long) section->sh_offset, section->sh_link, SECTION_HEADER_INDEX (section->sh_link) < elf_header.e_shnum ? SECTION_NAME (SECTION_HEADER (section->sh_link)) : ""); edefs = get_data (NULL, file, section->sh_offset, 1, section->sh_size, _("version definition section")); if (!edefs) break; for (idx = cnt = 0; cnt < section->sh_info; ++cnt) { char *vstart; Elf_External_Verdef *edef; Elf_Internal_Verdef ent; Elf_External_Verdaux *eaux; Elf_Internal_Verdaux aux; int j; int isum; vstart = ((char *) edefs) + idx; edef = (Elf_External_Verdef *) vstart; ent.vd_version = BYTE_GET (edef->vd_version); ent.vd_flags = BYTE_GET (edef->vd_flags); ent.vd_ndx = BYTE_GET (edef->vd_ndx); ent.vd_cnt = BYTE_GET (edef->vd_cnt); ent.vd_hash = BYTE_GET (edef->vd_hash); ent.vd_aux = BYTE_GET (edef->vd_aux); ent.vd_next = BYTE_GET (edef->vd_next); printf (_(" %#06x: Rev: %d Flags: %s"), idx, ent.vd_version, get_ver_flags (ent.vd_flags)); printf (_(" Index: %d Cnt: %d "), ent.vd_ndx, ent.vd_cnt); vstart += ent.vd_aux; eaux = (Elf_External_Verdaux *) vstart; aux.vda_name = BYTE_GET (eaux->vda_name); aux.vda_next = BYTE_GET (eaux->vda_next); if (VALID_DYNAMIC_NAME (aux.vda_name)) printf (_("Name: %s\n"), GET_DYNAMIC_NAME (aux.vda_name)); else printf (_("Name index: %ld\n"), aux.vda_name); isum = idx + ent.vd_aux; for (j = 1; j < ent.vd_cnt; j++) { isum += aux.vda_next; vstart += aux.vda_next; eaux = (Elf_External_Verdaux *) vstart; aux.vda_name = BYTE_GET (eaux->vda_name); aux.vda_next = BYTE_GET (eaux->vda_next); if (VALID_DYNAMIC_NAME (aux.vda_name)) printf (_(" %#06x: Parent %d: %s\n"), isum, j, GET_DYNAMIC_NAME (aux.vda_name)); else printf (_(" %#06x: Parent %d, name index: %ld\n"), isum, j, aux.vda_name); } idx += ent.vd_next; } free (edefs); } break; case SHT_GNU_verneed: { Elf_External_Verneed *eneed; unsigned int idx; unsigned int cnt; found = 1; printf (_("\nVersion needs section '%s' contains %ld entries:\n"), SECTION_NAME (section), section->sh_info); printf (_(" Addr: 0x")); printf_vma (section->sh_addr); printf (_(" Offset: %#08lx Link to section: %ld (%s)\n"), (unsigned long) section->sh_offset, section->sh_link, SECTION_HEADER_INDEX (section->sh_link) < elf_header.e_shnum ? SECTION_NAME (SECTION_HEADER (section->sh_link)) : ""); eneed = get_data (NULL, file, section->sh_offset, 1, section->sh_size, _("version need section")); if (!eneed) break; for (idx = cnt = 0; cnt < section->sh_info; ++cnt) { Elf_External_Verneed *entry; Elf_Internal_Verneed ent; int j; int isum; char *vstart; vstart = ((char *) eneed) + idx; entry = (Elf_External_Verneed *) vstart; ent.vn_version = BYTE_GET (entry->vn_version); ent.vn_cnt = BYTE_GET (entry->vn_cnt); ent.vn_file = BYTE_GET (entry->vn_file); ent.vn_aux = BYTE_GET (entry->vn_aux); ent.vn_next = BYTE_GET (entry->vn_next); printf (_(" %#06x: Version: %d"), idx, ent.vn_version); if (VALID_DYNAMIC_NAME (ent.vn_file)) printf (_(" File: %s"), GET_DYNAMIC_NAME (ent.vn_file)); else printf (_(" File: %lx"), ent.vn_file); printf (_(" Cnt: %d\n"), ent.vn_cnt); vstart += ent.vn_aux; for (j = 0, isum = idx + ent.vn_aux; j < ent.vn_cnt; ++j) { Elf_External_Vernaux *eaux; Elf_Internal_Vernaux aux; eaux = (Elf_External_Vernaux *) vstart; aux.vna_hash = BYTE_GET (eaux->vna_hash); aux.vna_flags = BYTE_GET (eaux->vna_flags); aux.vna_other = BYTE_GET (eaux->vna_other); aux.vna_name = BYTE_GET (eaux->vna_name); aux.vna_next = BYTE_GET (eaux->vna_next); if (VALID_DYNAMIC_NAME (aux.vna_name)) printf (_(" %#06x: Name: %s"), isum, GET_DYNAMIC_NAME (aux.vna_name)); else printf (_(" %#06x: Name index: %lx"), isum, aux.vna_name); printf (_(" Flags: %s Version: %d\n"), get_ver_flags (aux.vna_flags), aux.vna_other); isum += aux.vna_next; vstart += aux.vna_next; } idx += ent.vn_next; } free (eneed); } break; case SHT_GNU_versym: { Elf_Internal_Shdr *link_section; int total; int cnt; unsigned char *edata; unsigned short *data; char *strtab; Elf_Internal_Sym *symbols; Elf_Internal_Shdr *string_sec; long off; if (SECTION_HEADER_INDEX (section->sh_link) >= elf_header.e_shnum) break; link_section = SECTION_HEADER (section->sh_link); total = section->sh_size / sizeof (Elf_External_Versym); if (SECTION_HEADER_INDEX (link_section->sh_link) >= elf_header.e_shnum) break; found = 1; symbols = GET_ELF_SYMBOLS (file, link_section); string_sec = SECTION_HEADER (link_section->sh_link); strtab = get_data (NULL, file, string_sec->sh_offset, 1, string_sec->sh_size, _("version string table")); if (!strtab) break; printf (_("\nVersion symbols section '%s' contains %d entries:\n"), SECTION_NAME (section), total); printf (_(" Addr: ")); printf_vma (section->sh_addr); printf (_(" Offset: %#08lx Link: %lx (%s)\n"), (unsigned long) section->sh_offset, section->sh_link, SECTION_NAME (link_section)); off = offset_from_vma (file, version_info[DT_VERSIONTAGIDX (DT_VERSYM)], total * sizeof (short)); edata = get_data (NULL, file, off, total, sizeof (short), _("version symbol data")); if (!edata) { free (strtab); break; } data = cmalloc (total, sizeof (short)); for (cnt = total; cnt --;) data[cnt] = byte_get (edata + cnt * sizeof (short), sizeof (short)); free (edata); for (cnt = 0; cnt < total; cnt += 4) { int j, nn; int check_def, check_need; char *name; printf (" %03x:", cnt); for (j = 0; (j < 4) && (cnt + j) < total; ++j) switch (data[cnt + j]) { case 0: fputs (_(" 0 (*local*) "), stdout); break; case 1: fputs (_(" 1 (*global*) "), stdout); break; default: nn = printf ("%4x%c", data[cnt + j] & 0x7fff, data[cnt + j] & 0x8000 ? 'h' : ' '); check_def = 1; check_need = 1; if (SECTION_HEADER_INDEX (symbols[cnt + j].st_shndx) >= elf_header.e_shnum || SECTION_HEADER (symbols[cnt + j].st_shndx)->sh_type != SHT_NOBITS) { if (symbols[cnt + j].st_shndx == SHN_UNDEF) check_def = 0; else check_need = 0; } if (check_need && version_info[DT_VERSIONTAGIDX (DT_VERNEED)]) { Elf_Internal_Verneed ivn; unsigned long offset; offset = offset_from_vma (file, version_info[DT_VERSIONTAGIDX (DT_VERNEED)], sizeof (Elf_External_Verneed)); do { Elf_Internal_Vernaux ivna; Elf_External_Verneed evn; Elf_External_Vernaux evna; unsigned long a_off; get_data (&evn, file, offset, sizeof (evn), 1, _("version need")); ivn.vn_aux = BYTE_GET (evn.vn_aux); ivn.vn_next = BYTE_GET (evn.vn_next); a_off = offset + ivn.vn_aux; do { get_data (&evna, file, a_off, sizeof (evna), 1, _("version need aux (2)")); ivna.vna_next = BYTE_GET (evna.vna_next); ivna.vna_other = BYTE_GET (evna.vna_other); a_off += ivna.vna_next; } while (ivna.vna_other != data[cnt + j] && ivna.vna_next != 0); if (ivna.vna_other == data[cnt + j]) { ivna.vna_name = BYTE_GET (evna.vna_name); name = strtab + ivna.vna_name; nn += printf ("(%s%-*s", name, 12 - (int) strlen (name), ")"); check_def = 0; break; } offset += ivn.vn_next; } while (ivn.vn_next); } if (check_def && data[cnt + j] != 0x8001 && version_info[DT_VERSIONTAGIDX (DT_VERDEF)]) { Elf_Internal_Verdef ivd; Elf_External_Verdef evd; unsigned long offset; offset = offset_from_vma (file, version_info[DT_VERSIONTAGIDX (DT_VERDEF)], sizeof evd); do { get_data (&evd, file, offset, sizeof (evd), 1, _("version def")); ivd.vd_next = BYTE_GET (evd.vd_next); ivd.vd_ndx = BYTE_GET (evd.vd_ndx); offset += ivd.vd_next; } while (ivd.vd_ndx != (data[cnt + j] & 0x7fff) && ivd.vd_next != 0); if (ivd.vd_ndx == (data[cnt + j] & 0x7fff)) { Elf_External_Verdaux evda; Elf_Internal_Verdaux ivda; ivd.vd_aux = BYTE_GET (evd.vd_aux); get_data (&evda, file, offset - ivd.vd_next + ivd.vd_aux, sizeof (evda), 1, _("version def aux")); ivda.vda_name = BYTE_GET (evda.vda_name); name = strtab + ivda.vda_name; nn += printf ("(%s%-*s", name, 12 - (int) strlen (name), ")"); } } if (nn < 18) printf ("%*c", 18 - nn, ' '); } putchar ('\n'); } free (data); free (strtab); free (symbols); } break; default: break; } } if (! found) printf (_("\nNo version information found in this file.\n")); return 1; } static const char * get_symbol_binding (unsigned int binding) { static char buff[32]; switch (binding) { case STB_LOCAL: return "LOCAL"; case STB_GLOBAL: return "GLOBAL"; case STB_WEAK: return "WEAK"; default: if (binding >= STB_LOPROC && binding <= STB_HIPROC) snprintf (buff, sizeof (buff), _(": %d"), binding); else if (binding >= STB_LOOS && binding <= STB_HIOS) snprintf (buff, sizeof (buff), _(": %d"), binding); else snprintf (buff, sizeof (buff), _(": %d"), binding); return buff; } } static const char * get_symbol_type (unsigned int type) { static char buff[32]; switch (type) { case STT_NOTYPE: return "NOTYPE"; case STT_OBJECT: return "OBJECT"; case STT_FUNC: return "FUNC"; case STT_SECTION: return "SECTION"; case STT_FILE: return "FILE"; case STT_COMMON: return "COMMON"; case STT_TLS: return "TLS"; case STT_RELC: return "RELC"; case STT_SRELC: return "SRELC"; default: if (type >= STT_LOPROC && type <= STT_HIPROC) { if (elf_header.e_machine == EM_ARM && type == STT_ARM_TFUNC) return "THUMB_FUNC"; if (elf_header.e_machine == EM_SPARCV9 && type == STT_REGISTER) return "REGISTER"; if (elf_header.e_machine == EM_PARISC && type == STT_PARISC_MILLI) return "PARISC_MILLI"; snprintf (buff, sizeof (buff), _(": %d"), type); } else if (type >= STT_LOOS && type <= STT_HIOS) { if (elf_header.e_machine == EM_PARISC) { if (type == STT_HP_OPAQUE) return "HP_OPAQUE"; if (type == STT_HP_STUB) return "HP_STUB"; } snprintf (buff, sizeof (buff), _(": %d"), type); } else snprintf (buff, sizeof (buff), _(": %d"), type); return buff; } } static const char * get_symbol_visibility (unsigned int visibility) { switch (visibility) { case STV_DEFAULT: return "DEFAULT"; case STV_INTERNAL: return "INTERNAL"; case STV_HIDDEN: return "HIDDEN"; case STV_PROTECTED: return "PROTECTED"; default: abort (); } } static const char * get_mips_symbol_other (unsigned int other) { switch (other) { case STO_OPTIONAL: return "OPTIONAL"; case STO_MIPS16: return "MIPS16"; default: return NULL; } } static const char * get_symbol_other (unsigned int other) { const char * result = NULL; static char buff [32]; if (other == 0) return ""; switch (elf_header.e_machine) { case EM_MIPS: result = get_mips_symbol_other (other); default: break; } if (result) return result; snprintf (buff, sizeof buff, _(": %x"), other); return buff; } static const char * get_symbol_index_type (unsigned int type) { static char buff[32]; switch (type) { case SHN_UNDEF: return "UND"; case SHN_ABS: return "ABS"; case SHN_COMMON: return "COM"; default: if (type == SHN_IA_64_ANSI_COMMON && elf_header.e_machine == EM_IA_64 && elf_header.e_ident[EI_OSABI] == ELFOSABI_HPUX) return "ANSI_COM"; else if (elf_header.e_machine == EM_X86_64 && type == SHN_X86_64_LCOMMON) return "LARGE_COM"; else if (type == SHN_MIPS_SCOMMON && elf_header.e_machine == EM_MIPS) return "SCOM"; else if (type == SHN_MIPS_SUNDEFINED && elf_header.e_machine == EM_MIPS) return "SUND"; else if (type >= SHN_LOPROC && type <= SHN_HIPROC) sprintf (buff, "PRC[0x%04x]", type); else if (type >= SHN_LOOS && type <= SHN_HIOS) sprintf (buff, "OS [0x%04x]", type); else if (type >= SHN_LORESERVE && type <= SHN_HIRESERVE) sprintf (buff, "RSV[0x%04x]", type); else sprintf (buff, "%3d", type); break; } return buff; } static bfd_vma * get_dynamic_data (FILE *file, unsigned int number, unsigned int ent_size) { unsigned char *e_data; bfd_vma *i_data; e_data = cmalloc (number, ent_size); if (e_data == NULL) { error (_("Out of memory\n")); return NULL; } if (fread (e_data, ent_size, number, file) != number) { error (_("Unable to read in dynamic data\n")); return NULL; } i_data = cmalloc (number, sizeof (*i_data)); if (i_data == NULL) { error (_("Out of memory\n")); free (e_data); return NULL; } while (number--) i_data[number] = byte_get (e_data + number * ent_size, ent_size); free (e_data); return i_data; } /* Dump the symbol table. */ static int process_symbol_table (FILE *file) { Elf_Internal_Shdr *section; bfd_vma nbuckets = 0; bfd_vma nchains = 0; bfd_vma *buckets = NULL; bfd_vma *chains = NULL; bfd_vma ngnubuckets = 0; bfd_vma *gnubuckets = NULL; bfd_vma *gnuchains = NULL; if (! do_syms && !do_histogram) return 1; if (dynamic_info[DT_HASH] && ((do_using_dynamic && dynamic_strings != NULL) || do_histogram)) { unsigned char nb[8]; unsigned char nc[8]; int hash_ent_size = 4; if ((elf_header.e_machine == EM_ALPHA || elf_header.e_machine == EM_S390 || elf_header.e_machine == EM_S390_OLD) && elf_header.e_ident[EI_CLASS] == ELFCLASS64) hash_ent_size = 8; if (fseek (file, (archive_file_offset + offset_from_vma (file, dynamic_info[DT_HASH], sizeof nb + sizeof nc)), SEEK_SET)) { error (_("Unable to seek to start of dynamic information\n")); return 0; } if (fread (nb, hash_ent_size, 1, file) != 1) { error (_("Failed to read in number of buckets\n")); return 0; } if (fread (nc, hash_ent_size, 1, file) != 1) { error (_("Failed to read in number of chains\n")); return 0; } nbuckets = byte_get (nb, hash_ent_size); nchains = byte_get (nc, hash_ent_size); buckets = get_dynamic_data (file, nbuckets, hash_ent_size); chains = get_dynamic_data (file, nchains, hash_ent_size); if (buckets == NULL || chains == NULL) return 0; } if (do_syms && dynamic_info[DT_HASH] && do_using_dynamic && dynamic_strings != NULL) { unsigned long hn; bfd_vma si; printf (_("\nSymbol table for image:\n")); if (is_32bit_elf) printf (_(" Num Buc: Value Size Type Bind Vis Ndx Name\n")); else printf (_(" Num Buc: Value Size Type Bind Vis Ndx Name\n")); for (hn = 0; hn < nbuckets; hn++) { if (! buckets[hn]) continue; for (si = buckets[hn]; si < nchains && si > 0; si = chains[si]) { Elf_Internal_Sym *psym; int n; psym = dynamic_symbols + si; n = print_vma (si, DEC_5); if (n < 5) fputs (&" "[n], stdout); printf (" %3lu: ", hn); print_vma (psym->st_value, LONG_HEX); putchar (' '); print_vma (psym->st_size, DEC_5); printf (" %6s", get_symbol_type (ELF_ST_TYPE (psym->st_info))); printf (" %6s", get_symbol_binding (ELF_ST_BIND (psym->st_info))); printf (" %3s", get_symbol_visibility (ELF_ST_VISIBILITY (psym->st_other))); /* Check to see if any other bits in the st_other field are set. Note - displaying this information disrupts the layout of the table being generated, but for the moment this case is very rare. */ if (psym->st_other ^ ELF_ST_VISIBILITY (psym->st_other)) printf (" [%s] ", get_symbol_other (psym->st_other ^ ELF_ST_VISIBILITY (psym->st_other))); printf (" %3.3s ", get_symbol_index_type (psym->st_shndx)); if (VALID_DYNAMIC_NAME (psym->st_name)) print_symbol (25, GET_DYNAMIC_NAME (psym->st_name)); else printf (" ", psym->st_name); putchar ('\n'); } } } else if (do_syms && !do_using_dynamic) { unsigned int i; for (i = 0, section = section_headers; i < elf_header.e_shnum; i++, section++) { unsigned int si; char *strtab = NULL; unsigned long int strtab_size = 0; Elf_Internal_Sym *symtab; Elf_Internal_Sym *psym; if ( section->sh_type != SHT_SYMTAB && section->sh_type != SHT_DYNSYM) continue; printf (_("\nSymbol table '%s' contains %lu entries:\n"), SECTION_NAME (section), (unsigned long) (section->sh_size / section->sh_entsize)); if (is_32bit_elf) printf (_(" Num: Value Size Type Bind Vis Ndx Name\n")); else printf (_(" Num: Value Size Type Bind Vis Ndx Name\n")); symtab = GET_ELF_SYMBOLS (file, section); if (symtab == NULL) continue; if (section->sh_link == elf_header.e_shstrndx) { strtab = string_table; strtab_size = string_table_length; } else if (SECTION_HEADER_INDEX (section->sh_link) < elf_header.e_shnum) { Elf_Internal_Shdr *string_sec; string_sec = SECTION_HEADER (section->sh_link); strtab = get_data (NULL, file, string_sec->sh_offset, 1, string_sec->sh_size, _("string table")); strtab_size = strtab != NULL ? string_sec->sh_size : 0; } for (si = 0, psym = symtab; si < section->sh_size / section->sh_entsize; si++, psym++) { printf ("%6d: ", si); print_vma (psym->st_value, LONG_HEX); putchar (' '); print_vma (psym->st_size, DEC_5); printf (" %-7s", get_symbol_type (ELF_ST_TYPE (psym->st_info))); printf (" %-6s", get_symbol_binding (ELF_ST_BIND (psym->st_info))); printf (" %-3s", get_symbol_visibility (ELF_ST_VISIBILITY (psym->st_other))); /* Check to see if any other bits in the st_other field are set. Note - displaying this information disrupts the layout of the table being generated, but for the moment this case is very rare. */ if (psym->st_other ^ ELF_ST_VISIBILITY (psym->st_other)) printf (" [%s] ", get_symbol_other (psym->st_other ^ ELF_ST_VISIBILITY (psym->st_other))); printf (" %4s ", get_symbol_index_type (psym->st_shndx)); print_symbol (25, psym->st_name < strtab_size ? strtab + psym->st_name : ""); if (section->sh_type == SHT_DYNSYM && version_info[DT_VERSIONTAGIDX (DT_VERSYM)] != 0) { unsigned char data[2]; unsigned short vers_data; unsigned long offset; int is_nobits; int check_def; offset = offset_from_vma (file, version_info[DT_VERSIONTAGIDX (DT_VERSYM)], sizeof data + si * sizeof (vers_data)); get_data (&data, file, offset + si * sizeof (vers_data), sizeof (data), 1, _("version data")); vers_data = byte_get (data, 2); is_nobits = (SECTION_HEADER_INDEX (psym->st_shndx) < elf_header.e_shnum && SECTION_HEADER (psym->st_shndx)->sh_type == SHT_NOBITS); check_def = (psym->st_shndx != SHN_UNDEF); if ((vers_data & 0x8000) || vers_data > 1) { if (version_info[DT_VERSIONTAGIDX (DT_VERNEED)] && (is_nobits || ! check_def)) { Elf_External_Verneed evn; Elf_Internal_Verneed ivn; Elf_Internal_Vernaux ivna; /* We must test both. */ offset = offset_from_vma (file, version_info[DT_VERSIONTAGIDX (DT_VERNEED)], sizeof evn); do { unsigned long vna_off; get_data (&evn, file, offset, sizeof (evn), 1, _("version need")); ivn.vn_aux = BYTE_GET (evn.vn_aux); ivn.vn_next = BYTE_GET (evn.vn_next); vna_off = offset + ivn.vn_aux; do { Elf_External_Vernaux evna; get_data (&evna, file, vna_off, sizeof (evna), 1, _("version need aux (3)")); ivna.vna_other = BYTE_GET (evna.vna_other); ivna.vna_next = BYTE_GET (evna.vna_next); ivna.vna_name = BYTE_GET (evna.vna_name); vna_off += ivna.vna_next; } while (ivna.vna_other != vers_data && ivna.vna_next != 0); if (ivna.vna_other == vers_data) break; offset += ivn.vn_next; } while (ivn.vn_next != 0); if (ivna.vna_other == vers_data) { printf ("@%s (%d)", ivna.vna_name < strtab_size ? strtab + ivna.vna_name : "", ivna.vna_other); check_def = 0; } else if (! is_nobits) error (_("bad dynamic symbol\n")); else check_def = 1; } if (check_def) { if (vers_data != 0x8001 && version_info[DT_VERSIONTAGIDX (DT_VERDEF)]) { Elf_Internal_Verdef ivd; Elf_Internal_Verdaux ivda; Elf_External_Verdaux evda; unsigned long offset; offset = offset_from_vma (file, version_info[DT_VERSIONTAGIDX (DT_VERDEF)], sizeof (Elf_External_Verdef)); do { Elf_External_Verdef evd; get_data (&evd, file, offset, sizeof (evd), 1, _("version def")); ivd.vd_ndx = BYTE_GET (evd.vd_ndx); ivd.vd_aux = BYTE_GET (evd.vd_aux); ivd.vd_next = BYTE_GET (evd.vd_next); offset += ivd.vd_next; } while (ivd.vd_ndx != (vers_data & 0x7fff) && ivd.vd_next != 0); offset -= ivd.vd_next; offset += ivd.vd_aux; get_data (&evda, file, offset, sizeof (evda), 1, _("version def aux")); ivda.vda_name = BYTE_GET (evda.vda_name); if (psym->st_name != ivda.vda_name) printf ((vers_data & 0x8000) ? "@%s" : "@@%s", ivda.vda_name < strtab_size ? strtab + ivda.vda_name : ""); } } } } putchar ('\n'); } free (symtab); if (strtab != string_table) free (strtab); } } else if (do_syms) printf (_("\nDynamic symbol information is not available for displaying symbols.\n")); if (do_histogram && buckets != NULL) { unsigned long *lengths; unsigned long *counts; unsigned long hn; bfd_vma si; unsigned long maxlength = 0; unsigned long nzero_counts = 0; unsigned long nsyms = 0; printf (_("\nHistogram for bucket list length (total of %lu buckets):\n"), (unsigned long) nbuckets); printf (_(" Length Number %% of total Coverage\n")); lengths = calloc (nbuckets, sizeof (*lengths)); if (lengths == NULL) { error (_("Out of memory\n")); return 0; } for (hn = 0; hn < nbuckets; ++hn) { for (si = buckets[hn]; si > 0 && si < nchains; si = chains[si]) { ++nsyms; if (maxlength < ++lengths[hn]) ++maxlength; } } counts = calloc (maxlength + 1, sizeof (*counts)); if (counts == NULL) { error (_("Out of memory\n")); return 0; } for (hn = 0; hn < nbuckets; ++hn) ++counts[lengths[hn]]; if (nbuckets > 0) { unsigned long i; printf (" 0 %-10lu (%5.1f%%)\n", counts[0], (counts[0] * 100.0) / nbuckets); for (i = 1; i <= maxlength; ++i) { nzero_counts += counts[i] * i; printf ("%7lu %-10lu (%5.1f%%) %5.1f%%\n", i, counts[i], (counts[i] * 100.0) / nbuckets, (nzero_counts * 100.0) / nsyms); } } free (counts); free (lengths); } if (buckets != NULL) { free (buckets); free (chains); } if (do_histogram && dynamic_info_DT_GNU_HASH) { unsigned char nb[16]; bfd_vma i, maxchain = 0xffffffff, symidx, bitmaskwords; unsigned long *lengths; unsigned long *counts; unsigned long hn; unsigned long maxlength = 0; unsigned long nzero_counts = 0; unsigned long nsyms = 0; bfd_vma buckets_vma; if (fseek (file, (archive_file_offset + offset_from_vma (file, dynamic_info_DT_GNU_HASH, sizeof nb)), SEEK_SET)) { error (_("Unable to seek to start of dynamic information\n")); return 0; } if (fread (nb, 16, 1, file) != 1) { error (_("Failed to read in number of buckets\n")); return 0; } ngnubuckets = byte_get (nb, 4); symidx = byte_get (nb + 4, 4); bitmaskwords = byte_get (nb + 8, 4); buckets_vma = dynamic_info_DT_GNU_HASH + 16; if (is_32bit_elf) buckets_vma += bitmaskwords * 4; else buckets_vma += bitmaskwords * 8; if (fseek (file, (archive_file_offset + offset_from_vma (file, buckets_vma, 4)), SEEK_SET)) { error (_("Unable to seek to start of dynamic information\n")); return 0; } gnubuckets = get_dynamic_data (file, ngnubuckets, 4); if (gnubuckets == NULL) return 0; for (i = 0; i < ngnubuckets; i++) if (gnubuckets[i] != 0) { if (gnubuckets[i] < symidx) return 0; if (maxchain == 0xffffffff || gnubuckets[i] > maxchain) maxchain = gnubuckets[i]; } if (maxchain == 0xffffffff) return 0; maxchain -= symidx; if (fseek (file, (archive_file_offset + offset_from_vma (file, buckets_vma + 4 * (ngnubuckets + maxchain), 4)), SEEK_SET)) { error (_("Unable to seek to start of dynamic information\n")); return 0; } do { if (fread (nb, 4, 1, file) != 1) { error (_("Failed to determine last chain length\n")); return 0; } if (maxchain + 1 == 0) return 0; ++maxchain; } while ((byte_get (nb, 4) & 1) == 0); if (fseek (file, (archive_file_offset + offset_from_vma (file, buckets_vma + 4 * ngnubuckets, 4)), SEEK_SET)) { error (_("Unable to seek to start of dynamic information\n")); return 0; } gnuchains = get_dynamic_data (file, maxchain, 4); if (gnuchains == NULL) return 0; lengths = calloc (ngnubuckets, sizeof (*lengths)); if (lengths == NULL) { error (_("Out of memory\n")); return 0; } printf (_("\nHistogram for `.gnu.hash' bucket list length (total of %lu buckets):\n"), (unsigned long) ngnubuckets); printf (_(" Length Number %% of total Coverage\n")); for (hn = 0; hn < ngnubuckets; ++hn) if (gnubuckets[hn] != 0) { bfd_vma off, length = 1; for (off = gnubuckets[hn] - symidx; (gnuchains[off] & 1) == 0; ++off) ++length; lengths[hn] = length; if (length > maxlength) maxlength = length; nsyms += length; } counts = calloc (maxlength + 1, sizeof (*counts)); if (counts == NULL) { error (_("Out of memory\n")); return 0; } for (hn = 0; hn < ngnubuckets; ++hn) ++counts[lengths[hn]]; if (ngnubuckets > 0) { unsigned long j; printf (" 0 %-10lu (%5.1f%%)\n", counts[0], (counts[0] * 100.0) / ngnubuckets); for (j = 1; j <= maxlength; ++j) { nzero_counts += counts[j] * j; printf ("%7lu %-10lu (%5.1f%%) %5.1f%%\n", j, counts[j], (counts[j] * 100.0) / ngnubuckets, (nzero_counts * 100.0) / nsyms); } } free (counts); free (lengths); free (gnubuckets); free (gnuchains); } return 1; } static int process_syminfo (FILE *file ATTRIBUTE_UNUSED) { unsigned int i; if (dynamic_syminfo == NULL || !do_dynamic) /* No syminfo, this is ok. */ return 1; /* There better should be a dynamic symbol section. */ if (dynamic_symbols == NULL || dynamic_strings == NULL) return 0; if (dynamic_addr) printf (_("\nDynamic info segment at offset 0x%lx contains %d entries:\n"), dynamic_syminfo_offset, dynamic_syminfo_nent); printf (_(" Num: Name BoundTo Flags\n")); for (i = 0; i < dynamic_syminfo_nent; ++i) { unsigned short int flags = dynamic_syminfo[i].si_flags; printf ("%4d: ", i); if (VALID_DYNAMIC_NAME (dynamic_symbols[i].st_name)) print_symbol (30, GET_DYNAMIC_NAME (dynamic_symbols[i].st_name)); else printf ("", dynamic_symbols[i].st_name); putchar (' '); switch (dynamic_syminfo[i].si_boundto) { case SYMINFO_BT_SELF: fputs ("SELF ", stdout); break; case SYMINFO_BT_PARENT: fputs ("PARENT ", stdout); break; default: if (dynamic_syminfo[i].si_boundto > 0 && dynamic_syminfo[i].si_boundto < dynamic_nent && VALID_DYNAMIC_NAME (dynamic_section[dynamic_syminfo[i].si_boundto].d_un.d_val)) { print_symbol (10, GET_DYNAMIC_NAME (dynamic_section[dynamic_syminfo[i].si_boundto].d_un.d_val)); putchar (' ' ); } else printf ("%-10d ", dynamic_syminfo[i].si_boundto); break; } if (flags & SYMINFO_FLG_DIRECT) printf (" DIRECT"); if (flags & SYMINFO_FLG_PASSTHRU) printf (" PASSTHRU"); if (flags & SYMINFO_FLG_COPY) printf (" COPY"); if (flags & SYMINFO_FLG_LAZYLOAD) printf (" LAZYLOAD"); puts (""); } return 1; } #ifdef SUPPORT_DISASSEMBLY static int disassemble_section (Elf_Internal_Shdr *section, FILE *file) { printf (_("\nAssembly dump of section %s\n"), SECTION_NAME (section)); /* XXX -- to be done --- XXX */ return 1; } #endif static int dump_section (Elf_Internal_Shdr *section, FILE *file) { Elf_Internal_Shdr *relsec; bfd_size_type bytes; bfd_vma addr; unsigned char *data; unsigned char *start; bytes = section->sh_size; if (bytes == 0 || section->sh_type == SHT_NOBITS) { printf (_("\nSection '%s' has no data to dump.\n"), SECTION_NAME (section)); return 0; } else printf (_("\nHex dump of section '%s':\n"), SECTION_NAME (section)); addr = section->sh_addr; start = get_data (NULL, file, section->sh_offset, 1, bytes, _("section data")); if (!start) return 0; /* If the section being dumped has relocations against it the user might be expecting these relocations to have been applied. Check for this case and issue a warning message in order to avoid confusion. FIXME: Maybe we ought to have an option that dumps a section with relocs applied ? */ for (relsec = section_headers; relsec < section_headers + elf_header.e_shnum; ++relsec) { if ((relsec->sh_type != SHT_RELA && relsec->sh_type != SHT_REL) || SECTION_HEADER_INDEX (relsec->sh_info) >= elf_header.e_shnum || SECTION_HEADER (relsec->sh_info) != section || relsec->sh_size == 0 || SECTION_HEADER_INDEX (relsec->sh_link) >= elf_header.e_shnum) continue; printf (_(" NOTE: This section has relocations against it, but these have NOT been applied to this dump.\n")); break; } data = start; while (bytes) { int j; int k; int lbytes; lbytes = (bytes > 16 ? 16 : bytes); printf (" 0x%8.8lx ", (unsigned long) addr); for (j = 0; j < 16; j++) { if (j < lbytes) printf ("%2.2x", data[j]); else printf (" "); if ((j & 3) == 3) printf (" "); } for (j = 0; j < lbytes; j++) { k = data[j]; if (k >= ' ' && k < 0x7f) printf ("%c", k); else printf ("."); } putchar ('\n'); data += lbytes; addr += lbytes; bytes -= lbytes; } free (start); putchar ('\n'); return 1; } /* Return the number of bytes affected by a given reloc. This information is architecture and reloc dependent. Returns 4 by default, although this is not always correct. It should return 0 if a decision cannot be made. FIXME: This is not the correct way to solve this problem. The proper way is to have target specific reloc sizing functions created by the reloc-macros.h header, in the same way that it already creates the reloc naming functions. */ static unsigned int get_reloc_size (Elf_Internal_Rela * reloc) { switch (elf_header.e_machine) { case EM_H8S: case EM_H8_300: case EM_H8_300H: case EM_H8_500: switch (ELF32_R_TYPE (reloc->r_info)) { /* PR gas/3800 - without this information we do not correctly decode the debug information generated by the h8300 assembler. */ case R_H8_DIR16: return 2; default: return 4; } default: /* FIXME: We need to extend this switch statement to cope with other architecture's relocs. (When those relocs are used against debug sections, and when their size is not 4). But see the multiple inclusions of for an example of the hoops that we need to jump through in order to obtain the reloc numbers. */ return 4; } } /* Apply addends of RELA relocations. */ static int debug_apply_rela_addends (void *file, Elf_Internal_Shdr *section, unsigned char *start) { Elf_Internal_Shdr *relsec; unsigned char *end = start + section->sh_size; if (!is_relocatable) return 1; /* SH uses RELA but uses in place value instead of the addend field. */ if (elf_header.e_machine == EM_SH) return 0; for (relsec = section_headers; relsec < section_headers + elf_header.e_shnum; ++relsec) { unsigned long nrelas; Elf_Internal_Rela *rela, *rp; Elf_Internal_Shdr *symsec; Elf_Internal_Sym *symtab; Elf_Internal_Sym *sym; if (relsec->sh_type != SHT_RELA || SECTION_HEADER_INDEX (relsec->sh_info) >= elf_header.e_shnum || SECTION_HEADER (relsec->sh_info) != section || relsec->sh_size == 0 || SECTION_HEADER_INDEX (relsec->sh_link) >= elf_header.e_shnum) continue; if (!slurp_rela_relocs (file, relsec->sh_offset, relsec->sh_size, &rela, &nrelas)) return 0; symsec = SECTION_HEADER (relsec->sh_link); symtab = GET_ELF_SYMBOLS (file, symsec); for (rp = rela; rp < rela + nrelas; ++rp) { unsigned char *loc; unsigned int reloc_size; reloc_size = get_reloc_size (rp); if (reloc_size == 0) { warn (_("skipping relocation of unknown size against offset 0x%lx in section %s\n"), (unsigned long) rp->r_offset, SECTION_NAME (section)); continue; } loc = start + rp->r_offset; if ((loc + reloc_size) > end) { warn (_("skipping invalid relocation offset 0x%lx in section %s\n"), (unsigned long) rp->r_offset, SECTION_NAME (section)); continue; } if (is_32bit_elf) { sym = symtab + ELF32_R_SYM (rp->r_info); if (ELF32_R_SYM (rp->r_info) != 0 && ELF32_ST_TYPE (sym->st_info) != STT_SECTION /* Relocations against symbols without type can happen. Gcc -feliminate-dwarf2-dups may generate symbols without type for debug info. */ && ELF32_ST_TYPE (sym->st_info) != STT_NOTYPE /* Relocations against object symbols can happen, eg when referencing a global array. For an example of this see the _clz.o binary in libgcc.a. */ && ELF32_ST_TYPE (sym->st_info) != STT_OBJECT) { warn (_("skipping unexpected symbol type %s in relocation in section .rela%s\n"), get_symbol_type (ELF32_ST_TYPE (sym->st_info)), SECTION_NAME (section)); continue; } } else { /* In MIPS little-endian objects, r_info isn't really a 64-bit little-endian value: it has a 32-bit little-endian symbol index followed by four individual byte fields. Reorder INFO accordingly. */ if (elf_header.e_machine == EM_MIPS && elf_header.e_ident[EI_DATA] != ELFDATA2MSB) rp->r_info = (((rp->r_info & 0xffffffff) << 32) | ((rp->r_info >> 56) & 0xff) | ((rp->r_info >> 40) & 0xff00) | ((rp->r_info >> 24) & 0xff0000) | ((rp->r_info >> 8) & 0xff000000)); sym = symtab + ELF64_R_SYM (rp->r_info); if (ELF64_R_SYM (rp->r_info) != 0 && ELF64_ST_TYPE (sym->st_info) != STT_SECTION && ELF64_ST_TYPE (sym->st_info) != STT_NOTYPE && ELF64_ST_TYPE (sym->st_info) != STT_OBJECT) { warn (_("skipping unexpected symbol type %s in relocation in section .rela.%s\n"), get_symbol_type (ELF64_ST_TYPE (sym->st_info)), SECTION_NAME (section)); continue; } } byte_put (loc, rp->r_addend, reloc_size); } free (symtab); free (rela); break; } return 1; } int load_debug_section (enum dwarf_section_display_enum debug, void *file) { struct dwarf_section *section = &debug_displays [debug].section; Elf_Internal_Shdr *sec; char buf [64]; /* If it is already loaded, do nothing. */ if (section->start != NULL) return 1; /* Locate the debug section. */ sec = find_section (section->name); if (sec == NULL) return 0; snprintf (buf, sizeof (buf), _("%s section data"), section->name); section->address = sec->sh_addr; section->size = sec->sh_size; section->start = get_data (NULL, file, sec->sh_offset, 1, sec->sh_size, buf); if (debug_displays [debug].relocate) debug_apply_rela_addends (file, sec, section->start); return section->start != NULL; } void free_debug_section (enum dwarf_section_display_enum debug) { struct dwarf_section *section = &debug_displays [debug].section; if (section->start == NULL) return; free ((char *) section->start); section->start = NULL; section->address = 0; section->size = 0; } static int display_debug_section (Elf_Internal_Shdr *section, FILE *file) { char *name = SECTION_NAME (section); bfd_size_type length; int result = 1; enum dwarf_section_display_enum i; length = section->sh_size; if (length == 0) { printf (_("\nSection '%s' has no debugging data.\n"), name); return 0; } if (const_strneq (name, ".gnu.linkonce.wi.")) name = ".debug_info"; /* See if we know how to display the contents of this section. */ for (i = 0; i < max; i++) if (streq (debug_displays[i].section.name, name)) { struct dwarf_section *sec = &debug_displays [i].section; if (load_debug_section (i, file)) { result &= debug_displays[i].display (sec, file); if (i != info && i != abbrev) free_debug_section (i); } break; } if (i == max) { printf (_("Unrecognized debug section: %s\n"), name); result = 0; } return result; } /* Set DUMP_SECTS for all sections where dumps were requested based on section name. */ static void initialise_dumps_byname (void) { struct dump_list_entry *cur; for (cur = dump_sects_byname; cur; cur = cur->next) { unsigned int i; int any; for (i = 0, any = 0; i < elf_header.e_shnum; i++) if (streq (SECTION_NAME (section_headers + i), cur->name)) { request_dump (i, cur->type); any = 1; } if (!any) warn (_("Section '%s' was not dumped because it does not exist!\n"), cur->name); } } static void process_section_contents (FILE *file) { Elf_Internal_Shdr *section; unsigned int i; if (! do_dump) return; initialise_dumps_byname (); for (i = 0, section = section_headers; i < elf_header.e_shnum && i < num_dump_sects; i++, section++) { #ifdef SUPPORT_DISASSEMBLY if (dump_sects[i] & DISASS_DUMP) disassemble_section (section, file); #endif if (dump_sects[i] & HEX_DUMP) dump_section (section, file); if (dump_sects[i] & DEBUG_DUMP) display_debug_section (section, file); } /* Check to see if the user requested a dump of a section that does not exist. */ while (i++ < num_dump_sects) if (dump_sects[i]) warn (_("Section %d was not dumped because it does not exist!\n"), i); } static void process_mips_fpe_exception (int mask) { if (mask) { int first = 1; if (mask & OEX_FPU_INEX) fputs ("INEX", stdout), first = 0; if (mask & OEX_FPU_UFLO) printf ("%sUFLO", first ? "" : "|"), first = 0; if (mask & OEX_FPU_OFLO) printf ("%sOFLO", first ? "" : "|"), first = 0; if (mask & OEX_FPU_DIV0) printf ("%sDIV0", first ? "" : "|"), first = 0; if (mask & OEX_FPU_INVAL) printf ("%sINVAL", first ? "" : "|"); } else fputs ("0", stdout); } /* ARM EABI attributes section. */ typedef struct { int tag; const char *name; /* 0 = special, 1 = string, 2 = uleb123, > 0x80 == table lookup. */ int type; const char **table; } arm_attr_public_tag; static const char *arm_attr_tag_CPU_arch[] = {"Pre-v4", "v4", "v4T", "v5T", "v5TE", "v5TEJ", "v6", "v6KZ", "v6T2", "v6K", "v7"}; static const char *arm_attr_tag_ARM_ISA_use[] = {"No", "Yes"}; static const char *arm_attr_tag_THUMB_ISA_use[] = {"No", "Thumb-1", "Thumb-2"}; /* FIXME: VFPv3 encoding was extrapolated! */ static const char *arm_attr_tag_VFP_arch[] = {"No", "VFPv1", "VFPv2", "VFPv3"}; static const char *arm_attr_tag_WMMX_arch[] = {"No", "WMMXv1"}; static const char *arm_attr_tag_NEON_arch[] = {"No", "NEONv1"}; static const char *arm_attr_tag_ABI_PCS_config[] = {"None", "Bare platform", "Linux application", "Linux DSO", "PalmOS 2004", "PalmOS (reserved)", "SymbianOS 2004", "SymbianOS (reserved)"}; static const char *arm_attr_tag_ABI_PCS_R9_use[] = {"V6", "SB", "TLS", "Unused"}; static const char *arm_attr_tag_ABI_PCS_RW_data[] = {"Absolute", "PC-relative", "SB-relative", "None"}; static const char *arm_attr_tag_ABI_PCS_RO_DATA[] = {"Absolute", "PC-relative", "None"}; static const char *arm_attr_tag_ABI_PCS_GOT_use[] = {"None", "direct", "GOT-indirect"}; static const char *arm_attr_tag_ABI_PCS_wchar_t[] = {"None", "??? 1", "2", "??? 3", "4"}; static const char *arm_attr_tag_ABI_FP_rounding[] = {"Unused", "Needed"}; static const char *arm_attr_tag_ABI_FP_denormal[] = {"Unused", "Needed"}; static const char *arm_attr_tag_ABI_FP_exceptions[] = {"Unused", "Needed"}; static const char *arm_attr_tag_ABI_FP_user_exceptions[] = {"Unused", "Needed"}; static const char *arm_attr_tag_ABI_FP_number_model[] = {"Unused", "Finite", "RTABI", "IEEE 754"}; static const char *arm_attr_tag_ABI_align8_needed[] = {"No", "Yes", "4-byte"}; static const char *arm_attr_tag_ABI_align8_preserved[] = {"No", "Yes, except leaf SP", "Yes"}; static const char *arm_attr_tag_ABI_enum_size[] = {"Unused", "small", "int", "forced to int"}; static const char *arm_attr_tag_ABI_HardFP_use[] = {"As Tag_VFP_arch", "SP only", "DP only", "SP and DP"}; static const char *arm_attr_tag_ABI_VFP_args[] = {"AAPCS", "VFP registers", "custom"}; static const char *arm_attr_tag_ABI_WMMX_args[] = {"AAPCS", "WMMX registers", "custom"}; static const char *arm_attr_tag_ABI_optimization_goals[] = {"None", "Prefer Speed", "Aggressive Speed", "Prefer Size", "Aggressive Size", "Prefer Debug", "Aggressive Debug"}; static const char *arm_attr_tag_ABI_FP_optimization_goals[] = {"None", "Prefer Speed", "Aggressive Speed", "Prefer Size", "Aggressive Size", "Prefer Accuracy", "Aggressive Accuracy"}; #define LOOKUP(id, name) \ {id, #name, 0x80 | ARRAY_SIZE(arm_attr_tag_##name), arm_attr_tag_##name} static arm_attr_public_tag arm_attr_public_tags[] = { {4, "CPU_raw_name", 1, NULL}, {5, "CPU_name", 1, NULL}, LOOKUP(6, CPU_arch), {7, "CPU_arch_profile", 0, NULL}, LOOKUP(8, ARM_ISA_use), LOOKUP(9, THUMB_ISA_use), LOOKUP(10, VFP_arch), LOOKUP(11, WMMX_arch), LOOKUP(12, NEON_arch), LOOKUP(13, ABI_PCS_config), LOOKUP(14, ABI_PCS_R9_use), LOOKUP(15, ABI_PCS_RW_data), LOOKUP(16, ABI_PCS_RO_DATA), LOOKUP(17, ABI_PCS_GOT_use), LOOKUP(18, ABI_PCS_wchar_t), LOOKUP(19, ABI_FP_rounding), LOOKUP(20, ABI_FP_denormal), LOOKUP(21, ABI_FP_exceptions), LOOKUP(22, ABI_FP_user_exceptions), LOOKUP(23, ABI_FP_number_model), LOOKUP(24, ABI_align8_needed), LOOKUP(25, ABI_align8_preserved), LOOKUP(26, ABI_enum_size), LOOKUP(27, ABI_HardFP_use), LOOKUP(28, ABI_VFP_args), LOOKUP(29, ABI_WMMX_args), LOOKUP(30, ABI_optimization_goals), LOOKUP(31, ABI_FP_optimization_goals), {32, "compatibility", 0, NULL} }; #undef LOOKUP /* Read an unsigned LEB128 encoded value from p. Set *PLEN to the number of bytes read. */ static unsigned int read_uleb128 (unsigned char *p, unsigned int *plen) { unsigned char c; unsigned int val; int shift; int len; val = 0; shift = 0; len = 0; do { c = *(p++); len++; val |= ((unsigned int)c & 0x7f) << shift; shift += 7; } while (c & 0x80); *plen = len; return val; } static unsigned char * display_arm_attribute (unsigned char *p) { int tag; unsigned int len; int val; arm_attr_public_tag *attr; unsigned i; int type; tag = read_uleb128 (p, &len); p += len; attr = NULL; for (i = 0; i < ARRAY_SIZE(arm_attr_public_tags); i++) { if (arm_attr_public_tags[i].tag == tag) { attr = &arm_attr_public_tags[i]; break; } } if (attr) { printf (" Tag_%s: ", attr->name); switch (attr->type) { case 0: switch (tag) { case 7: /* Tag_CPU_arch_profile. */ val = read_uleb128 (p, &len); p += len; switch (val) { case 0: printf ("None\n"); break; case 'A': printf ("Application\n"); break; case 'R': printf ("Realtime\n"); break; case 'M': printf ("Microcontroller\n"); break; default: printf ("??? (%d)\n", val); break; } break; case 32: /* Tag_compatibility. */ val = read_uleb128 (p, &len); p += len; printf ("flag = %d, vendor = %s\n", val, p); p += strlen((char *)p) + 1; break; default: abort(); } return p; case 1: case 2: type = attr->type; break; default: assert (attr->type & 0x80); val = read_uleb128 (p, &len); p += len; type = attr->type & 0x7f; if (val >= type) printf ("??? (%d)\n", val); else printf ("%s\n", attr->table[val]); return p; } } else { if (tag & 1) type = 1; /* String. */ else type = 2; /* uleb128. */ printf (" Tag_unknown_%d: ", tag); } if (type == 1) { printf ("\"%s\"\n", p); p += strlen((char *)p) + 1; } else { val = read_uleb128 (p, &len); p += len; printf ("%d (0x%x)\n", val, val); } return p; } static unsigned char * display_gnu_attribute (unsigned char *p, unsigned char *(*display_proc_gnu_attribute) (unsigned char *, int)) { int tag; unsigned int len; int val; int type; tag = read_uleb128 (p, &len); p += len; /* Tag_compatibility is the only generic GNU attribute defined at present. */ if (tag == 32) { val = read_uleb128 (p, &len); p += len; printf ("flag = %d, vendor = %s\n", val, p); p += strlen((char *)p) + 1; return p; } if ((tag & 2) == 0 && display_proc_gnu_attribute) return display_proc_gnu_attribute (p, tag); if (tag & 1) type = 1; /* String. */ else type = 2; /* uleb128. */ printf (" Tag_unknown_%d: ", tag); if (type == 1) { printf ("\"%s\"\n", p); p += strlen ((char *)p) + 1; } else { val = read_uleb128 (p, &len); p += len; printf ("%d (0x%x)\n", val, val); } return p; } static unsigned char * display_power_gnu_attribute (unsigned char *p, int tag) { int type; unsigned int len; int val; if (tag == Tag_GNU_Power_ABI_FP) { val = read_uleb128 (p, &len); p += len; printf (" Tag_GNU_Power_ABI_FP: "); switch (val) { case 0: printf ("Hard or soft float\n"); break; case 1: printf ("Hard float\n"); break; case 2: printf ("Soft float\n"); break; default: printf ("??? (%d)\n", val); break; } return p; } if (tag & 1) type = 1; /* String. */ else type = 2; /* uleb128. */ printf (" Tag_unknown_%d: ", tag); if (type == 1) { printf ("\"%s\"\n", p); p += strlen ((char *)p) + 1; } else { val = read_uleb128 (p, &len); p += len; printf ("%d (0x%x)\n", val, val); } return p; } static unsigned char * display_mips_gnu_attribute (unsigned char *p, int tag) { int type; unsigned int len; int val; if (tag == Tag_GNU_MIPS_ABI_FP) { val = read_uleb128 (p, &len); p += len; printf (" Tag_GNU_MIPS_ABI_FP: "); switch (val) { case 0: printf ("Hard or soft float\n"); break; case 1: printf ("Hard float (-mdouble-float)\n"); break; case 2: printf ("Hard float (-msingle-float)\n"); break; case 3: printf ("Soft float\n"); break; default: printf ("??? (%d)\n", val); break; } return p; } if (tag & 1) type = 1; /* String. */ else type = 2; /* uleb128. */ printf (" Tag_unknown_%d: ", tag); if (type == 1) { printf ("\"%s\"\n", p); p += strlen ((char *)p) + 1; } else { val = read_uleb128 (p, &len); p += len; printf ("%d (0x%x)\n", val, val); } return p; } static int process_attributes (FILE *file, const char *public_name, unsigned int proc_type, unsigned char *(*display_pub_attribute) (unsigned char *), unsigned char *(*display_proc_gnu_attribute) (unsigned char *, int)) { Elf_Internal_Shdr *sect; unsigned char *contents; unsigned char *p; unsigned char *end; bfd_vma section_len; bfd_vma len; unsigned i; /* Find the section header so that we get the size. */ for (i = 0, sect = section_headers; i < elf_header.e_shnum; i++, sect++) { if (sect->sh_type != proc_type && sect->sh_type != SHT_GNU_ATTRIBUTES) continue; contents = get_data (NULL, file, sect->sh_offset, 1, sect->sh_size, _("attributes")); if (!contents) continue; p = contents; if (*p == 'A') { len = sect->sh_size - 1; p++; while (len > 0) { int namelen; bfd_boolean public_section; bfd_boolean gnu_section; section_len = byte_get (p, 4); p += 4; if (section_len > len) { printf (_("ERROR: Bad section length (%d > %d)\n"), (int)section_len, (int)len); section_len = len; } len -= section_len; printf ("Attribute Section: %s\n", p); if (public_name && strcmp ((char *)p, public_name) == 0) public_section = TRUE; else public_section = FALSE; if (strcmp ((char *)p, "gnu") == 0) gnu_section = TRUE; else gnu_section = FALSE; namelen = strlen ((char *)p) + 1; p += namelen; section_len -= namelen + 4; while (section_len > 0) { int tag = *(p++); int val; bfd_vma size; size = byte_get (p, 4); if (size > section_len) { printf (_("ERROR: Bad subsection length (%d > %d)\n"), (int)size, (int)section_len); size = section_len; } section_len -= size; end = p + size - 1; p += 4; switch (tag) { case 1: printf ("File Attributes\n"); break; case 2: printf ("Section Attributes:"); goto do_numlist; case 3: printf ("Symbol Attributes:"); do_numlist: for (;;) { unsigned int i; val = read_uleb128 (p, &i); p += i; if (val == 0) break; printf (" %d", val); } printf ("\n"); break; default: printf ("Unknown tag: %d\n", tag); public_section = FALSE; break; } if (public_section) { while (p < end) p = display_pub_attribute (p); } else if (gnu_section) { while (p < end) p = display_gnu_attribute (p, display_proc_gnu_attribute); } else { /* ??? Do something sensible, like dump hex. */ printf (" Unknown section contexts\n"); p = end; } } } } else { printf (_("Unknown format '%c'\n"), *p); } free(contents); } return 1; } static int process_arm_specific (FILE *file) { return process_attributes (file, "aeabi", SHT_ARM_ATTRIBUTES, display_arm_attribute, NULL); } static int process_power_specific (FILE *file) { return process_attributes (file, NULL, SHT_GNU_ATTRIBUTES, NULL, display_power_gnu_attribute); } static int process_mips_specific (FILE *file) { Elf_Internal_Dyn *entry; size_t liblist_offset = 0; size_t liblistno = 0; size_t conflictsno = 0; size_t options_offset = 0; size_t conflicts_offset = 0; process_attributes (file, NULL, SHT_GNU_ATTRIBUTES, NULL, display_mips_gnu_attribute); /* We have a lot of special sections. Thanks SGI! */ if (dynamic_section == NULL) /* No information available. */ return 0; for (entry = dynamic_section; entry->d_tag != DT_NULL; ++entry) switch (entry->d_tag) { case DT_MIPS_LIBLIST: liblist_offset = offset_from_vma (file, entry->d_un.d_val, liblistno * sizeof (Elf32_External_Lib)); break; case DT_MIPS_LIBLISTNO: liblistno = entry->d_un.d_val; break; case DT_MIPS_OPTIONS: options_offset = offset_from_vma (file, entry->d_un.d_val, 0); break; case DT_MIPS_CONFLICT: conflicts_offset = offset_from_vma (file, entry->d_un.d_val, conflictsno * sizeof (Elf32_External_Conflict)); break; case DT_MIPS_CONFLICTNO: conflictsno = entry->d_un.d_val; break; default: break; } if (liblist_offset != 0 && liblistno != 0 && do_dynamic) { Elf32_External_Lib *elib; size_t cnt; elib = get_data (NULL, file, liblist_offset, liblistno, sizeof (Elf32_External_Lib), _("liblist")); if (elib) { printf ("\nSection '.liblist' contains %lu entries:\n", (unsigned long) liblistno); fputs (" Library Time Stamp Checksum Version Flags\n", stdout); for (cnt = 0; cnt < liblistno; ++cnt) { Elf32_Lib liblist; time_t time; char timebuf[20]; struct tm *tmp; liblist.l_name = BYTE_GET (elib[cnt].l_name); time = BYTE_GET (elib[cnt].l_time_stamp); liblist.l_checksum = BYTE_GET (elib[cnt].l_checksum); liblist.l_version = BYTE_GET (elib[cnt].l_version); liblist.l_flags = BYTE_GET (elib[cnt].l_flags); tmp = gmtime (&time); snprintf (timebuf, sizeof (timebuf), "%04u-%02u-%02uT%02u:%02u:%02u", tmp->tm_year + 1900, tmp->tm_mon + 1, tmp->tm_mday, tmp->tm_hour, tmp->tm_min, tmp->tm_sec); printf ("%3lu: ", (unsigned long) cnt); if (VALID_DYNAMIC_NAME (liblist.l_name)) print_symbol (20, GET_DYNAMIC_NAME (liblist.l_name)); else printf ("", liblist.l_name); printf (" %s %#10lx %-7ld", timebuf, liblist.l_checksum, liblist.l_version); if (liblist.l_flags == 0) puts (" NONE"); else { static const struct { const char *name; int bit; } l_flags_vals[] = { { " EXACT_MATCH", LL_EXACT_MATCH }, { " IGNORE_INT_VER", LL_IGNORE_INT_VER }, { " REQUIRE_MINOR", LL_REQUIRE_MINOR }, { " EXPORTS", LL_EXPORTS }, { " DELAY_LOAD", LL_DELAY_LOAD }, { " DELTA", LL_DELTA } }; int flags = liblist.l_flags; size_t fcnt; for (fcnt = 0; fcnt < sizeof (l_flags_vals) / sizeof (l_flags_vals[0]); ++fcnt) if ((flags & l_flags_vals[fcnt].bit) != 0) { fputs (l_flags_vals[fcnt].name, stdout); flags ^= l_flags_vals[fcnt].bit; } if (flags != 0) printf (" %#x", (unsigned int) flags); puts (""); } } free (elib); } } if (options_offset != 0) { Elf_External_Options *eopt; Elf_Internal_Shdr *sect = section_headers; Elf_Internal_Options *iopt; Elf_Internal_Options *option; size_t offset; int cnt; /* Find the section header so that we get the size. */ while (sect->sh_type != SHT_MIPS_OPTIONS) ++sect; eopt = get_data (NULL, file, options_offset, 1, sect->sh_size, _("options")); if (eopt) { iopt = cmalloc ((sect->sh_size / sizeof (eopt)), sizeof (*iopt)); if (iopt == NULL) { error (_("Out of memory\n")); return 0; } offset = cnt = 0; option = iopt; while (offset < sect->sh_size) { Elf_External_Options *eoption; eoption = (Elf_External_Options *) ((char *) eopt + offset); option->kind = BYTE_GET (eoption->kind); option->size = BYTE_GET (eoption->size); option->section = BYTE_GET (eoption->section); option->info = BYTE_GET (eoption->info); offset += option->size; ++option; ++cnt; } printf (_("\nSection '%s' contains %d entries:\n"), SECTION_NAME (sect), cnt); option = iopt; while (cnt-- > 0) { size_t len; switch (option->kind) { case ODK_NULL: /* This shouldn't happen. */ printf (" NULL %d %lx", option->section, option->info); break; case ODK_REGINFO: printf (" REGINFO "); if (elf_header.e_machine == EM_MIPS) { /* 32bit form. */ Elf32_External_RegInfo *ereg; Elf32_RegInfo reginfo; ereg = (Elf32_External_RegInfo *) (option + 1); reginfo.ri_gprmask = BYTE_GET (ereg->ri_gprmask); reginfo.ri_cprmask[0] = BYTE_GET (ereg->ri_cprmask[0]); reginfo.ri_cprmask[1] = BYTE_GET (ereg->ri_cprmask[1]); reginfo.ri_cprmask[2] = BYTE_GET (ereg->ri_cprmask[2]); reginfo.ri_cprmask[3] = BYTE_GET (ereg->ri_cprmask[3]); reginfo.ri_gp_value = BYTE_GET (ereg->ri_gp_value); printf ("GPR %08lx GP 0x%lx\n", reginfo.ri_gprmask, (unsigned long) reginfo.ri_gp_value); printf (" CPR0 %08lx CPR1 %08lx CPR2 %08lx CPR3 %08lx\n", reginfo.ri_cprmask[0], reginfo.ri_cprmask[1], reginfo.ri_cprmask[2], reginfo.ri_cprmask[3]); } else { /* 64 bit form. */ Elf64_External_RegInfo *ereg; Elf64_Internal_RegInfo reginfo; ereg = (Elf64_External_RegInfo *) (option + 1); reginfo.ri_gprmask = BYTE_GET (ereg->ri_gprmask); reginfo.ri_cprmask[0] = BYTE_GET (ereg->ri_cprmask[0]); reginfo.ri_cprmask[1] = BYTE_GET (ereg->ri_cprmask[1]); reginfo.ri_cprmask[2] = BYTE_GET (ereg->ri_cprmask[2]); reginfo.ri_cprmask[3] = BYTE_GET (ereg->ri_cprmask[3]); reginfo.ri_gp_value = BYTE_GET (ereg->ri_gp_value); printf ("GPR %08lx GP 0x", reginfo.ri_gprmask); printf_vma (reginfo.ri_gp_value); printf ("\n"); printf (" CPR0 %08lx CPR1 %08lx CPR2 %08lx CPR3 %08lx\n", reginfo.ri_cprmask[0], reginfo.ri_cprmask[1], reginfo.ri_cprmask[2], reginfo.ri_cprmask[3]); } ++option; continue; case ODK_EXCEPTIONS: fputs (" EXCEPTIONS fpe_min(", stdout); process_mips_fpe_exception (option->info & OEX_FPU_MIN); fputs (") fpe_max(", stdout); process_mips_fpe_exception ((option->info & OEX_FPU_MAX) >> 8); fputs (")", stdout); if (option->info & OEX_PAGE0) fputs (" PAGE0", stdout); if (option->info & OEX_SMM) fputs (" SMM", stdout); if (option->info & OEX_FPDBUG) fputs (" FPDBUG", stdout); if (option->info & OEX_DISMISS) fputs (" DISMISS", stdout); break; case ODK_PAD: fputs (" PAD ", stdout); if (option->info & OPAD_PREFIX) fputs (" PREFIX", stdout); if (option->info & OPAD_POSTFIX) fputs (" POSTFIX", stdout); if (option->info & OPAD_SYMBOL) fputs (" SYMBOL", stdout); break; case ODK_HWPATCH: fputs (" HWPATCH ", stdout); if (option->info & OHW_R4KEOP) fputs (" R4KEOP", stdout); if (option->info & OHW_R8KPFETCH) fputs (" R8KPFETCH", stdout); if (option->info & OHW_R5KEOP) fputs (" R5KEOP", stdout); if (option->info & OHW_R5KCVTL) fputs (" R5KCVTL", stdout); break; case ODK_FILL: fputs (" FILL ", stdout); /* XXX Print content of info word? */ break; case ODK_TAGS: fputs (" TAGS ", stdout); /* XXX Print content of info word? */ break; case ODK_HWAND: fputs (" HWAND ", stdout); if (option->info & OHWA0_R4KEOP_CHECKED) fputs (" R4KEOP_CHECKED", stdout); if (option->info & OHWA0_R4KEOP_CLEAN) fputs (" R4KEOP_CLEAN", stdout); break; case ODK_HWOR: fputs (" HWOR ", stdout); if (option->info & OHWA0_R4KEOP_CHECKED) fputs (" R4KEOP_CHECKED", stdout); if (option->info & OHWA0_R4KEOP_CLEAN) fputs (" R4KEOP_CLEAN", stdout); break; case ODK_GP_GROUP: printf (" GP_GROUP %#06lx self-contained %#06lx", option->info & OGP_GROUP, (option->info & OGP_SELF) >> 16); break; case ODK_IDENT: printf (" IDENT %#06lx self-contained %#06lx", option->info & OGP_GROUP, (option->info & OGP_SELF) >> 16); break; default: /* This shouldn't happen. */ printf (" %3d ??? %d %lx", option->kind, option->section, option->info); break; } len = sizeof (*eopt); while (len < option->size) if (((char *) option)[len] >= ' ' && ((char *) option)[len] < 0x7f) printf ("%c", ((char *) option)[len++]); else printf ("\\%03o", ((char *) option)[len++]); fputs ("\n", stdout); ++option; } free (eopt); } } if (conflicts_offset != 0 && conflictsno != 0) { Elf32_Conflict *iconf; size_t cnt; if (dynamic_symbols == NULL) { error (_("conflict list found without a dynamic symbol table\n")); return 0; } iconf = cmalloc (conflictsno, sizeof (*iconf)); if (iconf == NULL) { error (_("Out of memory\n")); return 0; } if (is_32bit_elf) { Elf32_External_Conflict *econf32; econf32 = get_data (NULL, file, conflicts_offset, conflictsno, sizeof (*econf32), _("conflict")); if (!econf32) return 0; for (cnt = 0; cnt < conflictsno; ++cnt) iconf[cnt] = BYTE_GET (econf32[cnt]); free (econf32); } else { Elf64_External_Conflict *econf64; econf64 = get_data (NULL, file, conflicts_offset, conflictsno, sizeof (*econf64), _("conflict")); if (!econf64) return 0; for (cnt = 0; cnt < conflictsno; ++cnt) iconf[cnt] = BYTE_GET (econf64[cnt]); free (econf64); } printf (_("\nSection '.conflict' contains %lu entries:\n"), (unsigned long) conflictsno); puts (_(" Num: Index Value Name")); for (cnt = 0; cnt < conflictsno; ++cnt) { Elf_Internal_Sym *psym = & dynamic_symbols[iconf[cnt]]; printf ("%5lu: %8lu ", (unsigned long) cnt, iconf[cnt]); print_vma (psym->st_value, FULL_HEX); putchar (' '); if (VALID_DYNAMIC_NAME (psym->st_name)) print_symbol (25, GET_DYNAMIC_NAME (psym->st_name)); else printf ("", psym->st_name); putchar ('\n'); } free (iconf); } return 1; } static int process_gnu_liblist (FILE *file) { Elf_Internal_Shdr *section, *string_sec; Elf32_External_Lib *elib; char *strtab; size_t strtab_size; size_t cnt; unsigned i; if (! do_arch) return 0; for (i = 0, section = section_headers; i < elf_header.e_shnum; i++, section++) { switch (section->sh_type) { case SHT_GNU_LIBLIST: if (SECTION_HEADER_INDEX (section->sh_link) >= elf_header.e_shnum) break; elib = get_data (NULL, file, section->sh_offset, 1, section->sh_size, _("liblist")); if (elib == NULL) break; string_sec = SECTION_HEADER (section->sh_link); strtab = get_data (NULL, file, string_sec->sh_offset, 1, string_sec->sh_size, _("liblist string table")); strtab_size = string_sec->sh_size; if (strtab == NULL || section->sh_entsize != sizeof (Elf32_External_Lib)) { free (elib); break; } printf (_("\nLibrary list section '%s' contains %lu entries:\n"), SECTION_NAME (section), (long) (section->sh_size / sizeof (Elf32_External_Lib))); puts (" Library Time Stamp Checksum Version Flags"); for (cnt = 0; cnt < section->sh_size / sizeof (Elf32_External_Lib); ++cnt) { Elf32_Lib liblist; time_t time; char timebuf[20]; struct tm *tmp; liblist.l_name = BYTE_GET (elib[cnt].l_name); time = BYTE_GET (elib[cnt].l_time_stamp); liblist.l_checksum = BYTE_GET (elib[cnt].l_checksum); liblist.l_version = BYTE_GET (elib[cnt].l_version); liblist.l_flags = BYTE_GET (elib[cnt].l_flags); tmp = gmtime (&time); snprintf (timebuf, sizeof (timebuf), "%04u-%02u-%02uT%02u:%02u:%02u", tmp->tm_year + 1900, tmp->tm_mon + 1, tmp->tm_mday, tmp->tm_hour, tmp->tm_min, tmp->tm_sec); printf ("%3lu: ", (unsigned long) cnt); if (do_wide) printf ("%-20s", liblist.l_name < strtab_size ? strtab + liblist.l_name : ""); else printf ("%-20.20s", liblist.l_name < strtab_size ? strtab + liblist.l_name : ""); printf (" %s %#010lx %-7ld %-7ld\n", timebuf, liblist.l_checksum, liblist.l_version, liblist.l_flags); } free (elib); } } return 1; } static const char * get_note_type (unsigned e_type) { static char buff[64]; if (elf_header.e_type == ET_CORE) switch (e_type) { case NT_AUXV: return _("NT_AUXV (auxiliary vector)"); case NT_PRSTATUS: return _("NT_PRSTATUS (prstatus structure)"); case NT_FPREGSET: return _("NT_FPREGSET (floating point registers)"); case NT_PRPSINFO: return _("NT_PRPSINFO (prpsinfo structure)"); case NT_TASKSTRUCT: return _("NT_TASKSTRUCT (task structure)"); case NT_PRXFPREG: return _("NT_PRXFPREG (user_xfpregs structure)"); case NT_PSTATUS: return _("NT_PSTATUS (pstatus structure)"); case NT_FPREGS: return _("NT_FPREGS (floating point registers)"); case NT_PSINFO: return _("NT_PSINFO (psinfo structure)"); case NT_LWPSTATUS: return _("NT_LWPSTATUS (lwpstatus_t structure)"); case NT_LWPSINFO: return _("NT_LWPSINFO (lwpsinfo_t structure)"); case NT_WIN32PSTATUS: return _("NT_WIN32PSTATUS (win32_pstatus structure)"); case NT_FILE: return _("NT_FILE"); case NT_SIGINFO: return _("NT_SIGINFO"); default: break; } else switch (e_type) { case NT_VERSION: return _("NT_VERSION (version)"); case NT_ARCH: return _("NT_ARCH (architecture)"); default: break; } snprintf (buff, sizeof (buff), _("Unknown note type: (0x%08x)"), e_type); return buff; } static const char * get_freebsd_note_type (unsigned e_type) { static char buff[64]; if (elf_header.e_type == ET_CORE) switch (e_type) { case NT_THRMISC: return _("NT_THRMISC (thrmisc structure)"); case NT_PROCSTAT_PROC: return _("NT_PROCSTAT_PROC (proc data)"); case NT_PROCSTAT_FILES: return _("NT_PROCSTAT_FILES (files data)"); case NT_PROCSTAT_VMMAP: return _("NT_PROCSTAT_VMMAP (vmmap data)"); case NT_PROCSTAT_GROUPS: return _("NT_PROCSTAT_GROUPS (groups data)"); case NT_PROCSTAT_UMASK: return _("NT_PROCSTAT_UMASK (umask data)"); case NT_PROCSTAT_RLIMIT: return _("NT_PROCSTAT_RLIMIT (rlimit data)"); case NT_PROCSTAT_OSREL: return _("NT_PROCSTAT_OSREL (osreldate data)"); case NT_PROCSTAT_PSSTRINGS: return _("NT_PROCSTAT_PSSTRINGS (ps_strings data)"); case NT_PROCSTAT_AUXV: return _("NT_PROCSTAT_AUXV (auxv data)"); + case NT_X86_XSTATE: + return _("NT_X86_XSTATE (x86 XSAVE extended state)"); default: return get_note_type(e_type); } else switch (e_type) { case NT_FREEBSD_ABI_TAG: return _("NT_FREEBSD_ABI_TAG"); case NT_FREEBSD_NOINIT_TAG: return _("NT_FREEBSD_NOINIT_TAG"); case NT_FREEBSD_ARCH_TAG: return _("NT_FREEBSD_ARCH_TAG"); default: break; } snprintf (buff, sizeof(buff), _("Unknown note type: (0x%08x)"), e_type); return buff; } static const char * get_gnu_note_type (unsigned e_type) { static char buff[64]; switch (e_type) { case NT_GNU_ABI_TAG: return _("NT_GNU_ABI_TAG"); case NT_GNU_BUILD_ID: return _("NT_GNU_BUILD_ID"); } snprintf (buff, sizeof(buff), _("Unknown GNU note type: (0x%08x)"), e_type); return buff; } static const char * get_netbsd_elfcore_note_type (unsigned e_type) { static char buff[64]; if (e_type == NT_NETBSDCORE_PROCINFO) { /* NetBSD core "procinfo" structure. */ return _("NetBSD procinfo structure"); } /* As of Jan 2002 there are no other machine-independent notes defined for NetBSD core files. If the note type is less than the start of the machine-dependent note types, we don't understand it. */ if (e_type < NT_NETBSDCORE_FIRSTMACH) { snprintf (buff, sizeof (buff), _("Unknown note type: (0x%08x)"), e_type); return buff; } switch (elf_header.e_machine) { /* On the Alpha, SPARC (32-bit and 64-bit), PT_GETREGS == mach+0 and PT_GETFPREGS == mach+2. */ case EM_OLD_ALPHA: case EM_ALPHA: case EM_SPARC: case EM_SPARC32PLUS: case EM_SPARCV9: switch (e_type) { case NT_NETBSDCORE_FIRSTMACH+0: return _("PT_GETREGS (reg structure)"); case NT_NETBSDCORE_FIRSTMACH+2: return _("PT_GETFPREGS (fpreg structure)"); default: break; } break; /* On all other arch's, PT_GETREGS == mach+1 and PT_GETFPREGS == mach+3. */ default: switch (e_type) { case NT_NETBSDCORE_FIRSTMACH+1: return _("PT_GETREGS (reg structure)"); case NT_NETBSDCORE_FIRSTMACH+3: return _("PT_GETFPREGS (fpreg structure)"); default: break; } } snprintf (buff, sizeof (buff), _("PT_FIRSTMACH+%d"), e_type - NT_NETBSDCORE_FIRSTMACH); return buff; } /* Note that by the ELF standard, the name field is already null byte terminated, and namesz includes the terminating null byte. I.E. the value of namesz for the name "FSF" is 4. If the value of namesz is zero, there is no name present. */ static int process_note (Elf_Internal_Note *pnote) { const char *nt; if (pnote->namesz == 0) /* If there is no note name, then use the default set of note type strings. */ nt = get_note_type (pnote->type); else if (const_strneq (pnote->namedata, "FreeBSD")) /* FreeBSD-specific core file notes. */ nt = get_freebsd_note_type (pnote->type); else if (const_strneq (pnote->namedata, "GNU")) /* GNU-specific notes */ nt = get_gnu_note_type (pnote->type); else if (const_strneq (pnote->namedata, "NetBSD-CORE")) /* NetBSD-specific core file notes. */ nt = get_netbsd_elfcore_note_type (pnote->type); else /* Don't recognize this note name; just use the default set of note type strings. */ nt = get_note_type (pnote->type); printf (" %-13s 0x%08lx\t%s\n", pnote->namesz ? pnote->namedata : "(NONE)", pnote->descsz, nt); return 1; } static int process_corefile_note_segment (FILE *file, bfd_vma offset, bfd_vma length) { Elf_External_Note *pnotes; Elf_External_Note *external; int res = 1; if (length <= 0) return 0; pnotes = get_data (NULL, file, offset, 1, length, _("notes")); if (!pnotes) return 0; external = pnotes; printf (_("\nNotes at offset 0x%08lx with length 0x%08lx:\n"), (unsigned long) offset, (unsigned long) length); printf (_(" Owner\t\tData size\tDescription\n")); while (external < (Elf_External_Note *)((char *) pnotes + length)) { Elf_External_Note *next; Elf_Internal_Note inote; char *temp = NULL; inote.type = BYTE_GET (external->type); inote.namesz = BYTE_GET (external->namesz); inote.namedata = external->name; inote.descsz = BYTE_GET (external->descsz); inote.descdata = inote.namedata + align_power (inote.namesz, 2); inote.descpos = offset + (inote.descdata - (char *) pnotes); next = (Elf_External_Note *)(inote.descdata + align_power (inote.descsz, 2)); if (((char *) next) > (((char *) pnotes) + length)) { warn (_("corrupt note found at offset %lx into core notes\n"), (long)((char *)external - (char *)pnotes)); warn (_(" type: %lx, namesize: %08lx, descsize: %08lx\n"), inote.type, inote.namesz, inote.descsz); break; } external = next; /* Verify that name is null terminated. It appears that at least one version of Linux (RedHat 6.0) generates corefiles that don't comply with the ELF spec by failing to include the null byte in namesz. */ if (inote.namedata[inote.namesz] != '\0') { temp = malloc (inote.namesz + 1); if (temp == NULL) { error (_("Out of memory\n")); res = 0; break; } strncpy (temp, inote.namedata, inote.namesz); temp[inote.namesz] = 0; /* warn (_("'%s' NOTE name not properly null terminated\n"), temp); */ inote.namedata = temp; } res &= process_note (& inote); if (temp != NULL) { free (temp); temp = NULL; } } free (pnotes); return res; } static int process_corefile_note_segments (FILE *file) { Elf_Internal_Phdr *segment; unsigned int i; int res = 1; if (! get_program_headers (file)) return 0; for (i = 0, segment = program_headers; i < elf_header.e_phnum; i++, segment++) { if (segment->p_type == PT_NOTE) res &= process_corefile_note_segment (file, (bfd_vma) segment->p_offset, (bfd_vma) segment->p_filesz); } return res; } static int process_note_sections (FILE *file) { Elf_Internal_Shdr *section; unsigned long i; int res = 1; for (i = 0, section = section_headers; i < elf_header.e_shnum; i++, section++) if (section->sh_type == SHT_NOTE) res &= process_corefile_note_segment (file, (bfd_vma) section->sh_offset, (bfd_vma) section->sh_size); return res; } static int process_notes (FILE *file) { /* If we have not been asked to display the notes then do nothing. */ if (! do_notes) return 1; if (elf_header.e_type != ET_CORE) return process_note_sections (file); /* No program headers means no NOTE segment. */ if (elf_header.e_phnum > 0) return process_corefile_note_segments (file); printf (_("No note segments present in the core file.\n")); return 1; } static int process_arch_specific (FILE *file) { if (! do_arch) return 1; switch (elf_header.e_machine) { case EM_ARM: return process_arm_specific (file); case EM_MIPS: case EM_MIPS_RS3_LE: return process_mips_specific (file); break; case EM_PPC: return process_power_specific (file); break; default: break; } return 1; } static int get_file_header (FILE *file) { /* Read in the identity array. */ if (fread (elf_header.e_ident, EI_NIDENT, 1, file) != 1) return 0; /* Determine how to read the rest of the header. */ switch (elf_header.e_ident[EI_DATA]) { default: /* fall through */ case ELFDATANONE: /* fall through */ case ELFDATA2LSB: byte_get = byte_get_little_endian; byte_put = byte_put_little_endian; break; case ELFDATA2MSB: byte_get = byte_get_big_endian; byte_put = byte_put_big_endian; break; } /* For now we only support 32 bit and 64 bit ELF files. */ is_32bit_elf = (elf_header.e_ident[EI_CLASS] != ELFCLASS64); /* Read in the rest of the header. */ if (is_32bit_elf) { Elf32_External_Ehdr ehdr32; if (fread (ehdr32.e_type, sizeof (ehdr32) - EI_NIDENT, 1, file) != 1) return 0; elf_header.e_type = BYTE_GET (ehdr32.e_type); elf_header.e_machine = BYTE_GET (ehdr32.e_machine); elf_header.e_version = BYTE_GET (ehdr32.e_version); elf_header.e_entry = BYTE_GET (ehdr32.e_entry); elf_header.e_phoff = BYTE_GET (ehdr32.e_phoff); elf_header.e_shoff = BYTE_GET (ehdr32.e_shoff); elf_header.e_flags = BYTE_GET (ehdr32.e_flags); elf_header.e_ehsize = BYTE_GET (ehdr32.e_ehsize); elf_header.e_phentsize = BYTE_GET (ehdr32.e_phentsize); elf_header.e_phnum = BYTE_GET (ehdr32.e_phnum); elf_header.e_shentsize = BYTE_GET (ehdr32.e_shentsize); elf_header.e_shnum = BYTE_GET (ehdr32.e_shnum); elf_header.e_shstrndx = BYTE_GET (ehdr32.e_shstrndx); } else { Elf64_External_Ehdr ehdr64; /* If we have been compiled with sizeof (bfd_vma) == 4, then we will not be able to cope with the 64bit data found in 64 ELF files. Detect this now and abort before we start overwriting things. */ if (sizeof (bfd_vma) < 8) { error (_("This instance of readelf has been built without support for a\n\ 64 bit data type and so it cannot read 64 bit ELF files.\n")); return 0; } if (fread (ehdr64.e_type, sizeof (ehdr64) - EI_NIDENT, 1, file) != 1) return 0; elf_header.e_type = BYTE_GET (ehdr64.e_type); elf_header.e_machine = BYTE_GET (ehdr64.e_machine); elf_header.e_version = BYTE_GET (ehdr64.e_version); elf_header.e_entry = BYTE_GET (ehdr64.e_entry); elf_header.e_phoff = BYTE_GET (ehdr64.e_phoff); elf_header.e_shoff = BYTE_GET (ehdr64.e_shoff); elf_header.e_flags = BYTE_GET (ehdr64.e_flags); elf_header.e_ehsize = BYTE_GET (ehdr64.e_ehsize); elf_header.e_phentsize = BYTE_GET (ehdr64.e_phentsize); elf_header.e_phnum = BYTE_GET (ehdr64.e_phnum); elf_header.e_shentsize = BYTE_GET (ehdr64.e_shentsize); elf_header.e_shnum = BYTE_GET (ehdr64.e_shnum); elf_header.e_shstrndx = BYTE_GET (ehdr64.e_shstrndx); } if (elf_header.e_shoff) { /* There may be some extensions in the first section header. Don't bomb if we can't read it. */ if (is_32bit_elf) get_32bit_section_headers (file, 1); else get_64bit_section_headers (file, 1); } is_relocatable = elf_header.e_type == ET_REL; return 1; } /* Process one ELF object file according to the command line options. This file may actually be stored in an archive. The file is positioned at the start of the ELF object. */ static int process_object (char *file_name, FILE *file) { unsigned int i; if (! get_file_header (file)) { error (_("%s: Failed to read file header\n"), file_name); return 1; } /* Initialise per file variables. */ for (i = NUM_ELEM (version_info); i--;) version_info[i] = 0; for (i = NUM_ELEM (dynamic_info); i--;) dynamic_info[i] = 0; /* Process the file. */ if (show_name) printf (_("\nFile: %s\n"), file_name); /* Initialise the dump_sects array from the cmdline_dump_sects array. Note we do this even if cmdline_dump_sects is empty because we must make sure that the dump_sets array is zeroed out before each object file is processed. */ if (num_dump_sects > num_cmdline_dump_sects) memset (dump_sects, 0, num_dump_sects); if (num_cmdline_dump_sects > 0) { if (num_dump_sects == 0) /* A sneaky way of allocating the dump_sects array. */ request_dump (num_cmdline_dump_sects, 0); assert (num_dump_sects >= num_cmdline_dump_sects); memcpy (dump_sects, cmdline_dump_sects, num_cmdline_dump_sects); } if (! process_file_header ()) return 1; if (! process_section_headers (file)) { /* Without loaded section headers we cannot process lots of things. */ do_unwind = do_version = do_dump = do_arch = 0; if (! do_using_dynamic) do_syms = do_reloc = 0; } if (! process_section_groups (file)) { /* Without loaded section groups we cannot process unwind. */ do_unwind = 0; } if (process_program_headers (file)) process_dynamic_section (file); process_relocs (file); process_unwind (file); process_symbol_table (file); process_syminfo (file); process_version_sections (file); process_section_contents (file); process_notes (file); process_gnu_liblist (file); process_arch_specific (file); if (program_headers) { free (program_headers); program_headers = NULL; } if (section_headers) { free (section_headers); section_headers = NULL; } if (string_table) { free (string_table); string_table = NULL; string_table_length = 0; } if (dynamic_strings) { free (dynamic_strings); dynamic_strings = NULL; dynamic_strings_length = 0; } if (dynamic_symbols) { free (dynamic_symbols); dynamic_symbols = NULL; num_dynamic_syms = 0; } if (dynamic_syminfo) { free (dynamic_syminfo); dynamic_syminfo = NULL; } if (section_headers_groups) { free (section_headers_groups); section_headers_groups = NULL; } if (section_groups) { struct group_list *g, *next; for (i = 0; i < group_count; i++) { for (g = section_groups [i].root; g != NULL; g = next) { next = g->next; free (g); } } free (section_groups); section_groups = NULL; } free_debug_memory (); return 0; } /* Process an ELF archive. The file is positioned just after the ARMAG string. */ static int process_archive (char *file_name, FILE *file) { struct ar_hdr arhdr; size_t got; unsigned long size; char *longnames = NULL; unsigned long longnames_size = 0; size_t file_name_size; int ret; show_name = 1; got = fread (&arhdr, 1, sizeof arhdr, file); if (got != sizeof arhdr) { if (got == 0) return 0; error (_("%s: failed to read archive header\n"), file_name); return 1; } if (const_strneq (arhdr.ar_name, "/ ")) { /* This is the archive symbol table. Skip it. FIXME: We should have an option to dump it. */ size = strtoul (arhdr.ar_size, NULL, 10); if (fseek (file, size + (size & 1), SEEK_CUR) != 0) { error (_("%s: failed to skip archive symbol table\n"), file_name); return 1; } got = fread (&arhdr, 1, sizeof arhdr, file); if (got != sizeof arhdr) { if (got == 0) return 0; error (_("%s: failed to read archive header\n"), file_name); return 1; } } if (const_strneq (arhdr.ar_name, "// ")) { /* This is the archive string table holding long member names. */ longnames_size = strtoul (arhdr.ar_size, NULL, 10); longnames = malloc (longnames_size); if (longnames == NULL) { error (_("Out of memory\n")); return 1; } if (fread (longnames, longnames_size, 1, file) != 1) { free (longnames); error (_("%s: failed to read string table\n"), file_name); return 1; } if ((longnames_size & 1) != 0) (void) getc (file); got = fread (&arhdr, 1, sizeof arhdr, file); if (got != sizeof arhdr) { free (longnames); if (got == 0) return 0; error (_("%s: failed to read archive header\n"), file_name); return 1; } } file_name_size = strlen (file_name); ret = 0; while (1) { char *name; char *nameend; char *namealc; if (arhdr.ar_name[0] == '/') { unsigned long off; off = strtoul (arhdr.ar_name + 1, NULL, 10); if (off >= longnames_size) { error (_("%s: invalid archive string table offset %lu\n"), file_name, off); ret = 1; break; } name = longnames + off; nameend = memchr (name, '/', longnames_size - off); } else { name = arhdr.ar_name; nameend = memchr (name, '/', 16); } if (nameend == NULL) { error (_("%s: bad archive file name\n"), file_name); ret = 1; break; } namealc = malloc (file_name_size + (nameend - name) + 3); if (namealc == NULL) { error (_("Out of memory\n")); ret = 1; break; } memcpy (namealc, file_name, file_name_size); namealc[file_name_size] = '('; memcpy (namealc + file_name_size + 1, name, nameend - name); namealc[file_name_size + 1 + (nameend - name)] = ')'; namealc[file_name_size + 2 + (nameend - name)] = '\0'; archive_file_offset = ftell (file); archive_file_size = strtoul (arhdr.ar_size, NULL, 10); ret |= process_object (namealc, file); free (namealc); if (fseek (file, (archive_file_offset + archive_file_size + (archive_file_size & 1)), SEEK_SET) != 0) { error (_("%s: failed to seek to next archive header\n"), file_name); ret = 1; break; } got = fread (&arhdr, 1, sizeof arhdr, file); if (got != sizeof arhdr) { if (got == 0) break; error (_("%s: failed to read archive header\n"), file_name); ret = 1; break; } } if (longnames != 0) free (longnames); return ret; } static int process_file (char *file_name) { FILE *file; struct stat statbuf; char armag[SARMAG]; int ret; if (stat (file_name, &statbuf) < 0) { if (errno == ENOENT) error (_("'%s': No such file\n"), file_name); else error (_("Could not locate '%s'. System error message: %s\n"), file_name, strerror (errno)); return 1; } if (! S_ISREG (statbuf.st_mode)) { error (_("'%s' is not an ordinary file\n"), file_name); return 1; } file = fopen (file_name, "rb"); if (file == NULL) { error (_("Input file '%s' is not readable.\n"), file_name); return 1; } if (fread (armag, SARMAG, 1, file) != 1) { error (_("%s: Failed to read file header\n"), file_name); fclose (file); return 1; } if (memcmp (armag, ARMAG, SARMAG) == 0) ret = process_archive (file_name, file); else { rewind (file); archive_file_size = archive_file_offset = 0; ret = process_object (file_name, file); } fclose (file); return ret; } #ifdef SUPPORT_DISASSEMBLY /* Needed by the i386 disassembler. For extra credit, someone could fix this so that we insert symbolic addresses here, esp for GOT/PLT symbols. */ void print_address (unsigned int addr, FILE *outfile) { fprintf (outfile,"0x%8.8x", addr); } /* Needed by the i386 disassembler. */ void db_task_printsym (unsigned int addr) { print_address (addr, stderr); } #endif int main (int argc, char **argv) { int err; #if defined (HAVE_SETLOCALE) && defined (HAVE_LC_MESSAGES) setlocale (LC_MESSAGES, ""); #endif #if defined (HAVE_SETLOCALE) setlocale (LC_CTYPE, ""); #endif bindtextdomain (PACKAGE, LOCALEDIR); textdomain (PACKAGE); expandargv (&argc, &argv); parse_args (argc, argv); if (num_dump_sects > 0) { /* Make a copy of the dump_sects array. */ cmdline_dump_sects = malloc (num_dump_sects); if (cmdline_dump_sects == NULL) error (_("Out of memory allocating dump request table.\n")); else { memcpy (cmdline_dump_sects, dump_sects, num_dump_sects); num_cmdline_dump_sects = num_dump_sects; } } if (optind < (argc - 1)) show_name = 1; err = 0; while (optind < argc) err |= process_file (argv[optind++]); if (dump_sects != NULL) free (dump_sects); if (cmdline_dump_sects != NULL) free (cmdline_dump_sects); return err; } Index: head/contrib/binutils/include/elf/common.h =================================================================== --- head/contrib/binutils/include/elf/common.h (revision 274816) +++ head/contrib/binutils/include/elf/common.h (revision 274817) @@ -1,801 +1,802 @@ /* ELF support for BFD. Copyright 1991, 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007 Free Software Foundation, Inc. Written by Fred Fish @ Cygnus Support, from information published in "UNIX System V Release 4, Programmers Guide: ANSI C and Programming Support Tools". This file is part of BFD, the Binary File Descriptor library. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA 02110-1301, USA. */ /* This file is part of ELF support for BFD, and contains the portions that are common to both the internal and external representations. For example, ELFMAG0 is the byte 0x7F in both the internal (in-memory) and external (in-file) representations. */ #ifndef _ELF_COMMON_H #define _ELF_COMMON_H /* Fields in e_ident[]. */ #define EI_MAG0 0 /* File identification byte 0 index */ #define ELFMAG0 0x7F /* Magic number byte 0 */ #define EI_MAG1 1 /* File identification byte 1 index */ #define ELFMAG1 'E' /* Magic number byte 1 */ #define EI_MAG2 2 /* File identification byte 2 index */ #define ELFMAG2 'L' /* Magic number byte 2 */ #define EI_MAG3 3 /* File identification byte 3 index */ #define ELFMAG3 'F' /* Magic number byte 3 */ #define EI_CLASS 4 /* File class */ #define ELFCLASSNONE 0 /* Invalid class */ #define ELFCLASS32 1 /* 32-bit objects */ #define ELFCLASS64 2 /* 64-bit objects */ #define EI_DATA 5 /* Data encoding */ #define ELFDATANONE 0 /* Invalid data encoding */ #define ELFDATA2LSB 1 /* 2's complement, little endian */ #define ELFDATA2MSB 2 /* 2's complement, big endian */ #define EI_VERSION 6 /* File version */ #define EI_OSABI 7 /* Operating System/ABI indication */ #define ELFOSABI_NONE 0 /* UNIX System V ABI */ #define ELFOSABI_HPUX 1 /* HP-UX operating system */ #define ELFOSABI_NETBSD 2 /* NetBSD */ #define ELFOSABI_LINUX 3 /* GNU/Linux */ #define ELFOSABI_HURD 4 /* GNU/Hurd */ #define ELFOSABI_SOLARIS 6 /* Solaris */ #define ELFOSABI_AIX 7 /* AIX */ #define ELFOSABI_IRIX 8 /* IRIX */ #define ELFOSABI_FREEBSD 9 /* FreeBSD */ #define ELFOSABI_TRU64 10 /* TRU64 UNIX */ #define ELFOSABI_MODESTO 11 /* Novell Modesto */ #define ELFOSABI_OPENBSD 12 /* OpenBSD */ #define ELFOSABI_OPENVMS 13 /* OpenVMS */ #define ELFOSABI_NSK 14 /* Hewlett-Packard Non-Stop Kernel */ #define ELFOSABI_AROS 15 /* Amiga Research OS */ #define ELFOSABI_ARM 97 /* ARM */ #define ELFOSABI_STANDALONE 255 /* Standalone (embedded) application */ #define EI_ABIVERSION 8 /* ABI version */ #define EI_PAD 9 /* Start of padding bytes */ /* Values for e_type, which identifies the object file type. */ #define ET_NONE 0 /* No file type */ #define ET_REL 1 /* Relocatable file */ #define ET_EXEC 2 /* Executable file */ #define ET_DYN 3 /* Shared object file */ #define ET_CORE 4 /* Core file */ #define ET_LOOS 0xFE00 /* Operating system-specific */ #define ET_HIOS 0xFEFF /* Operating system-specific */ #define ET_LOPROC 0xFF00 /* Processor-specific */ #define ET_HIPROC 0xFFFF /* Processor-specific */ /* Values for e_machine, which identifies the architecture. These numbers are officially assigned by registry@caldera.com. See below for a list of ad-hoc numbers used during initial development. */ #define EM_NONE 0 /* No machine */ #define EM_M32 1 /* AT&T WE 32100 */ #define EM_SPARC 2 /* SUN SPARC */ #define EM_386 3 /* Intel 80386 */ #define EM_68K 4 /* Motorola m68k family */ #define EM_88K 5 /* Motorola m88k family */ #define EM_486 6 /* Intel 80486 *//* Reserved for future use */ #define EM_860 7 /* Intel 80860 */ #define EM_MIPS 8 /* MIPS R3000 (officially, big-endian only) */ #define EM_S370 9 /* IBM System/370 */ #define EM_MIPS_RS3_LE 10 /* MIPS R3000 little-endian (Oct 4 1999 Draft) Deprecated */ #define EM_PARISC 15 /* HPPA */ #define EM_VPP550 17 /* Fujitsu VPP500 */ #define EM_SPARC32PLUS 18 /* Sun's "v8plus" */ #define EM_960 19 /* Intel 80960 */ #define EM_PPC 20 /* PowerPC */ #define EM_PPC64 21 /* 64-bit PowerPC */ #define EM_S390 22 /* IBM S/390 */ #define EM_SPU 23 /* Sony/Toshiba/IBM SPU */ #define EM_V800 36 /* NEC V800 series */ #define EM_FR20 37 /* Fujitsu FR20 */ #define EM_RH32 38 /* TRW RH32 */ #define EM_MCORE 39 /* Motorola M*Core */ /* May also be taken by Fujitsu MMA */ #define EM_RCE 39 /* Old name for MCore */ #define EM_ARM 40 /* ARM */ #define EM_OLD_ALPHA 41 /* Digital Alpha */ #define EM_SH 42 /* Renesas (formerly Hitachi) / SuperH SH */ #define EM_SPARCV9 43 /* SPARC v9 64-bit */ #define EM_TRICORE 44 /* Siemens Tricore embedded processor */ #define EM_ARC 45 /* ARC Cores */ #define EM_H8_300 46 /* Renesas (formerly Hitachi) H8/300 */ #define EM_H8_300H 47 /* Renesas (formerly Hitachi) H8/300H */ #define EM_H8S 48 /* Renesas (formerly Hitachi) H8S */ #define EM_H8_500 49 /* Renesas (formerly Hitachi) H8/500 */ #define EM_IA_64 50 /* Intel IA-64 Processor */ #define EM_MIPS_X 51 /* Stanford MIPS-X */ #define EM_COLDFIRE 52 /* Motorola Coldfire */ #define EM_68HC12 53 /* Motorola M68HC12 */ #define EM_MMA 54 /* Fujitsu Multimedia Accelerator */ #define EM_PCP 55 /* Siemens PCP */ #define EM_NCPU 56 /* Sony nCPU embedded RISC processor */ #define EM_NDR1 57 /* Denso NDR1 microprocesspr */ #define EM_STARCORE 58 /* Motorola Star*Core processor */ #define EM_ME16 59 /* Toyota ME16 processor */ #define EM_ST100 60 /* STMicroelectronics ST100 processor */ #define EM_TINYJ 61 /* Advanced Logic Corp. TinyJ embedded processor */ #define EM_X86_64 62 /* Advanced Micro Devices X86-64 processor */ #define EM_PDP10 64 /* Digital Equipment Corp. PDP-10 */ #define EM_PDP11 65 /* Digital Equipment Corp. PDP-11 */ #define EM_FX66 66 /* Siemens FX66 microcontroller */ #define EM_ST9PLUS 67 /* STMicroelectronics ST9+ 8/16 bit microcontroller */ #define EM_ST7 68 /* STMicroelectronics ST7 8-bit microcontroller */ #define EM_68HC16 69 /* Motorola MC68HC16 Microcontroller */ #define EM_68HC11 70 /* Motorola MC68HC11 Microcontroller */ #define EM_68HC08 71 /* Motorola MC68HC08 Microcontroller */ #define EM_68HC05 72 /* Motorola MC68HC05 Microcontroller */ #define EM_SVX 73 /* Silicon Graphics SVx */ #define EM_ST19 74 /* STMicroelectronics ST19 8-bit cpu */ #define EM_VAX 75 /* Digital VAX */ #define EM_CRIS 76 /* Axis Communications 32-bit embedded processor */ #define EM_JAVELIN 77 /* Infineon Technologies 32-bit embedded cpu */ #define EM_FIREPATH 78 /* Element 14 64-bit DSP processor */ #define EM_ZSP 79 /* LSI Logic's 16-bit DSP processor */ #define EM_MMIX 80 /* Donald Knuth's educational 64-bit processor */ #define EM_HUANY 81 /* Harvard's machine-independent format */ #define EM_PRISM 82 /* SiTera Prism */ #define EM_AVR 83 /* Atmel AVR 8-bit microcontroller */ #define EM_FR30 84 /* Fujitsu FR30 */ #define EM_D10V 85 /* Mitsubishi D10V */ #define EM_D30V 86 /* Mitsubishi D30V */ #define EM_V850 87 /* NEC v850 */ #define EM_M32R 88 /* Renesas M32R (formerly Mitsubishi M32R) */ #define EM_MN10300 89 /* Matsushita MN10300 */ #define EM_MN10200 90 /* Matsushita MN10200 */ #define EM_PJ 91 /* picoJava */ #define EM_OPENRISC 92 /* OpenRISC 32-bit embedded processor */ #define EM_ARC_A5 93 /* ARC Cores Tangent-A5 */ #define EM_XTENSA 94 /* Tensilica Xtensa Architecture */ #define EM_IP2K 101 /* Ubicom IP2022 micro controller */ #define EM_CR 103 /* National Semiconductor CompactRISC */ #define EM_MSP430 105 /* TI msp430 micro controller */ #define EM_BLACKFIN 106 /* ADI Blackfin */ #define EM_ALTERA_NIOS2 113 /* Altera Nios II soft-core processor */ #define EM_CRX 114 /* National Semiconductor CRX */ #define EM_CR16 115 /* National Semiconductor CompactRISC - CR16 */ #define EM_SCORE 135 /* Sunplus Score */ /* If it is necessary to assign new unofficial EM_* values, please pick large random numbers (0x8523, 0xa7f2, etc.) to minimize the chances of collision with official or non-GNU unofficial values. NOTE: Do not just increment the most recent number by one. Somebody else somewhere will do exactly the same thing, and you will have a collision. Instead, pick a random number. Normally, each entity or maintainer responsible for a machine with an unofficial e_machine number should eventually ask registry@caldera.com for an officially blessed number to be added to the list above. */ /* Old version of Sparc v9, from before the ABI; This should be removed shortly. */ #define EM_OLD_SPARCV9 11 /* Old version of PowerPC, this should be removed shortly. */ #define EM_PPC_OLD 17 /* picoJava */ #define EM_PJ_OLD 99 /* AVR magic number. Written in the absense of an ABI. */ #define EM_AVR_OLD 0x1057 /* MSP430 magic number. Written in the absense of everything. */ #define EM_MSP430_OLD 0x1059 /* Morpho MT. Written in the absense of an ABI. */ #define EM_MT 0x2530 /* FR30 magic number - no EABI available. */ #define EM_CYGNUS_FR30 0x3330 /* OpenRISC magic number. Written in the absense of an ABI. */ #define EM_OPENRISC_OLD 0x3426 /* DLX magic number. Written in the absense of an ABI. */ #define EM_DLX 0x5aa5 /* FRV magic number - no EABI available??. */ #define EM_CYGNUS_FRV 0x5441 /* Infineon Technologies 16-bit microcontroller with C166-V2 core. */ #define EM_XC16X 0x4688 /* D10V backend magic number. Written in the absence of an ABI. */ #define EM_CYGNUS_D10V 0x7650 /* D30V backend magic number. Written in the absence of an ABI. */ #define EM_CYGNUS_D30V 0x7676 /* Ubicom IP2xxx; Written in the absense of an ABI. */ #define EM_IP2K_OLD 0x8217 /* (Deprecated) Temporary number for the OpenRISC processor. */ #define EM_OR32 0x8472 /* Cygnus PowerPC ELF backend. Written in the absence of an ABI. */ #define EM_CYGNUS_POWERPC 0x9025 /* Alpha backend magic number. Written in the absence of an ABI. */ #define EM_ALPHA 0x9026 /* Cygnus M32R ELF backend. Written in the absence of an ABI. */ #define EM_CYGNUS_M32R 0x9041 /* V850 backend magic number. Written in the absense of an ABI. */ #define EM_CYGNUS_V850 0x9080 /* old S/390 backend magic number. Written in the absence of an ABI. */ #define EM_S390_OLD 0xa390 /* Old, unofficial value for Xtensa. */ #define EM_XTENSA_OLD 0xabc7 #define EM_XSTORMY16 0xad45 /* mn10200 and mn10300 backend magic numbers. Written in the absense of an ABI. */ #define EM_CYGNUS_MN10300 0xbeef #define EM_CYGNUS_MN10200 0xdead /* Renesas M32C and M16C. */ #define EM_M32C 0xFEB0 /* Vitesse IQ2000. */ #define EM_IQ2000 0xFEBA /* NIOS magic number - no EABI available. */ #define EM_NIOS32 0xFEBB #define EM_CYGNUS_MEP 0xF00D /* Toshiba MeP */ /* See the above comment before you add a new EM_* value here. */ /* Values for e_version. */ #define EV_NONE 0 /* Invalid ELF version */ #define EV_CURRENT 1 /* Current version */ /* Values for program header, p_type field. */ #define PT_NULL 0 /* Program header table entry unused */ #define PT_LOAD 1 /* Loadable program segment */ #define PT_DYNAMIC 2 /* Dynamic linking information */ #define PT_INTERP 3 /* Program interpreter */ #define PT_NOTE 4 /* Auxiliary information */ #define PT_SHLIB 5 /* Reserved, unspecified semantics */ #define PT_PHDR 6 /* Entry for header table itself */ #define PT_TLS 7 /* Thread local storage segment */ #define PT_LOOS 0x60000000 /* OS-specific */ #define PT_HIOS 0x6fffffff /* OS-specific */ #define PT_LOPROC 0x70000000 /* Processor-specific */ #define PT_HIPROC 0x7FFFFFFF /* Processor-specific */ #define PT_GNU_EH_FRAME (PT_LOOS + 0x474e550) /* Frame unwind information */ #define PT_SUNW_EH_FRAME PT_GNU_EH_FRAME /* Solaris uses the same value */ #define PT_GNU_STACK (PT_LOOS + 0x474e551) /* Stack flags */ #define PT_GNU_RELRO (PT_LOOS + 0x474e552) /* Read-only after relocation */ /* Program segment permissions, in program header p_flags field. */ #define PF_X (1 << 0) /* Segment is executable */ #define PF_W (1 << 1) /* Segment is writable */ #define PF_R (1 << 2) /* Segment is readable */ /* #define PF_MASKOS 0x0F000000 *//* OS-specific reserved bits */ #define PF_MASKOS 0x0FF00000 /* New value, Oct 4, 1999 Draft */ #define PF_MASKPROC 0xF0000000 /* Processor-specific reserved bits */ /* Values for section header, sh_type field. */ #define SHT_NULL 0 /* Section header table entry unused */ #define SHT_PROGBITS 1 /* Program specific (private) data */ #define SHT_SYMTAB 2 /* Link editing symbol table */ #define SHT_STRTAB 3 /* A string table */ #define SHT_RELA 4 /* Relocation entries with addends */ #define SHT_HASH 5 /* A symbol hash table */ #define SHT_DYNAMIC 6 /* Information for dynamic linking */ #define SHT_NOTE 7 /* Information that marks file */ #define SHT_NOBITS 8 /* Section occupies no space in file */ #define SHT_REL 9 /* Relocation entries, no addends */ #define SHT_SHLIB 10 /* Reserved, unspecified semantics */ #define SHT_DYNSYM 11 /* Dynamic linking symbol table */ #define SHT_INIT_ARRAY 14 /* Array of ptrs to init functions */ #define SHT_FINI_ARRAY 15 /* Array of ptrs to finish functions */ #define SHT_PREINIT_ARRAY 16 /* Array of ptrs to pre-init funcs */ #define SHT_GROUP 17 /* Section contains a section group */ #define SHT_SYMTAB_SHNDX 18 /* Indicies for SHN_XINDEX entries */ #define SHT_LOOS 0x60000000 /* First of OS specific semantics */ #define SHT_HIOS 0x6fffffff /* Last of OS specific semantics */ #define SHT_GNU_ATTRIBUTES 0x6ffffff5 /* Object attributes */ #define SHT_GNU_HASH 0x6ffffff6 /* GNU style symbol hash table */ #define SHT_GNU_LIBLIST 0x6ffffff7 /* List of prelink dependencies */ /* The next three section types are defined by Solaris, and are named SHT_SUNW*. We use them in GNU code, so we also define SHT_GNU* versions. */ #define SHT_SUNW_verdef 0x6ffffffd /* Versions defined by file */ #define SHT_SUNW_verneed 0x6ffffffe /* Versions needed by file */ #define SHT_SUNW_versym 0x6fffffff /* Symbol versions */ #define SHT_GNU_verdef SHT_SUNW_verdef #define SHT_GNU_verneed SHT_SUNW_verneed #define SHT_GNU_versym SHT_SUNW_versym #define SHT_LOPROC 0x70000000 /* Processor-specific semantics, lo */ #define SHT_HIPROC 0x7FFFFFFF /* Processor-specific semantics, hi */ #define SHT_LOUSER 0x80000000 /* Application-specific semantics */ /* #define SHT_HIUSER 0x8FFFFFFF *//* Application-specific semantics */ #define SHT_HIUSER 0xFFFFFFFF /* New value, defined in Oct 4, 1999 Draft */ /* Values for section header, sh_flags field. */ #define SHF_WRITE (1 << 0) /* Writable data during execution */ #define SHF_ALLOC (1 << 1) /* Occupies memory during execution */ #define SHF_EXECINSTR (1 << 2) /* Executable machine instructions */ #define SHF_MERGE (1 << 4) /* Data in this section can be merged */ #define SHF_STRINGS (1 << 5) /* Contains null terminated character strings */ #define SHF_INFO_LINK (1 << 6) /* sh_info holds section header table index */ #define SHF_LINK_ORDER (1 << 7) /* Preserve section ordering when linking */ #define SHF_OS_NONCONFORMING (1 << 8) /* OS specific processing required */ #define SHF_GROUP (1 << 9) /* Member of a section group */ #define SHF_TLS (1 << 10) /* Thread local storage section */ /* #define SHF_MASKOS 0x0F000000 *//* OS-specific semantics */ #define SHF_MASKOS 0x0FF00000 /* New value, Oct 4, 1999 Draft */ #define SHF_MASKPROC 0xF0000000 /* Processor-specific semantics */ /* Values of note segment descriptor types for core files. */ #define NT_PRSTATUS 1 /* Contains copy of prstatus struct */ #define NT_FPREGSET 2 /* Contains copy of fpregset struct */ #define NT_PRPSINFO 3 /* Contains copy of prpsinfo struct */ #define NT_TASKSTRUCT 4 /* Contains copy of task struct */ #define NT_AUXV 6 /* Contains copy of Elfxx_auxv_t */ #define NT_FILE 0x46494c45 #define NT_PRXFPREG 0x46e62b7f /* Contains a user_xfpregs_struct; */ /* note name must be "LINUX". */ #define NT_SIGINFO 0x53494749 /* Note segments for core files on dir-style procfs systems. */ #define NT_PSTATUS 10 /* Has a struct pstatus */ #define NT_FPREGS 12 /* Has a struct fpregset */ #define NT_PSINFO 13 /* Has a struct psinfo */ #define NT_LWPSTATUS 16 /* Has a struct lwpstatus_t */ #define NT_LWPSINFO 17 /* Has a struct lwpsinfo_t */ #define NT_WIN32PSTATUS 18 /* Has a struct win32_pstatus */ /* Note segments for core files on FreeBSD systems. Note name must start with "FreeBSD". */ #define NT_THRMISC 7 /* Contains copy of thrmisc struct */ #define NT_PROCSTAT_PROC 8 #define NT_PROCSTAT_FILES 9 #define NT_PROCSTAT_VMMAP 10 #define NT_PROCSTAT_GROUPS 11 #define NT_PROCSTAT_UMASK 12 #define NT_PROCSTAT_RLIMIT 13 #define NT_PROCSTAT_OSREL 14 #define NT_PROCSTAT_PSSTRINGS 15 #define NT_PROCSTAT_AUXV 16 +#define NT_X86_XSTATE 0x202 /* Note segments for core files on NetBSD systems. Note name must start with "NetBSD-CORE". */ #define NT_NETBSDCORE_PROCINFO 1 /* Has a struct procinfo */ #define NT_NETBSDCORE_FIRSTMACH 32 /* start of machdep note types */ /* Values of note segment descriptor types for object files. */ #define NT_VERSION 1 /* Contains a version string. */ #define NT_ARCH 2 /* Contains an architecture string. */ /* Values for GNU .note.ABI-tag notes. Note name is "GNU". */ #define NT_GNU_ABI_TAG 1 #define GNU_ABI_TAG_LINUX 0 #define GNU_ABI_TAG_HURD 1 #define GNU_ABI_TAG_SOLARIS 2 #define GNU_ABI_TAG_FREEBSD 3 #define GNU_ABI_TAG_NETBSD 4 /* Values for GNU .note.gnu.build-id notes. Note name is "GNU"." */ #define NT_GNU_BUILD_ID 3 /* Values for NetBSD .note.netbsd.ident notes. Note name is "NetBSD". */ #define NT_NETBSD_IDENT 1 /* Values for OpenBSD .note.openbsd.ident notes. Note name is "OpenBSD". */ #define NT_OPENBSD_IDENT 1 /* Values for FreeBSD .note.ABI-tag notes. Note name is "FreeBSD". */ #define NT_FREEBSD_ABI_TAG 1 /* Values for FreeBSD .note.tag notes. Note name is "FreeBSD". */ #define NT_FREEBSD_TAG 1 #define NT_FREEBSD_NOINIT_TAG 2 #define NT_FREEBSD_ARCH_TAG 3 /* These three macros disassemble and assemble a symbol table st_info field, which contains the symbol binding and symbol type. The STB_ and STT_ defines identify the binding and type. */ #define ELF_ST_BIND(val) (((unsigned int)(val)) >> 4) #define ELF_ST_TYPE(val) ((val) & 0xF) #define ELF_ST_INFO(bind,type) (((bind) << 4) + ((type) & 0xF)) /* The 64bit and 32bit versions of these macros are identical, but the ELF spec defines them, so here they are. */ #define ELF32_ST_BIND ELF_ST_BIND #define ELF32_ST_TYPE ELF_ST_TYPE #define ELF32_ST_INFO ELF_ST_INFO #define ELF64_ST_BIND ELF_ST_BIND #define ELF64_ST_TYPE ELF_ST_TYPE #define ELF64_ST_INFO ELF_ST_INFO /* This macro disassembles and assembles a symbol's visibility into the st_other field. The STV_ defines specify the actual visibility. */ #define ELF_ST_VISIBILITY(v) ((v) & 0x3) /* The remaining bits in the st_other field are not currently used. They should be set to zero. */ #define ELF32_ST_VISIBILITY ELF_ST_VISIBILITY #define ELF64_ST_VISIBILITY ELF_ST_VISIBILITY #define STN_UNDEF 0 /* Undefined symbol index */ #define STB_LOCAL 0 /* Symbol not visible outside obj */ #define STB_GLOBAL 1 /* Symbol visible outside obj */ #define STB_WEAK 2 /* Like globals, lower precedence */ #define STB_LOOS 10 /* OS-specific semantics */ #define STB_HIOS 12 /* OS-specific semantics */ #define STB_LOPROC 13 /* Application-specific semantics */ #define STB_HIPROC 15 /* Application-specific semantics */ #define STT_NOTYPE 0 /* Symbol type is unspecified */ #define STT_OBJECT 1 /* Symbol is a data object */ #define STT_FUNC 2 /* Symbol is a code object */ #define STT_SECTION 3 /* Symbol associated with a section */ #define STT_FILE 4 /* Symbol gives a file name */ #define STT_COMMON 5 /* An uninitialised common block */ #define STT_TLS 6 /* Thread local data object */ #define STT_RELC 8 /* Complex relocation expression */ #define STT_SRELC 9 /* Signed Complex relocation expression */ #define STT_LOOS 10 /* OS-specific semantics */ #define STT_HIOS 12 /* OS-specific semantics */ #define STT_LOPROC 13 /* Application-specific semantics */ #define STT_HIPROC 15 /* Application-specific semantics */ /* Special section indices, which may show up in st_shndx fields, among other places. */ #define SHN_UNDEF 0 /* Undefined section reference */ #define SHN_LORESERVE 0xFF00 /* Begin range of reserved indices */ #define SHN_LOPROC 0xFF00 /* Begin range of appl-specific */ #define SHN_HIPROC 0xFF1F /* End range of appl-specific */ #define SHN_LOOS 0xFF20 /* OS specific semantics, lo */ #define SHN_HIOS 0xFF3F /* OS specific semantics, hi */ #define SHN_ABS 0xFFF1 /* Associated symbol is absolute */ #define SHN_COMMON 0xFFF2 /* Associated symbol is in common */ #define SHN_XINDEX 0xFFFF /* Section index is held elsewhere */ #define SHN_HIRESERVE 0xFFFF /* End range of reserved indices */ #define SHN_BAD ((unsigned) -1) /* Used internally by bfd */ /* The following constants control how a symbol may be accessed once it has become part of an executable or shared library. */ #define STV_DEFAULT 0 /* Visibility is specified by binding type */ #define STV_INTERNAL 1 /* OS specific version of STV_HIDDEN */ #define STV_HIDDEN 2 /* Can only be seen inside currect component */ #define STV_PROTECTED 3 /* Treat as STB_LOCAL inside current component */ /* Relocation info handling macros. */ #define ELF32_R_SYM(i) ((i) >> 8) #define ELF32_R_TYPE(i) ((i) & 0xff) #define ELF32_R_INFO(s,t) (((s) << 8) + ((t) & 0xff)) #define ELF64_R_SYM(i) ((i) >> 32) #define ELF64_R_TYPE(i) ((i) & 0xffffffff) #define ELF64_R_INFO(s,t) (((bfd_vma) (s) << 31 << 1) + (bfd_vma) (t)) /* Dynamic section tags. */ #define DT_NULL 0 #define DT_NEEDED 1 #define DT_PLTRELSZ 2 #define DT_PLTGOT 3 #define DT_HASH 4 #define DT_STRTAB 5 #define DT_SYMTAB 6 #define DT_RELA 7 #define DT_RELASZ 8 #define DT_RELAENT 9 #define DT_STRSZ 10 #define DT_SYMENT 11 #define DT_INIT 12 #define DT_FINI 13 #define DT_SONAME 14 #define DT_RPATH 15 #define DT_SYMBOLIC 16 #define DT_REL 17 #define DT_RELSZ 18 #define DT_RELENT 19 #define DT_PLTREL 20 #define DT_DEBUG 21 #define DT_TEXTREL 22 #define DT_JMPREL 23 #define DT_BIND_NOW 24 #define DT_INIT_ARRAY 25 #define DT_FINI_ARRAY 26 #define DT_INIT_ARRAYSZ 27 #define DT_FINI_ARRAYSZ 28 #define DT_RUNPATH 29 #define DT_FLAGS 30 #define DT_ENCODING 32 #define DT_PREINIT_ARRAY 32 #define DT_PREINIT_ARRAYSZ 33 /* Note, the Oct 4, 1999 draft of the ELF ABI changed the values for DT_LOOS and DT_HIOS. Some implementations however, use values outside of the new range (see below). */ #define OLD_DT_LOOS 0x60000000 #define DT_LOOS 0x6000000d #define DT_HIOS 0x6ffff000 #define OLD_DT_HIOS 0x6fffffff #define DT_LOPROC 0x70000000 #define DT_HIPROC 0x7fffffff /* The next four dynamic tags are used on Solaris. We support them everywhere. Note these values lie outside of the (new) range for OS specific values. This is a deliberate special case and we maintain it for backwards compatability. */ #define DT_VALRNGLO 0x6ffffd00 #define DT_GNU_PRELINKED 0x6ffffdf5 #define DT_GNU_CONFLICTSZ 0x6ffffdf6 #define DT_GNU_LIBLISTSZ 0x6ffffdf7 #define DT_CHECKSUM 0x6ffffdf8 #define DT_PLTPADSZ 0x6ffffdf9 #define DT_MOVEENT 0x6ffffdfa #define DT_MOVESZ 0x6ffffdfb #define DT_FEATURE 0x6ffffdfc #define DT_POSFLAG_1 0x6ffffdfd #define DT_SYMINSZ 0x6ffffdfe #define DT_SYMINENT 0x6ffffdff #define DT_VALRNGHI 0x6ffffdff #define DT_ADDRRNGLO 0x6ffffe00 #define DT_GNU_HASH 0x6ffffef5 #define DT_TLSDESC_PLT 0x6ffffef6 #define DT_TLSDESC_GOT 0x6ffffef7 #define DT_GNU_CONFLICT 0x6ffffef8 #define DT_GNU_LIBLIST 0x6ffffef9 #define DT_CONFIG 0x6ffffefa #define DT_DEPAUDIT 0x6ffffefb #define DT_AUDIT 0x6ffffefc #define DT_PLTPAD 0x6ffffefd #define DT_MOVETAB 0x6ffffefe #define DT_SYMINFO 0x6ffffeff #define DT_ADDRRNGHI 0x6ffffeff #define DT_RELACOUNT 0x6ffffff9 #define DT_RELCOUNT 0x6ffffffa #define DT_FLAGS_1 0x6ffffffb #define DT_VERDEF 0x6ffffffc #define DT_VERDEFNUM 0x6ffffffd #define DT_VERNEED 0x6ffffffe #define DT_VERNEEDNUM 0x6fffffff /* This tag is a GNU extension to the Solaris version scheme. */ #define DT_VERSYM 0x6ffffff0 #define DT_LOPROC 0x70000000 #define DT_HIPROC 0x7fffffff /* These section tags are used on Solaris. We support them everywhere, and hope they do not conflict. */ #define DT_AUXILIARY 0x7ffffffd #define DT_USED 0x7ffffffe #define DT_FILTER 0x7fffffff /* Values used in DT_FEATURE .dynamic entry. */ #define DTF_1_PARINIT 0x00000001 /* From http://docs.sun.com:80/ab2/coll.45.13/LLM/@Ab2PageView/21165?Ab2Lang=C&Ab2Enc=iso-8859-1 DTF_1_CONFEXP is the same as DTF_1_PARINIT. It is a typo. The value defined here is the same as the one in on Solaris 8. */ #define DTF_1_CONFEXP 0x00000002 /* Flag values used in the DT_POSFLAG_1 .dynamic entry. */ #define DF_P1_LAZYLOAD 0x00000001 #define DF_P1_GROUPPERM 0x00000002 /* Flag value in in the DT_FLAGS_1 .dynamic entry. */ #define DF_1_NOW 0x00000001 #define DF_1_GLOBAL 0x00000002 #define DF_1_GROUP 0x00000004 #define DF_1_NODELETE 0x00000008 #define DF_1_LOADFLTR 0x00000010 #define DF_1_INITFIRST 0x00000020 #define DF_1_NOOPEN 0x00000040 #define DF_1_ORIGIN 0x00000080 #define DF_1_DIRECT 0x00000100 #define DF_1_TRANS 0x00000200 #define DF_1_INTERPOSE 0x00000400 #define DF_1_NODEFLIB 0x00000800 #define DF_1_NODUMP 0x00001000 #define DF_1_CONLFAT 0x00002000 /* Flag values for the DT_FLAGS entry. */ #define DF_ORIGIN (1 << 0) #define DF_SYMBOLIC (1 << 1) #define DF_TEXTREL (1 << 2) #define DF_BIND_NOW (1 << 3) #define DF_STATIC_TLS (1 << 4) /* These constants are used for the version number of a Elf32_Verdef structure. */ #define VER_DEF_NONE 0 #define VER_DEF_CURRENT 1 /* These constants appear in the vd_flags field of a Elf32_Verdef structure. */ #define VER_FLG_BASE 0x1 #define VER_FLG_WEAK 0x2 /* These special constants can be found in an Elf32_Versym field. */ #define VER_NDX_LOCAL 0 #define VER_NDX_GLOBAL 1 /* These constants are used for the version number of a Elf32_Verneed structure. */ #define VER_NEED_NONE 0 #define VER_NEED_CURRENT 1 /* This flag appears in a Versym structure. It means that the symbol is hidden, and is only visible with an explicit version number. This is a GNU extension. */ #define VERSYM_HIDDEN 0x8000 /* This is the mask for the rest of the Versym information. */ #define VERSYM_VERSION 0x7fff /* This is a special token which appears as part of a symbol name. It indictes that the rest of the name is actually the name of a version node, and is not part of the actual name. This is a GNU extension. For example, the symbol name `stat@ver2' is taken to mean the symbol `stat' in version `ver2'. */ #define ELF_VER_CHR '@' /* Possible values for si_boundto. */ #define SYMINFO_BT_SELF 0xffff /* Symbol bound to self */ #define SYMINFO_BT_PARENT 0xfffe /* Symbol bound to parent */ #define SYMINFO_BT_LOWRESERVE 0xff00 /* Beginning of reserved entries */ /* Possible bitmasks for si_flags. */ #define SYMINFO_FLG_DIRECT 0x0001 /* Direct bound symbol */ #define SYMINFO_FLG_PASSTHRU 0x0002 /* Pass-thru symbol for translator */ #define SYMINFO_FLG_COPY 0x0004 /* Symbol is a copy-reloc */ #define SYMINFO_FLG_LAZYLOAD 0x0008 /* Symbol bound to object to be lazy loaded */ /* Syminfo version values. */ #define SYMINFO_NONE 0 #define SYMINFO_CURRENT 1 #define SYMINFO_NUM 2 /* Section Group Flags. */ #define GRP_COMDAT 0x1 /* A COMDAT group */ /* Auxv a_type values. */ #define AT_NULL 0 /* End of vector */ #define AT_IGNORE 1 /* Entry should be ignored */ #define AT_EXECFD 2 /* File descriptor of program */ #define AT_PHDR 3 /* Program headers for program */ #define AT_PHENT 4 /* Size of program header entry */ #define AT_PHNUM 5 /* Number of program headers */ #define AT_PAGESZ 6 /* System page size */ #define AT_BASE 7 /* Base address of interpreter */ #define AT_FLAGS 8 /* Flags */ #define AT_ENTRY 9 /* Entry point of program */ #define AT_NOTELF 10 /* Program is not ELF */ #define AT_UID 11 /* Real uid */ #define AT_EUID 12 /* Effective uid */ #define AT_GID 13 /* Real gid */ #define AT_EGID 14 /* Effective gid */ #define AT_CLKTCK 17 /* Frequency of times() */ #define AT_PLATFORM 15 /* String identifying platform. */ #define AT_HWCAP 16 /* Machine dependent hints about processor capabilities. */ #define AT_FPUCW 18 /* Used FPU control word. */ #define AT_DCACHEBSIZE 19 /* Data cache block size. */ #define AT_ICACHEBSIZE 20 /* Instruction cache block size. */ #define AT_UCACHEBSIZE 21 /* Unified cache block size. */ #define AT_IGNOREPPC 22 /* Entry should be ignored */ #define AT_SECURE 23 /* Boolean, was exec setuid-like? */ /* Pointer to the global system page used for system calls and other nice things. */ #define AT_SYSINFO 32 #define AT_SYSINFO_EHDR 33 /* Pointer to ELF header of system-supplied DSO. */ #define AT_SUN_UID 2000 /* Effective user ID. */ #define AT_SUN_RUID 2001 /* Real user ID. */ #define AT_SUN_GID 2002 /* Effective group ID. */ #define AT_SUN_RGID 2003 /* Real group ID. */ #define AT_SUN_LDELF 2004 /* Dynamic linker's ELF header. */ #define AT_SUN_LDSHDR 2005 /* Dynamic linker's section headers. */ #define AT_SUN_LDNAME 2006 /* String giving name of dynamic linker. */ #define AT_SUN_LPAGESZ 2007 /* Large pagesize. */ #define AT_SUN_PLATFORM 2008 /* Platform name string. */ #define AT_SUN_HWCAP 2009 /* Machine dependent hints about processor capabilities. */ #define AT_SUN_IFLUSH 2010 /* Should flush icache? */ #define AT_SUN_CPU 2011 /* CPU name string. */ #define AT_SUN_EMUL_ENTRY 2012 /* COFF entry point address. */ #define AT_SUN_EMUL_EXECFD 2013 /* COFF executable file descriptor. */ #define AT_SUN_EXECNAME 2014 /* Canonicalized file name given to execve. */ #define AT_SUN_MMU 2015 /* String for name of MMU module. */ #define AT_SUN_LDDATA 2016 /* Dynamic linker's data segment address. */ #endif /* _ELF_COMMON_H */ Index: head/sys/amd64/amd64/elf_machdep.c =================================================================== --- head/sys/amd64/amd64/elf_machdep.c (revision 274816) +++ head/sys/amd64/amd64/elf_machdep.c (revision 274817) @@ -1,278 +1,294 @@ /*- * Copyright 1996-1998 John D. Polstra. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include +#include #include struct sysentvec elf64_freebsd_sysvec = { .sv_size = SYS_MAXSYSCALL, .sv_table = sysent, .sv_mask = 0, .sv_sigsize = 0, .sv_sigtbl = NULL, .sv_errsize = 0, .sv_errtbl = NULL, .sv_transtrap = NULL, .sv_fixup = __elfN(freebsd_fixup), .sv_sendsig = sendsig, .sv_sigcode = sigcode, .sv_szsigcode = &szsigcode, .sv_prepsyscall = NULL, .sv_name = "FreeBSD ELF64", .sv_coredump = __elfN(coredump), .sv_imgact_try = NULL, .sv_minsigstksz = MINSIGSTKSZ, .sv_pagesize = PAGE_SIZE, .sv_minuser = VM_MIN_ADDRESS, .sv_maxuser = VM_MAXUSER_ADDRESS, .sv_usrstack = USRSTACK, .sv_psstrings = PS_STRINGS, .sv_stackprot = VM_PROT_ALL, .sv_copyout_strings = exec_copyout_strings, .sv_setregs = exec_setregs, .sv_fixlimit = NULL, .sv_maxssiz = NULL, .sv_flags = SV_ABI_FREEBSD | SV_LP64 | SV_SHP, .sv_set_syscall_retval = cpu_set_syscall_retval, .sv_fetch_syscall_args = cpu_fetch_syscall_args, .sv_syscallnames = syscallnames, .sv_shared_page_base = SHAREDPAGE, .sv_shared_page_len = PAGE_SIZE, .sv_schedtail = NULL, }; INIT_SYSENTVEC(elf64_sysvec, &elf64_freebsd_sysvec); static Elf64_Brandinfo freebsd_brand_info = { .brand = ELFOSABI_FREEBSD, .machine = EM_X86_64, .compat_3_brand = "FreeBSD", .emul_path = NULL, .interp_path = "/libexec/ld-elf.so.1", .sysvec = &elf64_freebsd_sysvec, .interp_newpath = NULL, .brand_note = &elf64_freebsd_brandnote, .flags = BI_CAN_EXEC_DYN | BI_BRAND_NOTE }; SYSINIT(elf64, SI_SUB_EXEC, SI_ORDER_FIRST, (sysinit_cfunc_t) elf64_insert_brand_entry, &freebsd_brand_info); static Elf64_Brandinfo freebsd_brand_oinfo = { .brand = ELFOSABI_FREEBSD, .machine = EM_X86_64, .compat_3_brand = "FreeBSD", .emul_path = NULL, .interp_path = "/usr/libexec/ld-elf.so.1", .sysvec = &elf64_freebsd_sysvec, .interp_newpath = NULL, .brand_note = &elf64_freebsd_brandnote, .flags = BI_CAN_EXEC_DYN | BI_BRAND_NOTE }; SYSINIT(oelf64, SI_SUB_EXEC, SI_ORDER_ANY, (sysinit_cfunc_t) elf64_insert_brand_entry, &freebsd_brand_oinfo); static Elf64_Brandinfo kfreebsd_brand_info = { .brand = ELFOSABI_FREEBSD, .machine = EM_X86_64, .compat_3_brand = "FreeBSD", .emul_path = NULL, .interp_path = "/lib/ld-kfreebsd-x86-64.so.1", .sysvec = &elf64_freebsd_sysvec, .interp_newpath = NULL, .brand_note = &elf64_kfreebsd_brandnote, .flags = BI_CAN_EXEC_DYN | BI_BRAND_NOTE_MANDATORY }; SYSINIT(kelf64, SI_SUB_EXEC, SI_ORDER_ANY, (sysinit_cfunc_t) elf64_insert_brand_entry, &kfreebsd_brand_info); void -elf64_dump_thread(struct thread *td __unused, void *dst __unused, - size_t *off __unused) +elf64_dump_thread(struct thread *td, void *dst, size_t *off) { -} + void *buf; + size_t len; + len = 0; + if (use_xsave) { + if (dst != NULL) { + fpugetregs(td); + len += elf64_populate_note(NT_X86_XSTATE, + get_pcb_user_save_td(td), dst, + cpu_max_ext_state_size, &buf); + *(uint64_t *)((char *)buf + X86_XSTATE_XCR0_OFFSET) = + xsave_mask; + } else + len += elf64_populate_note(NT_X86_XSTATE, NULL, NULL, + cpu_max_ext_state_size, NULL); + } + *off = len; +} /* Process one elf relocation with addend. */ static int elf_reloc_internal(linker_file_t lf, Elf_Addr relocbase, const void *data, int type, int local, elf_lookup_fn lookup) { Elf64_Addr *where, val; Elf32_Addr *where32, val32; Elf_Addr addr; Elf_Addr addend; Elf_Size rtype, symidx; const Elf_Rel *rel; const Elf_Rela *rela; switch (type) { case ELF_RELOC_REL: rel = (const Elf_Rel *)data; where = (Elf_Addr *) (relocbase + rel->r_offset); rtype = ELF_R_TYPE(rel->r_info); symidx = ELF_R_SYM(rel->r_info); /* Addend is 32 bit on 32 bit relocs */ switch (rtype) { case R_X86_64_PC32: case R_X86_64_32S: addend = *(Elf32_Addr *)where; break; default: addend = *where; break; } break; case ELF_RELOC_RELA: rela = (const Elf_Rela *)data; where = (Elf_Addr *) (relocbase + rela->r_offset); addend = rela->r_addend; rtype = ELF_R_TYPE(rela->r_info); symidx = ELF_R_SYM(rela->r_info); break; default: panic("unknown reloc type %d\n", type); } switch (rtype) { case R_X86_64_NONE: /* none */ break; case R_X86_64_64: /* S + A */ addr = lookup(lf, symidx, 1); val = addr + addend; if (addr == 0) return -1; if (*where != val) *where = val; break; case R_X86_64_PC32: /* S + A - P */ addr = lookup(lf, symidx, 1); where32 = (Elf32_Addr *)where; val32 = (Elf32_Addr)(addr + addend - (Elf_Addr)where); if (addr == 0) return -1; if (*where32 != val32) *where32 = val32; break; case R_X86_64_32S: /* S + A sign extend */ addr = lookup(lf, symidx, 1); val32 = (Elf32_Addr)(addr + addend); where32 = (Elf32_Addr *)where; if (addr == 0) return -1; if (*where32 != val32) *where32 = val32; break; case R_X86_64_COPY: /* none */ /* * There shouldn't be copy relocations in kernel * objects. */ printf("kldload: unexpected R_COPY relocation\n"); return -1; break; case R_X86_64_GLOB_DAT: /* S */ case R_X86_64_JMP_SLOT: /* XXX need addend + offset */ addr = lookup(lf, symidx, 1); if (addr == 0) return -1; if (*where != addr) *where = addr; break; case R_X86_64_RELATIVE: /* B + A */ addr = relocbase + addend; val = addr; if (*where != val) *where = val; break; default: printf("kldload: unexpected relocation type %ld\n", rtype); return -1; } return(0); } int elf_reloc(linker_file_t lf, Elf_Addr relocbase, const void *data, int type, elf_lookup_fn lookup) { return (elf_reloc_internal(lf, relocbase, data, type, 0, lookup)); } int elf_reloc_local(linker_file_t lf, Elf_Addr relocbase, const void *data, int type, elf_lookup_fn lookup) { return (elf_reloc_internal(lf, relocbase, data, type, 1, lookup)); } int elf_cpu_load_file(linker_file_t lf __unused) { return (0); } int elf_cpu_unload_file(linker_file_t lf __unused) { return (0); } Index: head/sys/amd64/amd64/fpu.c =================================================================== --- head/sys/amd64/amd64/fpu.c (revision 274816) +++ head/sys/amd64/amd64/fpu.c (revision 274817) @@ -1,1048 +1,1055 @@ /*- * Copyright (c) 1990 William Jolitz. * Copyright (c) 1991 The Regents of the University of California. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * from: @(#)npx.c 7.2 (Berkeley) 5/12/91 */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* * Floating point support. */ #if defined(__GNUCLIKE_ASM) && !defined(lint) #define fldcw(cw) __asm __volatile("fldcw %0" : : "m" (cw)) #define fnclex() __asm __volatile("fnclex") #define fninit() __asm __volatile("fninit") #define fnstcw(addr) __asm __volatile("fnstcw %0" : "=m" (*(addr))) #define fnstsw(addr) __asm __volatile("fnstsw %0" : "=am" (*(addr))) #define fxrstor(addr) __asm __volatile("fxrstor %0" : : "m" (*(addr))) #define fxsave(addr) __asm __volatile("fxsave %0" : "=m" (*(addr))) #define ldmxcsr(csr) __asm __volatile("ldmxcsr %0" : : "m" (csr)) #define stmxcsr(addr) __asm __volatile("stmxcsr %0" : : "m" (*(addr))) static __inline void xrstor(char *addr, uint64_t mask) { uint32_t low, hi; low = mask; hi = mask >> 32; __asm __volatile("xrstor %0" : : "m" (*addr), "a" (low), "d" (hi)); } static __inline void xsave(char *addr, uint64_t mask) { uint32_t low, hi; low = mask; hi = mask >> 32; __asm __volatile("xsave %0" : "=m" (*addr) : "a" (low), "d" (hi) : "memory"); } #else /* !(__GNUCLIKE_ASM && !lint) */ void fldcw(u_short cw); void fnclex(void); void fninit(void); void fnstcw(caddr_t addr); void fnstsw(caddr_t addr); void fxsave(caddr_t addr); void fxrstor(caddr_t addr); void ldmxcsr(u_int csr); void stmxcsr(u_int *csr); void xrstor(char *addr, uint64_t mask); void xsave(char *addr, uint64_t mask); #endif /* __GNUCLIKE_ASM && !lint */ #define start_emulating() load_cr0(rcr0() | CR0_TS) #define stop_emulating() clts() CTASSERT(sizeof(struct savefpu) == 512); CTASSERT(sizeof(struct xstate_hdr) == 64); CTASSERT(sizeof(struct savefpu_ymm) == 832); /* * This requirement is to make it easier for asm code to calculate * offset of the fpu save area from the pcb address. FPU save area * must be 64-byte aligned. */ CTASSERT(sizeof(struct pcb) % XSAVE_AREA_ALIGN == 0); +/* + * Ensure the copy of XCR0 saved in a core is contained in the padding + * area. + */ +CTASSERT(X86_XSTATE_XCR0_OFFSET >= offsetof(struct savefpu, sv_pad) && + X86_XSTATE_XCR0_OFFSET + sizeof(uint64_t) <= sizeof(struct savefpu)); + static void fpu_clean_state(void); SYSCTL_INT(_hw, HW_FLOATINGPT, floatingpoint, CTLFLAG_RD, SYSCTL_NULL_INT_PTR, 1, "Floating point instructions executed in hardware"); int use_xsave; /* non-static for cpu_switch.S */ uint64_t xsave_mask; /* the same */ static uma_zone_t fpu_save_area_zone; static struct savefpu *fpu_initialstate; struct xsave_area_elm_descr { u_int offset; u_int size; } *xsave_area_desc; void fpusave(void *addr) { if (use_xsave) xsave((char *)addr, xsave_mask); else fxsave((char *)addr); } void fpurestore(void *addr) { if (use_xsave) xrstor((char *)addr, xsave_mask); else fxrstor((char *)addr); } void fpususpend(void *addr) { u_long cr0; cr0 = rcr0(); stop_emulating(); fpusave(addr); load_cr0(cr0); } void fpuresume(void *addr) { u_long cr0; cr0 = rcr0(); stop_emulating(); fninit(); if (use_xsave) load_xcr(XCR0, xsave_mask); fpurestore(addr); load_cr0(cr0); } /* * Enable XSAVE if supported and allowed by user. * Calculate the xsave_mask. */ static void fpuinit_bsp1(void) { u_int cp[4]; uint64_t xsave_mask_user; if ((cpu_feature2 & CPUID2_XSAVE) != 0) { use_xsave = 1; TUNABLE_INT_FETCH("hw.use_xsave", &use_xsave); } if (!use_xsave) return; cpuid_count(0xd, 0x0, cp); xsave_mask = XFEATURE_ENABLED_X87 | XFEATURE_ENABLED_SSE; if ((cp[0] & xsave_mask) != xsave_mask) panic("CPU0 does not support X87 or SSE: %x", cp[0]); xsave_mask = ((uint64_t)cp[3] << 32) | cp[0]; xsave_mask_user = xsave_mask; TUNABLE_ULONG_FETCH("hw.xsave_mask", &xsave_mask_user); xsave_mask_user |= XFEATURE_ENABLED_X87 | XFEATURE_ENABLED_SSE; xsave_mask &= xsave_mask_user; if ((xsave_mask & XFEATURE_AVX512) != XFEATURE_AVX512) xsave_mask &= ~XFEATURE_AVX512; if ((xsave_mask & XFEATURE_MPX) != XFEATURE_MPX) xsave_mask &= ~XFEATURE_MPX; cpuid_count(0xd, 0x1, cp); if ((cp[0] & CPUID_EXTSTATE_XSAVEOPT) != 0) { /* * Patch the XSAVE instruction in the cpu_switch code * to XSAVEOPT. We assume that XSAVE encoding used * REX byte, and set the bit 4 of the r/m byte. */ ctx_switch_xsave[3] |= 0x10; } } /* * Calculate the fpu save area size. */ static void fpuinit_bsp2(void) { u_int cp[4]; if (use_xsave) { cpuid_count(0xd, 0x0, cp); cpu_max_ext_state_size = cp[1]; /* * Reload the cpu_feature2, since we enabled OSXSAVE. */ do_cpuid(1, cp); cpu_feature2 = cp[2]; } else cpu_max_ext_state_size = sizeof(struct savefpu); } /* * Initialize the floating point unit. */ void fpuinit(void) { register_t saveintr; u_int mxcsr; u_short control; if (IS_BSP()) fpuinit_bsp1(); if (use_xsave) { load_cr4(rcr4() | CR4_XSAVE); load_xcr(XCR0, xsave_mask); } /* * XCR0 shall be set up before CPU can report the save area size. */ if (IS_BSP()) fpuinit_bsp2(); /* * It is too early for critical_enter() to work on AP. */ saveintr = intr_disable(); stop_emulating(); fninit(); control = __INITIAL_FPUCW__; fldcw(control); mxcsr = __INITIAL_MXCSR__; ldmxcsr(mxcsr); start_emulating(); intr_restore(saveintr); } /* * On the boot CPU we generate a clean state that is used to * initialize the floating point unit when it is first used by a * process. */ static void fpuinitstate(void *arg __unused) { register_t saveintr; int cp[4], i, max_ext_n; fpu_initialstate = malloc(cpu_max_ext_state_size, M_DEVBUF, M_WAITOK | M_ZERO); saveintr = intr_disable(); stop_emulating(); fpusave(fpu_initialstate); if (fpu_initialstate->sv_env.en_mxcsr_mask) cpu_mxcsr_mask = fpu_initialstate->sv_env.en_mxcsr_mask; else cpu_mxcsr_mask = 0xFFBF; /* * The fninit instruction does not modify XMM registers. The * fpusave call dumped the garbage contained in the registers * after reset to the initial state saved. Clear XMM * registers file image to make the startup program state and * signal handler XMM register content predictable. */ bzero(&fpu_initialstate->sv_xmm[0], sizeof(struct xmmacc)); /* * Create a table describing the layout of the CPU Extended * Save Area. */ if (use_xsave) { max_ext_n = flsl(xsave_mask); xsave_area_desc = malloc(max_ext_n * sizeof(struct xsave_area_elm_descr), M_DEVBUF, M_WAITOK | M_ZERO); /* x87 state */ xsave_area_desc[0].offset = 0; xsave_area_desc[0].size = 160; /* XMM */ xsave_area_desc[1].offset = 160; xsave_area_desc[1].size = 288 - 160; for (i = 2; i < max_ext_n; i++) { cpuid_count(0xd, i, cp); xsave_area_desc[i].offset = cp[1]; xsave_area_desc[i].size = cp[0]; } } fpu_save_area_zone = uma_zcreate("FPU_save_area", cpu_max_ext_state_size, NULL, NULL, NULL, NULL, XSAVE_AREA_ALIGN - 1, 0); start_emulating(); intr_restore(saveintr); } SYSINIT(fpuinitstate, SI_SUB_DRIVERS, SI_ORDER_ANY, fpuinitstate, NULL); /* * Free coprocessor (if we have it). */ void fpuexit(struct thread *td) { critical_enter(); if (curthread == PCPU_GET(fpcurthread)) { stop_emulating(); fpusave(curpcb->pcb_save); start_emulating(); PCPU_SET(fpcurthread, NULL); } critical_exit(); } int fpuformat() { return (_MC_FPFMT_XMM); } /* * The following mechanism is used to ensure that the FPE_... value * that is passed as a trapcode to the signal handler of the user * process does not have more than one bit set. * * Multiple bits may be set if the user process modifies the control * word while a status word bit is already set. While this is a sign * of bad coding, we have no choise than to narrow them down to one * bit, since we must not send a trapcode that is not exactly one of * the FPE_ macros. * * The mechanism has a static table with 127 entries. Each combination * of the 7 FPU status word exception bits directly translates to a * position in this table, where a single FPE_... value is stored. * This FPE_... value stored there is considered the "most important" * of the exception bits and will be sent as the signal code. The * precedence of the bits is based upon Intel Document "Numerical * Applications", Chapter "Special Computational Situations". * * The macro to choose one of these values does these steps: 1) Throw * away status word bits that cannot be masked. 2) Throw away the bits * currently masked in the control word, assuming the user isn't * interested in them anymore. 3) Reinsert status word bit 7 (stack * fault) if it is set, which cannot be masked but must be presered. * 4) Use the remaining bits to point into the trapcode table. * * The 6 maskable bits in order of their preference, as stated in the * above referenced Intel manual: * 1 Invalid operation (FP_X_INV) * 1a Stack underflow * 1b Stack overflow * 1c Operand of unsupported format * 1d SNaN operand. * 2 QNaN operand (not an exception, irrelavant here) * 3 Any other invalid-operation not mentioned above or zero divide * (FP_X_INV, FP_X_DZ) * 4 Denormal operand (FP_X_DNML) * 5 Numeric over/underflow (FP_X_OFL, FP_X_UFL) * 6 Inexact result (FP_X_IMP) */ static char fpetable[128] = { 0, FPE_FLTINV, /* 1 - INV */ FPE_FLTUND, /* 2 - DNML */ FPE_FLTINV, /* 3 - INV | DNML */ FPE_FLTDIV, /* 4 - DZ */ FPE_FLTINV, /* 5 - INV | DZ */ FPE_FLTDIV, /* 6 - DNML | DZ */ FPE_FLTINV, /* 7 - INV | DNML | DZ */ FPE_FLTOVF, /* 8 - OFL */ FPE_FLTINV, /* 9 - INV | OFL */ FPE_FLTUND, /* A - DNML | OFL */ FPE_FLTINV, /* B - INV | DNML | OFL */ FPE_FLTDIV, /* C - DZ | OFL */ FPE_FLTINV, /* D - INV | DZ | OFL */ FPE_FLTDIV, /* E - DNML | DZ | OFL */ FPE_FLTINV, /* F - INV | DNML | DZ | OFL */ FPE_FLTUND, /* 10 - UFL */ FPE_FLTINV, /* 11 - INV | UFL */ FPE_FLTUND, /* 12 - DNML | UFL */ FPE_FLTINV, /* 13 - INV | DNML | UFL */ FPE_FLTDIV, /* 14 - DZ | UFL */ FPE_FLTINV, /* 15 - INV | DZ | UFL */ FPE_FLTDIV, /* 16 - DNML | DZ | UFL */ FPE_FLTINV, /* 17 - INV | DNML | DZ | UFL */ FPE_FLTOVF, /* 18 - OFL | UFL */ FPE_FLTINV, /* 19 - INV | OFL | UFL */ FPE_FLTUND, /* 1A - DNML | OFL | UFL */ FPE_FLTINV, /* 1B - INV | DNML | OFL | UFL */ FPE_FLTDIV, /* 1C - DZ | OFL | UFL */ FPE_FLTINV, /* 1D - INV | DZ | OFL | UFL */ FPE_FLTDIV, /* 1E - DNML | DZ | OFL | UFL */ FPE_FLTINV, /* 1F - INV | DNML | DZ | OFL | UFL */ FPE_FLTRES, /* 20 - IMP */ FPE_FLTINV, /* 21 - INV | IMP */ FPE_FLTUND, /* 22 - DNML | IMP */ FPE_FLTINV, /* 23 - INV | DNML | IMP */ FPE_FLTDIV, /* 24 - DZ | IMP */ FPE_FLTINV, /* 25 - INV | DZ | IMP */ FPE_FLTDIV, /* 26 - DNML | DZ | IMP */ FPE_FLTINV, /* 27 - INV | DNML | DZ | IMP */ FPE_FLTOVF, /* 28 - OFL | IMP */ FPE_FLTINV, /* 29 - INV | OFL | IMP */ FPE_FLTUND, /* 2A - DNML | OFL | IMP */ FPE_FLTINV, /* 2B - INV | DNML | OFL | IMP */ FPE_FLTDIV, /* 2C - DZ | OFL | IMP */ FPE_FLTINV, /* 2D - INV | DZ | OFL | IMP */ FPE_FLTDIV, /* 2E - DNML | DZ | OFL | IMP */ FPE_FLTINV, /* 2F - INV | DNML | DZ | OFL | IMP */ FPE_FLTUND, /* 30 - UFL | IMP */ FPE_FLTINV, /* 31 - INV | UFL | IMP */ FPE_FLTUND, /* 32 - DNML | UFL | IMP */ FPE_FLTINV, /* 33 - INV | DNML | UFL | IMP */ FPE_FLTDIV, /* 34 - DZ | UFL | IMP */ FPE_FLTINV, /* 35 - INV | DZ | UFL | IMP */ FPE_FLTDIV, /* 36 - DNML | DZ | UFL | IMP */ FPE_FLTINV, /* 37 - INV | DNML | DZ | UFL | IMP */ FPE_FLTOVF, /* 38 - OFL | UFL | IMP */ FPE_FLTINV, /* 39 - INV | OFL | UFL | IMP */ FPE_FLTUND, /* 3A - DNML | OFL | UFL | IMP */ FPE_FLTINV, /* 3B - INV | DNML | OFL | UFL | IMP */ FPE_FLTDIV, /* 3C - DZ | OFL | UFL | IMP */ FPE_FLTINV, /* 3D - INV | DZ | OFL | UFL | IMP */ FPE_FLTDIV, /* 3E - DNML | DZ | OFL | UFL | IMP */ FPE_FLTINV, /* 3F - INV | DNML | DZ | OFL | UFL | IMP */ FPE_FLTSUB, /* 40 - STK */ FPE_FLTSUB, /* 41 - INV | STK */ FPE_FLTUND, /* 42 - DNML | STK */ FPE_FLTSUB, /* 43 - INV | DNML | STK */ FPE_FLTDIV, /* 44 - DZ | STK */ FPE_FLTSUB, /* 45 - INV | DZ | STK */ FPE_FLTDIV, /* 46 - DNML | DZ | STK */ FPE_FLTSUB, /* 47 - INV | DNML | DZ | STK */ FPE_FLTOVF, /* 48 - OFL | STK */ FPE_FLTSUB, /* 49 - INV | OFL | STK */ FPE_FLTUND, /* 4A - DNML | OFL | STK */ FPE_FLTSUB, /* 4B - INV | DNML | OFL | STK */ FPE_FLTDIV, /* 4C - DZ | OFL | STK */ FPE_FLTSUB, /* 4D - INV | DZ | OFL | STK */ FPE_FLTDIV, /* 4E - DNML | DZ | OFL | STK */ FPE_FLTSUB, /* 4F - INV | DNML | DZ | OFL | STK */ FPE_FLTUND, /* 50 - UFL | STK */ FPE_FLTSUB, /* 51 - INV | UFL | STK */ FPE_FLTUND, /* 52 - DNML | UFL | STK */ FPE_FLTSUB, /* 53 - INV | DNML | UFL | STK */ FPE_FLTDIV, /* 54 - DZ | UFL | STK */ FPE_FLTSUB, /* 55 - INV | DZ | UFL | STK */ FPE_FLTDIV, /* 56 - DNML | DZ | UFL | STK */ FPE_FLTSUB, /* 57 - INV | DNML | DZ | UFL | STK */ FPE_FLTOVF, /* 58 - OFL | UFL | STK */ FPE_FLTSUB, /* 59 - INV | OFL | UFL | STK */ FPE_FLTUND, /* 5A - DNML | OFL | UFL | STK */ FPE_FLTSUB, /* 5B - INV | DNML | OFL | UFL | STK */ FPE_FLTDIV, /* 5C - DZ | OFL | UFL | STK */ FPE_FLTSUB, /* 5D - INV | DZ | OFL | UFL | STK */ FPE_FLTDIV, /* 5E - DNML | DZ | OFL | UFL | STK */ FPE_FLTSUB, /* 5F - INV | DNML | DZ | OFL | UFL | STK */ FPE_FLTRES, /* 60 - IMP | STK */ FPE_FLTSUB, /* 61 - INV | IMP | STK */ FPE_FLTUND, /* 62 - DNML | IMP | STK */ FPE_FLTSUB, /* 63 - INV | DNML | IMP | STK */ FPE_FLTDIV, /* 64 - DZ | IMP | STK */ FPE_FLTSUB, /* 65 - INV | DZ | IMP | STK */ FPE_FLTDIV, /* 66 - DNML | DZ | IMP | STK */ FPE_FLTSUB, /* 67 - INV | DNML | DZ | IMP | STK */ FPE_FLTOVF, /* 68 - OFL | IMP | STK */ FPE_FLTSUB, /* 69 - INV | OFL | IMP | STK */ FPE_FLTUND, /* 6A - DNML | OFL | IMP | STK */ FPE_FLTSUB, /* 6B - INV | DNML | OFL | IMP | STK */ FPE_FLTDIV, /* 6C - DZ | OFL | IMP | STK */ FPE_FLTSUB, /* 6D - INV | DZ | OFL | IMP | STK */ FPE_FLTDIV, /* 6E - DNML | DZ | OFL | IMP | STK */ FPE_FLTSUB, /* 6F - INV | DNML | DZ | OFL | IMP | STK */ FPE_FLTUND, /* 70 - UFL | IMP | STK */ FPE_FLTSUB, /* 71 - INV | UFL | IMP | STK */ FPE_FLTUND, /* 72 - DNML | UFL | IMP | STK */ FPE_FLTSUB, /* 73 - INV | DNML | UFL | IMP | STK */ FPE_FLTDIV, /* 74 - DZ | UFL | IMP | STK */ FPE_FLTSUB, /* 75 - INV | DZ | UFL | IMP | STK */ FPE_FLTDIV, /* 76 - DNML | DZ | UFL | IMP | STK */ FPE_FLTSUB, /* 77 - INV | DNML | DZ | UFL | IMP | STK */ FPE_FLTOVF, /* 78 - OFL | UFL | IMP | STK */ FPE_FLTSUB, /* 79 - INV | OFL | UFL | IMP | STK */ FPE_FLTUND, /* 7A - DNML | OFL | UFL | IMP | STK */ FPE_FLTSUB, /* 7B - INV | DNML | OFL | UFL | IMP | STK */ FPE_FLTDIV, /* 7C - DZ | OFL | UFL | IMP | STK */ FPE_FLTSUB, /* 7D - INV | DZ | OFL | UFL | IMP | STK */ FPE_FLTDIV, /* 7E - DNML | DZ | OFL | UFL | IMP | STK */ FPE_FLTSUB, /* 7F - INV | DNML | DZ | OFL | UFL | IMP | STK */ }; /* * Read the FP status and control words, then generate si_code value * for SIGFPE. The error code chosen will be one of the * FPE_... macros. It will be sent as the second argument to old * BSD-style signal handlers and as "siginfo_t->si_code" (second * argument) to SA_SIGINFO signal handlers. * * Some time ago, we cleared the x87 exceptions with FNCLEX there. * Clearing exceptions was necessary mainly to avoid IRQ13 bugs. The * usermode code which understands the FPU hardware enough to enable * the exceptions, can also handle clearing the exception state in the * handler. The only consequence of not clearing the exception is the * rethrow of the SIGFPE on return from the signal handler and * reexecution of the corresponding instruction. * * For XMM traps, the exceptions were never cleared. */ int fputrap_x87(void) { struct savefpu *pcb_save; u_short control, status; critical_enter(); /* * Interrupt handling (for another interrupt) may have pushed the * state to memory. Fetch the relevant parts of the state from * wherever they are. */ if (PCPU_GET(fpcurthread) != curthread) { pcb_save = curpcb->pcb_save; control = pcb_save->sv_env.en_cw; status = pcb_save->sv_env.en_sw; } else { fnstcw(&control); fnstsw(&status); } critical_exit(); return (fpetable[status & ((~control & 0x3f) | 0x40)]); } int fputrap_sse(void) { u_int mxcsr; critical_enter(); if (PCPU_GET(fpcurthread) != curthread) mxcsr = curpcb->pcb_save->sv_env.en_mxcsr; else stmxcsr(&mxcsr); critical_exit(); return (fpetable[(mxcsr & (~mxcsr >> 7)) & 0x3f]); } /* * Device Not Available (DNA, #NM) exception handler. * * It would be better to switch FP context here (if curthread != * fpcurthread) and not necessarily for every context switch, but it * is too hard to access foreign pcb's. */ void fpudna(void) { /* * This handler is entered with interrupts enabled, so context * switches may occur before critical_enter() is executed. If * a context switch occurs, then when we regain control, our * state will have been completely restored. The CPU may * change underneath us, but the only part of our context that * lives in the CPU is CR0.TS and that will be "restored" by * setting it on the new CPU. */ critical_enter(); if (PCPU_GET(fpcurthread) == curthread) { printf("fpudna: fpcurthread == curthread\n"); stop_emulating(); critical_exit(); return; } if (PCPU_GET(fpcurthread) != NULL) { panic("fpudna: fpcurthread = %p (%d), curthread = %p (%d)\n", PCPU_GET(fpcurthread), PCPU_GET(fpcurthread)->td_tid, curthread, curthread->td_tid); } stop_emulating(); /* * Record new context early in case frstor causes a trap. */ PCPU_SET(fpcurthread, curthread); fpu_clean_state(); if ((curpcb->pcb_flags & PCB_FPUINITDONE) == 0) { /* * This is the first time this thread has used the FPU or * the PCB doesn't contain a clean FPU state. Explicitly * load an initial state. * * We prefer to restore the state from the actual save * area in PCB instead of directly loading from * fpu_initialstate, to ignite the XSAVEOPT * tracking engine. */ bcopy(fpu_initialstate, curpcb->pcb_save, cpu_max_ext_state_size); fpurestore(curpcb->pcb_save); if (curpcb->pcb_initial_fpucw != __INITIAL_FPUCW__) fldcw(curpcb->pcb_initial_fpucw); if (PCB_USER_FPU(curpcb)) set_pcb_flags(curpcb, PCB_FPUINITDONE | PCB_USERFPUINITDONE); else set_pcb_flags(curpcb, PCB_FPUINITDONE); } else fpurestore(curpcb->pcb_save); critical_exit(); } void fpudrop() { struct thread *td; td = PCPU_GET(fpcurthread); KASSERT(td == curthread, ("fpudrop: fpcurthread != curthread")); CRITICAL_ASSERT(td); PCPU_SET(fpcurthread, NULL); clear_pcb_flags(td->td_pcb, PCB_FPUINITDONE); start_emulating(); } /* * Get the user state of the FPU into pcb->pcb_user_save without * dropping ownership (if possible). It returns the FPU ownership * status. */ int fpugetregs(struct thread *td) { struct pcb *pcb; uint64_t *xstate_bv, bit; char *sa; int max_ext_n, i, owned; pcb = td->td_pcb; if ((pcb->pcb_flags & PCB_USERFPUINITDONE) == 0) { bcopy(fpu_initialstate, get_pcb_user_save_pcb(pcb), cpu_max_ext_state_size); get_pcb_user_save_pcb(pcb)->sv_env.en_cw = pcb->pcb_initial_fpucw; fpuuserinited(td); return (_MC_FPOWNED_PCB); } critical_enter(); if (td == PCPU_GET(fpcurthread) && PCB_USER_FPU(pcb)) { fpusave(get_pcb_user_save_pcb(pcb)); owned = _MC_FPOWNED_FPU; } else { owned = _MC_FPOWNED_PCB; } critical_exit(); if (use_xsave) { /* * Handle partially saved state. */ sa = (char *)get_pcb_user_save_pcb(pcb); xstate_bv = (uint64_t *)(sa + sizeof(struct savefpu) + offsetof(struct xstate_hdr, xstate_bv)); max_ext_n = flsl(xsave_mask); for (i = 0; i < max_ext_n; i++) { bit = 1ULL << i; if ((xsave_mask & bit) == 0 || (*xstate_bv & bit) != 0) continue; bcopy((char *)fpu_initialstate + xsave_area_desc[i].offset, sa + xsave_area_desc[i].offset, xsave_area_desc[i].size); *xstate_bv |= bit; } } return (owned); } void fpuuserinited(struct thread *td) { struct pcb *pcb; pcb = td->td_pcb; if (PCB_USER_FPU(pcb)) set_pcb_flags(pcb, PCB_FPUINITDONE | PCB_USERFPUINITDONE); else set_pcb_flags(pcb, PCB_FPUINITDONE); } int fpusetxstate(struct thread *td, char *xfpustate, size_t xfpustate_size) { struct xstate_hdr *hdr, *ehdr; size_t len, max_len; uint64_t bv; /* XXXKIB should we clear all extended state in xstate_bv instead ? */ if (xfpustate == NULL) return (0); if (!use_xsave) return (EOPNOTSUPP); len = xfpustate_size; if (len < sizeof(struct xstate_hdr)) return (EINVAL); max_len = cpu_max_ext_state_size - sizeof(struct savefpu); if (len > max_len) return (EINVAL); ehdr = (struct xstate_hdr *)xfpustate; bv = ehdr->xstate_bv; /* * Avoid #gp. */ if (bv & ~xsave_mask) return (EINVAL); hdr = (struct xstate_hdr *)(get_pcb_user_save_td(td) + 1); hdr->xstate_bv = bv; bcopy(xfpustate + sizeof(struct xstate_hdr), (char *)(hdr + 1), len - sizeof(struct xstate_hdr)); return (0); } /* * Set the state of the FPU. */ int fpusetregs(struct thread *td, struct savefpu *addr, char *xfpustate, size_t xfpustate_size) { struct pcb *pcb; int error; pcb = td->td_pcb; critical_enter(); if (td == PCPU_GET(fpcurthread) && PCB_USER_FPU(pcb)) { error = fpusetxstate(td, xfpustate, xfpustate_size); if (error != 0) { critical_exit(); return (error); } bcopy(addr, get_pcb_user_save_td(td), sizeof(*addr)); fpurestore(get_pcb_user_save_td(td)); critical_exit(); set_pcb_flags(pcb, PCB_FPUINITDONE | PCB_USERFPUINITDONE); } else { critical_exit(); error = fpusetxstate(td, xfpustate, xfpustate_size); if (error != 0) return (error); bcopy(addr, get_pcb_user_save_td(td), sizeof(*addr)); fpuuserinited(td); } return (0); } /* * On AuthenticAMD processors, the fxrstor instruction does not restore * the x87's stored last instruction pointer, last data pointer, and last * opcode values, except in the rare case in which the exception summary * (ES) bit in the x87 status word is set to 1. * * In order to avoid leaking this information across processes, we clean * these values by performing a dummy load before executing fxrstor(). */ static void fpu_clean_state(void) { static float dummy_variable = 0.0; u_short status; /* * Clear the ES bit in the x87 status word if it is currently * set, in order to avoid causing a fault in the upcoming load. */ fnstsw(&status); if (status & 0x80) fnclex(); /* * Load the dummy variable into the x87 stack. This mangles * the x87 stack, but we don't care since we're about to call * fxrstor() anyway. */ __asm __volatile("ffree %%st(7); flds %0" : : "m" (dummy_variable)); } /* * This really sucks. We want the acpi version only, but it requires * the isa_if.h file in order to get the definitions. */ #include "opt_isa.h" #ifdef DEV_ISA #include /* * This sucks up the legacy ISA support assignments from PNPBIOS/ACPI. */ static struct isa_pnp_id fpupnp_ids[] = { { 0x040cd041, "Legacy ISA coprocessor support" }, /* PNP0C04 */ { 0 } }; static int fpupnp_probe(device_t dev) { int result; result = ISA_PNP_PROBE(device_get_parent(dev), dev, fpupnp_ids); if (result <= 0) device_quiet(dev); return (result); } static int fpupnp_attach(device_t dev) { return (0); } static device_method_t fpupnp_methods[] = { /* Device interface */ DEVMETHOD(device_probe, fpupnp_probe), DEVMETHOD(device_attach, fpupnp_attach), DEVMETHOD(device_detach, bus_generic_detach), DEVMETHOD(device_shutdown, bus_generic_shutdown), DEVMETHOD(device_suspend, bus_generic_suspend), DEVMETHOD(device_resume, bus_generic_resume), { 0, 0 } }; static driver_t fpupnp_driver = { "fpupnp", fpupnp_methods, 1, /* no softc */ }; static devclass_t fpupnp_devclass; DRIVER_MODULE(fpupnp, acpi, fpupnp_driver, fpupnp_devclass, 0, 0); #endif /* DEV_ISA */ static MALLOC_DEFINE(M_FPUKERN_CTX, "fpukern_ctx", "Kernel contexts for FPU state"); #define FPU_KERN_CTX_FPUINITDONE 0x01 #define FPU_KERN_CTX_DUMMY 0x02 /* avoided save for the kern thread */ struct fpu_kern_ctx { struct savefpu *prev; uint32_t flags; char hwstate1[]; }; struct fpu_kern_ctx * fpu_kern_alloc_ctx(u_int flags) { struct fpu_kern_ctx *res; size_t sz; sz = sizeof(struct fpu_kern_ctx) + XSAVE_AREA_ALIGN + cpu_max_ext_state_size; res = malloc(sz, M_FPUKERN_CTX, ((flags & FPU_KERN_NOWAIT) ? M_NOWAIT : M_WAITOK) | M_ZERO); return (res); } void fpu_kern_free_ctx(struct fpu_kern_ctx *ctx) { /* XXXKIB clear the memory ? */ free(ctx, M_FPUKERN_CTX); } static struct savefpu * fpu_kern_ctx_savefpu(struct fpu_kern_ctx *ctx) { vm_offset_t p; p = (vm_offset_t)&ctx->hwstate1; p = roundup2(p, XSAVE_AREA_ALIGN); return ((struct savefpu *)p); } int fpu_kern_enter(struct thread *td, struct fpu_kern_ctx *ctx, u_int flags) { struct pcb *pcb; if ((flags & FPU_KERN_KTHR) != 0 && is_fpu_kern_thread(0)) { ctx->flags = FPU_KERN_CTX_DUMMY; return (0); } pcb = td->td_pcb; KASSERT(!PCB_USER_FPU(pcb) || pcb->pcb_save == get_pcb_user_save_pcb(pcb), ("mangled pcb_save")); ctx->flags = 0; if ((pcb->pcb_flags & PCB_FPUINITDONE) != 0) ctx->flags |= FPU_KERN_CTX_FPUINITDONE; fpuexit(td); ctx->prev = pcb->pcb_save; pcb->pcb_save = fpu_kern_ctx_savefpu(ctx); set_pcb_flags(pcb, PCB_KERNFPU); clear_pcb_flags(pcb, PCB_FPUINITDONE); return (0); } int fpu_kern_leave(struct thread *td, struct fpu_kern_ctx *ctx) { struct pcb *pcb; if (is_fpu_kern_thread(0) && (ctx->flags & FPU_KERN_CTX_DUMMY) != 0) return (0); KASSERT((ctx->flags & FPU_KERN_CTX_DUMMY) == 0, ("dummy ctx")); pcb = td->td_pcb; critical_enter(); if (curthread == PCPU_GET(fpcurthread)) fpudrop(); critical_exit(); pcb->pcb_save = ctx->prev; if (pcb->pcb_save == get_pcb_user_save_pcb(pcb)) { if ((pcb->pcb_flags & PCB_USERFPUINITDONE) != 0) { set_pcb_flags(pcb, PCB_FPUINITDONE); clear_pcb_flags(pcb, PCB_KERNFPU); } else clear_pcb_flags(pcb, PCB_FPUINITDONE | PCB_KERNFPU); } else { if ((ctx->flags & FPU_KERN_CTX_FPUINITDONE) != 0) set_pcb_flags(pcb, PCB_FPUINITDONE); else clear_pcb_flags(pcb, PCB_FPUINITDONE); KASSERT(!PCB_USER_FPU(pcb), ("unpaired fpu_kern_leave")); } return (0); } int fpu_kern_thread(u_int flags) { KASSERT((curthread->td_pflags & TDP_KTHREAD) != 0, ("Only kthread may use fpu_kern_thread")); KASSERT(curpcb->pcb_save == get_pcb_user_save_pcb(curpcb), ("mangled pcb_save")); KASSERT(PCB_USER_FPU(curpcb), ("recursive call")); set_pcb_flags(curpcb, PCB_KERNFPU); return (0); } int is_fpu_kern_thread(u_int flags) { if ((curthread->td_pflags & TDP_KTHREAD) == 0) return (0); return ((curpcb->pcb_flags & PCB_KERNFPU) != 0); } /* * FPU save area alloc/free/init utility routines */ struct savefpu * fpu_save_area_alloc(void) { return (uma_zalloc(fpu_save_area_zone, 0)); } void fpu_save_area_free(struct savefpu *fsa) { uma_zfree(fpu_save_area_zone, fsa); } void fpu_save_area_reset(struct savefpu *fsa) { bcopy(fpu_initialstate, fsa, cpu_max_ext_state_size); } Index: head/sys/amd64/amd64/ptrace_machdep.c =================================================================== --- head/sys/amd64/amd64/ptrace_machdep.c (revision 274816) +++ head/sys/amd64/amd64/ptrace_machdep.c (revision 274817) @@ -1,152 +1,184 @@ /*- * Copyright (c) 2011 Konstantin Belousov * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * */ #include __FBSDID("$FreeBSD$"); #include "opt_compat.h" #include #include #include #include #include #include #include #include static int cpu_ptrace_xstate(struct thread *td, int req, void *addr, int data) { + struct ptrace_xstate_info info; char *savefpu; int error; if (!use_xsave) return (EOPNOTSUPP); switch (req) { - case PT_GETXSTATE: + case PT_GETXSTATE_OLD: fpugetregs(td); savefpu = (char *)(get_pcb_user_save_td(td) + 1); error = copyout(savefpu, addr, cpu_max_ext_state_size - sizeof(struct savefpu)); break; - case PT_SETXSTATE: + case PT_SETXSTATE_OLD: if (data > cpu_max_ext_state_size - sizeof(struct savefpu)) { error = EINVAL; break; } savefpu = malloc(data, M_TEMP, M_WAITOK); error = copyin(addr, savefpu, data); if (error == 0) { fpugetregs(td); error = fpusetxstate(td, savefpu, data); } free(savefpu, M_TEMP); break; + case PT_GETXSTATE_INFO: + if (data != sizeof(info)) { + error = EINVAL; + break; + } + info.xsave_len = cpu_max_ext_state_size; + info.xsave_mask = xsave_mask; + error = copyout(&info, addr, data); + break; + + case PT_GETXSTATE: + fpugetregs(td); + savefpu = (char *)(get_pcb_user_save_td(td)); + error = copyout(savefpu, addr, cpu_max_ext_state_size); + break; + + case PT_SETXSTATE: + if (data > cpu_max_ext_state_size) { + error = EINVAL; + break; + } + savefpu = malloc(data, M_TEMP, M_WAITOK); + error = copyin(addr, savefpu, data); + if (error == 0) + error = fpusetregs(td, (struct savefpu *)savefpu, + savefpu + sizeof(struct savefpu), data - + sizeof(struct savefpu)); + free(savefpu, M_TEMP); + break; + default: error = EINVAL; break; } return (error); } #ifdef COMPAT_FREEBSD32 #define PT_I386_GETXMMREGS (PT_FIRSTMACH + 0) #define PT_I386_SETXMMREGS (PT_FIRSTMACH + 1) -#define PT_I386_GETXSTATE (PT_FIRSTMACH + 2) -#define PT_I386_SETXSTATE (PT_FIRSTMACH + 3) static int cpu32_ptrace(struct thread *td, int req, void *addr, int data) { struct savefpu *fpstate; int error; switch (req) { case PT_I386_GETXMMREGS: fpugetregs(td); error = copyout(get_pcb_user_save_td(td), addr, sizeof(*fpstate)); break; case PT_I386_SETXMMREGS: fpugetregs(td); fpstate = get_pcb_user_save_td(td); error = copyin(addr, fpstate, sizeof(*fpstate)); fpstate->sv_env.en_mxcsr &= cpu_mxcsr_mask; break; - case PT_I386_GETXSTATE: - error = cpu_ptrace_xstate(td, PT_GETXSTATE, addr, data); + case PT_GETXSTATE_OLD: + case PT_SETXSTATE_OLD: + case PT_GETXSTATE_INFO: + case PT_GETXSTATE: + case PT_SETXSTATE: + error = cpu_ptrace_xstate(td, req, addr, data); break; - case PT_I386_SETXSTATE: - error = cpu_ptrace_xstate(td, PT_SETXSTATE, addr, data); - break; - default: error = EINVAL; break; } return (error); } #endif int cpu_ptrace(struct thread *td, int req, void *addr, int data) { int error; #ifdef COMPAT_FREEBSD32 if (SV_CURPROC_FLAG(SV_ILP32)) return (cpu32_ptrace(td, req, addr, data)); #endif - /* Support old values of PT_GETXSTATE and PT_SETXSTATE. */ + /* Support old values of PT_GETXSTATE_OLD and PT_SETXSTATE_OLD. */ if (req == PT_FIRSTMACH + 0) - req = PT_GETXSTATE; + req = PT_GETXSTATE_OLD; if (req == PT_FIRSTMACH + 1) - req = PT_SETXSTATE; + req = PT_SETXSTATE_OLD; switch (req) { + case PT_GETXSTATE_OLD: + case PT_SETXSTATE_OLD: + case PT_GETXSTATE_INFO: case PT_GETXSTATE: case PT_SETXSTATE: error = cpu_ptrace_xstate(td, req, addr, data); break; default: error = EINVAL; break; } return (error); } Index: head/sys/compat/ia32/ia32_sysvec.c =================================================================== --- head/sys/compat/ia32/ia32_sysvec.c (revision 274816) +++ head/sys/compat/ia32/ia32_sysvec.c (revision 274817) @@ -1,225 +1,241 @@ /*- * Copyright (c) 2002 Doug Rabson * Copyright (c) 2003 Peter Wemm * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include __FBSDID("$FreeBSD$"); #include "opt_compat.h" #define __ELF_WORD_SIZE 32 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include CTASSERT(sizeof(struct ia32_mcontext) == 640); CTASSERT(sizeof(struct ia32_ucontext) == 704); CTASSERT(sizeof(struct ia32_sigframe) == 800); CTASSERT(sizeof(struct siginfo32) == 64); #ifdef COMPAT_FREEBSD4 CTASSERT(sizeof(struct ia32_mcontext4) == 260); CTASSERT(sizeof(struct ia32_ucontext4) == 324); CTASSERT(sizeof(struct ia32_sigframe4) == 408); #endif extern const char *freebsd32_syscallnames[]; static SYSCTL_NODE(_compat, OID_AUTO, ia32, CTLFLAG_RW, 0, "ia32 mode"); static u_long ia32_maxdsiz = IA32_MAXDSIZ; SYSCTL_ULONG(_compat_ia32, OID_AUTO, maxdsiz, CTLFLAG_RWTUN, &ia32_maxdsiz, 0, ""); u_long ia32_maxssiz = IA32_MAXSSIZ; SYSCTL_ULONG(_compat_ia32, OID_AUTO, maxssiz, CTLFLAG_RWTUN, &ia32_maxssiz, 0, ""); static u_long ia32_maxvmem = IA32_MAXVMEM; SYSCTL_ULONG(_compat_ia32, OID_AUTO, maxvmem, CTLFLAG_RWTUN, &ia32_maxvmem, 0, ""); struct sysentvec ia32_freebsd_sysvec = { .sv_size = FREEBSD32_SYS_MAXSYSCALL, .sv_table = freebsd32_sysent, .sv_mask = 0, .sv_sigsize = 0, .sv_sigtbl = NULL, .sv_errsize = 0, .sv_errtbl = NULL, .sv_transtrap = NULL, .sv_fixup = elf32_freebsd_fixup, .sv_sendsig = ia32_sendsig, .sv_sigcode = ia32_sigcode, .sv_szsigcode = &sz_ia32_sigcode, .sv_prepsyscall = NULL, .sv_name = "FreeBSD ELF32", .sv_coredump = elf32_coredump, .sv_imgact_try = NULL, .sv_minsigstksz = MINSIGSTKSZ, .sv_pagesize = IA32_PAGE_SIZE, .sv_minuser = FREEBSD32_MINUSER, .sv_maxuser = FREEBSD32_MAXUSER, .sv_usrstack = FREEBSD32_USRSTACK, .sv_psstrings = FREEBSD32_PS_STRINGS, .sv_stackprot = VM_PROT_ALL, .sv_copyout_strings = freebsd32_copyout_strings, .sv_setregs = ia32_setregs, .sv_fixlimit = ia32_fixlimit, .sv_maxssiz = &ia32_maxssiz, .sv_flags = SV_ABI_FREEBSD | SV_IA32 | SV_ILP32 | #ifdef __amd64__ SV_SHP #else 0 #endif , .sv_set_syscall_retval = ia32_set_syscall_retval, .sv_fetch_syscall_args = ia32_fetch_syscall_args, .sv_syscallnames = freebsd32_syscallnames, .sv_shared_page_base = FREEBSD32_SHAREDPAGE, .sv_shared_page_len = PAGE_SIZE, .sv_schedtail = NULL, }; INIT_SYSENTVEC(elf_ia32_sysvec, &ia32_freebsd_sysvec); static Elf32_Brandinfo ia32_brand_info = { .brand = ELFOSABI_FREEBSD, .machine = EM_386, .compat_3_brand = "FreeBSD", .emul_path = NULL, .interp_path = "/libexec/ld-elf.so.1", .sysvec = &ia32_freebsd_sysvec, .interp_newpath = "/libexec/ld-elf32.so.1", .brand_note = &elf32_freebsd_brandnote, .flags = BI_CAN_EXEC_DYN | BI_BRAND_NOTE }; SYSINIT(ia32, SI_SUB_EXEC, SI_ORDER_MIDDLE, (sysinit_cfunc_t) elf32_insert_brand_entry, &ia32_brand_info); static Elf32_Brandinfo ia32_brand_oinfo = { .brand = ELFOSABI_FREEBSD, .machine = EM_386, .compat_3_brand = "FreeBSD", .emul_path = NULL, .interp_path = "/usr/libexec/ld-elf.so.1", .sysvec = &ia32_freebsd_sysvec, .interp_newpath = "/libexec/ld-elf32.so.1", .brand_note = &elf32_freebsd_brandnote, .flags = BI_CAN_EXEC_DYN | BI_BRAND_NOTE }; SYSINIT(oia32, SI_SUB_EXEC, SI_ORDER_ANY, (sysinit_cfunc_t) elf32_insert_brand_entry, &ia32_brand_oinfo); static Elf32_Brandinfo kia32_brand_info = { .brand = ELFOSABI_FREEBSD, .machine = EM_386, .compat_3_brand = "FreeBSD", .emul_path = NULL, .interp_path = "/lib/ld.so.1", .sysvec = &ia32_freebsd_sysvec, .brand_note = &elf32_kfreebsd_brandnote, .flags = BI_CAN_EXEC_DYN | BI_BRAND_NOTE_MANDATORY }; SYSINIT(kia32, SI_SUB_EXEC, SI_ORDER_ANY, (sysinit_cfunc_t) elf32_insert_brand_entry, &kia32_brand_info); void -elf32_dump_thread(struct thread *td __unused, void *dst __unused, - size_t *off __unused) +elf32_dump_thread(struct thread *td, void *dst, size_t *off) { + void *buf; + size_t len; + + len = 0; + if (use_xsave) { + if (dst != NULL) { + fpugetregs(td); + len += elf32_populate_note(NT_X86_XSTATE, + get_pcb_user_save_td(td), dst, + cpu_max_ext_state_size, &buf); + *(uint64_t *)((char *)buf + X86_XSTATE_XCR0_OFFSET) = + xsave_mask; + } else + len += elf32_populate_note(NT_X86_XSTATE, NULL, NULL, + cpu_max_ext_state_size, NULL); + } + *off = len; } void ia32_fixlimit(struct rlimit *rl, int which) { switch (which) { case RLIMIT_DATA: if (ia32_maxdsiz != 0) { if (rl->rlim_cur > ia32_maxdsiz) rl->rlim_cur = ia32_maxdsiz; if (rl->rlim_max > ia32_maxdsiz) rl->rlim_max = ia32_maxdsiz; } break; case RLIMIT_STACK: if (ia32_maxssiz != 0) { if (rl->rlim_cur > ia32_maxssiz) rl->rlim_cur = ia32_maxssiz; if (rl->rlim_max > ia32_maxssiz) rl->rlim_max = ia32_maxssiz; } break; case RLIMIT_VMEM: if (ia32_maxvmem != 0) { if (rl->rlim_cur > ia32_maxvmem) rl->rlim_cur = ia32_maxvmem; if (rl->rlim_max > ia32_maxvmem) rl->rlim_max = ia32_maxvmem; } break; } } Index: head/sys/i386/i386/elf_machdep.c =================================================================== --- head/sys/i386/i386/elf_machdep.c (revision 274816) +++ head/sys/i386/i386/elf_machdep.c (revision 274817) @@ -1,262 +1,278 @@ /*- * Copyright 1996-1998 John D. Polstra. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include +#include struct sysentvec elf32_freebsd_sysvec = { .sv_size = SYS_MAXSYSCALL, .sv_table = sysent, .sv_mask = 0, .sv_sigsize = 0, .sv_sigtbl = NULL, .sv_errsize = 0, .sv_errtbl = NULL, .sv_transtrap = NULL, .sv_fixup = __elfN(freebsd_fixup), .sv_sendsig = sendsig, .sv_sigcode = sigcode, .sv_szsigcode = &szsigcode, .sv_prepsyscall = NULL, .sv_name = "FreeBSD ELF32", .sv_coredump = __elfN(coredump), .sv_imgact_try = NULL, .sv_minsigstksz = MINSIGSTKSZ, .sv_pagesize = PAGE_SIZE, .sv_minuser = VM_MIN_ADDRESS, .sv_maxuser = VM_MAXUSER_ADDRESS, .sv_usrstack = USRSTACK, .sv_psstrings = PS_STRINGS, .sv_stackprot = VM_PROT_ALL, .sv_copyout_strings = exec_copyout_strings, .sv_setregs = exec_setregs, .sv_fixlimit = NULL, .sv_maxssiz = NULL, .sv_flags = SV_ABI_FREEBSD | SV_IA32 | SV_ILP32 | SV_SHP, .sv_set_syscall_retval = cpu_set_syscall_retval, .sv_fetch_syscall_args = cpu_fetch_syscall_args, .sv_syscallnames = syscallnames, .sv_shared_page_base = SHAREDPAGE, .sv_shared_page_len = PAGE_SIZE, .sv_schedtail = NULL, }; INIT_SYSENTVEC(elf32_sysvec, &elf32_freebsd_sysvec); static Elf32_Brandinfo freebsd_brand_info = { .brand = ELFOSABI_FREEBSD, .machine = EM_386, .compat_3_brand = "FreeBSD", .emul_path = NULL, .interp_path = "/libexec/ld-elf.so.1", .sysvec = &elf32_freebsd_sysvec, .interp_newpath = NULL, .brand_note = &elf32_freebsd_brandnote, .flags = BI_CAN_EXEC_DYN | BI_BRAND_NOTE }; SYSINIT(elf32, SI_SUB_EXEC, SI_ORDER_FIRST, (sysinit_cfunc_t) elf32_insert_brand_entry, &freebsd_brand_info); static Elf32_Brandinfo freebsd_brand_oinfo = { .brand = ELFOSABI_FREEBSD, .machine = EM_386, .compat_3_brand = "FreeBSD", .emul_path = NULL, .interp_path = "/usr/libexec/ld-elf.so.1", .sysvec = &elf32_freebsd_sysvec, .interp_newpath = NULL, .brand_note = &elf32_freebsd_brandnote, .flags = BI_CAN_EXEC_DYN | BI_BRAND_NOTE }; SYSINIT(oelf32, SI_SUB_EXEC, SI_ORDER_ANY, (sysinit_cfunc_t) elf32_insert_brand_entry, &freebsd_brand_oinfo); static Elf32_Brandinfo kfreebsd_brand_info = { .brand = ELFOSABI_FREEBSD, .machine = EM_386, .compat_3_brand = "FreeBSD", .emul_path = NULL, .interp_path = "/lib/ld.so.1", .sysvec = &elf32_freebsd_sysvec, .interp_newpath = NULL, .brand_note = &elf32_kfreebsd_brandnote, .flags = BI_CAN_EXEC_DYN | BI_BRAND_NOTE_MANDATORY }; SYSINIT(kelf32, SI_SUB_EXEC, SI_ORDER_ANY, (sysinit_cfunc_t) elf32_insert_brand_entry, &kfreebsd_brand_info); void -elf32_dump_thread(struct thread *td __unused, void *dst __unused, - size_t *off __unused) +elf32_dump_thread(struct thread *td, void *dst, size_t *off) { -} + void *buf; + size_t len; + len = 0; + if (use_xsave) { + if (dst != NULL) { + npxgetregs(td); + len += elf32_populate_note(NT_X86_XSTATE, + get_pcb_user_save_td(td), dst, + cpu_max_ext_state_size, &buf); + *(uint64_t *)((char *)buf + X86_XSTATE_XCR0_OFFSET) = + xsave_mask; + } else + len += elf32_populate_note(NT_X86_XSTATE, NULL, NULL, + cpu_max_ext_state_size, NULL); + } + *off = len; +} /* Process one elf relocation with addend. */ static int elf_reloc_internal(linker_file_t lf, Elf_Addr relocbase, const void *data, int type, int local, elf_lookup_fn lookup) { Elf_Addr *where; Elf_Addr addr; Elf_Addr addend; Elf_Word rtype, symidx; const Elf_Rel *rel; const Elf_Rela *rela; switch (type) { case ELF_RELOC_REL: rel = (const Elf_Rel *)data; where = (Elf_Addr *) (relocbase + rel->r_offset); addend = *where; rtype = ELF_R_TYPE(rel->r_info); symidx = ELF_R_SYM(rel->r_info); break; case ELF_RELOC_RELA: rela = (const Elf_Rela *)data; where = (Elf_Addr *) (relocbase + rela->r_offset); addend = rela->r_addend; rtype = ELF_R_TYPE(rela->r_info); symidx = ELF_R_SYM(rela->r_info); break; default: panic("unknown reloc type %d\n", type); } if (local) { if (rtype == R_386_RELATIVE) { /* A + B */ addr = elf_relocaddr(lf, relocbase + addend); if (*where != addr) *where = addr; } return (0); } switch (rtype) { case R_386_NONE: /* none */ break; case R_386_32: /* S + A */ addr = lookup(lf, symidx, 1); if (addr == 0) return -1; addr += addend; if (*where != addr) *where = addr; break; case R_386_PC32: /* S + A - P */ addr = lookup(lf, symidx, 1); if (addr == 0) return -1; addr += addend - (Elf_Addr)where; if (*where != addr) *where = addr; break; case R_386_COPY: /* none */ /* * There shouldn't be copy relocations in kernel * objects. */ printf("kldload: unexpected R_COPY relocation\n"); return -1; break; case R_386_GLOB_DAT: /* S */ addr = lookup(lf, symidx, 1); if (addr == 0) return -1; if (*where != addr) *where = addr; break; case R_386_RELATIVE: break; default: printf("kldload: unexpected relocation type %d\n", rtype); return -1; } return(0); } int elf_reloc(linker_file_t lf, Elf_Addr relocbase, const void *data, int type, elf_lookup_fn lookup) { return (elf_reloc_internal(lf, relocbase, data, type, 0, lookup)); } int elf_reloc_local(linker_file_t lf, Elf_Addr relocbase, const void *data, int type, elf_lookup_fn lookup) { return (elf_reloc_internal(lf, relocbase, data, type, 1, lookup)); } int elf_cpu_load_file(linker_file_t lf __unused) { return (0); } int elf_cpu_unload_file(linker_file_t lf __unused) { return (0); } Index: head/sys/i386/i386/ptrace_machdep.c =================================================================== --- head/sys/i386/i386/ptrace_machdep.c (revision 274816) +++ head/sys/i386/i386/ptrace_machdep.c (revision 274817) @@ -1,122 +1,156 @@ /*- * Copyright (c) 2005 Doug Rabson * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * */ #include __FBSDID("$FreeBSD$"); #include "opt_cpu.h" #include #include #include #include #include #include #include #if !defined(CPU_DISABLE_SSE) && defined(I686_CPU) #define CPU_ENABLE_SSE #endif #ifdef CPU_ENABLE_SSE static int cpu_ptrace_xstate(struct thread *td, int req, void *addr, int data) { + struct ptrace_xstate_info info; char *savefpu; int error; if (!use_xsave) return (EOPNOTSUPP); switch (req) { - case PT_GETXSTATE: + case PT_GETXSTATE_OLD: npxgetregs(td); savefpu = (char *)(get_pcb_user_save_td(td) + 1); error = copyout(savefpu, addr, cpu_max_ext_state_size - sizeof(union savefpu)); break; - case PT_SETXSTATE: + case PT_SETXSTATE_OLD: if (data > cpu_max_ext_state_size - sizeof(union savefpu)) { error = EINVAL; break; } savefpu = malloc(data, M_TEMP, M_WAITOK); error = copyin(addr, savefpu, data); if (error == 0) { npxgetregs(td); error = npxsetxstate(td, savefpu, data); } free(savefpu, M_TEMP); break; + case PT_GETXSTATE_INFO: + if (data != sizeof(info)) { + error = EINVAL; + break; + } + info.xsave_len = cpu_max_ext_state_size; + info.xsave_mask = xsave_mask; + error = copyout(&info, addr, data); + break; + + case PT_GETXSTATE: + npxgetregs(td); + savefpu = (char *)(get_pcb_user_save_td(td)); + error = copyout(savefpu, addr, cpu_max_ext_state_size); + break; + + case PT_SETXSTATE: + if (data > cpu_max_ext_state_size) { + error = EINVAL; + break; + } + savefpu = malloc(data, M_TEMP, M_WAITOK); + error = copyin(addr, savefpu, data); + if (error == 0) + error = npxsetregs(td, (union savefpu *)savefpu, + savefpu + sizeof(union savefpu), data - + sizeof(union savefpu)); + free(savefpu, M_TEMP); + break; + default: error = EINVAL; break; } return (error); } #endif int cpu_ptrace(struct thread *td, int req, void *addr, int data) { #ifdef CPU_ENABLE_SSE struct savexmm *fpstate; int error; if (!cpu_fxsr) return (EINVAL); fpstate = &get_pcb_user_save_td(td)->sv_xmm; switch (req) { case PT_GETXMMREGS: npxgetregs(td); error = copyout(fpstate, addr, sizeof(*fpstate)); break; case PT_SETXMMREGS: npxgetregs(td); error = copyin(addr, fpstate, sizeof(*fpstate)); fpstate->sv_env.en_mxcsr &= cpu_mxcsr_mask; break; + case PT_GETXSTATE_OLD: + case PT_SETXSTATE_OLD: + case PT_GETXSTATE_INFO: case PT_GETXSTATE: case PT_SETXSTATE: error = cpu_ptrace_xstate(td, req, addr, data); break; default: return (EINVAL); } return (error); #else return (EINVAL); #endif } Index: head/sys/i386/isa/npx.c =================================================================== --- head/sys/i386/isa/npx.c (revision 274816) +++ head/sys/i386/isa/npx.c (revision 274817) @@ -1,1405 +1,1412 @@ /*- * Copyright (c) 1990 William Jolitz. * Copyright (c) 1991 The Regents of the University of California. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * from: @(#)npx.c 7.2 (Berkeley) 5/12/91 */ #include __FBSDID("$FreeBSD$"); #include "opt_cpu.h" #include "opt_isa.h" #include "opt_npx.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef NPX_DEBUG #include #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef XEN #include #include #endif #ifdef DEV_ISA #include #endif #if !defined(CPU_DISABLE_SSE) && defined(I686_CPU) #define CPU_ENABLE_SSE #endif /* * 387 and 287 Numeric Coprocessor Extension (NPX) Driver. */ #if defined(__GNUCLIKE_ASM) && !defined(lint) #define fldcw(cw) __asm __volatile("fldcw %0" : : "m" (cw)) #define fnclex() __asm __volatile("fnclex") #define fninit() __asm __volatile("fninit") #define fnsave(addr) __asm __volatile("fnsave %0" : "=m" (*(addr))) #define fnstcw(addr) __asm __volatile("fnstcw %0" : "=m" (*(addr))) #define fnstsw(addr) __asm __volatile("fnstsw %0" : "=am" (*(addr))) #define fp_divide_by_0() __asm __volatile( \ "fldz; fld1; fdiv %st,%st(1); fnop") #define frstor(addr) __asm __volatile("frstor %0" : : "m" (*(addr))) #ifdef CPU_ENABLE_SSE #define fxrstor(addr) __asm __volatile("fxrstor %0" : : "m" (*(addr))) #define fxsave(addr) __asm __volatile("fxsave %0" : "=m" (*(addr))) #define ldmxcsr(csr) __asm __volatile("ldmxcsr %0" : : "m" (csr)) #define stmxcsr(addr) __asm __volatile("stmxcsr %0" : : "m" (*(addr))) static __inline void xrstor(char *addr, uint64_t mask) { uint32_t low, hi; low = mask; hi = mask >> 32; __asm __volatile("xrstor %0" : : "m" (*addr), "a" (low), "d" (hi)); } static __inline void xsave(char *addr, uint64_t mask) { uint32_t low, hi; low = mask; hi = mask >> 32; __asm __volatile("xsave %0" : "=m" (*addr) : "a" (low), "d" (hi) : "memory"); } static __inline void xsaveopt(char *addr, uint64_t mask) { uint32_t low, hi; low = mask; hi = mask >> 32; __asm __volatile("xsaveopt %0" : "=m" (*addr) : "a" (low), "d" (hi) : "memory"); } #endif #else /* !(__GNUCLIKE_ASM && !lint) */ void fldcw(u_short cw); void fnclex(void); void fninit(void); void fnsave(caddr_t addr); void fnstcw(caddr_t addr); void fnstsw(caddr_t addr); void fp_divide_by_0(void); void frstor(caddr_t addr); #ifdef CPU_ENABLE_SSE void fxsave(caddr_t addr); void fxrstor(caddr_t addr); void ldmxcsr(u_int csr); void stmxcsr(u_int *csr); void xrstor(char *addr, uint64_t mask); void xsave(char *addr, uint64_t mask); void xsaveopt(char *addr, uint64_t mask); #endif #endif /* __GNUCLIKE_ASM && !lint */ #ifdef XEN #define start_emulating() (HYPERVISOR_fpu_taskswitch(1)) #define stop_emulating() (HYPERVISOR_fpu_taskswitch(0)) #else #define start_emulating() load_cr0(rcr0() | CR0_TS) #define stop_emulating() clts() #endif #ifdef CPU_ENABLE_SSE #define GET_FPU_CW(thread) \ (cpu_fxsr ? \ (thread)->td_pcb->pcb_save->sv_xmm.sv_env.en_cw : \ (thread)->td_pcb->pcb_save->sv_87.sv_env.en_cw) #define GET_FPU_SW(thread) \ (cpu_fxsr ? \ (thread)->td_pcb->pcb_save->sv_xmm.sv_env.en_sw : \ (thread)->td_pcb->pcb_save->sv_87.sv_env.en_sw) #define SET_FPU_CW(savefpu, value) do { \ if (cpu_fxsr) \ (savefpu)->sv_xmm.sv_env.en_cw = (value); \ else \ (savefpu)->sv_87.sv_env.en_cw = (value); \ } while (0) #else /* CPU_ENABLE_SSE */ #define GET_FPU_CW(thread) \ (thread->td_pcb->pcb_save->sv_87.sv_env.en_cw) #define GET_FPU_SW(thread) \ (thread->td_pcb->pcb_save->sv_87.sv_env.en_sw) #define SET_FPU_CW(savefpu, value) \ (savefpu)->sv_87.sv_env.en_cw = (value) #endif /* CPU_ENABLE_SSE */ #ifdef CPU_ENABLE_SSE CTASSERT(sizeof(union savefpu) == 512); CTASSERT(sizeof(struct xstate_hdr) == 64); CTASSERT(sizeof(struct savefpu_ymm) == 832); /* * This requirement is to make it easier for asm code to calculate * offset of the fpu save area from the pcb address. FPU save area * must be 64-byte aligned. */ CTASSERT(sizeof(struct pcb) % XSAVE_AREA_ALIGN == 0); +/* + * Ensure the copy of XCR0 saved in a core is contained in the padding + * area. + */ +CTASSERT(X86_XSTATE_XCR0_OFFSET >= offsetof(struct savexmm, sv_pad) && + X86_XSTATE_XCR0_OFFSET + sizeof(uint64_t) <= sizeof(struct savexmm)); + static void fpu_clean_state(void); #endif static void fpusave(union savefpu *); static void fpurstor(union savefpu *); int hw_float; SYSCTL_INT(_hw, HW_FLOATINGPT, floatingpoint, CTLFLAG_RD, &hw_float, 0, "Floating point instructions executed in hardware"); #ifdef CPU_ENABLE_SSE int use_xsave; uint64_t xsave_mask; #endif static uma_zone_t fpu_save_area_zone; static union savefpu *npx_initialstate; #ifdef CPU_ENABLE_SSE struct xsave_area_elm_descr { u_int offset; u_int size; } *xsave_area_desc; static int use_xsaveopt; #endif static volatile u_int npx_traps_while_probing; alias_for_inthand_t probetrap; __asm(" \n\ .text \n\ .p2align 2,0x90 \n\ .type " __XSTRING(CNAME(probetrap)) ",@function \n\ " __XSTRING(CNAME(probetrap)) ": \n\ ss \n\ incl " __XSTRING(CNAME(npx_traps_while_probing)) " \n\ fnclex \n\ iret \n\ "); /* * Determine if an FPU is present and how to use it. */ static int npx_probe(void) { struct gate_descriptor save_idt_npxtrap; u_short control, status; /* * Modern CPUs all have an FPU that uses the INT16 interface * and provide a simple way to verify that, so handle the * common case right away. */ if (cpu_feature & CPUID_FPU) { hw_float = 1; return (1); } save_idt_npxtrap = idt[IDT_MF]; setidt(IDT_MF, probetrap, SDT_SYS386TGT, SEL_KPL, GSEL(GCODE_SEL, SEL_KPL)); /* * Don't trap while we're probing. */ stop_emulating(); /* * Finish resetting the coprocessor, if any. If there is an error * pending, then we may get a bogus IRQ13, but npx_intr() will handle * it OK. Bogus halts have never been observed, but we enabled * IRQ13 and cleared the BUSY# latch early to handle them anyway. */ fninit(); /* * Don't use fwait here because it might hang. * Don't use fnop here because it usually hangs if there is no FPU. */ DELAY(1000); /* wait for any IRQ13 */ #ifdef DIAGNOSTIC if (npx_traps_while_probing != 0) printf("fninit caused %u bogus npx trap(s)\n", npx_traps_while_probing); #endif /* * Check for a status of mostly zero. */ status = 0x5a5a; fnstsw(&status); if ((status & 0xb8ff) == 0) { /* * Good, now check for a proper control word. */ control = 0x5a5a; fnstcw(&control); if ((control & 0x1f3f) == 0x033f) { /* * We have an npx, now divide by 0 to see if exception * 16 works. */ control &= ~(1 << 2); /* enable divide by 0 trap */ fldcw(control); #ifdef FPU_ERROR_BROKEN /* * FPU error signal doesn't work on some CPU * accelerator board. */ hw_float = 1; return (1); #endif npx_traps_while_probing = 0; fp_divide_by_0(); if (npx_traps_while_probing != 0) { /* * Good, exception 16 works. */ hw_float = 1; goto cleanup; } printf( "FPU does not use exception 16 for error reporting\n"); goto cleanup; } } /* * Probe failed. Floating point simply won't work. * Notify user and disable FPU/MMX/SSE instruction execution. */ printf("WARNING: no FPU!\n"); __asm __volatile("smsw %%ax; orb %0,%%al; lmsw %%ax" : : "n" (CR0_EM | CR0_MP) : "ax"); cleanup: idt[IDT_MF] = save_idt_npxtrap; return (hw_float); } #ifdef CPU_ENABLE_SSE /* * Enable XSAVE if supported and allowed by user. * Calculate the xsave_mask. */ static void npxinit_bsp1(void) { u_int cp[4]; uint64_t xsave_mask_user; if (cpu_fxsr && (cpu_feature2 & CPUID2_XSAVE) != 0) { use_xsave = 1; TUNABLE_INT_FETCH("hw.use_xsave", &use_xsave); } if (!use_xsave) return; cpuid_count(0xd, 0x0, cp); xsave_mask = XFEATURE_ENABLED_X87 | XFEATURE_ENABLED_SSE; if ((cp[0] & xsave_mask) != xsave_mask) panic("CPU0 does not support X87 or SSE: %x", cp[0]); xsave_mask = ((uint64_t)cp[3] << 32) | cp[0]; xsave_mask_user = xsave_mask; TUNABLE_QUAD_FETCH("hw.xsave_mask", &xsave_mask_user); xsave_mask_user |= XFEATURE_ENABLED_X87 | XFEATURE_ENABLED_SSE; xsave_mask &= xsave_mask_user; if ((xsave_mask & XFEATURE_AVX512) != XFEATURE_AVX512) xsave_mask &= ~XFEATURE_AVX512; if ((xsave_mask & XFEATURE_MPX) != XFEATURE_MPX) xsave_mask &= ~XFEATURE_MPX; cpuid_count(0xd, 0x1, cp); if ((cp[0] & CPUID_EXTSTATE_XSAVEOPT) != 0) use_xsaveopt = 1; } #endif /* * Calculate the fpu save area size. */ static void npxinit_bsp2(void) { #ifdef CPU_ENABLE_SSE u_int cp[4]; if (use_xsave) { cpuid_count(0xd, 0x0, cp); cpu_max_ext_state_size = cp[1]; /* * Reload the cpu_feature2, since we enabled OSXSAVE. */ do_cpuid(1, cp); cpu_feature2 = cp[2]; } else #endif cpu_max_ext_state_size = sizeof(union savefpu); } /* * Initialize floating point unit. */ void npxinit(bool bsp) { static union savefpu dummy; register_t saveintr; #ifdef CPU_ENABLE_SSE u_int mxcsr; #endif u_short control; if (bsp) { if (!npx_probe()) return; #ifdef CPU_ENABLE_SSE npxinit_bsp1(); #endif } #ifdef CPU_ENABLE_SSE if (use_xsave) { load_cr4(rcr4() | CR4_XSAVE); load_xcr(XCR0, xsave_mask); } #endif /* * XCR0 shall be set up before CPU can report the save area size. */ if (bsp) npxinit_bsp2(); /* * fninit has the same h/w bugs as fnsave. Use the detoxified * fnsave to throw away any junk in the fpu. fpusave() initializes * the fpu. * * It is too early for critical_enter() to work on AP. */ saveintr = intr_disable(); stop_emulating(); #ifdef CPU_ENABLE_SSE if (cpu_fxsr) fninit(); else #endif fnsave(&dummy); control = __INITIAL_NPXCW__; fldcw(control); #ifdef CPU_ENABLE_SSE if (cpu_fxsr) { mxcsr = __INITIAL_MXCSR__; ldmxcsr(mxcsr); } #endif start_emulating(); intr_restore(saveintr); } /* * On the boot CPU we generate a clean state that is used to * initialize the floating point unit when it is first used by a * process. */ static void npxinitstate(void *arg __unused) { register_t saveintr; #ifdef CPU_ENABLE_SSE int cp[4], i, max_ext_n; #endif if (!hw_float) return; npx_initialstate = malloc(cpu_max_ext_state_size, M_DEVBUF, M_WAITOK | M_ZERO); saveintr = intr_disable(); stop_emulating(); fpusave(npx_initialstate); #ifdef CPU_ENABLE_SSE if (cpu_fxsr) { if (npx_initialstate->sv_xmm.sv_env.en_mxcsr_mask) cpu_mxcsr_mask = npx_initialstate->sv_xmm.sv_env.en_mxcsr_mask; else cpu_mxcsr_mask = 0xFFBF; /* * The fninit instruction does not modify XMM * registers. The fpusave call dumped the garbage * contained in the registers after reset to the * initial state saved. Clear XMM registers file * image to make the startup program state and signal * handler XMM register content predictable. */ bzero(npx_initialstate->sv_xmm.sv_fp, sizeof(npx_initialstate->sv_xmm.sv_fp)); bzero(npx_initialstate->sv_xmm.sv_xmm, sizeof(npx_initialstate->sv_xmm.sv_xmm)); } else #endif bzero(npx_initialstate->sv_87.sv_ac, sizeof(npx_initialstate->sv_87.sv_ac)); #ifdef CPU_ENABLE_SSE /* * Create a table describing the layout of the CPU Extended * Save Area. */ if (use_xsave) { if (xsave_mask >> 32 != 0) max_ext_n = fls(xsave_mask >> 32) + 32; else max_ext_n = fls(xsave_mask); xsave_area_desc = malloc(max_ext_n * sizeof(struct xsave_area_elm_descr), M_DEVBUF, M_WAITOK | M_ZERO); /* x87 state */ xsave_area_desc[0].offset = 0; xsave_area_desc[0].size = 160; /* XMM */ xsave_area_desc[1].offset = 160; xsave_area_desc[1].size = 288 - 160; for (i = 2; i < max_ext_n; i++) { cpuid_count(0xd, i, cp); xsave_area_desc[i].offset = cp[1]; xsave_area_desc[i].size = cp[0]; } } #endif fpu_save_area_zone = uma_zcreate("FPU_save_area", cpu_max_ext_state_size, NULL, NULL, NULL, NULL, XSAVE_AREA_ALIGN - 1, 0); start_emulating(); intr_restore(saveintr); } SYSINIT(npxinitstate, SI_SUB_DRIVERS, SI_ORDER_ANY, npxinitstate, NULL); /* * Free coprocessor (if we have it). */ void npxexit(td) struct thread *td; { critical_enter(); if (curthread == PCPU_GET(fpcurthread)) { stop_emulating(); fpusave(curpcb->pcb_save); start_emulating(); PCPU_SET(fpcurthread, NULL); } critical_exit(); #ifdef NPX_DEBUG if (hw_float) { u_int masked_exceptions; masked_exceptions = GET_FPU_CW(td) & GET_FPU_SW(td) & 0x7f; /* * Log exceptions that would have trapped with the old * control word (overflow, divide by 0, and invalid operand). */ if (masked_exceptions & 0x0d) log(LOG_ERR, "pid %d (%s) exited with masked floating point exceptions 0x%02x\n", td->td_proc->p_pid, td->td_proc->p_comm, masked_exceptions); } #endif } int npxformat() { if (!hw_float) return (_MC_FPFMT_NODEV); #ifdef CPU_ENABLE_SSE if (cpu_fxsr) return (_MC_FPFMT_XMM); #endif return (_MC_FPFMT_387); } /* * The following mechanism is used to ensure that the FPE_... value * that is passed as a trapcode to the signal handler of the user * process does not have more than one bit set. * * Multiple bits may be set if the user process modifies the control * word while a status word bit is already set. While this is a sign * of bad coding, we have no choise than to narrow them down to one * bit, since we must not send a trapcode that is not exactly one of * the FPE_ macros. * * The mechanism has a static table with 127 entries. Each combination * of the 7 FPU status word exception bits directly translates to a * position in this table, where a single FPE_... value is stored. * This FPE_... value stored there is considered the "most important" * of the exception bits and will be sent as the signal code. The * precedence of the bits is based upon Intel Document "Numerical * Applications", Chapter "Special Computational Situations". * * The macro to choose one of these values does these steps: 1) Throw * away status word bits that cannot be masked. 2) Throw away the bits * currently masked in the control word, assuming the user isn't * interested in them anymore. 3) Reinsert status word bit 7 (stack * fault) if it is set, which cannot be masked but must be presered. * 4) Use the remaining bits to point into the trapcode table. * * The 6 maskable bits in order of their preference, as stated in the * above referenced Intel manual: * 1 Invalid operation (FP_X_INV) * 1a Stack underflow * 1b Stack overflow * 1c Operand of unsupported format * 1d SNaN operand. * 2 QNaN operand (not an exception, irrelavant here) * 3 Any other invalid-operation not mentioned above or zero divide * (FP_X_INV, FP_X_DZ) * 4 Denormal operand (FP_X_DNML) * 5 Numeric over/underflow (FP_X_OFL, FP_X_UFL) * 6 Inexact result (FP_X_IMP) */ static char fpetable[128] = { 0, FPE_FLTINV, /* 1 - INV */ FPE_FLTUND, /* 2 - DNML */ FPE_FLTINV, /* 3 - INV | DNML */ FPE_FLTDIV, /* 4 - DZ */ FPE_FLTINV, /* 5 - INV | DZ */ FPE_FLTDIV, /* 6 - DNML | DZ */ FPE_FLTINV, /* 7 - INV | DNML | DZ */ FPE_FLTOVF, /* 8 - OFL */ FPE_FLTINV, /* 9 - INV | OFL */ FPE_FLTUND, /* A - DNML | OFL */ FPE_FLTINV, /* B - INV | DNML | OFL */ FPE_FLTDIV, /* C - DZ | OFL */ FPE_FLTINV, /* D - INV | DZ | OFL */ FPE_FLTDIV, /* E - DNML | DZ | OFL */ FPE_FLTINV, /* F - INV | DNML | DZ | OFL */ FPE_FLTUND, /* 10 - UFL */ FPE_FLTINV, /* 11 - INV | UFL */ FPE_FLTUND, /* 12 - DNML | UFL */ FPE_FLTINV, /* 13 - INV | DNML | UFL */ FPE_FLTDIV, /* 14 - DZ | UFL */ FPE_FLTINV, /* 15 - INV | DZ | UFL */ FPE_FLTDIV, /* 16 - DNML | DZ | UFL */ FPE_FLTINV, /* 17 - INV | DNML | DZ | UFL */ FPE_FLTOVF, /* 18 - OFL | UFL */ FPE_FLTINV, /* 19 - INV | OFL | UFL */ FPE_FLTUND, /* 1A - DNML | OFL | UFL */ FPE_FLTINV, /* 1B - INV | DNML | OFL | UFL */ FPE_FLTDIV, /* 1C - DZ | OFL | UFL */ FPE_FLTINV, /* 1D - INV | DZ | OFL | UFL */ FPE_FLTDIV, /* 1E - DNML | DZ | OFL | UFL */ FPE_FLTINV, /* 1F - INV | DNML | DZ | OFL | UFL */ FPE_FLTRES, /* 20 - IMP */ FPE_FLTINV, /* 21 - INV | IMP */ FPE_FLTUND, /* 22 - DNML | IMP */ FPE_FLTINV, /* 23 - INV | DNML | IMP */ FPE_FLTDIV, /* 24 - DZ | IMP */ FPE_FLTINV, /* 25 - INV | DZ | IMP */ FPE_FLTDIV, /* 26 - DNML | DZ | IMP */ FPE_FLTINV, /* 27 - INV | DNML | DZ | IMP */ FPE_FLTOVF, /* 28 - OFL | IMP */ FPE_FLTINV, /* 29 - INV | OFL | IMP */ FPE_FLTUND, /* 2A - DNML | OFL | IMP */ FPE_FLTINV, /* 2B - INV | DNML | OFL | IMP */ FPE_FLTDIV, /* 2C - DZ | OFL | IMP */ FPE_FLTINV, /* 2D - INV | DZ | OFL | IMP */ FPE_FLTDIV, /* 2E - DNML | DZ | OFL | IMP */ FPE_FLTINV, /* 2F - INV | DNML | DZ | OFL | IMP */ FPE_FLTUND, /* 30 - UFL | IMP */ FPE_FLTINV, /* 31 - INV | UFL | IMP */ FPE_FLTUND, /* 32 - DNML | UFL | IMP */ FPE_FLTINV, /* 33 - INV | DNML | UFL | IMP */ FPE_FLTDIV, /* 34 - DZ | UFL | IMP */ FPE_FLTINV, /* 35 - INV | DZ | UFL | IMP */ FPE_FLTDIV, /* 36 - DNML | DZ | UFL | IMP */ FPE_FLTINV, /* 37 - INV | DNML | DZ | UFL | IMP */ FPE_FLTOVF, /* 38 - OFL | UFL | IMP */ FPE_FLTINV, /* 39 - INV | OFL | UFL | IMP */ FPE_FLTUND, /* 3A - DNML | OFL | UFL | IMP */ FPE_FLTINV, /* 3B - INV | DNML | OFL | UFL | IMP */ FPE_FLTDIV, /* 3C - DZ | OFL | UFL | IMP */ FPE_FLTINV, /* 3D - INV | DZ | OFL | UFL | IMP */ FPE_FLTDIV, /* 3E - DNML | DZ | OFL | UFL | IMP */ FPE_FLTINV, /* 3F - INV | DNML | DZ | OFL | UFL | IMP */ FPE_FLTSUB, /* 40 - STK */ FPE_FLTSUB, /* 41 - INV | STK */ FPE_FLTUND, /* 42 - DNML | STK */ FPE_FLTSUB, /* 43 - INV | DNML | STK */ FPE_FLTDIV, /* 44 - DZ | STK */ FPE_FLTSUB, /* 45 - INV | DZ | STK */ FPE_FLTDIV, /* 46 - DNML | DZ | STK */ FPE_FLTSUB, /* 47 - INV | DNML | DZ | STK */ FPE_FLTOVF, /* 48 - OFL | STK */ FPE_FLTSUB, /* 49 - INV | OFL | STK */ FPE_FLTUND, /* 4A - DNML | OFL | STK */ FPE_FLTSUB, /* 4B - INV | DNML | OFL | STK */ FPE_FLTDIV, /* 4C - DZ | OFL | STK */ FPE_FLTSUB, /* 4D - INV | DZ | OFL | STK */ FPE_FLTDIV, /* 4E - DNML | DZ | OFL | STK */ FPE_FLTSUB, /* 4F - INV | DNML | DZ | OFL | STK */ FPE_FLTUND, /* 50 - UFL | STK */ FPE_FLTSUB, /* 51 - INV | UFL | STK */ FPE_FLTUND, /* 52 - DNML | UFL | STK */ FPE_FLTSUB, /* 53 - INV | DNML | UFL | STK */ FPE_FLTDIV, /* 54 - DZ | UFL | STK */ FPE_FLTSUB, /* 55 - INV | DZ | UFL | STK */ FPE_FLTDIV, /* 56 - DNML | DZ | UFL | STK */ FPE_FLTSUB, /* 57 - INV | DNML | DZ | UFL | STK */ FPE_FLTOVF, /* 58 - OFL | UFL | STK */ FPE_FLTSUB, /* 59 - INV | OFL | UFL | STK */ FPE_FLTUND, /* 5A - DNML | OFL | UFL | STK */ FPE_FLTSUB, /* 5B - INV | DNML | OFL | UFL | STK */ FPE_FLTDIV, /* 5C - DZ | OFL | UFL | STK */ FPE_FLTSUB, /* 5D - INV | DZ | OFL | UFL | STK */ FPE_FLTDIV, /* 5E - DNML | DZ | OFL | UFL | STK */ FPE_FLTSUB, /* 5F - INV | DNML | DZ | OFL | UFL | STK */ FPE_FLTRES, /* 60 - IMP | STK */ FPE_FLTSUB, /* 61 - INV | IMP | STK */ FPE_FLTUND, /* 62 - DNML | IMP | STK */ FPE_FLTSUB, /* 63 - INV | DNML | IMP | STK */ FPE_FLTDIV, /* 64 - DZ | IMP | STK */ FPE_FLTSUB, /* 65 - INV | DZ | IMP | STK */ FPE_FLTDIV, /* 66 - DNML | DZ | IMP | STK */ FPE_FLTSUB, /* 67 - INV | DNML | DZ | IMP | STK */ FPE_FLTOVF, /* 68 - OFL | IMP | STK */ FPE_FLTSUB, /* 69 - INV | OFL | IMP | STK */ FPE_FLTUND, /* 6A - DNML | OFL | IMP | STK */ FPE_FLTSUB, /* 6B - INV | DNML | OFL | IMP | STK */ FPE_FLTDIV, /* 6C - DZ | OFL | IMP | STK */ FPE_FLTSUB, /* 6D - INV | DZ | OFL | IMP | STK */ FPE_FLTDIV, /* 6E - DNML | DZ | OFL | IMP | STK */ FPE_FLTSUB, /* 6F - INV | DNML | DZ | OFL | IMP | STK */ FPE_FLTUND, /* 70 - UFL | IMP | STK */ FPE_FLTSUB, /* 71 - INV | UFL | IMP | STK */ FPE_FLTUND, /* 72 - DNML | UFL | IMP | STK */ FPE_FLTSUB, /* 73 - INV | DNML | UFL | IMP | STK */ FPE_FLTDIV, /* 74 - DZ | UFL | IMP | STK */ FPE_FLTSUB, /* 75 - INV | DZ | UFL | IMP | STK */ FPE_FLTDIV, /* 76 - DNML | DZ | UFL | IMP | STK */ FPE_FLTSUB, /* 77 - INV | DNML | DZ | UFL | IMP | STK */ FPE_FLTOVF, /* 78 - OFL | UFL | IMP | STK */ FPE_FLTSUB, /* 79 - INV | OFL | UFL | IMP | STK */ FPE_FLTUND, /* 7A - DNML | OFL | UFL | IMP | STK */ FPE_FLTSUB, /* 7B - INV | DNML | OFL | UFL | IMP | STK */ FPE_FLTDIV, /* 7C - DZ | OFL | UFL | IMP | STK */ FPE_FLTSUB, /* 7D - INV | DZ | OFL | UFL | IMP | STK */ FPE_FLTDIV, /* 7E - DNML | DZ | OFL | UFL | IMP | STK */ FPE_FLTSUB, /* 7F - INV | DNML | DZ | OFL | UFL | IMP | STK */ }; /* * Read the FP status and control words, then generate si_code value * for SIGFPE. The error code chosen will be one of the * FPE_... macros. It will be sent as the second argument to old * BSD-style signal handlers and as "siginfo_t->si_code" (second * argument) to SA_SIGINFO signal handlers. * * Some time ago, we cleared the x87 exceptions with FNCLEX there. * Clearing exceptions was necessary mainly to avoid IRQ13 bugs. The * usermode code which understands the FPU hardware enough to enable * the exceptions, can also handle clearing the exception state in the * handler. The only consequence of not clearing the exception is the * rethrow of the SIGFPE on return from the signal handler and * reexecution of the corresponding instruction. * * For XMM traps, the exceptions were never cleared. */ int npxtrap_x87(void) { u_short control, status; if (!hw_float) { printf( "npxtrap_x87: fpcurthread = %p, curthread = %p, hw_float = %d\n", PCPU_GET(fpcurthread), curthread, hw_float); panic("npxtrap from nowhere"); } critical_enter(); /* * Interrupt handling (for another interrupt) may have pushed the * state to memory. Fetch the relevant parts of the state from * wherever they are. */ if (PCPU_GET(fpcurthread) != curthread) { control = GET_FPU_CW(curthread); status = GET_FPU_SW(curthread); } else { fnstcw(&control); fnstsw(&status); } critical_exit(); return (fpetable[status & ((~control & 0x3f) | 0x40)]); } #ifdef CPU_ENABLE_SSE int npxtrap_sse(void) { u_int mxcsr; if (!hw_float) { printf( "npxtrap_sse: fpcurthread = %p, curthread = %p, hw_float = %d\n", PCPU_GET(fpcurthread), curthread, hw_float); panic("npxtrap from nowhere"); } critical_enter(); if (PCPU_GET(fpcurthread) != curthread) mxcsr = curthread->td_pcb->pcb_save->sv_xmm.sv_env.en_mxcsr; else stmxcsr(&mxcsr); critical_exit(); return (fpetable[(mxcsr & (~mxcsr >> 7)) & 0x3f]); } #endif /* * Implement device not available (DNA) exception * * It would be better to switch FP context here (if curthread != fpcurthread) * and not necessarily for every context switch, but it is too hard to * access foreign pcb's. */ static int err_count = 0; int npxdna(void) { if (!hw_float) return (0); critical_enter(); if (PCPU_GET(fpcurthread) == curthread) { printf("npxdna: fpcurthread == curthread %d times\n", ++err_count); stop_emulating(); critical_exit(); return (1); } if (PCPU_GET(fpcurthread) != NULL) { printf("npxdna: fpcurthread = %p (%d), curthread = %p (%d)\n", PCPU_GET(fpcurthread), PCPU_GET(fpcurthread)->td_proc->p_pid, curthread, curthread->td_proc->p_pid); panic("npxdna"); } stop_emulating(); /* * Record new context early in case frstor causes a trap. */ PCPU_SET(fpcurthread, curthread); #ifdef CPU_ENABLE_SSE if (cpu_fxsr) fpu_clean_state(); #endif if ((curpcb->pcb_flags & PCB_NPXINITDONE) == 0) { /* * This is the first time this thread has used the FPU or * the PCB doesn't contain a clean FPU state. Explicitly * load an initial state. * * We prefer to restore the state from the actual save * area in PCB instead of directly loading from * npx_initialstate, to ignite the XSAVEOPT * tracking engine. */ bcopy(npx_initialstate, curpcb->pcb_save, cpu_max_ext_state_size); fpurstor(curpcb->pcb_save); if (curpcb->pcb_initial_npxcw != __INITIAL_NPXCW__) fldcw(curpcb->pcb_initial_npxcw); curpcb->pcb_flags |= PCB_NPXINITDONE; if (PCB_USER_FPU(curpcb)) curpcb->pcb_flags |= PCB_NPXUSERINITDONE; } else { fpurstor(curpcb->pcb_save); } critical_exit(); return (1); } /* * Wrapper for fpusave() called from context switch routines. * * npxsave() must be called with interrupts disabled, so that it clears * fpcurthread atomically with saving the state. We require callers to do the * disabling, since most callers need to disable interrupts anyway to call * npxsave() atomically with checking fpcurthread. */ void npxsave(addr) union savefpu *addr; { stop_emulating(); #ifdef CPU_ENABLE_SSE if (use_xsaveopt) xsaveopt((char *)addr, xsave_mask); else #endif fpusave(addr); start_emulating(); PCPU_SET(fpcurthread, NULL); } /* * Unconditionally save the current co-processor state across suspend and * resume. */ void npxsuspend(union savefpu *addr) { register_t cr0; if (!hw_float) return; if (PCPU_GET(fpcurthread) == NULL) { bcopy(npx_initialstate, addr, cpu_max_ext_state_size); return; } cr0 = rcr0(); stop_emulating(); fpusave(addr); load_cr0(cr0); } void npxresume(union savefpu *addr) { register_t cr0; if (!hw_float) return; cr0 = rcr0(); npxinit(false); stop_emulating(); fpurstor(addr); load_cr0(cr0); } void npxdrop() { struct thread *td; /* * Discard pending exceptions in the !cpu_fxsr case so that unmasked * ones don't cause a panic on the next frstor. */ #ifdef CPU_ENABLE_SSE if (!cpu_fxsr) #endif fnclex(); td = PCPU_GET(fpcurthread); KASSERT(td == curthread, ("fpudrop: fpcurthread != curthread")); CRITICAL_ASSERT(td); PCPU_SET(fpcurthread, NULL); td->td_pcb->pcb_flags &= ~PCB_NPXINITDONE; start_emulating(); } /* * Get the user state of the FPU into pcb->pcb_user_save without * dropping ownership (if possible). It returns the FPU ownership * status. */ int npxgetregs(struct thread *td) { struct pcb *pcb; #ifdef CPU_ENABLE_SSE uint64_t *xstate_bv, bit; char *sa; int max_ext_n, i; #endif int owned; if (!hw_float) return (_MC_FPOWNED_NONE); pcb = td->td_pcb; if ((pcb->pcb_flags & PCB_NPXINITDONE) == 0) { bcopy(npx_initialstate, get_pcb_user_save_pcb(pcb), cpu_max_ext_state_size); SET_FPU_CW(get_pcb_user_save_pcb(pcb), pcb->pcb_initial_npxcw); npxuserinited(td); return (_MC_FPOWNED_PCB); } critical_enter(); if (td == PCPU_GET(fpcurthread)) { fpusave(get_pcb_user_save_pcb(pcb)); #ifdef CPU_ENABLE_SSE if (!cpu_fxsr) #endif /* * fnsave initializes the FPU and destroys whatever * context it contains. Make sure the FPU owner * starts with a clean state next time. */ npxdrop(); owned = _MC_FPOWNED_FPU; } else { owned = _MC_FPOWNED_PCB; } critical_exit(); #ifdef CPU_ENABLE_SSE if (use_xsave) { /* * Handle partially saved state. */ sa = (char *)get_pcb_user_save_pcb(pcb); xstate_bv = (uint64_t *)(sa + sizeof(union savefpu) + offsetof(struct xstate_hdr, xstate_bv)); if (xsave_mask >> 32 != 0) max_ext_n = fls(xsave_mask >> 32) + 32; else max_ext_n = fls(xsave_mask); for (i = 0; i < max_ext_n; i++) { bit = 1ULL << i; if ((xsave_mask & bit) == 0 || (*xstate_bv & bit) != 0) continue; bcopy((char *)npx_initialstate + xsave_area_desc[i].offset, sa + xsave_area_desc[i].offset, xsave_area_desc[i].size); *xstate_bv |= bit; } } #endif return (owned); } void npxuserinited(struct thread *td) { struct pcb *pcb; pcb = td->td_pcb; if (PCB_USER_FPU(pcb)) pcb->pcb_flags |= PCB_NPXINITDONE; pcb->pcb_flags |= PCB_NPXUSERINITDONE; } #ifdef CPU_ENABLE_SSE int npxsetxstate(struct thread *td, char *xfpustate, size_t xfpustate_size) { struct xstate_hdr *hdr, *ehdr; size_t len, max_len; uint64_t bv; /* XXXKIB should we clear all extended state in xstate_bv instead ? */ if (xfpustate == NULL) return (0); if (!use_xsave) return (EOPNOTSUPP); len = xfpustate_size; if (len < sizeof(struct xstate_hdr)) return (EINVAL); max_len = cpu_max_ext_state_size - sizeof(union savefpu); if (len > max_len) return (EINVAL); ehdr = (struct xstate_hdr *)xfpustate; bv = ehdr->xstate_bv; /* * Avoid #gp. */ if (bv & ~xsave_mask) return (EINVAL); hdr = (struct xstate_hdr *)(get_pcb_user_save_td(td) + 1); hdr->xstate_bv = bv; bcopy(xfpustate + sizeof(struct xstate_hdr), (char *)(hdr + 1), len - sizeof(struct xstate_hdr)); return (0); } #endif int npxsetregs(struct thread *td, union savefpu *addr, char *xfpustate, size_t xfpustate_size) { struct pcb *pcb; #ifdef CPU_ENABLE_SSE int error; #endif if (!hw_float) return (ENXIO); pcb = td->td_pcb; critical_enter(); if (td == PCPU_GET(fpcurthread) && PCB_USER_FPU(pcb)) { #ifdef CPU_ENABLE_SSE error = npxsetxstate(td, xfpustate, xfpustate_size); if (error != 0) { critical_exit(); return (error); } if (!cpu_fxsr) #endif fnclex(); /* As in npxdrop(). */ bcopy(addr, get_pcb_user_save_td(td), sizeof(*addr)); fpurstor(get_pcb_user_save_td(td)); critical_exit(); pcb->pcb_flags |= PCB_NPXUSERINITDONE | PCB_NPXINITDONE; } else { critical_exit(); #ifdef CPU_ENABLE_SSE error = npxsetxstate(td, xfpustate, xfpustate_size); if (error != 0) return (error); #endif bcopy(addr, get_pcb_user_save_td(td), sizeof(*addr)); npxuserinited(td); } return (0); } static void fpusave(addr) union savefpu *addr; { #ifdef CPU_ENABLE_SSE if (use_xsave) xsave((char *)addr, xsave_mask); else if (cpu_fxsr) fxsave(addr); else #endif fnsave(addr); } #ifdef CPU_ENABLE_SSE /* * On AuthenticAMD processors, the fxrstor instruction does not restore * the x87's stored last instruction pointer, last data pointer, and last * opcode values, except in the rare case in which the exception summary * (ES) bit in the x87 status word is set to 1. * * In order to avoid leaking this information across processes, we clean * these values by performing a dummy load before executing fxrstor(). */ static void fpu_clean_state(void) { static float dummy_variable = 0.0; u_short status; /* * Clear the ES bit in the x87 status word if it is currently * set, in order to avoid causing a fault in the upcoming load. */ fnstsw(&status); if (status & 0x80) fnclex(); /* * Load the dummy variable into the x87 stack. This mangles * the x87 stack, but we don't care since we're about to call * fxrstor() anyway. */ __asm __volatile("ffree %%st(7); flds %0" : : "m" (dummy_variable)); } #endif /* CPU_ENABLE_SSE */ static void fpurstor(addr) union savefpu *addr; { #ifdef CPU_ENABLE_SSE if (use_xsave) xrstor((char *)addr, xsave_mask); else if (cpu_fxsr) fxrstor(addr); else #endif frstor(addr); } #ifdef DEV_ISA /* * This sucks up the legacy ISA support assignments from PNPBIOS/ACPI. */ static struct isa_pnp_id npxisa_ids[] = { { 0x040cd041, "Legacy ISA coprocessor support" }, /* PNP0C04 */ { 0 } }; static int npxisa_probe(device_t dev) { int result; if ((result = ISA_PNP_PROBE(device_get_parent(dev), dev, npxisa_ids)) <= 0) { device_quiet(dev); } return(result); } static int npxisa_attach(device_t dev) { return (0); } static device_method_t npxisa_methods[] = { /* Device interface */ DEVMETHOD(device_probe, npxisa_probe), DEVMETHOD(device_attach, npxisa_attach), DEVMETHOD(device_detach, bus_generic_detach), DEVMETHOD(device_shutdown, bus_generic_shutdown), DEVMETHOD(device_suspend, bus_generic_suspend), DEVMETHOD(device_resume, bus_generic_resume), { 0, 0 } }; static driver_t npxisa_driver = { "npxisa", npxisa_methods, 1, /* no softc */ }; static devclass_t npxisa_devclass; DRIVER_MODULE(npxisa, isa, npxisa_driver, npxisa_devclass, 0, 0); #ifndef PC98 DRIVER_MODULE(npxisa, acpi, npxisa_driver, npxisa_devclass, 0, 0); #endif #endif /* DEV_ISA */ static MALLOC_DEFINE(M_FPUKERN_CTX, "fpukern_ctx", "Kernel contexts for FPU state"); #define FPU_KERN_CTX_NPXINITDONE 0x01 #define FPU_KERN_CTX_DUMMY 0x02 struct fpu_kern_ctx { union savefpu *prev; uint32_t flags; char hwstate1[]; }; struct fpu_kern_ctx * fpu_kern_alloc_ctx(u_int flags) { struct fpu_kern_ctx *res; size_t sz; sz = sizeof(struct fpu_kern_ctx) + XSAVE_AREA_ALIGN + cpu_max_ext_state_size; res = malloc(sz, M_FPUKERN_CTX, ((flags & FPU_KERN_NOWAIT) ? M_NOWAIT : M_WAITOK) | M_ZERO); return (res); } void fpu_kern_free_ctx(struct fpu_kern_ctx *ctx) { /* XXXKIB clear the memory ? */ free(ctx, M_FPUKERN_CTX); } static union savefpu * fpu_kern_ctx_savefpu(struct fpu_kern_ctx *ctx) { vm_offset_t p; p = (vm_offset_t)&ctx->hwstate1; p = roundup2(p, XSAVE_AREA_ALIGN); return ((union savefpu *)p); } int fpu_kern_enter(struct thread *td, struct fpu_kern_ctx *ctx, u_int flags) { struct pcb *pcb; if ((flags & FPU_KERN_KTHR) != 0 && is_fpu_kern_thread(0)) { ctx->flags = FPU_KERN_CTX_DUMMY; return (0); } pcb = td->td_pcb; KASSERT(!PCB_USER_FPU(pcb) || pcb->pcb_save == get_pcb_user_save_pcb(pcb), ("mangled pcb_save")); ctx->flags = 0; if ((pcb->pcb_flags & PCB_NPXINITDONE) != 0) ctx->flags |= FPU_KERN_CTX_NPXINITDONE; npxexit(td); ctx->prev = pcb->pcb_save; pcb->pcb_save = fpu_kern_ctx_savefpu(ctx); pcb->pcb_flags |= PCB_KERNNPX; pcb->pcb_flags &= ~PCB_NPXINITDONE; return (0); } int fpu_kern_leave(struct thread *td, struct fpu_kern_ctx *ctx) { struct pcb *pcb; if (is_fpu_kern_thread(0) && (ctx->flags & FPU_KERN_CTX_DUMMY) != 0) return (0); pcb = td->td_pcb; critical_enter(); if (curthread == PCPU_GET(fpcurthread)) npxdrop(); critical_exit(); pcb->pcb_save = ctx->prev; if (pcb->pcb_save == get_pcb_user_save_pcb(pcb)) { if ((pcb->pcb_flags & PCB_NPXUSERINITDONE) != 0) pcb->pcb_flags |= PCB_NPXINITDONE; else pcb->pcb_flags &= ~PCB_NPXINITDONE; pcb->pcb_flags &= ~PCB_KERNNPX; } else { if ((ctx->flags & FPU_KERN_CTX_NPXINITDONE) != 0) pcb->pcb_flags |= PCB_NPXINITDONE; else pcb->pcb_flags &= ~PCB_NPXINITDONE; KASSERT(!PCB_USER_FPU(pcb), ("unpaired fpu_kern_leave")); } return (0); } int fpu_kern_thread(u_int flags) { struct pcb *pcb; pcb = curpcb; KASSERT((curthread->td_pflags & TDP_KTHREAD) != 0, ("Only kthread may use fpu_kern_thread")); KASSERT(curpcb->pcb_save == get_pcb_user_save_pcb(curpcb), ("mangled pcb_save")); KASSERT(PCB_USER_FPU(curpcb), ("recursive call")); curpcb->pcb_flags |= PCB_KERNNPX; return (0); } int is_fpu_kern_thread(u_int flags) { if ((curthread->td_pflags & TDP_KTHREAD) == 0) return (0); return ((curpcb->pcb_flags & PCB_KERNNPX) != 0); } /* * FPU save area alloc/free/init utility routines */ union savefpu * fpu_save_area_alloc(void) { return (uma_zalloc(fpu_save_area_zone, 0)); } void fpu_save_area_free(union savefpu *fsa) { uma_zfree(fpu_save_area_zone, fsa); } void fpu_save_area_reset(union savefpu *fsa) { bcopy(npx_initialstate, fsa, cpu_max_ext_state_size); } Index: head/sys/kern/imgact_elf.c =================================================================== --- head/sys/kern/imgact_elf.c (revision 274816) +++ head/sys/kern/imgact_elf.c (revision 274817) @@ -1,2151 +1,2194 @@ /*- * Copyright (c) 2000 David O'Brien * Copyright (c) 1995-1996 Søren Schmidt * Copyright (c) 1996 Peter Wemm * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer * in this position and unchanged. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include __FBSDID("$FreeBSD$"); #include "opt_capsicum.h" #include "opt_compat.h" #include "opt_core.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define ELF_NOTE_ROUNDSIZE 4 #define OLD_EI_BRAND 8 static int __elfN(check_header)(const Elf_Ehdr *hdr); static Elf_Brandinfo *__elfN(get_brandinfo)(struct image_params *imgp, const char *interp, int interp_name_len, int32_t *osrel); static int __elfN(load_file)(struct proc *p, const char *file, u_long *addr, u_long *entry, size_t pagesize); static int __elfN(load_section)(struct image_params *imgp, vm_offset_t offset, caddr_t vmaddr, size_t memsz, size_t filsz, vm_prot_t prot, size_t pagesize); static int __CONCAT(exec_, __elfN(imgact))(struct image_params *imgp); static boolean_t __elfN(freebsd_trans_osrel)(const Elf_Note *note, int32_t *osrel); static boolean_t kfreebsd_trans_osrel(const Elf_Note *note, int32_t *osrel); static boolean_t __elfN(check_note)(struct image_params *imgp, Elf_Brandnote *checknote, int32_t *osrel); static vm_prot_t __elfN(trans_prot)(Elf_Word); static Elf_Word __elfN(untrans_prot)(vm_prot_t); SYSCTL_NODE(_kern, OID_AUTO, __CONCAT(elf, __ELF_WORD_SIZE), CTLFLAG_RW, 0, ""); #ifdef COMPRESS_USER_CORES static int compress_core(gzFile, char *, char *, unsigned int, struct thread * td); #endif #define CORE_BUF_SIZE (16 * 1024) int __elfN(fallback_brand) = -1; SYSCTL_INT(__CONCAT(_kern_elf, __ELF_WORD_SIZE), OID_AUTO, fallback_brand, CTLFLAG_RWTUN, &__elfN(fallback_brand), 0, __XSTRING(__CONCAT(ELF, __ELF_WORD_SIZE)) " brand of last resort"); static int elf_legacy_coredump = 0; SYSCTL_INT(_debug, OID_AUTO, __elfN(legacy_coredump), CTLFLAG_RW, &elf_legacy_coredump, 0, ""); int __elfN(nxstack) = #if defined(__amd64__) || defined(__powerpc64__) /* both 64 and 32 bit */ 1; #else 0; #endif SYSCTL_INT(__CONCAT(_kern_elf, __ELF_WORD_SIZE), OID_AUTO, nxstack, CTLFLAG_RW, &__elfN(nxstack), 0, __XSTRING(__CONCAT(ELF, __ELF_WORD_SIZE)) ": enable non-executable stack"); #if __ELF_WORD_SIZE == 32 #if defined(__amd64__) int i386_read_exec = 0; SYSCTL_INT(_kern_elf32, OID_AUTO, read_exec, CTLFLAG_RW, &i386_read_exec, 0, "enable execution from readable segments"); #endif #endif static Elf_Brandinfo *elf_brand_list[MAX_BRANDS]; #define trunc_page_ps(va, ps) ((va) & ~(ps - 1)) #define round_page_ps(va, ps) (((va) + (ps - 1)) & ~(ps - 1)) #define aligned(a, t) (trunc_page_ps((u_long)(a), sizeof(t)) == (u_long)(a)) static const char FREEBSD_ABI_VENDOR[] = "FreeBSD"; Elf_Brandnote __elfN(freebsd_brandnote) = { .hdr.n_namesz = sizeof(FREEBSD_ABI_VENDOR), .hdr.n_descsz = sizeof(int32_t), .hdr.n_type = 1, .vendor = FREEBSD_ABI_VENDOR, .flags = BN_TRANSLATE_OSREL, .trans_osrel = __elfN(freebsd_trans_osrel) }; static boolean_t __elfN(freebsd_trans_osrel)(const Elf_Note *note, int32_t *osrel) { uintptr_t p; p = (uintptr_t)(note + 1); p += roundup2(note->n_namesz, ELF_NOTE_ROUNDSIZE); *osrel = *(const int32_t *)(p); return (TRUE); } static const char GNU_ABI_VENDOR[] = "GNU"; static int GNU_KFREEBSD_ABI_DESC = 3; Elf_Brandnote __elfN(kfreebsd_brandnote) = { .hdr.n_namesz = sizeof(GNU_ABI_VENDOR), .hdr.n_descsz = 16, /* XXX at least 16 */ .hdr.n_type = 1, .vendor = GNU_ABI_VENDOR, .flags = BN_TRANSLATE_OSREL, .trans_osrel = kfreebsd_trans_osrel }; static boolean_t kfreebsd_trans_osrel(const Elf_Note *note, int32_t *osrel) { const Elf32_Word *desc; uintptr_t p; p = (uintptr_t)(note + 1); p += roundup2(note->n_namesz, ELF_NOTE_ROUNDSIZE); desc = (const Elf32_Word *)p; if (desc[0] != GNU_KFREEBSD_ABI_DESC) return (FALSE); /* * Debian GNU/kFreeBSD embed the earliest compatible kernel version * (__FreeBSD_version: Rxx) in the LSB way. */ *osrel = desc[1] * 100000 + desc[2] * 1000 + desc[3]; return (TRUE); } int __elfN(insert_brand_entry)(Elf_Brandinfo *entry) { int i; for (i = 0; i < MAX_BRANDS; i++) { if (elf_brand_list[i] == NULL) { elf_brand_list[i] = entry; break; } } if (i == MAX_BRANDS) { printf("WARNING: %s: could not insert brandinfo entry: %p\n", __func__, entry); return (-1); } return (0); } int __elfN(remove_brand_entry)(Elf_Brandinfo *entry) { int i; for (i = 0; i < MAX_BRANDS; i++) { if (elf_brand_list[i] == entry) { elf_brand_list[i] = NULL; break; } } if (i == MAX_BRANDS) return (-1); return (0); } int __elfN(brand_inuse)(Elf_Brandinfo *entry) { struct proc *p; int rval = FALSE; sx_slock(&allproc_lock); FOREACH_PROC_IN_SYSTEM(p) { if (p->p_sysent == entry->sysvec) { rval = TRUE; break; } } sx_sunlock(&allproc_lock); return (rval); } static Elf_Brandinfo * __elfN(get_brandinfo)(struct image_params *imgp, const char *interp, int interp_name_len, int32_t *osrel) { const Elf_Ehdr *hdr = (const Elf_Ehdr *)imgp->image_header; Elf_Brandinfo *bi; boolean_t ret; int i; /* * We support four types of branding -- (1) the ELF EI_OSABI field * that SCO added to the ELF spec, (2) FreeBSD 3.x's traditional string * branding w/in the ELF header, (3) path of the `interp_path' * field, and (4) the ".note.ABI-tag" ELF section. */ /* Look for an ".note.ABI-tag" ELF section */ for (i = 0; i < MAX_BRANDS; i++) { bi = elf_brand_list[i]; if (bi == NULL) continue; if (hdr->e_machine == bi->machine && (bi->flags & (BI_BRAND_NOTE|BI_BRAND_NOTE_MANDATORY)) != 0) { ret = __elfN(check_note)(imgp, bi->brand_note, osrel); if (ret) return (bi); } } /* If the executable has a brand, search for it in the brand list. */ for (i = 0; i < MAX_BRANDS; i++) { bi = elf_brand_list[i]; if (bi == NULL || bi->flags & BI_BRAND_NOTE_MANDATORY) continue; if (hdr->e_machine == bi->machine && (hdr->e_ident[EI_OSABI] == bi->brand || strncmp((const char *)&hdr->e_ident[OLD_EI_BRAND], bi->compat_3_brand, strlen(bi->compat_3_brand)) == 0)) return (bi); } /* No known brand, see if the header is recognized by any brand */ for (i = 0; i < MAX_BRANDS; i++) { bi = elf_brand_list[i]; if (bi == NULL || bi->flags & BI_BRAND_NOTE_MANDATORY || bi->header_supported == NULL) continue; if (hdr->e_machine == bi->machine) { ret = bi->header_supported(imgp); if (ret) return (bi); } } /* Lacking a known brand, search for a recognized interpreter. */ if (interp != NULL) { for (i = 0; i < MAX_BRANDS; i++) { bi = elf_brand_list[i]; if (bi == NULL || bi->flags & BI_BRAND_NOTE_MANDATORY) continue; if (hdr->e_machine == bi->machine && /* ELF image p_filesz includes terminating zero */ strlen(bi->interp_path) + 1 == interp_name_len && strncmp(interp, bi->interp_path, interp_name_len) == 0) return (bi); } } /* Lacking a recognized interpreter, try the default brand */ for (i = 0; i < MAX_BRANDS; i++) { bi = elf_brand_list[i]; if (bi == NULL || bi->flags & BI_BRAND_NOTE_MANDATORY) continue; if (hdr->e_machine == bi->machine && __elfN(fallback_brand) == bi->brand) return (bi); } return (NULL); } static int __elfN(check_header)(const Elf_Ehdr *hdr) { Elf_Brandinfo *bi; int i; if (!IS_ELF(*hdr) || hdr->e_ident[EI_CLASS] != ELF_TARG_CLASS || hdr->e_ident[EI_DATA] != ELF_TARG_DATA || hdr->e_ident[EI_VERSION] != EV_CURRENT || hdr->e_phentsize != sizeof(Elf_Phdr) || hdr->e_version != ELF_TARG_VER) return (ENOEXEC); /* * Make sure we have at least one brand for this machine. */ for (i = 0; i < MAX_BRANDS; i++) { bi = elf_brand_list[i]; if (bi != NULL && bi->machine == hdr->e_machine) break; } if (i == MAX_BRANDS) return (ENOEXEC); return (0); } static int __elfN(map_partial)(vm_map_t map, vm_object_t object, vm_ooffset_t offset, vm_offset_t start, vm_offset_t end, vm_prot_t prot) { struct sf_buf *sf; int error; vm_offset_t off; /* * Create the page if it doesn't exist yet. Ignore errors. */ vm_map_lock(map); vm_map_insert(map, NULL, 0, trunc_page(start), round_page(end), VM_PROT_ALL, VM_PROT_ALL, 0); vm_map_unlock(map); /* * Find the page from the underlying object. */ if (object) { sf = vm_imgact_map_page(object, offset); if (sf == NULL) return (KERN_FAILURE); off = offset - trunc_page(offset); error = copyout((caddr_t)sf_buf_kva(sf) + off, (caddr_t)start, end - start); vm_imgact_unmap_page(sf); if (error) { return (KERN_FAILURE); } } return (KERN_SUCCESS); } static int __elfN(map_insert)(vm_map_t map, vm_object_t object, vm_ooffset_t offset, vm_offset_t start, vm_offset_t end, vm_prot_t prot, int cow) { struct sf_buf *sf; vm_offset_t off; vm_size_t sz; int error, rv; if (start != trunc_page(start)) { rv = __elfN(map_partial)(map, object, offset, start, round_page(start), prot); if (rv) return (rv); offset += round_page(start) - start; start = round_page(start); } if (end != round_page(end)) { rv = __elfN(map_partial)(map, object, offset + trunc_page(end) - start, trunc_page(end), end, prot); if (rv) return (rv); end = trunc_page(end); } if (end > start) { if (offset & PAGE_MASK) { /* * The mapping is not page aligned. This means we have * to copy the data. Sigh. */ rv = vm_map_find(map, NULL, 0, &start, end - start, 0, VMFS_NO_SPACE, prot | VM_PROT_WRITE, VM_PROT_ALL, 0); if (rv) return (rv); if (object == NULL) return (KERN_SUCCESS); for (; start < end; start += sz) { sf = vm_imgact_map_page(object, offset); if (sf == NULL) return (KERN_FAILURE); off = offset - trunc_page(offset); sz = end - start; if (sz > PAGE_SIZE - off) sz = PAGE_SIZE - off; error = copyout((caddr_t)sf_buf_kva(sf) + off, (caddr_t)start, sz); vm_imgact_unmap_page(sf); if (error) { return (KERN_FAILURE); } offset += sz; } rv = KERN_SUCCESS; } else { vm_object_reference(object); vm_map_lock(map); rv = vm_map_insert(map, object, offset, start, end, prot, VM_PROT_ALL, cow); vm_map_unlock(map); if (rv != KERN_SUCCESS) vm_object_deallocate(object); } return (rv); } else { return (KERN_SUCCESS); } } static int __elfN(load_section)(struct image_params *imgp, vm_offset_t offset, caddr_t vmaddr, size_t memsz, size_t filsz, vm_prot_t prot, size_t pagesize) { struct sf_buf *sf; size_t map_len; vm_map_t map; vm_object_t object; vm_offset_t map_addr; int error, rv, cow; size_t copy_len; vm_offset_t file_addr; /* * It's necessary to fail if the filsz + offset taken from the * header is greater than the actual file pager object's size. * If we were to allow this, then the vm_map_find() below would * walk right off the end of the file object and into the ether. * * While I'm here, might as well check for something else that * is invalid: filsz cannot be greater than memsz. */ if ((off_t)filsz + offset > imgp->attr->va_size || filsz > memsz) { uprintf("elf_load_section: truncated ELF file\n"); return (ENOEXEC); } object = imgp->object; map = &imgp->proc->p_vmspace->vm_map; map_addr = trunc_page_ps((vm_offset_t)vmaddr, pagesize); file_addr = trunc_page_ps(offset, pagesize); /* * We have two choices. We can either clear the data in the last page * of an oversized mapping, or we can start the anon mapping a page * early and copy the initialized data into that first page. We * choose the second.. */ if (memsz > filsz) map_len = trunc_page_ps(offset + filsz, pagesize) - file_addr; else map_len = round_page_ps(offset + filsz, pagesize) - file_addr; if (map_len != 0) { /* cow flags: don't dump readonly sections in core */ cow = MAP_COPY_ON_WRITE | MAP_PREFAULT | (prot & VM_PROT_WRITE ? 0 : MAP_DISABLE_COREDUMP); rv = __elfN(map_insert)(map, object, file_addr, /* file offset */ map_addr, /* virtual start */ map_addr + map_len,/* virtual end */ prot, cow); if (rv != KERN_SUCCESS) return (EINVAL); /* we can stop now if we've covered it all */ if (memsz == filsz) { return (0); } } /* * We have to get the remaining bit of the file into the first part * of the oversized map segment. This is normally because the .data * segment in the file is extended to provide bss. It's a neat idea * to try and save a page, but it's a pain in the behind to implement. */ copy_len = (offset + filsz) - trunc_page_ps(offset + filsz, pagesize); map_addr = trunc_page_ps((vm_offset_t)vmaddr + filsz, pagesize); map_len = round_page_ps((vm_offset_t)vmaddr + memsz, pagesize) - map_addr; /* This had damn well better be true! */ if (map_len != 0) { rv = __elfN(map_insert)(map, NULL, 0, map_addr, map_addr + map_len, VM_PROT_ALL, 0); if (rv != KERN_SUCCESS) { return (EINVAL); } } if (copy_len != 0) { vm_offset_t off; sf = vm_imgact_map_page(object, offset + filsz); if (sf == NULL) return (EIO); /* send the page fragment to user space */ off = trunc_page_ps(offset + filsz, pagesize) - trunc_page(offset + filsz); error = copyout((caddr_t)sf_buf_kva(sf) + off, (caddr_t)map_addr, copy_len); vm_imgact_unmap_page(sf); if (error) { return (error); } } /* * set it to the specified protection. * XXX had better undo the damage from pasting over the cracks here! */ vm_map_protect(map, trunc_page(map_addr), round_page(map_addr + map_len), prot, FALSE); return (0); } /* * Load the file "file" into memory. It may be either a shared object * or an executable. * * The "addr" reference parameter is in/out. On entry, it specifies * the address where a shared object should be loaded. If the file is * an executable, this value is ignored. On exit, "addr" specifies * where the file was actually loaded. * * The "entry" reference parameter is out only. On exit, it specifies * the entry point for the loaded file. */ static int __elfN(load_file)(struct proc *p, const char *file, u_long *addr, u_long *entry, size_t pagesize) { struct { struct nameidata nd; struct vattr attr; struct image_params image_params; } *tempdata; const Elf_Ehdr *hdr = NULL; const Elf_Phdr *phdr = NULL; struct nameidata *nd; struct vattr *attr; struct image_params *imgp; vm_prot_t prot; u_long rbase; u_long base_addr = 0; int error, i, numsegs; #ifdef CAPABILITY_MODE /* * XXXJA: This check can go away once we are sufficiently confident * that the checks in namei() are correct. */ if (IN_CAPABILITY_MODE(curthread)) return (ECAPMODE); #endif tempdata = malloc(sizeof(*tempdata), M_TEMP, M_WAITOK); nd = &tempdata->nd; attr = &tempdata->attr; imgp = &tempdata->image_params; /* * Initialize part of the common data */ imgp->proc = p; imgp->attr = attr; imgp->firstpage = NULL; imgp->image_header = NULL; imgp->object = NULL; imgp->execlabel = NULL; NDINIT(nd, LOOKUP, LOCKLEAF | FOLLOW, UIO_SYSSPACE, file, curthread); if ((error = namei(nd)) != 0) { nd->ni_vp = NULL; goto fail; } NDFREE(nd, NDF_ONLY_PNBUF); imgp->vp = nd->ni_vp; /* * Check permissions, modes, uid, etc on the file, and "open" it. */ error = exec_check_permissions(imgp); if (error) goto fail; error = exec_map_first_page(imgp); if (error) goto fail; /* * Also make certain that the interpreter stays the same, so set * its VV_TEXT flag, too. */ VOP_SET_TEXT(nd->ni_vp); imgp->object = nd->ni_vp->v_object; hdr = (const Elf_Ehdr *)imgp->image_header; if ((error = __elfN(check_header)(hdr)) != 0) goto fail; if (hdr->e_type == ET_DYN) rbase = *addr; else if (hdr->e_type == ET_EXEC) rbase = 0; else { error = ENOEXEC; goto fail; } /* Only support headers that fit within first page for now */ if ((hdr->e_phoff > PAGE_SIZE) || (u_int)hdr->e_phentsize * hdr->e_phnum > PAGE_SIZE - hdr->e_phoff) { error = ENOEXEC; goto fail; } phdr = (const Elf_Phdr *)(imgp->image_header + hdr->e_phoff); if (!aligned(phdr, Elf_Addr)) { error = ENOEXEC; goto fail; } for (i = 0, numsegs = 0; i < hdr->e_phnum; i++) { if (phdr[i].p_type == PT_LOAD && phdr[i].p_memsz != 0) { /* Loadable segment */ prot = __elfN(trans_prot)(phdr[i].p_flags); error = __elfN(load_section)(imgp, phdr[i].p_offset, (caddr_t)(uintptr_t)phdr[i].p_vaddr + rbase, phdr[i].p_memsz, phdr[i].p_filesz, prot, pagesize); if (error != 0) goto fail; /* * Establish the base address if this is the * first segment. */ if (numsegs == 0) base_addr = trunc_page(phdr[i].p_vaddr + rbase); numsegs++; } } *addr = base_addr; *entry = (unsigned long)hdr->e_entry + rbase; fail: if (imgp->firstpage) exec_unmap_first_page(imgp); if (nd->ni_vp) vput(nd->ni_vp); free(tempdata, M_TEMP); return (error); } static int __CONCAT(exec_, __elfN(imgact))(struct image_params *imgp) { const Elf_Ehdr *hdr = (const Elf_Ehdr *)imgp->image_header; const Elf_Phdr *phdr; Elf_Auxargs *elf_auxargs; struct vmspace *vmspace; vm_prot_t prot; u_long text_size = 0, data_size = 0, total_size = 0; u_long text_addr = 0, data_addr = 0; u_long seg_size, seg_addr; u_long addr, baddr, et_dyn_addr, entry = 0, proghdr = 0; int32_t osrel = 0; int error = 0, i, n, interp_name_len = 0; const char *interp = NULL, *newinterp = NULL; Elf_Brandinfo *brand_info; char *path; struct sysentvec *sv; /* * Do we have a valid ELF header ? * * Only allow ET_EXEC & ET_DYN here, reject ET_DYN later * if particular brand doesn't support it. */ if (__elfN(check_header)(hdr) != 0 || (hdr->e_type != ET_EXEC && hdr->e_type != ET_DYN)) return (-1); /* * From here on down, we return an errno, not -1, as we've * detected an ELF file. */ if ((hdr->e_phoff > PAGE_SIZE) || (u_int)hdr->e_phentsize * hdr->e_phnum > PAGE_SIZE - hdr->e_phoff) { /* Only support headers in first page for now */ return (ENOEXEC); } phdr = (const Elf_Phdr *)(imgp->image_header + hdr->e_phoff); if (!aligned(phdr, Elf_Addr)) return (ENOEXEC); n = 0; baddr = 0; for (i = 0; i < hdr->e_phnum; i++) { switch (phdr[i].p_type) { case PT_LOAD: if (n == 0) baddr = phdr[i].p_vaddr; n++; break; case PT_INTERP: /* Path to interpreter */ if (phdr[i].p_filesz > MAXPATHLEN || phdr[i].p_offset > PAGE_SIZE || phdr[i].p_filesz > PAGE_SIZE - phdr[i].p_offset) return (ENOEXEC); interp = imgp->image_header + phdr[i].p_offset; interp_name_len = phdr[i].p_filesz; break; case PT_GNU_STACK: if (__elfN(nxstack)) imgp->stack_prot = __elfN(trans_prot)(phdr[i].p_flags); break; } } brand_info = __elfN(get_brandinfo)(imgp, interp, interp_name_len, &osrel); if (brand_info == NULL) { uprintf("ELF binary type \"%u\" not known.\n", hdr->e_ident[EI_OSABI]); return (ENOEXEC); } if (hdr->e_type == ET_DYN) { if ((brand_info->flags & BI_CAN_EXEC_DYN) == 0) return (ENOEXEC); /* * Honour the base load address from the dso if it is * non-zero for some reason. */ if (baddr == 0) et_dyn_addr = ET_DYN_LOAD_ADDR; else et_dyn_addr = 0; } else et_dyn_addr = 0; sv = brand_info->sysvec; if (interp != NULL && brand_info->interp_newpath != NULL) newinterp = brand_info->interp_newpath; /* * Avoid a possible deadlock if the current address space is destroyed * and that address space maps the locked vnode. In the common case, * the locked vnode's v_usecount is decremented but remains greater * than zero. Consequently, the vnode lock is not needed by vrele(). * However, in cases where the vnode lock is external, such as nullfs, * v_usecount may become zero. * * The VV_TEXT flag prevents modifications to the executable while * the vnode is unlocked. */ VOP_UNLOCK(imgp->vp, 0); error = exec_new_vmspace(imgp, sv); imgp->proc->p_sysent = sv; vn_lock(imgp->vp, LK_EXCLUSIVE | LK_RETRY); if (error) return (error); for (i = 0; i < hdr->e_phnum; i++) { switch (phdr[i].p_type) { case PT_LOAD: /* Loadable segment */ if (phdr[i].p_memsz == 0) break; prot = __elfN(trans_prot)(phdr[i].p_flags); error = __elfN(load_section)(imgp, phdr[i].p_offset, (caddr_t)(uintptr_t)phdr[i].p_vaddr + et_dyn_addr, phdr[i].p_memsz, phdr[i].p_filesz, prot, sv->sv_pagesize); if (error != 0) return (error); /* * If this segment contains the program headers, * remember their virtual address for the AT_PHDR * aux entry. Static binaries don't usually include * a PT_PHDR entry. */ if (phdr[i].p_offset == 0 && hdr->e_phoff + hdr->e_phnum * hdr->e_phentsize <= phdr[i].p_filesz) proghdr = phdr[i].p_vaddr + hdr->e_phoff + et_dyn_addr; seg_addr = trunc_page(phdr[i].p_vaddr + et_dyn_addr); seg_size = round_page(phdr[i].p_memsz + phdr[i].p_vaddr + et_dyn_addr - seg_addr); /* * Make the largest executable segment the official * text segment and all others data. * * Note that obreak() assumes that data_addr + * data_size == end of data load area, and the ELF * file format expects segments to be sorted by * address. If multiple data segments exist, the * last one will be used. */ if (phdr[i].p_flags & PF_X && text_size < seg_size) { text_size = seg_size; text_addr = seg_addr; } else { data_size = seg_size; data_addr = seg_addr; } total_size += seg_size; break; case PT_PHDR: /* Program header table info */ proghdr = phdr[i].p_vaddr + et_dyn_addr; break; default: break; } } if (data_addr == 0 && data_size == 0) { data_addr = text_addr; data_size = text_size; } entry = (u_long)hdr->e_entry + et_dyn_addr; /* * Check limits. It should be safe to check the * limits after loading the segments since we do * not actually fault in all the segments pages. */ PROC_LOCK(imgp->proc); if (data_size > lim_cur(imgp->proc, RLIMIT_DATA) || text_size > maxtsiz || total_size > lim_cur(imgp->proc, RLIMIT_VMEM) || racct_set(imgp->proc, RACCT_DATA, data_size) != 0 || racct_set(imgp->proc, RACCT_VMEM, total_size) != 0) { PROC_UNLOCK(imgp->proc); return (ENOMEM); } vmspace = imgp->proc->p_vmspace; vmspace->vm_tsize = text_size >> PAGE_SHIFT; vmspace->vm_taddr = (caddr_t)(uintptr_t)text_addr; vmspace->vm_dsize = data_size >> PAGE_SHIFT; vmspace->vm_daddr = (caddr_t)(uintptr_t)data_addr; /* * We load the dynamic linker where a userland call * to mmap(0, ...) would put it. The rationale behind this * calculation is that it leaves room for the heap to grow to * its maximum allowed size. */ addr = round_page((vm_offset_t)vmspace->vm_daddr + lim_max(imgp->proc, RLIMIT_DATA)); PROC_UNLOCK(imgp->proc); imgp->entry_addr = entry; if (interp != NULL) { int have_interp = FALSE; VOP_UNLOCK(imgp->vp, 0); if (brand_info->emul_path != NULL && brand_info->emul_path[0] != '\0') { path = malloc(MAXPATHLEN, M_TEMP, M_WAITOK); snprintf(path, MAXPATHLEN, "%s%s", brand_info->emul_path, interp); error = __elfN(load_file)(imgp->proc, path, &addr, &imgp->entry_addr, sv->sv_pagesize); free(path, M_TEMP); if (error == 0) have_interp = TRUE; } if (!have_interp && newinterp != NULL) { error = __elfN(load_file)(imgp->proc, newinterp, &addr, &imgp->entry_addr, sv->sv_pagesize); if (error == 0) have_interp = TRUE; } if (!have_interp) { error = __elfN(load_file)(imgp->proc, interp, &addr, &imgp->entry_addr, sv->sv_pagesize); } vn_lock(imgp->vp, LK_EXCLUSIVE | LK_RETRY); if (error != 0) { uprintf("ELF interpreter %s not found\n", interp); return (error); } } else addr = et_dyn_addr; /* * Construct auxargs table (used by the fixup routine) */ elf_auxargs = malloc(sizeof(Elf_Auxargs), M_TEMP, M_WAITOK); elf_auxargs->execfd = -1; elf_auxargs->phdr = proghdr; elf_auxargs->phent = hdr->e_phentsize; elf_auxargs->phnum = hdr->e_phnum; elf_auxargs->pagesz = PAGE_SIZE; elf_auxargs->base = addr; elf_auxargs->flags = 0; elf_auxargs->entry = entry; imgp->auxargs = elf_auxargs; imgp->interpreted = 0; imgp->reloc_base = addr; imgp->proc->p_osrel = osrel; return (error); } #define suword __CONCAT(suword, __ELF_WORD_SIZE) int __elfN(freebsd_fixup)(register_t **stack_base, struct image_params *imgp) { Elf_Auxargs *args = (Elf_Auxargs *)imgp->auxargs; Elf_Addr *base; Elf_Addr *pos; base = (Elf_Addr *)*stack_base; pos = base + (imgp->args->argc + imgp->args->envc + 2); if (args->execfd != -1) AUXARGS_ENTRY(pos, AT_EXECFD, args->execfd); AUXARGS_ENTRY(pos, AT_PHDR, args->phdr); AUXARGS_ENTRY(pos, AT_PHENT, args->phent); AUXARGS_ENTRY(pos, AT_PHNUM, args->phnum); AUXARGS_ENTRY(pos, AT_PAGESZ, args->pagesz); AUXARGS_ENTRY(pos, AT_FLAGS, args->flags); AUXARGS_ENTRY(pos, AT_ENTRY, args->entry); AUXARGS_ENTRY(pos, AT_BASE, args->base); if (imgp->execpathp != 0) AUXARGS_ENTRY(pos, AT_EXECPATH, imgp->execpathp); AUXARGS_ENTRY(pos, AT_OSRELDATE, osreldate); if (imgp->canary != 0) { AUXARGS_ENTRY(pos, AT_CANARY, imgp->canary); AUXARGS_ENTRY(pos, AT_CANARYLEN, imgp->canarylen); } AUXARGS_ENTRY(pos, AT_NCPUS, mp_ncpus); if (imgp->pagesizes != 0) { AUXARGS_ENTRY(pos, AT_PAGESIZES, imgp->pagesizes); AUXARGS_ENTRY(pos, AT_PAGESIZESLEN, imgp->pagesizeslen); } if (imgp->sysent->sv_timekeep_base != 0) { AUXARGS_ENTRY(pos, AT_TIMEKEEP, imgp->sysent->sv_timekeep_base); } AUXARGS_ENTRY(pos, AT_STACKPROT, imgp->sysent->sv_shared_page_obj != NULL && imgp->stack_prot != 0 ? imgp->stack_prot : imgp->sysent->sv_stackprot); AUXARGS_ENTRY(pos, AT_NULL, 0); free(imgp->auxargs, M_TEMP); imgp->auxargs = NULL; base--; suword(base, (long)imgp->args->argc); *stack_base = (register_t *)base; return (0); } /* * Code for generating ELF core dumps. */ typedef void (*segment_callback)(vm_map_entry_t, void *); /* Closure for cb_put_phdr(). */ struct phdr_closure { Elf_Phdr *phdr; /* Program header to fill in */ Elf_Off offset; /* Offset of segment in core file */ }; /* Closure for cb_size_segment(). */ struct sseg_closure { int count; /* Count of writable segments. */ size_t size; /* Total size of all writable segments. */ }; typedef void (*outfunc_t)(void *, struct sbuf *, size_t *); struct note_info { int type; /* Note type. */ outfunc_t outfunc; /* Output function. */ void *outarg; /* Argument for the output function. */ size_t outsize; /* Output size. */ TAILQ_ENTRY(note_info) link; /* Link to the next note info. */ }; TAILQ_HEAD(note_info_list, note_info); static void cb_put_phdr(vm_map_entry_t, void *); static void cb_size_segment(vm_map_entry_t, void *); static void each_writable_segment(struct thread *, segment_callback, void *); static int __elfN(corehdr)(struct thread *, struct vnode *, struct ucred *, int, void *, size_t, struct note_info_list *, size_t, gzFile); static void __elfN(prepare_notes)(struct thread *, struct note_info_list *, size_t *); static void __elfN(puthdr)(struct thread *, void *, size_t, int, size_t); static void __elfN(putnote)(struct note_info *, struct sbuf *); static size_t register_note(struct note_info_list *, int, outfunc_t, void *); static int sbuf_drain_core_output(void *, const char *, int); static int sbuf_drain_count(void *arg, const char *data, int len); static void __elfN(note_fpregset)(void *, struct sbuf *, size_t *); static void __elfN(note_prpsinfo)(void *, struct sbuf *, size_t *); static void __elfN(note_prstatus)(void *, struct sbuf *, size_t *); static void __elfN(note_threadmd)(void *, struct sbuf *, size_t *); static void __elfN(note_thrmisc)(void *, struct sbuf *, size_t *); static void __elfN(note_procstat_auxv)(void *, struct sbuf *, size_t *); static void __elfN(note_procstat_proc)(void *, struct sbuf *, size_t *); static void __elfN(note_procstat_psstrings)(void *, struct sbuf *, size_t *); static void note_procstat_files(void *, struct sbuf *, size_t *); static void note_procstat_groups(void *, struct sbuf *, size_t *); static void note_procstat_osrel(void *, struct sbuf *, size_t *); static void note_procstat_rlimit(void *, struct sbuf *, size_t *); static void note_procstat_umask(void *, struct sbuf *, size_t *); static void note_procstat_vmmap(void *, struct sbuf *, size_t *); #ifdef COMPRESS_USER_CORES extern int compress_user_cores; extern int compress_user_cores_gzlevel; #endif static int core_output(struct vnode *vp, void *base, size_t len, off_t offset, struct ucred *active_cred, struct ucred *file_cred, struct thread *td, char *core_buf, gzFile gzfile) { int error; if (gzfile) { #ifdef COMPRESS_USER_CORES error = compress_core(gzfile, base, core_buf, len, td); #else panic("shouldn't be here"); #endif } else { error = vn_rdwr_inchunks(UIO_WRITE, vp, base, len, offset, UIO_USERSPACE, IO_UNIT | IO_DIRECT | IO_RANGELOCKED, active_cred, file_cred, NULL, td); } return (error); } /* Coredump output parameters for sbuf drain routine. */ struct sbuf_drain_core_params { off_t offset; struct ucred *active_cred; struct ucred *file_cred; struct thread *td; struct vnode *vp; #ifdef COMPRESS_USER_CORES gzFile gzfile; #endif }; /* * Drain into a core file. */ static int sbuf_drain_core_output(void *arg, const char *data, int len) { struct sbuf_drain_core_params *p; int error, locked; p = (struct sbuf_drain_core_params *)arg; /* * Some kern_proc out routines that print to this sbuf may * call us with the process lock held. Draining with the * non-sleepable lock held is unsafe. The lock is needed for * those routines when dumping a live process. In our case we * can safely release the lock before draining and acquire * again after. */ locked = PROC_LOCKED(p->td->td_proc); if (locked) PROC_UNLOCK(p->td->td_proc); #ifdef COMPRESS_USER_CORES if (p->gzfile != Z_NULL) error = compress_core(p->gzfile, NULL, __DECONST(char *, data), len, p->td); else #endif error = vn_rdwr_inchunks(UIO_WRITE, p->vp, __DECONST(void *, data), len, p->offset, UIO_SYSSPACE, IO_UNIT | IO_DIRECT | IO_RANGELOCKED, p->active_cred, p->file_cred, NULL, p->td); if (locked) PROC_LOCK(p->td->td_proc); if (error != 0) return (-error); p->offset += len; return (len); } /* * Drain into a counter. */ static int sbuf_drain_count(void *arg, const char *data __unused, int len) { size_t *sizep; sizep = (size_t *)arg; *sizep += len; return (len); } int __elfN(coredump)(struct thread *td, struct vnode *vp, off_t limit, int flags) { struct ucred *cred = td->td_ucred; int error = 0; struct sseg_closure seginfo; struct note_info_list notelst; struct note_info *ninfo; void *hdr; size_t hdrsize, notesz, coresize; gzFile gzfile = Z_NULL; char *core_buf = NULL; #ifdef COMPRESS_USER_CORES char gzopen_flags[8]; char *p; int doing_compress = flags & IMGACT_CORE_COMPRESS; #endif hdr = NULL; TAILQ_INIT(¬elst); #ifdef COMPRESS_USER_CORES if (doing_compress) { p = gzopen_flags; *p++ = 'w'; if (compress_user_cores_gzlevel >= 0 && compress_user_cores_gzlevel <= 9) *p++ = '0' + compress_user_cores_gzlevel; *p = 0; gzfile = gz_open("", gzopen_flags, vp); if (gzfile == Z_NULL) { error = EFAULT; goto done; } core_buf = malloc(CORE_BUF_SIZE, M_TEMP, M_WAITOK | M_ZERO); if (!core_buf) { error = ENOMEM; goto done; } } #endif /* Size the program segments. */ seginfo.count = 0; seginfo.size = 0; each_writable_segment(td, cb_size_segment, &seginfo); /* * Collect info about the core file header area. */ hdrsize = sizeof(Elf_Ehdr) + sizeof(Elf_Phdr) * (1 + seginfo.count); __elfN(prepare_notes)(td, ¬elst, ¬esz); coresize = round_page(hdrsize + notesz) + seginfo.size; #ifdef RACCT PROC_LOCK(td->td_proc); error = racct_add(td->td_proc, RACCT_CORE, coresize); PROC_UNLOCK(td->td_proc); if (error != 0) { error = EFAULT; goto done; } #endif if (coresize >= limit) { error = EFAULT; goto done; } /* * Allocate memory for building the header, fill it up, * and write it out following the notes. */ hdr = malloc(hdrsize, M_TEMP, M_WAITOK); if (hdr == NULL) { error = EINVAL; goto done; } error = __elfN(corehdr)(td, vp, cred, seginfo.count, hdr, hdrsize, ¬elst, notesz, gzfile); /* Write the contents of all of the writable segments. */ if (error == 0) { Elf_Phdr *php; off_t offset; int i; php = (Elf_Phdr *)((char *)hdr + sizeof(Elf_Ehdr)) + 1; offset = round_page(hdrsize + notesz); for (i = 0; i < seginfo.count; i++) { error = core_output(vp, (caddr_t)(uintptr_t)php->p_vaddr, php->p_filesz, offset, cred, NOCRED, curthread, core_buf, gzfile); if (error != 0) break; offset += php->p_filesz; php++; } } if (error) { log(LOG_WARNING, "Failed to write core file for process %s (error %d)\n", curproc->p_comm, error); } done: #ifdef COMPRESS_USER_CORES if (core_buf) free(core_buf, M_TEMP); if (gzfile) gzclose(gzfile); #endif while ((ninfo = TAILQ_FIRST(¬elst)) != NULL) { TAILQ_REMOVE(¬elst, ninfo, link); free(ninfo, M_TEMP); } if (hdr != NULL) free(hdr, M_TEMP); return (error); } /* * A callback for each_writable_segment() to write out the segment's * program header entry. */ static void cb_put_phdr(entry, closure) vm_map_entry_t entry; void *closure; { struct phdr_closure *phc = (struct phdr_closure *)closure; Elf_Phdr *phdr = phc->phdr; phc->offset = round_page(phc->offset); phdr->p_type = PT_LOAD; phdr->p_offset = phc->offset; phdr->p_vaddr = entry->start; phdr->p_paddr = 0; phdr->p_filesz = phdr->p_memsz = entry->end - entry->start; phdr->p_align = PAGE_SIZE; phdr->p_flags = __elfN(untrans_prot)(entry->protection); phc->offset += phdr->p_filesz; phc->phdr++; } /* * A callback for each_writable_segment() to gather information about * the number of segments and their total size. */ static void cb_size_segment(entry, closure) vm_map_entry_t entry; void *closure; { struct sseg_closure *ssc = (struct sseg_closure *)closure; ssc->count++; ssc->size += entry->end - entry->start; } /* * For each writable segment in the process's memory map, call the given * function with a pointer to the map entry and some arbitrary * caller-supplied data. */ static void each_writable_segment(td, func, closure) struct thread *td; segment_callback func; void *closure; { struct proc *p = td->td_proc; vm_map_t map = &p->p_vmspace->vm_map; vm_map_entry_t entry; vm_object_t backing_object, object; boolean_t ignore_entry; vm_map_lock_read(map); for (entry = map->header.next; entry != &map->header; entry = entry->next) { /* * Don't dump inaccessible mappings, deal with legacy * coredump mode. * * Note that read-only segments related to the elf binary * are marked MAP_ENTRY_NOCOREDUMP now so we no longer * need to arbitrarily ignore such segments. */ if (elf_legacy_coredump) { if ((entry->protection & VM_PROT_RW) != VM_PROT_RW) continue; } else { if ((entry->protection & VM_PROT_ALL) == 0) continue; } /* * Dont include memory segment in the coredump if * MAP_NOCORE is set in mmap(2) or MADV_NOCORE in * madvise(2). Do not dump submaps (i.e. parts of the * kernel map). */ if (entry->eflags & (MAP_ENTRY_NOCOREDUMP|MAP_ENTRY_IS_SUB_MAP)) continue; if ((object = entry->object.vm_object) == NULL) continue; /* Ignore memory-mapped devices and such things. */ VM_OBJECT_RLOCK(object); while ((backing_object = object->backing_object) != NULL) { VM_OBJECT_RLOCK(backing_object); VM_OBJECT_RUNLOCK(object); object = backing_object; } ignore_entry = object->type != OBJT_DEFAULT && object->type != OBJT_SWAP && object->type != OBJT_VNODE; VM_OBJECT_RUNLOCK(object); if (ignore_entry) continue; (*func)(entry, closure); } vm_map_unlock_read(map); } /* * Write the core file header to the file, including padding up to * the page boundary. */ static int __elfN(corehdr)(struct thread *td, struct vnode *vp, struct ucred *cred, int numsegs, void *hdr, size_t hdrsize, struct note_info_list *notelst, size_t notesz, gzFile gzfile) { struct sbuf_drain_core_params params; struct note_info *ninfo; struct sbuf *sb; int error; /* Fill in the header. */ bzero(hdr, hdrsize); __elfN(puthdr)(td, hdr, hdrsize, numsegs, notesz); params.offset = 0; params.active_cred = cred; params.file_cred = NOCRED; params.td = td; params.vp = vp; #ifdef COMPRESS_USER_CORES params.gzfile = gzfile; #endif sb = sbuf_new(NULL, NULL, CORE_BUF_SIZE, SBUF_FIXEDLEN); sbuf_set_drain(sb, sbuf_drain_core_output, ¶ms); sbuf_start_section(sb, NULL); sbuf_bcat(sb, hdr, hdrsize); TAILQ_FOREACH(ninfo, notelst, link) __elfN(putnote)(ninfo, sb); /* Align up to a page boundary for the program segments. */ sbuf_end_section(sb, -1, PAGE_SIZE, 0); error = sbuf_finish(sb); sbuf_delete(sb); return (error); } static void __elfN(prepare_notes)(struct thread *td, struct note_info_list *list, size_t *sizep) { struct proc *p; struct thread *thr; size_t size; p = td->td_proc; size = 0; size += register_note(list, NT_PRPSINFO, __elfN(note_prpsinfo), p); /* * To have the debugger select the right thread (LWP) as the initial * thread, we dump the state of the thread passed to us in td first. * This is the thread that causes the core dump and thus likely to * be the right thread one wants to have selected in the debugger. */ thr = td; while (thr != NULL) { size += register_note(list, NT_PRSTATUS, __elfN(note_prstatus), thr); size += register_note(list, NT_FPREGSET, __elfN(note_fpregset), thr); size += register_note(list, NT_THRMISC, __elfN(note_thrmisc), thr); size += register_note(list, -1, __elfN(note_threadmd), thr); thr = (thr == td) ? TAILQ_FIRST(&p->p_threads) : TAILQ_NEXT(thr, td_plist); if (thr == td) thr = TAILQ_NEXT(thr, td_plist); } size += register_note(list, NT_PROCSTAT_PROC, __elfN(note_procstat_proc), p); size += register_note(list, NT_PROCSTAT_FILES, note_procstat_files, p); size += register_note(list, NT_PROCSTAT_VMMAP, note_procstat_vmmap, p); size += register_note(list, NT_PROCSTAT_GROUPS, note_procstat_groups, p); size += register_note(list, NT_PROCSTAT_UMASK, note_procstat_umask, p); size += register_note(list, NT_PROCSTAT_RLIMIT, note_procstat_rlimit, p); size += register_note(list, NT_PROCSTAT_OSREL, note_procstat_osrel, p); size += register_note(list, NT_PROCSTAT_PSSTRINGS, __elfN(note_procstat_psstrings), p); size += register_note(list, NT_PROCSTAT_AUXV, __elfN(note_procstat_auxv), p); *sizep = size; } static void __elfN(puthdr)(struct thread *td, void *hdr, size_t hdrsize, int numsegs, size_t notesz) { Elf_Ehdr *ehdr; Elf_Phdr *phdr; struct phdr_closure phc; ehdr = (Elf_Ehdr *)hdr; phdr = (Elf_Phdr *)((char *)hdr + sizeof(Elf_Ehdr)); ehdr->e_ident[EI_MAG0] = ELFMAG0; ehdr->e_ident[EI_MAG1] = ELFMAG1; ehdr->e_ident[EI_MAG2] = ELFMAG2; ehdr->e_ident[EI_MAG3] = ELFMAG3; ehdr->e_ident[EI_CLASS] = ELF_CLASS; ehdr->e_ident[EI_DATA] = ELF_DATA; ehdr->e_ident[EI_VERSION] = EV_CURRENT; ehdr->e_ident[EI_OSABI] = ELFOSABI_FREEBSD; ehdr->e_ident[EI_ABIVERSION] = 0; ehdr->e_ident[EI_PAD] = 0; ehdr->e_type = ET_CORE; #if defined(COMPAT_FREEBSD32) && __ELF_WORD_SIZE == 32 ehdr->e_machine = ELF_ARCH32; #else ehdr->e_machine = ELF_ARCH; #endif ehdr->e_version = EV_CURRENT; ehdr->e_entry = 0; ehdr->e_phoff = sizeof(Elf_Ehdr); ehdr->e_flags = 0; ehdr->e_ehsize = sizeof(Elf_Ehdr); ehdr->e_phentsize = sizeof(Elf_Phdr); ehdr->e_phnum = numsegs + 1; ehdr->e_shentsize = sizeof(Elf_Shdr); ehdr->e_shnum = 0; ehdr->e_shstrndx = SHN_UNDEF; /* * Fill in the program header entries. */ /* The note segement. */ phdr->p_type = PT_NOTE; phdr->p_offset = hdrsize; phdr->p_vaddr = 0; phdr->p_paddr = 0; phdr->p_filesz = notesz; phdr->p_memsz = 0; phdr->p_flags = PF_R; phdr->p_align = ELF_NOTE_ROUNDSIZE; phdr++; /* All the writable segments from the program. */ phc.phdr = phdr; phc.offset = round_page(hdrsize + notesz); each_writable_segment(td, cb_put_phdr, &phc); } static size_t register_note(struct note_info_list *list, int type, outfunc_t out, void *arg) { struct note_info *ninfo; size_t size, notesize; size = 0; out(arg, NULL, &size); ninfo = malloc(sizeof(*ninfo), M_TEMP, M_ZERO | M_WAITOK); ninfo->type = type; ninfo->outfunc = out; ninfo->outarg = arg; ninfo->outsize = size; TAILQ_INSERT_TAIL(list, ninfo, link); if (type == -1) return (size); notesize = sizeof(Elf_Note) + /* note header */ - roundup2(8, ELF_NOTE_ROUNDSIZE) + /* note name ("FreeBSD") */ + roundup2(sizeof(FREEBSD_ABI_VENDOR), ELF_NOTE_ROUNDSIZE) + + /* note name */ roundup2(size, ELF_NOTE_ROUNDSIZE); /* note description */ return (notesize); } +static size_t +append_note_data(const void *src, void *dst, size_t len) +{ + size_t padded_len; + + padded_len = roundup2(len, ELF_NOTE_ROUNDSIZE); + if (dst != NULL) { + bcopy(src, dst, len); + bzero((char *)dst + len, padded_len - len); + } + return (padded_len); +} + +size_t +__elfN(populate_note)(int type, void *src, void *dst, size_t size, void **descp) +{ + Elf_Note *note; + char *buf; + size_t notesize; + + buf = dst; + if (buf != NULL) { + note = (Elf_Note *)buf; + note->n_namesz = sizeof(FREEBSD_ABI_VENDOR); + note->n_descsz = size; + note->n_type = type; + buf += sizeof(*note); + buf += append_note_data(FREEBSD_ABI_VENDOR, buf, + sizeof(FREEBSD_ABI_VENDOR)); + append_note_data(src, buf, size); + if (descp != NULL) + *descp = buf; + } + + notesize = sizeof(Elf_Note) + /* note header */ + roundup2(sizeof(FREEBSD_ABI_VENDOR), ELF_NOTE_ROUNDSIZE) + + /* note name */ + roundup2(size, ELF_NOTE_ROUNDSIZE); /* note description */ + + return (notesize); +} + static void __elfN(putnote)(struct note_info *ninfo, struct sbuf *sb) { Elf_Note note; ssize_t old_len; if (ninfo->type == -1) { ninfo->outfunc(ninfo->outarg, sb, &ninfo->outsize); return; } - note.n_namesz = 8; /* strlen("FreeBSD") + 1 */ + note.n_namesz = sizeof(FREEBSD_ABI_VENDOR); note.n_descsz = ninfo->outsize; note.n_type = ninfo->type; sbuf_bcat(sb, ¬e, sizeof(note)); sbuf_start_section(sb, &old_len); - sbuf_bcat(sb, "FreeBSD", note.n_namesz); + sbuf_bcat(sb, FREEBSD_ABI_VENDOR, sizeof(FREEBSD_ABI_VENDOR)); sbuf_end_section(sb, old_len, ELF_NOTE_ROUNDSIZE, 0); if (note.n_descsz == 0) return; sbuf_start_section(sb, &old_len); ninfo->outfunc(ninfo->outarg, sb, &ninfo->outsize); sbuf_end_section(sb, old_len, ELF_NOTE_ROUNDSIZE, 0); } /* * Miscellaneous note out functions. */ #if defined(COMPAT_FREEBSD32) && __ELF_WORD_SIZE == 32 #include typedef struct prstatus32 elf_prstatus_t; typedef struct prpsinfo32 elf_prpsinfo_t; typedef struct fpreg32 elf_prfpregset_t; typedef struct fpreg32 elf_fpregset_t; typedef struct reg32 elf_gregset_t; typedef struct thrmisc32 elf_thrmisc_t; #define ELF_KERN_PROC_MASK KERN_PROC_MASK32 typedef struct kinfo_proc32 elf_kinfo_proc_t; typedef uint32_t elf_ps_strings_t; #else typedef prstatus_t elf_prstatus_t; typedef prpsinfo_t elf_prpsinfo_t; typedef prfpregset_t elf_prfpregset_t; typedef prfpregset_t elf_fpregset_t; typedef gregset_t elf_gregset_t; typedef thrmisc_t elf_thrmisc_t; #define ELF_KERN_PROC_MASK 0 typedef struct kinfo_proc elf_kinfo_proc_t; typedef vm_offset_t elf_ps_strings_t; #endif static void __elfN(note_prpsinfo)(void *arg, struct sbuf *sb, size_t *sizep) { struct proc *p; elf_prpsinfo_t *psinfo; p = (struct proc *)arg; if (sb != NULL) { KASSERT(*sizep == sizeof(*psinfo), ("invalid size")); psinfo = malloc(sizeof(*psinfo), M_TEMP, M_ZERO | M_WAITOK); psinfo->pr_version = PRPSINFO_VERSION; psinfo->pr_psinfosz = sizeof(elf_prpsinfo_t); strlcpy(psinfo->pr_fname, p->p_comm, sizeof(psinfo->pr_fname)); /* * XXX - We don't fill in the command line arguments properly * yet. */ strlcpy(psinfo->pr_psargs, p->p_comm, sizeof(psinfo->pr_psargs)); sbuf_bcat(sb, psinfo, sizeof(*psinfo)); free(psinfo, M_TEMP); } *sizep = sizeof(*psinfo); } static void __elfN(note_prstatus)(void *arg, struct sbuf *sb, size_t *sizep) { struct thread *td; elf_prstatus_t *status; td = (struct thread *)arg; if (sb != NULL) { KASSERT(*sizep == sizeof(*status), ("invalid size")); status = malloc(sizeof(*status), M_TEMP, M_ZERO | M_WAITOK); status->pr_version = PRSTATUS_VERSION; status->pr_statussz = sizeof(elf_prstatus_t); status->pr_gregsetsz = sizeof(elf_gregset_t); status->pr_fpregsetsz = sizeof(elf_fpregset_t); status->pr_osreldate = osreldate; status->pr_cursig = td->td_proc->p_sig; status->pr_pid = td->td_tid; #if defined(COMPAT_FREEBSD32) && __ELF_WORD_SIZE == 32 fill_regs32(td, &status->pr_reg); #else fill_regs(td, &status->pr_reg); #endif sbuf_bcat(sb, status, sizeof(*status)); free(status, M_TEMP); } *sizep = sizeof(*status); } static void __elfN(note_fpregset)(void *arg, struct sbuf *sb, size_t *sizep) { struct thread *td; elf_prfpregset_t *fpregset; td = (struct thread *)arg; if (sb != NULL) { KASSERT(*sizep == sizeof(*fpregset), ("invalid size")); fpregset = malloc(sizeof(*fpregset), M_TEMP, M_ZERO | M_WAITOK); #if defined(COMPAT_FREEBSD32) && __ELF_WORD_SIZE == 32 fill_fpregs32(td, fpregset); #else fill_fpregs(td, fpregset); #endif sbuf_bcat(sb, fpregset, sizeof(*fpregset)); free(fpregset, M_TEMP); } *sizep = sizeof(*fpregset); } static void __elfN(note_thrmisc)(void *arg, struct sbuf *sb, size_t *sizep) { struct thread *td; elf_thrmisc_t thrmisc; td = (struct thread *)arg; if (sb != NULL) { KASSERT(*sizep == sizeof(thrmisc), ("invalid size")); bzero(&thrmisc._pad, sizeof(thrmisc._pad)); strcpy(thrmisc.pr_tname, td->td_name); sbuf_bcat(sb, &thrmisc, sizeof(thrmisc)); } *sizep = sizeof(thrmisc); } /* * Allow for MD specific notes, as well as any MD * specific preparations for writing MI notes. */ static void __elfN(note_threadmd)(void *arg, struct sbuf *sb, size_t *sizep) { struct thread *td; void *buf; size_t size; td = (struct thread *)arg; size = *sizep; if (size != 0 && sb != NULL) buf = malloc(size, M_TEMP, M_ZERO | M_WAITOK); else buf = NULL; size = 0; __elfN(dump_thread)(td, buf, &size); KASSERT(*sizep == size, ("invalid size")); if (size != 0 && sb != NULL) sbuf_bcat(sb, buf, size); free(buf, M_TEMP); *sizep = size; } #ifdef KINFO_PROC_SIZE CTASSERT(sizeof(struct kinfo_proc) == KINFO_PROC_SIZE); #endif static void __elfN(note_procstat_proc)(void *arg, struct sbuf *sb, size_t *sizep) { struct proc *p; size_t size; int structsize; p = (struct proc *)arg; size = sizeof(structsize) + p->p_numthreads * sizeof(elf_kinfo_proc_t); if (sb != NULL) { KASSERT(*sizep == size, ("invalid size")); structsize = sizeof(elf_kinfo_proc_t); sbuf_bcat(sb, &structsize, sizeof(structsize)); sx_slock(&proctree_lock); PROC_LOCK(p); kern_proc_out(p, sb, ELF_KERN_PROC_MASK); sx_sunlock(&proctree_lock); } *sizep = size; } #ifdef KINFO_FILE_SIZE CTASSERT(sizeof(struct kinfo_file) == KINFO_FILE_SIZE); #endif static void note_procstat_files(void *arg, struct sbuf *sb, size_t *sizep) { struct proc *p; size_t size; int structsize; p = (struct proc *)arg; if (sb == NULL) { size = 0; sb = sbuf_new(NULL, NULL, 128, SBUF_FIXEDLEN); sbuf_set_drain(sb, sbuf_drain_count, &size); sbuf_bcat(sb, &structsize, sizeof(structsize)); PROC_LOCK(p); kern_proc_filedesc_out(p, sb, -1); sbuf_finish(sb); sbuf_delete(sb); *sizep = size; } else { structsize = sizeof(struct kinfo_file); sbuf_bcat(sb, &structsize, sizeof(structsize)); PROC_LOCK(p); kern_proc_filedesc_out(p, sb, -1); } } #ifdef KINFO_VMENTRY_SIZE CTASSERT(sizeof(struct kinfo_vmentry) == KINFO_VMENTRY_SIZE); #endif static void note_procstat_vmmap(void *arg, struct sbuf *sb, size_t *sizep) { struct proc *p; size_t size; int structsize; p = (struct proc *)arg; if (sb == NULL) { size = 0; sb = sbuf_new(NULL, NULL, 128, SBUF_FIXEDLEN); sbuf_set_drain(sb, sbuf_drain_count, &size); sbuf_bcat(sb, &structsize, sizeof(structsize)); PROC_LOCK(p); kern_proc_vmmap_out(p, sb); sbuf_finish(sb); sbuf_delete(sb); *sizep = size; } else { structsize = sizeof(struct kinfo_vmentry); sbuf_bcat(sb, &structsize, sizeof(structsize)); PROC_LOCK(p); kern_proc_vmmap_out(p, sb); } } static void note_procstat_groups(void *arg, struct sbuf *sb, size_t *sizep) { struct proc *p; size_t size; int structsize; p = (struct proc *)arg; size = sizeof(structsize) + p->p_ucred->cr_ngroups * sizeof(gid_t); if (sb != NULL) { KASSERT(*sizep == size, ("invalid size")); structsize = sizeof(gid_t); sbuf_bcat(sb, &structsize, sizeof(structsize)); sbuf_bcat(sb, p->p_ucred->cr_groups, p->p_ucred->cr_ngroups * sizeof(gid_t)); } *sizep = size; } static void note_procstat_umask(void *arg, struct sbuf *sb, size_t *sizep) { struct proc *p; size_t size; int structsize; p = (struct proc *)arg; size = sizeof(structsize) + sizeof(p->p_fd->fd_cmask); if (sb != NULL) { KASSERT(*sizep == size, ("invalid size")); structsize = sizeof(p->p_fd->fd_cmask); sbuf_bcat(sb, &structsize, sizeof(structsize)); sbuf_bcat(sb, &p->p_fd->fd_cmask, sizeof(p->p_fd->fd_cmask)); } *sizep = size; } static void note_procstat_rlimit(void *arg, struct sbuf *sb, size_t *sizep) { struct proc *p; struct rlimit rlim[RLIM_NLIMITS]; size_t size; int structsize, i; p = (struct proc *)arg; size = sizeof(structsize) + sizeof(rlim); if (sb != NULL) { KASSERT(*sizep == size, ("invalid size")); structsize = sizeof(rlim); sbuf_bcat(sb, &structsize, sizeof(structsize)); PROC_LOCK(p); for (i = 0; i < RLIM_NLIMITS; i++) lim_rlimit(p, i, &rlim[i]); PROC_UNLOCK(p); sbuf_bcat(sb, rlim, sizeof(rlim)); } *sizep = size; } static void note_procstat_osrel(void *arg, struct sbuf *sb, size_t *sizep) { struct proc *p; size_t size; int structsize; p = (struct proc *)arg; size = sizeof(structsize) + sizeof(p->p_osrel); if (sb != NULL) { KASSERT(*sizep == size, ("invalid size")); structsize = sizeof(p->p_osrel); sbuf_bcat(sb, &structsize, sizeof(structsize)); sbuf_bcat(sb, &p->p_osrel, sizeof(p->p_osrel)); } *sizep = size; } static void __elfN(note_procstat_psstrings)(void *arg, struct sbuf *sb, size_t *sizep) { struct proc *p; elf_ps_strings_t ps_strings; size_t size; int structsize; p = (struct proc *)arg; size = sizeof(structsize) + sizeof(ps_strings); if (sb != NULL) { KASSERT(*sizep == size, ("invalid size")); structsize = sizeof(ps_strings); #if defined(COMPAT_FREEBSD32) && __ELF_WORD_SIZE == 32 ps_strings = PTROUT(p->p_sysent->sv_psstrings); #else ps_strings = p->p_sysent->sv_psstrings; #endif sbuf_bcat(sb, &structsize, sizeof(structsize)); sbuf_bcat(sb, &ps_strings, sizeof(ps_strings)); } *sizep = size; } static void __elfN(note_procstat_auxv)(void *arg, struct sbuf *sb, size_t *sizep) { struct proc *p; size_t size; int structsize; p = (struct proc *)arg; if (sb == NULL) { size = 0; sb = sbuf_new(NULL, NULL, 128, SBUF_FIXEDLEN); sbuf_set_drain(sb, sbuf_drain_count, &size); sbuf_bcat(sb, &structsize, sizeof(structsize)); PHOLD(p); proc_getauxv(curthread, p, sb); PRELE(p); sbuf_finish(sb); sbuf_delete(sb); *sizep = size; } else { structsize = sizeof(Elf_Auxinfo); sbuf_bcat(sb, &structsize, sizeof(structsize)); PHOLD(p); proc_getauxv(curthread, p, sb); PRELE(p); } } static boolean_t __elfN(parse_notes)(struct image_params *imgp, Elf_Brandnote *checknote, int32_t *osrel, const Elf_Phdr *pnote) { const Elf_Note *note, *note0, *note_end; const char *note_name; int i; if (pnote == NULL || pnote->p_offset > PAGE_SIZE || pnote->p_filesz > PAGE_SIZE - pnote->p_offset) return (FALSE); note = note0 = (const Elf_Note *)(imgp->image_header + pnote->p_offset); note_end = (const Elf_Note *)(imgp->image_header + pnote->p_offset + pnote->p_filesz); for (i = 0; i < 100 && note >= note0 && note < note_end; i++) { if (!aligned(note, Elf32_Addr) || (const char *)note_end - (const char *)note < sizeof(Elf_Note)) return (FALSE); if (note->n_namesz != checknote->hdr.n_namesz || note->n_descsz != checknote->hdr.n_descsz || note->n_type != checknote->hdr.n_type) goto nextnote; note_name = (const char *)(note + 1); if (note_name + checknote->hdr.n_namesz >= (const char *)note_end || strncmp(checknote->vendor, note_name, checknote->hdr.n_namesz) != 0) goto nextnote; /* * Fetch the osreldate for binary * from the ELF OSABI-note if necessary. */ if ((checknote->flags & BN_TRANSLATE_OSREL) != 0 && checknote->trans_osrel != NULL) return (checknote->trans_osrel(note, osrel)); return (TRUE); nextnote: note = (const Elf_Note *)((const char *)(note + 1) + roundup2(note->n_namesz, ELF_NOTE_ROUNDSIZE) + roundup2(note->n_descsz, ELF_NOTE_ROUNDSIZE)); } return (FALSE); } /* * Try to find the appropriate ABI-note section for checknote, * fetch the osreldate for binary from the ELF OSABI-note. Only the * first page of the image is searched, the same as for headers. */ static boolean_t __elfN(check_note)(struct image_params *imgp, Elf_Brandnote *checknote, int32_t *osrel) { const Elf_Phdr *phdr; const Elf_Ehdr *hdr; int i; hdr = (const Elf_Ehdr *)imgp->image_header; phdr = (const Elf_Phdr *)(imgp->image_header + hdr->e_phoff); for (i = 0; i < hdr->e_phnum; i++) { if (phdr[i].p_type == PT_NOTE && __elfN(parse_notes)(imgp, checknote, osrel, &phdr[i])) return (TRUE); } return (FALSE); } /* * Tell kern_execve.c about it, with a little help from the linker. */ static struct execsw __elfN(execsw) = { __CONCAT(exec_, __elfN(imgact)), __XSTRING(__CONCAT(ELF, __ELF_WORD_SIZE)) }; EXEC_SET(__CONCAT(elf, __ELF_WORD_SIZE), __elfN(execsw)); #ifdef COMPRESS_USER_CORES /* * Compress and write out a core segment for a user process. * * 'inbuf' is the starting address of a VM segment in the process' address * space that is to be compressed and written out to the core file. 'dest_buf' * is a buffer in the kernel's address space. The segment is copied from * 'inbuf' to 'dest_buf' first before being processed by the compression * routine gzwrite(). This copying is necessary because the content of the VM * segment may change between the compression pass and the crc-computation pass * in gzwrite(). This is because realtime threads may preempt the UNIX kernel. * * If inbuf is NULL it is assumed that data is already copied to 'dest_buf'. */ static int compress_core (gzFile file, char *inbuf, char *dest_buf, unsigned int len, struct thread *td) { int len_compressed; int error = 0; unsigned int chunk_len; while (len) { if (inbuf != NULL) { chunk_len = (len > CORE_BUF_SIZE) ? CORE_BUF_SIZE : len; copyin(inbuf, dest_buf, chunk_len); inbuf += chunk_len; } else { chunk_len = len; } len_compressed = gzwrite(file, dest_buf, chunk_len); EVENTHANDLER_INVOKE(app_coredump_progress, td, len_compressed); if ((unsigned int)len_compressed != chunk_len) { log(LOG_WARNING, "compress_core: length mismatch (0x%x returned, " "0x%x expected)\n", len_compressed, chunk_len); EVENTHANDLER_INVOKE(app_coredump_error, td, "compress_core: length mismatch %x -> %x", chunk_len, len_compressed); error = EFAULT; break; } len -= chunk_len; maybe_yield(); } return (error); } #endif /* COMPRESS_USER_CORES */ static vm_prot_t __elfN(trans_prot)(Elf_Word flags) { vm_prot_t prot; prot = 0; if (flags & PF_X) prot |= VM_PROT_EXECUTE; if (flags & PF_W) prot |= VM_PROT_WRITE; if (flags & PF_R) prot |= VM_PROT_READ; #if __ELF_WORD_SIZE == 32 #if defined(__amd64__) if (i386_read_exec && (flags & PF_R)) prot |= VM_PROT_EXECUTE; #endif #endif return (prot); } static Elf_Word __elfN(untrans_prot)(vm_prot_t prot) { Elf_Word flags; flags = 0; if (prot & VM_PROT_EXECUTE) flags |= PF_X; if (prot & VM_PROT_READ) flags |= PF_R; if (prot & VM_PROT_WRITE) flags |= PF_W; return (flags); } Index: head/sys/sys/elf_common.h =================================================================== --- head/sys/sys/elf_common.h (revision 274816) +++ head/sys/sys/elf_common.h (revision 274817) @@ -1,999 +1,1000 @@ /*- * Copyright (c) 2000, 2001, 2008, 2011, David E. O'Brien * Copyright (c) 1998 John D. Polstra. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $FreeBSD$ */ #ifndef _SYS_ELF_COMMON_H_ #define _SYS_ELF_COMMON_H_ 1 /* * ELF definitions that are independent of architecture or word size. */ /* * Note header. The ".note" section contains an array of notes. Each * begins with this header, aligned to a word boundary. Immediately * following the note header is n_namesz bytes of name, padded to the * next word boundary. Then comes n_descsz bytes of descriptor, again * padded to a word boundary. The values of n_namesz and n_descsz do * not include the padding. */ typedef struct { u_int32_t n_namesz; /* Length of name. */ u_int32_t n_descsz; /* Length of descriptor. */ u_int32_t n_type; /* Type of this note. */ } Elf_Note; /* * The header for GNU-style hash sections. */ typedef struct { u_int32_t gh_nbuckets; /* Number of hash buckets. */ u_int32_t gh_symndx; /* First visible symbol in .dynsym. */ u_int32_t gh_maskwords; /* #maskwords used in bloom filter. */ u_int32_t gh_shift2; /* Bloom filter shift count. */ } Elf_GNU_Hash_Header; /* Indexes into the e_ident array. Keep synced with http://www.sco.com/developers/gabi/latest/ch4.eheader.html */ #define EI_MAG0 0 /* Magic number, byte 0. */ #define EI_MAG1 1 /* Magic number, byte 1. */ #define EI_MAG2 2 /* Magic number, byte 2. */ #define EI_MAG3 3 /* Magic number, byte 3. */ #define EI_CLASS 4 /* Class of machine. */ #define EI_DATA 5 /* Data format. */ #define EI_VERSION 6 /* ELF format version. */ #define EI_OSABI 7 /* Operating system / ABI identification */ #define EI_ABIVERSION 8 /* ABI version */ #define OLD_EI_BRAND 8 /* Start of architecture identification. */ #define EI_PAD 9 /* Start of padding (per SVR4 ABI). */ #define EI_NIDENT 16 /* Size of e_ident array. */ /* Values for the magic number bytes. */ #define ELFMAG0 0x7f #define ELFMAG1 'E' #define ELFMAG2 'L' #define ELFMAG3 'F' #define ELFMAG "\177ELF" /* magic string */ #define SELFMAG 4 /* magic string size */ /* Values for e_ident[EI_VERSION] and e_version. */ #define EV_NONE 0 #define EV_CURRENT 1 /* Values for e_ident[EI_CLASS]. */ #define ELFCLASSNONE 0 /* Unknown class. */ #define ELFCLASS32 1 /* 32-bit architecture. */ #define ELFCLASS64 2 /* 64-bit architecture. */ /* Values for e_ident[EI_DATA]. */ #define ELFDATANONE 0 /* Unknown data format. */ #define ELFDATA2LSB 1 /* 2's complement little-endian. */ #define ELFDATA2MSB 2 /* 2's complement big-endian. */ /* Values for e_ident[EI_OSABI]. */ #define ELFOSABI_NONE 0 /* UNIX System V ABI */ #define ELFOSABI_HPUX 1 /* HP-UX operating system */ #define ELFOSABI_NETBSD 2 /* NetBSD */ #define ELFOSABI_LINUX 3 /* GNU/Linux */ #define ELFOSABI_HURD 4 /* GNU/Hurd */ #define ELFOSABI_86OPEN 5 /* 86Open common IA32 ABI */ #define ELFOSABI_SOLARIS 6 /* Solaris */ #define ELFOSABI_AIX 7 /* AIX */ #define ELFOSABI_IRIX 8 /* IRIX */ #define ELFOSABI_FREEBSD 9 /* FreeBSD */ #define ELFOSABI_TRU64 10 /* TRU64 UNIX */ #define ELFOSABI_MODESTO 11 /* Novell Modesto */ #define ELFOSABI_OPENBSD 12 /* OpenBSD */ #define ELFOSABI_OPENVMS 13 /* Open VMS */ #define ELFOSABI_NSK 14 /* HP Non-Stop Kernel */ #define ELFOSABI_AROS 15 /* Amiga Research OS */ #define ELFOSABI_ARM 97 /* ARM */ #define ELFOSABI_STANDALONE 255 /* Standalone (embedded) application */ #define ELFOSABI_SYSV ELFOSABI_NONE /* symbol used in old spec */ #define ELFOSABI_MONTEREY ELFOSABI_AIX /* Monterey */ /* e_ident */ #define IS_ELF(ehdr) ((ehdr).e_ident[EI_MAG0] == ELFMAG0 && \ (ehdr).e_ident[EI_MAG1] == ELFMAG1 && \ (ehdr).e_ident[EI_MAG2] == ELFMAG2 && \ (ehdr).e_ident[EI_MAG3] == ELFMAG3) /* Values for e_type. */ #define ET_NONE 0 /* Unknown type. */ #define ET_REL 1 /* Relocatable. */ #define ET_EXEC 2 /* Executable. */ #define ET_DYN 3 /* Shared object. */ #define ET_CORE 4 /* Core file. */ #define ET_LOOS 0xfe00 /* First operating system specific. */ #define ET_HIOS 0xfeff /* Last operating system-specific. */ #define ET_LOPROC 0xff00 /* First processor-specific. */ #define ET_HIPROC 0xffff /* Last processor-specific. */ /* Values for e_machine. */ #define EM_NONE 0 /* Unknown machine. */ #define EM_M32 1 /* AT&T WE32100. */ #define EM_SPARC 2 /* Sun SPARC. */ #define EM_386 3 /* Intel i386. */ #define EM_68K 4 /* Motorola 68000. */ #define EM_88K 5 /* Motorola 88000. */ #define EM_860 7 /* Intel i860. */ #define EM_MIPS 8 /* MIPS R3000 Big-Endian only. */ #define EM_S370 9 /* IBM System/370. */ #define EM_MIPS_RS3_LE 10 /* MIPS R3000 Little-Endian. */ #define EM_PARISC 15 /* HP PA-RISC. */ #define EM_VPP500 17 /* Fujitsu VPP500. */ #define EM_SPARC32PLUS 18 /* SPARC v8plus. */ #define EM_960 19 /* Intel 80960. */ #define EM_PPC 20 /* PowerPC 32-bit. */ #define EM_PPC64 21 /* PowerPC 64-bit. */ #define EM_S390 22 /* IBM System/390. */ #define EM_V800 36 /* NEC V800. */ #define EM_FR20 37 /* Fujitsu FR20. */ #define EM_RH32 38 /* TRW RH-32. */ #define EM_RCE 39 /* Motorola RCE. */ #define EM_ARM 40 /* ARM. */ #define EM_SH 42 /* Hitachi SH. */ #define EM_SPARCV9 43 /* SPARC v9 64-bit. */ #define EM_TRICORE 44 /* Siemens TriCore embedded processor. */ #define EM_ARC 45 /* Argonaut RISC Core. */ #define EM_H8_300 46 /* Hitachi H8/300. */ #define EM_H8_300H 47 /* Hitachi H8/300H. */ #define EM_H8S 48 /* Hitachi H8S. */ #define EM_H8_500 49 /* Hitachi H8/500. */ #define EM_IA_64 50 /* Intel IA-64 Processor. */ #define EM_MIPS_X 51 /* Stanford MIPS-X. */ #define EM_COLDFIRE 52 /* Motorola ColdFire. */ #define EM_68HC12 53 /* Motorola M68HC12. */ #define EM_MMA 54 /* Fujitsu MMA. */ #define EM_PCP 55 /* Siemens PCP. */ #define EM_NCPU 56 /* Sony nCPU. */ #define EM_NDR1 57 /* Denso NDR1 microprocessor. */ #define EM_STARCORE 58 /* Motorola Star*Core processor. */ #define EM_ME16 59 /* Toyota ME16 processor. */ #define EM_ST100 60 /* STMicroelectronics ST100 processor. */ #define EM_TINYJ 61 /* Advanced Logic Corp. TinyJ processor. */ #define EM_X86_64 62 /* Advanced Micro Devices x86-64 */ #define EM_AMD64 EM_X86_64 /* Advanced Micro Devices x86-64 (compat) */ #define EM_PDSP 63 /* Sony DSP Processor. */ #define EM_FX66 66 /* Siemens FX66 microcontroller. */ #define EM_ST9PLUS 67 /* STMicroelectronics ST9+ 8/16 microcontroller. */ #define EM_ST7 68 /* STmicroelectronics ST7 8-bit microcontroller. */ #define EM_68HC16 69 /* Motorola MC68HC16 microcontroller. */ #define EM_68HC11 70 /* Motorola MC68HC11 microcontroller. */ #define EM_68HC08 71 /* Motorola MC68HC08 microcontroller. */ #define EM_68HC05 72 /* Motorola MC68HC05 microcontroller. */ #define EM_SVX 73 /* Silicon Graphics SVx. */ #define EM_ST19 74 /* STMicroelectronics ST19 8-bit mc. */ #define EM_VAX 75 /* Digital VAX. */ #define EM_CRIS 76 /* Axis Communications 32-bit embedded processor. */ #define EM_JAVELIN 77 /* Infineon Technologies 32-bit embedded processor. */ #define EM_FIREPATH 78 /* Element 14 64-bit DSP Processor. */ #define EM_ZSP 79 /* LSI Logic 16-bit DSP Processor. */ #define EM_MMIX 80 /* Donald Knuth's educational 64-bit proc. */ #define EM_HUANY 81 /* Harvard University machine-independent object files. */ #define EM_PRISM 82 /* SiTera Prism. */ #define EM_AVR 83 /* Atmel AVR 8-bit microcontroller. */ #define EM_FR30 84 /* Fujitsu FR30. */ #define EM_D10V 85 /* Mitsubishi D10V. */ #define EM_D30V 86 /* Mitsubishi D30V. */ #define EM_V850 87 /* NEC v850. */ #define EM_M32R 88 /* Mitsubishi M32R. */ #define EM_MN10300 89 /* Matsushita MN10300. */ #define EM_MN10200 90 /* Matsushita MN10200. */ #define EM_PJ 91 /* picoJava. */ #define EM_OPENRISC 92 /* OpenRISC 32-bit embedded processor. */ #define EM_ARC_A5 93 /* ARC Cores Tangent-A5. */ #define EM_XTENSA 94 /* Tensilica Xtensa Architecture. */ #define EM_VIDEOCORE 95 /* Alphamosaic VideoCore processor. */ #define EM_TMM_GPP 96 /* Thompson Multimedia General Purpose Processor. */ #define EM_NS32K 97 /* National Semiconductor 32000 series. */ #define EM_TPC 98 /* Tenor Network TPC processor. */ #define EM_SNP1K 99 /* Trebia SNP 1000 processor. */ #define EM_ST200 100 /* STMicroelectronics ST200 microcontroller. */ #define EM_IP2K 101 /* Ubicom IP2xxx microcontroller family. */ #define EM_MAX 102 /* MAX Processor. */ #define EM_CR 103 /* National Semiconductor CompactRISC microprocessor. */ #define EM_F2MC16 104 /* Fujitsu F2MC16. */ #define EM_MSP430 105 /* Texas Instruments embedded microcontroller msp430. */ #define EM_BLACKFIN 106 /* Analog Devices Blackfin (DSP) processor. */ #define EM_SE_C33 107 /* S1C33 Family of Seiko Epson processors. */ #define EM_SEP 108 /* Sharp embedded microprocessor. */ #define EM_ARCA 109 /* Arca RISC Microprocessor. */ #define EM_UNICORE 110 /* Microprocessor series from PKU-Unity Ltd. and MPRC of Peking University */ #define EM_AARCH64 183 /* AArch64 (64-bit ARM) */ /* Non-standard or deprecated. */ #define EM_486 6 /* Intel i486. */ #define EM_MIPS_RS4_BE 10 /* MIPS R4000 Big-Endian */ #define EM_ALPHA_STD 41 /* Digital Alpha (standard value). */ #define EM_ALPHA 0x9026 /* Alpha (written in the absence of an ABI) */ /* Special section indexes. */ #define SHN_UNDEF 0 /* Undefined, missing, irrelevant. */ #define SHN_LORESERVE 0xff00 /* First of reserved range. */ #define SHN_LOPROC 0xff00 /* First processor-specific. */ #define SHN_HIPROC 0xff1f /* Last processor-specific. */ #define SHN_LOOS 0xff20 /* First operating system-specific. */ #define SHN_HIOS 0xff3f /* Last operating system-specific. */ #define SHN_ABS 0xfff1 /* Absolute values. */ #define SHN_COMMON 0xfff2 /* Common data. */ #define SHN_XINDEX 0xffff /* Escape -- index stored elsewhere. */ #define SHN_HIRESERVE 0xffff /* Last of reserved range. */ /* sh_type */ #define SHT_NULL 0 /* inactive */ #define SHT_PROGBITS 1 /* program defined information */ #define SHT_SYMTAB 2 /* symbol table section */ #define SHT_STRTAB 3 /* string table section */ #define SHT_RELA 4 /* relocation section with addends */ #define SHT_HASH 5 /* symbol hash table section */ #define SHT_DYNAMIC 6 /* dynamic section */ #define SHT_NOTE 7 /* note section */ #define SHT_NOBITS 8 /* no space section */ #define SHT_REL 9 /* relocation section - no addends */ #define SHT_SHLIB 10 /* reserved - purpose unknown */ #define SHT_DYNSYM 11 /* dynamic symbol table section */ #define SHT_INIT_ARRAY 14 /* Initialization function pointers. */ #define SHT_FINI_ARRAY 15 /* Termination function pointers. */ #define SHT_PREINIT_ARRAY 16 /* Pre-initialization function ptrs. */ #define SHT_GROUP 17 /* Section group. */ #define SHT_SYMTAB_SHNDX 18 /* Section indexes (see SHN_XINDEX). */ #define SHT_LOOS 0x60000000 /* First of OS specific semantics */ #define SHT_LOSUNW 0x6ffffff4 #define SHT_SUNW_dof 0x6ffffff4 #define SHT_SUNW_cap 0x6ffffff5 #define SHT_SUNW_SIGNATURE 0x6ffffff6 #define SHT_GNU_HASH 0x6ffffff6 #define SHT_GNU_LIBLIST 0x6ffffff7 #define SHT_SUNW_ANNOTATE 0x6ffffff7 #define SHT_SUNW_DEBUGSTR 0x6ffffff8 #define SHT_SUNW_DEBUG 0x6ffffff9 #define SHT_SUNW_move 0x6ffffffa #define SHT_SUNW_COMDAT 0x6ffffffb #define SHT_SUNW_syminfo 0x6ffffffc #define SHT_SUNW_verdef 0x6ffffffd #define SHT_GNU_verdef 0x6ffffffd /* Symbol versions provided */ #define SHT_SUNW_verneed 0x6ffffffe #define SHT_GNU_verneed 0x6ffffffe /* Symbol versions required */ #define SHT_SUNW_versym 0x6fffffff #define SHT_GNU_versym 0x6fffffff /* Symbol version table */ #define SHT_HISUNW 0x6fffffff #define SHT_HIOS 0x6fffffff /* Last of OS specific semantics */ #define SHT_LOPROC 0x70000000 /* reserved range for processor */ #define SHT_AMD64_UNWIND 0x70000001 /* unwind information */ #define SHT_ARM_EXIDX 0x70000001 /* Exception index table. */ #define SHT_ARM_PREEMPTMAP 0x70000002 /* BPABI DLL dynamic linking pre-emption map. */ #define SHT_ARM_ATTRIBUTES 0x70000003 /* Object file compatibility attributes. */ #define SHT_ARM_DEBUGOVERLAY 0x70000004 /* See DBGOVL for details. */ #define SHT_ARM_OVERLAYSECTION 0x70000005 /* See DBGOVL for details. */ #define SHT_MIPS_REGINFO 0x70000006 #define SHT_MIPS_OPTIONS 0x7000000d #define SHT_MIPS_DWARF 0x7000001e /* MIPS gcc uses MIPS_DWARF */ #define SHT_HIPROC 0x7fffffff /* specific section header types */ #define SHT_LOUSER 0x80000000 /* reserved range for application */ #define SHT_HIUSER 0xffffffff /* specific indexes */ /* Flags for sh_flags. */ #define SHF_WRITE 0x1 /* Section contains writable data. */ #define SHF_ALLOC 0x2 /* Section occupies memory. */ #define SHF_EXECINSTR 0x4 /* Section contains instructions. */ #define SHF_MERGE 0x10 /* Section may be merged. */ #define SHF_STRINGS 0x20 /* Section contains strings. */ #define SHF_INFO_LINK 0x40 /* sh_info holds section index. */ #define SHF_LINK_ORDER 0x80 /* Special ordering requirements. */ #define SHF_OS_NONCONFORMING 0x100 /* OS-specific processing required. */ #define SHF_GROUP 0x200 /* Member of section group. */ #define SHF_TLS 0x400 /* Section contains TLS data. */ #define SHF_MASKOS 0x0ff00000 /* OS-specific semantics. */ #define SHF_MASKPROC 0xf0000000 /* Processor-specific semantics. */ /* Values for p_type. */ #define PT_NULL 0 /* Unused entry. */ #define PT_LOAD 1 /* Loadable segment. */ #define PT_DYNAMIC 2 /* Dynamic linking information segment. */ #define PT_INTERP 3 /* Pathname of interpreter. */ #define PT_NOTE 4 /* Auxiliary information. */ #define PT_SHLIB 5 /* Reserved (not used). */ #define PT_PHDR 6 /* Location of program header itself. */ #define PT_TLS 7 /* Thread local storage segment */ #define PT_LOOS 0x60000000 /* First OS-specific. */ #define PT_SUNW_UNWIND 0x6464e550 /* amd64 UNWIND program header */ #define PT_GNU_EH_FRAME 0x6474e550 #define PT_GNU_STACK 0x6474e551 #define PT_GNU_RELRO 0x6474e552 #define PT_DUMP_DELTA 0x6fb5d000 /* va->pa map for kernel dumps (currently arm). */ #define PT_LOSUNW 0x6ffffffa #define PT_SUNWBSS 0x6ffffffa /* Sun Specific segment */ #define PT_SUNWSTACK 0x6ffffffb /* describes the stack segment */ #define PT_SUNWDTRACE 0x6ffffffc /* private */ #define PT_SUNWCAP 0x6ffffffd /* hard/soft capabilities segment */ #define PT_HISUNW 0x6fffffff #define PT_HIOS 0x6fffffff /* Last OS-specific. */ #define PT_LOPROC 0x70000000 /* First processor-specific type. */ #define PT_HIPROC 0x7fffffff /* Last processor-specific type. */ /* Values for p_flags. */ #define PF_X 0x1 /* Executable. */ #define PF_W 0x2 /* Writable. */ #define PF_R 0x4 /* Readable. */ #define PF_MASKOS 0x0ff00000 /* Operating system-specific. */ #define PF_MASKPROC 0xf0000000 /* Processor-specific. */ /* Extended program header index. */ #define PN_XNUM 0xffff /* Values for d_tag. */ #define DT_NULL 0 /* Terminating entry. */ #define DT_NEEDED 1 /* String table offset of a needed shared library. */ #define DT_PLTRELSZ 2 /* Total size in bytes of PLT relocations. */ #define DT_PLTGOT 3 /* Processor-dependent address. */ #define DT_HASH 4 /* Address of symbol hash table. */ #define DT_STRTAB 5 /* Address of string table. */ #define DT_SYMTAB 6 /* Address of symbol table. */ #define DT_RELA 7 /* Address of ElfNN_Rela relocations. */ #define DT_RELASZ 8 /* Total size of ElfNN_Rela relocations. */ #define DT_RELAENT 9 /* Size of each ElfNN_Rela relocation entry. */ #define DT_STRSZ 10 /* Size of string table. */ #define DT_SYMENT 11 /* Size of each symbol table entry. */ #define DT_INIT 12 /* Address of initialization function. */ #define DT_FINI 13 /* Address of finalization function. */ #define DT_SONAME 14 /* String table offset of shared object name. */ #define DT_RPATH 15 /* String table offset of library path. [sup] */ #define DT_SYMBOLIC 16 /* Indicates "symbolic" linking. [sup] */ #define DT_REL 17 /* Address of ElfNN_Rel relocations. */ #define DT_RELSZ 18 /* Total size of ElfNN_Rel relocations. */ #define DT_RELENT 19 /* Size of each ElfNN_Rel relocation. */ #define DT_PLTREL 20 /* Type of relocation used for PLT. */ #define DT_DEBUG 21 /* Reserved (not used). */ #define DT_TEXTREL 22 /* Indicates there may be relocations in non-writable segments. [sup] */ #define DT_JMPREL 23 /* Address of PLT relocations. */ #define DT_BIND_NOW 24 /* [sup] */ #define DT_INIT_ARRAY 25 /* Address of the array of pointers to initialization functions */ #define DT_FINI_ARRAY 26 /* Address of the array of pointers to termination functions */ #define DT_INIT_ARRAYSZ 27 /* Size in bytes of the array of initialization functions. */ #define DT_FINI_ARRAYSZ 28 /* Size in bytes of the array of termination functions. */ #define DT_RUNPATH 29 /* String table offset of a null-terminated library search path string. */ #define DT_FLAGS 30 /* Object specific flag values. */ #define DT_ENCODING 32 /* Values greater than or equal to DT_ENCODING and less than DT_LOOS follow the rules for the interpretation of the d_un union as follows: even == 'd_ptr', odd == 'd_val' or none */ #define DT_PREINIT_ARRAY 32 /* Address of the array of pointers to pre-initialization functions. */ #define DT_PREINIT_ARRAYSZ 33 /* Size in bytes of the array of pre-initialization functions. */ #define DT_MAXPOSTAGS 34 /* number of positive tags */ #define DT_LOOS 0x6000000d /* First OS-specific */ #define DT_SUNW_AUXILIARY 0x6000000d /* symbol auxiliary name */ #define DT_SUNW_RTLDINF 0x6000000e /* ld.so.1 info (private) */ #define DT_SUNW_FILTER 0x6000000f /* symbol filter name */ #define DT_SUNW_CAP 0x60000010 /* hardware/software */ #define DT_HIOS 0x6ffff000 /* Last OS-specific */ /* * DT_* entries which fall between DT_VALRNGHI & DT_VALRNGLO use the * Dyn.d_un.d_val field of the Elf*_Dyn structure. */ #define DT_VALRNGLO 0x6ffffd00 #define DT_CHECKSUM 0x6ffffdf8 /* elf checksum */ #define DT_PLTPADSZ 0x6ffffdf9 /* pltpadding size */ #define DT_MOVEENT 0x6ffffdfa /* move table entry size */ #define DT_MOVESZ 0x6ffffdfb /* move table size */ #define DT_FEATURE 0x6ffffdfc /* feature holder */ #define DT_POSFLAG_1 0x6ffffdfd /* flags for DT_* entries, effecting */ /* the following DT_* entry. */ /* See DF_P1_* definitions */ #define DT_SYMINSZ 0x6ffffdfe /* syminfo table size (in bytes) */ #define DT_SYMINENT 0x6ffffdff /* syminfo entry size (in bytes) */ #define DT_VALRNGHI 0x6ffffdff /* * DT_* entries which fall between DT_ADDRRNGHI & DT_ADDRRNGLO use the * Dyn.d_un.d_ptr field of the Elf*_Dyn structure. * * If any adjustment is made to the ELF object after it has been * built, these entries will need to be adjusted. */ #define DT_ADDRRNGLO 0x6ffffe00 #define DT_GNU_HASH 0x6ffffef5 /* GNU-style hash table */ #define DT_CONFIG 0x6ffffefa /* configuration information */ #define DT_DEPAUDIT 0x6ffffefb /* dependency auditing */ #define DT_AUDIT 0x6ffffefc /* object auditing */ #define DT_PLTPAD 0x6ffffefd /* pltpadding (sparcv9) */ #define DT_MOVETAB 0x6ffffefe /* move table */ #define DT_SYMINFO 0x6ffffeff /* syminfo table */ #define DT_ADDRRNGHI 0x6ffffeff #define DT_VERSYM 0x6ffffff0 /* Address of versym section. */ #define DT_RELACOUNT 0x6ffffff9 /* number of RELATIVE relocations */ #define DT_RELCOUNT 0x6ffffffa /* number of RELATIVE relocations */ #define DT_FLAGS_1 0x6ffffffb /* state flags - see DF_1_* defs */ #define DT_VERDEF 0x6ffffffc /* Address of verdef section. */ #define DT_VERDEFNUM 0x6ffffffd /* Number of elems in verdef section */ #define DT_VERNEED 0x6ffffffe /* Address of verneed section. */ #define DT_VERNEEDNUM 0x6fffffff /* Number of elems in verneed section */ #define DT_LOPROC 0x70000000 /* First processor-specific type. */ #define DT_DEPRECATED_SPARC_REGISTER 0x7000001 #define DT_AUXILIARY 0x7ffffffd /* shared library auxiliary name */ #define DT_USED 0x7ffffffe /* ignored - same as needed */ #define DT_FILTER 0x7fffffff /* shared library filter name */ #define DT_HIPROC 0x7fffffff /* Last processor-specific type. */ /* Values for DT_FLAGS */ #define DF_ORIGIN 0x0001 /* Indicates that the object being loaded may make reference to the $ORIGIN substitution string */ #define DF_SYMBOLIC 0x0002 /* Indicates "symbolic" linking. */ #define DF_TEXTREL 0x0004 /* Indicates there may be relocations in non-writable segments. */ #define DF_BIND_NOW 0x0008 /* Indicates that the dynamic linker should process all relocations for the object containing this entry before transferring control to the program. */ #define DF_STATIC_TLS 0x0010 /* Indicates that the shared object or executable contains code using a static thread-local storage scheme. */ /* Values for DT_FLAGS_1 */ #define DF_1_BIND_NOW 0x00000001 /* Same as DF_BIND_NOW */ #define DF_1_GLOBAL 0x00000002 /* Set the RTLD_GLOBAL for object */ #define DF_1_NODELETE 0x00000008 /* Set the RTLD_NODELETE for object */ #define DF_1_LOADFLTR 0x00000010 /* Immediate loading of filtees */ #define DF_1_NOOPEN 0x00000040 /* Do not allow loading on dlopen() */ #define DF_1_ORIGIN 0x00000080 /* Process $ORIGIN */ #define DF_1_INTERPOSE 0x00000400 /* Interpose all objects but main */ #define DF_1_NODEFLIB 0x00000800 /* Do not search default paths */ /* Values for n_type. Used in core files. */ #define NT_PRSTATUS 1 /* Process status. */ #define NT_FPREGSET 2 /* Floating point registers. */ #define NT_PRPSINFO 3 /* Process state info. */ #define NT_THRMISC 7 /* Thread miscellaneous info. */ #define NT_PROCSTAT_PROC 8 /* Procstat proc data. */ #define NT_PROCSTAT_FILES 9 /* Procstat files data. */ #define NT_PROCSTAT_VMMAP 10 /* Procstat vmmap data. */ #define NT_PROCSTAT_GROUPS 11 /* Procstat groups data. */ #define NT_PROCSTAT_UMASK 12 /* Procstat umask data. */ #define NT_PROCSTAT_RLIMIT 13 /* Procstat rlimit data. */ #define NT_PROCSTAT_OSREL 14 /* Procstat osreldate data. */ #define NT_PROCSTAT_PSSTRINGS 15 /* Procstat ps_strings data. */ #define NT_PROCSTAT_AUXV 16 /* Procstat auxv data. */ +#define NT_X86_XSTATE 0x202 /* x86 XSAVE extended state. */ /* Symbol Binding - ELFNN_ST_BIND - st_info */ #define STB_LOCAL 0 /* Local symbol */ #define STB_GLOBAL 1 /* Global symbol */ #define STB_WEAK 2 /* like global - lower precedence */ #define STB_LOOS 10 /* Reserved range for operating system */ #define STB_HIOS 12 /* specific semantics. */ #define STB_LOPROC 13 /* reserved range for processor */ #define STB_HIPROC 15 /* specific semantics. */ /* Symbol type - ELFNN_ST_TYPE - st_info */ #define STT_NOTYPE 0 /* Unspecified type. */ #define STT_OBJECT 1 /* Data object. */ #define STT_FUNC 2 /* Function. */ #define STT_SECTION 3 /* Section. */ #define STT_FILE 4 /* Source file. */ #define STT_COMMON 5 /* Uninitialized common block. */ #define STT_TLS 6 /* TLS object. */ #define STT_NUM 7 #define STT_LOOS 10 /* Reserved range for operating system */ #define STT_GNU_IFUNC 10 #define STT_HIOS 12 /* specific semantics. */ #define STT_LOPROC 13 /* reserved range for processor */ #define STT_HIPROC 15 /* specific semantics. */ /* Symbol visibility - ELFNN_ST_VISIBILITY - st_other */ #define STV_DEFAULT 0x0 /* Default visibility (see binding). */ #define STV_INTERNAL 0x1 /* Special meaning in relocatable objects. */ #define STV_HIDDEN 0x2 /* Not visible. */ #define STV_PROTECTED 0x3 /* Visible but not preemptible. */ #define STV_EXPORTED 0x4 #define STV_SINGLETON 0x5 #define STV_ELIMINATE 0x6 /* Special symbol table indexes. */ #define STN_UNDEF 0 /* Undefined symbol index. */ /* Symbol versioning flags. */ #define VER_DEF_CURRENT 1 #define VER_DEF_IDX(x) VER_NDX(x) #define VER_FLG_BASE 0x01 #define VER_FLG_WEAK 0x02 #define VER_NEED_CURRENT 1 #define VER_NEED_WEAK (1u << 15) #define VER_NEED_HIDDEN VER_NDX_HIDDEN #define VER_NEED_IDX(x) VER_NDX(x) #define VER_NDX_LOCAL 0 #define VER_NDX_GLOBAL 1 #define VER_NDX_GIVEN 2 #define VER_NDX_HIDDEN (1u << 15) #define VER_NDX(x) ((x) & ~(1u << 15)) #define CA_SUNW_NULL 0 #define CA_SUNW_HW_1 1 /* first hardware capabilities entry */ #define CA_SUNW_SF_1 2 /* first software capabilities entry */ /* * Syminfo flag values */ #define SYMINFO_FLG_DIRECT 0x0001 /* symbol ref has direct association */ /* to object containing defn. */ #define SYMINFO_FLG_PASSTHRU 0x0002 /* ignored - see SYMINFO_FLG_FILTER */ #define SYMINFO_FLG_COPY 0x0004 /* symbol is a copy-reloc */ #define SYMINFO_FLG_LAZYLOAD 0x0008 /* object containing defn should be */ /* lazily-loaded */ #define SYMINFO_FLG_DIRECTBIND 0x0010 /* ref should be bound directly to */ /* object containing defn. */ #define SYMINFO_FLG_NOEXTDIRECT 0x0020 /* don't let an external reference */ /* directly bind to this symbol */ #define SYMINFO_FLG_FILTER 0x0002 /* symbol ref is associated to a */ #define SYMINFO_FLG_AUXILIARY 0x0040 /* standard or auxiliary filter */ /* * Syminfo.si_boundto values. */ #define SYMINFO_BT_SELF 0xffff /* symbol bound to self */ #define SYMINFO_BT_PARENT 0xfffe /* symbol bound to parent */ #define SYMINFO_BT_NONE 0xfffd /* no special symbol binding */ #define SYMINFO_BT_EXTERN 0xfffc /* symbol defined as external */ #define SYMINFO_BT_LOWRESERVE 0xff00 /* beginning of reserved entries */ /* * Syminfo version values. */ #define SYMINFO_NONE 0 /* Syminfo version */ #define SYMINFO_CURRENT 1 #define SYMINFO_NUM 2 /* * Relocation types. * * All machine architectures are defined here to allow tools on one to * handle others. */ #define R_386_NONE 0 /* No relocation. */ #define R_386_32 1 /* Add symbol value. */ #define R_386_PC32 2 /* Add PC-relative symbol value. */ #define R_386_GOT32 3 /* Add PC-relative GOT offset. */ #define R_386_PLT32 4 /* Add PC-relative PLT offset. */ #define R_386_COPY 5 /* Copy data from shared object. */ #define R_386_GLOB_DAT 6 /* Set GOT entry to data address. */ #define R_386_JMP_SLOT 7 /* Set GOT entry to code address. */ #define R_386_RELATIVE 8 /* Add load address of shared object. */ #define R_386_GOTOFF 9 /* Add GOT-relative symbol address. */ #define R_386_GOTPC 10 /* Add PC-relative GOT table address. */ #define R_386_TLS_TPOFF 14 /* Negative offset in static TLS block */ #define R_386_TLS_IE 15 /* Absolute address of GOT for -ve static TLS */ #define R_386_TLS_GOTIE 16 /* GOT entry for negative static TLS block */ #define R_386_TLS_LE 17 /* Negative offset relative to static TLS */ #define R_386_TLS_GD 18 /* 32 bit offset to GOT (index,off) pair */ #define R_386_TLS_LDM 19 /* 32 bit offset to GOT (index,zero) pair */ #define R_386_TLS_GD_32 24 /* 32 bit offset to GOT (index,off) pair */ #define R_386_TLS_GD_PUSH 25 /* pushl instruction for Sun ABI GD sequence */ #define R_386_TLS_GD_CALL 26 /* call instruction for Sun ABI GD sequence */ #define R_386_TLS_GD_POP 27 /* popl instruction for Sun ABI GD sequence */ #define R_386_TLS_LDM_32 28 /* 32 bit offset to GOT (index,zero) pair */ #define R_386_TLS_LDM_PUSH 29 /* pushl instruction for Sun ABI LD sequence */ #define R_386_TLS_LDM_CALL 30 /* call instruction for Sun ABI LD sequence */ #define R_386_TLS_LDM_POP 31 /* popl instruction for Sun ABI LD sequence */ #define R_386_TLS_LDO_32 32 /* 32 bit offset from start of TLS block */ #define R_386_TLS_IE_32 33 /* 32 bit offset to GOT static TLS offset entry */ #define R_386_TLS_LE_32 34 /* 32 bit offset within static TLS block */ #define R_386_TLS_DTPMOD32 35 /* GOT entry containing TLS index */ #define R_386_TLS_DTPOFF32 36 /* GOT entry containing TLS offset */ #define R_386_TLS_TPOFF32 37 /* GOT entry of -ve static TLS offset */ #define R_386_IRELATIVE 42 /* PLT entry resolved indirectly at runtime */ #define R_ARM_NONE 0 /* No relocation. */ #define R_ARM_PC24 1 #define R_ARM_ABS32 2 #define R_ARM_REL32 3 #define R_ARM_PC13 4 #define R_ARM_ABS16 5 #define R_ARM_ABS12 6 #define R_ARM_THM_ABS5 7 #define R_ARM_ABS8 8 #define R_ARM_SBREL32 9 #define R_ARM_THM_PC22 10 #define R_ARM_THM_PC8 11 #define R_ARM_AMP_VCALL9 12 #define R_ARM_SWI24 13 #define R_ARM_THM_SWI8 14 #define R_ARM_XPC25 15 #define R_ARM_THM_XPC22 16 /* TLS relocations */ #define R_ARM_TLS_DTPMOD32 17 /* ID of module containing symbol */ #define R_ARM_TLS_DTPOFF32 18 /* Offset in TLS block */ #define R_ARM_TLS_TPOFF32 19 /* Offset in static TLS block */ #define R_ARM_COPY 20 /* Copy data from shared object. */ #define R_ARM_GLOB_DAT 21 /* Set GOT entry to data address. */ #define R_ARM_JUMP_SLOT 22 /* Set GOT entry to code address. */ #define R_ARM_RELATIVE 23 /* Add load address of shared object. */ #define R_ARM_GOTOFF 24 /* Add GOT-relative symbol address. */ #define R_ARM_GOTPC 25 /* Add PC-relative GOT table address. */ #define R_ARM_GOT32 26 /* Add PC-relative GOT offset. */ #define R_ARM_PLT32 27 /* Add PC-relative PLT offset. */ #define R_ARM_GNU_VTENTRY 100 #define R_ARM_GNU_VTINHERIT 101 #define R_ARM_RSBREL32 250 #define R_ARM_THM_RPC22 251 #define R_ARM_RREL32 252 #define R_ARM_RABS32 253 #define R_ARM_RPC24 254 #define R_ARM_RBASE 255 /* Name Value Field Calculation */ #define R_IA_64_NONE 0 /* None */ #define R_IA_64_IMM14 0x21 /* immediate14 S + A */ #define R_IA_64_IMM22 0x22 /* immediate22 S + A */ #define R_IA_64_IMM64 0x23 /* immediate64 S + A */ #define R_IA_64_DIR32MSB 0x24 /* word32 MSB S + A */ #define R_IA_64_DIR32LSB 0x25 /* word32 LSB S + A */ #define R_IA_64_DIR64MSB 0x26 /* word64 MSB S + A */ #define R_IA_64_DIR64LSB 0x27 /* word64 LSB S + A */ #define R_IA_64_GPREL22 0x2a /* immediate22 @gprel(S + A) */ #define R_IA_64_GPREL64I 0x2b /* immediate64 @gprel(S + A) */ #define R_IA_64_GPREL32MSB 0x2c /* word32 MSB @gprel(S + A) */ #define R_IA_64_GPREL32LSB 0x2d /* word32 LSB @gprel(S + A) */ #define R_IA_64_GPREL64MSB 0x2e /* word64 MSB @gprel(S + A) */ #define R_IA_64_GPREL64LSB 0x2f /* word64 LSB @gprel(S + A) */ #define R_IA_64_LTOFF22 0x32 /* immediate22 @ltoff(S + A) */ #define R_IA_64_LTOFF64I 0x33 /* immediate64 @ltoff(S + A) */ #define R_IA_64_PLTOFF22 0x3a /* immediate22 @pltoff(S + A) */ #define R_IA_64_PLTOFF64I 0x3b /* immediate64 @pltoff(S + A) */ #define R_IA_64_PLTOFF64MSB 0x3e /* word64 MSB @pltoff(S + A) */ #define R_IA_64_PLTOFF64LSB 0x3f /* word64 LSB @pltoff(S + A) */ #define R_IA_64_FPTR64I 0x43 /* immediate64 @fptr(S + A) */ #define R_IA_64_FPTR32MSB 0x44 /* word32 MSB @fptr(S + A) */ #define R_IA_64_FPTR32LSB 0x45 /* word32 LSB @fptr(S + A) */ #define R_IA_64_FPTR64MSB 0x46 /* word64 MSB @fptr(S + A) */ #define R_IA_64_FPTR64LSB 0x47 /* word64 LSB @fptr(S + A) */ #define R_IA_64_PCREL60B 0x48 /* immediate60 form1 S + A - P */ #define R_IA_64_PCREL21B 0x49 /* immediate21 form1 S + A - P */ #define R_IA_64_PCREL21M 0x4a /* immediate21 form2 S + A - P */ #define R_IA_64_PCREL21F 0x4b /* immediate21 form3 S + A - P */ #define R_IA_64_PCREL32MSB 0x4c /* word32 MSB S + A - P */ #define R_IA_64_PCREL32LSB 0x4d /* word32 LSB S + A - P */ #define R_IA_64_PCREL64MSB 0x4e /* word64 MSB S + A - P */ #define R_IA_64_PCREL64LSB 0x4f /* word64 LSB S + A - P */ #define R_IA_64_LTOFF_FPTR22 0x52 /* immediate22 @ltoff(@fptr(S + A)) */ #define R_IA_64_LTOFF_FPTR64I 0x53 /* immediate64 @ltoff(@fptr(S + A)) */ #define R_IA_64_LTOFF_FPTR32MSB 0x54 /* word32 MSB @ltoff(@fptr(S + A)) */ #define R_IA_64_LTOFF_FPTR32LSB 0x55 /* word32 LSB @ltoff(@fptr(S + A)) */ #define R_IA_64_LTOFF_FPTR64MSB 0x56 /* word64 MSB @ltoff(@fptr(S + A)) */ #define R_IA_64_LTOFF_FPTR64LSB 0x57 /* word64 LSB @ltoff(@fptr(S + A)) */ #define R_IA_64_SEGREL32MSB 0x5c /* word32 MSB @segrel(S + A) */ #define R_IA_64_SEGREL32LSB 0x5d /* word32 LSB @segrel(S + A) */ #define R_IA_64_SEGREL64MSB 0x5e /* word64 MSB @segrel(S + A) */ #define R_IA_64_SEGREL64LSB 0x5f /* word64 LSB @segrel(S + A) */ #define R_IA_64_SECREL32MSB 0x64 /* word32 MSB @secrel(S + A) */ #define R_IA_64_SECREL32LSB 0x65 /* word32 LSB @secrel(S + A) */ #define R_IA_64_SECREL64MSB 0x66 /* word64 MSB @secrel(S + A) */ #define R_IA_64_SECREL64LSB 0x67 /* word64 LSB @secrel(S + A) */ #define R_IA_64_REL32MSB 0x6c /* word32 MSB BD + A */ #define R_IA_64_REL32LSB 0x6d /* word32 LSB BD + A */ #define R_IA_64_REL64MSB 0x6e /* word64 MSB BD + A */ #define R_IA_64_REL64LSB 0x6f /* word64 LSB BD + A */ #define R_IA_64_LTV32MSB 0x74 /* word32 MSB S + A */ #define R_IA_64_LTV32LSB 0x75 /* word32 LSB S + A */ #define R_IA_64_LTV64MSB 0x76 /* word64 MSB S + A */ #define R_IA_64_LTV64LSB 0x77 /* word64 LSB S + A */ #define R_IA_64_PCREL21BI 0x79 /* immediate21 form1 S + A - P */ #define R_IA_64_PCREL22 0x7a /* immediate22 S + A - P */ #define R_IA_64_PCREL64I 0x7b /* immediate64 S + A - P */ #define R_IA_64_IPLTMSB 0x80 /* function descriptor MSB special */ #define R_IA_64_IPLTLSB 0x81 /* function descriptor LSB speciaal */ #define R_IA_64_SUB 0x85 /* immediate64 A - S */ #define R_IA_64_LTOFF22X 0x86 /* immediate22 special */ #define R_IA_64_LDXMOV 0x87 /* immediate22 special */ #define R_IA_64_TPREL14 0x91 /* imm14 @tprel(S + A) */ #define R_IA_64_TPREL22 0x92 /* imm22 @tprel(S + A) */ #define R_IA_64_TPREL64I 0x93 /* imm64 @tprel(S + A) */ #define R_IA_64_TPREL64MSB 0x96 /* word64 MSB @tprel(S + A) */ #define R_IA_64_TPREL64LSB 0x97 /* word64 LSB @tprel(S + A) */ #define R_IA_64_LTOFF_TPREL22 0x9a /* imm22 @ltoff(@tprel(S+A)) */ #define R_IA_64_DTPMOD64MSB 0xa6 /* word64 MSB @dtpmod(S + A) */ #define R_IA_64_DTPMOD64LSB 0xa7 /* word64 LSB @dtpmod(S + A) */ #define R_IA_64_LTOFF_DTPMOD22 0xaa /* imm22 @ltoff(@dtpmod(S+A)) */ #define R_IA_64_DTPREL14 0xb1 /* imm14 @dtprel(S + A) */ #define R_IA_64_DTPREL22 0xb2 /* imm22 @dtprel(S + A) */ #define R_IA_64_DTPREL64I 0xb3 /* imm64 @dtprel(S + A) */ #define R_IA_64_DTPREL32MSB 0xb4 /* word32 MSB @dtprel(S + A) */ #define R_IA_64_DTPREL32LSB 0xb5 /* word32 LSB @dtprel(S + A) */ #define R_IA_64_DTPREL64MSB 0xb6 /* word64 MSB @dtprel(S + A) */ #define R_IA_64_DTPREL64LSB 0xb7 /* word64 LSB @dtprel(S + A) */ #define R_IA_64_LTOFF_DTPREL22 0xba /* imm22 @ltoff(@dtprel(S+A)) */ #define R_MIPS_NONE 0 /* No reloc */ #define R_MIPS_16 1 /* Direct 16 bit */ #define R_MIPS_32 2 /* Direct 32 bit */ #define R_MIPS_REL32 3 /* PC relative 32 bit */ #define R_MIPS_26 4 /* Direct 26 bit shifted */ #define R_MIPS_HI16 5 /* High 16 bit */ #define R_MIPS_LO16 6 /* Low 16 bit */ #define R_MIPS_GPREL16 7 /* GP relative 16 bit */ #define R_MIPS_LITERAL 8 /* 16 bit literal entry */ #define R_MIPS_GOT16 9 /* 16 bit GOT entry */ #define R_MIPS_PC16 10 /* PC relative 16 bit */ #define R_MIPS_CALL16 11 /* 16 bit GOT entry for function */ #define R_MIPS_GPREL32 12 /* GP relative 32 bit */ #define R_MIPS_64 18 /* Direct 64 bit */ #define R_MIPS_GOTHI16 21 /* GOT HI 16 bit */ #define R_MIPS_GOTLO16 22 /* GOT LO 16 bit */ #define R_MIPS_CALLHI16 30 /* upper 16 bit GOT entry for function */ #define R_MIPS_CALLLO16 31 /* lower 16 bit GOT entry for function */ #define R_PPC_NONE 0 /* No relocation. */ #define R_PPC_ADDR32 1 #define R_PPC_ADDR24 2 #define R_PPC_ADDR16 3 #define R_PPC_ADDR16_LO 4 #define R_PPC_ADDR16_HI 5 #define R_PPC_ADDR16_HA 6 #define R_PPC_ADDR14 7 #define R_PPC_ADDR14_BRTAKEN 8 #define R_PPC_ADDR14_BRNTAKEN 9 #define R_PPC_REL24 10 #define R_PPC_REL14 11 #define R_PPC_REL14_BRTAKEN 12 #define R_PPC_REL14_BRNTAKEN 13 #define R_PPC_GOT16 14 #define R_PPC_GOT16_LO 15 #define R_PPC_GOT16_HI 16 #define R_PPC_GOT16_HA 17 #define R_PPC_PLTREL24 18 #define R_PPC_COPY 19 #define R_PPC_GLOB_DAT 20 #define R_PPC_JMP_SLOT 21 #define R_PPC_RELATIVE 22 #define R_PPC_LOCAL24PC 23 #define R_PPC_UADDR32 24 #define R_PPC_UADDR16 25 #define R_PPC_REL32 26 #define R_PPC_PLT32 27 #define R_PPC_PLTREL32 28 #define R_PPC_PLT16_LO 29 #define R_PPC_PLT16_HI 30 #define R_PPC_PLT16_HA 31 #define R_PPC_SDAREL16 32 #define R_PPC_SECTOFF 33 #define R_PPC_SECTOFF_LO 34 #define R_PPC_SECTOFF_HI 35 #define R_PPC_SECTOFF_HA 36 /* * 64-bit relocations */ #define R_PPC64_ADDR64 38 #define R_PPC64_ADDR16_HIGHER 39 #define R_PPC64_ADDR16_HIGHERA 40 #define R_PPC64_ADDR16_HIGHEST 41 #define R_PPC64_ADDR16_HIGHESTA 42 #define R_PPC64_UADDR64 43 #define R_PPC64_REL64 44 #define R_PPC64_PLT64 45 #define R_PPC64_PLTREL64 46 #define R_PPC64_TOC16 47 #define R_PPC64_TOC16_LO 48 #define R_PPC64_TOC16_HI 49 #define R_PPC64_TOC16_HA 50 #define R_PPC64_TOC 51 #define R_PPC64_DTPMOD64 68 #define R_PPC64_TPREL64 73 #define R_PPC64_DTPREL64 78 /* * TLS relocations */ #define R_PPC_TLS 67 #define R_PPC_DTPMOD32 68 #define R_PPC_TPREL16 69 #define R_PPC_TPREL16_LO 70 #define R_PPC_TPREL16_HI 71 #define R_PPC_TPREL16_HA 72 #define R_PPC_TPREL32 73 #define R_PPC_DTPREL16 74 #define R_PPC_DTPREL16_LO 75 #define R_PPC_DTPREL16_HI 76 #define R_PPC_DTPREL16_HA 77 #define R_PPC_DTPREL32 78 #define R_PPC_GOT_TLSGD16 79 #define R_PPC_GOT_TLSGD16_LO 80 #define R_PPC_GOT_TLSGD16_HI 81 #define R_PPC_GOT_TLSGD16_HA 82 #define R_PPC_GOT_TLSLD16 83 #define R_PPC_GOT_TLSLD16_LO 84 #define R_PPC_GOT_TLSLD16_HI 85 #define R_PPC_GOT_TLSLD16_HA 86 #define R_PPC_GOT_TPREL16 87 #define R_PPC_GOT_TPREL16_LO 88 #define R_PPC_GOT_TPREL16_HI 89 #define R_PPC_GOT_TPREL16_HA 90 /* * The remaining relocs are from the Embedded ELF ABI, and are not in the * SVR4 ELF ABI. */ #define R_PPC_EMB_NADDR32 101 #define R_PPC_EMB_NADDR16 102 #define R_PPC_EMB_NADDR16_LO 103 #define R_PPC_EMB_NADDR16_HI 104 #define R_PPC_EMB_NADDR16_HA 105 #define R_PPC_EMB_SDAI16 106 #define R_PPC_EMB_SDA2I16 107 #define R_PPC_EMB_SDA2REL 108 #define R_PPC_EMB_SDA21 109 #define R_PPC_EMB_MRKREF 110 #define R_PPC_EMB_RELSEC16 111 #define R_PPC_EMB_RELST_LO 112 #define R_PPC_EMB_RELST_HI 113 #define R_PPC_EMB_RELST_HA 114 #define R_PPC_EMB_BIT_FLD 115 #define R_PPC_EMB_RELSDA 116 #define R_SPARC_NONE 0 #define R_SPARC_8 1 #define R_SPARC_16 2 #define R_SPARC_32 3 #define R_SPARC_DISP8 4 #define R_SPARC_DISP16 5 #define R_SPARC_DISP32 6 #define R_SPARC_WDISP30 7 #define R_SPARC_WDISP22 8 #define R_SPARC_HI22 9 #define R_SPARC_22 10 #define R_SPARC_13 11 #define R_SPARC_LO10 12 #define R_SPARC_GOT10 13 #define R_SPARC_GOT13 14 #define R_SPARC_GOT22 15 #define R_SPARC_PC10 16 #define R_SPARC_PC22 17 #define R_SPARC_WPLT30 18 #define R_SPARC_COPY 19 #define R_SPARC_GLOB_DAT 20 #define R_SPARC_JMP_SLOT 21 #define R_SPARC_RELATIVE 22 #define R_SPARC_UA32 23 #define R_SPARC_PLT32 24 #define R_SPARC_HIPLT22 25 #define R_SPARC_LOPLT10 26 #define R_SPARC_PCPLT32 27 #define R_SPARC_PCPLT22 28 #define R_SPARC_PCPLT10 29 #define R_SPARC_10 30 #define R_SPARC_11 31 #define R_SPARC_64 32 #define R_SPARC_OLO10 33 #define R_SPARC_HH22 34 #define R_SPARC_HM10 35 #define R_SPARC_LM22 36 #define R_SPARC_PC_HH22 37 #define R_SPARC_PC_HM10 38 #define R_SPARC_PC_LM22 39 #define R_SPARC_WDISP16 40 #define R_SPARC_WDISP19 41 #define R_SPARC_GLOB_JMP 42 #define R_SPARC_7 43 #define R_SPARC_5 44 #define R_SPARC_6 45 #define R_SPARC_DISP64 46 #define R_SPARC_PLT64 47 #define R_SPARC_HIX22 48 #define R_SPARC_LOX10 49 #define R_SPARC_H44 50 #define R_SPARC_M44 51 #define R_SPARC_L44 52 #define R_SPARC_REGISTER 53 #define R_SPARC_UA64 54 #define R_SPARC_UA16 55 #define R_SPARC_TLS_GD_HI22 56 #define R_SPARC_TLS_GD_LO10 57 #define R_SPARC_TLS_GD_ADD 58 #define R_SPARC_TLS_GD_CALL 59 #define R_SPARC_TLS_LDM_HI22 60 #define R_SPARC_TLS_LDM_LO10 61 #define R_SPARC_TLS_LDM_ADD 62 #define R_SPARC_TLS_LDM_CALL 63 #define R_SPARC_TLS_LDO_HIX22 64 #define R_SPARC_TLS_LDO_LOX10 65 #define R_SPARC_TLS_LDO_ADD 66 #define R_SPARC_TLS_IE_HI22 67 #define R_SPARC_TLS_IE_LO10 68 #define R_SPARC_TLS_IE_LD 69 #define R_SPARC_TLS_IE_LDX 70 #define R_SPARC_TLS_IE_ADD 71 #define R_SPARC_TLS_LE_HIX22 72 #define R_SPARC_TLS_LE_LOX10 73 #define R_SPARC_TLS_DTPMOD32 74 #define R_SPARC_TLS_DTPMOD64 75 #define R_SPARC_TLS_DTPOFF32 76 #define R_SPARC_TLS_DTPOFF64 77 #define R_SPARC_TLS_TPOFF32 78 #define R_SPARC_TLS_TPOFF64 79 #define R_X86_64_NONE 0 /* No relocation. */ #define R_X86_64_64 1 /* Add 64 bit symbol value. */ #define R_X86_64_PC32 2 /* PC-relative 32 bit signed sym value. */ #define R_X86_64_GOT32 3 /* PC-relative 32 bit GOT offset. */ #define R_X86_64_PLT32 4 /* PC-relative 32 bit PLT offset. */ #define R_X86_64_COPY 5 /* Copy data from shared object. */ #define R_X86_64_GLOB_DAT 6 /* Set GOT entry to data address. */ #define R_X86_64_JMP_SLOT 7 /* Set GOT entry to code address. */ #define R_X86_64_RELATIVE 8 /* Add load address of shared object. */ #define R_X86_64_GOTPCREL 9 /* Add 32 bit signed pcrel offset to GOT. */ #define R_X86_64_32 10 /* Add 32 bit zero extended symbol value */ #define R_X86_64_32S 11 /* Add 32 bit sign extended symbol value */ #define R_X86_64_16 12 /* Add 16 bit zero extended symbol value */ #define R_X86_64_PC16 13 /* Add 16 bit signed extended pc relative symbol value */ #define R_X86_64_8 14 /* Add 8 bit zero extended symbol value */ #define R_X86_64_PC8 15 /* Add 8 bit signed extended pc relative symbol value */ #define R_X86_64_DTPMOD64 16 /* ID of module containing symbol */ #define R_X86_64_DTPOFF64 17 /* Offset in TLS block */ #define R_X86_64_TPOFF64 18 /* Offset in static TLS block */ #define R_X86_64_TLSGD 19 /* PC relative offset to GD GOT entry */ #define R_X86_64_TLSLD 20 /* PC relative offset to LD GOT entry */ #define R_X86_64_DTPOFF32 21 /* Offset in TLS block */ #define R_X86_64_GOTTPOFF 22 /* PC relative offset to IE GOT entry */ #define R_X86_64_TPOFF32 23 /* Offset in static TLS block */ #define R_X86_64_IRELATIVE 37 #endif /* !_SYS_ELF_COMMON_H_ */ Index: head/sys/sys/imgact_elf.h =================================================================== --- head/sys/sys/imgact_elf.h (revision 274816) +++ head/sys/sys/imgact_elf.h (revision 274817) @@ -1,102 +1,103 @@ /*- * Copyright (c) 1995-1996 Søren Schmidt * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer * in this position and unchanged. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * $FreeBSD$ */ #ifndef _SYS_IMGACT_ELF_H_ #define _SYS_IMGACT_ELF_H_ #include #ifdef _KERNEL #define AUXARGS_ENTRY(pos, id, val) {suword(pos++, id); suword(pos++, val);} struct thread; /* * Structure used to pass infomation from the loader to the * stack fixup routine. */ typedef struct { Elf_Ssize execfd; Elf_Size phdr; Elf_Size phent; Elf_Size phnum; Elf_Size pagesz; Elf_Size base; Elf_Size flags; Elf_Size entry; } __ElfN(Auxargs); typedef struct { Elf_Note hdr; const char * vendor; int flags; boolean_t (*trans_osrel)(const Elf_Note *, int32_t *); #define BN_CAN_FETCH_OSREL 0x0001 /* Deprecated. */ #define BN_TRANSLATE_OSREL 0x0002 /* Use trans_osrel to fetch osrel */ /* after checking the image ABI specification, if needed. */ } Elf_Brandnote; typedef struct { int brand; int machine; const char *compat_3_brand; /* pre Binutils 2.10 method (FBSD 3) */ const char *emul_path; const char *interp_path; struct sysentvec *sysvec; const char *interp_newpath; int flags; Elf_Brandnote *brand_note; boolean_t (*header_supported)(struct image_params *); #define BI_CAN_EXEC_DYN 0x0001 #define BI_BRAND_NOTE 0x0002 /* May have note.ABI-tag section. */ #define BI_BRAND_NOTE_MANDATORY 0x0004 /* Must have note.ABI-tag section. */ } __ElfN(Brandinfo); __ElfType(Auxargs); __ElfType(Brandinfo); #define MAX_BRANDS 8 int __elfN(brand_inuse)(Elf_Brandinfo *entry); int __elfN(insert_brand_entry)(Elf_Brandinfo *entry); int __elfN(remove_brand_entry)(Elf_Brandinfo *entry); int __elfN(freebsd_fixup)(register_t **, struct image_params *); int __elfN(coredump)(struct thread *, struct vnode *, off_t, int); +size_t __elfN(populate_note)(int, void *, void *, size_t, void **); /* Machine specific function to dump per-thread information. */ void __elfN(dump_thread)(struct thread *, void *, size_t *); extern int __elfN(fallback_brand); extern Elf_Brandnote __elfN(freebsd_brandnote); extern Elf_Brandnote __elfN(kfreebsd_brandnote); #endif /* _KERNEL */ #endif /* !_SYS_IMGACT_ELF_H_ */ Index: head/sys/x86/include/fpu.h =================================================================== --- head/sys/x86/include/fpu.h (revision 274816) +++ head/sys/x86/include/fpu.h (revision 274817) @@ -1,218 +1,217 @@ /*- * Copyright (c) 1990 The Regents of the University of California. * All rights reserved. * * This code is derived from software contributed to Berkeley by * William Jolitz. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * from: @(#)npx.h 5.3 (Berkeley) 1/18/91 * $FreeBSD$ */ /* * Floating Point Data Structures and Constants * W. Jolitz 1/90 */ #ifndef _X86_FPU_H_ #define _X86_FPU_H_ /* Environment information of floating point unit. */ struct env87 { int32_t en_cw; /* control word (16bits) */ int32_t en_sw; /* status word (16bits) */ int32_t en_tw; /* tag word (16bits) */ int32_t en_fip; /* fp instruction pointer */ uint16_t en_fcs; /* fp code segment selector */ uint16_t en_opcode; /* opcode last executed (11 bits) */ int32_t en_foo; /* fp operand offset */ int32_t en_fos; /* fp operand segment selector */ }; /* Contents of each x87 floating point accumulator. */ struct fpacc87 { uint8_t fp_bytes[10]; }; /* Floating point context. (i386 fnsave/frstor) */ struct save87 { struct env87 sv_env; /* floating point control/status */ struct fpacc87 sv_ac[8]; /* accumulator contents, 0-7 */ uint8_t sv_pad0[4]; /* saved status word (now unused) */ - /* - * Bogus padding for emulators. Emulators should use their own - * struct and arrange to store into this struct (ending here) - * before it is inspected for ptracing or for core dumps. Some - * emulators overwrite the whole struct. We have no good way of - * knowing how much padding to leave. Leave just enough for the - * GPL emulator's i387_union (176 bytes total). - */ - uint8_t sv_pad[64]; /* padding; used by emulators */ + uint8_t sv_pad[64]; }; /* Contents of each SSE extended accumulator. */ struct xmmacc { uint8_t xmm_bytes[16]; }; /* Contents of the upper 16 bytes of each AVX extended accumulator. */ struct ymmacc { uint8_t ymm_bytes[16]; }; /* Rename structs below depending on machine architecture. */ #ifdef __i386__ #define __envxmm32 envxmm #else #define __envxmm32 envxmm32 #define __envxmm64 envxmm #endif struct __envxmm32 { uint16_t en_cw; /* control word (16bits) */ uint16_t en_sw; /* status word (16bits) */ uint16_t en_tw; /* tag word (16bits) */ uint16_t en_opcode; /* opcode last executed (11 bits) */ uint32_t en_fip; /* fp instruction pointer */ uint16_t en_fcs; /* fp code segment selector */ uint16_t en_pad0; /* padding */ uint32_t en_foo; /* fp operand offset */ uint16_t en_fos; /* fp operand segment selector */ uint16_t en_pad1; /* padding */ uint32_t en_mxcsr; /* SSE control/status register */ uint32_t en_mxcsr_mask; /* valid bits in mxcsr */ }; struct __envxmm64 { uint16_t en_cw; /* control word (16bits) */ uint16_t en_sw; /* status word (16bits) */ uint8_t en_tw; /* tag word (8bits) */ uint8_t en_zero; uint16_t en_opcode; /* opcode last executed (11 bits ) */ uint64_t en_rip; /* fp instruction pointer */ uint64_t en_rdp; /* fp operand pointer */ uint32_t en_mxcsr; /* SSE control/status register */ uint32_t en_mxcsr_mask; /* valid bits in mxcsr */ }; /* Floating point context. (i386 fxsave/fxrstor) */ struct savexmm { struct __envxmm32 sv_env; struct { struct fpacc87 fp_acc; uint8_t fp_pad[6]; /* padding */ } sv_fp[8]; struct xmmacc sv_xmm[8]; uint8_t sv_pad[224]; } __aligned(16); #ifdef __i386__ union savefpu { struct save87 sv_87; struct savexmm sv_xmm; }; #else /* Floating point context. (amd64 fxsave/fxrstor) */ struct savefpu { struct __envxmm64 sv_env; struct { struct fpacc87 fp_acc; uint8_t fp_pad[6]; /* padding */ } sv_fp[8]; struct xmmacc sv_xmm[16]; uint8_t sv_pad[96]; } __aligned(16); #endif struct xstate_hdr { uint64_t xstate_bv; uint64_t xstate_xcomp_bv; uint8_t xstate_rsrv0[8]; uint8_t xstate_rsrv[40]; }; #define XSTATE_XCOMP_BV_COMPACT (1ULL << 63) struct savexmm_xstate { struct xstate_hdr sx_hd; struct ymmacc sx_ymm[16]; }; struct savexmm_ymm { struct __envxmm32 sv_env; struct { struct fpacc87 fp_acc; int8_t fp_pad[6]; /* padding */ } sv_fp[8]; struct xmmacc sv_xmm[16]; uint8_t sv_pad[96]; struct savexmm_xstate sv_xstate; } __aligned(64); struct savefpu_xstate { struct xstate_hdr sx_hd; struct ymmacc sx_ymm[16]; }; struct savefpu_ymm { struct __envxmm64 sv_env; struct { struct fpacc87 fp_acc; int8_t fp_pad[6]; /* padding */ } sv_fp[8]; struct xmmacc sv_xmm[16]; uint8_t sv_pad[96]; struct savefpu_xstate sv_xstate; } __aligned(64); #undef __envxmm32 #undef __envxmm64 /* * The hardware default control word for i387's and later coprocessors is * 0x37F, giving: * * round to nearest * 64-bit precision * all exceptions masked. * * FreeBSD/i386 uses 53 bit precision for things like fadd/fsub/fsqrt etc * because of the difference between memory and fpu register stack arguments. * If its using an intermediate fpu register, it has 80/64 bits to work * with. If it uses memory, it has 64/53 bits to work with. However, * gcc is aware of this and goes to a fair bit of trouble to make the * best use of it. * * This is mostly academic for AMD64, because the ABI prefers the use * SSE2 based math. For FreeBSD/amd64, we go with the default settings. */ #define __INITIAL_FPUCW__ 0x037F #define __INITIAL_FPUCW_I386__ 0x127F #define __INITIAL_NPXCW__ __INITIAL_FPUCW_I386__ #define __INITIAL_MXCSR__ 0x1F80 #define __INITIAL_MXCSR_MASK__ 0xFFBF + +/* + * The current value of %xcr0 is saved in the sv_pad[] field of the FPU + * state in the NT_X86_XSTATE note in core dumps. This offset is chosen + * to match the offset used by NT_X86_XSTATE in other systems. + */ +#define X86_XSTATE_XCR0_OFFSET 464 #endif /* !_X86_FPU_H_ */ Index: head/sys/x86/include/ptrace.h =================================================================== --- head/sys/x86/include/ptrace.h (revision 274816) +++ head/sys/x86/include/ptrace.h (revision 274817) @@ -1,50 +1,61 @@ /*- * Copyright (c) 1992, 1993 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * @(#)ptrace.h 8.1 (Berkeley) 6/11/93 * $FreeBSD$ */ #ifndef _MACHINE_PTRACE_H_ #define _MACHINE_PTRACE_H_ #define __HAVE_PTRACE_MACHDEP /* * On amd64 (PT_FIRSTMACH + 0) and (PT_FIRSTMACH + 1) are old values for - * PT_GETXSTATE and PT_SETXSTATE. They should not be (re)used. + * PT_GETXSTATE_OLD and PT_SETXSTATE_OLD. They should not be (re)used. */ #ifdef __i386__ #define PT_GETXMMREGS (PT_FIRSTMACH + 0) #define PT_SETXMMREGS (PT_FIRSTMACH + 1) #endif -#define PT_GETXSTATE (PT_FIRSTMACH + 2) -#define PT_SETXSTATE (PT_FIRSTMACH + 3) +#ifdef _KERNEL +#define PT_GETXSTATE_OLD (PT_FIRSTMACH + 2) +#define PT_SETXSTATE_OLD (PT_FIRSTMACH + 3) +#endif +#define PT_GETXSTATE_INFO (PT_FIRSTMACH + 4) +#define PT_GETXSTATE (PT_FIRSTMACH + 5) +#define PT_SETXSTATE (PT_FIRSTMACH + 6) + +/* Argument structure for PT_GETXSTATE_INFO. */ +struct ptrace_xstate_info { + uint64_t xsave_mask; + uint32_t xsave_len; +}; #endif Index: head/usr.bin/gcore/elfcore.c =================================================================== --- head/usr.bin/gcore/elfcore.c (revision 274816) +++ head/usr.bin/gcore/elfcore.c (revision 274817) @@ -1,742 +1,777 @@ /*- * Copyright (c) 2007 Sandvine Incorporated * Copyright (c) 1998 John D. Polstra * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include +#include #include #include #include #include #include #include #include "extern.h" /* * Code for generating ELF core dumps. */ typedef void (*segment_callback)(vm_map_entry_t, void *); /* Closure for cb_put_phdr(). */ struct phdr_closure { Elf_Phdr *phdr; /* Program header to fill in */ Elf_Off offset; /* Offset of segment in core file */ }; /* Closure for cb_size_segment(). */ struct sseg_closure { int count; /* Count of writable segments. */ size_t size; /* Total size of all writable segments. */ }; #ifdef ELFCORE_COMPAT_32 typedef struct fpreg32 elfcore_fpregset_t; typedef struct reg32 elfcore_gregset_t; typedef struct prpsinfo32 elfcore_prpsinfo_t; typedef struct prstatus32 elfcore_prstatus_t; static void elf_convert_gregset(elfcore_gregset_t *rd, struct reg *rs); static void elf_convert_fpregset(elfcore_fpregset_t *rd, struct fpreg *rs); #else typedef fpregset_t elfcore_fpregset_t; typedef gregset_t elfcore_gregset_t; typedef prpsinfo_t elfcore_prpsinfo_t; typedef prstatus_t elfcore_prstatus_t; #define elf_convert_gregset(d,s) *d = *s #define elf_convert_fpregset(d,s) *d = *s #endif typedef void* (*notefunc_t)(void *, size_t *); static void cb_put_phdr(vm_map_entry_t, void *); static void cb_size_segment(vm_map_entry_t, void *); static void each_writable_segment(vm_map_entry_t, segment_callback, void *closure); static void elf_detach(void); /* atexit() handler. */ static void *elf_note_fpregset(void *, size_t *); static void *elf_note_prpsinfo(void *, size_t *); static void *elf_note_prstatus(void *, size_t *); static void *elf_note_thrmisc(void *, size_t *); +#if defined(__i386__) || defined(__amd64__) +static void *elf_note_x86_xstate(void *, size_t *); +#endif static void *elf_note_procstat_auxv(void *, size_t *); static void *elf_note_procstat_files(void *, size_t *); static void *elf_note_procstat_groups(void *, size_t *); static void *elf_note_procstat_osrel(void *, size_t *); static void *elf_note_procstat_proc(void *, size_t *); static void *elf_note_procstat_psstrings(void *, size_t *); static void *elf_note_procstat_rlimit(void *, size_t *); static void *elf_note_procstat_umask(void *, size_t *); static void *elf_note_procstat_vmmap(void *, size_t *); static void elf_puthdr(pid_t, vm_map_entry_t, void *, size_t, size_t, size_t, int); static void elf_putnote(int, notefunc_t, void *, struct sbuf *); static void elf_putnotes(pid_t, struct sbuf *, size_t *); static void freemap(vm_map_entry_t); static vm_map_entry_t readmap(pid_t); static void *procstat_sysctl(void *, int, size_t, size_t *sizep); static pid_t g_pid; /* Pid being dumped, global for elf_detach */ static int elf_ident(int efd, pid_t pid __unused, char *binfile __unused) { Elf_Ehdr hdr; int cnt; uint16_t machine; cnt = read(efd, &hdr, sizeof(hdr)); if (cnt != sizeof(hdr)) return (0); if (!IS_ELF(hdr)) return (0); switch (hdr.e_ident[EI_DATA]) { case ELFDATA2LSB: machine = le16toh(hdr.e_machine); break; case ELFDATA2MSB: machine = be16toh(hdr.e_machine); break; default: return (0); } if (!ELF_MACHINE_OK(machine)) return (0); /* Looks good. */ return (1); } static void elf_detach(void) { if (g_pid != 0) ptrace(PT_DETACH, g_pid, (caddr_t)1, 0); } /* * Write an ELF coredump for the given pid to the given fd. */ static void elf_coredump(int efd __unused, int fd, pid_t pid) { vm_map_entry_t map; struct sseg_closure seginfo; struct sbuf *sb; void *hdr; size_t hdrsize, notesz, segoff; ssize_t n, old_len; Elf_Phdr *php; int i; /* Attach to process to dump. */ g_pid = pid; if (atexit(elf_detach) != 0) err(1, "atexit"); errno = 0; ptrace(PT_ATTACH, pid, NULL, 0); if (errno) err(1, "PT_ATTACH"); if (waitpid(pid, NULL, 0) == -1) err(1, "waitpid"); /* Get the program's memory map. */ map = readmap(pid); /* Size the program segments. */ seginfo.count = 0; seginfo.size = 0; each_writable_segment(map, cb_size_segment, &seginfo); /* * Build the header and the notes using sbuf and write to the file. */ sb = sbuf_new_auto(); hdrsize = sizeof(Elf_Ehdr) + sizeof(Elf_Phdr) * (1 + seginfo.count); /* Start header + notes section. */ sbuf_start_section(sb, NULL); /* Make empty header subsection. */ sbuf_start_section(sb, &old_len); sbuf_putc(sb, 0); sbuf_end_section(sb, old_len, hdrsize, 0); /* Put notes. */ elf_putnotes(pid, sb, ¬esz); /* Align up to a page boundary for the program segments. */ sbuf_end_section(sb, -1, PAGE_SIZE, 0); if (sbuf_finish(sb) != 0) err(1, "sbuf_finish"); hdr = sbuf_data(sb); segoff = sbuf_len(sb); /* Fill in the header. */ elf_puthdr(pid, map, hdr, hdrsize, notesz, segoff, seginfo.count); n = write(fd, hdr, segoff); if (n == -1) err(1, "write"); if (n < segoff) errx(1, "short write"); /* Write the contents of all of the writable segments. */ php = (Elf_Phdr *)((char *)hdr + sizeof(Elf_Ehdr)) + 1; for (i = 0; i < seginfo.count; i++) { struct ptrace_io_desc iorequest; uintmax_t nleft = php->p_filesz; iorequest.piod_op = PIOD_READ_D; iorequest.piod_offs = (caddr_t)(uintptr_t)php->p_vaddr; while (nleft > 0) { char buf[8*1024]; size_t nwant; ssize_t ngot; if (nleft > sizeof(buf)) nwant = sizeof buf; else nwant = nleft; iorequest.piod_addr = buf; iorequest.piod_len = nwant; ptrace(PT_IO, pid, (caddr_t)&iorequest, 0); ngot = iorequest.piod_len; if ((size_t)ngot < nwant) errx(1, "short read wanted %zu, got %zd", nwant, ngot); ngot = write(fd, buf, nwant); if (ngot == -1) err(1, "write of segment %d failed", i); if ((size_t)ngot != nwant) errx(1, "short write"); nleft -= nwant; iorequest.piod_offs += ngot; } php++; } sbuf_delete(sb); freemap(map); } /* * A callback for each_writable_segment() to write out the segment's * program header entry. */ static void cb_put_phdr(vm_map_entry_t entry, void *closure) { struct phdr_closure *phc = (struct phdr_closure *)closure; Elf_Phdr *phdr = phc->phdr; phc->offset = round_page(phc->offset); phdr->p_type = PT_LOAD; phdr->p_offset = phc->offset; phdr->p_vaddr = entry->start; phdr->p_paddr = 0; phdr->p_filesz = phdr->p_memsz = entry->end - entry->start; phdr->p_align = PAGE_SIZE; phdr->p_flags = 0; if (entry->protection & VM_PROT_READ) phdr->p_flags |= PF_R; if (entry->protection & VM_PROT_WRITE) phdr->p_flags |= PF_W; if (entry->protection & VM_PROT_EXECUTE) phdr->p_flags |= PF_X; phc->offset += phdr->p_filesz; phc->phdr++; } /* * A callback for each_writable_segment() to gather information about * the number of segments and their total size. */ static void cb_size_segment(vm_map_entry_t entry, void *closure) { struct sseg_closure *ssc = (struct sseg_closure *)closure; ssc->count++; ssc->size += entry->end - entry->start; } /* * For each segment in the given memory map, call the given function * with a pointer to the map entry and some arbitrary caller-supplied * data. */ static void each_writable_segment(vm_map_entry_t map, segment_callback func, void *closure) { vm_map_entry_t entry; for (entry = map; entry != NULL; entry = entry->next) (*func)(entry, closure); } static void elf_putnotes(pid_t pid, struct sbuf *sb, size_t *sizep) { lwpid_t *tids; size_t threads, old_len; ssize_t size; int i; errno = 0; threads = ptrace(PT_GETNUMLWPS, pid, NULL, 0); if (errno) err(1, "PT_GETNUMLWPS"); tids = malloc(threads * sizeof(*tids)); if (tids == NULL) errx(1, "out of memory"); errno = 0; ptrace(PT_GETLWPLIST, pid, (void *)tids, threads); if (errno) err(1, "PT_GETLWPLIST"); sbuf_start_section(sb, &old_len); elf_putnote(NT_PRPSINFO, elf_note_prpsinfo, &pid, sb); for (i = 0; i < threads; ++i) { elf_putnote(NT_PRSTATUS, elf_note_prstatus, tids + i, sb); elf_putnote(NT_FPREGSET, elf_note_fpregset, tids + i, sb); elf_putnote(NT_THRMISC, elf_note_thrmisc, tids + i, sb); +#if defined(__i386__) || defined(__amd64__) + elf_putnote(NT_X86_XSTATE, elf_note_x86_xstate, tids + i, sb); +#endif } #ifndef ELFCORE_COMPAT_32 elf_putnote(NT_PROCSTAT_PROC, elf_note_procstat_proc, &pid, sb); elf_putnote(NT_PROCSTAT_FILES, elf_note_procstat_files, &pid, sb); elf_putnote(NT_PROCSTAT_VMMAP, elf_note_procstat_vmmap, &pid, sb); elf_putnote(NT_PROCSTAT_GROUPS, elf_note_procstat_groups, &pid, sb); elf_putnote(NT_PROCSTAT_UMASK, elf_note_procstat_umask, &pid, sb); elf_putnote(NT_PROCSTAT_RLIMIT, elf_note_procstat_rlimit, &pid, sb); elf_putnote(NT_PROCSTAT_OSREL, elf_note_procstat_osrel, &pid, sb); elf_putnote(NT_PROCSTAT_PSSTRINGS, elf_note_procstat_psstrings, &pid, sb); elf_putnote(NT_PROCSTAT_AUXV, elf_note_procstat_auxv, &pid, sb); #endif size = sbuf_end_section(sb, old_len, 1, 0); if (size == -1) err(1, "sbuf_end_section"); free(tids); *sizep = size; } /* * Emit one note section to sbuf. */ static void elf_putnote(int type, notefunc_t notefunc, void *arg, struct sbuf *sb) { Elf_Note note; size_t descsz; ssize_t old_len; void *desc; desc = notefunc(arg, &descsz); note.n_namesz = 8; /* strlen("FreeBSD") + 1 */ note.n_descsz = descsz; note.n_type = type; sbuf_bcat(sb, ¬e, sizeof(note)); sbuf_start_section(sb, &old_len); sbuf_bcat(sb, "FreeBSD", note.n_namesz); sbuf_end_section(sb, old_len, sizeof(Elf32_Size), 0); if (descsz == 0) return; sbuf_start_section(sb, &old_len); sbuf_bcat(sb, desc, descsz); sbuf_end_section(sb, old_len, sizeof(Elf32_Size), 0); free(desc); } /* * Generate the ELF coredump header. */ static void elf_puthdr(pid_t pid, vm_map_entry_t map, void *hdr, size_t hdrsize, size_t notesz, size_t segoff, int numsegs) { Elf_Ehdr *ehdr; Elf_Phdr *phdr; struct phdr_closure phc; ehdr = (Elf_Ehdr *)hdr; phdr = (Elf_Phdr *)((char *)hdr + sizeof(Elf_Ehdr)); ehdr->e_ident[EI_MAG0] = ELFMAG0; ehdr->e_ident[EI_MAG1] = ELFMAG1; ehdr->e_ident[EI_MAG2] = ELFMAG2; ehdr->e_ident[EI_MAG3] = ELFMAG3; ehdr->e_ident[EI_CLASS] = ELF_CLASS; ehdr->e_ident[EI_DATA] = ELF_DATA; ehdr->e_ident[EI_VERSION] = EV_CURRENT; ehdr->e_ident[EI_OSABI] = ELFOSABI_FREEBSD; ehdr->e_ident[EI_ABIVERSION] = 0; ehdr->e_ident[EI_PAD] = 0; ehdr->e_type = ET_CORE; ehdr->e_machine = ELF_ARCH; ehdr->e_version = EV_CURRENT; ehdr->e_entry = 0; ehdr->e_phoff = sizeof(Elf_Ehdr); ehdr->e_flags = 0; ehdr->e_ehsize = sizeof(Elf_Ehdr); ehdr->e_phentsize = sizeof(Elf_Phdr); ehdr->e_phnum = numsegs + 1; ehdr->e_shentsize = sizeof(Elf_Shdr); ehdr->e_shnum = 0; ehdr->e_shstrndx = SHN_UNDEF; /* * Fill in the program header entries. */ /* The note segement. */ phdr->p_type = PT_NOTE; phdr->p_offset = hdrsize; phdr->p_vaddr = 0; phdr->p_paddr = 0; phdr->p_filesz = notesz; phdr->p_memsz = 0; phdr->p_flags = PF_R; phdr->p_align = sizeof(Elf32_Size); phdr++; /* All the writable segments from the program. */ phc.phdr = phdr; phc.offset = segoff; each_writable_segment(map, cb_put_phdr, &phc); } /* * Free the memory map. */ static void freemap(vm_map_entry_t map) { while (map != NULL) { vm_map_entry_t next = map->next; free(map); map = next; } } /* * Read the process's memory map using kinfo_getvmmap(), and return a list of * VM map entries. Only the non-device read/writable segments are * returned. The map entries in the list aren't fully filled in; only * the items we need are present. */ static vm_map_entry_t readmap(pid_t pid) { vm_map_entry_t ent, *linkp, map; struct kinfo_vmentry *vmentl, *kve; int i, nitems; vmentl = kinfo_getvmmap(pid, &nitems); if (vmentl == NULL) err(1, "cannot retrieve mappings for %u process", pid); map = NULL; linkp = ↦ for (i = 0; i < nitems; i++) { kve = &vmentl[i]; /* * Ignore 'malformed' segments or ones representing memory * mapping with MAP_NOCORE on. * If the 'full' support is disabled, just dump the most * meaningful data segments. */ if ((kve->kve_protection & KVME_PROT_READ) == 0 || (kve->kve_flags & KVME_FLAG_NOCOREDUMP) != 0 || kve->kve_type == KVME_TYPE_DEAD || kve->kve_type == KVME_TYPE_UNKNOWN || ((pflags & PFLAGS_FULL) == 0 && kve->kve_type != KVME_TYPE_DEFAULT && kve->kve_type != KVME_TYPE_VNODE && kve->kve_type != KVME_TYPE_SWAP)) continue; ent = calloc(1, sizeof(*ent)); if (ent == NULL) errx(1, "out of memory"); ent->start = (vm_offset_t)kve->kve_start; ent->end = (vm_offset_t)kve->kve_end; ent->protection = VM_PROT_READ | VM_PROT_WRITE; if ((kve->kve_protection & KVME_PROT_EXEC) != 0) ent->protection |= VM_PROT_EXECUTE; *linkp = ent; linkp = &ent->next; } free(vmentl); return (map); } /* * Miscellaneous note out functions. */ static void * elf_note_prpsinfo(void *arg, size_t *sizep) { pid_t pid; elfcore_prpsinfo_t *psinfo; struct kinfo_proc kip; size_t len; int name[4]; pid = *(pid_t *)arg; psinfo = calloc(1, sizeof(*psinfo)); if (psinfo == NULL) errx(1, "out of memory"); psinfo->pr_version = PRPSINFO_VERSION; psinfo->pr_psinfosz = sizeof(*psinfo); name[0] = CTL_KERN; name[1] = KERN_PROC; name[2] = KERN_PROC_PID; name[3] = pid; len = sizeof(kip); if (sysctl(name, 4, &kip, &len, NULL, 0) == -1) err(1, "kern.proc.pid.%u", pid); if (kip.ki_pid != pid) err(1, "kern.proc.pid.%u", pid); strncpy(psinfo->pr_fname, kip.ki_comm, MAXCOMLEN); strncpy(psinfo->pr_psargs, psinfo->pr_fname, PRARGSZ); *sizep = sizeof(*psinfo); return (psinfo); } static void * elf_note_prstatus(void *arg, size_t *sizep) { lwpid_t tid; elfcore_prstatus_t *status; struct reg greg; tid = *(lwpid_t *)arg; status = calloc(1, sizeof(*status)); if (status == NULL) errx(1, "out of memory"); status->pr_version = PRSTATUS_VERSION; status->pr_statussz = sizeof(*status); status->pr_gregsetsz = sizeof(elfcore_gregset_t); status->pr_fpregsetsz = sizeof(elfcore_fpregset_t); status->pr_osreldate = __FreeBSD_version; status->pr_pid = tid; ptrace(PT_GETREGS, tid, (void *)&greg, 0); elf_convert_gregset(&status->pr_reg, &greg); *sizep = sizeof(*status); return (status); } static void * elf_note_fpregset(void *arg, size_t *sizep) { lwpid_t tid; elfcore_fpregset_t *fpregset; fpregset_t fpreg; tid = *(lwpid_t *)arg; fpregset = calloc(1, sizeof(*fpregset)); if (fpregset == NULL) errx(1, "out of memory"); ptrace(PT_GETFPREGS, tid, (void *)&fpreg, 0); elf_convert_fpregset(fpregset, &fpreg); *sizep = sizeof(*fpregset); return (fpregset); } static void * elf_note_thrmisc(void *arg, size_t *sizep) { lwpid_t tid; struct ptrace_lwpinfo lwpinfo; thrmisc_t *thrmisc; tid = *(lwpid_t *)arg; thrmisc = calloc(1, sizeof(*thrmisc)); if (thrmisc == NULL) errx(1, "out of memory"); ptrace(PT_LWPINFO, tid, (void *)&lwpinfo, sizeof(lwpinfo)); memset(&thrmisc->_pad, 0, sizeof(thrmisc->_pad)); strcpy(thrmisc->pr_tname, lwpinfo.pl_tdname); *sizep = sizeof(*thrmisc); return (thrmisc); } + +#if defined(__i386__) || defined(__amd64__) +static void * +elf_note_x86_xstate(void *arg, size_t *sizep) +{ + lwpid_t tid; + char *xstate; + static bool xsave_checked = false; + static struct ptrace_xstate_info info; + + tid = *(lwpid_t *)arg; + if (!xsave_checked) { + if (ptrace(PT_GETXSTATE_INFO, tid, (void *)&info, + sizeof(info)) != 0) + info.xsave_len = 0; + xsave_checked = true; + } + if (info.xsave_len == 0) { + *sizep = 0; + return (NULL); + } + xstate = calloc(1, info.xsave_len); + ptrace(PT_GETXSTATE, tid, xstate, 0); + *(uint64_t *)(xstate + X86_XSTATE_XCR0_OFFSET) = info.xsave_mask; + *sizep = info.xsave_len; + return (xstate); +} +#endif static void * procstat_sysctl(void *arg, int what, size_t structsz, size_t *sizep) { size_t len; pid_t pid; int name[4], structsize; void *buf, *p; pid = *(pid_t *)arg; structsize = structsz; name[0] = CTL_KERN; name[1] = KERN_PROC; name[2] = what; name[3] = pid; len = 0; if (sysctl(name, 4, NULL, &len, NULL, 0) == -1) err(1, "kern.proc.%d.%u", what, pid); buf = calloc(1, sizeof(structsize) + len * 4 / 3); if (buf == NULL) errx(1, "out of memory"); bcopy(&structsize, buf, sizeof(structsize)); p = (char *)buf + sizeof(structsize); if (sysctl(name, 4, p, &len, NULL, 0) == -1) err(1, "kern.proc.%d.%u", what, pid); *sizep = sizeof(structsize) + len; return (buf); } static void * elf_note_procstat_proc(void *arg, size_t *sizep) { return (procstat_sysctl(arg, KERN_PROC_PID | KERN_PROC_INC_THREAD, sizeof(struct kinfo_proc), sizep)); } static void * elf_note_procstat_files(void *arg, size_t *sizep) { return (procstat_sysctl(arg, KERN_PROC_FILEDESC, sizeof(struct kinfo_file), sizep)); } static void * elf_note_procstat_vmmap(void *arg, size_t *sizep) { return (procstat_sysctl(arg, KERN_PROC_VMMAP, sizeof(struct kinfo_vmentry), sizep)); } static void * elf_note_procstat_groups(void *arg, size_t *sizep) { return (procstat_sysctl(arg, KERN_PROC_GROUPS, sizeof(gid_t), sizep)); } static void * elf_note_procstat_umask(void *arg, size_t *sizep) { return (procstat_sysctl(arg, KERN_PROC_UMASK, sizeof(u_short), sizep)); } static void * elf_note_procstat_osrel(void *arg, size_t *sizep) { return (procstat_sysctl(arg, KERN_PROC_OSREL, sizeof(int), sizep)); } static void * elf_note_procstat_psstrings(void *arg, size_t *sizep) { return (procstat_sysctl(arg, KERN_PROC_PS_STRINGS, sizeof(vm_offset_t), sizep)); } static void * elf_note_procstat_auxv(void *arg, size_t *sizep) { return (procstat_sysctl(arg, KERN_PROC_AUXV, sizeof(Elf_Auxinfo), sizep)); } static void * elf_note_procstat_rlimit(void *arg, size_t *sizep) { pid_t pid; size_t len; int i, name[5], structsize; void *buf, *p; pid = *(pid_t *)arg; structsize = sizeof(struct rlimit) * RLIM_NLIMITS; buf = calloc(1, sizeof(structsize) + structsize); if (buf == NULL) errx(1, "out of memory"); bcopy(&structsize, buf, sizeof(structsize)); p = (char *)buf + sizeof(structsize); name[0] = CTL_KERN; name[1] = KERN_PROC; name[2] = KERN_PROC_RLIMIT; name[3] = pid; len = sizeof(struct rlimit); for (i = 0; i < RLIM_NLIMITS; i++) { name[4] = i; if (sysctl(name, 5, p, &len, NULL, 0) == -1) err(1, "kern.proc.rlimit.%u", pid); if (len != sizeof(struct rlimit)) errx(1, "kern.proc.rlimit.%u: short read", pid); p += len; } *sizep = sizeof(structsize) + structsize; return (buf); } struct dumpers __elfN(dump) = { elf_ident, elf_coredump }; TEXT_SET(dumpset, __elfN(dump));