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