xref: /netbsd-src/external/gpl3/binutils/dist/libctf/ctf-util.c (revision cb63e24e8d6aae7ddac1859a9015f48b1d8bd90e)
16f4ced0bSchristos /* Miscellaneous utilities.
2*cb63e24eSchristos    Copyright (C) 2019-2024 Free Software Foundation, Inc.
36f4ced0bSchristos 
46f4ced0bSchristos    This file is part of libctf.
56f4ced0bSchristos 
66f4ced0bSchristos    libctf is free software; you can redistribute it and/or modify it under
76f4ced0bSchristos    the terms of the GNU General Public License as published by the Free
86f4ced0bSchristos    Software Foundation; either version 3, or (at your option) any later
96f4ced0bSchristos    version.
106f4ced0bSchristos 
116f4ced0bSchristos    This program is distributed in the hope that it will be useful, but
126f4ced0bSchristos    WITHOUT ANY WARRANTY; without even the implied warranty of
136f4ced0bSchristos    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
146f4ced0bSchristos    See the GNU General Public License for more details.
156f4ced0bSchristos 
166f4ced0bSchristos    You should have received a copy of the GNU General Public License
176f4ced0bSchristos    along with this program; see the file COPYING.  If not see
186f4ced0bSchristos    <http://www.gnu.org/licenses/>.  */
196f4ced0bSchristos 
206f4ced0bSchristos #include <ctf-impl.h>
216f4ced0bSchristos #include <string.h>
224f645668Schristos #include "ctf-endian.h"
236f4ced0bSchristos 
246f4ced0bSchristos /* Simple doubly-linked list append routine.  This implementation assumes that
256f4ced0bSchristos    each list element contains an embedded ctf_list_t as the first member.
266f4ced0bSchristos    An additional ctf_list_t is used to store the head (l_next) and tail
276f4ced0bSchristos    (l_prev) pointers.  The current head and tail list elements have their
286f4ced0bSchristos    previous and next pointers set to NULL, respectively.  */
296f4ced0bSchristos 
306f4ced0bSchristos void
ctf_list_append(ctf_list_t * lp,void * newp)316f4ced0bSchristos ctf_list_append (ctf_list_t *lp, void *newp)
326f4ced0bSchristos {
336f4ced0bSchristos   ctf_list_t *p = lp->l_prev;	/* p = tail list element.  */
346f4ced0bSchristos   ctf_list_t *q = newp;		/* q = new list element.  */
356f4ced0bSchristos 
366f4ced0bSchristos   lp->l_prev = q;
376f4ced0bSchristos   q->l_prev = p;
386f4ced0bSchristos   q->l_next = NULL;
396f4ced0bSchristos 
406f4ced0bSchristos   if (p != NULL)
416f4ced0bSchristos     p->l_next = q;
426f4ced0bSchristos   else
436f4ced0bSchristos     lp->l_next = q;
446f4ced0bSchristos }
456f4ced0bSchristos 
466f4ced0bSchristos /* Prepend the specified existing element to the given ctf_list_t.  The
476f4ced0bSchristos    existing pointer should be pointing at a struct with embedded ctf_list_t.  */
486f4ced0bSchristos 
496f4ced0bSchristos void
ctf_list_prepend(ctf_list_t * lp,void * newp)506f4ced0bSchristos ctf_list_prepend (ctf_list_t * lp, void *newp)
516f4ced0bSchristos {
526f4ced0bSchristos   ctf_list_t *p = newp;		/* p = new list element.  */
536f4ced0bSchristos   ctf_list_t *q = lp->l_next;	/* q = head list element.  */
546f4ced0bSchristos 
556f4ced0bSchristos   lp->l_next = p;
566f4ced0bSchristos   p->l_prev = NULL;
576f4ced0bSchristos   p->l_next = q;
586f4ced0bSchristos 
596f4ced0bSchristos   if (q != NULL)
606f4ced0bSchristos     q->l_prev = p;
616f4ced0bSchristos   else
626f4ced0bSchristos     lp->l_prev = p;
636f4ced0bSchristos }
646f4ced0bSchristos 
656f4ced0bSchristos /* Delete the specified existing element from the given ctf_list_t.  The
666f4ced0bSchristos    existing pointer should be pointing at a struct with embedded ctf_list_t.  */
676f4ced0bSchristos 
686f4ced0bSchristos void
ctf_list_delete(ctf_list_t * lp,void * existing)696f4ced0bSchristos ctf_list_delete (ctf_list_t *lp, void *existing)
706f4ced0bSchristos {
716f4ced0bSchristos   ctf_list_t *p = existing;
726f4ced0bSchristos 
736f4ced0bSchristos   if (p->l_prev != NULL)
746f4ced0bSchristos     p->l_prev->l_next = p->l_next;
756f4ced0bSchristos   else
766f4ced0bSchristos     lp->l_next = p->l_next;
776f4ced0bSchristos 
786f4ced0bSchristos   if (p->l_next != NULL)
796f4ced0bSchristos     p->l_next->l_prev = p->l_prev;
806f4ced0bSchristos   else
816f4ced0bSchristos     lp->l_prev = p->l_prev;
826f4ced0bSchristos }
836f4ced0bSchristos 
846f4ced0bSchristos /* Return 1 if the list is empty.  */
856f4ced0bSchristos 
866f4ced0bSchristos int
ctf_list_empty_p(ctf_list_t * lp)876f4ced0bSchristos ctf_list_empty_p (ctf_list_t *lp)
886f4ced0bSchristos {
896f4ced0bSchristos   return (lp->l_next == NULL && lp->l_prev == NULL);
906f4ced0bSchristos }
916f4ced0bSchristos 
924f645668Schristos /* Splice one entire list onto the end of another one.  The existing list is
934f645668Schristos    emptied.  */
946f4ced0bSchristos 
954f645668Schristos void
ctf_list_splice(ctf_list_t * lp,ctf_list_t * append)964f645668Schristos ctf_list_splice (ctf_list_t *lp, ctf_list_t *append)
976f4ced0bSchristos {
984f645668Schristos   if (ctf_list_empty_p (append))
994f645668Schristos     return;
1004f645668Schristos 
1014f645668Schristos   if (lp->l_prev != NULL)
1024f645668Schristos     lp->l_prev->l_next = append->l_next;
1034f645668Schristos   else
1044f645668Schristos     lp->l_next = append->l_next;
1054f645668Schristos 
1064f645668Schristos   append->l_next->l_prev = lp->l_prev;
1074f645668Schristos   lp->l_prev = append->l_prev;
1084f645668Schristos   append->l_next = NULL;
1094f645668Schristos   append->l_prev = NULL;
1104f645668Schristos }
1114f645668Schristos 
1124f645668Schristos /* Convert a 32-bit ELF symbol to a ctf_link_sym_t.  */
1134f645668Schristos 
1144f645668Schristos ctf_link_sym_t *
ctf_elf32_to_link_sym(ctf_dict_t * fp,ctf_link_sym_t * dst,const Elf32_Sym * src,uint32_t symidx)1154f645668Schristos ctf_elf32_to_link_sym (ctf_dict_t *fp, ctf_link_sym_t *dst, const Elf32_Sym *src,
1164f645668Schristos 		       uint32_t symidx)
1174f645668Schristos {
1184f645668Schristos   Elf32_Sym tmp;
1194f645668Schristos   int needs_flipping = 0;
1204f645668Schristos 
1214f645668Schristos #ifdef WORDS_BIGENDIAN
1224f645668Schristos   if (fp->ctf_symsect_little_endian)
1234f645668Schristos     needs_flipping = 1;
1244f645668Schristos #else
1254f645668Schristos   if (!fp->ctf_symsect_little_endian)
1264f645668Schristos     needs_flipping = 1;
1274f645668Schristos #endif
1284f645668Schristos 
1294f645668Schristos   memcpy (&tmp, src, sizeof (Elf32_Sym));
1304f645668Schristos   if (needs_flipping)
1314f645668Schristos     {
1324f645668Schristos       swap_thing (tmp.st_name);
1334f645668Schristos       swap_thing (tmp.st_size);
1344f645668Schristos       swap_thing (tmp.st_shndx);
1354f645668Schristos       swap_thing (tmp.st_value);
1364f645668Schristos     }
1374f645668Schristos   /* The name must be in the external string table.  */
1384f645668Schristos   if (tmp.st_name < fp->ctf_str[CTF_STRTAB_1].cts_len)
1394f645668Schristos     dst->st_name = (const char *) fp->ctf_str[CTF_STRTAB_1].cts_strs + tmp.st_name;
1404f645668Schristos   else
1414f645668Schristos     dst->st_name = _CTF_NULLSTR;
1424f645668Schristos   dst->st_nameidx_set = 0;
1434f645668Schristos   dst->st_symidx = symidx;
1444f645668Schristos   dst->st_shndx = tmp.st_shndx;
1454f645668Schristos   dst->st_type = ELF32_ST_TYPE (tmp.st_info);
1464f645668Schristos   dst->st_value = tmp.st_value;
1474f645668Schristos 
1484f645668Schristos   return dst;
1494f645668Schristos }
1504f645668Schristos 
1514f645668Schristos /* Convert a 64-bit ELF symbol to a ctf_link_sym_t.  */
1524f645668Schristos 
1534f645668Schristos ctf_link_sym_t *
ctf_elf64_to_link_sym(ctf_dict_t * fp,ctf_link_sym_t * dst,const Elf64_Sym * src,uint32_t symidx)1544f645668Schristos ctf_elf64_to_link_sym (ctf_dict_t *fp, ctf_link_sym_t *dst, const Elf64_Sym *src,
1554f645668Schristos 		       uint32_t symidx)
1564f645668Schristos {
1574f645668Schristos   Elf64_Sym tmp;
1584f645668Schristos   int needs_flipping = 0;
1594f645668Schristos 
1604f645668Schristos #ifdef WORDS_BIGENDIAN
1614f645668Schristos   if (fp->ctf_symsect_little_endian)
1624f645668Schristos     needs_flipping = 1;
1634f645668Schristos #else
1644f645668Schristos   if (!fp->ctf_symsect_little_endian)
1654f645668Schristos     needs_flipping = 1;
1664f645668Schristos #endif
1674f645668Schristos 
1684f645668Schristos   memcpy (&tmp, src, sizeof (Elf64_Sym));
1694f645668Schristos   if (needs_flipping)
1704f645668Schristos     {
1714f645668Schristos       swap_thing (tmp.st_name);
1724f645668Schristos       swap_thing (tmp.st_size);
1734f645668Schristos       swap_thing (tmp.st_shndx);
1744f645668Schristos       swap_thing (tmp.st_value);
1754f645668Schristos     }
1764f645668Schristos 
1774f645668Schristos   /* The name must be in the external string table.  */
1784f645668Schristos   if (tmp.st_name < fp->ctf_str[CTF_STRTAB_1].cts_len)
1794f645668Schristos     dst->st_name = (const char *) fp->ctf_str[CTF_STRTAB_1].cts_strs + tmp.st_name;
1804f645668Schristos   else
1814f645668Schristos     dst->st_name = _CTF_NULLSTR;
1824f645668Schristos   dst->st_nameidx_set = 0;
1834f645668Schristos   dst->st_symidx = symidx;
1844f645668Schristos   dst->st_shndx = tmp.st_shndx;
1854f645668Schristos   dst->st_type = ELF32_ST_TYPE (tmp.st_info);
1864f645668Schristos 
1874f645668Schristos   /* We only care if the value is zero, so avoid nonzeroes turning into
1884f645668Schristos      zeroes.  */
1894f645668Schristos   if (_libctf_unlikely_ (tmp.st_value != 0 && ((uint32_t) tmp.st_value == 0)))
1904f645668Schristos     dst->st_value = 1;
1914f645668Schristos   else
1924f645668Schristos     dst->st_value = (uint32_t) tmp.st_value;
1936f4ced0bSchristos 
1946f4ced0bSchristos   return dst;
1956f4ced0bSchristos }
1966f4ced0bSchristos 
1976f4ced0bSchristos /* A string appender working on dynamic strings.  Returns NULL on OOM.  */
1986f4ced0bSchristos 
1996f4ced0bSchristos char *
ctf_str_append(char * s,const char * append)2006f4ced0bSchristos ctf_str_append (char *s, const char *append)
2016f4ced0bSchristos {
2026f4ced0bSchristos   size_t s_len = 0;
2036f4ced0bSchristos 
2046f4ced0bSchristos   if (append == NULL)
2056f4ced0bSchristos     return s;
2066f4ced0bSchristos 
2076f4ced0bSchristos   if (s != NULL)
2086f4ced0bSchristos     s_len = strlen (s);
2096f4ced0bSchristos 
2106f4ced0bSchristos   size_t append_len = strlen (append);
2116f4ced0bSchristos 
2126f4ced0bSchristos   if ((s = realloc (s, s_len + append_len + 1)) == NULL)
2136f4ced0bSchristos     return NULL;
2146f4ced0bSchristos 
2156f4ced0bSchristos   memcpy (s + s_len, append, append_len);
2166f4ced0bSchristos   s[s_len + append_len] = '\0';
2176f4ced0bSchristos 
2186f4ced0bSchristos   return s;
2196f4ced0bSchristos }
2206f4ced0bSchristos 
2216f4ced0bSchristos /* A version of ctf_str_append that returns the old string on OOM.  */
2226f4ced0bSchristos 
2236f4ced0bSchristos char *
ctf_str_append_noerr(char * s,const char * append)2246f4ced0bSchristos ctf_str_append_noerr (char *s, const char *append)
2256f4ced0bSchristos {
2266f4ced0bSchristos   char *new_s;
2276f4ced0bSchristos 
2286f4ced0bSchristos   new_s = ctf_str_append (s, append);
2296f4ced0bSchristos   if (!new_s)
2306f4ced0bSchristos     return s;
2316f4ced0bSchristos   return new_s;
2326f4ced0bSchristos }
2336f4ced0bSchristos 
2346f4ced0bSchristos /* A realloc() that fails noisily if called with any ctf_str_num_users.  */
2356f4ced0bSchristos void *
ctf_realloc(ctf_dict_t * fp,void * ptr,size_t size)2364f645668Schristos ctf_realloc (ctf_dict_t *fp, void *ptr, size_t size)
2376f4ced0bSchristos {
2386f4ced0bSchristos   if (fp->ctf_str_num_refs > 0)
2396f4ced0bSchristos     {
2406f4ced0bSchristos       ctf_dprintf ("%p: attempt to realloc() string table with %lu active refs\n",
2416f4ced0bSchristos 		   (void *) fp, (unsigned long) fp->ctf_str_num_refs);
2426f4ced0bSchristos       return NULL;
2436f4ced0bSchristos     }
2446f4ced0bSchristos   return realloc (ptr, size);
2456f4ced0bSchristos }
2466f4ced0bSchristos 
2476f4ced0bSchristos /* Store the specified error code into errp if it is non-NULL, and then
2486f4ced0bSchristos    return NULL for the benefit of the caller.  */
2496f4ced0bSchristos 
2506f4ced0bSchristos void *
ctf_set_open_errno(int * errp,int error)2516f4ced0bSchristos ctf_set_open_errno (int *errp, int error)
2526f4ced0bSchristos {
2536f4ced0bSchristos   if (errp != NULL)
2546f4ced0bSchristos     *errp = error;
2556f4ced0bSchristos   return NULL;
2566f4ced0bSchristos }
2576f4ced0bSchristos 
2584f645668Schristos /* Create a ctf_next_t.  */
2594f645668Schristos 
2604f645668Schristos ctf_next_t *
ctf_next_create(void)2614f645668Schristos ctf_next_create (void)
2624f645668Schristos {
2634f645668Schristos   return calloc (1, sizeof (struct ctf_next));
2644f645668Schristos }
2654f645668Schristos 
2664f645668Schristos /* Destroy a ctf_next_t, for early exit from iterators.  */
2674f645668Schristos 
2684f645668Schristos void
ctf_next_destroy(ctf_next_t * i)2694f645668Schristos ctf_next_destroy (ctf_next_t *i)
2704f645668Schristos {
2714f645668Schristos   if (i == NULL)
2724f645668Schristos     return;
2734f645668Schristos 
2744f645668Schristos   if (i->ctn_iter_fun == (void (*) (void)) ctf_dynhash_next_sorted)
2754f645668Schristos     free (i->u.ctn_sorted_hkv);
2764f645668Schristos   if (i->ctn_next)
2774f645668Schristos     ctf_next_destroy (i->ctn_next);
2784f645668Schristos   free (i);
2794f645668Schristos }
2804f645668Schristos 
2814f645668Schristos /* Copy a ctf_next_t.  */
2824f645668Schristos 
2834f645668Schristos ctf_next_t *
ctf_next_copy(ctf_next_t * i)2844f645668Schristos ctf_next_copy (ctf_next_t *i)
2854f645668Schristos {
2864f645668Schristos   ctf_next_t *i2;
2874f645668Schristos 
2884f645668Schristos   if ((i2 = ctf_next_create()) == NULL)
2894f645668Schristos     return NULL;
2904f645668Schristos   memcpy (i2, i, sizeof (struct ctf_next));
2914f645668Schristos 
2924f645668Schristos   if (i2->ctn_iter_fun == (void (*) (void)) ctf_dynhash_next_sorted)
2934f645668Schristos     {
2944f645668Schristos       size_t els = ctf_dynhash_elements ((ctf_dynhash_t *) i->cu.ctn_h);
2954f645668Schristos       if ((i2->u.ctn_sorted_hkv = calloc (els, sizeof (ctf_next_hkv_t))) == NULL)
2964f645668Schristos 	{
2974f645668Schristos 	  free (i2);
2984f645668Schristos 	  return NULL;
2994f645668Schristos 	}
3004f645668Schristos       memcpy (i2->u.ctn_sorted_hkv, i->u.ctn_sorted_hkv,
3014f645668Schristos 	      els * sizeof (ctf_next_hkv_t));
3024f645668Schristos     }
3034f645668Schristos   return i2;
3044f645668Schristos }
305