libctf, link: fix CU-mapped links with CTF_LINK_EMPTY_CU_MAPPINGS
authorNick Alcock <nick.alcock@oracle.com>
Fri, 7 Apr 2023 19:09:24 +0000 (20:09 +0100)
committerNick Alcock <nick.alcock@oracle.com>
Sat, 8 Apr 2023 15:07:17 +0000 (16:07 +0100)
This is a bug in the intersection of two obscure options that cannot
even be invoked from ld with a feature added to stop ld of the
same input file repeatedly from crashing the linker.

The latter fix involved tracking input files (internally to libctf) not
just with their input CU name but with a version of their input CU name
that was augmented with a numeric prefix if their linker input file name
was changed, to prevent distinct CTF dicts with the same cuname from
overwriting each other. (We can't use just the linker input file name
because one linker input can contain many CU dicts, particularly under
ld -r).  If these inputs then produced conflicting types, those types
were emitted into similarly-named output dicts, so we needed similar
machinery to detect clashing output dicts and add a numeric prefix to
them as well.

This works fine, except that if you used the cu-mapping feature to force
double-linking of CTF (so that your CTF can be grouped into output dicts
larger than a single translation unit) and then also used
CTF_LINK_EMPTY_CU_MAPPINGS to force every possible output dict in the
mapping to be created (even if empty), we did the creation of empty dicts
first, and then all the actual content got considered to be a clash. So
you ended up with a pile of useless empty dicts and then all the content
was in full dicts with the same names suffixed with a #0.  This seems
likely to confuse consumers that use this facility.

Fixed by generating all the EMPTY_CU_MAPPINGS empty dicts after linking
is complete, not before it runs.

No impact on ld, which does not do cu-mapped links or pass
CTF_LINK_EMPTY_CU_MAPPINGS to ctf_link().

libctf/
* ctf-link.c (ctf_create_per_cu): Don't create new dicts iff one
        already exists and we are making one for no input in particular.
        (ctf_link): Emit empty CTF dicts corresponding to no input in
        particular only after linkiing is complete.

libctf/ctf-link.c

index 016bce5f6d6f62bfde2243280ef11731c7982959..9babec2aa37bf99ff36239802fd0c92422fdc2f1 100644 (file)
@@ -321,12 +321,12 @@ ctf_create_per_cu (ctf_dict_t *fp, ctf_dict_t *input, const char *cu_name)
   if (ctf_name == NULL)
     ctf_name = cu_name;
 
-  /* Look up the per-CU dict.  If we don't know of one, or it is for
-     a different input CU which just happens to have the same name,
-     create a new one.  */
+  /* Look up the per-CU dict.  If we don't know of one, or it is for a different input
+     CU which just happens to have the same name, create a new one.  If we are creating
+     a dict with no input specified, anything will do.  */
 
   if ((cu_fp = ctf_dynhash_lookup (fp->ctf_link_outputs, ctf_name)) == NULL
-      || cu_fp->ctf_link_in_out != fp)
+      || (input && cu_fp->ctf_link_in_out != fp))
     {
       int err;
 
@@ -1505,11 +1505,17 @@ ctf_link (ctf_dict_t *fp, int flags)
   if (fp->ctf_link_outputs == NULL)
     return ctf_set_errno (fp, ENOMEM);
 
+  fp->ctf_flags |= LCTF_LINKING;
+  ctf_link_deduplicating (fp);
+  fp->ctf_flags &= ~LCTF_LINKING;
+
+  if ((ctf_errno (fp) != 0) && (ctf_errno (fp) != ECTF_NOCTFDATA))
+    return -1;
+
   /* Create empty CUs if requested.  We do not currently claim that multiple
      links in succession with CTF_LINK_EMPTY_CU_MAPPINGS set in some calls and
      not set in others will do anything especially sensible.  */
 
-  fp->ctf_flags |= LCTF_LINKING;
   if (fp->ctf_link_out_cu_mapping && (flags & CTF_LINK_EMPTY_CU_MAPPINGS))
     {
       ctf_next_t *i = NULL;
@@ -1535,11 +1541,6 @@ ctf_link (ctf_dict_t *fp, int flags)
        }
     }
 
-  ctf_link_deduplicating (fp);
-
-  fp->ctf_flags &= ~LCTF_LINKING;
-  if ((ctf_errno (fp) != 0) && (ctf_errno (fp) != ECTF_NOCTFDATA))
-    return -1;
   return 0;
 }