libctf: add linking of the variable section
authorNick Alcock <nick.alcock@oracle.com>
Sat, 13 Jul 2019 20:41:25 +0000 (21:41 +0100)
committerNick Alcock <nick.alcock@oracle.com>
Thu, 3 Oct 2019 16:04:55 +0000 (17:04 +0100)
The compiler describes the name and type of all file-scope variables in
this section.  Merging it at link time requires using the type mapping
added in the previous commit to determine the appropriate type for the
variable in the output, given its type in the input: we check the shared
container first, and if the type doesn't exist there, it must be a
conflicted type in the per-CU child, and the variable should go there
too.  We also put the variable in the per-CU child if a variable with
the same name but a different type already exists in the parent: we
ignore any such conflict in the child because CTF cannot represent such
things, nor can they happen unless a third-party linking program has
overridden the mapping of CU to CTF archive member name (using machinery
added in a later commit).

v3: rewritten using an algorithm that actually works in the case of
    conflicting names.  Some code motion from the next commit.  Set
    the per-CU parent name.
v4: check for strdup failure.
v5: fix tabdamage.

include/
* ctf-api.h (ECTF_INTERNAL): New.

libctf/
* ctf-link.c (ctf_create_per_cu): New, refactored out of...
(ctf_link_one_type): ... here, with parent-name setting added.
(check_variable): New.
(ctf_link_one_variable): Likewise.
(ctf_link_one_input_archive_member): Call it.
* ctf-error.c (_ctf_errlist): Updated with new errors.

include/ChangeLog
include/ctf-api.h
libctf/ChangeLog
libctf/ctf-error.c
libctf/ctf-link.c

index 6980ec2510e65c92eb38e0a82ff3b0c49bcdd973..0122c1d628009d0c65077e109b921fdfab297c1b 100644 (file)
@@ -1,3 +1,7 @@
+2019-07-13  Nick Alcock  <nick.alcock@oracle.com>
+
+       * ctf-api.h (ECTF_INTERNAL): New.
+
 2019-07-13  Nick Alcock  <nick.alcock@oracle.com>
 
        * ctf-api.h (struct ctf_link_sym): New, a symbol in flight to the
index e4c6f9fc5b344967b9259488e8c5c77f86ab2723..4130a2ecd1934a140be0be4966dc254e4ef281f1 100644 (file)
@@ -203,7 +203,8 @@ enum
    ECTF_SLICEOVERFLOW,         /* Overflow of type bitness or offset in slice.  */
    ECTF_DUMPSECTUNKNOWN,       /* Unknown section number in dump.  */
    ECTF_DUMPSECTCHANGED,       /* Section changed in middle of dump.  */
-   ECTF_NOTYET                 /* Feature not yet implemented.  */
+   ECTF_NOTYET,                        /* Feature not yet implemented.  */
+   ECTF_INTERNAL               /* Internal error in link.  */
   };
 
 /* The CTF data model is inferred to be the caller's data model or the data
index 7dc32b89ce3560e54fc73b1ec8d97f2f4a1d6afa..a5995eb25d808d75e2f7722a316d33b2f903a42b 100644 (file)
@@ -1,3 +1,12 @@
+2019-07-13  Nick Alcock  <nick.alcock@oracle.com>
+
+       * ctf-link.c (ctf_create_per_cu): New, refactored out of...
+       (ctf_link_one_type): ... here, with parent-name setting added.
+       (check_variable): New.
+       (ctf_link_one_variable): Likewise.
+       (ctf_link_one_input_archive_member): Call it.
+       * ctf-error.c (_ctf_errlist): Updated with new errors.
+
 2019-07-13  Nick Alcock  <nick.alcock@oracle.com>
 
        * ctf-impl.h (ctf_file_t): New field ctf_link_type_mapping.
index 7f6a4ce5d477bb9a902d691af36bdab016ee7a4c..93ffc6acc09c470c8a172a0fb2893ad02ed97c15 100644 (file)
@@ -69,7 +69,8 @@ static const char *const _ctf_errlist[] = {
   "Overflow of type bitness or offset in slice",     /* ECTF_SLICEOVERFLOW */
   "Unknown section number in dump",                 /* ECTF_DUMPSECTUNKNOWN */
   "Section changed in middle of dump",              /* ECTF_DUMPSECTCHANGED */
-  "Feature not yet implemented"                             /* ECTF_NOTYET */
+  "Feature not yet implemented",                    /* ECTF_NOTYET */
+  "Internal error in link"                          /* ECTF_INTERNAL */
 };
 
 static const int _ctf_nerr = sizeof (_ctf_errlist) / sizeof (_ctf_errlist[0]);
index e10edf2eb413ccaa3f2621a5cc1dcb81d88f60ee..8dd81d1f124192c335866fd052542fff580bae7f 100644 (file)
@@ -175,6 +175,46 @@ ctf_link_add_ctf (ctf_file_t *fp, ctf_archive_t *ctf, const char *name)
   return (ctf_set_errno (fp, ENOMEM));
 }
 
+/* Return a per-CU output CTF dictionary suitable for the given CU, creating and
+   interning it if need be.  */
+
+static ctf_file_t *
+ctf_create_per_cu (ctf_file_t *fp, const char *filename, const char *cuname)
+{
+  ctf_file_t *cu_fp;
+  char *dynname = NULL;
+
+  if ((cu_fp = ctf_dynhash_lookup (fp->ctf_link_outputs, filename)) == NULL)
+    {
+      int err;
+
+      if ((cu_fp = ctf_create (&err)) == NULL)
+       {
+         ctf_dprintf ("Cannot create per-CU CTF archive for CU %s from "
+                      "input file %s: %s\n", cuname, filename,
+                      ctf_errmsg (err));
+         ctf_set_errno (fp, err);
+         return NULL;
+       }
+
+      if ((dynname = strdup (filename)) == NULL)
+       goto oom;
+      if (ctf_dynhash_insert (fp->ctf_link_outputs, dynname, cu_fp) < 0)
+       goto oom;
+
+      ctf_import (cu_fp, fp);
+      ctf_cuname_set (cu_fp, cuname);
+      ctf_parent_name_set (cu_fp, _CTF_SECTION);
+    }
+  return cu_fp;
+
+ oom:
+  free (dynname);
+  ctf_file_close (cu_fp);
+  ctf_set_errno (fp, ENOMEM);
+  return NULL;
+}
+
 typedef struct ctf_link_in_member_cb_arg
 {
   ctf_file_t *out_fp;
@@ -226,29 +266,9 @@ ctf_link_one_type (ctf_id_t type, int isroot _libctf_unused_, void *arg_)
       ctf_set_errno (arg->out_fp, 0);
     }
 
-  if ((per_cu_out_fp = ctf_dynhash_lookup (arg->out_fp->ctf_link_outputs,
-                                          arg->arcname)) == NULL)
-    {
-      int err;
-
-      if ((per_cu_out_fp = ctf_create (&err)) == NULL)
-       {
-         ctf_dprintf ("Cannot create per-CU CTF archive for member %s: %s\n",
-                      arg->arcname, ctf_errmsg (err));
-         ctf_set_errno (arg->out_fp, err);
-         return -1;
-       }
-
-      if (ctf_dynhash_insert (arg->out_fp->ctf_link_outputs, arg->arcname,
-                             per_cu_out_fp) < 0)
-       {
-         ctf_set_errno (arg->out_fp, ENOMEM);
-         return -1;
-       }
-
-      ctf_import (per_cu_out_fp, arg->out_fp);
-      ctf_cuname_set (per_cu_out_fp, arg->cu_name);
-    }
+  if ((per_cu_out_fp = ctf_create_per_cu (arg->out_fp, arg->arcname,
+                                         arg->cu_name)) == NULL)
+    return -1;                                 /* Errno is set for us.  */
 
   if (ctf_add_type (per_cu_out_fp, arg->in_fp, type) != CTF_ERR)
     return 0;
@@ -263,6 +283,95 @@ ctf_link_one_type (ctf_id_t type, int isroot _libctf_unused_, void *arg_)
   return 0;                                    /* As above: do not lose types.  */
 }
 
+/* Check if we can safely add a variable with the given type to this container.  */
+
+static int
+check_variable (const char *name, ctf_file_t *fp, ctf_id_t type,
+               ctf_dvdef_t **out_dvd)
+{
+  ctf_dvdef_t *dvd;
+
+  dvd = ctf_dynhash_lookup (fp->ctf_dvhash, name);
+  *out_dvd = dvd;
+  if (!dvd)
+    return 1;
+
+  if (dvd->dvd_type != type)
+    {
+      /* Variable here.  Wrong type: cannot add.  Just skip it, because there is
+        no way to express this in CTF.  (This might be the parent, in which
+        case we'll try adding in the child first, and only then give up.)  */
+      ctf_dprintf ("Inexpressible duplicate variable %s skipped.\n", name);
+    }
+
+  return 0;                                  /* Already exists.  */
+}
+
+/* Link one variable in.  */
+
+static int
+ctf_link_one_variable (const char *name, ctf_id_t type, void *arg_)
+{
+  ctf_link_in_member_cb_arg_t *arg = (ctf_link_in_member_cb_arg_t *) arg_;
+  ctf_file_t *per_cu_out_fp;
+  ctf_id_t dst_type = 0;
+  ctf_file_t *check_fp;
+  ctf_dvdef_t *dvd;
+
+  /* In unconflicted link mode, if this type is mapped to a type in the parent
+     container, we want to try to add to that first: if it reports a duplicate,
+     or if the type is in a child already, add straight to the child.  */
+
+  check_fp = arg->out_fp;
+
+  dst_type = ctf_type_mapping (arg->in_fp, type, &check_fp);
+  if (dst_type != 0)
+    {
+      if (check_fp == arg->out_fp)
+       {
+         if (check_variable (name, check_fp, dst_type, &dvd))
+           {
+             /* No variable here: we can add it.  */
+             if (ctf_add_variable (check_fp, name, dst_type) < 0)
+               return (ctf_set_errno (arg->out_fp, ctf_errno (check_fp)));
+             return 0;
+           }
+
+         /* Already present?  Nothing to do.  */
+         if (dvd && dvd->dvd_type == type)
+           return 0;
+       }
+    }
+
+  /* Can't add to the parent due to a name clash, or because it references a
+     type only present in the child.  Try adding to the child, creating if need
+     be.  */
+
+  if ((per_cu_out_fp = ctf_create_per_cu (arg->out_fp, arg->arcname,
+                                         arg->cu_name)) == NULL)
+    return -1;                                 /* Errno is set for us.  */
+
+  /* If the type was not found, check for it in the child too. */
+  if (dst_type == 0)
+    {
+      check_fp = per_cu_out_fp;
+      dst_type = ctf_type_mapping (arg->in_fp, type, &check_fp);
+
+      if (dst_type == 0)
+       {
+         ctf_dprintf ("Type %lx for variable %s in input file %s not "
+                      "found: skipped.\n", type, name, arg->file_name);
+         /* Do not terminate the link: just skip the variable.  */
+         return 0;
+       }
+    }
+
+  if (check_variable (name, per_cu_out_fp, dst_type, &dvd))
+    if (ctf_add_variable (per_cu_out_fp, name, dst_type) < 0)
+      return (ctf_set_errno (arg->out_fp, ctf_errno (per_cu_out_fp)));
+  return 0;
+}
+
 /* Merge every type and variable in this archive member into the link, so we can
    relink things that have already had ld run on them.  We use the archive
    member name, sans any leading '.ctf.', as the CU name for ambiguous types if
@@ -316,7 +425,8 @@ ctf_link_one_input_archive_member (ctf_file_t *in_fp, const char *name, void *ar
     arg->cu_name += strlen (".ctf.");
   arg->in_fp = in_fp;
 
-  err = ctf_type_iter_all (in_fp, ctf_link_one_type, arg);
+  if ((err = ctf_type_iter_all (in_fp, ctf_link_one_type, arg)) > -1)
+    err = ctf_variable_iter (in_fp, ctf_link_one_variable, arg);
 
   arg->in_input_cu_file = 0;
   free (arg->arcname);