|  | /* xcoff.c -- Get debug data from an XCOFF file for backtraces. | 
|  | Copyright (C) 2012-2024 Free Software Foundation, Inc. | 
|  | Adapted from elf.c. | 
|  |  | 
|  | 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. | 
|  |  | 
|  | (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 "config.h" | 
|  |  | 
|  | #include <errno.h> | 
|  | #include <stdlib.h> | 
|  | #include <string.h> | 
|  | #include <sys/types.h> | 
|  |  | 
|  | #ifdef HAVE_LOADQUERY | 
|  | #include <sys/ldr.h> | 
|  | #endif | 
|  |  | 
|  | #include "backtrace.h" | 
|  | #include "internal.h" | 
|  |  | 
|  | /* The configure script must tell us whether we are 32-bit or 64-bit | 
|  | XCOFF.  We could make this code test and support either possibility, | 
|  | but there is no point.  This code only works for the currently | 
|  | running executable, which means that we know the XCOFF mode at | 
|  | configure time.  */ | 
|  |  | 
|  | #if BACKTRACE_XCOFF_SIZE != 32 && BACKTRACE_XCOFF_SIZE != 64 | 
|  | #error "Unknown BACKTRACE_XCOFF_SIZE" | 
|  | #endif | 
|  |  | 
|  | /* XCOFF file header.  */ | 
|  |  | 
|  | #if BACKTRACE_XCOFF_SIZE == 32 | 
|  |  | 
|  | typedef struct { | 
|  | uint16_t f_magic; | 
|  | uint16_t f_nscns; | 
|  | uint32_t f_timdat; | 
|  | uint32_t f_symptr; | 
|  | uint32_t f_nsyms; | 
|  | uint16_t f_opthdr; | 
|  | uint16_t f_flags; | 
|  | } b_xcoff_filhdr; | 
|  |  | 
|  | #define XCOFF_MAGIC	0737 | 
|  |  | 
|  | #else /* BACKTRACE_XCOFF_SIZE != 32 */ | 
|  |  | 
|  | typedef struct { | 
|  | uint16_t f_magic; | 
|  | uint16_t f_nscns; | 
|  | uint32_t f_timdat; | 
|  | uint64_t f_symptr; | 
|  | uint16_t f_opthdr; | 
|  | uint16_t f_flags; | 
|  | uint32_t f_nsyms; | 
|  | } b_xcoff_filhdr; | 
|  |  | 
|  | #define XCOFF_MAGIC	0767 | 
|  |  | 
|  | #endif /* BACKTRACE_XCOFF_SIZE != 32 */ | 
|  |  | 
|  | #define F_SHROBJ	0x2000	/* File is a shared object.  */ | 
|  |  | 
|  | /* XCOFF section header.  */ | 
|  |  | 
|  | #if BACKTRACE_XCOFF_SIZE == 32 | 
|  |  | 
|  | typedef struct { | 
|  | char s_name[8]; | 
|  | uint32_t s_paddr; | 
|  | uint32_t s_vaddr; | 
|  | uint32_t s_size; | 
|  | uint32_t s_scnptr; | 
|  | uint32_t s_relptr; | 
|  | uint32_t s_lnnoptr; | 
|  | uint16_t s_nreloc; | 
|  | uint16_t s_nlnno; | 
|  | uint32_t s_flags; | 
|  | } b_xcoff_scnhdr; | 
|  |  | 
|  | #define _OVERFLOW_MARKER	65535 | 
|  |  | 
|  | #else /* BACKTRACE_XCOFF_SIZE != 32 */ | 
|  |  | 
|  | typedef struct { | 
|  | char name[8]; | 
|  | uint64_t s_paddr; | 
|  | uint64_t s_vaddr; | 
|  | uint64_t s_size; | 
|  | uint64_t s_scnptr; | 
|  | uint64_t s_relptr; | 
|  | uint64_t s_lnnoptr; | 
|  | uint32_t s_nreloc; | 
|  | uint32_t s_nlnno; | 
|  | uint32_t s_flags; | 
|  | } b_xcoff_scnhdr; | 
|  |  | 
|  | #endif /* BACKTRACE_XCOFF_SIZE != 32 */ | 
|  |  | 
|  | #define STYP_DWARF	0x10	/* DWARF debugging section.  */ | 
|  | #define STYP_TEXT	0x20	/* Executable text (code) section.  */ | 
|  | #define STYP_OVRFLO	0x8000	/* Line-number field overflow section.  */ | 
|  |  | 
|  | #define SSUBTYP_DWINFO	0x10000	/* DWARF info section.  */ | 
|  | #define SSUBTYP_DWLINE	0x20000	/* DWARF line-number section.  */ | 
|  | #define SSUBTYP_DWARNGE	0x50000	/* DWARF aranges section.  */ | 
|  | #define SSUBTYP_DWABREV	0x60000	/* DWARF abbreviation section.  */ | 
|  | #define SSUBTYP_DWSTR	0x70000	/* DWARF strings section.  */ | 
|  | #define SSUBTYP_DWRNGES	0x80000	/* DWARF ranges section.  */ | 
|  |  | 
|  | /* XCOFF symbol.  */ | 
|  |  | 
|  | #define SYMNMLEN	8 | 
|  |  | 
|  | #if BACKTRACE_XCOFF_SIZE == 32 | 
|  |  | 
|  | typedef struct { | 
|  | union { | 
|  | char _name[SYMNMLEN]; | 
|  | struct { | 
|  | uint32_t _zeroes; | 
|  | uint32_t _offset; | 
|  | } _s; | 
|  | } _u; | 
|  | #define n_name		_u._name | 
|  | #define n_zeroes	_u._s._zeroes | 
|  | #define n_offset_	_u._s._offset | 
|  |  | 
|  | uint32_t n_value; | 
|  | int16_t  n_scnum; | 
|  | uint16_t n_type; | 
|  | uint8_t  n_sclass; | 
|  | uint8_t  n_numaux; | 
|  | } __attribute__ ((packed)) b_xcoff_syment; | 
|  |  | 
|  | #else /* BACKTRACE_XCOFF_SIZE != 32 */ | 
|  |  | 
|  | typedef struct { | 
|  | uint64_t n_value; | 
|  | uint32_t n_offset_; | 
|  | int16_t  n_scnum; | 
|  | uint16_t n_type; | 
|  | uint8_t  n_sclass; | 
|  | uint8_t  n_numaux; | 
|  | } __attribute__ ((packed)) b_xcoff_syment; | 
|  |  | 
|  | #endif /* BACKTRACE_XCOFF_SIZE != 32 */ | 
|  |  | 
|  | #define SYMESZ	18 | 
|  |  | 
|  | #define C_EXT		2	/* External symbol.  */ | 
|  | #define C_FCN		101	/* Beginning or end of function.  */ | 
|  | #define C_FILE		103	/* Source file name.  */ | 
|  | #define C_HIDEXT	107	/* Unnamed external symbol.  */ | 
|  | #define C_BINCL		108	/* Beginning of include file.  */ | 
|  | #define C_EINCL		109	/* End of include file.  */ | 
|  | #define C_WEAKEXT	111	/* Weak external symbol.  */ | 
|  |  | 
|  | #define ISFCN(x)	((x) & 0x0020) | 
|  |  | 
|  | /* XCOFF AUX entry.  */ | 
|  |  | 
|  | #define AUXESZ		18 | 
|  | #define FILNMLEN	14 | 
|  |  | 
|  | typedef union { | 
|  | #if BACKTRACE_XCOFF_SIZE == 32 | 
|  | struct { | 
|  | uint16_t pad; | 
|  | uint16_t x_lnnohi; | 
|  | uint16_t x_lnno; | 
|  | } x_block; | 
|  | #else | 
|  | struct { | 
|  | uint32_t x_lnno; | 
|  | } x_block; | 
|  | #endif | 
|  | union { | 
|  | char x_fname[FILNMLEN]; | 
|  | struct { | 
|  | uint32_t x_zeroes; | 
|  | uint32_t x_offset; | 
|  | char     pad[FILNMLEN-8]; | 
|  | uint8_t  x_ftype; | 
|  | } _x; | 
|  | } x_file; | 
|  | #if BACKTRACE_XCOFF_SIZE == 32 | 
|  | struct { | 
|  | uint32_t x_exptr; | 
|  | uint32_t x_fsize; | 
|  | uint32_t x_lnnoptr; | 
|  | uint32_t x_endndx; | 
|  | } x_fcn; | 
|  | #else | 
|  | struct { | 
|  | uint64_t x_lnnoptr; | 
|  | uint32_t x_fsize; | 
|  | uint32_t x_endndx; | 
|  | } x_fcn; | 
|  | #endif | 
|  | struct { | 
|  | uint8_t pad[AUXESZ-1]; | 
|  | uint8_t x_auxtype; | 
|  | } x_auxtype; | 
|  | } __attribute__ ((packed)) b_xcoff_auxent; | 
|  |  | 
|  | /* XCOFF line number entry.  */ | 
|  |  | 
|  | #if BACKTRACE_XCOFF_SIZE == 32 | 
|  |  | 
|  | typedef struct { | 
|  | union { | 
|  | uint32_t l_symndx; | 
|  | uint32_t l_paddr; | 
|  | } l_addr; | 
|  | uint16_t l_lnno; | 
|  | } b_xcoff_lineno; | 
|  |  | 
|  | #define LINESZ	6 | 
|  |  | 
|  | #else /* BACKTRACE_XCOFF_SIZE != 32 */ | 
|  |  | 
|  | typedef struct { | 
|  | union { | 
|  | uint32_t l_symndx; | 
|  | uint64_t l_paddr; | 
|  | } l_addr; | 
|  | uint32_t l_lnno; | 
|  | } b_xcoff_lineno; | 
|  |  | 
|  | #define LINESZ	12 | 
|  |  | 
|  | #endif /* BACKTRACE_XCOFF_SIZE != 32 */ | 
|  |  | 
|  | #if BACKTRACE_XCOFF_SIZE == 32 | 
|  | #define XCOFF_AIX_TEXTBASE	0x10000000u | 
|  | #else | 
|  | #define XCOFF_AIX_TEXTBASE	0x100000000ul | 
|  | #endif | 
|  |  | 
|  | /* AIX big archive fixed-length header.  */ | 
|  |  | 
|  | #define AIAMAGBIG	"<bigaf>\n" | 
|  |  | 
|  | typedef struct { | 
|  | char fl_magic[8];	/* Archive magic string.  */ | 
|  | char fl_memoff[20];	/* Offset to member table.  */ | 
|  | char fl_gstoff[20];	/* Offset to global symbol table.  */ | 
|  | char fl_gst64off[20];	/* Offset to global symbol table for 64-bit objects.  */ | 
|  | char fl_fstmoff[20];	/* Offset to first archive member.  */ | 
|  | char fl_freeoff[20];	/* Offset to first member on free list.  */ | 
|  | } b_ar_fl_hdr; | 
|  |  | 
|  | /* AIX big archive file member header.  */ | 
|  |  | 
|  | typedef struct { | 
|  | char ar_size[20];	/* File member size - decimal.  */ | 
|  | char ar_nxtmem[20];	/* Next member offset - decimal.  */ | 
|  | char ar_prvmem[20];	/* Previous member offset - decimal.  */ | 
|  | char ar_date[12];	/* File member date - decimal.  */ | 
|  | char ar_uid[12];	/* File member userid - decimal.  */ | 
|  | char ar_gid[12];	/* File member group id - decimal.  */ | 
|  | char ar_mode[12];	/* File member mode - octal.  */ | 
|  | char ar_namlen[4];	/* File member name length - decimal.  */ | 
|  | char ar_name[2];	/* Start of member name.  */ | 
|  | } b_ar_hdr; | 
|  |  | 
|  |  | 
|  | /* Information we keep for an XCOFF symbol.  */ | 
|  |  | 
|  | struct xcoff_symbol | 
|  | { | 
|  | /* The name of the symbol.  */ | 
|  | const char *name; | 
|  | /* The address of the symbol.  */ | 
|  | uintptr_t address; | 
|  | /* The size of the symbol.  */ | 
|  | size_t size; | 
|  | }; | 
|  |  | 
|  | /* Information to pass to xcoff_syminfo.  */ | 
|  |  | 
|  | struct xcoff_syminfo_data | 
|  | { | 
|  | /* Symbols for the next module.  */ | 
|  | struct xcoff_syminfo_data *next; | 
|  | /* The XCOFF symbols, sorted by address.  */ | 
|  | struct xcoff_symbol *symbols; | 
|  | /* The number of symbols.  */ | 
|  | size_t count; | 
|  | }; | 
|  |  | 
|  | /* Information about an include file.  */ | 
|  |  | 
|  | struct xcoff_incl | 
|  | { | 
|  | /* File name.  */ | 
|  | const char *filename; | 
|  | /* Offset to first line number from the include file.  */ | 
|  | uintptr_t begin; | 
|  | /* Offset to last line number from the include file.  */ | 
|  | uintptr_t end; | 
|  | }; | 
|  |  | 
|  | /* A growable vector of include files information.  */ | 
|  |  | 
|  | struct xcoff_incl_vector | 
|  | { | 
|  | /* Memory.  This is an array of struct xcoff_incl.  */ | 
|  | struct backtrace_vector vec; | 
|  | /* Number of include files.  */ | 
|  | size_t count; | 
|  | }; | 
|  |  | 
|  | /* A growable vector of functions information.  */ | 
|  |  | 
|  | struct xcoff_func | 
|  | { | 
|  | /* PC.  */ | 
|  | uintptr_t pc; | 
|  | /* The size of the function.  */ | 
|  | size_t size; | 
|  | /* Function name.  */ | 
|  | const char *name; | 
|  | /* File name.  */ | 
|  | const char *filename; | 
|  | /* Pointer to first lnno entry.  */ | 
|  | uintptr_t lnnoptr; | 
|  | /* Base address of containing section.  */ | 
|  | uintptr_t sect_base; | 
|  | /* Starting source line number.  */ | 
|  | int lnno; | 
|  | }; | 
|  |  | 
|  | /* A growable vector of function information.  This is used while | 
|  | reading the function symbols.  */ | 
|  |  | 
|  | struct xcoff_func_vector | 
|  | { | 
|  | /* Memory.  This is an array of struct xcoff_func.  */ | 
|  | struct backtrace_vector vec; | 
|  | /* Number of valid mappings.  */ | 
|  | size_t count; | 
|  | }; | 
|  |  | 
|  | /* The information we need to map a PC to a file and line.  */ | 
|  |  | 
|  | struct xcoff_fileline_data | 
|  | { | 
|  | /* The data for the next file we know about.  */ | 
|  | struct xcoff_fileline_data *next; | 
|  | /* Functions information.  */ | 
|  | struct xcoff_func_vector func_vec; | 
|  | /* Include files information.  */ | 
|  | struct xcoff_incl_vector incl_vec; | 
|  | /* Line numbers information.  */ | 
|  | const unsigned char *linenos; | 
|  | size_t linenos_size; | 
|  | uint64_t lnnoptr0; | 
|  | /* Loader address.  */ | 
|  | uintptr_t base_address; | 
|  | }; | 
|  |  | 
|  | /* Information we gather for the DWARF sections we care about.  */ | 
|  |  | 
|  | struct dwsect_info | 
|  | { | 
|  | /* Section file offset.  */ | 
|  | off_t offset; | 
|  | /* Section size.  */ | 
|  | size_t size; | 
|  | /* Section contents, after read from file.  */ | 
|  | const unsigned char *data; | 
|  | }; | 
|  |  | 
|  | /* A dummy callback function used when we can't find any debug info.  */ | 
|  |  | 
|  | static int | 
|  | xcoff_nodebug (struct backtrace_state *state ATTRIBUTE_UNUSED, | 
|  | uintptr_t pc ATTRIBUTE_UNUSED, | 
|  | backtrace_full_callback callback ATTRIBUTE_UNUSED, | 
|  | backtrace_error_callback error_callback, void *data) | 
|  | { | 
|  | error_callback (data, "no debug info in XCOFF executable", -1); | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | /* A dummy callback function used when we can't find a symbol | 
|  | table.  */ | 
|  |  | 
|  | static void | 
|  | xcoff_nosyms (struct backtrace_state *state ATTRIBUTE_UNUSED, | 
|  | uintptr_t addr ATTRIBUTE_UNUSED, | 
|  | backtrace_syminfo_callback callback ATTRIBUTE_UNUSED, | 
|  | backtrace_error_callback error_callback, void *data) | 
|  | { | 
|  | error_callback (data, "no symbol table in XCOFF executable", -1); | 
|  | } | 
|  |  | 
|  | /* Compare struct xcoff_symbol for qsort.  */ | 
|  |  | 
|  | static int | 
|  | xcoff_symbol_compare (const void *v1, const void *v2) | 
|  | { | 
|  | const struct xcoff_symbol *e1 = (const struct xcoff_symbol *) v1; | 
|  | const struct xcoff_symbol *e2 = (const struct xcoff_symbol *) v2; | 
|  |  | 
|  | if (e1->address < e2->address) | 
|  | return -1; | 
|  | else if (e1->address > e2->address) | 
|  | return 1; | 
|  | else | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | /* Compare an ADDR against an xcoff_symbol for bsearch.  */ | 
|  |  | 
|  | static int | 
|  | xcoff_symbol_search (const void *vkey, const void *ventry) | 
|  | { | 
|  | const uintptr_t *key = (const uintptr_t *) vkey; | 
|  | const struct xcoff_symbol *entry = (const struct xcoff_symbol *) ventry; | 
|  | uintptr_t addr; | 
|  |  | 
|  | addr = *key; | 
|  | if (addr < entry->address) | 
|  | return -1; | 
|  | else if ((entry->size == 0 && addr > entry->address) | 
|  | || (entry->size > 0 && addr >= entry->address + entry->size)) | 
|  | return 1; | 
|  | else | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | /* Add XDATA to the list in STATE.  */ | 
|  |  | 
|  | static void | 
|  | xcoff_add_syminfo_data (struct backtrace_state *state, | 
|  | struct xcoff_syminfo_data *xdata) | 
|  | { | 
|  | if (!state->threaded) | 
|  | { | 
|  | struct xcoff_syminfo_data **pp; | 
|  |  | 
|  | for (pp = (struct xcoff_syminfo_data **) (void *) &state->syminfo_data; | 
|  | *pp != NULL; | 
|  | pp = &(*pp)->next) | 
|  | ; | 
|  | *pp = xdata; | 
|  | } | 
|  | else | 
|  | { | 
|  | while (1) | 
|  | { | 
|  | struct xcoff_syminfo_data **pp; | 
|  |  | 
|  | pp = (struct xcoff_syminfo_data **) (void *) &state->syminfo_data; | 
|  |  | 
|  | while (1) | 
|  | { | 
|  | struct xcoff_syminfo_data *p; | 
|  |  | 
|  | p = backtrace_atomic_load_pointer (pp); | 
|  |  | 
|  | if (p == NULL) | 
|  | break; | 
|  |  | 
|  | pp = &p->next; | 
|  | } | 
|  |  | 
|  | if (__sync_bool_compare_and_swap (pp, NULL, xdata)) | 
|  | break; | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | /* Return the symbol name and value for an ADDR.  */ | 
|  |  | 
|  | static void | 
|  | xcoff_syminfo (struct backtrace_state *state ATTRIBUTE_UNUSED, uintptr_t addr, | 
|  | backtrace_syminfo_callback callback, | 
|  | backtrace_error_callback error_callback ATTRIBUTE_UNUSED, | 
|  | void *data) | 
|  | { | 
|  | struct xcoff_syminfo_data *edata; | 
|  | struct xcoff_symbol *sym = NULL; | 
|  | const char *name; | 
|  |  | 
|  | if (!state->threaded) | 
|  | { | 
|  | for (edata = (struct xcoff_syminfo_data *) state->syminfo_data; | 
|  | edata != NULL; | 
|  | edata = edata->next) | 
|  | { | 
|  | sym = ((struct xcoff_symbol *) | 
|  | bsearch (&addr, edata->symbols, edata->count, | 
|  | sizeof (struct xcoff_symbol), xcoff_symbol_search)); | 
|  | if (sym != NULL) | 
|  | break; | 
|  | } | 
|  | } | 
|  | else | 
|  | { | 
|  | struct xcoff_syminfo_data **pp; | 
|  |  | 
|  | pp = (struct xcoff_syminfo_data **) (void *) &state->syminfo_data; | 
|  | while (1) | 
|  | { | 
|  | edata = backtrace_atomic_load_pointer (pp); | 
|  | if (edata == NULL) | 
|  | break; | 
|  |  | 
|  | sym = ((struct xcoff_symbol *) | 
|  | bsearch (&addr, edata->symbols, edata->count, | 
|  | sizeof (struct xcoff_symbol), xcoff_symbol_search)); | 
|  | if (sym != NULL) | 
|  | break; | 
|  |  | 
|  | pp = &edata->next; | 
|  | } | 
|  | } | 
|  |  | 
|  | if (sym == NULL) | 
|  | callback (data, addr, NULL, 0, 0); | 
|  | else | 
|  | { | 
|  | name = sym->name; | 
|  | /* AIX prepends a '.' to function entry points, remove it.  */ | 
|  | if (name && *name == '.') | 
|  | ++name; | 
|  | callback (data, addr, name, sym->address, sym->size); | 
|  | } | 
|  | } | 
|  |  | 
|  | /* Return the name of an XCOFF symbol.  */ | 
|  |  | 
|  | static const char * | 
|  | xcoff_symname (const b_xcoff_syment *asym, | 
|  | const unsigned char *strtab, size_t strtab_size) | 
|  | { | 
|  | #if BACKTRACE_XCOFF_SIZE == 32 | 
|  | if (asym->n_zeroes != 0) | 
|  | { | 
|  | /* Make a copy as we will release the symtab view.  */ | 
|  | char name[SYMNMLEN+1]; | 
|  | strncpy (name, asym->n_name, SYMNMLEN); | 
|  | name[SYMNMLEN] = '\0'; | 
|  | return strdup (name); | 
|  | } | 
|  | #endif | 
|  | if (asym->n_sclass & 0x80) | 
|  | return NULL; /* .debug */ | 
|  | if (asym->n_offset_ >= strtab_size) | 
|  | return NULL; | 
|  | return (const char *) strtab + asym->n_offset_; | 
|  | } | 
|  |  | 
|  | /* Initialize the symbol table info for xcoff_syminfo.  */ | 
|  |  | 
|  | static int | 
|  | xcoff_initialize_syminfo (struct backtrace_state *state, | 
|  | uintptr_t base_address, | 
|  | const b_xcoff_syment *syms, size_t nsyms, | 
|  | const unsigned char *strtab, size_t strtab_size, | 
|  | backtrace_error_callback error_callback, void *data, | 
|  | struct xcoff_syminfo_data *sdata) | 
|  | { | 
|  | size_t xcoff_symbol_count; | 
|  | size_t xcoff_symbol_size; | 
|  | struct xcoff_symbol *xcoff_symbols; | 
|  | size_t i; | 
|  | unsigned int j; | 
|  |  | 
|  | /* We only care about function symbols.  Count them.  */ | 
|  | xcoff_symbol_count = 0; | 
|  | for (i = 0; i < nsyms; ++i) | 
|  | { | 
|  | const b_xcoff_syment *asym = &syms[i]; | 
|  | if ((asym->n_sclass == C_EXT || asym->n_sclass == C_HIDEXT | 
|  | || asym->n_sclass == C_WEAKEXT) | 
|  | && ISFCN (asym->n_type) && asym->n_numaux > 0 && asym->n_scnum > 0) | 
|  | ++xcoff_symbol_count; | 
|  |  | 
|  | i += asym->n_numaux; | 
|  | } | 
|  |  | 
|  | xcoff_symbol_size = xcoff_symbol_count * sizeof (struct xcoff_symbol); | 
|  | xcoff_symbols = ((struct xcoff_symbol *) | 
|  | backtrace_alloc (state, xcoff_symbol_size, error_callback, | 
|  | data)); | 
|  | if (xcoff_symbols == NULL) | 
|  | return 0; | 
|  |  | 
|  | j = 0; | 
|  | for (i = 0; i < nsyms; ++i) | 
|  | { | 
|  | const b_xcoff_syment *asym = &syms[i]; | 
|  | if ((asym->n_sclass == C_EXT || asym->n_sclass == C_HIDEXT | 
|  | || asym->n_sclass == C_WEAKEXT) | 
|  | && ISFCN (asym->n_type) && asym->n_numaux > 0 && asym->n_scnum > 0) | 
|  | { | 
|  | const b_xcoff_auxent *aux = (const b_xcoff_auxent *) (asym + 1); | 
|  | xcoff_symbols[j].name = xcoff_symname (asym, strtab, strtab_size); | 
|  | xcoff_symbols[j].address = base_address + asym->n_value; | 
|  | /* x_fsize will be 0 if there is no debug information.  */ | 
|  | xcoff_symbols[j].size = aux->x_fcn.x_fsize; | 
|  | ++j; | 
|  | } | 
|  |  | 
|  | i += asym->n_numaux; | 
|  | } | 
|  |  | 
|  | backtrace_qsort (xcoff_symbols, xcoff_symbol_count, | 
|  | sizeof (struct xcoff_symbol), xcoff_symbol_compare); | 
|  |  | 
|  | sdata->next = NULL; | 
|  | sdata->symbols = xcoff_symbols; | 
|  | sdata->count = xcoff_symbol_count; | 
|  |  | 
|  | return 1; | 
|  | } | 
|  |  | 
|  | /* Compare struct xcoff_func for qsort.  */ | 
|  |  | 
|  | static int | 
|  | xcoff_func_compare (const void *v1, const void *v2) | 
|  | { | 
|  | const struct xcoff_func *fn1 = (const struct xcoff_func *) v1; | 
|  | const struct xcoff_func *fn2 = (const struct xcoff_func *) v2; | 
|  |  | 
|  | if (fn1->pc < fn2->pc) | 
|  | return -1; | 
|  | else if (fn1->pc > fn2->pc) | 
|  | return 1; | 
|  | else | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | /* Compare a PC against an xcoff_func for bsearch.  */ | 
|  |  | 
|  | static int | 
|  | xcoff_func_search (const void *vkey, const void *ventry) | 
|  | { | 
|  | const uintptr_t *key = (const uintptr_t *) vkey; | 
|  | const struct xcoff_func *entry = (const struct xcoff_func *) ventry; | 
|  | uintptr_t pc; | 
|  |  | 
|  | pc = *key; | 
|  | if (pc < entry->pc) | 
|  | return -1; | 
|  | else if ((entry->size == 0 && pc > entry->pc) | 
|  | || (entry->size > 0 && pc >= entry->pc + entry->size)) | 
|  | return 1; | 
|  | else | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | /* Compare struct xcoff_incl for qsort.  */ | 
|  |  | 
|  | static int | 
|  | xcoff_incl_compare (const void *v1, const void *v2) | 
|  | { | 
|  | const struct xcoff_incl *in1 = (const struct xcoff_incl *) v1; | 
|  | const struct xcoff_incl *in2 = (const struct xcoff_incl *) v2; | 
|  |  | 
|  | if (in1->begin < in2->begin) | 
|  | return -1; | 
|  | else if (in1->begin > in2->begin) | 
|  | return 1; | 
|  | else | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | /* Find a lnnoptr in an include file.  */ | 
|  |  | 
|  | static int | 
|  | xcoff_incl_search (const void *vkey, const void *ventry) | 
|  | { | 
|  | const uintptr_t *key = (const uintptr_t *) vkey; | 
|  | const struct xcoff_incl *entry = (const struct xcoff_incl *) ventry; | 
|  | uintptr_t lnno; | 
|  |  | 
|  | lnno = *key; | 
|  | if (lnno < entry->begin) | 
|  | return -1; | 
|  | else if (lnno > entry->end) | 
|  | return 1; | 
|  | else | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | /* Look for a PC in the function vector for one module.  On success, | 
|  | call CALLBACK and return whatever it returns.  On error, call | 
|  | ERROR_CALLBACK and return 0.  Sets *FOUND to 1 if the PC is found, | 
|  | 0 if not.  */ | 
|  |  | 
|  | static int | 
|  | xcoff_lookup_pc (struct backtrace_state *state ATTRIBUTE_UNUSED, | 
|  | struct xcoff_fileline_data *fdata, uintptr_t pc, | 
|  | backtrace_full_callback callback, | 
|  | backtrace_error_callback error_callback ATTRIBUTE_UNUSED, | 
|  | void *data, int *found) | 
|  | { | 
|  | const struct xcoff_incl *incl, *bincl; | 
|  | const struct xcoff_func *fn; | 
|  | const b_xcoff_lineno *lineno; | 
|  | const unsigned char *lineptr; | 
|  | const char *function; | 
|  | const char *filename; | 
|  | uintptr_t lnnoptr, match; | 
|  | uint32_t lnno = 0; | 
|  |  | 
|  | *found = 1; | 
|  |  | 
|  | if ((pc & 3) != 0) | 
|  | ++pc; | 
|  |  | 
|  | /* Find the function first.  */ | 
|  | fn = ((struct xcoff_func *) | 
|  | bsearch (&pc, fdata->func_vec.vec.base, fdata->func_vec.count, | 
|  | sizeof (struct xcoff_func), xcoff_func_search)); | 
|  | if (fn == NULL) | 
|  | { | 
|  | *found = 0; | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | filename = fn->filename; | 
|  |  | 
|  | /* Find the line number next.  */ | 
|  |  | 
|  | /* Skip first entry that points to symtab.  */ | 
|  | lnnoptr = fn->lnnoptr + LINESZ; | 
|  | match = lnnoptr; | 
|  |  | 
|  | lineptr = fdata->linenos + (lnnoptr - fdata->lnnoptr0); | 
|  | while (lineptr + LINESZ <= fdata->linenos + fdata->linenos_size) | 
|  | { | 
|  | lineno = (const b_xcoff_lineno *) lineptr; | 
|  | if (lineno->l_lnno == 0) | 
|  | break; | 
|  | if (pc <= fdata->base_address + lineno->l_addr.l_paddr) | 
|  | break; | 
|  | match = lnnoptr; | 
|  | lnno = lineno->l_lnno; | 
|  |  | 
|  | lnnoptr += LINESZ; | 
|  | lineptr += LINESZ; | 
|  | } | 
|  |  | 
|  | /* If part of a function other than the beginning comes from an | 
|  | include file, the line numbers are absolute, rather than | 
|  | relative to the beginning of the function.  */ | 
|  | incl = ((struct xcoff_incl *) | 
|  | bsearch (&match, fdata->incl_vec.vec.base, | 
|  | fdata->incl_vec.count, sizeof (struct xcoff_incl), | 
|  | xcoff_incl_search)); | 
|  | if (incl != NULL) | 
|  | { | 
|  | bincl = ((struct xcoff_incl *) | 
|  | bsearch (&fn->lnnoptr, fdata->incl_vec.vec.base, | 
|  | fdata->incl_vec.count, sizeof (struct xcoff_incl), | 
|  | xcoff_incl_search)); | 
|  | if (bincl != NULL && strcmp (incl->filename, bincl->filename) == 0) | 
|  | { | 
|  | lnno += fn->lnno - 1; | 
|  | } | 
|  | filename = incl->filename; | 
|  | } | 
|  | else | 
|  | { | 
|  | lnno += fn->lnno - 1; | 
|  | } | 
|  |  | 
|  | function = fn->name; | 
|  | /* AIX prepends a '.' to function entry points, remove it.  */ | 
|  | if (function != NULL && *function == '.') | 
|  | ++function; | 
|  | return callback (data, pc, filename, lnno, function); | 
|  | } | 
|  |  | 
|  | /* Return the file/line information for a PC using the XCOFF lineno | 
|  | mapping we built earlier.  */ | 
|  |  | 
|  | static int | 
|  | xcoff_fileline (struct backtrace_state *state, uintptr_t pc, | 
|  | backtrace_full_callback callback, | 
|  | backtrace_error_callback error_callback, void *data) | 
|  |  | 
|  | { | 
|  | struct xcoff_fileline_data *fdata; | 
|  | int found; | 
|  | int ret; | 
|  |  | 
|  | if (!state->threaded) | 
|  | { | 
|  | for (fdata = (struct xcoff_fileline_data *) state->fileline_data; | 
|  | fdata != NULL; | 
|  | fdata = fdata->next) | 
|  | { | 
|  | ret = xcoff_lookup_pc (state, fdata, pc, callback, error_callback, | 
|  | data, &found); | 
|  | if (ret != 0 || found) | 
|  | return ret; | 
|  | } | 
|  | } | 
|  | else | 
|  | { | 
|  | struct xcoff_fileline_data **pp; | 
|  |  | 
|  | pp = (struct xcoff_fileline_data **) (void *) &state->fileline_data; | 
|  | while (1) | 
|  | { | 
|  | fdata = backtrace_atomic_load_pointer (pp); | 
|  | if (fdata == NULL) | 
|  | break; | 
|  |  | 
|  | ret = xcoff_lookup_pc (state, fdata, pc, callback, error_callback, | 
|  | data, &found); | 
|  | if (ret != 0 || found) | 
|  | return ret; | 
|  |  | 
|  | pp = &fdata->next; | 
|  | } | 
|  | } | 
|  |  | 
|  | /* FIXME: See if any libraries have been dlopen'ed.  */ | 
|  |  | 
|  | return callback (data, pc, NULL, 0, NULL); | 
|  | } | 
|  |  | 
|  | /* Initialize the function vector info for xcoff_fileline.  */ | 
|  |  | 
|  | static int | 
|  | xcoff_initialize_fileline (struct backtrace_state *state, | 
|  | uintptr_t base_address, | 
|  | const b_xcoff_scnhdr *sects, | 
|  | const b_xcoff_syment *syms, size_t nsyms, | 
|  | const unsigned char *strtab, size_t strtab_size, | 
|  | const unsigned char *linenos, size_t linenos_size, | 
|  | uint64_t lnnoptr0, | 
|  | backtrace_error_callback error_callback, void *data) | 
|  | { | 
|  | struct xcoff_fileline_data *fdata; | 
|  | struct xcoff_func *fn; | 
|  | const b_xcoff_syment *fsym; | 
|  | const b_xcoff_auxent *aux; | 
|  | const char *filename; | 
|  | const char *name; | 
|  | struct xcoff_incl *incl; | 
|  | uintptr_t begin, end; | 
|  | uintptr_t lnno, lnnoptr; | 
|  | uint32_t fsize; | 
|  | size_t i; | 
|  |  | 
|  | fdata = ((struct xcoff_fileline_data *) | 
|  | backtrace_alloc (state, sizeof (struct xcoff_fileline_data), | 
|  | error_callback, data)); | 
|  | if (fdata == NULL) | 
|  | return 0; | 
|  | memset (fdata, 0, sizeof *fdata); | 
|  | fdata->base_address = base_address; | 
|  | fdata->linenos = linenos; | 
|  | fdata->linenos_size = linenos_size; | 
|  | fdata->lnnoptr0 = lnnoptr0; | 
|  |  | 
|  | begin = 0; | 
|  | filename = NULL; | 
|  | fsym = NULL; | 
|  | lnnoptr = 0; | 
|  | fsize = 0; | 
|  | for (i = 0; i < nsyms; ++i) | 
|  | { | 
|  | const b_xcoff_syment *asym = &syms[i]; | 
|  |  | 
|  | switch (asym->n_sclass) | 
|  | { | 
|  | case C_BINCL: | 
|  | begin = asym->n_value; | 
|  | break; | 
|  |  | 
|  | case C_EINCL: | 
|  | if (begin == 0) | 
|  | break; | 
|  | end = asym->n_value; | 
|  | incl = ((struct xcoff_incl *) | 
|  | backtrace_vector_grow (state, sizeof (struct xcoff_incl), | 
|  | error_callback, data, | 
|  | &fdata->incl_vec.vec)); | 
|  | if (incl != NULL) | 
|  | { | 
|  | incl->filename = xcoff_symname (asym, strtab, strtab_size); | 
|  | incl->begin = begin; | 
|  | incl->end = end; | 
|  | ++fdata->incl_vec.count; | 
|  | } | 
|  | begin = 0; | 
|  | break; | 
|  |  | 
|  | case C_FILE: | 
|  | filename = xcoff_symname (asym, strtab, strtab_size); | 
|  | if (filename == NULL) | 
|  | break; | 
|  |  | 
|  | /* If the file auxiliary entry is not used, the symbol name is | 
|  | the name of the source file. If the file auxiliary entry is | 
|  | used, then the symbol name should be .file, and the first | 
|  | file auxiliary entry (by convention) contains the source | 
|  | file name.  */ | 
|  |  | 
|  | if (asym->n_numaux > 0 && strcmp (filename, ".file") == 0) | 
|  | { | 
|  | aux = (const b_xcoff_auxent *) (asym + 1); | 
|  | if (aux->x_file._x.x_zeroes != 0) | 
|  | { | 
|  | /* Make a copy as we will release the symtab view.  */ | 
|  | char name[FILNMLEN+1]; | 
|  | strncpy (name, aux->x_file.x_fname, FILNMLEN); | 
|  | name[FILNMLEN] = '\0'; | 
|  | filename = strdup (name); | 
|  | } | 
|  | else if (aux->x_file._x.x_offset < strtab_size) | 
|  | filename = (const char *) strtab + aux->x_file._x.x_offset; | 
|  | else | 
|  | filename = NULL; | 
|  | } | 
|  | break; | 
|  |  | 
|  | case C_EXT: | 
|  | case C_HIDEXT: | 
|  | case C_WEAKEXT: | 
|  | fsym = NULL; | 
|  | lnnoptr = 0; | 
|  | fsize = 0; | 
|  | if (!ISFCN (asym->n_type) || asym->n_numaux == 0 | 
|  | || asym->n_scnum <= 0) | 
|  | break; | 
|  | if (filename == NULL) | 
|  | break; | 
|  | aux = (const b_xcoff_auxent *) (asym + 1); | 
|  | lnnoptr = aux->x_fcn.x_lnnoptr; | 
|  | if (lnnoptr < lnnoptr0 | 
|  | || lnnoptr + LINESZ > lnnoptr0 + linenos_size) | 
|  | break; | 
|  | /* x_fsize will be 0 if there is no debug information.  */ | 
|  | fsize = aux->x_fcn.x_fsize; | 
|  | fsym = asym; | 
|  | break; | 
|  |  | 
|  | case C_FCN: | 
|  | if (asym->n_numaux == 0) | 
|  | break; | 
|  | if (fsym == NULL) | 
|  | break; | 
|  | name = xcoff_symname (asym, strtab, strtab_size); | 
|  | if (name == NULL || strcmp (name, ".bf") != 0) | 
|  | { | 
|  | fsym = NULL; | 
|  | break; | 
|  | } | 
|  | aux = (const b_xcoff_auxent *) (asym + 1); | 
|  | #if BACKTRACE_XCOFF_SIZE == 32 | 
|  | lnno = (uint32_t) aux->x_block.x_lnnohi << 16 | 
|  | | aux->x_block.x_lnno; | 
|  | #else | 
|  | lnno = aux->x_block.x_lnno; | 
|  | #endif | 
|  | fn = ((struct xcoff_func *) | 
|  | backtrace_vector_grow (state, sizeof (struct xcoff_func), | 
|  | error_callback, data, | 
|  | &fdata->func_vec.vec)); | 
|  | if (fn == NULL) | 
|  | break; | 
|  | fn->name = xcoff_symname (fsym, strtab, strtab_size); | 
|  | fn->filename = filename; | 
|  | fn->sect_base = sects[fsym->n_scnum - 1].s_paddr; | 
|  | fn->pc = base_address + fsym->n_value; | 
|  | fn->size = fsize; | 
|  | fn->lnno = lnno; | 
|  | fn->lnnoptr = lnnoptr; | 
|  | ++fdata->func_vec.count; | 
|  | break; | 
|  | } | 
|  |  | 
|  | i += asym->n_numaux; | 
|  | } | 
|  |  | 
|  | if (!backtrace_vector_release (state, &fdata->func_vec.vec, error_callback, | 
|  | data)) | 
|  | goto fail; | 
|  | backtrace_qsort (fdata->func_vec.vec.base, fdata->func_vec.count, | 
|  | sizeof (struct xcoff_func), xcoff_func_compare); | 
|  |  | 
|  | if (!backtrace_vector_release (state, &fdata->incl_vec.vec, error_callback, | 
|  | data)) | 
|  | goto fail; | 
|  | backtrace_qsort (fdata->incl_vec.vec.base, fdata->incl_vec.count, | 
|  | sizeof (struct xcoff_incl), xcoff_incl_compare); | 
|  |  | 
|  | if (!state->threaded) | 
|  | { | 
|  | struct xcoff_fileline_data **pp; | 
|  |  | 
|  | for (pp = (struct xcoff_fileline_data **) (void *) &state->fileline_data; | 
|  | *pp != NULL; | 
|  | pp = &(*pp)->next) | 
|  | ; | 
|  | *pp = fdata; | 
|  | } | 
|  | else | 
|  | { | 
|  | while (1) | 
|  | { | 
|  | struct xcoff_fileline_data **pp; | 
|  |  | 
|  | pp = (struct xcoff_fileline_data **) (void *) &state->fileline_data; | 
|  |  | 
|  | while (1) | 
|  | { | 
|  | struct xcoff_fileline_data *p; | 
|  |  | 
|  | p = backtrace_atomic_load_pointer (pp); | 
|  |  | 
|  | if (p == NULL) | 
|  | break; | 
|  |  | 
|  | pp = &p->next; | 
|  | } | 
|  |  | 
|  | if (__sync_bool_compare_and_swap (pp, NULL, fdata)) | 
|  | break; | 
|  | } | 
|  | } | 
|  |  | 
|  | return 1; | 
|  |  | 
|  | fail: | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | /* Add the backtrace data for one XCOFF file.  Returns 1 on success, | 
|  | 0 on failure (in both cases descriptor is closed).  */ | 
|  |  | 
|  | static int | 
|  | xcoff_add (struct backtrace_state *state, int descriptor, off_t offset, | 
|  | uintptr_t base_address, backtrace_error_callback error_callback, | 
|  | void *data, fileline *fileline_fn, int *found_sym, int exe) | 
|  | { | 
|  | struct backtrace_view fhdr_view; | 
|  | struct backtrace_view sects_view; | 
|  | struct backtrace_view linenos_view; | 
|  | struct backtrace_view syms_view; | 
|  | struct backtrace_view str_view; | 
|  | struct backtrace_view dwarf_view; | 
|  | b_xcoff_filhdr fhdr; | 
|  | const b_xcoff_scnhdr *sects; | 
|  | const b_xcoff_scnhdr *stext; | 
|  | uint64_t lnnoptr; | 
|  | uint32_t nlnno; | 
|  | off_t str_off; | 
|  | off_t min_offset; | 
|  | off_t max_offset; | 
|  | struct dwsect_info dwsect[DEBUG_MAX]; | 
|  | size_t sects_size; | 
|  | size_t syms_size; | 
|  | int32_t str_size; | 
|  | int sects_view_valid; | 
|  | int linenos_view_valid; | 
|  | int syms_view_valid; | 
|  | int str_view_valid; | 
|  | int dwarf_view_valid; | 
|  | int magic_ok; | 
|  | int i; | 
|  | struct dwarf_sections dwarf_sections; | 
|  |  | 
|  | *found_sym = 0; | 
|  |  | 
|  | sects_view_valid = 0; | 
|  | linenos_view_valid = 0; | 
|  | syms_view_valid = 0; | 
|  | str_view_valid = 0; | 
|  | dwarf_view_valid = 0; | 
|  |  | 
|  | str_size = 0; | 
|  |  | 
|  | /* Map the XCOFF file header.  */ | 
|  | if (!backtrace_get_view (state, descriptor, offset, sizeof (b_xcoff_filhdr), | 
|  | error_callback, data, &fhdr_view)) | 
|  | goto fail; | 
|  |  | 
|  | memcpy (&fhdr, fhdr_view.data, sizeof fhdr); | 
|  | magic_ok = (fhdr.f_magic == XCOFF_MAGIC); | 
|  |  | 
|  | backtrace_release_view (state, &fhdr_view, error_callback, data); | 
|  |  | 
|  | if (!magic_ok) | 
|  | { | 
|  | if (exe) | 
|  | error_callback (data, "executable file is not XCOFF", 0); | 
|  | goto fail; | 
|  | } | 
|  |  | 
|  | /* Verify object is of expected type.  */ | 
|  | if ((exe && (fhdr.f_flags & F_SHROBJ)) | 
|  | || (!exe && !(fhdr.f_flags & F_SHROBJ))) | 
|  | goto fail; | 
|  |  | 
|  | /* Read the section headers.  */ | 
|  |  | 
|  | sects_size = fhdr.f_nscns * sizeof (b_xcoff_scnhdr); | 
|  |  | 
|  | if (!backtrace_get_view (state, descriptor, | 
|  | offset + sizeof (fhdr) + fhdr.f_opthdr, | 
|  | sects_size, error_callback, data, §s_view)) | 
|  | goto fail; | 
|  | sects_view_valid = 1; | 
|  | sects = (const b_xcoff_scnhdr *) sects_view.data; | 
|  |  | 
|  | /* FIXME: assumes only one .text section.  */ | 
|  | for (i = 0; i < fhdr.f_nscns; ++i) | 
|  | if ((sects[i].s_flags & 0xffff) == STYP_TEXT) | 
|  | break; | 
|  | if (i == fhdr.f_nscns) | 
|  | goto fail; | 
|  |  | 
|  | stext = §s[i]; | 
|  |  | 
|  | /* base_address represents the difference between the | 
|  | virtual memory address of the shared object or a loaded | 
|  | executable and the offset of that object in the file | 
|  | from which it was loaded. | 
|  | On AIX, virtual address is either fixed for executable | 
|  | or given by ldinfo.  This address will include the XCOFF | 
|  | headers.  */ | 
|  | base_address = ((exe ? XCOFF_AIX_TEXTBASE : base_address) | 
|  | + stext->s_scnptr | 
|  | - stext->s_paddr); | 
|  |  | 
|  | lnnoptr = stext->s_lnnoptr; | 
|  | nlnno = stext->s_nlnno; | 
|  |  | 
|  | #if BACKTRACE_XCOFF_SIZE == 32 | 
|  | if (nlnno == _OVERFLOW_MARKER) | 
|  | { | 
|  | int sntext = i + 1; | 
|  | /* Find the matching .ovrflo section.  */ | 
|  | for (i = 0; i < fhdr.f_nscns; ++i) | 
|  | { | 
|  | if (((sects[i].s_flags & 0xffff) == STYP_OVRFLO) | 
|  | && sects[i].s_nlnno == sntext) | 
|  | { | 
|  | nlnno = sects[i].s_vaddr; | 
|  | break; | 
|  | } | 
|  | } | 
|  | } | 
|  | #endif | 
|  |  | 
|  | /* Read the symbol table and the string table.  */ | 
|  |  | 
|  | if (fhdr.f_symptr != 0) | 
|  | { | 
|  | struct xcoff_syminfo_data *sdata; | 
|  |  | 
|  | /* Symbol table is followed by the string table.  The string table | 
|  | starts with its length (on 4 bytes). | 
|  | Map the symbol table and the length of the string table.  */ | 
|  | syms_size = fhdr.f_nsyms * sizeof (b_xcoff_syment); | 
|  |  | 
|  | if (!backtrace_get_view (state, descriptor, offset + fhdr.f_symptr, | 
|  | syms_size + 4, error_callback, data, | 
|  | &syms_view)) | 
|  | goto fail; | 
|  | syms_view_valid = 1; | 
|  |  | 
|  | memcpy (&str_size, syms_view.data + syms_size, 4); | 
|  |  | 
|  | str_off = fhdr.f_symptr + syms_size; | 
|  |  | 
|  | if (str_size > 4) | 
|  | { | 
|  | /* Map string table (including the length word).  */ | 
|  |  | 
|  | if (!backtrace_get_view (state, descriptor, offset + str_off, | 
|  | str_size, error_callback, data, &str_view)) | 
|  | goto fail; | 
|  | str_view_valid = 1; | 
|  | } | 
|  |  | 
|  | sdata = ((struct xcoff_syminfo_data *) | 
|  | backtrace_alloc (state, sizeof *sdata, error_callback, data)); | 
|  | if (sdata == NULL) | 
|  | goto fail; | 
|  |  | 
|  | if (!xcoff_initialize_syminfo (state, base_address, | 
|  | syms_view.data, fhdr.f_nsyms, | 
|  | str_view.data, str_size, | 
|  | error_callback, data, sdata)) | 
|  | { | 
|  | backtrace_free (state, sdata, sizeof *sdata, error_callback, data); | 
|  | goto fail; | 
|  | } | 
|  |  | 
|  | *found_sym = 1; | 
|  |  | 
|  | xcoff_add_syminfo_data (state, sdata); | 
|  | } | 
|  |  | 
|  | /* Read all the DWARF sections in a single view, since they are | 
|  | probably adjacent in the file.  We never release this view.  */ | 
|  |  | 
|  | min_offset = 0; | 
|  | max_offset = 0; | 
|  | memset (dwsect, 0, sizeof dwsect); | 
|  | for (i = 0; i < fhdr.f_nscns; ++i) | 
|  | { | 
|  | off_t end; | 
|  | int idx; | 
|  |  | 
|  | if ((sects[i].s_flags & 0xffff) != STYP_DWARF | 
|  | || sects[i].s_size == 0) | 
|  | continue; | 
|  | /* Map DWARF section to array index.  */ | 
|  | switch (sects[i].s_flags & 0xffff0000) | 
|  | { | 
|  | case SSUBTYP_DWINFO: | 
|  | idx = DEBUG_INFO; | 
|  | break; | 
|  | case SSUBTYP_DWLINE: | 
|  | idx = DEBUG_LINE; | 
|  | break; | 
|  | case SSUBTYP_DWABREV: | 
|  | idx = DEBUG_ABBREV; | 
|  | break; | 
|  | case SSUBTYP_DWRNGES: | 
|  | idx = DEBUG_RANGES; | 
|  | break; | 
|  | case SSUBTYP_DWSTR: | 
|  | idx = DEBUG_STR; | 
|  | break; | 
|  | default: | 
|  | continue; | 
|  | } | 
|  | if (min_offset == 0 || (off_t) sects[i].s_scnptr < min_offset) | 
|  | min_offset = sects[i].s_scnptr; | 
|  | end = sects[i].s_scnptr + sects[i].s_size; | 
|  | if (end > max_offset) | 
|  | max_offset = end; | 
|  | dwsect[idx].offset = sects[i].s_scnptr; | 
|  | dwsect[idx].size = sects[i].s_size; | 
|  | } | 
|  | if (min_offset != 0 && max_offset != 0) | 
|  | { | 
|  | if (!backtrace_get_view (state, descriptor, offset + min_offset, | 
|  | max_offset - min_offset, | 
|  | error_callback, data, &dwarf_view)) | 
|  | goto fail; | 
|  | dwarf_view_valid = 1; | 
|  |  | 
|  | for (i = 0; i < (int) DEBUG_MAX; ++i) | 
|  | { | 
|  | if (dwsect[i].offset == 0) | 
|  | dwsect[i].data = NULL; | 
|  | else | 
|  | dwsect[i].data = ((const unsigned char *) dwarf_view.data | 
|  | + (dwsect[i].offset - min_offset)); | 
|  | } | 
|  |  | 
|  | memset (&dwarf_sections, 0, sizeof dwarf_sections); | 
|  |  | 
|  | dwarf_sections.data[DEBUG_INFO] = dwsect[DEBUG_INFO].data; | 
|  | dwarf_sections.size[DEBUG_INFO] = dwsect[DEBUG_INFO].size; | 
|  | dwarf_sections.data[DEBUG_LINE] = dwsect[DEBUG_LINE].data; | 
|  | dwarf_sections.size[DEBUG_LINE] = dwsect[DEBUG_LINE].size; | 
|  | dwarf_sections.data[DEBUG_ABBREV] = dwsect[DEBUG_ABBREV].data; | 
|  | dwarf_sections.size[DEBUG_ABBREV] = dwsect[DEBUG_ABBREV].size; | 
|  | dwarf_sections.data[DEBUG_RANGES] = dwsect[DEBUG_RANGES].data; | 
|  | dwarf_sections.size[DEBUG_RANGES] = dwsect[DEBUG_RANGES].size; | 
|  | dwarf_sections.data[DEBUG_STR] = dwsect[DEBUG_STR].data; | 
|  | dwarf_sections.size[DEBUG_STR] = dwsect[DEBUG_STR].size; | 
|  |  | 
|  | if (!backtrace_dwarf_add (state, base_address, &dwarf_sections, | 
|  | 1, /* big endian */ | 
|  | NULL, /* altlink */ | 
|  | error_callback, data, fileline_fn, | 
|  | NULL /* returned fileline_entry */)) | 
|  | goto fail; | 
|  | } | 
|  |  | 
|  | /* Read the XCOFF line number entries if DWARF sections not found.  */ | 
|  |  | 
|  | if (!dwarf_view_valid && fhdr.f_symptr != 0 && lnnoptr != 0) | 
|  | { | 
|  | size_t linenos_size = (size_t) nlnno * LINESZ; | 
|  |  | 
|  | /* We never release this view.  */ | 
|  | if (!backtrace_get_view (state, descriptor, offset + lnnoptr, | 
|  | linenos_size, | 
|  | error_callback, data, &linenos_view)) | 
|  | goto fail; | 
|  | linenos_view_valid = 1; | 
|  |  | 
|  | if (xcoff_initialize_fileline (state, base_address, sects, | 
|  | syms_view.data, fhdr.f_nsyms, | 
|  | str_view.data, str_size, | 
|  | linenos_view.data, linenos_size, | 
|  | lnnoptr, error_callback, data)) | 
|  | *fileline_fn = xcoff_fileline; | 
|  | } | 
|  |  | 
|  | backtrace_release_view (state, §s_view, error_callback, data); | 
|  | sects_view_valid = 0; | 
|  | if (syms_view_valid) | 
|  | backtrace_release_view (state, &syms_view, error_callback, data); | 
|  | syms_view_valid = 0; | 
|  |  | 
|  | /* We've read all we need from the executable.  */ | 
|  | if (!backtrace_close (descriptor, error_callback, data)) | 
|  | goto fail; | 
|  | descriptor = -1; | 
|  |  | 
|  | return 1; | 
|  |  | 
|  | fail: | 
|  | if (sects_view_valid) | 
|  | backtrace_release_view (state, §s_view, error_callback, data); | 
|  | if (str_view_valid) | 
|  | backtrace_release_view (state, &str_view, error_callback, data); | 
|  | if (syms_view_valid) | 
|  | backtrace_release_view (state, &syms_view, error_callback, data); | 
|  | if (linenos_view_valid) | 
|  | backtrace_release_view (state, &linenos_view, error_callback, data); | 
|  | if (dwarf_view_valid) | 
|  | backtrace_release_view (state, &dwarf_view, error_callback, data); | 
|  | if (descriptor != -1 && offset == 0) | 
|  | backtrace_close (descriptor, error_callback, data); | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | #ifdef HAVE_LOADQUERY | 
|  |  | 
|  | /* Read an integer value in human-readable format from an AIX | 
|  | big archive fixed-length or member header.  */ | 
|  |  | 
|  | static int | 
|  | xcoff_parse_decimal (const char *buf, size_t size, off_t *off) | 
|  | { | 
|  | char str[32]; | 
|  | char *end; | 
|  |  | 
|  | if (size >= sizeof str) | 
|  | return 0; | 
|  | memcpy (str, buf, size); | 
|  | str[size] = '\0'; | 
|  | *off = strtol (str, &end, 10); | 
|  | if (*end != '\0' && *end != ' ') | 
|  | return 0; | 
|  |  | 
|  | return 1; | 
|  | } | 
|  |  | 
|  | /* Add the backtrace data for a member of an AIX big archive. | 
|  | Returns 1 on success, 0 on failure.  */ | 
|  |  | 
|  | static int | 
|  | xcoff_armem_add (struct backtrace_state *state, int descriptor, | 
|  | uintptr_t base_address, const char *member, | 
|  | backtrace_error_callback error_callback, void *data, | 
|  | fileline *fileline_fn, int *found_sym) | 
|  | { | 
|  | struct backtrace_view view; | 
|  | b_ar_fl_hdr fl_hdr; | 
|  | const b_ar_hdr *ar_hdr; | 
|  | off_t off; | 
|  | off_t len; | 
|  | int memlen; | 
|  |  | 
|  | *found_sym = 0; | 
|  |  | 
|  | /* Map archive fixed-length header.  */ | 
|  |  | 
|  | if (!backtrace_get_view (state, descriptor, 0, sizeof (b_ar_fl_hdr), | 
|  | error_callback, data, &view)) | 
|  | goto fail; | 
|  |  | 
|  | memcpy (&fl_hdr, view.data, sizeof (b_ar_fl_hdr)); | 
|  |  | 
|  | backtrace_release_view (state, &view, error_callback, data); | 
|  |  | 
|  | if (memcmp (fl_hdr.fl_magic, AIAMAGBIG, 8) != 0) | 
|  | goto fail; | 
|  |  | 
|  | memlen = strlen (member); | 
|  |  | 
|  | /* Read offset of first archive member.  */ | 
|  | if (!xcoff_parse_decimal (fl_hdr.fl_fstmoff, sizeof fl_hdr.fl_fstmoff, &off)) | 
|  | goto fail; | 
|  | while (off != 0) | 
|  | { | 
|  | /* Map archive member header and member name.  */ | 
|  |  | 
|  | if (!backtrace_get_view (state, descriptor, off, | 
|  | sizeof (b_ar_hdr) + memlen, | 
|  | error_callback, data, &view)) | 
|  | break; | 
|  |  | 
|  | ar_hdr = (const b_ar_hdr *) view.data; | 
|  |  | 
|  | /* Read archive member name length.  */ | 
|  | if (!xcoff_parse_decimal (ar_hdr->ar_namlen, sizeof ar_hdr->ar_namlen, | 
|  | &len)) | 
|  | { | 
|  | backtrace_release_view (state, &view, error_callback, data); | 
|  | break; | 
|  | } | 
|  | if (len == memlen && !memcmp (ar_hdr->ar_name, member, memlen)) | 
|  | { | 
|  | off = (off + sizeof (b_ar_hdr) + memlen + 1) & ~1; | 
|  |  | 
|  | /* The archive can contain several members with the same name | 
|  | (e.g. 32-bit and 64-bit), so continue if not ok.  */ | 
|  |  | 
|  | if (xcoff_add (state, descriptor, off, base_address, error_callback, | 
|  | data, fileline_fn, found_sym, 0)) | 
|  | { | 
|  | backtrace_release_view (state, &view, error_callback, data); | 
|  | return 1; | 
|  | } | 
|  | } | 
|  |  | 
|  | /* Read offset of next archive member.  */ | 
|  | if (!xcoff_parse_decimal (ar_hdr->ar_nxtmem, sizeof ar_hdr->ar_nxtmem, | 
|  | &off)) | 
|  | { | 
|  | backtrace_release_view (state, &view, error_callback, data); | 
|  | break; | 
|  | } | 
|  | backtrace_release_view (state, &view, error_callback, data); | 
|  | } | 
|  |  | 
|  | fail: | 
|  | /* No matching member found.  */ | 
|  | backtrace_close (descriptor, error_callback, data); | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | /* Add the backtrace data for dynamically loaded libraries.  */ | 
|  |  | 
|  | static void | 
|  | xcoff_add_shared_libs (struct backtrace_state *state, | 
|  | backtrace_error_callback error_callback, | 
|  | void *data, fileline *fileline_fn, int *found_sym) | 
|  | { | 
|  | const struct ld_info *ldinfo; | 
|  | void *buf; | 
|  | unsigned int buflen; | 
|  | const char *member; | 
|  | int descriptor; | 
|  | int does_not_exist; | 
|  | int lib_found_sym; | 
|  | int ret; | 
|  |  | 
|  | /* Retrieve the list of loaded libraries.  */ | 
|  |  | 
|  | buf = NULL; | 
|  | buflen = 512; | 
|  | do | 
|  | { | 
|  | buf = realloc (buf, buflen); | 
|  | if (buf == NULL) | 
|  | { | 
|  | ret = -1; | 
|  | break; | 
|  | } | 
|  | ret = loadquery (L_GETINFO, buf, buflen); | 
|  | if (ret == 0) | 
|  | break; | 
|  | buflen *= 2; | 
|  | } | 
|  | while (ret == -1 && errno == ENOMEM); | 
|  | if (ret != 0) | 
|  | { | 
|  | free (buf); | 
|  | return; | 
|  | } | 
|  |  | 
|  | ldinfo = (const struct ld_info *) buf; | 
|  | while ((const char *) ldinfo < (const char *) buf + buflen) | 
|  | { | 
|  | if (*ldinfo->ldinfo_filename != '/') | 
|  | goto next; | 
|  |  | 
|  | descriptor = backtrace_open (ldinfo->ldinfo_filename, error_callback, | 
|  | data, &does_not_exist); | 
|  | if (descriptor < 0) | 
|  | goto next; | 
|  |  | 
|  | /* Check if it is an archive (member name not empty).  */ | 
|  |  | 
|  | member = ldinfo->ldinfo_filename + strlen (ldinfo->ldinfo_filename) + 1; | 
|  | if (*member) | 
|  | { | 
|  | xcoff_armem_add (state, descriptor, | 
|  | (uintptr_t) ldinfo->ldinfo_textorg, member, | 
|  | error_callback, data, fileline_fn, &lib_found_sym); | 
|  | } | 
|  | else | 
|  | { | 
|  | xcoff_add (state, descriptor, 0, (uintptr_t) ldinfo->ldinfo_textorg, | 
|  | error_callback, data, fileline_fn, &lib_found_sym, 0); | 
|  | } | 
|  | if (lib_found_sym) | 
|  | *found_sym = 1; | 
|  |  | 
|  | next: | 
|  | if (ldinfo->ldinfo_next == 0) | 
|  | break; | 
|  | ldinfo = (const struct ld_info *) ((const char *) ldinfo | 
|  | + ldinfo->ldinfo_next); | 
|  | } | 
|  |  | 
|  | free (buf); | 
|  | } | 
|  | #endif /* HAVE_LOADQUERY */ | 
|  |  | 
|  | /* Initialize the backtrace data we need from an XCOFF executable. | 
|  | Returns 1 on success, 0 on failure.  */ | 
|  |  | 
|  | int | 
|  | backtrace_initialize (struct backtrace_state *state, | 
|  | const char *filename ATTRIBUTE_UNUSED, int descriptor, | 
|  | backtrace_error_callback error_callback, | 
|  | void *data, fileline *fileline_fn) | 
|  | { | 
|  | int ret; | 
|  | int found_sym; | 
|  | fileline xcoff_fileline_fn = xcoff_nodebug; | 
|  |  | 
|  | ret = xcoff_add (state, descriptor, 0, 0, error_callback, data, | 
|  | &xcoff_fileline_fn, &found_sym, 1); | 
|  | if (!ret) | 
|  | return 0; | 
|  |  | 
|  | #ifdef HAVE_LOADQUERY | 
|  | xcoff_add_shared_libs (state, error_callback, data, &xcoff_fileline_fn, | 
|  | &found_sym); | 
|  | #endif | 
|  |  | 
|  | if (!state->threaded) | 
|  | { | 
|  | if (found_sym) | 
|  | state->syminfo_fn = xcoff_syminfo; | 
|  | else if (state->syminfo_fn == NULL) | 
|  | state->syminfo_fn = xcoff_nosyms; | 
|  | } | 
|  | else | 
|  | { | 
|  | if (found_sym) | 
|  | backtrace_atomic_store_pointer (&state->syminfo_fn, xcoff_syminfo); | 
|  | else | 
|  | (void) __sync_bool_compare_and_swap (&state->syminfo_fn, NULL, | 
|  | xcoff_nosyms); | 
|  | } | 
|  |  | 
|  | if (!state->threaded) | 
|  | { | 
|  | if (state->fileline_fn == NULL || state->fileline_fn == xcoff_nodebug) | 
|  | *fileline_fn = xcoff_fileline_fn; | 
|  | } | 
|  | else | 
|  | { | 
|  | fileline current_fn; | 
|  |  | 
|  | current_fn = backtrace_atomic_load_pointer (&state->fileline_fn); | 
|  | if (current_fn == NULL || current_fn == xcoff_nodebug) | 
|  | *fileline_fn = xcoff_fileline_fn; | 
|  | } | 
|  |  | 
|  | return 1; | 
|  | } |