| /* Symbol, variable and name lookup. | 
 |    Copyright (C) 2019-2024 Free Software Foundation, Inc. | 
 |  | 
 |    This file is part of libctf. | 
 |  | 
 |    libctf 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 3, 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; see the file COPYING.  If not see | 
 |    <http://www.gnu.org/licenses/>.  */ | 
 |  | 
 | #include <ctf-impl.h> | 
 | #include <elf.h> | 
 | #include <string.h> | 
 | #include <assert.h> | 
 |  | 
 | /* Grow the pptrtab so that it is at least NEW_LEN long.  */ | 
 | static int | 
 | grow_pptrtab (ctf_dict_t *fp, size_t new_len) | 
 | { | 
 |   uint32_t *new_pptrtab; | 
 |  | 
 |   if ((new_pptrtab = realloc (fp->ctf_pptrtab, sizeof (uint32_t) | 
 | 			      * new_len)) == NULL) | 
 |     return (ctf_set_errno (fp, ENOMEM)); | 
 |  | 
 |   fp->ctf_pptrtab = new_pptrtab; | 
 |  | 
 |   memset (fp->ctf_pptrtab + fp->ctf_pptrtab_len, 0, | 
 | 	  sizeof (uint32_t) * (new_len - fp->ctf_pptrtab_len)); | 
 |  | 
 |   fp->ctf_pptrtab_len = new_len; | 
 |   return 0; | 
 | } | 
 |  | 
 | /* Update entries in the pptrtab that relate to types newly added in the | 
 |    child.  */ | 
 | static int | 
 | refresh_pptrtab (ctf_dict_t *fp, ctf_dict_t *pfp) | 
 | { | 
 |   uint32_t i; | 
 |   for (i = fp->ctf_pptrtab_typemax; i <= fp->ctf_typemax; i++) | 
 |     { | 
 |       ctf_id_t type = LCTF_INDEX_TO_TYPE (fp, i, 1); | 
 |       ctf_id_t reffed_type; | 
 |  | 
 |       if (ctf_type_kind (fp, type) != CTF_K_POINTER) | 
 | 	continue; | 
 |  | 
 |       reffed_type = ctf_type_reference (fp, type); | 
 |  | 
 |       if (LCTF_TYPE_ISPARENT (fp, reffed_type)) | 
 | 	{ | 
 | 	  uint32_t idx = LCTF_TYPE_TO_INDEX (fp, reffed_type); | 
 |  | 
 | 	  /* Guard against references to invalid types.  No need to consider | 
 | 	     the CTF dict corrupt in this case: this pointer just can't be a | 
 | 	     pointer to any type we know about.  */ | 
 | 	  if (idx <= pfp->ctf_typemax) | 
 | 	    { | 
 | 	      if (idx >= fp->ctf_pptrtab_len | 
 | 		  && grow_pptrtab (fp, pfp->ctf_ptrtab_len) < 0) | 
 | 		return -1;			/* errno is set for us.  */ | 
 |  | 
 | 	      fp->ctf_pptrtab[idx] = i; | 
 | 	    } | 
 | 	} | 
 |     } | 
 |  | 
 |   fp->ctf_pptrtab_typemax = fp->ctf_typemax; | 
 |  | 
 |   return 0; | 
 | } | 
 |  | 
 | /* Compare the given input string and length against a table of known C storage | 
 |    qualifier keywords.  We just ignore these in ctf_lookup_by_name, below.  To | 
 |    do this quickly, we use a pre-computed Perfect Hash Function similar to the | 
 |    technique originally described in the classic paper: | 
 |  | 
 |    R.J. Cichelli, "Minimal Perfect Hash Functions Made Simple", | 
 |    Communications of the ACM, Volume 23, Issue 1, January 1980, pp. 17-19. | 
 |  | 
 |    For an input string S of length N, we use hash H = S[N - 1] + N - 105, which | 
 |    for the current set of qualifiers yields a unique H in the range [0 .. 20]. | 
 |    The hash can be modified when the keyword set changes as necessary.  We also | 
 |    store the length of each keyword and check it prior to the final strcmp(). | 
 |  | 
 |    TODO: just use gperf.  */ | 
 |  | 
 | static int | 
 | isqualifier (const char *s, size_t len) | 
 | { | 
 |   static const struct qual | 
 |   { | 
 |     const char *q_name; | 
 |     size_t q_len; | 
 |   } qhash[] = { | 
 |     {"static", 6}, {"", 0}, {"", 0}, {"", 0}, | 
 |     {"volatile", 8}, {"", 0}, {"", 0}, {"", 0}, {"", 0}, | 
 |     {"", 0}, {"auto", 4}, {"extern", 6}, {"", 0}, {"", 0}, | 
 |     {"", 0}, {"", 0}, {"const", 5}, {"register", 8}, | 
 |     {"", 0}, {"restrict", 8}, {"_Restrict", 9} | 
 |   }; | 
 |  | 
 |   int h = s[len - 1] + (int) len - 105; | 
 |   const struct qual *qp; | 
 |  | 
 |   if (h < 0 || (size_t) h >= sizeof (qhash) / sizeof (qhash[0])) | 
 |     return 0; | 
 |  | 
 |   qp = &qhash[h]; | 
 |  | 
 |   return ((size_t) len == qp->q_len && | 
 | 	  strncmp (qp->q_name, s, qp->q_len) == 0); | 
 | } | 
 |  | 
 | /* Attempt to convert the given C type name into the corresponding CTF type ID. | 
 |    It is not possible to do complete and proper conversion of type names | 
 |    without implementing a more full-fledged parser, which is necessary to | 
 |    handle things like types that are function pointers to functions that | 
 |    have arguments that are function pointers, and fun stuff like that. | 
 |    Instead, this function implements a very simple conversion algorithm that | 
 |    finds the things that we actually care about: structs, unions, enums, | 
 |    integers, floats, typedefs, and pointers to any of these named types.  */ | 
 |  | 
 | static ctf_id_t | 
 | ctf_lookup_by_name_internal (ctf_dict_t *fp, ctf_dict_t *child, | 
 | 			     const char *name) | 
 | { | 
 |   static const char delimiters[] = " \t\n\r\v\f*"; | 
 |  | 
 |   const ctf_lookup_t *lp; | 
 |   const char *p, *q, *end; | 
 |   ctf_id_t type = 0; | 
 |   ctf_id_t ntype, ptype; | 
 |  | 
 |   if (name == NULL) | 
 |     return (ctf_set_typed_errno (fp, EINVAL)); | 
 |  | 
 |   for (p = name, end = name + strlen (name); *p != '\0'; p = q) | 
 |     { | 
 |       while (isspace ((int) *p)) | 
 | 	p++;			/* Skip leading whitespace.  */ | 
 |  | 
 |       if (p == end) | 
 | 	break; | 
 |  | 
 |       if ((q = strpbrk (p + 1, delimiters)) == NULL) | 
 | 	q = end;		/* Compare until end.  */ | 
 |  | 
 |       if (*p == '*') | 
 | 	{ | 
 | 	  /* Find a pointer to type by looking in child->ctf_pptrtab (if child | 
 | 	     is set) and fp->ctf_ptrtab.  If we can't find a pointer to the | 
 | 	     given type, see if we can compute a pointer to the type resulting | 
 | 	     from resolving the type down to its base type and use that instead. | 
 | 	     This helps with cases where the CTF data includes "struct foo *" | 
 | 	     but not "foo_t *" and the user tries to access "foo_t *" in the | 
 | 	     debugger. | 
 |  | 
 | 	     There is extra complexity here because uninitialized elements in | 
 | 	     the pptrtab and ptrtab are set to zero, but zero (as the type ID | 
 | 	     meaning the unimplemented type) is a valid return type from | 
 | 	     ctf_lookup_by_name.  (Pointers to types are never of type 0, so | 
 | 	     this is unambiguous, just fiddly to deal with.)  */ | 
 |  | 
 | 	  uint32_t idx = LCTF_TYPE_TO_INDEX (fp, type); | 
 | 	  int in_child = 0; | 
 |  | 
 | 	  ntype = CTF_ERR; | 
 | 	  if (child && idx < child->ctf_pptrtab_len) | 
 | 	    { | 
 | 	      ntype = child->ctf_pptrtab[idx]; | 
 | 	      if (ntype) | 
 | 		in_child = 1; | 
 | 	      else | 
 | 		ntype = CTF_ERR; | 
 | 	    } | 
 |  | 
 | 	  if (ntype == CTF_ERR) | 
 | 	    { | 
 | 	      ntype = fp->ctf_ptrtab[idx]; | 
 | 	      if (ntype == 0) | 
 | 		ntype = CTF_ERR; | 
 | 	    } | 
 |  | 
 | 	  /* Try resolving to its base type and check again.  */ | 
 | 	  if (ntype == CTF_ERR) | 
 | 	    { | 
 | 	      if (child) | 
 | 		ntype = ctf_type_resolve_unsliced (child, type); | 
 | 	      else | 
 | 		ntype = ctf_type_resolve_unsliced (fp, type); | 
 |  | 
 | 	      if (ntype == CTF_ERR) | 
 | 		goto notype; | 
 |  | 
 | 	      idx = LCTF_TYPE_TO_INDEX (fp, ntype); | 
 |  | 
 | 	      ntype = CTF_ERR; | 
 | 	      if (child && idx < child->ctf_pptrtab_len) | 
 | 		{ | 
 | 		  ntype = child->ctf_pptrtab[idx]; | 
 | 		  if (ntype) | 
 | 		    in_child = 1; | 
 | 		  else | 
 | 		    ntype = CTF_ERR; | 
 | 		} | 
 |  | 
 | 	      if (ntype == CTF_ERR) | 
 | 		{ | 
 | 		  ntype = fp->ctf_ptrtab[idx]; | 
 | 		  if (ntype == 0) | 
 | 		    ntype = CTF_ERR; | 
 | 		} | 
 | 	      if (ntype == CTF_ERR) | 
 | 		goto notype; | 
 | 	    } | 
 |  | 
 | 	  type = LCTF_INDEX_TO_TYPE (fp, ntype, (fp->ctf_flags & LCTF_CHILD) | 
 | 				     || in_child); | 
 |  | 
 | 	  /* We are looking up a type in the parent, but the pointed-to type is | 
 | 	     in the child.  Switch to looking in the child: if we need to go | 
 | 	     back into the parent, we can recurse again.  */ | 
 | 	  if (in_child) | 
 | 	    { | 
 | 	      fp = child; | 
 | 	      child = NULL; | 
 | 	    } | 
 |  | 
 | 	  q = p + 1; | 
 | 	  continue; | 
 | 	} | 
 |  | 
 |       if (isqualifier (p, (size_t) (q - p))) | 
 | 	continue;		/* Skip qualifier keyword.  */ | 
 |  | 
 |       for (lp = fp->ctf_lookups; lp->ctl_prefix != NULL; lp++) | 
 | 	{ | 
 | 	  /* TODO: This is not MT-safe.  */ | 
 | 	  if ((lp->ctl_prefix[0] == '\0' || | 
 | 	       strncmp (p, lp->ctl_prefix, (size_t) (q - p)) == 0) && | 
 | 	      (size_t) (q - p) >= lp->ctl_len) | 
 | 	    { | 
 | 	      for (p += lp->ctl_len; isspace ((int) *p); p++) | 
 | 		continue;	/* Skip prefix and next whitespace.  */ | 
 |  | 
 | 	      if ((q = strchr (p, '*')) == NULL) | 
 | 		q = end;	/* Compare until end.  */ | 
 |  | 
 | 	      while (isspace ((int) q[-1])) | 
 | 		q--;		/* Exclude trailing whitespace.  */ | 
 |  | 
 | 	      /* Expand and/or allocate storage for a slice of the name, then | 
 | 		 copy it in.  */ | 
 |  | 
 | 	      if (fp->ctf_tmp_typeslicelen >= (size_t) (q - p) + 1) | 
 | 		{ | 
 | 		  memcpy (fp->ctf_tmp_typeslice, p, (size_t) (q - p)); | 
 | 		  fp->ctf_tmp_typeslice[(size_t) (q - p)] = '\0'; | 
 | 		} | 
 | 	      else | 
 | 		{ | 
 | 		  free (fp->ctf_tmp_typeslice); | 
 | 		  fp->ctf_tmp_typeslice = xstrndup (p, (size_t) (q - p)); | 
 | 		  if (fp->ctf_tmp_typeslice == NULL) | 
 | 		    return ctf_set_typed_errno (fp, ENOMEM); | 
 | 		} | 
 |  | 
 | 	      if ((type = (ctf_id_t) (uintptr_t) | 
 | 		   ctf_dynhash_lookup (lp->ctl_hash, | 
 | 				       fp->ctf_tmp_typeslice)) == 0) | 
 | 		goto notype; | 
 |  | 
 | 	      break; | 
 | 	    } | 
 | 	} | 
 |  | 
 |       if (lp->ctl_prefix == NULL) | 
 | 	goto notype; | 
 |     } | 
 |  | 
 |   if (*p != '\0' || type == 0) | 
 |     return (ctf_set_typed_errno (fp, ECTF_SYNTAX)); | 
 |  | 
 |   return type; | 
 |  | 
 |  notype: | 
 |   ctf_set_errno (fp, ECTF_NOTYPE); | 
 |   if (fp->ctf_parent != NULL) | 
 |     { | 
 |       /* Need to look up in the parent, from the child's perspective. | 
 | 	 Make sure the pptrtab is up to date.  */ | 
 |  | 
 |       if (fp->ctf_pptrtab_typemax < fp->ctf_typemax) | 
 | 	{ | 
 | 	  if (refresh_pptrtab (fp, fp->ctf_parent) < 0) | 
 | 	    return CTF_ERR;			/* errno is set for us.  */ | 
 | 	} | 
 |  | 
 |       if ((ptype = ctf_lookup_by_name_internal (fp->ctf_parent, fp, | 
 | 						name)) != CTF_ERR) | 
 | 	return ptype; | 
 |       return (ctf_set_typed_errno (fp, ctf_errno (fp->ctf_parent))); | 
 |     } | 
 |  | 
 |   return CTF_ERR; | 
 | } | 
 |  | 
 | ctf_id_t | 
 | ctf_lookup_by_name (ctf_dict_t *fp, const char *name) | 
 | { | 
 |   return ctf_lookup_by_name_internal (fp, NULL, name); | 
 | } | 
 |  | 
 | /* Return the pointer to the internal CTF type data corresponding to the | 
 |    given type ID.  If the ID is invalid, the function returns NULL. | 
 |    This function is not exported outside of the library.  */ | 
 |  | 
 | const ctf_type_t * | 
 | ctf_lookup_by_id (ctf_dict_t **fpp, ctf_id_t type) | 
 | { | 
 |   ctf_dict_t *fp = *fpp; | 
 |   ctf_id_t idx; | 
 |  | 
 |   if ((fp = ctf_get_dict (fp, type)) == NULL) | 
 |     { | 
 |       (void) ctf_set_errno (*fpp, ECTF_NOPARENT); | 
 |       return NULL; | 
 |     } | 
 |  | 
 |   idx = LCTF_TYPE_TO_INDEX (fp, type); | 
 |   if (idx > 0 && (unsigned long) idx <= fp->ctf_typemax) | 
 |     { | 
 |       *fpp = fp;		/* Possibly the parent CTF dict.  */ | 
 |       return (LCTF_INDEX_TO_TYPEPTR (fp, idx)); | 
 |     } | 
 |  | 
 |   (void) ctf_set_errno (*fpp, ECTF_BADID); | 
 |   return NULL; | 
 | } | 
 |  | 
 | typedef struct ctf_lookup_idx_key | 
 | { | 
 |   ctf_dict_t *clik_fp; | 
 |   const char *clik_name; | 
 |   uint32_t *clik_names; | 
 | } ctf_lookup_idx_key_t; | 
 |  | 
 | /* A bsearch function for variable names.  */ | 
 |  | 
 | static int | 
 | ctf_lookup_var (const void *key_, const void *lookup_) | 
 | { | 
 |   const ctf_lookup_idx_key_t *key = key_; | 
 |   const ctf_varent_t *lookup = lookup_; | 
 |  | 
 |   return (strcmp (key->clik_name, ctf_strptr (key->clik_fp, lookup->ctv_name))); | 
 | } | 
 |  | 
 | /* Given a variable name, return the type of the variable with that name. | 
 |    Look only in this dict, not in the parent. */ | 
 |  | 
 | ctf_id_t | 
 | ctf_lookup_variable_here (ctf_dict_t *fp, const char *name) | 
 | { | 
 |   ctf_dvdef_t *dvd = ctf_dvd_lookup (fp, name); | 
 |   ctf_varent_t *ent; | 
 |   ctf_lookup_idx_key_t key = { fp, name, NULL }; | 
 |  | 
 |   if (dvd != NULL) | 
 |     return dvd->dvd_type; | 
 |  | 
 |   /* This array is sorted, so we can bsearch for it.  */ | 
 |  | 
 |   ent = bsearch (&key, fp->ctf_vars, fp->ctf_nvars, sizeof (ctf_varent_t), | 
 | 		 ctf_lookup_var); | 
 |  | 
 |   if (ent == NULL) | 
 |       return (ctf_set_typed_errno (fp, ECTF_NOTYPEDAT)); | 
 |  | 
 |   return ent->ctv_type; | 
 | } | 
 |  | 
 | /* As above, but look in the parent too.  */ | 
 |  | 
 | ctf_id_t | 
 | ctf_lookup_variable (ctf_dict_t *fp, const char *name) | 
 | { | 
 |   ctf_id_t type; | 
 |  | 
 |   if ((type = ctf_lookup_variable_here (fp, name)) == CTF_ERR) | 
 |     { | 
 |       if (ctf_errno (fp) == ECTF_NOTYPEDAT && fp->ctf_parent != NULL) | 
 | 	{ | 
 | 	  if ((type = ctf_lookup_variable_here (fp->ctf_parent, name)) != CTF_ERR) | 
 | 	    return type; | 
 | 	  return (ctf_set_typed_errno (fp, ctf_errno (fp->ctf_parent))); | 
 | 	} | 
 |  | 
 |       return -1;				/* errno is set for us.  */ | 
 |     } | 
 |  | 
 |   return type; | 
 | } | 
 |  | 
 | /* Look up a single enumerator by enumeration constant name.  Returns the ID of | 
 |    the enum it is contained within and optionally its value.  Error out with | 
 |    ECTF_DUPLICATE if multiple exist (which can happen in some older dicts).  See | 
 |    ctf_lookup_enumerator_next in that case.  Enumeration constants in non-root | 
 |    types are not returned, but constants in parents are, if not overridden by | 
 |    an enum in the child..  */ | 
 |  | 
 | ctf_id_t | 
 | ctf_lookup_enumerator (ctf_dict_t *fp, const char *name, int64_t *enum_value) | 
 | { | 
 |   ctf_id_t type; | 
 |   int enum_int_value; | 
 |  | 
 |   if (ctf_dynset_lookup (fp->ctf_conflicting_enums, name)) | 
 |     return (ctf_set_typed_errno (fp, ECTF_DUPLICATE)); | 
 |  | 
 |   /* CTF_K_UNKNOWN suffices for things like enumeration constants that aren't | 
 |      actually types at all (ending up in the global name table).  */ | 
 |   type = ctf_lookup_by_rawname (fp, CTF_K_UNKNOWN, name); | 
 |   /* Nonexistent type? It may be in the parent.  */ | 
 |   if (type == 0 && fp->ctf_parent) | 
 |     { | 
 |       if ((type = ctf_lookup_enumerator (fp->ctf_parent, name, enum_value)) == 0) | 
 | 	return ctf_set_typed_errno (fp, ECTF_NOENUMNAM); | 
 |       return type; | 
 |     } | 
 |  | 
 |   /* Nothing more to do if this type didn't exist or we don't have to look up | 
 |      the enum value.  */ | 
 |   if (type == 0) | 
 |     return ctf_set_typed_errno (fp, ECTF_NOENUMNAM); | 
 |  | 
 |   if (enum_value == NULL) | 
 |     return type; | 
 |  | 
 |   if (ctf_enum_value (fp, type, name, &enum_int_value) < 0) | 
 |     return CTF_ERR; | 
 |   *enum_value = enum_int_value; | 
 |  | 
 |   return type; | 
 | } | 
 |  | 
 | /* Return all enumeration constants with a given name in a given dict, similar | 
 |    to ctf_lookup_enumerator above but capable of returning multiple values. | 
 |    Enumerators in parent dictionaries are not returned: enumerators in | 
 |    hidden types *are* returned.  */ | 
 |  | 
 | ctf_id_t | 
 | ctf_lookup_enumerator_next (ctf_dict_t *fp, const char *name, | 
 | 			    ctf_next_t **it, int64_t *val) | 
 | { | 
 |   ctf_next_t *i = *it; | 
 |   int found = 0; | 
 |  | 
 |   /* We use ctf_type_next() to iterate across all types, but then traverse each | 
 |      enumerator found by hand: traversing enumerators is very easy, and it would | 
 |      probably be more confusing to use two nested iterators than to do it this | 
 |      way.  We use ctn_next to work over enums, then ctn_en and ctn_n to work | 
 |      over enumerators within each enum.  */ | 
 |   if (!i) | 
 |     { | 
 |       if ((i = ctf_next_create ()) == NULL) | 
 | 	return ctf_set_typed_errno (fp, ENOMEM); | 
 |  | 
 |       i->cu.ctn_fp = fp; | 
 |       i->ctn_iter_fun = (void (*) (void)) ctf_lookup_enumerator_next; | 
 |       i->ctn_increment = 0; | 
 |       i->ctn_tp = NULL; | 
 |       i->u.ctn_en = NULL; | 
 |       i->ctn_n = 0; | 
 |       *it = i; | 
 |     } | 
 |  | 
 |   if ((void (*) (void)) ctf_lookup_enumerator_next != i->ctn_iter_fun) | 
 |     return (ctf_set_typed_errno (fp, ECTF_NEXT_WRONGFUN)); | 
 |  | 
 |   if (fp != i->cu.ctn_fp) | 
 |     return (ctf_set_typed_errno (fp, ECTF_NEXT_WRONGFP)); | 
 |  | 
 |   do | 
 |     { | 
 |       const char *this_name; | 
 |  | 
 |       /* At end of enum? Traverse to next one, if any are left.  */ | 
 |  | 
 |       if (i->u.ctn_en == NULL || i->ctn_n == 0) | 
 | 	{ | 
 | 	  const ctf_type_t *tp; | 
 | 	  ctf_dtdef_t *dtd; | 
 |  | 
 | 	  do | 
 | 	    i->ctn_type = ctf_type_next (i->cu.ctn_fp, &i->ctn_next, NULL, 1); | 
 | 	  while (i->ctn_type != CTF_ERR | 
 | 		 && ctf_type_kind_unsliced (i->cu.ctn_fp, i->ctn_type) | 
 | 		 != CTF_K_ENUM); | 
 |  | 
 | 	  if (i->ctn_type == CTF_ERR) | 
 | 	    { | 
 | 	      /* Conveniently, when the iterator over all types is done, so is the | 
 | 		 iteration as a whole: so we can just pass all errors from the | 
 | 		 internal iterator straight back out..  */ | 
 | 	      ctf_next_destroy (i); | 
 | 	      *it = NULL; | 
 | 	      return CTF_ERR;			/* errno is set for us.  */ | 
 | 	    } | 
 |  | 
 | 	  if ((tp = ctf_lookup_by_id (&fp, i->ctn_type)) == NULL) | 
 | 	    return CTF_ERR;			/* errno is set for us.  */ | 
 | 	  i->ctn_n = LCTF_INFO_VLEN (fp, tp->ctt_info); | 
 |  | 
 | 	  dtd = ctf_dynamic_type (fp, i->ctn_type); | 
 |  | 
 | 	  if (dtd == NULL) | 
 | 	    { | 
 | 	      (void) ctf_get_ctt_size (fp, tp, NULL, &i->ctn_increment); | 
 | 	      i->u.ctn_en = (const ctf_enum_t *) ((uintptr_t) tp + | 
 | 						  i->ctn_increment); | 
 | 	    } | 
 | 	  else | 
 | 	    i->u.ctn_en = (const ctf_enum_t *) dtd->dtd_vlen; | 
 | 	} | 
 |  | 
 |       this_name = ctf_strptr (fp, i->u.ctn_en->cte_name); | 
 |  | 
 |       i->ctn_n--; | 
 |  | 
 |       if (strcmp (name, this_name) == 0) | 
 | 	{ | 
 | 	  if (val) | 
 | 	    *val = i->u.ctn_en->cte_value; | 
 | 	  found = 1; | 
 |  | 
 | 	  /* Constant found in this enum: try the next one.  (Constant names | 
 | 	     cannot be duplicated within a given enum.)  */ | 
 |  | 
 | 	  i->ctn_n = 0; | 
 | 	} | 
 |  | 
 |       i->u.ctn_en++; | 
 |     } | 
 |   while (!found); | 
 |  | 
 |   return i->ctn_type; | 
 | } | 
 |  | 
 | typedef struct ctf_symidx_sort_arg_cb | 
 | { | 
 |   ctf_dict_t *fp; | 
 |   uint32_t *names; | 
 | } ctf_symidx_sort_arg_cb_t; | 
 |  | 
 | static int | 
 | sort_symidx_by_name (const void *one_, const void *two_, void *arg_) | 
 | { | 
 |   const uint32_t *one = one_; | 
 |   const uint32_t *two = two_; | 
 |   ctf_symidx_sort_arg_cb_t *arg = arg_; | 
 |  | 
 |   return (strcmp (ctf_strptr (arg->fp, arg->names[*one]), | 
 | 		  ctf_strptr (arg->fp, arg->names[*two]))); | 
 | } | 
 |  | 
 | /* Sort a symbol index section by name.  Takes a 1:1 mapping of names to the | 
 |    corresponding symbol table.  Returns a lexicographically sorted array of idx | 
 |    indexes (and thus, of indexes into the corresponding func info / data object | 
 |    section).  */ | 
 |  | 
 | static uint32_t * | 
 | ctf_symidx_sort (ctf_dict_t *fp, uint32_t *idx, size_t *nidx, | 
 | 			 size_t len) | 
 | { | 
 |   uint32_t *sorted; | 
 |   size_t i; | 
 |  | 
 |   if ((sorted = malloc (len)) == NULL) | 
 |     { | 
 |       ctf_set_errno (fp, ENOMEM); | 
 |       return NULL; | 
 |     } | 
 |  | 
 |   *nidx = len / sizeof (uint32_t); | 
 |   for (i = 0; i < *nidx; i++) | 
 |     sorted[i] = i; | 
 |  | 
 |   if (!(fp->ctf_header->cth_flags & CTF_F_IDXSORTED)) | 
 |     { | 
 |       ctf_symidx_sort_arg_cb_t arg = { fp, idx }; | 
 |       ctf_dprintf ("Index section unsorted: sorting.\n"); | 
 |       ctf_qsort_r (sorted, *nidx, sizeof (uint32_t), sort_symidx_by_name, &arg); | 
 |       fp->ctf_header->cth_flags |= CTF_F_IDXSORTED; | 
 |     } | 
 |  | 
 |   return sorted; | 
 | } | 
 |  | 
 | /* Given a symbol index, return the name of that symbol from the table provided | 
 |    by ctf_link_shuffle_syms, or failing that from the secondary string table, or | 
 |    the null string.  */ | 
 | static const char * | 
 | ctf_lookup_symbol_name (ctf_dict_t *fp, unsigned long symidx) | 
 | { | 
 |   const ctf_sect_t *sp = &fp->ctf_ext_symtab; | 
 |   ctf_link_sym_t sym; | 
 |   int err; | 
 |  | 
 |   if (fp->ctf_dynsymidx) | 
 |     { | 
 |       err = EINVAL; | 
 |       if (symidx > fp->ctf_dynsymmax) | 
 | 	goto try_parent; | 
 |  | 
 |       ctf_link_sym_t *symp = fp->ctf_dynsymidx[symidx]; | 
 |  | 
 |       if (!symp) | 
 | 	goto try_parent; | 
 |  | 
 |       return symp->st_name; | 
 |     } | 
 |  | 
 |   err = ECTF_NOSYMTAB; | 
 |   if (sp->cts_data == NULL) | 
 |     goto try_parent; | 
 |  | 
 |   if (symidx >= fp->ctf_nsyms) | 
 |     goto try_parent; | 
 |  | 
 |   switch (sp->cts_entsize) | 
 |     { | 
 |     case sizeof (Elf64_Sym): | 
 |       { | 
 | 	const Elf64_Sym *symp = (Elf64_Sym *) sp->cts_data + symidx; | 
 | 	ctf_elf64_to_link_sym (fp, &sym, symp, symidx); | 
 |       } | 
 |       break; | 
 |     case sizeof (Elf32_Sym): | 
 |       { | 
 | 	const Elf32_Sym *symp = (Elf32_Sym *) sp->cts_data + symidx; | 
 | 	ctf_elf32_to_link_sym (fp, &sym, symp, symidx); | 
 |       } | 
 |       break; | 
 |     default: | 
 |       ctf_set_errno (fp, ECTF_SYMTAB); | 
 |       return _CTF_NULLSTR; | 
 |     } | 
 |  | 
 |   assert (!sym.st_nameidx_set); | 
 |  | 
 |   return sym.st_name; | 
 |  | 
 |  try_parent: | 
 |   if (fp->ctf_parent) | 
 |     { | 
 |       const char *ret; | 
 |       ret = ctf_lookup_symbol_name (fp->ctf_parent, symidx); | 
 |       if (ret == NULL) | 
 | 	ctf_set_errno (fp, ctf_errno (fp->ctf_parent)); | 
 |       return ret; | 
 |     } | 
 |   else | 
 |     { | 
 |       ctf_set_errno (fp, err); | 
 |       return _CTF_NULLSTR; | 
 |     } | 
 | } | 
 |  | 
 | /* Given a symbol name, return the index of that symbol, or -1 on error or if | 
 |    not found.  If is_function is >= 0, return only function or data object | 
 |    symbols, respectively.  */ | 
 | static unsigned long | 
 | ctf_lookup_symbol_idx (ctf_dict_t *fp, const char *symname, int try_parent, | 
 | 		       int is_function) | 
 | { | 
 |   const ctf_sect_t *sp = &fp->ctf_ext_symtab; | 
 |   ctf_link_sym_t sym; | 
 |   void *known_idx; | 
 |   int err; | 
 |   ctf_dict_t *cache = fp; | 
 |  | 
 |   if (fp->ctf_dynsyms) | 
 |     { | 
 |       err = EINVAL; | 
 |  | 
 |       ctf_link_sym_t *symp; | 
 |  | 
 |       if (((symp = ctf_dynhash_lookup (fp->ctf_dynsyms, symname)) == NULL) | 
 | 	  || (symp->st_type != STT_OBJECT && is_function == 0) | 
 | 	  || (symp->st_type != STT_FUNC && is_function == 1)) | 
 | 	goto try_parent; | 
 |  | 
 |       return symp->st_symidx; | 
 |     } | 
 |  | 
 |   err = ECTF_NOSYMTAB; | 
 |   if (sp->cts_data == NULL) | 
 |     goto try_parent; | 
 |  | 
 |   /* First, try a hash lookup to see if we have already spotted this symbol | 
 |      during a past iteration: create the hash first if need be.  The | 
 |      lifespan of the strings is equal to the lifespan of the cts_data, so we | 
 |      don't need to strdup them.  If this dict was opened as part of an | 
 |      archive, and this archive has a crossdict_cache to cache results that | 
 |      are the same across all dicts in an archive, use it.  */ | 
 |  | 
 |   if (fp->ctf_archive && fp->ctf_archive->ctfi_crossdict_cache) | 
 |     cache = fp->ctf_archive->ctfi_crossdict_cache; | 
 |  | 
 |   if (!cache->ctf_symhash_func) | 
 |     if ((cache->ctf_symhash_func = ctf_dynhash_create (ctf_hash_string, | 
 | 						       ctf_hash_eq_string, | 
 | 						       NULL, NULL)) == NULL) | 
 |       goto oom; | 
 |  | 
 |   if (!cache->ctf_symhash_objt) | 
 |     if ((cache->ctf_symhash_objt = ctf_dynhash_create (ctf_hash_string, | 
 | 						       ctf_hash_eq_string, | 
 | 						       NULL, NULL)) == NULL) | 
 |       goto oom; | 
 |  | 
 |   if (is_function != 0 && | 
 |       ctf_dynhash_lookup_kv (cache->ctf_symhash_func, symname, NULL, &known_idx)) | 
 |     return (unsigned long) (uintptr_t) known_idx; | 
 |  | 
 |   if (is_function != 1 && | 
 |       ctf_dynhash_lookup_kv (cache->ctf_symhash_objt, symname, NULL, &known_idx)) | 
 |     return (unsigned long) (uintptr_t) known_idx; | 
 |  | 
 |   /* Hash lookup unsuccessful: linear search, populating the hashtab for later | 
 |      lookups as we go.  */ | 
 |  | 
 |   for (; cache->ctf_symhash_latest < sp->cts_size / sp->cts_entsize; | 
 |        cache->ctf_symhash_latest++) | 
 |     { | 
 |       ctf_dynhash_t *h; | 
 |  | 
 |       switch (sp->cts_entsize) | 
 | 	{ | 
 | 	case sizeof (Elf64_Sym): | 
 | 	  { | 
 | 	    Elf64_Sym *symp = (Elf64_Sym *) sp->cts_data; | 
 |  | 
 | 	    ctf_elf64_to_link_sym (fp, &sym, &symp[cache->ctf_symhash_latest], | 
 | 				   cache->ctf_symhash_latest); | 
 | 	  } | 
 | 	  break; | 
 | 	case sizeof (Elf32_Sym): | 
 | 	  { | 
 | 	    Elf32_Sym *symp = (Elf32_Sym *) sp->cts_data; | 
 | 	    ctf_elf32_to_link_sym (fp, &sym, &symp[cache->ctf_symhash_latest], | 
 | 				   cache->ctf_symhash_latest); | 
 | 	    break; | 
 | 	  } | 
 | 	default: | 
 | 	  ctf_set_errno (fp, ECTF_SYMTAB); | 
 | 	  return (unsigned long) -1; | 
 | 	} | 
 |  | 
 |       if (sym.st_type == STT_FUNC) | 
 | 	h = cache->ctf_symhash_func; | 
 |       else if (sym.st_type == STT_OBJECT) | 
 | 	h = cache->ctf_symhash_objt; | 
 |       else | 
 | 	continue;					/* Not of interest.  */ | 
 |  | 
 |       if (!ctf_dynhash_lookup_kv (h, sym.st_name, | 
 | 				  NULL, NULL)) | 
 | 	if (ctf_dynhash_cinsert (h, sym.st_name, | 
 | 				 (const void *) (uintptr_t) | 
 | 				 cache->ctf_symhash_latest) < 0) | 
 | 	  goto oom; | 
 |       if (strcmp (sym.st_name, symname) == 0) | 
 | 	return cache->ctf_symhash_latest++; | 
 |     } | 
 |  | 
 |   /* Searched everything, still not found.  */ | 
 |  | 
 |   return (unsigned long) -1; | 
 |  | 
 |  try_parent: | 
 |   if (fp->ctf_parent && try_parent) | 
 |     { | 
 |       unsigned long psym; | 
 |  | 
 |       if ((psym = ctf_lookup_symbol_idx (fp->ctf_parent, symname, try_parent, | 
 | 					 is_function)) | 
 |           != (unsigned long) -1) | 
 |         return psym; | 
 |  | 
 |       ctf_set_errno (fp, ctf_errno (fp->ctf_parent)); | 
 |       return (unsigned long) -1; | 
 |     } | 
 |   else | 
 |     { | 
 |       ctf_set_errno (fp, err); | 
 |       return (unsigned long) -1; | 
 |     } | 
 | oom: | 
 |   ctf_set_errno (fp, ENOMEM); | 
 |   ctf_err_warn (fp, 0, 0, _("cannot allocate memory for symbol " | 
 | 			    "lookup hashtab")); | 
 |   return (unsigned long) -1; | 
 |  | 
 | } | 
 |  | 
 | ctf_id_t | 
 | ctf_symbol_next_static (ctf_dict_t *fp, ctf_next_t **it, const char **name, | 
 | 			int functions); | 
 |  | 
 | /* Iterate over all symbols with types: if FUNC, function symbols, | 
 |    otherwise, data symbols.  The name argument is not optional.  The return | 
 |    order is arbitrary, though is likely to be in symbol index or name order. | 
 |    Changing the value of 'functions' in the middle of iteration has | 
 |    unpredictable effects (probably skipping symbols, etc) and is not | 
 |    recommended.  Adding symbols while iteration is underway may also lead | 
 |    to other symbols being skipped.  */ | 
 |  | 
 | ctf_id_t | 
 | ctf_symbol_next (ctf_dict_t *fp, ctf_next_t **it, const char **name, | 
 | 		 int functions) | 
 | { | 
 |   ctf_id_t sym = CTF_ERR; | 
 |   ctf_next_t *i = *it; | 
 |   int err; | 
 |  | 
 |   if (!i) | 
 |     { | 
 |       if ((i = ctf_next_create ()) == NULL) | 
 | 	return ctf_set_typed_errno (fp, ENOMEM); | 
 |  | 
 |       i->cu.ctn_fp = fp; | 
 |       i->ctn_iter_fun = (void (*) (void)) ctf_symbol_next; | 
 |       i->ctn_n = 0; | 
 |       *it = i; | 
 |     } | 
 |  | 
 |   if ((void (*) (void)) ctf_symbol_next != i->ctn_iter_fun) | 
 |     return (ctf_set_typed_errno (fp, ECTF_NEXT_WRONGFUN)); | 
 |  | 
 |   if (fp != i->cu.ctn_fp) | 
 |     return (ctf_set_typed_errno (fp, ECTF_NEXT_WRONGFP)); | 
 |  | 
 |   /* Check the dynamic set of names first, to allow previously-written names | 
 |      to be replaced with dynamic ones (there is still no way to remove them, | 
 |      though). | 
 |  | 
 |      We intentionally use raw access, not ctf_lookup_by_symbol, to avoid | 
 |      incurring additional sorting cost for unsorted symtypetabs coming from the | 
 |      compiler, to allow ctf_symbol_next to work in the absence of a symtab, and | 
 |      finally because it's easier to work out what the name of each symbol is if | 
 |      we do that.  */ | 
 |  | 
 |   ctf_dynhash_t *dynh = functions ? fp->ctf_funchash : fp->ctf_objthash; | 
 |   void *dyn_name = NULL, *dyn_value = NULL; | 
 |   size_t dyn_els = dynh ? ctf_dynhash_elements (dynh) : 0; | 
 |  | 
 |   if (i->ctn_n < dyn_els) | 
 |     { | 
 |       err = ctf_dynhash_next (dynh, &i->ctn_next, &dyn_name, &dyn_value); | 
 |  | 
 |       /* This covers errors and also end-of-iteration.  */ | 
 |       if (err != 0) | 
 | 	{ | 
 | 	  ctf_next_destroy (i); | 
 | 	  *it = NULL; | 
 | 	  return ctf_set_typed_errno (fp, err); | 
 | 	} | 
 |  | 
 |       *name = dyn_name; | 
 |       sym = (ctf_id_t) (uintptr_t) dyn_value; | 
 |       i->ctn_n++; | 
 |  | 
 |       return sym; | 
 |     } | 
 |  | 
 |   return ctf_symbol_next_static (fp, it, name, functions); | 
 | } | 
 |  | 
 | /* ctf_symbol_next, but only for static symbols.  Mostly an internal | 
 |    implementation detail of ctf_symbol_next, but also used to simplify | 
 |    serialization.  */ | 
 | ctf_id_t | 
 | ctf_symbol_next_static (ctf_dict_t *fp, ctf_next_t **it, const char **name, | 
 | 			int functions) | 
 | { | 
 |   ctf_id_t sym = CTF_ERR; | 
 |   ctf_next_t *i = *it; | 
 |   ctf_dynhash_t *dynh = functions ? fp->ctf_funchash : fp->ctf_objthash; | 
 |   size_t dyn_els = dynh ? ctf_dynhash_elements (dynh) : 0; | 
 |  | 
 |   /* Only relevant for direct internal-to-library calls, not via | 
 |      ctf_symbol_next (but important then).  */ | 
 |  | 
 |   if (!i) | 
 |     { | 
 |       if ((i = ctf_next_create ()) == NULL) | 
 | 	return ctf_set_typed_errno (fp, ENOMEM); | 
 |  | 
 |       i->cu.ctn_fp = fp; | 
 |       i->ctn_iter_fun = (void (*) (void)) ctf_symbol_next; | 
 |       i->ctn_n = dyn_els; | 
 |       *it = i; | 
 |     } | 
 |  | 
 |   if ((void (*) (void)) ctf_symbol_next != i->ctn_iter_fun) | 
 |     return (ctf_set_typed_errno (fp, ECTF_NEXT_WRONGFUN)); | 
 |  | 
 |   if (fp != i->cu.ctn_fp) | 
 |     return (ctf_set_typed_errno (fp, ECTF_NEXT_WRONGFP)); | 
 |  | 
 |   /* TODO-v4: Indexed after non-indexed portions?  */ | 
 |  | 
 |   if ((!functions && fp->ctf_objtidx_names) || | 
 |       (functions && fp->ctf_funcidx_names)) | 
 |     { | 
 |       ctf_header_t *hp = fp->ctf_header; | 
 |       uint32_t *idx = functions ? fp->ctf_funcidx_names : fp->ctf_objtidx_names; | 
 |       uint32_t *tab; | 
 |       size_t len; | 
 |  | 
 |       if (functions) | 
 | 	{ | 
 | 	  len = (hp->cth_varoff - hp->cth_funcidxoff) / sizeof (uint32_t); | 
 | 	  tab = (uint32_t *) (fp->ctf_buf + hp->cth_funcoff); | 
 | 	} | 
 |       else | 
 | 	{ | 
 | 	  len = (hp->cth_funcidxoff - hp->cth_objtidxoff) / sizeof (uint32_t); | 
 | 	  tab = (uint32_t *) (fp->ctf_buf + hp->cth_objtoff); | 
 | 	} | 
 |  | 
 |       do | 
 | 	{ | 
 | 	  if (i->ctn_n - dyn_els >= len) | 
 | 	    goto end; | 
 |  | 
 | 	  *name = ctf_strptr (fp, idx[i->ctn_n - dyn_els]); | 
 | 	  sym = tab[i->ctn_n - dyn_els]; | 
 | 	  i->ctn_n++; | 
 | 	} | 
 |       while (sym == -1u || sym == 0); | 
 |     } | 
 |   else | 
 |     { | 
 |       /* Skip over pads in ctf_sxlate, padding for typeless symbols in the | 
 | 	 symtypetab itself, and symbols in the wrong table.  */ | 
 |       for (; i->ctn_n - dyn_els < fp->ctf_nsyms; i->ctn_n++) | 
 | 	{ | 
 | 	  ctf_header_t *hp = fp->ctf_header; | 
 | 	  size_t n = i->ctn_n - dyn_els; | 
 |  | 
 | 	  if (fp->ctf_sxlate[n] == -1u) | 
 | 	    continue; | 
 |  | 
 | 	  sym = *(uint32_t *) ((uintptr_t) fp->ctf_buf + fp->ctf_sxlate[n]); | 
 |  | 
 | 	  if (sym == 0) | 
 | 	    continue; | 
 |  | 
 | 	  if (functions) | 
 | 	    { | 
 | 	      if (fp->ctf_sxlate[n] >= hp->cth_funcoff | 
 | 		  && fp->ctf_sxlate[n] < hp->cth_objtidxoff) | 
 | 		break; | 
 | 	    } | 
 | 	  else | 
 | 	    { | 
 | 	      if (fp->ctf_sxlate[n] >= hp->cth_objtoff | 
 | 		  && fp->ctf_sxlate[n] < hp->cth_funcoff) | 
 | 		break; | 
 | 	    } | 
 | 	} | 
 |  | 
 |       if (i->ctn_n - dyn_els >= fp->ctf_nsyms) | 
 | 	goto end; | 
 |  | 
 |       *name = ctf_lookup_symbol_name (fp, i->ctn_n - dyn_els); | 
 |       i->ctn_n++; | 
 |     } | 
 |  | 
 |   return sym; | 
 |  | 
 |  end: | 
 |   ctf_next_destroy (i); | 
 |   *it = NULL; | 
 |   return (ctf_set_typed_errno (fp, ECTF_NEXT_END)); | 
 | } | 
 |  | 
 | /* A bsearch function for function and object index names.  */ | 
 |  | 
 | static int | 
 | ctf_lookup_idx_name (const void *key_, const void *idx_) | 
 | { | 
 |   const ctf_lookup_idx_key_t *key = key_; | 
 |   const uint32_t *idx = idx_; | 
 |  | 
 |   return (strcmp (key->clik_name, ctf_strptr (key->clik_fp, key->clik_names[*idx]))); | 
 | } | 
 |  | 
 | /* Given a symbol name or (failing that) number, look up that symbol in the | 
 |    function or object index table (which must exist).  Return 0 if not found | 
 |    there (or pad).  */ | 
 |  | 
 | static ctf_id_t | 
 | ctf_try_lookup_indexed (ctf_dict_t *fp, unsigned long symidx, | 
 | 			const char *symname, int is_function) | 
 | { | 
 |   struct ctf_header *hp = fp->ctf_header; | 
 |   uint32_t *symtypetab; | 
 |   uint32_t *names; | 
 |   uint32_t *sxlate; | 
 |   size_t nidx; | 
 |  | 
 |   if (symname == NULL) | 
 |     symname = ctf_lookup_symbol_name (fp, symidx); | 
 |  | 
 |   /* Dynamic dict with no static portion: just return.  */ | 
 |   if (!hp) | 
 |     { | 
 |       ctf_dprintf ("%s not found in idx: dict is dynamic\n", symname); | 
 |       return 0; | 
 |     } | 
 |  | 
 |   ctf_dprintf ("Looking up type of object with symtab idx %lx or name %s in " | 
 | 	       "indexed symtypetab\n", symidx, symname); | 
 |  | 
 |   if (symname[0] == '\0') | 
 |     return CTF_ERR;					/* errno is set for us.  */ | 
 |  | 
 |   if (is_function) | 
 |     { | 
 |       if (!fp->ctf_funcidx_sxlate) | 
 | 	{ | 
 | 	  if ((fp->ctf_funcidx_sxlate | 
 | 	       = ctf_symidx_sort (fp, (uint32_t *) | 
 | 				  (fp->ctf_buf + hp->cth_funcidxoff), | 
 | 				  &fp->ctf_nfuncidx, | 
 | 				  hp->cth_varoff - hp->cth_funcidxoff)) | 
 | 	      == NULL) | 
 | 	    { | 
 | 	      ctf_err_warn (fp, 0, 0, _("cannot sort function symidx")); | 
 | 	      return CTF_ERR;				/* errno is set for us.  */ | 
 | 	    } | 
 | 	} | 
 |       symtypetab = (uint32_t *) (fp->ctf_buf + hp->cth_funcoff); | 
 |       sxlate = fp->ctf_funcidx_sxlate; | 
 |       names = fp->ctf_funcidx_names; | 
 |       nidx = fp->ctf_nfuncidx; | 
 |     } | 
 |   else | 
 |     { | 
 |       if (!fp->ctf_objtidx_sxlate) | 
 | 	{ | 
 | 	  if ((fp->ctf_objtidx_sxlate | 
 | 	       = ctf_symidx_sort (fp, (uint32_t *) | 
 | 				  (fp->ctf_buf + hp->cth_objtidxoff), | 
 | 				  &fp->ctf_nobjtidx, | 
 | 				  hp->cth_funcidxoff - hp->cth_objtidxoff)) | 
 | 	      == NULL) | 
 | 	    { | 
 | 	      ctf_err_warn (fp, 0, 0, _("cannot sort object symidx")); | 
 | 	      return CTF_ERR;				/* errno is set for us. */ | 
 | 	    } | 
 | 	} | 
 |  | 
 |       symtypetab = (uint32_t *) (fp->ctf_buf + hp->cth_objtoff); | 
 |       sxlate = fp->ctf_objtidx_sxlate; | 
 |       names = fp->ctf_objtidx_names; | 
 |       nidx = fp->ctf_nobjtidx; | 
 |     } | 
 |  | 
 |   ctf_lookup_idx_key_t key = { fp, symname, names }; | 
 |   uint32_t *idx; | 
 |  | 
 |   idx = bsearch (&key, sxlate, nidx, sizeof (uint32_t), ctf_lookup_idx_name); | 
 |  | 
 |   if (!idx) | 
 |     { | 
 |       ctf_dprintf ("%s not found in idx\n", symname); | 
 |       return 0; | 
 |     } | 
 |  | 
 |   /* Should be impossible, but be paranoid.  */ | 
 |   if ((idx - sxlate) > (ptrdiff_t) nidx) | 
 |     return (ctf_set_typed_errno (fp, ECTF_CORRUPT)); | 
 |  | 
 |   ctf_dprintf ("Symbol %lx (%s) is of type %x\n", symidx, symname, | 
 | 	       symtypetab[*idx]); | 
 |   return symtypetab[*idx]; | 
 | } | 
 |  | 
 | /* Given a symbol name or (if NULL) symbol index, return the type of the | 
 |    function or data object described by the corresponding entry in the symbol | 
 |    table.  We can only return symbols in read-only dicts and in dicts for which | 
 |    ctf_link_shuffle_syms has been called to assign symbol indexes to symbol | 
 |    names. | 
 |  | 
 |    If try_parent is false, do not check the parent dict too. | 
 |  | 
 |    If is_function is > -1, only look for data objects or functions in | 
 |    particular.  */ | 
 |  | 
 | ctf_id_t | 
 | ctf_lookup_by_sym_or_name (ctf_dict_t *fp, unsigned long symidx, | 
 | 			   const char *symname, int try_parent, | 
 | 			   int is_function) | 
 | { | 
 |   const ctf_sect_t *sp = &fp->ctf_ext_symtab; | 
 |   ctf_id_t type = 0; | 
 |   int err = 0; | 
 |  | 
 |   /* Shuffled dynsymidx present?  Use that.  For now, the dynsymidx and | 
 |      shuffled-symbol lookup only support dynamically-added symbols, because | 
 |      this interface is meant for use by linkers, and linkers are only going | 
 |      to report symbols against newly-created, freshly-ctf_link'ed dicts: so | 
 |      there will be no static component in any case.  */ | 
 |   if (fp->ctf_dynsymidx) | 
 |     { | 
 |       const ctf_link_sym_t *sym; | 
 |  | 
 |       if (symname) | 
 | 	ctf_dprintf ("Looking up type of object with symname %s in " | 
 | 		     "writable dict symtypetab\n", symname); | 
 |       else | 
 | 	ctf_dprintf ("Looking up type of object with symtab idx %lx in " | 
 | 		     "writable dict symtypetab\n", symidx); | 
 |  | 
 |       /* No name? Need to look it up.  */ | 
 |       if (!symname) | 
 | 	{ | 
 | 	  err = EINVAL; | 
 | 	  if (symidx > fp->ctf_dynsymmax) | 
 | 	    goto try_parent; | 
 |  | 
 | 	  sym = fp->ctf_dynsymidx[symidx]; | 
 | 	  err = ECTF_NOTYPEDAT; | 
 | 	  if (!sym || (sym->st_type != STT_OBJECT && sym->st_type != STT_FUNC) | 
 | 	      || (sym->st_type != STT_OBJECT && is_function == 0) | 
 | 	      || (sym->st_type != STT_FUNC && is_function == 1)) | 
 | 	    goto try_parent; | 
 |  | 
 | 	  if (!ctf_assert (fp, !sym->st_nameidx_set)) | 
 | 	    return CTF_ERR; | 
 | 	  symname = sym->st_name; | 
 |      } | 
 |  | 
 |       if (fp->ctf_objthash == NULL | 
 | 	  || is_function == 1 | 
 | 	  || (type = (ctf_id_t) (uintptr_t) | 
 | 	      ctf_dynhash_lookup (fp->ctf_objthash, symname)) == 0) | 
 | 	{ | 
 | 	  if (fp->ctf_funchash == NULL | 
 | 	      || is_function == 0 | 
 | 	      || (type = (ctf_id_t) (uintptr_t) | 
 | 		  ctf_dynhash_lookup (fp->ctf_funchash, symname)) == 0) | 
 | 	    goto try_parent; | 
 | 	} | 
 |  | 
 |       return type; | 
 |     } | 
 |  | 
 |   /* Dict not shuffled: look for a dynamic sym first, and look it up | 
 |      directly.  */ | 
 |   if (symname) | 
 |     { | 
 |       if (fp->ctf_objthash != NULL | 
 | 	  && is_function != 1 | 
 | 	  && ((type = (ctf_id_t) (uintptr_t) | 
 | 	       ctf_dynhash_lookup (fp->ctf_objthash, symname)) != 0)) | 
 | 	return type; | 
 |  | 
 |       if (fp->ctf_funchash != NULL | 
 | 	  && is_function != 0 | 
 | 	  && ((type = (ctf_id_t) (uintptr_t) | 
 | 	       ctf_dynhash_lookup (fp->ctf_funchash, symname)) != 0)) | 
 | 	return type; | 
 |     } | 
 |  | 
 |   err = ECTF_NOSYMTAB; | 
 |   if (sp->cts_data == NULL && symname == NULL && | 
 |       ((is_function && !fp->ctf_funcidx_names) || | 
 |        (!is_function && !fp->ctf_objtidx_names))) | 
 |     goto try_parent; | 
 |  | 
 |   /* This covers both out-of-range lookups by index and a dynamic dict which | 
 |      hasn't been shuffled yet.  */ | 
 |   err = EINVAL; | 
 |   if (symname == NULL && symidx >= fp->ctf_nsyms) | 
 |     goto try_parent; | 
 |  | 
 |   /* Try an indexed lookup.  */ | 
 |  | 
 |   if (fp->ctf_objtidx_names && is_function != 1) | 
 |     { | 
 |       if ((type = ctf_try_lookup_indexed (fp, symidx, symname, 0)) == CTF_ERR) | 
 | 	return CTF_ERR;				/* errno is set for us.  */ | 
 |     } | 
 |   if (type == 0 && fp->ctf_funcidx_names && is_function != 0) | 
 |     { | 
 |       if ((type = ctf_try_lookup_indexed (fp, symidx, symname, 1)) == CTF_ERR) | 
 | 	return CTF_ERR;				/* errno is set for us.  */ | 
 |     } | 
 |   if (type != 0) | 
 |     return type; | 
 |  | 
 |   /* Indexed but no symbol found -> not present, try the parent.  */ | 
 |   err = ECTF_NOTYPEDAT; | 
 |   if (fp->ctf_objtidx_names && fp->ctf_funcidx_names) | 
 |     goto try_parent; | 
 |  | 
 |   /* Table must be nonindexed.  */ | 
 |  | 
 |   ctf_dprintf ("Looking up object type %lx in 1:1 dict symtypetab\n", symidx); | 
 |  | 
 |   if (symname != NULL) | 
 |     if ((symidx = ctf_lookup_symbol_idx (fp, symname, try_parent, is_function)) | 
 | 	== (unsigned long) -1) | 
 |       goto try_parent; | 
 |  | 
 |   if (fp->ctf_sxlate[symidx] == -1u) | 
 |     goto try_parent; | 
 |  | 
 |   type = *(uint32_t *) ((uintptr_t) fp->ctf_buf + fp->ctf_sxlate[symidx]); | 
 |  | 
 |   if (type == 0) | 
 |     goto try_parent; | 
 |  | 
 |   return type; | 
 |  | 
 |  try_parent: | 
 |   if (!try_parent) | 
 |     return ctf_set_errno (fp, err); | 
 |  | 
 |   if (fp->ctf_parent) | 
 |     { | 
 |       ctf_id_t ret = ctf_lookup_by_sym_or_name (fp->ctf_parent, symidx, | 
 | 						symname, try_parent, | 
 | 						is_function); | 
 |       if (ret == CTF_ERR) | 
 | 	ctf_set_errno (fp, ctf_errno (fp->ctf_parent)); | 
 |       return ret; | 
 |     } | 
 |   else | 
 |     return (ctf_set_typed_errno (fp, err)); | 
 | } | 
 |  | 
 | /* Given a symbol table index, return the type of the function or data object | 
 |    described by the corresponding entry in the symbol table.  */ | 
 | ctf_id_t | 
 | ctf_lookup_by_symbol (ctf_dict_t *fp, unsigned long symidx) | 
 | { | 
 |   return ctf_lookup_by_sym_or_name (fp, symidx, NULL, 1, -1); | 
 | } | 
 |  | 
 | /* Given a symbol name, return the type of the function or data object described | 
 |    by the corresponding entry in the symbol table.  */ | 
 | ctf_id_t | 
 | ctf_lookup_by_symbol_name (ctf_dict_t *fp, const char *symname) | 
 | { | 
 |   return ctf_lookup_by_sym_or_name (fp, 0, symname, 1, -1); | 
 | } | 
 |  | 
 | /* Given a symbol table index, return the info for the function described | 
 |    by the corresponding entry in the symbol table, which may be a function | 
 |    symbol or may be a data symbol that happens to be a function pointer.  */ | 
 |  | 
 | int | 
 | ctf_func_info (ctf_dict_t *fp, unsigned long symidx, ctf_funcinfo_t *fip) | 
 | { | 
 |   ctf_id_t type; | 
 |  | 
 |   if ((type = ctf_lookup_by_symbol (fp, symidx)) == CTF_ERR) | 
 |     return -1;					/* errno is set for us.  */ | 
 |  | 
 |   if (ctf_type_kind (fp, type) != CTF_K_FUNCTION) | 
 |     return (ctf_set_errno (fp, ECTF_NOTFUNC)); | 
 |  | 
 |   return ctf_func_type_info (fp, type, fip); | 
 | } | 
 |  | 
 | /* Given a symbol table index, return the arguments for the function described | 
 |    by the corresponding entry in the symbol table.  */ | 
 |  | 
 | int | 
 | ctf_func_args (ctf_dict_t *fp, unsigned long symidx, uint32_t argc, | 
 | 	       ctf_id_t *argv) | 
 | { | 
 |   ctf_id_t type; | 
 |  | 
 |   if ((type = ctf_lookup_by_symbol (fp, symidx)) == CTF_ERR) | 
 |     return -1;					/* errno is set for us.  */ | 
 |  | 
 |   if (ctf_type_kind (fp, type) != CTF_K_FUNCTION) | 
 |     return (ctf_set_errno (fp, ECTF_NOTFUNC)); | 
 |  | 
 |   return ctf_func_type_args (fp, type, argc, argv); | 
 | } |