+2021-01-05 Nick Alcock <nick.alcock@oracle.com>
+
+ * ctf-impl.h (ctf_dict_t) <ctf_pptrtab>: New.
+ <ctf_pptrtab_len>: New.
+ <ctf_pptrtab_typemax>: New.
+ * ctf-create.c (ctf_serialize): Update accordingly.
+ (ctf_add_reftype): Note that we don't need to update pptrtab here,
+ despite updating ptrtab.
+ * ctf-open.c (ctf_dict_close): Destroy the pptrtab.
+ (ctf_import): Likewise.
+ (ctf_import_unref): Likewise.
+ * ctf-lookup.c (grow_pptrtab): New.
+ (refresh_pptrtab): New, update a pptrtab.
+ (ctf_lookup_by_name): Turn into a wrapper around (and rename to)...
+ (ctf_lookup_by_name_internal): ... this: construct the pptrtab, and
+ use it in addition to the parent's ptrtab when parent dicts are
+ searched.
+ * testsuite/libctf-regression/regression.exp: New testsuite for
+ regression tests.
+ * testsuite/libctf-regression/pptrtab*: New test.
+ * testsuite/libctf-writable/writable.exp: New testsuite for tests of
+ writable CTF dicts.
+ * testsuite/libctf-writable/pptrtab*: New test.
+
2021-01-05 Nick Alcock <nick.alcock@oracle.com>
* ctf-archive.c (ctf_archive_iter): Remove outdated comment.
nfp->ctf_funchash = fp->ctf_funchash;
nfp->ctf_dynsyms = fp->ctf_dynsyms;
nfp->ctf_ptrtab = fp->ctf_ptrtab;
+ nfp->ctf_pptrtab = fp->ctf_pptrtab;
nfp->ctf_dynsymidx = fp->ctf_dynsymidx;
nfp->ctf_dynsymmax = fp->ctf_dynsymmax;
nfp->ctf_ptrtab_len = fp->ctf_ptrtab_len;
+ nfp->ctf_pptrtab_len = fp->ctf_pptrtab_len;
nfp->ctf_link_inputs = fp->ctf_link_inputs;
nfp->ctf_link_outputs = fp->ctf_link_outputs;
nfp->ctf_errs_warnings = fp->ctf_errs_warnings;
nfp->ctf_objtidx_sxlate = fp->ctf_objtidx_sxlate;
nfp->ctf_str_prov_offset = fp->ctf_str_prov_offset;
nfp->ctf_syn_ext_strtab = fp->ctf_syn_ext_strtab;
+ nfp->ctf_pptrtab_typemax = fp->ctf_pptrtab_typemax;
nfp->ctf_in_flight_dynsyms = fp->ctf_in_flight_dynsyms;
nfp->ctf_link_in_cu_mapping = fp->ctf_link_in_cu_mapping;
nfp->ctf_link_out_cu_mapping = fp->ctf_link_out_cu_mapping;
memset (&fp->ctf_errs_warnings, 0, sizeof (ctf_list_t));
fp->ctf_add_processing = NULL;
fp->ctf_ptrtab = NULL;
+ fp->ctf_pptrtab = NULL;
fp->ctf_funcidx_names = NULL;
fp->ctf_objtidx_names = NULL;
fp->ctf_funcidx_sxlate = NULL;
type and (if an anonymous typedef node is being pointed at) the type that
points at too. Note that ctf_typemax is at this point one higher than we
want to check against, because it's just been incremented for the addition
- of this type. */
+ of this type. The pptrtab is lazily-updated as needed, so is not touched
+ here. */
uint32_t type_idx = LCTF_TYPE_TO_INDEX (fp, type);
uint32_t ref_idx = LCTF_TYPE_TO_INDEX (fp, ref);
uint32_t *ctf_txlate; /* Translation table for type IDs. */
uint32_t *ctf_ptrtab; /* Translation table for pointer-to lookups. */
size_t ctf_ptrtab_len; /* Num types storable in ptrtab currently. */
+ uint32_t *ctf_pptrtab; /* Parent types pointed to by child dicts. */
+ size_t ctf_pptrtab_len; /* Num types storable in pptrtab currently. */
+ uint32_t ctf_pptrtab_typemax; /* Max child type when pptrtab last updated. */
uint32_t *ctf_funcidx_names; /* Name of each function symbol in symtypetab
(if indexed). */
uint32_t *ctf_objtidx_names; /* Likewise, for object symbols. */
#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;
+ int updated;
+
+ 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;
+ updated = 1;
+ }
+ }
+ if (!updated)
+ continue;
+
+ /* If we updated the ptrtab entry for this type's referent, and it's an
+ anonymous typedef node, we also want to chase down its referent and
+ change that as well. */
+
+ if ((ctf_type_kind (fp, reffed_type) == CTF_K_TYPEDEF)
+ && strcmp (ctf_type_name_raw (fp, reffed_type), "") == 0)
+ {
+ uint32_t idx;
+ idx = LCTF_TYPE_TO_INDEX (pfp, ctf_type_reference (fp, reffed_type));
+
+ 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
finds the things that we actually care about: structs, unions, enums,
integers, floats, typedefs, and pointers to any of these named types. */
-ctf_id_t
-ctf_lookup_by_name (ctf_dict_t *fp, const char *name)
+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*";
if (*p == '*')
{
- /* Find a pointer to type by looking in 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.
+ /* 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. */
+
+ uint32_t idx = LCTF_TYPE_TO_INDEX (fp, type);
+ int in_child = 0;
+
+ ntype = type;
+ if (child && idx <= child->ctf_pptrtab_len)
+ {
+ ntype = child->ctf_pptrtab[idx];
+ if (ntype)
+ in_child = 1;
+ }
- TODO need to handle parent dicts too. */
+ if (ntype == 0)
+ ntype = fp->ctf_ptrtab[idx];
- ntype = fp->ctf_ptrtab[LCTF_TYPE_TO_INDEX (fp, type)];
+ /* Try resolving to its base type and check again. */
if (ntype == 0)
{
- ntype = ctf_type_resolve_unsliced (fp, type);
- if (ntype == CTF_ERR
- || (ntype =
- fp->ctf_ptrtab[LCTF_TYPE_TO_INDEX (fp, ntype)]) == 0)
+ 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 = 0;
+ if (child && idx <= child->ctf_pptrtab_len)
{
- (void) ctf_set_errno (fp, ECTF_NOTYPE);
- goto err;
+ ntype = child->ctf_pptrtab[idx];
+ if (ntype)
+ in_child = 1;
}
+
+ if (ntype == 0)
+ ntype = fp->ctf_ptrtab[idx];
+ if (ntype == CTF_ERR)
+ goto notype;
}
- type = LCTF_INDEX_TO_TYPE (fp, ntype, (fp->ctf_flags & LCTF_CHILD));
+ 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;
fp->ctf_tmp_typeslice = xstrndup (p, (size_t) (q - p));
if (fp->ctf_tmp_typeslice == NULL)
{
- (void) ctf_set_errno (fp, ENOMEM);
+ ctf_set_errno (fp, ENOMEM);
return CTF_ERR;
}
}
if ((type = ctf_lookup_by_rawhash (fp, lp->ctl_hash,
fp->ctf_tmp_typeslice)) == 0)
- {
- (void) ctf_set_errno (fp, ECTF_NOTYPE);
- goto err;
- }
+ goto notype;
break;
}
}
if (lp->ctl_prefix == NULL)
- {
- (void) ctf_set_errno (fp, ECTF_NOTYPE);
- goto err;
- }
+ goto notype;
}
if (*p != '\0' || type == 0)
return type;
-err:
- if (fp->ctf_parent != NULL
- && (ptype = ctf_lookup_by_name (fp->ctf_parent, name)) != CTF_ERR)
- return ptype;
+ 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 -1; /* errno is set for us. */
+ }
+
+ if ((ptype = ctf_lookup_by_name_internal (fp->ctf_parent, fp,
+ name)) != CTF_ERR)
+ return ptype;
+ return (ctf_set_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. */
free (fp->ctf_sxlate);
free (fp->ctf_txlate);
free (fp->ctf_ptrtab);
+ free (fp->ctf_pptrtab);
free (fp->ctf_header);
free (fp);
/* Import the types from the specified parent dict by storing a pointer to it in
ctf_parent and incrementing its reference count. Only one parent is allowed:
- if a parent already exists, it is replaced by the new parent. */
+ if a parent already exists, it is replaced by the new parent. The pptrtab
+ is wiped, and will be refreshed by the next ctf_lookup_by_name call. */
int
ctf_import (ctf_dict_t *fp, ctf_dict_t *pfp)
{
ctf_dict_close (fp->ctf_parent);
fp->ctf_parent = NULL;
+ free (fp->ctf_pptrtab);
+ fp->ctf_pptrtab = NULL;
+ fp->ctf_pptrtab_len = 0;
+ fp->ctf_pptrtab_typemax = 0;
+
if (pfp != NULL)
{
int err;
ctf_dict_close (fp->ctf_parent);
fp->ctf_parent = NULL;
+ free (fp->ctf_pptrtab);
+ fp->ctf_pptrtab = NULL;
+ fp->ctf_pptrtab_len = 0;
+ fp->ctf_pptrtab_typemax = 0;
if (pfp != NULL)
{
int err;
--- /dev/null
+typedef long a_t;
+
+a_t *a;
--- /dev/null
+typedef long a_t;
+
+a_t b;
+
--- /dev/null
+#include <ctf-api.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+int
+main (int argc, char *argv[])
+{
+ ctf_dict_t *fp;
+ ctf_archive_t *ctf;
+ ctf_next_t *i = NULL;
+ ctf_id_t type;
+ const char *arcname;
+ char *type_name;
+ int err;
+
+ if (argc != 2)
+ {
+ fprintf (stderr, "Syntax: %s PROGRAM\n", argv[0]);
+ exit(1);
+ }
+
+ if ((ctf = ctf_open (argv[1], NULL, &err)) == NULL)
+ goto open_err;
+
+ /* Make sure we can look up a_t * by name in all non-parent dicts, even though
+ the a_t * and the type it points to are in distinct dicts. */
+
+ while ((fp = ctf_archive_next (ctf, &i, &arcname, 1, &err)) != NULL)
+ {
+ if ((type = ctf_lookup_by_name (fp, "a_t *")) == CTF_ERR)
+ goto err;
+
+ if (ctf_type_reference (fp, type) == CTF_ERR)
+ goto err;
+
+ printf ("%s: a_t * points to a type of kind %i\n", arcname,
+ ctf_type_kind (fp, ctf_type_reference (fp, type)));
+
+ ctf_dict_close (fp);
+ }
+ if (err != ECTF_NEXT_END)
+ goto open_err;
+
+ ctf_close (ctf);
+
+ return 0;
+
+ open_err:
+ fprintf (stderr, "%s: cannot open: %s\n", argv[0], ctf_errmsg (err));
+ return 1;
+ err:
+ fprintf (stderr, "Lookup failed in %s: %s\n", arcname, ctf_errmsg (ctf_errno (fp)));
+ return 1;
+}
--- /dev/null
+# source: pptrtab-a.c
+# source: pptrtab-b.c
+# link_flags: -Wl,--ctf-share-types=share-duplicated
+.*/pptrtab-[ab]\.c: .* points to a type of kind 10
--- /dev/null
+# Copyright (C) 2021 Free Software Foundation, Inc.
+#
+# This file is part of the GNU Binutils.
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston,
+# MA 02110-1301, USA.
+#
+
+if ![is_elf_format] {
+ unsupported "CTF needs bfd changes to be emitted on non-ELF"
+ return 0
+}
+
+if {[info exists env(LC_ALL)]} {
+ set old_lc_all $env(LC_ALL)
+}
+set env(LC_ALL) "C"
+
+set ctf_test_list [lsort [glob -nocomplain $srcdir/$subdir/*.lk]]
+
+foreach ctf_test $ctf_test_list {
+ verbose [file rootname $ctf_test]
+ verbose running lookup test on $ctf_test
+ run_lookup_test [file rootname $ctf_test]
+}
+
+if {[info exists old_lc_all]} {
+ set env(LC_ALL) $old_lc_all
+} else {
+ unset env(LC_ALL)
+}
--- /dev/null
+#include <ctf-api.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+int
+main (int argc, char *argv[])
+{
+ ctf_dict_t *pfp;
+ ctf_dict_t *cfp;
+ ctf_id_t base, base2, ptr, ptr2, type, last_type;
+ ctf_encoding_t encoding = { CTF_INT_SIGNED, 0, sizeof (int) };
+ ctf_encoding_t encoding2 = { CTF_INT_SIGNED, 0, sizeof (long) };
+ char *type_name;
+ int err;
+
+ if ((pfp = ctf_create (&err)) == NULL)
+ goto create_err;
+
+ if ((cfp = ctf_create (&err)) == NULL)
+ goto create_err;
+
+ if (ctf_import (cfp, pfp) < 0)
+ goto create_child;
+
+ /* First, try an int in the parent with a pointer in the child. Also make
+ another pair of types we will chain to later: these end up before the
+ pptrtab lazy-update watermark. */
+
+ if ((base = ctf_add_integer (pfp, CTF_ADD_ROOT, "int", &encoding)) == CTF_ERR)
+ goto create_parent;
+
+ if ((base2 = ctf_add_integer (pfp, CTF_ADD_ROOT, "long int", &encoding2)) == CTF_ERR)
+ goto create_parent;
+
+ if ((ptr = ctf_add_pointer (cfp, CTF_ADD_ROOT, base)) == CTF_ERR)
+ goto create_child;
+
+ if ((type = ctf_lookup_by_name (cfp, "int *") ) == CTF_ERR)
+ goto err;
+
+ type_name = ctf_type_aname (cfp, type);
+ printf ("First lookup: %s in the child points to a type of kind %i\n",
+ type_name, ctf_type_kind (cfp, ctf_type_reference (cfp, type)));
+ free (type_name);
+
+ if (ctf_type_reference (cfp, type) != base)
+ printf ("First lookup ref diff: %lx versus %lx\n", base,
+ ctf_type_reference (cfp, type));
+ last_type = type;
+
+ /* Add another pointer to the same type in the parent and try a lookup. */
+
+ if ((ptr = ctf_add_pointer (pfp, CTF_ADD_ROOT, base2)) == CTF_ERR)
+ goto create_parent;
+
+ if ((type = ctf_lookup_by_name (cfp, "long int *") ) == CTF_ERR)
+ goto err;
+
+ type_name = ctf_type_aname (cfp, type);
+ printf ("Second lookup: %s in the child points to a type of kind %i\n",
+ type_name, ctf_type_kind (cfp, ctf_type_reference (cfp, type)));
+ free (type_name);
+
+ if (ctf_type_reference (cfp, type) != base2)
+ printf ("Second lookup ref diff: %lx versus %lx\n", base2,
+ ctf_type_reference (cfp, type));
+ if (last_type == type)
+ printf ("Second lookup should not return the same type as the first: %lx\n", type);
+
+ last_type = type;
+
+ /* Add another pointer to the same type in the child and try a lookup. */
+
+ if ((ptr = ctf_add_pointer (cfp, CTF_ADD_ROOT, base2)) == CTF_ERR)
+ goto create_child;
+
+ if ((type = ctf_lookup_by_name (cfp, "long int *") ) == CTF_ERR)
+ goto err;
+
+ type_name = ctf_type_aname (cfp, type);
+ printf ("Third lookup: %s in the child points to a type of kind %i\n",
+ type_name, ctf_type_kind (cfp, ctf_type_reference (cfp, type)));
+ free (type_name);
+
+ if (ctf_type_reference (cfp, type) != base2)
+ printf ("Third lookup ref diff: %lx versus %lx\n", base2,
+ ctf_type_reference (cfp, type));
+
+ if (last_type == type)
+ printf ("Third lookup should not return the same type as the second: %lx\n", type);
+
+ ctf_file_close (cfp);
+ ctf_file_close (pfp);
+
+ return 0;
+
+ create_err:
+ fprintf (stderr, "Creation failed: %s\n", ctf_errmsg (err));
+ return 1;
+ create_parent:
+ fprintf (stderr, "Cannot create type: %s\n", ctf_errmsg (ctf_errno (pfp)));
+ return 1;
+ create_child:
+ fprintf (stderr, "Cannot create type: %s\n", ctf_errmsg (ctf_errno (cfp)));
+ return 1;
+ err:
+ fprintf (stderr, "Lookup failed: %s\n", ctf_errmsg (ctf_errno (cfp)));
+ return 1;
+}
--- /dev/null
+First lookup: int \* in the child points to a type of kind 1
+Second lookup: long int \* in the child points to a type of kind 1
+Third lookup: long int \* in the child points to a type of kind 1
--- /dev/null
+# Copyright (C) 2021 Free Software Foundation, Inc.
+#
+# This file is part of the GNU Binutils.
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston,
+# MA 02110-1301, USA.
+#
+
+if {[info exists env(LC_ALL)]} {
+ set old_lc_all $env(LC_ALL)
+}
+set env(LC_ALL) "C"
+
+set ctf_test_list [lsort [glob -nocomplain $srcdir/$subdir/*.lk]]
+
+foreach ctf_test $ctf_test_list {
+ verbose [file rootname $ctf_test]
+ verbose running lookup test on $ctf_test
+ run_lookup_test [file rootname $ctf_test]
+}
+
+if {[info exists old_lc_all]} {
+ set env(LC_ALL) $old_lc_all
+} else {
+ unset env(LC_ALL)
+}