aboutsummaryrefslogtreecommitdiff
path: root/libctf
diff options
context:
space:
mode:
Diffstat (limited to 'libctf')
-rw-r--r--libctf/ChangeLog22
-rw-r--r--libctf/ctf-dedup.c6
-rw-r--r--libctf/ctf-impl.h12
-rw-r--r--libctf/ctf-lookup.c2
-rw-r--r--libctf/ctf-types.c227
-rw-r--r--libctf/ctf-util.c5
-rw-r--r--libctf/testsuite/libctf-lookup/struct-iteration-ctf.c28
-rw-r--r--libctf/testsuite/libctf-lookup/struct-iteration.c92
-rw-r--r--libctf/testsuite/libctf-lookup/struct-iteration.lk24
-rw-r--r--libctf/testsuite/libctf-lookup/struct-lookup.c60
-rw-r--r--libctf/testsuite/libctf-lookup/struct-lookup.lk4
11 files changed, 381 insertions, 101 deletions
diff --git a/libctf/ChangeLog b/libctf/ChangeLog
index db75fd5..57507c4 100644
--- a/libctf/ChangeLog
+++ b/libctf/ChangeLog
@@ -1,5 +1,27 @@
2021-01-05 Nick Alcock <nick.alcock@oracle.com>
+ * ctf-impl.h (struct ctf_next) <u.ctn_next>: Move to...
+ <ctn_next>: ... here.
+ * ctf-util.c (ctf_next_destroy): Unconditionally destroy it.
+ * ctf-lookup.c (ctf_symbol_next): Adjust accordingly.
+ * ctf-types.c (ctf_member_iter): Reimplement in terms of...
+ (ctf_member_next): ... this. Support recursive unnamed member
+ iteration (off by default).
+ (ctf_member_info): Look up members in unnamed sub-structs.
+ * ctf-dedup.c (ctf_dedup_rhash_type): Adjust ctf_member_next call.
+ (ctf_dedup_emit_struct_members): Likewise.
+ * testsuite/libctf-lookup/struct-iteration-ctf.c: Test empty unnamed
+ members, and a normal member after the end.
+ * testsuite/libctf-lookup/struct-iteration.c: Verify that
+ ctf_member_count is consistent with the number of successful returns
+ from a non-recursive ctf_member_next.
+ * testsuite/libctf-lookup/struct-iteration-*: New, test iteration
+ over struct members.
+ * testsuite/libctf-lookup/struct-lookup.c: New test.
+ * testsuite/libctf-lookup/struct-lookup.lk: New test.
+
+2021-01-05 Nick Alcock <nick.alcock@oracle.com>
+
* ctf-link.c (ctf_link_warn_outdated_inputs): New.
(ctf_link_write): Call it.
diff --git a/libctf/ctf-dedup.c b/libctf/ctf-dedup.c
index fd72a60..da88ae371 100644
--- a/libctf/ctf-dedup.c
+++ b/libctf/ctf-dedup.c
@@ -887,8 +887,8 @@ ctf_dedup_rhash_type (ctf_dict_t *fp, ctf_dict_t *input, ctf_dict_t **inputs,
ctf_dedup_sha1_add (&hash, &size, sizeof (ssize_t), "struct size",
depth);
- while ((offset = ctf_member_next (input, type, &i, &mname,
- &membtype)) >= 0)
+ while ((offset = ctf_member_next (input, type, &i, &mname, &membtype,
+ 0)) >= 0)
{
if (mname == NULL)
mname = "";
@@ -2956,7 +2956,7 @@ ctf_dedup_emit_struct_members (ctf_dict_t *output, ctf_dict_t **inputs,
target_type = CTF_DEDUP_GID_TO_TYPE (target_id);
while ((offset = ctf_member_next (input_fp, input_type, &j, &name,
- &membtype)) >= 0)
+ &membtype, 0)) >= 0)
{
err_fp = target;
err_type = target_type;
diff --git a/libctf/ctf-impl.h b/libctf/ctf-impl.h
index b19ae69..8a173c1 100644
--- a/libctf/ctf-impl.h
+++ b/libctf/ctf-impl.h
@@ -532,13 +532,15 @@ struct ctf_next
ssize_t ctn_size;
ssize_t ctn_increment;
uint32_t ctn_n;
+
+ /* Some iterators contain other iterators, in addition to their other
+ state. */
+ ctf_next_t *ctn_next;
+
/* We can save space on this side of things by noting that a dictionary is
either dynamic or not, as a whole, and a given iterator can only iterate
over one kind of thing at once: so we can overlap the DTD and non-DTD
- members, and the structure, variable and enum members, etc.
-
- Some of the _next iterators actually thunk down to another _next iterator
- themselves, so one of the options in here is a _next iterator! */
+ members, and the structure, variable and enum members, etc. */
union
{
const ctf_member_t *ctn_mp;
@@ -546,10 +548,10 @@ struct ctf_next
const ctf_dmdef_t *ctn_dmd;
const ctf_enum_t *ctn_en;
const ctf_dvdef_t *ctn_dvd;
- ctf_next_t *ctn_next;
ctf_next_hkv_t *ctn_sorted_hkv;
void **ctn_hash_slot;
} u;
+
/* This union is of various sorts of dict we can iterate over:
currently dictionaries and archives, dynhashes, and dynsets. */
union
diff --git a/libctf/ctf-lookup.c b/libctf/ctf-lookup.c
index 869c3843..0d6ef3c 100644
--- a/libctf/ctf-lookup.c
+++ b/libctf/ctf-lookup.c
@@ -442,7 +442,7 @@ ctf_symbol_next (ctf_dict_t *fp, ctf_next_t **it, const char **name,
return (ctf_set_errno (fp, ECTF_NEXT_END));
}
- err = ctf_dynhash_next (dynh, &i->u.ctn_next, &dyn_name, &dyn_value);
+ err = ctf_dynhash_next (dynh, &i->ctn_next, &dyn_name, &dyn_value);
/* This covers errors and also end-of-iteration. */
if (err != 0)
{
diff --git a/libctf/ctf-types.c b/libctf/ctf-types.c
index a3d824b..6275be0 100644
--- a/libctf/ctf-types.c
+++ b/libctf/ctf-types.c
@@ -41,76 +41,33 @@ ctf_type_ischild (ctf_dict_t * fp, ctf_id_t id)
int
ctf_member_iter (ctf_dict_t *fp, ctf_id_t type, ctf_member_f *func, void *arg)
{
- ctf_dict_t *ofp = fp;
- const ctf_type_t *tp;
- ctf_dtdef_t *dtd;
- ssize_t size, increment;
- uint32_t kind, n;
+ ctf_next_t *i = NULL;
+ ssize_t offset;
+ const char *name;
+ ctf_id_t membtype;
int rc;
- if ((type = ctf_type_resolve (fp, type)) == CTF_ERR)
- return -1; /* errno is set for us. */
-
- if ((tp = ctf_lookup_by_id (&fp, type)) == NULL)
- return -1; /* errno is set for us. */
-
- (void) ctf_get_ctt_size (fp, tp, &size, &increment);
- kind = LCTF_INFO_KIND (fp, tp->ctt_info);
-
- if (kind != CTF_K_STRUCT && kind != CTF_K_UNION)
- return (ctf_set_errno (ofp, ECTF_NOTSOU));
-
- if ((dtd = ctf_dynamic_type (fp, type)) == NULL)
+ while ((offset = ctf_member_next (fp, type, &i, &name, &membtype, 0)) >= 0)
{
- if (size < CTF_LSTRUCT_THRESH)
- {
- const ctf_member_t *mp = (const ctf_member_t *) ((uintptr_t) tp +
- increment);
-
- for (n = LCTF_INFO_VLEN (fp, tp->ctt_info); n != 0; n--, mp++)
- {
- const char *name = ctf_strptr (fp, mp->ctm_name);
- if ((rc = func (name, mp->ctm_type, mp->ctm_offset, arg)) != 0)
- return rc;
- }
- }
- else
- {
- const ctf_lmember_t *lmp = (const ctf_lmember_t *) ((uintptr_t) tp +
- increment);
-
- for (n = LCTF_INFO_VLEN (fp, tp->ctt_info); n != 0; n--, lmp++)
- {
- const char *name = ctf_strptr (fp, lmp->ctlm_name);
- if ((rc = func (name, lmp->ctlm_type,
- (unsigned long) CTF_LMEM_OFFSET (lmp), arg)) != 0)
- return rc;
- }
- }
- }
- else
- {
- ctf_dmdef_t *dmd;
-
- for (dmd = ctf_list_next (&dtd->dtd_u.dtu_members);
- dmd != NULL; dmd = ctf_list_next (dmd))
+ if ((rc = func (name, membtype, offset, arg)) != 0)
{
- if ((rc = func (dmd->dmd_name, dmd->dmd_type,
- dmd->dmd_offset, arg)) != 0)
- return rc;
+ ctf_next_destroy (i);
+ return rc;
}
}
+ if (ctf_errno (fp) != ECTF_NEXT_END)
+ return -1; /* errno is set for us. */
return 0;
}
/* Iterate over the members of a STRUCT or UNION, returning each member's
offset and optionally name and member type in turn. On end-of-iteration,
- returns -1. */
+ returns -1. If FLAGS is CTF_MN_RECURSE, recurse into unnamed members. */
ssize_t
ctf_member_next (ctf_dict_t *fp, ctf_id_t type, ctf_next_t **it,
- const char **name, ctf_id_t *membtype)
+ const char **name, ctf_id_t *membtype, int flags)
{
ctf_dict_t *ofp = fp;
uint32_t kind;
@@ -121,6 +78,7 @@ ctf_member_next (ctf_dict_t *fp, ctf_id_t type, ctf_next_t **it,
{
const ctf_type_t *tp;
ctf_dtdef_t *dtd;
+ ssize_t increment;
if ((type = ctf_type_resolve (fp, type)) == CTF_ERR)
return -1; /* errno is set for us. */
@@ -132,8 +90,7 @@ ctf_member_next (ctf_dict_t *fp, ctf_id_t type, ctf_next_t **it,
return ctf_set_errno (ofp, ENOMEM);
i->cu.ctn_fp = ofp;
- (void) ctf_get_ctt_size (fp, tp, &i->ctn_size,
- &i->ctn_increment);
+ (void) ctf_get_ctt_size (fp, tp, &i->ctn_size, &increment);
kind = LCTF_INFO_KIND (fp, tp->ctt_info);
if (kind != CTF_K_STRUCT && kind != CTF_K_UNION)
@@ -156,11 +113,9 @@ ctf_member_next (ctf_dict_t *fp, ctf_id_t type, ctf_next_t **it,
i->ctn_n = LCTF_INFO_VLEN (fp, tp->ctt_info);
if (i->ctn_size < CTF_LSTRUCT_THRESH)
- i->u.ctn_mp = (const ctf_member_t *) ((uintptr_t) tp +
- i->ctn_increment);
+ i->u.ctn_mp = (const ctf_member_t *) ((uintptr_t) tp + increment);
else
- i->u.ctn_lmp = (const ctf_lmember_t *) ((uintptr_t) tp +
- i->ctn_increment);
+ i->u.ctn_lmp = (const ctf_lmember_t *) ((uintptr_t) tp + increment);
}
else
i->u.ctn_dmd = ctf_list_next (&dtd->dtd_u.dtu_members);
@@ -178,41 +133,112 @@ ctf_member_next (ctf_dict_t *fp, ctf_id_t type, ctf_next_t **it,
if ((fp = ctf_get_dict (ofp, type)) == NULL)
return (ctf_set_errno (ofp, ECTF_NOPARENT));
- if (!(fp->ctf_flags & LCTF_RDWR))
- {
- if (i->ctn_n == 0)
- goto end_iter;
+ /* When we hit an unnamed struct/union member, we set ctn_type to indicate
+ that we are inside one, then return the unnamed member: on the next call,
+ we must skip over top-level member iteration in favour of iteration within
+ the sub-struct until it later turns out that that iteration has ended. */
- if (i->ctn_size < CTF_LSTRUCT_THRESH)
+ retry:
+ if (!i->ctn_type)
+ {
+ if (!(fp->ctf_flags & LCTF_RDWR))
{
- if (name)
- *name = ctf_strptr (fp, i->u.ctn_mp->ctm_name);
- if (membtype)
- *membtype = i->u.ctn_mp->ctm_type;
- offset = i->u.ctn_mp->ctm_offset;
- i->u.ctn_mp++;
+ if (i->ctn_n == 0)
+ goto end_iter;
+
+ if (i->ctn_size < CTF_LSTRUCT_THRESH)
+ {
+ const char *membname = ctf_strptr (fp, i->u.ctn_mp->ctm_name);
+
+ if (name)
+ *name = membname;
+ if (membtype)
+ *membtype = i->u.ctn_mp->ctm_type;
+ offset = i->u.ctn_mp->ctm_offset;
+
+ if (membname[0] == 0
+ && (ctf_type_kind (fp, i->u.ctn_mp->ctm_type) == CTF_K_STRUCT
+ || ctf_type_kind (fp, i->u.ctn_mp->ctm_type) == CTF_K_UNION))
+ i->ctn_type = i->u.ctn_mp->ctm_type;
+
+ i->u.ctn_mp++;
+ }
+ else
+ {
+ const char *membname = ctf_strptr (fp, i->u.ctn_lmp->ctlm_name);
+
+ if (name)
+ *name = membname;
+ if (membtype)
+ *membtype = i->u.ctn_lmp->ctlm_type;
+ offset = (unsigned long) CTF_LMEM_OFFSET (i->u.ctn_lmp);
+
+ if (membname[0] == 0
+ && (ctf_type_kind (fp, i->u.ctn_lmp->ctlm_type) == CTF_K_STRUCT
+ || ctf_type_kind (fp, i->u.ctn_lmp->ctlm_type) == CTF_K_UNION))
+ i->ctn_type = i->u.ctn_lmp->ctlm_type;
+
+ i->u.ctn_lmp++;
+ }
+ i->ctn_n--;
}
else
{
+ if (i->u.ctn_dmd == NULL)
+ goto end_iter;
+ /* The dmd contains a NULL for unnamed dynamic members. Don't inflict
+ this on our callers. */
if (name)
- *name = ctf_strptr (fp, i->u.ctn_lmp->ctlm_name);
+ {
+ if (i->u.ctn_dmd->dmd_name)
+ *name = i->u.ctn_dmd->dmd_name;
+ else
+ *name = "";
+ }
if (membtype)
- *membtype = i->u.ctn_lmp->ctlm_type;
- offset = (unsigned long) CTF_LMEM_OFFSET (i->u.ctn_lmp);
- i->u.ctn_lmp++;
+ *membtype = i->u.ctn_dmd->dmd_type;
+ offset = i->u.ctn_dmd->dmd_offset;
+
+ if (i->u.ctn_dmd->dmd_name == NULL
+ && (ctf_type_kind (fp, i->u.ctn_dmd->dmd_type) == CTF_K_STRUCT
+ || ctf_type_kind (fp, i->u.ctn_dmd->dmd_type) == CTF_K_UNION))
+ i->ctn_type = i->u.ctn_dmd->dmd_type;
+
+ i->u.ctn_dmd = ctf_list_next (i->u.ctn_dmd);
}
- i->ctn_n--;
+
+ /* The callers might want automatic recursive sub-struct traversal. */
+ if (!(flags & CTF_MN_RECURSE))
+ i->ctn_type = 0;
+
+ /* Sub-struct traversal starting? Take note of the offset of this member,
+ for later boosting of sub-struct members' offsets. */
+ if (i->ctn_type)
+ i->ctn_increment = offset;
}
+ /* Traversing a sub-struct? Just return it, with the offset adjusted. */
else
{
- if (i->u.ctn_dmd == NULL)
- goto end_iter;
- if (name)
- *name = i->u.ctn_dmd->dmd_name;
- if (membtype)
- *membtype = i->u.ctn_dmd->dmd_type;
- offset = i->u.ctn_dmd->dmd_offset;
- i->u.ctn_dmd = ctf_list_next (i->u.ctn_dmd);
+ ssize_t ret = ctf_member_next (fp, i->ctn_type, &i->ctn_next, name,
+ membtype, flags);
+
+ if (ret >= 0)
+ return ret + i->ctn_increment;
+
+ if (ctf_errno (fp) != ECTF_NEXT_END)
+ {
+ ctf_next_destroy (i);
+ *it = NULL;
+ i->ctn_type = 0;
+ return ret; /* errno is set for us. */
+ }
+
+ if (!ctf_assert (fp, (i->ctn_next == NULL)))
+ return -1; /* errno is set for us. */
+
+ i->ctn_type = 0;
+ /* This sub-struct has ended: on to the next real member. */
+ goto retry;
}
return offset;
@@ -1377,7 +1403,7 @@ ctf_type_compat (ctf_dict_t *lfp, ctf_id_t ltype,
}
/* Return the number of members in a STRUCT or UNION, or the number of
- enumerators in an ENUM. */
+ enumerators in an ENUM. The count does not include unnamed sub-members. */
int
ctf_member_count (ctf_dict_t *fp, ctf_id_t type)
@@ -1433,7 +1459,15 @@ ctf_member_info (ctf_dict_t *fp, ctf_id_t type, const char *name,
for (n = LCTF_INFO_VLEN (fp, tp->ctt_info); n != 0; n--, mp++)
{
- if (strcmp (ctf_strptr (fp, mp->ctm_name), name) == 0)
+ const char *membname = ctf_strptr (fp, mp->ctm_name);
+
+ if (membname[0] == 0
+ && (ctf_type_kind (fp, mp->ctm_type) == CTF_K_STRUCT
+ || ctf_type_kind (fp, mp->ctm_type) == CTF_K_UNION)
+ && (ctf_member_info (fp, mp->ctm_type, name, mip) == 0))
+ return 0;
+
+ if (strcmp (membname, name) == 0)
{
mip->ctm_type = mp->ctm_type;
mip->ctm_offset = mp->ctm_offset;
@@ -1448,7 +1482,15 @@ ctf_member_info (ctf_dict_t *fp, ctf_id_t type, const char *name,
for (n = LCTF_INFO_VLEN (fp, tp->ctt_info); n != 0; n--, lmp++)
{
- if (strcmp (ctf_strptr (fp, lmp->ctlm_name), name) == 0)
+ const char *membname = ctf_strptr (fp, lmp->ctlm_name);
+
+ if (membname[0] == 0
+ && (ctf_type_kind (fp, lmp->ctlm_type) == CTF_K_STRUCT
+ || ctf_type_kind (fp, lmp->ctlm_type) == CTF_K_UNION)
+ && (ctf_member_info (fp, lmp->ctlm_type, name, mip) == 0))
+ return 0;
+
+ if (strcmp (membname, name) == 0)
{
mip->ctm_type = lmp->ctlm_type;
mip->ctm_offset = (unsigned long) CTF_LMEM_OFFSET (lmp);
@@ -1464,7 +1506,14 @@ ctf_member_info (ctf_dict_t *fp, ctf_id_t type, const char *name,
for (dmd = ctf_list_next (&dtd->dtd_u.dtu_members);
dmd != NULL; dmd = ctf_list_next (dmd))
{
- if (strcmp (dmd->dmd_name, name) == 0)
+ if (dmd->dmd_name == NULL
+ && (ctf_type_kind (fp, dmd->dmd_type) == CTF_K_STRUCT
+ || ctf_type_kind (fp, dmd->dmd_type) == CTF_K_UNION)
+ && (ctf_member_info (fp, dmd->dmd_type, name, mip) == 0))
+ return 0;
+
+ if (dmd->dmd_name != NULL
+ && strcmp (dmd->dmd_name, name) == 0)
{
mip->ctm_type = dmd->dmd_type;
mip->ctm_offset = dmd->dmd_offset;
diff --git a/libctf/ctf-util.c b/libctf/ctf-util.c
index 879ebbf..4f126ba 100644
--- a/libctf/ctf-util.c
+++ b/libctf/ctf-util.c
@@ -283,9 +283,8 @@ ctf_next_destroy (ctf_next_t *i)
if (i->ctn_iter_fun == (void (*) (void)) ctf_dynhash_next_sorted)
free (i->u.ctn_sorted_hkv);
- if (i->ctn_iter_fun == (void (*) (void)) ctf_symbol_next
- && i->cu.ctn_fp->ctf_flags & LCTF_RDWR)
- ctf_next_destroy (i->u.ctn_next);
+ if (i->ctn_next)
+ ctf_next_destroy (i->ctn_next);
free (i);
}
diff --git a/libctf/testsuite/libctf-lookup/struct-iteration-ctf.c b/libctf/testsuite/libctf-lookup/struct-iteration-ctf.c
new file mode 100644
index 0000000..7df67ad
--- /dev/null
+++ b/libctf/testsuite/libctf-lookup/struct-iteration-ctf.c
@@ -0,0 +1,28 @@
+#include <unistd.h>
+
+struct foo_t
+{
+ int foo;
+ size_t bar;
+ const char *baz;
+ struct foo_t *self;
+ union
+ {
+ double should_not_appear;
+ char *nor_should_this;
+ } named;
+ struct
+ {
+ long unnamed_sub_member;
+ union
+ {
+ double one_more_level;
+ long yes_really_one_more;
+ };
+ };
+ struct {}; /* Empty ones */
+ union {};
+ int after_the_end;
+};
+
+struct foo_t used;
diff --git a/libctf/testsuite/libctf-lookup/struct-iteration.c b/libctf/testsuite/libctf-lookup/struct-iteration.c
new file mode 100644
index 0000000..0375060
--- /dev/null
+++ b/libctf/testsuite/libctf-lookup/struct-iteration.c
@@ -0,0 +1,92 @@
+#include <ctf-api.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+static int
+print_struct (const char *name, ctf_id_t membtype, unsigned long offset,
+ void *fp_)
+{
+ ctf_dict_t *fp = (ctf_dict_t *) fp_;
+ char *type_name = ctf_type_aname (fp, membtype);
+
+ printf ("iter test: %s, offset %lx, has type %lx/%s\n",
+ name, offset, membtype, type_name);
+ free (type_name);
+
+ return 0;
+}
+
+int
+main (int argc, char *argv[])
+{
+ ctf_dict_t *fp;
+ ctf_archive_t *ctf;
+ ctf_id_t type;
+ ctf_next_t *i = NULL;
+ const char *name;
+ ctf_id_t membtype;
+ ssize_t offset;
+ ssize_t icount = 0;
+ int err;
+
+ if (argc != 2)
+ {
+ fprintf (stderr, "Syntax: %s PROGRAM\n", argv[0]);
+ exit(1);
+ }
+
+ if ((ctf = ctf_open (argv[1], NULL, &err)) == NULL)
+ goto open_err;
+ if ((fp = ctf_dict_open (ctf, NULL, &err)) == NULL)
+ goto open_err;
+
+ /* Iterate over the structure members with each iterator type in turn. */
+
+ if ((type = ctf_lookup_by_name (fp, "struct foo_t") ) == CTF_ERR)
+ goto err;
+
+ if (ctf_member_iter (fp, type, print_struct, fp) < 0)
+ goto ierr;
+
+ while ((offset = ctf_member_next (fp, type, &i, &name, &membtype,
+ CTF_MN_RECURSE)) >= 0)
+ {
+ char *type_name = ctf_type_aname (fp, membtype);
+
+ printf ("next test: %s, offset %lx, has type %lx/%s\n",
+ name, offset, membtype, type_name);
+ free (type_name);
+ }
+ if (ctf_errno (fp) != ECTF_NEXT_END)
+ goto nerr;
+
+ /* Now make sure the count of members does not include any recursive
+ members. */
+ while ((offset = ctf_member_next (fp, type, &i, &name, &membtype, 0)) >= 0)
+ icount++;
+
+ if (ctf_errno (fp) != ECTF_NEXT_END)
+ goto nerr;
+
+ if (icount != ctf_member_count (fp, type))
+ printf ("member counts differ: %li by direct iteration, "
+ "%li by ctf_member_count\n", icount, ctf_member_count (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;
+ err:
+ fprintf (stderr, "Lookup failed: %s\n", ctf_errmsg (ctf_errno (fp)));
+ return 1;
+ ierr:
+ fprintf (stderr, "_iter iteration failed: %s\n", ctf_errmsg (ctf_errno (fp)));
+ return 1;
+ nerr:
+ fprintf (stderr, "_next iteration failed: %s\n", ctf_errmsg (ctf_errno (fp)));
+ return 1;
+}
diff --git a/libctf/testsuite/libctf-lookup/struct-iteration.lk b/libctf/testsuite/libctf-lookup/struct-iteration.lk
new file mode 100644
index 0000000..fd64454
--- /dev/null
+++ b/libctf/testsuite/libctf-lookup/struct-iteration.lk
@@ -0,0 +1,24 @@
+# source: struct-iteration-ctf.c
+# link: on
+iter test: foo, offset [0-9a-f]*, has type [0-9a-f]*/int
+iter test: bar, offset [0-9a-f]*, has type [0-9a-f]*/size_t
+iter test: baz, offset [0-9a-f]*, has type [0-9a-f]*/const char \*
+iter test: self, offset [0-9a-f]*, has type [0-9a-f]*/struct foo_t \*
+iter test: named, offset [0-9a-f]*, has type [0-9a-f]*/union
+iter test: , offset [0-9a-f]*, has type [0-9a-f]*/struct
+iter test: , offset [0-9a-f]*, has type [0-9a-f]*/struct
+iter test: , offset [0-9a-f]*, has type [0-9a-f]*/union
+iter test: after_the_end, offset [0-9a-f]*, has type [0-9a-f]*/int
+next test: foo, offset [0-9a-f]*, has type [0-9a-f]*/int
+next test: bar, offset [0-9a-f]*, has type [0-9a-f]*/size_t
+next test: baz, offset [0-9a-f]*, has type [0-9a-f]*/const char \*
+next test: self, offset [0-9a-f]*, has type [0-9a-f]*/struct foo_t \*
+next test: named, offset [0-9a-f]*, has type [0-9a-f]*/union
+next test: , offset [0-9a-f]*, has type [0-9a-f]*/struct
+next test: unnamed_sub_member, offset [0-9a-f]*, has type [0-9a-f]*/long int
+next test: , offset [0-9a-f]*, has type [0-9a-f]*/union
+next test: one_more_level, offset [0-9a-f]*, has type [0-9a-f]*/double
+next test: yes_really_one_more, offset [0-9a-f]*, has type [0-9a-f]*/long int
+next test: , offset [0-9a-f]*, has type [0-9a-f]*/struct
+next test: , offset [0-9a-f]*, has type [0-9a-f]*/union
+next test: after_the_end, offset [0-9a-f]*, has type [0-9a-f]*/int
diff --git a/libctf/testsuite/libctf-lookup/struct-lookup.c b/libctf/testsuite/libctf-lookup/struct-lookup.c
new file mode 100644
index 0000000..9b95317
--- /dev/null
+++ b/libctf/testsuite/libctf-lookup/struct-lookup.c
@@ -0,0 +1,60 @@
+#include <ctf-api.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+int
+main (int argc, char *argv[])
+{
+ ctf_dict_t *fp;
+ ctf_archive_t *ctf;
+ ctf_id_t type;
+ char *type_name;
+ ctf_membinfo_t mi;
+ int err;
+
+ if (argc != 2)
+ {
+ fprintf (stderr, "Syntax: %s PROGRAM\n", argv[0]);
+ exit(1);
+ }
+
+ if ((ctf = ctf_open (argv[1], NULL, &err)) == NULL)
+ goto open_err;
+ if ((fp = ctf_dict_open (ctf, NULL, &err)) == NULL)
+ goto open_err;
+
+ /* Dig out some strucutre members by name. */
+
+ if ((type = ctf_lookup_by_name (fp, "struct foo_t") ) == CTF_ERR)
+ goto err;
+
+ if (ctf_member_info (fp, type, "baz", &mi) < 0)
+ goto err;
+
+ type_name = ctf_type_aname (fp, mi.ctm_type);
+ printf ("baz is of type %s, at offset %lx\n", type_name, mi.ctm_offset);
+ free (type_name);
+
+ if (ctf_member_info (fp, type, "one_more_level", &mi) < 0)
+ goto err;
+
+ type_name = ctf_type_aname (fp, mi.ctm_type);
+ printf ("one_more_level is of type %s, at offset %lx\n", type_name, mi.ctm_offset);
+ free (type_name);
+
+ if (ctf_member_info (fp, type, "should_not_appear", &mi) >= 0
+ || ctf_errno (fp) != ECTF_NOMEMBNAM)
+ fprintf (stderr, "should_not_appear appeared.\n");
+
+ 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;
+ err:
+ fprintf (stderr, "Lookup failed: %s\n", ctf_errmsg (ctf_errno (fp)));
+ return 1;
+}
diff --git a/libctf/testsuite/libctf-lookup/struct-lookup.lk b/libctf/testsuite/libctf-lookup/struct-lookup.lk
new file mode 100644
index 0000000..b848823
--- /dev/null
+++ b/libctf/testsuite/libctf-lookup/struct-lookup.lk
@@ -0,0 +1,4 @@
+# source: struct-iteration-ctf.c
+# link: on
+baz is of type const char \*, at offset [0-9a-z]*
+one_more_level is of type double, at offset [0-9a-z]*