libctf: create, types: functions, linkage, arg names (API ADVICE)

Functions change in CTFv4 by growing argument names as well as argument
types; the representation changes into a two-element array of (type, string
offset) rather than a simple array of arg types.  Functions also gain an
explicit linkage in a different type kind (CTF_K_FUNC_LINKAGE, which
corresponds to BTF_KIND_FUNC).

New API:

 typedef struct ctf_funcinfo {
 /* ... */
-  uint32_t ctc_argc;		/* Number of typed arguments to function.  */
+  size_t ctc_argc;		/* Number of typed arguments to function.  */
};

int ctf_func_arg_names (ctf_dict_t *, unsigned long, uint32_t, const char **);
int ctf_func_type_arg_names (ctf_dict_t *, ctf_id_t, uint32_t,
		 	    const char **names);
+extern int ctf_type_linkage (ctf_dict_t *, ctf_id_t);
-extern ctf_id_t ctf_add_function (ctf_dict_t *, uint32_t,
-				  const ctf_funcinfo_t *, const ctf_id_t *);
+extern ctf_id_t ctf_add_function (ctf_dict_t *, uint32_t,
+				  const ctf_funcinfo_t *, const ctf_id_t *,
+				  const char **arg_names);
+extern ctf_id_t ctf_add_function_linkage (ctf_dict_t *, uint32_t,
+					  ctf_id_t, const char *, int linkage);

Adding this is fairly straightforward; the only annoying part is the way the
callers need to allocate space for the arg name and type arrays.  Maybe we
should rethink these into something like ctf_type_aname(), allocating
space for the caller so the caller doesn't need to?  It would certainly
make all the callers in libctf much less complex...

While we're at it, adjust ctf_type_reference, ctf_type_align, and
ctf_type_size for the new internal API changes (they also all have
special-case code for functions).
This commit is contained in:
Nick Alcock
2025-04-25 10:55:41 +01:00
parent a632f3ed33
commit 39cdb3e395
6 changed files with 302 additions and 81 deletions

View File

@@ -888,14 +888,13 @@ ctf_set_array (ctf_dict_t *fp, ctf_id_t type, const ctf_arinfo_t *arp)
ctf_id_t
ctf_add_function (ctf_dict_t *fp, uint32_t flag,
const ctf_funcinfo_t *ctc, const ctf_id_t *argv)
const ctf_funcinfo_t *ctc, const ctf_id_t *argv,
const char **arg_names)
{
ctf_dtdef_t *dtd;
ctf_id_t type;
uint32_t vlen;
uint32_t *vdat;
ctf_param_t *vdat;
ctf_dict_t *tmp = fp;
size_t initial_vbytes;
size_t i;
if (ctc == NULL || (ctc->ctc_flags & ~CTF_FUNC_VARARG) != 0
@@ -903,40 +902,77 @@ ctf_add_function (ctf_dict_t *fp, uint32_t flag,
return (ctf_set_typed_errno (fp, EINVAL));
vlen = ctc->ctc_argc;
/* UPTODO: CTF_K_BIG prefix for big functions? */
if (vlen > 0xffff)
return (ctf_set_typed_errno (fp, EOVERFLOW));
if (ctc->ctc_flags & CTF_FUNC_VARARG)
vlen++; /* Add trailing zero to indicate varargs (see below). */
if (ctc->ctc_return != 0
&& ctf_lookup_by_id (&tmp, ctc->ctc_return) == NULL)
&& ctf_lookup_by_id (&tmp, ctc->ctc_return, NULL) == NULL)
return CTF_ERR; /* errno is set for us. */
if (vlen > CTF_MAX_VLEN)
return (ctf_set_typed_errno (fp, EOVERFLOW));
/* One word extra allocated for padding for 4-byte alignment if need be.
Not reflected in vlen: we don't want to copy anything into it, and
it's in addition to (e.g.) the trailing 0 indicating varargs. */
initial_vbytes = (sizeof (uint32_t) * (vlen + (vlen & 1)));
if ((type = ctf_add_generic (fp, flag, NULL, CTF_K_FUNCTION,
initial_vbytes, &dtd)) == CTF_ERR)
return CTF_ERR; /* errno is set for us. */
vdat = (uint32_t *) dtd->dtd_vlen;
for (i = 0; i < ctc->ctc_argc; i++)
{
tmp = fp;
if (argv[i] != 0 && ctf_lookup_by_id (&tmp, argv[i]) == NULL)
if (argv[i] != 0 && ctf_lookup_by_id (&tmp, argv[i], NULL) == NULL)
return CTF_ERR; /* errno is set for us. */
vdat[i] = (uint32_t) argv[i];
}
dtd->dtd_data.ctt_info = CTF_TYPE_INFO (CTF_K_FUNCTION, flag, vlen);
dtd->dtd_data.ctt_type = (uint32_t) ctc->ctc_return;
if (vlen > CTF_MAX_VLEN)
return (ctf_set_typed_errno (fp, EOVERFLOW));
if ((dtd = ctf_add_generic (fp, flag, NULL, CTF_K_FUNCTION, 0,
sizeof (ctf_param_t) * vlen, 0, NULL)) == NULL)
return CTF_ERR; /* errno is set for us. */
vdat = (ctf_param_t *) dtd->dtd_vlen;
for (i = 0; i < ctc->ctc_argc; i++)
{
vdat[i].cfp_name = ctf_str_add (fp, arg_names[i]);
vdat[i].cfp_type = (uint32_t) argv[i];
}
dtd->dtd_data->ctt_info = CTF_TYPE_INFO (CTF_K_FUNCTION, 0, vlen);
dtd->dtd_data->ctt_type = (uint32_t) ctc->ctc_return;
if (ctc->ctc_flags & CTF_FUNC_VARARG)
vdat[vlen - 1] = 0; /* Add trailing zero to indicate varargs. */
{
vdat[vlen - 1].cfp_type = 0; /* Add trailing zero to indicate varargs. */
vdat[vlen - 1].cfp_name = 0;
}
return dtd->dtd_type;
}
ctf_id_t
ctf_add_function_linkage (ctf_dict_t *fp, uint32_t flag,
ctf_id_t ref, const char *name, int linkage)
{
ctf_dtdef_t *dtd;
ctf_dict_t *tmp = fp;
if (ref == CTF_ERR || ref > CTF_MAX_TYPE)
return (ctf_set_typed_errno (fp, EINVAL));
if (linkage < 0 || linkage > 2)
return (ctf_set_typed_errno (fp, ECTF_LINKAGE));
if (ref != 0 && ctf_lookup_by_id (&tmp, ref, NULL) == NULL)
return CTF_ERR; /* errno is set for us. */
if (ctf_type_kind (fp, ref) != CTF_K_FUNCTION)
return (ctf_set_typed_errno (fp, ECTF_NOTFUNC));
if ((dtd = ctf_add_generic (fp, flag, name, CTF_K_FUNC_LINKAGE, 0,
0, 0, NULL)) == NULL)
return CTF_ERR; /* errno is set for us. */
dtd->dtd_data->ctt_info = CTF_TYPE_INFO (CTF_K_FUNC_LINKAGE, 0, linkage);
dtd->dtd_data->ctt_type = (uint32_t) ref;
return dtd->dtd_type;
}
@@ -1871,7 +1907,7 @@ ctf_add_funcobjt_sym_forced (ctf_dict_t *fp, int is_function, const char *name,
if (fp->ctf_flags & LCTF_NO_TYPE)
return (ctf_set_errno (fp, ECTF_NOTSERIALIZED));
if (ctf_lookup_by_id (&tmp, id) == NULL)
if (ctf_lookup_by_id (&tmp, id, NULL) == NULL)
return -1; /* errno is set for us. */
if (is_function && ctf_type_kind (fp, id) != CTF_K_FUNCTION)
@@ -2176,8 +2212,6 @@ ctf_add_type_internal (ctf_dict_t *dst_fp, ctf_dict_t *src_fp, ctf_id_t src_type
ctf_encoding_t src_en, dst_en;
ctf_arinfo_t src_ar, dst_ar;
ctf_funcinfo_t ctc;
ctf_id_t orig_src_type = src_type;
if ((src_prefix = ctf_lookup_by_id (&src_fp, src_type, &src_tp)) == NULL)
@@ -2423,18 +2457,68 @@ ctf_add_type_internal (ctf_dict_t *dst_fp, ctf_dict_t *src_fp, ctf_id_t src_type
dst_type = ctf_add_array (dst_fp, flag, &src_ar);
break;
/* UPTODO: FUNC_LINKAGE, DATASEC, VAR, *TAG */
case CTF_K_FUNCTION:
ctc.ctc_return = ctf_add_type_internal (dst_fp, src_fp,
src_tp->ctt_type,
proc_tracking_fp);
ctc.ctc_argc = 0;
ctc.ctc_flags = 0;
{
ctf_funcinfo_t fi;
ctf_id_t *argv;
const char **arg_names;
size_t i;
if (ctc.ctc_return == CTF_ERR)
return CTF_ERR; /* errno is set for us. */
if (ctf_func_type_info (src_fp, src_type, &fi) < 0)
return CTF_ERR; /* errno is set for us. */
dst_type = ctf_add_function (dst_fp, flag, &ctc, NULL);
break;
fi.ctc_return = ctf_add_type_internal (dst_fp, src_fp,
src_tp->ctt_type,
proc_tracking_fp);
if (fi.ctc_return == CTF_ERR)
return CTF_ERR; /* errno is set for us. */
if ((argv = calloc (fi.ctc_argc, sizeof (ctf_id_t *))) == NULL)
{
ctf_set_errno (src_fp, errno);
return CTF_ERR;
}
if (ctf_func_type_args (src_fp, src_type, fi.ctc_argc, argv) < 0)
{
free (argv);
return CTF_ERR; /* errno is set for us. */
}
for (i = 0; i < fi.ctc_argc; i++)
{
argv[i] = ctf_add_type_internal (dst_fp, src_fp,
argv[i],
proc_tracking_fp);
if (argv[i] == CTF_ERR)
{
free (argv);
return CTF_ERR; /* errno is set for us. */
}
}
if ((arg_names = calloc (fi.ctc_argc, sizeof (const char **))) == NULL)
{
free (argv);
free (arg_names);
return CTF_ERR; /* errno is set for us. */
}
if (ctf_func_type_arg_names (src_fp, src_type, fi.ctc_argc,
arg_names) < 0)
{
free (argv);
free (arg_names);
return CTF_ERR; /* errno is set for us. */
}
dst_type = ctf_add_function (dst_fp, flag, &fi, argv, arg_names);
free (argv);
free (arg_names);
break;
}
case CTF_K_STRUCT:
case CTF_K_UNION: