libctf: dump CTF array dimensions in the right order

Before GCC PR114186, all looked good in the land of
multidimensional arrays: you wrote

int a[5][10];

and ctf_type_aname() et al would print it as

int [5][10]

Unfortunately this was two bugs in one. GCC was emitting the array as if
it were int a[10][5], i.e. as this:

a -> [10] -> [5] -> int

rather than

a -> [5] -> [10] -> int

as it should be. libctf was hiding this by printing them in the wrong
order, concealing the bug from anyone using objdump --ctf or anything
but actual type graph traversal. Once this was fixed for GCC, the bug
was visible in libctf: multidimensional arrays were printed backwards!
(But this is just a print-time bug: the underlying bug, that something
traversing the type graph would see the array in backwards order, was
fixed by the fix to GCC.)

Fix this libctf bug, printing the arrays the right way round. In a
possibly futile attempt to retain some vestige of backwards
compatibility, introduce a new bug-compat flag CTF_F_ARRNELEMS, which,
if on, indicates that PR114186 is fixed and GCC is emitting array
elements the right way round. (Unfortunately, the fix went in without
this flag, so some GCCs will still emit CTF that will cause libctf to
print them wrong, even with this fix -- but it's no wronger than it was
before, and new GCC and new binutils, as well as GCC older than any fix
for PR114186 and new binutils, will print things properly. Someone
traversing the type graph will see things right after the GCC fix, wrong
before it, and there isn't really any reliable way to tell which you
have, though if CTF_F_ARRNELEMS is set, you definitely have a fixed GCC.
The test checks for this, but it's not something we expect actual users
to ever do -- CTF dict flags are an internal implementation detail with
no user-visible API for a reason.)

[nca: log message, test compat with older compilers]

include/
	* ctf.h (CTF_F_ARRNELEMS): New bug-compat flag.
	(CTF_F_MAX): Adjust.

libctf/
	PR libctf/32161
	* ctf-decl.c (ctf_decl_push): Prepend if this is an array and
	the bug-compat flag is set.
	* ctf-dump.c (ctf_dump_header): Dump the new bug-compat flag.
	* testsuite/libctf-lookup/multidim-array*: New test.
This commit is contained in:
Bruce McCulloch
2024-11-01 12:31:46 -04:00
committed by Nick Alcock
parent 572b0825c7
commit 47111cfd4f
6 changed files with 97 additions and 4 deletions

View File

@@ -213,8 +213,9 @@ typedef struct ctf_header
#define CTF_F_NEWFUNCINFO 0x2 /* New v3 func info section format. */
#define CTF_F_IDXSORTED 0x4 /* Index sections already sorted. */
#define CTF_F_DYNSTR 0x8 /* Strings come from .dynstr. */
#define CTF_F_ARRNELEMS 0x10 /* Array elems no longer reversed. */
#define CTF_F_MAX (CTF_F_COMPRESS | CTF_F_NEWFUNCINFO | CTF_F_IDXSORTED \
| CTF_F_DYNSTR)
| CTF_F_DYNSTR | CTF_F_ARRNELEMS)
typedef struct ctf_lblent
{

View File

@@ -154,9 +154,12 @@ ctf_decl_push (ctf_decl_t *cd, ctf_dict_t *fp, ctf_id_t type)
cd->cd_qualp = prec;
/* By convention qualifiers of base types precede the type specifier (e.g.
const int vs. int const) even though the two forms are equivalent. */
const int vs. int const) even though the two forms are equivalent.
As of gcc-14.2.0, arrays must also be prepended in order to dump with the
dimensions properly ordered. */
if (is_qual && prec == CTF_PREC_BASE)
if ((is_qual && prec == CTF_PREC_BASE) || ((kind == CTF_K_ARRAY) &&
(fp->ctf_openflags & (CTF_F_ARRNELEMS))))
ctf_list_prepend (&cd->cd_nodes[prec], cdp);
else
ctf_list_append (&cd->cd_nodes[prec], cdp);

View File

@@ -326,7 +326,7 @@ ctf_dump_header (ctf_dict_t *fp, ctf_dump_state_t *state)
if (fp->ctf_openflags > 0)
{
if (asprintf (&flagstr, "%s%s%s%s%s%s%s",
if (asprintf (&flagstr, "%s%s%s%s%s%s%s%s%s",
fp->ctf_openflags & CTF_F_COMPRESS
? "CTF_F_COMPRESS": "",
(fp->ctf_openflags & CTF_F_COMPRESS)
@@ -343,6 +343,12 @@ ctf_dump_header (ctf_dict_t *fp, ctf_dump_state_t *state)
&& (fp->ctf_openflags & ~(CTF_F_COMPRESS | CTF_F_NEWFUNCINFO
| CTF_F_IDXSORTED))
? ", " : "",
fp->ctf_openflags & CTF_F_ARRNELEMS
? "CTF_F_ARRNELEMS" : "",
fp->ctf_openflags & (CTF_F_ARRNELEMS)
&& (fp->ctf_openflags & ~(CTF_F_COMPRESS | CTF_F_NEWFUNCINFO
| CTF_F_IDXSORTED | CTF_F_ARRNELEMS))
? ", " : "",
fp->ctf_openflags & CTF_F_DYNSTR
? "CTF_F_DYNSTR" : "") < 0)
goto err;

View File

@@ -0,0 +1,3 @@
int a[3][5][9];
int b[1][2];

View File

@@ -0,0 +1,71 @@
#include <ctf-api.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int
main (int argc, char *argv[])
{
ctf_archive_t *ctf;
ctf_dict_t *fp;
int err;
ctf_dump_state_t *dump_state = NULL;
char *dumpstr;
ctf_next_t *it = NULL;
ctf_id_t type;
int flagged = 0;
if ((ctf = ctf_open (argv[1], NULL, &err)) == NULL)
goto open_err;
if ((fp = ctf_dict_open (ctf, NULL, &err)) == NULL)
goto open_err;
/* First, check for signs that the compiler is fixed but not emitting the
relevant flag yet. This combination is not expected to work right. */
while ((dumpstr = ctf_dump (fp, &dump_state, CTF_SECT_HEADER,
NULL, NULL)) != NULL)
{
if (strstr (dumpstr, "CTF_F_ARRNELEMS") != NULL)
flagged = 1;
free (dumpstr);
}
if (!flagged)
{
ctf_arinfo_t ar;
if ((type = ctf_lookup_by_symbol_name (fp, "a")) == CTF_ERR)
goto unexpected;
if (ctf_array_info (fp, type, &ar) < 0)
goto unexpected;
if (ar.ctr_nelems == 3)
{
fprintf (stderr, "UNSUPPORTED: compiler has GCC PR114186 fixed but "
"no indicative flag\n");
return 0;
}
}
/* Now check for the actual bug. */
while ((type = ctf_type_next (fp, &it, NULL, 1)) != -1)
printf ("%s\n", ctf_type_aname (fp, type));
ctf_dict_close (fp);
ctf_close (ctf);
return 0;
open_err:
fprintf (stderr, "%s: cannot open: %s\n", argv[0], ctf_errmsg (err));
return 1;
unexpected:
fprintf (stderr, "Cannot look up symbol to determine compiler bugginess: %s\n",
ctf_errmsg (ctf_errno (fp)));
return 1;
}

View File

@@ -0,0 +1,9 @@
# source: multidim-array-ctf.c
int
long unsigned int
int \[9\]
int \[5\]\[9\]
int \[3\]\[5\]\[9\]
int \[2\]
int \[1\]\[2\]