]> git.ipfire.org Git - thirdparty/binutils-gdb.git/commitdiff
libctf: fix querying of large structures
authorNick Alcock <nick.alcock@oracle.com>
Mon, 22 Sep 2025 20:07:11 +0000 (21:07 +0100)
committerNick Alcock <nick.alcock@oracle.com>
Thu, 25 Sep 2025 04:42:27 +0000 (05:42 +0100)
After GCC PR 121411 is fixed, large structures (with offsets > 512MiB)
are written correctly... but libctf cannot query them properly unless
they are even bigger (> 4GiB), because it checks to see if the ctt_size
is CTF_LSIZE_SENT to decide whether to use a ctf_lmember_t or a
ctf_member_t to encode the structure members.  But the structure member
offsets are in *bits*, not bytes: the right value to check is
CTF_LSTRUCT_THRESH, which is 1/8th the size.

(Thanks to Martin Pirker <martin.pirker1@chello.at> for the diagnosis
and fix.)

Testing this is a bit fun, because we don't want to emit an error if the
compiler is broken: but we cannot tell whether the compiler is broken
using the existing lookup harness, because its input is passed through
the linker (and thus the broken ld).  So add another sort of link mode,
"objects", which keeps the constituent object files around and passes
both the final linker output and the object files that make it up to the
lookup program.  Our testcase can then check the linker input to see if
the compiler is buggy, and only if it isn't check the linker output and
fail if things aren't right.

libctf/
PR libctf/33339
* ctf-types.c (ctf_struct_member): Check CTF_LSTRUCT_THRESH, not
CTF_LSIZE_SENT.
* testsuite/lib/ctf-lib.exp (run_lookup_test): New 'objects'
link option.
* testsuite/libctf-lookup/big-struct-corruption.*: New test.
* testsuite/libctf-lookup/big-struct-ctf.c: New test input.

libctf/ctf-types.c
libctf/testsuite/lib/ctf-lib.exp
libctf/testsuite/libctf-lookup/big-struct-corruption.c [new file with mode: 0644]
libctf/testsuite/libctf-lookup/big-struct-corruption.lk [new file with mode: 0644]
libctf/testsuite/libctf-lookup/big-struct-ctf.c [new file with mode: 0644]

index 044a1852c1a5f7d3739aeb3ba1433e10487e9083..d08d4542dc197936043e0086c983a8addbc577e6 100644 (file)
@@ -45,7 +45,7 @@ ctf_struct_member (ctf_dict_t *fp, ctf_lmember_t *dst, const ctf_type_t *tp,
     return -1;                                 /* errno is set for us.  */
 
   /* Already large.  */
-  if (tp->ctt_size == CTF_LSIZE_SENT)
+  if (tp->ctt_size >= CTF_LSTRUCT_THRESH)
     {
       ctf_lmember_t *lmp = (ctf_lmember_t *) vlen;
 
index 4df6619606b99ee75b6d7ceb10b42f80b610b3bd..7175e14c30a286b154b7970fa7982d7fa91feeb9 100644 (file)
@@ -141,6 +141,8 @@ proc compile_link_one_host_cc { src output additional_args } {
 #
 #   link:
 #      If set, link the SOURCE together even if only one file is specified.
+#       If "objects", keep the input object files and pass them as additional
+#       arguments to LOOKUP.
 #
 #   link_flags:
 #      If set, extra flags to pass to the linker.
@@ -265,11 +267,14 @@ proc run_lookup_test { name } {
     # Compile the inputs and posibly link them together.
 
     set lookup_output ""
+    set objs {}
     if { [llength $opts(source)] > 0 } {
        set lookup_flags ""
+       set ld_flags ""
        if { $run_ld } {
            set lookup_output "tmpdir/out.so"
-           set lookup_flags "-gctf -fPIC $shared $opts(link_flags)"
+           set lookup_flags "-gctf -fPIC"
+           set ld_flags "$shared $opts(link_flags)"
        } else {
            set lookup_output "tmpdir/out.o"
            set lookup_flags "-gctf -fPIC -c"
@@ -280,19 +285,38 @@ proc run_lookup_test { name } {
        if [board_info [target_info name] exists ldflags] {
            append lookup_flags " [board_info [target_info name] ldflags]"
        }
-       set src {}
+       set objsrcs {}
+       set local_srcs {}
        foreach sfile $opts(source) {
+           set local_src [file join [file dirname $file] $sfile]
+           lappend local_srcs $local_src
+
            if [is_remote host] {
-               lappend src [remote_download host [file join [file dirname $file] $sfile]]
+               set src [remote_download host [file join [file dirname $file] $sfile]]
+           } else {
+               set src [file join [file dirname $file] $sfile]
+           }
+
+           if { $opts(link) == "objects" } {
+               set obj "[file rootname $src].o"
+               set comp_output [prune_warnings [run_host_cmd "$CC_FOR_TARGET" "$CFLAGS_FOR_TARGET $lookup_flags $src -c -o $obj"]]
+
+               if { $comp_output != ""} {
+                   send_log "compilation of CTF program $local_src failed with <$comp_output>"
+                   fail $testname
+                   return 0
+               }
+               lappend objsrcs $obj
+               lappend objs $obj
            } else {
-               lappend src [file join [file dirname $file] $sfile]
+               lappend objsrcs $src
            }
        }
 
-       set comp_output [prune_warnings [run_host_cmd "$CC_FOR_TARGET" "$CFLAGS_FOR_TARGET $lookup_flags [concat $src] -o $lookup_output"]]
+       set comp_output [prune_warnings [run_host_cmd "$CC_FOR_TARGET" "$CFLAGS_FOR_TARGET $lookup_flags $ld_flags [concat $objsrcs] -o $lookup_output"]]
 
        if { $comp_output != ""} {
-           send_log "compilation of CTF program [concat $src] failed with <$comp_output>"
+           send_log "compilation of CTF program [concat $local_srcs] failed with <$comp_output>"
            fail $testname
            return 0
        }
@@ -306,12 +330,13 @@ proc run_lookup_test { name } {
        }
     }
 
-    # Invoke the lookup program on the outputs, possibly through the wrapper.
+    # Invoke the lookup program on the outputs, possibly through the wrapper, including all
+    # the object file names if they were filled out.
 
     if { [llength $opts(wrapper)] == 0 } {
-       set results [run_host_cmd tmpdir/lookup $lookup_output]
+       set results [run_host_cmd tmpdir/lookup "$lookup_output $objs"]
     } else {
-       set results [run_host_cmd "$opts(wrapper) tmpdir/lookup" $lookup_output]
+       set results [run_host_cmd "$opts(wrapper) tmpdir/lookup" "$lookup_output $objs"]
     }
 
     if { [regexp {^UNSUPPORTED: (.*)$} $results -> reason] } {
diff --git a/libctf/testsuite/libctf-lookup/big-struct-corruption.c b/libctf/testsuite/libctf-lookup/big-struct-corruption.c
new file mode 100644 (file)
index 0000000..2cc05be
--- /dev/null
@@ -0,0 +1,118 @@
+/* Determine whether libctf/33339 is fixed, if and only if GCC PR 121411 is also
+   fixed.  */
+
+#include "config.h"
+#include <ctf-api.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+/* Determine whether the passed-in struct's member's offsets ever descend.  */
+static int
+offsets_ascending (ctf_dict_t *fp, ctf_id_t type)
+{
+  ctf_next_t *it = NULL;
+  ssize_t offset, last_offset = 0;
+
+  while ((offset = ctf_member_next (fp, type, &it, NULL, NULL, 0)) >= 0)
+    {
+      if (offset < last_offset)
+       return 0;
+      last_offset = offset;
+    }
+  if (ctf_errno (fp) != ECTF_NEXT_END)
+    {
+      fprintf (stderr, "Cannot check member offsets: %s\n",
+              ctf_errmsg (ctf_errno (fp)));
+      exit (0);
+    }
+
+  return 1;
+}
+
+int
+main (int argc, char *argv[])
+{
+  ctf_archive_t *ctf;
+  ctf_dict_t *fp;
+  ctf_id_t type;
+  int err;
+
+  if (argc != 3)
+    {
+      fprintf (stderr, "Syntax: %s PROGRAM OBJ\n", argv[0]);
+      exit(1);
+    }
+
+  /* Check for bugginess of compiler.  */
+
+  if ((ctf = ctf_open (argv[2], NULL, &err)) == NULL)
+    {
+      fprintf (stderr, "Cannot open compiler object file %s: %s\n",
+              argv[2], ctf_errmsg (err));
+      exit (1);
+    }
+
+  /* Verify that offsets only ascend.  */
+
+  if ((fp = ctf_arc_lookup_symbol_name (ctf, "huge_used", &type, &err)) == NULL)
+    {
+      fprintf (stderr, "UNSUPPORTED: compiler does not provide expected symbol.\n");
+      exit (0);
+    }
+
+  if (!offsets_ascending (fp, type))
+    {
+      fprintf (stderr, "UNSUPPORTED: GCC bug PR121411 detected.\n");
+      exit (0);
+    }
+
+  ctf_dict_close (fp);
+  ctf_close (ctf);
+
+  /* Check if test is disabled (e.g. on 32-bit).  */
+
+  if ((ctf = ctf_open (argv[1], NULL, &err)) == NULL)
+    {
+      fprintf (stderr, "Cannot open linked binary test file %s: %s\n",
+              argv[1], ctf_errmsg (err));
+      exit (1);
+    }
+
+  if ((fp = ctf_arc_lookup_symbol_name (ctf, "test_disabled", &type, &err)) != NULL)
+    {
+      fprintf (stderr, "UNSUPPORTED: test not necessary on 32-bit targets.\n");
+      exit (0);
+    }
+
+  if ((fp = ctf_arc_lookup_symbol_name (ctf, "big_used", &type, &err)) == NULL)
+    {
+      fprintf (stderr, "big struct symbol not found.\n");
+      exit (1);
+    }
+
+  if (!offsets_ascending (fp, type))
+    {
+      fprintf (stderr, "large struct offsets incorrect.\n");
+      exit (1);
+    }
+  ctf_dict_close (fp);
+
+  if ((fp = ctf_arc_lookup_symbol_name (ctf, "huge_used", &type, &err)) == NULL)
+    {
+      fprintf (stderr, "huge struct symbol not found.\n");
+      exit (1);
+    }
+
+  if (!offsets_ascending (fp, type))
+    {
+      fprintf (stderr, "huge struct offsets incorrect.\n");
+      exit (1);
+    }
+
+  ctf_dict_close (fp);
+  ctf_close (ctf);
+
+  fprintf (stderr, "Large and huge structs working fine.\n");
+  exit (0);
+}
diff --git a/libctf/testsuite/libctf-lookup/big-struct-corruption.lk b/libctf/testsuite/libctf-lookup/big-struct-corruption.lk
new file mode 100644 (file)
index 0000000..980cb0b
--- /dev/null
@@ -0,0 +1,3 @@
+# source: big-struct-ctf.c
+# link: objects
+Large and huge structs working fine.
diff --git a/libctf/testsuite/libctf-lookup/big-struct-ctf.c b/libctf/testsuite/libctf-lookup/big-struct-ctf.c
new file mode 100644 (file)
index 0000000..fc99a3e
--- /dev/null
@@ -0,0 +1,72 @@
+#if defined (__SIZEOF_PTRDIFF_T__) && __SIZEOF_PTRDIFF_T__ > 4
+
+#define CONCAT_(a,b) a ## b
+#define CONCAT(a,b) CONCAT_(a, b)
+#define COUNT(name) CONCAT(name, __COUNTER__)
+#define MEMBNAME const char COUNT(memb)[1024 * 1024]
+#define MEMB10                                 \
+  MEMBNAME;                                    \
+  MEMBNAME;                                    \
+  MEMBNAME;                                    \
+  MEMBNAME;                                    \
+  MEMBNAME;                                    \
+  MEMBNAME;                                    \
+  MEMBNAME;                                    \
+  MEMBNAME;                                    \
+  MEMBNAME;                                    \
+  MEMBNAME;
+
+#define MEMB100                                        \
+  MEMB10                                       \
+  MEMB10                                       \
+  MEMB10                                       \
+  MEMB10                                       \
+  MEMB10                                       \
+  MEMB10                                       \
+  MEMB10                                       \
+  MEMB10                                       \
+  MEMB10                                       \
+  MEMB10
+
+#define MEMB1000                               \
+  MEMB100                                      \
+  MEMB100                                      \
+  MEMB100                                      \
+  MEMB100                                      \
+  MEMB100                                      \
+  MEMB100                                      \
+  MEMB100                                      \
+  MEMB100                                      \
+  MEMB100                                      \
+  MEMB100
+
+#define MEMB10000                              \
+  MEMB1000                                     \
+  MEMB1000                                     \
+  MEMB1000                                     \
+  MEMB1000                                     \
+  MEMB1000                                     \
+  MEMB1000                                     \
+  MEMB1000                                     \
+  MEMB1000                                     \
+  MEMB1000                                     \
+  MEMB1000
+
+struct big
+{
+  MEMB1000;
+};
+
+struct huge
+{
+  MEMB10000;
+};
+
+struct big big_used;
+struct huge huge_used;
+
+#else
+
+int test_disabled;
+
+#endif