uint32_t cth_parname; /* Ref to basename of parent. */
uint32_t cth_cuname; /* Ref to CU name (may be 0). */
uint32_t cth_parent_strlen; /* cth_strlen of parent (may be 0). */
+ uint32_t cth_parent_typemax; /* Number of types in parent (may be 0). */
uint32_t cth_lbloff; /* Offset of label section. */
uint32_t cth_objtoff; /* Offset of object section. */
uint32_t cth_funcoff; /* Offset of function section. */
Version: 5 \(CTF_VERSION_4\)
#...
0x[0-9a-f]*: \(kind 6\) struct A \(.*
- \[0x0\] : ID 0x[0-9a-f]*: \(kind 7\) union \(.*
+ \[0x0\] : ID 0x[0-9a-f]*: \(kind 7\) union \(.*
#...
0x[0-9a-f]*: \(kind 6\) struct A \(.*
- \[0x0\] : ID 0x[0-9a-f]*: \(kind 7\) union \(.*
+ \[0x0\] : ID 0x[0-9a-f]*: \(kind 7\) union \(.*
#...
Function objects:
Variables:
- b -> 0x80000001: \(kind 6\) struct B \(size 0x[0-9a-f]*\) \(aligned at 0x[0-9a-f]*\)
+ b -> 0x[0-9a-f]*: \(kind 6\) struct B \(size 0x[0-9a-f]*\) \(aligned at 0x[0-9a-f]*\)
Types:
- 0x8[0-9a-f]*: \(kind 6\) struct B .*
+ 0x[0-9a-f]*: \(kind 6\) struct B .*
*\[0x0\] c: ID 0x[0-9a-f]*: \(kind 3\) struct C \* \(.*
Strings:
Function objects:
Variables:
- b -> 0x80000001: \(kind 6\) struct B \(.*
+ b -> 0x[0-9a-f]*: \(kind 6\) struct B \(.*
Types:
- 0x8[0-9a-f]*: \(kind 6\) struct B \(.*
+ 0x[0-9a-f]*: \(kind 6\) struct B \(.*
*\[0x0\] c: ID 0x[0-9a-f]*: \(kind 3\) struct C \* \(.*
*\[0x[0-9a-f]*\] wombat: ID 0x[0-9a-f]*: \(kind 1\) int \(format 0x1\) \(size 0x[0-9a-f]*\) \(aligned at 0x[0-9a-f]*\)
Function objects:
Variables:
- a -> 0x80000001: \(kind 6\) struct A \(size 0x[0-9a-f]*\) \(aligned at 0x[0-9a-f]*\)
+ a -> 0x[0-9a-f]*: \(kind 6\) struct A \(size 0x[0-9a-f]*\) \(aligned at 0x[0-9a-f]*\)
Types:
- 0x8[0-9a-f]*: \(kind 6\) struct A \(size 0x[0-9a-f]*\) \(aligned at 0x[0-9a-f]*\)
+ 0x[0-9a-f]*: \(kind 6\) struct A \(size 0x[0-9a-f]*\) \(aligned at 0x[0-9a-f]*\)
*\[0x0\] b: ID 0x[0-9a-f]*: \(kind 3\) struct B \* \(size 0x[0-9a-f]*\) \(aligned at 0x[0-9a-f]*\)
Strings:
Function objects:
Variables:
- a -> 0x80000001: \(kind 6\) struct A \(size 0x[0-9a-f]*\) \(aligned at 0x[0-9a-f]*\)
+ a -> 0x[0-9a-f]*: \(kind 6\) struct A \(size 0x[0-9a-f]*\) \(aligned at 0x[0-9a-f]*\)
Types:
- 0x8[0-9a-f]*: \(kind 6\) struct A \(size 0x[0-9a-f]*\) \(aligned at 0x[0-9a-f]*\)
+ 0x[0-9a-f]*: \(kind 6\) struct A \(size 0x[0-9a-f]*\) \(aligned at 0x[0-9a-f]*\)
*\[0x0\] b: ID 0x[0-9a-f]*: \(kind 3\) struct B \* \(size 0x[0-9a-f]*\) \(aligned at 0x[0-9a-f]*\)
*\[0x[0-9a-f]*\] wombat: ID 0x[0-9a-f]*: \(kind 1\) int \(format 0x1\) \(size 0x[0-9a-f]*\) \(aligned at 0x[0-9a-f]*\)
Function objects:
Variables:
- c -> 0x80000001: \(kind 6\) struct C \(size 0x[0-9a-f]*\) \(aligned at 0x[0-9a-f]*\)
+ c -> 0x[0-9a-f]*: \(kind 6\) struct C \(size 0x[0-9a-f]*\) \(aligned at 0x[0-9a-f]*\)
Types:
- 0x80000001: \(kind 6\) struct C \(size 0x[0-9a-f]*\) \(aligned at 0x[0-9a-f]*\)
+ 0x[0-9a-f]*: \(kind 6\) struct C \(size 0x[0-9a-f]*\) \(aligned at 0x[0-9a-f]*\)
*\[0x0\] a: ID 0x[0-9a-f]*: \(kind 3\) struct A \* \(size 0x[0-9a-f]*\) \(aligned at 0x[0-9a-f]*\)
Strings:
Function objects:
Variables:
- c -> 0x80000001: \(kind 6\) struct C \(size 0x[0-9a-f]*\) \(aligned at 0x[0-9a-f]*\)
+ c -> 0x[0-9a-f]*: \(kind 6\) struct C \(size 0x[0-9a-f]*\) \(aligned at 0x[0-9a-f]*\)
Types:
- 0x80000001: \(kind 6\) struct C \(size 0x[0-9a-f]*\) \(aligned at 0x[0-9a-f]*\)
- \[0x0\] a: ID 0x[0-9a-f]*: \(kind 3\) struct A \* \(size 0x[0-9a-f]*\) \(aligned at 0x[0-9a-f]*\)
- \[0x[0-9a-f]*\] wombat: ID 0x[0-9a-f]*: \(kind 1\) int \(format 0x1\) \(size 0x[0-9a-f]*\) \(aligned at 0x[0-9a-f]*\)
+ 0x[0-9a-f]*: \(kind 6\) struct C \(size 0x[0-9a-f]*\) \(aligned at 0x[0-9a-f]*\)
+ \[0x0\] a: ID 0x[0-9a-f]*: \(kind 3\) struct A \* \(size 0x[0-9a-f]*\) \(aligned at 0x[0-9a-f]*\)
+ \[0x[0-9a-f]*\] wombat: ID 0x[0-9a-f]*: \(kind 1\) int \(format 0x1\) \(size 0x[0-9a-f]*\) \(aligned at 0x[0-9a-f]*\)
Strings:
#...
CTF archive member: .*enum.*\.c:
#...
Types:
- 0x80000001: \(kind 8\) enum day_of_the_week \(size 0x[0-9a-f]*\) \(aligned at 0x[0-9a-f]*\)
- Sunday: 0
- Monday: 1
- Tuesday: 2
- Wednesday: 3
- Thursday: 4
- Friday: 5
- Saturday: 6
+ 0x[0-9a-f]*: \(kind 8\) enum day_of_the_week \(size 0x[0-9a-f]*\) \(aligned at 0x[0-9a-f]*\)
+ Sunday: 0
+ Monday: 1
+ Tuesday: 2
+ Wednesday: 3
+ Thursday: 4
+ Friday: 5
+ Saturday: 6
Strings:
#...
CTF archive member: .*enum.*\.c:
#...
Types:
- 0x80000001: \(kind 8\) enum day_of_the_week \(size 0x[0-9a-f]*\) \(aligned at 0x[0-9a-f]*\)
- Monday: 0
- Tuesday: 1
- Wednesday: 2
- Thursday: 3
- Friday: 4
- Saturday: 5
- Sunday: 6
+ 0x[0-9a-f]*: \(kind 8\) enum day_of_the_week \(size 0x[0-9a-f]*\) \(aligned at 0x[0-9a-f]*\)
+ Monday: 0
+ Tuesday: 1
+ Wednesday: 2
+ Thursday: 3
+ Friday: 4
+ Saturday: 5
+ Sunday: 6
Strings:
#...
#...
Types:
0x1: .*int .*
- 0x[0-9]: \(kind 10\) word .* -> 0x[0-9]: \(kind 1\) .*int \(format 0x1\) \(size 0x[0-9a-f]*\) \(aligned at 0x[0-9a-f]*\)
- 0x[0-9]:.*int .*
+ 0x[0-9a-f]: \(kind 10\) word .* -> 0x[0-9]: \(kind 1\) .*int \(format 0x1\) \(size 0x[0-9a-f]*\) \(aligned at 0x[0-9a-f]*\)
+ 0x[0-9a-f]:.*int .*
Strings:
#...
CTF archive member: .*typedef.*\.c:
#...
Types:
- 0x80000001: \(kind 10\) word .* -> 0x[0-9]: \(kind 1\) .*int \(format 0x1\) \(size 0x[0-9a-f]*\) \(aligned at 0x[0-9a-f]*\)
+ 0x[0-9a-f]*: \(kind 10\) word .* -> 0x[0-9]: \(kind 1\) .*int \(format 0x1\) \(size 0x[0-9a-f]*\) \(aligned at 0x[0-9a-f]*\)
Strings:
#...
CTF archive member: .*/ld/testsuite/ld-ctf/cross-tu-cyclic-1\.c:
#...
Types:
- 0x80[0-9a-f]*: \(kind 6\) struct A .*
+ 0x[0-9a-f]*: \(kind 6\) struct A .*
*\[0x0\] a: ID 0x[0-9a-f]*: \(kind 1\) long int .*
*\[0x[0-9a-f]*\] foo: ID 0x[0-9a-f]*\: \(kind 3\) struct B \* .*
CTF archive member: .*/ld/testsuite/ld-ctf/cross-tu-cyclic-2\.c:
#...
Types:
- 0x80[0-9a-f]*: \(kind 6\) struct A .*
+ 0x[0-9a-f]*: \(kind 6\) struct A .*
*\[0x0\] a: ID 0x[0-9a-f]*: \(kind 1\) long int .*
*\[0x[0-9a-f]*\] foo: ID 0x[0-9a-f]*: \(kind 3\) struct B \* .*
*\[0x[0-9a-f]*\] bar: ID 0x[0-9a-f]*: \(kind 3\) struct C \* .*
Labels:
Data objects:
- var_[0-9]* -> 0x80000001*: \(kind 10\) foo_t \(size 0x[0-9a-f]*\) \(aligned at 0x[0-9a-f]*\) -> .*
- var_[0-9]* -> 0x80000001*: \(kind 10\) foo_t \(size 0x[0-9a-f]*\) \(aligned at 0x[0-9a-f]*\) -> .*
- var_[0-9]* -> 0x80000001*: \(kind 10\) foo_t \(size 0x[0-9a-f]*\) \(aligned at 0x[0-9a-f]*\) -> .*
- var_[0-9]* -> 0x80000001*: \(kind 10\) foo_t \(size 0x[0-9a-f]*\) \(aligned at 0x[0-9a-f]*\) -> .*
- var_[0-9]* -> 0x80000001*: \(kind 10\) foo_t \(size 0x[0-9a-f]*\) \(aligned at 0x[0-9a-f]*\) -> .*
- var_[0-9]* -> 0x80000001*: \(kind 10\) foo_t \(size 0x[0-9a-f]*\) \(aligned at 0x[0-9a-f]*\) -> .*
- var_[0-9]* -> 0x80000001*: \(kind 10\) foo_t \(size 0x[0-9a-f]*\) \(aligned at 0x[0-9a-f]*\) -> .*
- var_[0-9]* -> 0x80000001*: \(kind 10\) foo_t \(size 0x[0-9a-f]*\) \(aligned at 0x[0-9a-f]*\) -> .*
+ var_[0-9]* -> 0x[0-9a-f]*: \(kind 10\) foo_t \(size 0x[0-9a-f]*\) \(aligned at 0x[0-9a-f]*\) -> .*
+ var_[0-9]* -> 0x[0-9a-f]*: \(kind 10\) foo_t \(size 0x[0-9a-f]*\) \(aligned at 0x[0-9a-f]*\) -> .*
+ var_[0-9]* -> 0x[0-9a-f]*: \(kind 10\) foo_t \(size 0x[0-9a-f]*\) \(aligned at 0x[0-9a-f]*\) -> .*
+ var_[0-9]* -> 0x[0-9a-f]*: \(kind 10\) foo_t \(size 0x[0-9a-f]*\) \(aligned at 0x[0-9a-f]*\) -> .*
+ var_[0-9]* -> 0x[0-9a-f]*: \(kind 10\) foo_t \(size 0x[0-9a-f]*\) \(aligned at 0x[0-9a-f]*\) -> .*
+ var_[0-9]* -> 0x[0-9a-f]*: \(kind 10\) foo_t \(size 0x[0-9a-f]*\) \(aligned at 0x[0-9a-f]*\) -> .*
+ var_[0-9]* -> 0x[0-9a-f]*: \(kind 10\) foo_t \(size 0x[0-9a-f]*\) \(aligned at 0x[0-9a-f]*\) -> .*
+ var_[0-9]* -> 0x[0-9a-f]*: \(kind 10\) foo_t \(size 0x[0-9a-f]*\) \(aligned at 0x[0-9a-f]*\) -> .*
#...
Function objects:
Variables:
Types:
- 0x80000001: \(kind 10\) foo_t .* -> .* int .*
+ 0x[0-9a-f]*: \(kind 10\) foo_t .* -> .* int .*
#...
Labels:
Data objects:
- var_[0-9]* -> 0x80000001*: \(kind 10\) foo_t \(size 0x[0-9a-f]*\) \(aligned at 0x[0-9a-f]*\) -> .*
- var_[0-9]* -> 0x80000001*: \(kind 10\) foo_t \(size 0x[0-9a-f]*\) \(aligned at 0x[0-9a-f]*\) -> .*
- var_[0-9]* -> 0x80000001*: \(kind 10\) foo_t \(size 0x[0-9a-f]*\) \(aligned at 0x[0-9a-f]*\) -> .*
- var_[0-9]* -> 0x80000001*: \(kind 10\) foo_t \(size 0x[0-9a-f]*\) \(aligned at 0x[0-9a-f]*\) -> .*
- var_[0-9]* -> 0x80000001*: \(kind 10\) foo_t \(size 0x[0-9a-f]*\) \(aligned at 0x[0-9a-f]*\) -> .*
- var_[0-9]* -> 0x80000001*: \(kind 10\) foo_t \(size 0x[0-9a-f]*\) \(aligned at 0x[0-9a-f]*\) -> .*
- var_[0-9]* -> 0x80000001*: \(kind 10\) foo_t \(size 0x[0-9a-f]*\) \(aligned at 0x[0-9a-f]*\) -> .*
- var_[0-9]* -> 0x80000001*: \(kind 10\) foo_t \(size 0x[0-9a-f]*\) \(aligned at 0x[0-9a-f]*\) -> .*
+ var_[0-9]* -> 0x[0-9a-f]*: \(kind 10\) foo_t \(size 0x[0-9a-f]*\) \(aligned at 0x[0-9a-f]*\) -> .*
+ var_[0-9]* -> 0x[0-9a-f]*: \(kind 10\) foo_t \(size 0x[0-9a-f]*\) \(aligned at 0x[0-9a-f]*\) -> .*
+ var_[0-9]* -> 0x[0-9a-f]*: \(kind 10\) foo_t \(size 0x[0-9a-f]*\) \(aligned at 0x[0-9a-f]*\) -> .*
+ var_[0-9]* -> 0x[0-9a-f]*: \(kind 10\) foo_t \(size 0x[0-9a-f]*\) \(aligned at 0x[0-9a-f]*\) -> .*
+ var_[0-9]* -> 0x[0-9a-f]*: \(kind 10\) foo_t \(size 0x[0-9a-f]*\) \(aligned at 0x[0-9a-f]*\) -> .*
+ var_[0-9]* -> 0x[0-9a-f]*: \(kind 10\) foo_t \(size 0x[0-9a-f]*\) \(aligned at 0x[0-9a-f]*\) -> .*
+ var_[0-9]* -> 0x[0-9a-f]*: \(kind 10\) foo_t \(size 0x[0-9a-f]*\) \(aligned at 0x[0-9a-f]*\) -> .*
+ var_[0-9]* -> 0x[0-9a-f]*: \(kind 10\) foo_t \(size 0x[0-9a-f]*\) \(aligned at 0x[0-9a-f]*\) -> .*
#...
Function objects:
Variables:
Types:
- 0x80000001: \(kind 10\) foo_t .* -> .* int .*
+ 0x[0-9a-f]*: \(kind 10\) foo_t .* -> .* int .*
#...
CTF archive member: .*enum.*\.c:
#...
Types:
- 0x80000001: \(kind 8\) enum intersecting_days_of_the_week \(size 0x[0-9a-f]*\) \(aligned at 0x[0-9a-f]*\)
- Montag: 1
- Tuesday: 2
+ 0x[0-9a-f]*: \(kind 8\) enum intersecting_days_of_the_week \(size 0x[0-9a-f]*\) \(aligned at 0x[0-9a-f]*\)
+ Montag: 1
+ Tuesday: 2
Strings:
#...
CTF archive member: .*enum.*\.c:
#...
Types:
- 0x80000001: \(kind 8\) enum first_day_of_the_week \(size 0x[0-9a-f]*\) \(aligned at 0x[0-9a-f]*\)
- Sunday: 0
+ 0x[0-9a-f]*: \(kind 8\) enum first_day_of_the_week \(size 0x[0-9a-f]*\) \(aligned at 0x[0-9a-f]*\)
+ Sunday: 0
Strings:
#...
#...
Types:
0x[0-9a-f]*: \(kind 6\) struct A .*
- \[0x0\] wurblefrotz: ID .*
+ \[0x0\] wurblefrotz: ID .*
Strings:
0x0: .ctf
#...
Types:
0x[0-9a-f]*: \(kind 6\) struct A .*
- \[0x0\] wurblefritz: ID .*
+ \[0x0\] wurblefritz: ID .*
Strings:
0x0: .ctf
if (arc->ctfi_crossdict_cache == NULL)
arc->ctfi_crossdict_cache = fp;
+ /* If this archive has multiple members, and this is a parent, pretend
+ that we have opened at least one child. This forces type and string
+ allocations in the parent to use provisional IDs, permitting you to
+ import children into it even if you modify the parent before you import
+ any. */
+ if (arc->ctfi_is_archive && arc->ctfi_archive->ctfa_ndicts > 1
+ && !(fp->ctf_flags & LCTF_CHILD))
+ {
+ ctf_dprintf ("archived parent: max children bumped.\n");
+ fp->ctf_max_children++;
+ }
+
return fp;
oom:
return 0;
}
+/* Assign an ID to a newly-created type.
+
+ The type ID assignment scheme in libctf divides types into three
+ classes.
+
+ - static types are types read in from an already-existing dict. They are
+ stored only in the ctf_buf and have type indexes ranging from 1 up to
+ fp->ctf_typemax (usually the same as fp->ctf_stypes, but may be differnt
+ for newly-created children just imported to parents with already-present
+ dynamic types). Their IDs are derived from their index in the ctf_buf and
+ are not explicitly assigned, though serialization tracks them in order to
+ update type IDs that reference them.
+
+ Type IDs in a child dict start from fp->ctf_header->ctf_parent_typemax
+ (fp->ctf_stypes in the parent). There is no gap as in CTFv3 and below:
+ the IDs run continuously.
+
+ - dynamic types are added by ctf_add_*() (ultimately, ctf_add_generic) and
+ have DTDs: their type IDs are stored in dtd->dtd_type, and the DTD hashtab
+ is indexed by type ID.
+
+ The simplest form of these types, nonprovisionally-numbered dynamic types,
+ have type IDs stretching from fp->ctf_stypes up to fp->ctf_idmax, and
+ corresponding indexes. Such types only exist for child dicts and for
+ parent dicts which had types added before any children were imported.
+
+ - As soon as a child is imported, the parent starts allocating provisionally-
+ numbered dynamic types from the top of the type space down, updating
+ ctf_provtypemax and ctf_nprovtypes as it goes, and bumping ctf_typemax:
+ ctf_idmax is no longer bumped. The child continues to allocate in lower
+ type space starting from the parent's ctf_idmax + 1. Obviously all
+ references to provisional types can't stick around: so at serialization
+ time we note down the position of every reference to a provisional type ID
+ and all child type IDs, then lay out the type table by going over the
+ nonprovisional types and then the provisional ones and dropping them in
+ place in their serialized buffers, work out what the final type IDs will
+ be, and update all the refs accordingly, changing every type ID that refers
+ to the old type to refer to the new one instead. (See ctf_serialize.)
+
+ The indexes of provisional types run identically to the indexes of
+ non-provisional types, i.e. straight upwards without breaks or
+ discontinuities, even though this probably overlaps type IDs in the child.
+ Indexes and type IDs are not the same!
+
+ At serialization time, we track references to type IDs in the same dict via
+ the refs system while the type table et al are being built (during
+ preserialization), and update them with the real type IDs at final
+ serialization time; the final type IDs are recorded in the dtd_final_type,
+ and we assert if a future serialization would assign a different ID (which
+ should be impossible). When child dicts are serialized, references to parent
+ types are updated with the dtd_final_type of that type whenever one is set.
+ It is considered an error to try to serialize a child while its parent has
+ provisional types that have not yet had IDs assigned.
+
+ (The refs system is not employed to track references from child dicts to
+ parents, since forward references are not possible between dicts: the parent
+ dict must have been completely serialized when serializing a child. We can't
+ be halfway through, which is the case the refs system is there to handle:
+ refs from structure members to types not yet known, etc.)
+
+ Only parents have provisional type IDs! Child IDs are always simply assigned
+ straight in the child. This means that the provisional ID space is not
+ sparse, and we don't need to worry about child and parent IDs being
+ interspersed in it. (Not yet, anyway: if we get multilevel parents this will
+ become a concern).
+
+ Note that you can add types to a parent at any time, even after children have
+ been serialized. This works fine, except that you cannot use the
+ newly-written dict as a parent for the same children, since they were written
+ out assuming a smaller number of types in the parent. */
+
+static ctf_id_t
+ctf_assign_id (ctf_dict_t *fp)
+{
+ uint32_t idx;
+
+ /* All type additions increase the max index. */
+
+ idx = ++fp->ctf_typemax;
+
+ /* Is this a parent with an attached child? Provisional type. */
+
+ if (!(fp->ctf_flags & LCTF_CHILD) && (fp->ctf_max_children > 0))
+ {
+ fp->ctf_provtypemax--;
+ fp->ctf_nprovtypes++;
+ }
+ else
+ fp->ctf_idmax++;
+
+ return ctf_index_to_type (fp, idx);
+}
+
/* Note: vlen is the amount of space *allocated* for the vlen. It may well not
be the amount of space used (yet): the space used is declared in per-kind
fashion in the dtd_data's info word. */
{
ctf_dtdef_t *dtd;
ctf_id_t type;
+ ctf_dict_t *pfp = fp;
+
+ if (fp->ctf_parent)
+ pfp = fp->ctf_parent;
if (flag != CTF_ADD_NONROOT && flag != CTF_ADD_ROOT)
return (ctf_set_typed_errno (fp, EINVAL));
- if (ctf_index_to_type (fp, fp->ctf_typemax) >= CTF_MAX_TYPE)
+ if (fp->ctf_typemax + 1 >= pfp->ctf_provtypemax)
return (ctf_set_typed_errno (fp, ECTF_FULL));
- if (ctf_index_to_type (fp, fp->ctf_typemax) == (CTF_MAX_PTYPE - 1))
- return (ctf_set_typed_errno (fp, ECTF_FULL));
+ /* Prohibit addition of types in the middle of serialization. */
+
+ if (fp->ctf_flags & LCTF_NO_TYPE)
+ return (ctf_set_errno (fp, ECTF_NOTSERIALIZED));
if (fp->ctf_flags & LCTF_NO_STR)
return (ctf_set_errno (fp, ECTF_NOPARENT));
+ if (fp->ctf_flags & LCTF_CHILD && fp->ctf_parent == NULL)
+ return (ctf_set_errno (fp, ECTF_NOPARENT));
+
/* Prohibit addition of a root-visible type that is already present
in the non-dynamic portion. */
/* Make sure ptrtab always grows to be big enough for all types. */
if (ctf_grow_ptrtab (fp) < 0)
- return CTF_ERR; /* errno is set for us. */
+ return CTF_ERR; /* errno is set for us. */
if ((dtd = calloc (1, sizeof (ctf_dtdef_t))) == NULL)
return (ctf_set_typed_errno (fp, EAGAIN));
else
dtd->dtd_vlen = NULL;
- type = ++fp->ctf_typemax;
- type = ctf_index_to_type (fp, type);
+ type = ctf_assign_id (fp);
dtd->dtd_data.ctt_name = ctf_str_add (fp, name);
dtd->dtd_type = type;
{
ctf_dtdef_t *dtd;
ctf_id_t type;
- ctf_dict_t *tmp = fp;
+ ctf_dict_t *typedict = fp;
+ ctf_dict_t *refdict = fp;
int child = fp->ctf_flags & LCTF_CHILD;
if (ref == CTF_ERR || ref > CTF_MAX_TYPE)
return (ctf_set_typed_errno (fp, EINVAL));
- if (ref != 0 && ctf_lookup_by_id (&tmp, ref) == NULL)
+ if (ref != 0 && ctf_lookup_by_id (&refdict, ref) == NULL)
return CTF_ERR; /* errno is set for us. */
if ((type = ctf_add_generic (fp, flag, NULL, kind, 0, &dtd)) == CTF_ERR)
addition of this type. The pptrtab is lazily-updated as needed, so is not
touched here. */
- uint32_t type_idx = ctf_type_to_index (fp, type);
- uint32_t ref_idx = ctf_type_to_index (fp, ref);
+ typedict = ctf_get_dict (fp, type);
+ uint32_t type_idx = ctf_type_to_index (typedict, type);
+ uint32_t ref_idx = ctf_type_to_index (refdict, ref);
if (ctf_type_ischild (fp, ref) == child
&& ref_idx < fp->ctf_typemax)
if (fp->ctf_flags & LCTF_NO_STR)
return (ctf_set_errno (fp, ECTF_NOPARENT));
+ if (fp->ctf_flags & LCTF_NO_TYPE)
+ return (ctf_set_errno (fp, ECTF_NOTSERIALIZED));
+
if ((fp->ctf_flags & LCTF_CHILD) && ctf_type_isparent (fp, souid))
{
/* Adding a child type to a parent, even via the child, is prohibited.
if (fp->ctf_flags & LCTF_NO_STR)
return (ctf_set_errno (fp, ECTF_NOPARENT));
+ if (fp->ctf_flags & LCTF_NO_TYPE)
+ return (ctf_set_errno (fp, ECTF_NOTSERIALIZED));
+
if (ctf_lookup_variable_here (fp, name) != CTF_ERR)
return (ctf_set_errno (fp, ECTF_DUPLICATE));
if (fp->ctf_flags & LCTF_NO_STR)
return (ctf_set_errno (fp, ECTF_NOPARENT));
+ if (fp->ctf_flags & LCTF_NO_TYPE)
+ return (ctf_set_errno (fp, ECTF_NOTSERIALIZED));
+
if (ctf_lookup_by_id (&tmp, id) == NULL)
return -1; /* errno is set for us. */
Our OOM handling here is just to not do anything, because this is called deep
enough in the call stack that doing anything useful is painfully difficult:
- the worst consequence if we do OOM is a bit of type duplication anyway. */
+ the worst consequence if we do OOM is a bit of type duplication anyway.
+ The non-imported checks are just paranoia and should never be able to
+ happen, but if they do we don't want a coredump. */
static void
ctf_add_type_mapping (ctf_dict_t *src_fp, ctf_id_t src_type,
if (ctf_dump_header_sizefield (fp, state, "Parent strlen", hp->cth_parent_strlen) < 0)
goto err;
+ if (ctf_dump_header_sizefield (fp, state, "Parent max type", hp->cth_parent_typemax) < 0)
+ goto err;
+
if (ctf_dump_header_sectfield (fp, state, "Label section", hp->cth_lbloff,
hp->cth_objtoff) < 0)
goto err;
{
ctf_list_t dtd_list; /* List forward/back pointers. */
ctf_id_t dtd_type; /* Type identifier for this definition. */
+ ctf_id_t dtd_final_type; /* Final (nonprovisional) id, if nonzero. */
+ ctf_list_t dtd_refs; /* Refs to this DTD's dtd_type: see below. */
ctf_type_t dtd_data; /* Type node, including name. */
size_t dtd_vlen_alloc; /* Total vlen space allocated (vbytes). */
unsigned char *dtd_vlen; /* Variable-length data for this type. */
ctf_list_t ctf_in_flight_dynsyms; /* Dynsyms during accumulation. */
struct ctf_varent *ctf_vars; /* Sorted variable->type mapping. */
unsigned long ctf_nvars; /* Number of variables in ctf_vars. */
- unsigned long ctf_typemax; /* Maximum valid type ID number. */
- unsigned long ctf_stypes; /* Number of static (non-dynamic) types. */
+ uint32_t ctf_typemax; /* Maximum valid type index. */
+ uint32_t ctf_idmax; /* Maximum valid non-provisional type ID. */
+ uint32_t ctf_stypes; /* Number of static (non-dynamic) types. */
+ uint32_t ctf_provtypemax; /* Latest valid provisional type ID.
+ Counts down. Parent only. */
+ uint32_t ctf_nprovtypes; /* Number of provisional types (convenience). */
const ctf_dmodel_t *ctf_dmodel; /* Data model pointer (see above). */
const char *ctf_cuname; /* Compilation unit name (if any). */
char *ctf_dyncuname; /* Dynamically allocated name of CU. */
const char *ctf_parlabel; /* Label in parent dict (if any). */
const char *ctf_parname; /* Basename of parent (if any). */
char *ctf_dynparname; /* Dynamically allocated name of parent. */
- uint32_t ctf_parmax; /* Highest type ID of a parent type. */
uint32_t ctf_refcnt; /* Reference count (for parent links). */
uint32_t ctf_flags; /* Libctf flags (see below). */
uint32_t ctf_max_children; /* Max number of child dicts. */
#define LCTF_LINKING 0x0002 /* CTF link is underway: respect ctf_link_flags. */
#define LCTF_STRICT_NO_DUP_ENUMERATORS 0x0004 /* Duplicate enums prohibited. */
#define LCTF_NO_STR 0x0008 /* No string lookup possible yet. */
-#define LCTF_NO_SERIALIZE 0x0010 /* Serialization of this dict prohibited. */
+#define LCTF_NO_TYPE 0x0010 /* No type additions possible. */
#define LCTF_PRESERIALIZED 0x0020 /* Already serialized all but the strtab. */
extern ctf_dynhash_t *ctf_name_table (ctf_dict_t *, int);
parent/child boundary is unchanged (and much lower). */
case CTF_VERSION_1_UPGRADED_3:
- fp->ctf_parmax = CTF_MAX_PTYPE_V1;
- fp->ctf_flags |= LCTF_NO_SERIALIZE;
+ fp->ctf_header->cth_parent_typemax = CTF_MAX_PTYPE_V1;
break;
/* v2 and v3 are currently just the same as v4 except for new types and
UPTODO: this is really going to change. */
case CTF_VERSION_2: ;
case CTF_VERSION_3: ;
+ fp->ctf_header->cth_parent_typemax = CTF_MAX_PTYPE;
/* FALLTHRU */
}
return 0;
const ctf_type_t *tp;
unsigned long typemax = 0;
+ /* Provisional types always start from the top of the type space and work
+ down. */
+
+ fp->ctf_provtypemax = (uint32_t) -1;
+
/* We determine whether the dict is a child or a parent based on the value of
cth_parname. */
fp->ctf_ptrtab_len = typemax + 1;
fp->ctf_stypes = typemax;
fp->ctf_typemax = typemax;
+ fp->ctf_idmax = typemax;
if (fp->ctf_txlate == NULL || fp->ctf_ptrtab == NULL)
return ENOMEM; /* Memory allocation failed. */
return 0;
}
- ctf_dprintf ("%lu types initialized (other than names)\n", fp->ctf_typemax);
+ ctf_dprintf ("%u types initialized (other than names)\n", fp->ctf_typemax);
return init_static_types_names (fp, cth);
}
fp->ctf_typemax--;
assert (fp->ctf_typemax == typemax);
- ctf_dprintf ("%lu total types processed\n", fp->ctf_typemax);
+ ctf_dprintf ("%u total types processed\n", fp->ctf_typemax);
/* In the third pass, we traverse the enums we spotted earlier and track all
the enumeration constants to aid in future detection of duplicates.
goto bad;
}
- fp->ctf_parmax = CTF_MAX_PTYPE; /* May be reset by upgrade_types. */
memcpy (&fp->ctf_data, ctfsect, sizeof (ctf_sect_t));
if (symsect != NULL)
return (ctf_set_errno (fp, ECTF_WRONGPARENT));
}
+ /* If the child dict expects the parent to have types, make sure it has that
+ number of types. (Provisional types excepted: they go at the top of the
+ type ID space, and will not overlap any child types.) */
+
+ if (pfp->ctf_idmax != fp->ctf_header->cth_parent_typemax)
+ {
+ if (fp->ctf_header->cth_parent_typemax != 0)
+ {
+ ctf_err_warn (fp, 0, ECTF_WRONGPARENT,
+ _("ctf_import: incorrect parent dict: %u types expected, %u found"),
+ fp->ctf_header->cth_parent_typemax, pfp->ctf_idmax);
+ return (ctf_set_errno (fp, ECTF_WRONGPARENT));
+ }
+ else if (fp->ctf_header->cth_parent_typemax == 0)
+ {
+ /* If we are importing into a parent dict, the child dict had better
+ be empty. Set its starting type ID, which need not be zero: the
+ parent can already have types. */
+
+ if (fp->ctf_typemax != 0)
+ {
+ ctf_err_warn (fp, 0, EINVAL,
+ _("ctf_import: dict already has %u types, cannot turn into a new child"),
+ fp->ctf_typemax);
+ return (ctf_set_errno (fp, EINVAL));
+ }
+ fp->ctf_header->cth_parent_typemax = pfp->ctf_typemax;
+ }
+ }
+
/* We might in time be able to lift this restriction, but it is unlikely to be
something anyone would want to do, so let's not bother for now. */
#include <ctf-ref.h>
+/* Functions in this file are roughly divided into two types: sizing functions,
+ which work out the size of various structures in the final serialized
+ representation, and emission functions that actually emit data into them.
+
+ When the sizing functions are called, the buffer into which the output will
+ be serialized has not yet been created: so no functions which create
+ references into that buffer (notably, ctf_*_add_ref) should be called.
+
+ This requirement is to some degree enforced by ctf_assert calls. */
+
/* Symtypetab sections. */
/* Symtypetab emission flags. */
size_t maxfunc;
} emit_symtypetab_state_t;
+/* Emit a ref to a type in this dict. As with string refs, this ref can be
+ updated later on to change the type ID recorded in this location. The ref
+ may not be emitted if the value is already known and cannot change.
+
+ All refs must point within the ctf_serializing_buf. */
+
+static int
+ctf_type_add_ref (ctf_dict_t *fp, uint32_t *ref)
+{
+ ctf_dtdef_t *dtd;
+
+ /* Type in the static portion: cannot change, value already correct. */
+ if (*ref <= fp->ctf_stypes)
+ return 0;
+
+ dtd = ctf_dtd_lookup (fp, *ref);
+
+ if (!ctf_assert (fp, dtd))
+ return 0;
+
+ if (!ctf_assert (fp, fp->ctf_serializing_buf != NULL
+ && (unsigned char *) ref > fp->ctf_serializing_buf
+ && (unsigned char *) ref < fp->ctf_serializing_buf + fp->ctf_serializing_buf_size))
+ return -1;
+
+ /* Simple case: final ID different from what is recorded, but already known.
+ Just set it. */
+ if (dtd->dtd_final_type)
+ *ref = dtd->dtd_final_type;
+ /* Otherwise, create a ref to it so we can set it later. */
+ else if (!ctf_create_ref (fp, &dtd->dtd_refs, ref))
+ return (ctf_set_errno (fp, ENOMEM));
+
+ return 0;
+}
+
+/* Purge all refs to this dict's dynamic types (all refs added by
+ ctf_type_add_ref while serializing this dict). */
+static void
+ctf_type_purge_refs (ctf_dict_t *fp)
+{
+ ctf_dtdef_t *dtd;
+
+ for (dtd = ctf_list_next (&fp->ctf_dtdefs); dtd != NULL;
+ dtd = ctf_list_next (dtd))
+ ctf_purge_ref_list (fp, &dtd->dtd_refs);
+}
+
/* Determine if a symbol is "skippable" and should never appear in the
symtypetab sections. */
will always be set in the flags.
Also figure out if any symbols need to be moved to the variable section, and
- add them (if not already present). */
+ add them (if not already present).
+
+ This is a sizing function, called before the output buffer is
+ constructed. Do not add any refs in this function! */
_libctf_nonnull_ ((1,3,4,5,6,7,8))
static int
elements in it: unindexed output would terminate at symbol OUTMAX and is in
any case no larger than SIZE bytes. Some index elements are expected to be
skipped: see symtypetab_density. The linker-reported set of symbols (if any)
- is found in SYMFP. */
+ is found in SYMFP.
+
+ Note down type ID refs as we go. */
+
static int
emit_symtypetab (ctf_dict_t *fp, ctf_dict_t *symfp, uint32_t *dp,
ctf_link_sym_t **idx, const char **nameidx, uint32_t nidx,
if (!ctf_assert (fp, (((char *) dpp) - (char *) dp) < size))
return -1; /* errno is set for us. */
- *dpp++ = (ctf_id_t) (uintptr_t) type;
+ *dpp = (ctf_id_t) (uintptr_t) type;
+ if (ctf_type_add_ref (fp, dpp++) < 0)
+ return -1; /* errno is set for us. */
/* When emitting unindexed output, all later symbols are pads: stop
early. */
return 0;
}
-/* Delete symbols that have been assigned names from the variable section. Must
- be called from within ctf_serialize, because that is the only place you can
- safely delete variables without messing up ctf_rollback. */
+/* Delete variables with the same name as symbols that have been reported by
+ the linker from the variable section. Must be called from within
+ ctf_serialize, because that is the only place you can safely delete
+ variables without messing up ctf_rollback. */
static int
symtypetab_delete_nonstatics (ctf_dict_t *fp, ctf_dict_t *symfp)
}
/* Figure out the sizes of the symtypetab sections, their indexed state,
- etc. */
+ etc.
+
+ This is a sizing function, called before the output buffer is
+ constructed. Do not add any refs in this function! */
+
static int
ctf_symtypetab_sect_sizes (ctf_dict_t *fp, emit_symtypetab_state_t *s,
ctf_header_t *hdr, size_t *objt_size,
return 0;
}
+/* Emit the symtypetab sections. */
+
static int
ctf_emit_symtypetab_sects (ctf_dict_t *fp, emit_symtypetab_state_t *s,
unsigned char **tptr, size_t objt_size,
/* Type section. */
/* Iterate through the static types and the dynamic type definition list and
- compute the size of the CTF type section. */
+ compute the size of the CTF type section.
+
+ This is a sizing function, called before the output buffer is
+ constructed. Do not add any refs in this function! */
static size_t
ctf_type_sect_size (ctf_dict_t *fp)
}
/* Take a final lap through the dynamic type definition list and copy the
- appropriate type records to the output buffer, noting down the strings as
- we go. */
+ appropriate type records to the output buffer, noting down the strings
+ and type IDs as we go. */
-static void
+static int
ctf_emit_type_sect (ctf_dict_t *fp, unsigned char **tptr)
{
unsigned char *t = *tptr;
ctf_dtdef_t *dtd;
+ ctf_id_t id;
+
+ if (!(fp->ctf_flags & LCTF_CHILD))
+ id = fp->ctf_stypes + 1;
+ else
+ id = fp->ctf_header->cth_parent_typemax + 1;
for (dtd = ctf_list_next (&fp->ctf_dtdefs);
- dtd != NULL; dtd = ctf_list_next (dtd))
+ dtd != NULL; dtd = ctf_list_next (dtd), id++)
{
uint32_t kind = LCTF_INFO_KIND (fp, dtd->dtd_data.ctt_info);
uint32_t vlen = LCTF_INFO_VLEN (fp, dtd->dtd_data.ctt_info);
const char *name;
size_t i;
+ /* Make sure the ID hasn't changed, if already assigned by a previous
+ serialization. */
+
+ if (dtd->dtd_final_type != 0
+ && !ctf_assert (fp, dtd->dtd_final_type == id))
+ return -1; /* errno is set for us. */
+
/* Shrink ctf_type_t-using types from a ctf_type_t to a ctf_stype_t
if possible. */
t += sizeof (uint32_t);
break;
+ case CTF_K_POINTER:
+ case CTF_K_VOLATILE:
+ case CTF_K_CONST:
+ case CTF_K_RESTRICT:
+ case CTF_K_TYPEDEF:
+ if (ctf_type_add_ref (fp, &copied->ctt_type) < 0)
+ return -1; /* errno is set for us. */
+ break;
+
case CTF_K_SLICE:
- memcpy (t, dtd->dtd_vlen, sizeof (struct ctf_slice));
+ {
+ ctf_slice_t *slice = (ctf_slice_t *) t;
+
+ memcpy (t, dtd->dtd_vlen, sizeof (struct ctf_slice));
+
+ if (ctf_type_add_ref (fp, &slice->cts_type) < 0)
+ return -1; /* errno is set for us. */
+ }
+
t += sizeof (struct ctf_slice);
+
break;
case CTF_K_ARRAY:
- memcpy (t, dtd->dtd_vlen, sizeof (struct ctf_array));
+ {
+ ctf_array_t *array = (ctf_array_t *) t;
+
+ memcpy (t, dtd->dtd_vlen, sizeof (struct ctf_array));
+
+ if (ctf_type_add_ref (fp, &array->cta_contents) < 0)
+ return -1; /* errno is set for us. */
+
+ if (ctf_type_add_ref (fp, &array->cta_index) < 0)
+ return -1; /* errno is set for us. */
+ }
t += sizeof (struct ctf_array);
break;
case CTF_K_FUNCTION:
- /* Functions with no args also have no vlen. */
- if (dtd->dtd_vlen)
- memcpy (t, dtd->dtd_vlen, sizeof (uint32_t) * (vlen + (vlen & 1)));
+ {
+ uint32_t *args = (uint32_t *) t;
+
+ /* Functions with no args also have no vlen. */
+
+ if (dtd->dtd_vlen)
+ memcpy (t, dtd->dtd_vlen, sizeof (uint32_t) * (vlen + (vlen & 1)));
+
+ if (ctf_type_add_ref (fp, &copied->ctt_type) < 0)
+ return -1; /* errno is set for us. */
+
+ for (i = 0; i < vlen; i++)
+ {
+ if (ctf_type_add_ref (fp, &args[i]) < 0)
+ return -1; /* errno is set for us. */
+ }
+
t += sizeof (uint32_t) * (vlen + (vlen & 1));
break;
+ }
/* These need to be copied across element by element, depending on
their ctt_size. */
t_vlen[i].ctm_name = dtd_vlen[i].ctlm_name;
t_vlen[i].ctm_type = dtd_vlen[i].ctlm_type;
t_vlen[i].ctm_offset = CTF_LMEM_OFFSET (&dtd_vlen[i]);
+
ctf_str_add_ref (fp, name, &t_vlen[i].ctm_name);
+ if (ctf_type_add_ref (fp, &t_vlen[i].ctm_type) < 0)
+ return -1; /* errno is set for us. */
}
else
{
t_lvlen[i] = dtd_vlen[i];
+
ctf_str_add_ref (fp, name, &t_lvlen[i].ctlm_name);
+ if (ctf_type_add_ref (fp, &t_lvlen[i].ctlm_type) < 0)
+ return -1; /* errno is set for us. */
}
}
}
break;
}
}
+ dtd->dtd_final_type = id;
}
*tptr = t;
+
+ return 0;
}
/* Variable section. */
/* Overall serialization. */
/* Do all aspects of serialization up to strtab writeout and variable table
- sorting. The resulting dict will have the LCTF_PRESERIALIZED flag on and
- must not be modified in any way before serialization. (This is not enforced,
- as this feature is internal-only, employed by the linker machinery.) */
+ sorting, including final type ID assignment. The resulting dict will have
+ the LCTF_PRESERIALIZED flag on and must not be modified in any way before
+ serialization. (This is only lightly enforced, as this feature is internal-
+ only, employed by the linker machinery.) */
int
ctf_preserialize (ctf_dict_t *fp)
{
ctf_header_t hdr, *hdrp;
ctf_dvdef_t *dvd;
+ ctf_dtdef_t *dtd;
int sym_functions = 0;
unsigned char *t;
emit_symtypetab_state_t symstate;
memset (&symstate, 0, sizeof (emit_symtypetab_state_t));
+ ctf_dprintf ("Preserializing dict for %s\n", ctf_cuname (fp));
+
if (fp->ctf_flags & LCTF_NO_STR)
return (ctf_set_errno (fp, ECTF_NOPARENT));
- /* Prohibit reserialization of dicts for which we have dynamic state inherited
- from the upgrade process which we cannot record in the dict. Right now,
- this applies only to CTFv1 dicts, which have a different parent/child type
- offset to v2 and higher, and nowhere to record this in CTFv4. */
+ /* Make sure that any parents have been serialized at least once since the
+ last type was added to them, so we have known final IDs for all their
+ types. */
- if (!fp->ctf_parent)
+ if (fp->ctf_parent)
+ {
+ if (fp->ctf_parent->ctf_nprovtypes > 0)
+ {
+ ctf_dtdef_t *dtd;
+
+ dtd = ctf_list_prev (&fp->ctf_parent->ctf_dtdefs);
+
+ if (dtd && dtd->dtd_final_type == 0)
+ {
+ ctf_set_errno (fp, ECTF_NOTSERIALIZED);
+ ctf_err_warn (fp, 0, 0, _("cannot write out child dict: write out the parent dict first"));
+ return -1; /* errno is set for us. */
+ }
+ }
+
+ /* Prohibit serialization of a dict which has already been serialized and
+ whose parent has had more types added to it since then: this dict would
+ have overlapping types if serialized, since we only pass through
+ newly-added types to renumber them, not already-existing types in the
+ read-in buffer. You can emit such dicts using ctf_link, which can
+ change type IDs arbitrarily, resolving all overlaps. */
+
+ if (fp->ctf_header->cth_stroff - fp->ctf_header->cth_typeoff > 0 &&
+ fp->ctf_header->cth_parent_typemax < fp->ctf_parent->ctf_typemax)
+ {
+ ctf_set_errno (fp, ECTF_NOTSERIALIZED);
+ ctf_err_warn (fp, 0, 0, _("cannot write out already-written child dict: parent has had %u types added"),
+ fp->ctf_parent->ctf_typemax - fp->ctf_header->cth_parent_typemax);
+ return -1; /* errno is set for us. */
+ }
+
+ fp->ctf_header->cth_parent_typemax = fp->ctf_parent->ctf_typemax;
+ }
+ else
{
/* Prohibit serialization of a parent dict which has already been
serialized, has children, and has had strings added since the last
hdr.cth_stroff = hdr.cth_typeoff + type_size;
hdr.cth_strlen = 0;
hdr.cth_parent_strlen = 0;
+ if (fp->ctf_parent)
+ hdr.cth_parent_typemax = fp->ctf_parent->ctf_typemax;
buf_size = sizeof (ctf_header_t) + hdr.cth_stroff + hdr.cth_strlen;
if ((buf = malloc (buf_size)) == NULL)
return (ctf_set_errno (fp, EAGAIN));
+ fp->ctf_serializing_buf = buf;
+ fp->ctf_serializing_buf_size = buf_size;
+
memcpy (buf, &hdr, sizeof (ctf_header_t));
t = (unsigned char *) buf + sizeof (ctf_header_t) + hdr.cth_objtoff;
if (ctf_emit_symtypetab_sects (fp, &symstate, &t, objt_size, func_size,
objtidx_size, funcidx_size) < 0)
- {
- free (buf);
- return -1; /* errno is set for us. */
- }
+ goto err;
assert (t == (unsigned char *) buf + sizeof (ctf_header_t) + hdr.cth_varoff);
ctf_str_add_ref (fp, dvd->dvd_name, &var->ctv_name);
var->ctv_type = (uint32_t) dvd->dvd_type;
+
+ if (ctf_type_add_ref (fp, &var->ctv_type) < 0)
+ goto err;
}
assert (i == nvars);
memcpy (t, fp->ctf_buf + fp->ctf_header->cth_typeoff,
fp->ctf_header->cth_stroff - fp->ctf_header->cth_typeoff);
t += fp->ctf_header->cth_stroff - fp->ctf_header->cth_typeoff;
- ctf_emit_type_sect (fp, &t);
+
+ if (ctf_emit_type_sect (fp, &t) < 0)
+ goto err;
assert (t == (unsigned char *) buf + sizeof (ctf_header_t) + hdr.cth_stroff);
- fp->ctf_serializing_buf = buf;
- fp->ctf_serializing_buf_size = buf_size;
+ /* All types laid out: update all refs to types to cite the final IDs. */
- /* Prohibit additions and the like from this point on. */
- fp->ctf_flags |= LCTF_NO_STR;
+ for (dtd = ctf_list_next (&fp->ctf_dtdefs);
+ dtd != NULL; dtd = ctf_list_next (dtd))
+ {
+ if (!ctf_assert (fp, dtd->dtd_type != 0 && dtd->dtd_final_type != 0))
+ goto err;
+
+ ctf_update_refs (&dtd->dtd_refs, dtd->dtd_final_type);
+ }
+
+ ctf_type_purge_refs (fp);
+
+ /* Prohibit type and string additions from this point on. */
+
+ fp->ctf_flags |= LCTF_NO_STR | LCTF_NO_TYPE;
return 0;
+
+ err:
+ fp->ctf_serializing_buf = NULL;
+ fp->ctf_serializing_buf_size = 0;
+
+ free (buf);
+ ctf_str_purge_refs (fp);
+ ctf_type_purge_refs (fp);
+
+ return -1; /* errno is set for us. */
}
/* Undo preserialization (called on error). */
void
ctf_depreserialize (ctf_dict_t *fp)
{
- fp->ctf_flags &= ~LCTF_NO_STR;
+ ctf_str_purge_refs (fp);
+ ctf_type_purge_refs (fp);
+
free (fp->ctf_serializing_buf);
fp->ctf_serializing_buf = NULL;
fp->ctf_serializing_vars = NULL;
fp->ctf_serializing_buf_size = 0;
fp->ctf_serializing_nvars = 0;
+
+ fp->ctf_flags &= ~(LCTF_NO_STR | LCTF_NO_TYPE);
}
-/* Emit a new CTF dict which is a serialized copy of this one: also reify
- the string table and update all offsets in the current dict suitably.
- (This simplifies ctf-string.c a little, at the cost of storing a second
- copy of the strtab if this dict was originally read in via ctf_open.)
+/* Emit a new CTF dict which is a serialized copy of this one: also reify the
+ string table and update all offsets in the newly-serialized dict suitably.
+ (This simplifies ctf-string.c a little, at the cost of storing a second copy
+ of the strtab during serialization.)
- Other aspects of the existing dict are unchanged, although some
- static entries may be duplicated in the dynamic state (which should
- have no effect on visible operation). */
+ Other aspects of the existing dict are unchanged, although some static
+ entries may be duplicated in the dynamic state (which should have no effect
+ on visible operation). */
static unsigned char *
ctf_serialize (ctf_dict_t *fp, size_t *bufsiz)
hdrp = (ctf_header_t *) fp->ctf_serializing_buf;
+ ctf_dprintf ("Writing strtab for %s\n", ctf_cuname (fp));
strtab = ctf_str_write_strtab (fp);
if (strtab == NULL)
goto err;
- /* Now the string table is constructed, we can sort the buffer of
- ctf_varent_t's. */
+ /* Now the string table is constructed and all the refs updated, we can sort
+ the buffer of ctf_varent_t's. */
+
ctf_sort_var_arg_cb_t sort_var_arg = { fp, (ctf_strs_t *) strtab };
ctf_qsort_r (fp->ctf_serializing_vars, fp->ctf_serializing_nvars,
sizeof (ctf_varent_t), ctf_sort_var, &sort_var_arg);
fp->ctf_serializing_vars = NULL;
fp->ctf_serializing_buf_size = 0;
fp->ctf_serializing_nvars = 0;
+ fp->ctf_flags &= ~LCTF_NO_TYPE;
return buf;
#include <assert.h>
#include <string.h>
-/* Determine whether a type is a parent or a child. */
+/* Determine whether a type is a parent or a child. Bad IDs are not
+ diagnosed! */
int
ctf_type_isparent (const ctf_dict_t *fp, ctf_id_t id)
{
- return (id <= fp->ctf_parmax);
+ /* All types visible in the parent are parent types, by definition. */
+
+ if (!(fp->ctf_flags & LCTF_CHILD))
+ return 1;
+
+ /* Not imported: no provisional types are possible because no types can
+ have been added. Simple range check. */
+
+ if (!fp->ctf_parent)
+ return (fp->ctf_header->cth_parent_typemax >= id);
+
+ /* Types in the parent's idmax range (which encompasses its stypes range) are
+ in the parent. */
+
+ if (id <= fp->ctf_parent->ctf_idmax)
+ return 1;
+
+ /* Types in the provisional ID range are in the parent: otherwise, they are in
+ the child. */
+
+ if (id >= fp->ctf_parent->ctf_provtypemax)
+ return (ctf_dynhash_lookup (fp->ctf_dthash,
+ (void *) (uintptr_t) id) == NULL);
+
+ /* Child type. */
+ return 0;
}
int
}
/* Get the index in the internal type array (or otherwise) for a given type ID.
- Only ever called on the right dictionary for the type and can fail otherwise.
+ Only ever called on the right dictionary for the type, and can fail otherwise.
If called on an invalid type, may return an index that does not correspond to
- any type (such as -1). */
+ any type (such as -1), but will not return an index that does correspond to a
+ type. */
+
+static uint32_t
+ctf_type_to_index_internal (const ctf_dict_t *fp, ctf_id_t type)
+{
+ uint32_t idx = type;
+
+ assert (((fp->ctf_flags & LCTF_CHILD) && (type > fp->ctf_header->cth_parent_typemax)) ||
+ (!(fp->ctf_flags & LCTF_CHILD)));
+
+ if (fp->ctf_flags & LCTF_CHILD)
+ {
+ /* Non-dynamic type in parent: no index permitted. */
+
+ assert (type > fp->ctf_header->cth_parent_typemax);
+
+ idx -= fp->ctf_header->cth_parent_typemax;
+ }
+
+ if (idx <= fp->ctf_stypes)
+ return idx;
+
+ /* Dynamic types. In children this is easy. */
+
+ if (fp->ctf_flags & LCTF_CHILD)
+ return idx;
+
+ /* For parents, there are three ranges of types: below stypes (static), above
+ stypes and below typemax - nprovtypes (dynamic, non-provisional, added
+ before any children were imported, type ID derived identically to stypes),
+ and above that (provisional, running backwards from the top of the ID
+ space). We have already handled the first. Once we start inserting
+ provisional types, no further nonprovisional types can be inserted:
+ typemax, provtypemax and nprovtypes will rise in concert. */
+
+ if (idx <= (fp->ctf_typemax - fp->ctf_nprovtypes))
+ return type;
+ else /* Provisional type. */
+ return fp->ctf_typemax - (type - fp->ctf_provtypemax);
+}
+
+/* Verification of type_to_index -> index_to_type roundtripping.
+ Doubles the cost of this core operation, so done under
+ hash debugging only. */
uint32_t
ctf_type_to_index (const ctf_dict_t *fp, ctf_id_t type)
{
- return type & fp->ctf_parmax;
+ uint32_t idx = ctf_type_to_index_internal (fp, type);
+
+#ifdef ENABLE_LIBCTF_HASH_DEBUGGING
+ assert (ctf_index_to_type (fp, idx) == type);
+#endif
+ return idx;
}
/* The inverse of ctf_type_to_index. */
ctf_id_t
ctf_index_to_type (const ctf_dict_t *fp, uint32_t idx)
{
- return (fp->ctf_flags & LCTF_CHILD) ? ((idx) | (fp->ctf_parmax+1)) : idx;
+ if (fp->ctf_flags & LCTF_CHILD)
+ return idx + fp->ctf_header->cth_parent_typemax;
+
+ if (idx <= (fp->ctf_typemax - fp->ctf_nprovtypes))
+ return idx;
+ else /* Provisional type. */
+ return fp->ctf_provtypemax + (fp->ctf_typemax - idx);
}
/* Expand a structure element into the passed-in ctf_lmember_t. */
We check specifically a subset of known-buggy functions.
Functions that require a buggy linker to expose, or that only fail on
- assertion-failure-incurring corrupted dicts, are not tested. */
+ assertion-failure-incurring corrupted dicts, are not tested.
+ This is very much a whitebox test: we do things no legitimate client should
+ do to be sure that we can get invalid type ID errors that you are normally
+ blocked from getting. */
+
+#include "config.h"
#include <ctf-api.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
+#include "ctf-impl.h"
static const char *desc;
return;
if (ctf_errno (parent) == expected)
- fprintf (stderr, "%s: error propagation failure: error \"%s\" not seen on child, "
- "but instead on parent\n", desc, ctf_errmsg (ctf_errno (parent)));
+ {
+ fprintf (stderr, "%s: error propagation failure: error \"%s\" not seen on child, "
+ "but instead on parent\n", desc, ctf_errmsg (ctf_errno (parent)));
+ exit (1);
+ }
else
- fprintf (stderr, "%s: expected error is entirely lost: "
- "\"%s\" seen on parent, \"%s\" on child\n", desc,
- ctf_errmsg (ctf_errno (parent)),
- ctf_errmsg (ctf_errno (child)));
+ {
+ fprintf (stderr, "%s: expected error is entirely lost: "
+ "\"%s\" seen on parent, \"%s\" on child\n", desc,
+ ctf_errmsg (ctf_errno (parent)),
+ ctf_errmsg (ctf_errno (child)));
+ exit (1);
+ }
}
static void
ctf_dict_t *parent;
ctf_dict_t *child;
ctf_dict_t *wrong;
+ ctf_dict_t *blank;
+ ctf_dict_t tmp;
ctf_id_t void_id;
ctf_id_t wrong_id;
ctf_id_t base;
if ((parent = ctf_create (&err)) == NULL
|| (child = ctf_create (&err)) == NULL
- || (wrong = ctf_create (&err)) == NULL)
+ || (wrong = ctf_create (&err)) == NULL
+ || (blank = ctf_create (&err)) == NULL)
{
fprintf (stderr, "Cannot create dicts: %s\n", ctf_errmsg (err));
return 1;
return 1;
}
- /* Populate two dicts, one with the same types in a different order. This
- passes all ctf_import checks (type and strtab count), but will still
- induce errors due to type mismatches with the child. In particular, base
- in the right parent is a non-integral type (a pointer) in the wrong one,
- and "void" in the parent is an unknown type in the wrong one. */
+ if ((ctf_import (blank, wrong)) < 0)
+ {
+ fprintf (stderr, "cannot import wrong-types dict: %s\n", ctf_errmsg (ctf_errno (blank)));
+ return 1;
+ }
+
+ /* Populate two dicts, one with the same types in a different order: both have
+ children, to ensure that all types in both dicts are provisional and have
+ the same IDs. This passes all ctf_import checks (type and strtab count),
+ but will still induce errors due to type mismatches with the child. In
+ particular, base in the right parent is a non-integral type (a pointer) in
+ the wrong one, and "void" in the parent is an unknown type in the wrong
+ one. */
if ((ctf_add_unknown (parent, CTF_ADD_ROOT, "spacer")) /* 1 */
== CTF_ERR)
== CTF_ERR)
goto parent_err;
- if ((ctf_add_unknown (parent, CTF_ADD_ROOT, "spacer")) /* 3 */
+ if ((ptr = ctf_add_pointer (wrong, CTF_ADD_ROOT, wrong_id)) /* 3 */
== CTF_ERR)
goto parent_err;
-
- if ((ptr = ctf_add_pointer (wrong, CTF_ADD_ROOT, wrong_id)) /* 4 */
+
+ if ((ctf_add_unknown (wrong, CTF_ADD_ROOT, "spacer")) /* 4 */
== CTF_ERR)
goto parent_err;
-
+
if ((ctf_add_unknown (wrong, CTF_ADD_ROOT, "spacer2")) /* 5 */
== CTF_ERR)
goto parent_err;
no_prop_err ();
check_prop_err (child, parent, ECTF_NOTFUNC);
- /* Write out and reopen to get a child with no parent. */
- if ((ctf_import (child, wrong)) < 0)
- {
- fprintf (stderr, "cannot reimport: %s\n", ctf_errmsg (ctf_errno (child)));
- return 1;
- }
+ /* Swap the insides of "parent" and "wrong" so we get a parent dict with
+ different types than it had. */
+
+ memcpy (&tmp, parent, sizeof (ctf_dict_t));
+ memcpy (parent, wrong, sizeof (ctf_dict_t));
+ memcpy (wrong, &tmp, sizeof (ctf_dict_t));
/* This is testing ctf_type_resolve_unsliced(), which is called by the enum
functions (which are not themselves buggy). This type isn't an enum, but
- that's OK: we're after an error, after all, and the type we're slicing is
- not visible any longer, so nothing can tell it's not an enum. */
+ that's OK: we're after an error, after all. */
desc = "child slice resolution";
if ((ctf_enum_value (child, slice, "foo", NULL)) != CTF_ERR)
no_prop_err ();
- check_prop_err (child, wrong, ECTF_NONREPRESENTABLE);
+ check_prop_err (child, parent, ECTF_NONREPRESENTABLE);
desc = "child slice encoding lookup";
if ((ctf_type_encoding (child, slice, &foo)) != CTF_ERR)
no_prop_err ();
- check_prop_err (child, wrong, ECTF_BADID);
+ check_prop_err (child, parent, ECTF_NONREPRESENTABLE);
desc = "func info lookup of nonrepresentable function";
if ((ctf_func_type_info (child, base, &fi)) != CTF_ERR)
no_prop_err ();
- check_prop_err (child, wrong, ECTF_NONREPRESENTABLE);
+ check_prop_err (child, parent, ECTF_NONREPRESENTABLE);
desc = "func args lookup of nonrepresentable function";
if ((ctf_func_type_args (child, base, 0, &bar)) != CTF_ERR)
no_prop_err ();
- check_prop_err (child, wrong, ECTF_NONREPRESENTABLE);
+ check_prop_err (child, parent, ECTF_NONREPRESENTABLE);
desc = "child slice addition";
if ((slice = ctf_add_slice (child, CTF_ADD_ROOT, base, &foo)) != CTF_ERR)
no_prop_err ();
- check_prop_err (child, wrong, ECTF_NOTINTFP);
+ check_prop_err (child, parent, ECTF_NONREPRESENTABLE);
desc = "variable lookup";
if (ctf_lookup_variable (child, "base") != CTF_ERR)
no_prop_err ();
- check_prop_err (child, wrong, ECTF_NOTYPEDAT);
+ check_prop_err (child, parent, ECTF_NOTYPEDAT);
ctf_dict_close (child);
ctf_dict_close (parent);
--- /dev/null
+/* Test parent / child ID assignment. */
+
+#include <ctf-api.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+int
+test (int empty_parent, int unserialized_parent)
+{
+ ctf_dict_t *parent;
+ ctf_dict_t *child;
+ ctf_id_t pint = 0, pprovint = 0, pptr = 0, parray = 0, pfunction = 0;
+ ctf_id_t ctype, ctype2, cslice, ctypedef, cfunction, cself;
+ ctf_id_t foo;
+ ctf_encoding_t encoding = { CTF_INT_SIGNED, 0, (sizeof (char) * 8) - 1 };
+ ctf_encoding_t slice_encoding = { CTF_INT_SIGNED, 1, (sizeof (char) * 8) - 1 };
+ ctf_encoding_t out;
+ unsigned char *pbuf = NULL, *cbuf = NULL, *pbuf2 = NULL, *cbuf2 = NULL;
+ size_t psize, csize;
+ int err;
+ ctf_id_t first_child_type = 1;
+ ctf_membinfo_t memb;
+ ctf_arinfo_t ar;
+ ctf_funcinfo_t func;
+ ctf_funcinfo_t pfunc, cfunc;
+ ctf_id_t args[2], pargs[2], cargs[2];
+
+ printf ("Testing with %s, %s parent\n", empty_parent ? "empty" : "nonempty",
+ unserialized_parent ? "unserialized" : "serialized");
+
+ if ((parent = ctf_create (&err)) == NULL)
+ goto create_err;
+
+ if ((child = ctf_create (&err)) == NULL)
+ goto create_err;
+
+ /* Try some tests with a parent that already has some types in it (thus, a
+ nonempty stypes range). */
+ if (!empty_parent)
+ {
+ if ((pint = ctf_add_integer (parent, CTF_ADD_ROOT, "int", &encoding)) == CTF_ERR)
+ goto parent_add_err;
+ first_child_type++;
+ }
+
+ if (!unserialized_parent)
+ {
+ if ((pbuf = ctf_write_mem (parent, &psize, -1)) == NULL)
+ goto parent_write_err;
+
+ ctf_dict_close (parent);
+
+ if ((parent = ctf_simple_open ((char *) pbuf, psize, NULL, 0, 0, NULL, 0, &err)) == NULL)
+ goto parent_open_err;
+
+ if (!empty_parent)
+ {
+ /* Look up int again: its ID will have changed. */
+ if ((pint = ctf_lookup_by_name (parent, "int")) == CTF_ERR)
+ {
+ fprintf (stderr, "Cannot look up int in parent: %s\n", ctf_errmsg (ctf_errno (parent)));
+ exit (1);
+ }
+ }
+ }
+
+ if (ctf_import (child, parent) < 0)
+ goto import_err;
+
+ /* Add some types that should end up with provisional IDs and be reassigned on
+ writeout, with all references to them in all dicts following along. */
+
+ if (!empty_parent)
+ {
+ if ((pprovint = ctf_add_integer (parent, CTF_ADD_ROOT, "provint", &encoding)) == CTF_ERR)
+ goto parent_add_err;
+ first_child_type++;
+
+ if ((pptr = ctf_add_pointer (parent, CTF_ADD_ROOT, pint)) == CTF_ERR)
+ goto parent_add_err;
+ first_child_type++;
+
+ ar.ctr_contents = pint;
+ ar.ctr_index = pint;
+ ar.ctr_nelems = 666;
+
+ if ((parray = ctf_add_array (parent, CTF_ADD_ROOT, &ar)) == CTF_ERR)
+ goto parent_add_err;
+ first_child_type++;
+
+ func.ctc_argc = 2;
+ func.ctc_flags = 0;
+ func.ctc_return = pprovint;
+ args[0] = pptr;
+ args[1] = parray;
+
+ if ((pfunction = ctf_add_function (parent, CTF_ADD_ROOT, &func, args)) == CTF_ERR)
+ goto parent_add_err;
+ first_child_type++;
+ }
+
+ if ((ctype = ctf_add_enum (child, CTF_ADD_ROOT, "wombat")) == CTF_ERR)
+ goto child_add_err;
+
+ if ((ctype2 = ctf_add_struct (child, CTF_ADD_ROOT, "foo")) == CTF_ERR)
+ goto child_add_err;
+
+ if (ctf_add_member (child, ctype2, "wombat_member", ctype) < 0)
+ goto child_add_memb_err;
+
+ if (!empty_parent)
+ {
+ /* pint is still valid: nonprovisional type. */
+
+ if (ctf_add_member (child, ctype2, "a", pint) < 0)
+ goto child_add_memb_err;
+
+ /* (pptr is provisional.) */
+
+ if (ctf_add_member (child, ctype2, "b", pptr) < 0)
+ goto child_add_memb_err;
+
+ if ((cself = ctf_add_pointer (child, CTF_ADD_ROOT, ctype2)) == CTF_ERR)
+ goto child_add_err;
+
+ if (ctf_add_member (child, ctype2, "self", cself) < 0)
+ goto child_add_memb_err;
+
+ /* Make sure types are distinct. */
+
+ if (pint == pptr || pint == ctype || pint == ctype2 ||
+ pptr == ctype || pptr == ctype2 || ctype == ctype2)
+ goto overlapping_err;
+
+ if (pint > pptr || ctype > pptr || ctype2 > pptr)
+ goto provisional_too_low_err;
+
+ /* Add an instance of every other serialized type-referencing type,
+ referencing a type provisional in the parent. */
+
+ if (ctf_add_typedef (child, CTF_ADD_ROOT, "td", parray) == CTF_ERR)
+ goto child_add_err;
+
+ if ((cslice = ctf_add_slice (child, CTF_ADD_ROOT, pprovint, &slice_encoding)) == CTF_ERR)
+ goto child_add_err;
+
+ if (ctf_add_member (child, ctype2, "c", cslice) < 0)
+ goto child_add_memb_err;
+
+ if (ctf_add_member (child, ctype2, "pfunc", pfunction) < 0)
+ goto child_add_memb_err;
+
+ func.ctc_argc = 2;
+ func.ctc_flags = 0;
+ func.ctc_return = pprovint;
+ args[0] = pptr;
+ args[1] = parray;
+
+ if ((cfunction = ctf_add_function (parent, CTF_ADD_ROOT, &func, args)) == CTF_ERR)
+ goto child_add_err;
+ first_child_type++;
+
+ if (ctf_add_member (child, ctype2, "cfunc", pfunction) < 0)
+ goto child_add_memb_err;
+ }
+
+ /* Make sure we can't write out the child before the parent. */
+ if (!empty_parent)
+ {
+ if ((cbuf = ctf_write_mem (child, &csize, -1)) != NULL)
+ {
+ fprintf (stderr, "writing child before parent works unexpectedly\n");
+ exit (1);
+ }
+ if (ctf_errno (child) != ECTF_NOTSERIALIZED)
+ {
+ fprintf (stderr, "writing child before parent: unexpected error %s\n",
+ ctf_errmsg (ctf_errno (child)));
+ exit (1);
+ }
+ }
+
+ /* Write out the parent, then the child: read both back in, reimport them,
+ do some lookups, make sure they work. Make sure we can't write the parent
+ out if it was already serialized, unless it was empty when that happened */
+
+ if (!empty_parent && !unserialized_parent)
+ {
+ if ((pbuf2 = ctf_write_mem (parent, &psize, -1)) != NULL)
+ {
+ fprintf (stderr, "Writing out modified already-serialized parent succeeded unexpectedly\n");
+ exit (1);
+ }
+
+ /* Nothing more to test in this case. */
+ ctf_dict_close (child);
+ ctf_dict_close (parent);
+ free (pbuf);
+ free (pbuf2);
+ return 0;
+ }
+
+ if ((pbuf2 = ctf_write_mem (parent, &psize, -1)) == NULL)
+ goto parent_write_err;
+
+ if ((cbuf2 = ctf_write_mem (child, &csize, -1)) == NULL)
+ goto child_write_err;
+
+ ctf_dict_close (child);
+ ctf_dict_close (parent);
+ free (pbuf);
+ free (cbuf);
+
+ if ((parent = ctf_simple_open ((char *) pbuf2, psize, NULL, 0, 0, NULL, 0, &err)) == NULL)
+ goto parent_open_err;
+
+ if ((child = ctf_simple_open ((char *) cbuf2, csize, NULL, 0, 0, NULL, 0, &err)) == NULL)
+ goto child_open_err;
+
+ if (ctf_import (child, parent) < 0)
+ goto import_err;
+
+ if (!empty_parent)
+ {
+ ctf_id_t foo2;
+
+ if ((foo = ctf_lookup_by_name (parent, "int")) == CTF_ERR)
+ {
+ fprintf (stderr, "Cannot look up int in parent: %s\n", ctf_errmsg (ctf_errno (parent)));
+ exit (1);
+ }
+
+ if ((foo2 = ctf_lookup_by_name (child, "int")) == CTF_ERR)
+ {
+ fprintf (stderr, "Cannot look up int in child: %s\n", ctf_errmsg (ctf_errno (parent)));
+ exit (1);
+ }
+
+ if (foo != foo2)
+ {
+ fprintf (stderr, "int in parent and child have different IDs: %lx versus %lx\n", foo, foo2);
+ exit (1);
+ }
+
+ /* Verify that ctf_type_pointer still works: it saw changes as part of
+ the CTFv4 type ID rework. In particular it works on parent types now
+ too. */
+
+ if ((foo2 = ctf_type_pointer (parent, foo)) == CTF_ERR)
+ {
+ fprintf (stderr, "pointer lookup in parent failed: %s\n", ctf_errmsg (ctf_errno (parent)));
+ exit (1);
+ }
+ if (ctf_type_kind (parent, foo2) != CTF_K_POINTER)
+ {
+ fprintf (stderr, "pointer lookup in parent yields non-pointer: %i\n", ctf_type_kind (parent, foo2));
+ exit (1);
+ }
+ if ((foo2 = ctf_type_pointer (child, foo)) == CTF_ERR)
+ {
+ fprintf (stderr, "pointer lookup in child failed: %s\n", ctf_errmsg (ctf_errno (child)));
+ exit (1);
+ }
+ if (ctf_type_kind (child, foo2) != CTF_K_POINTER)
+ {
+ fprintf (stderr, "pointer lookup in child yields non-pointer: %i\n", ctf_type_kind (child, foo2));
+ exit (1);
+ }
+ }
+
+ if ((ctype = ctf_lookup_by_name (child, "enum wombat")) == CTF_ERR)
+ {
+ fprintf (stderr, "Cannot look up enum wombat in child: %s\n", ctf_errmsg (ctf_errno (child)));
+ exit (1);
+ }
+
+ /* Check consecutiveness. */
+
+ if (ctype != first_child_type)
+ {
+ fprintf (stderr, "expected first child type to be ID %lx but is %lx\n", first_child_type, ctype);
+ exit (1);
+ }
+
+ if ((ctype2 = ctf_lookup_by_name (child, "struct foo")) == CTF_ERR)
+ {
+ fprintf (stderr, "Cannot look up struct foo in child: %s\n", ctf_errmsg (ctf_errno (child)));
+ exit (1);
+ }
+
+ /* Check consecutiveness. */
+
+ if (ctype2 != ctype + 1)
+ {
+ fprintf (stderr, "expected second child type to be ID %lx but is %lx\n", ctype + 1, ctype2);
+ exit (1);
+ }
+
+ if (!empty_parent)
+ {
+ if ((ctypedef = ctf_lookup_by_name (child, "td")) == CTF_ERR)
+ goto typedef_td_err;
+
+ if ((parray = ctf_type_reference (child, ctypedef)) == CTF_ERR)
+ goto typedef_err;
+
+ if (ctf_array_info (child, parray, &ar) < 0)
+ goto array_err;
+
+ char *name;
+ if ((name = ctf_type_aname (child, ar.ctr_contents)) == NULL)
+ goto type_name_err;
+
+ if (strcmp (name, "int") != 0)
+ {
+ fprintf (stderr, "expected array member to be int, but was %s\n", name);
+ exit (1);
+ }
+ free (name);
+
+ if (ar.ctr_contents != ar.ctr_index)
+ {
+ fprintf (stderr, "array contents and index are not the same type: %lx versus %lx",
+ ar.ctr_contents, ar.ctr_index);
+ exit (1);
+ }
+ }
+
+ /* Check membership links. */
+
+ if (ctf_member_info (child, ctype2, "wombat_member", &memb) < 0)
+ goto memb_err;
+
+ if (memb.ctm_type != ctype)
+ {
+ fprintf (stderr, "child enum member lookup yielded %lx, not %lx\n", memb.ctm_type, ctype);
+ exit (1);
+ }
+
+ if (!empty_parent)
+ {
+ if (ctf_member_info (child, ctype2, "a", &memb) < 0)
+ goto memb_err;
+
+ if (ctf_type_kind (child, memb.ctm_type) != CTF_K_INTEGER)
+ {
+ fprintf (stderr, "parent member integer lookup yielded %lx, not %x\n", memb.ctm_type, CTF_K_INTEGER);
+ exit (1);
+ }
+
+ if (ctf_member_info (child, ctype2, "b", &memb) < 0)
+ goto memb_err;
+
+ if (ctf_type_kind (child, memb.ctm_type) != CTF_K_POINTER)
+ goto memb_ptr_err;
+
+ if ((foo = ctf_type_reference (child, memb.ctm_type)) == CTF_ERR)
+ goto memb_err;
+
+ if ((ctf_type_kind (child, foo)) != CTF_K_INTEGER)
+ {
+ fprintf (stderr, "parent member pointer final lookup yielded kind %x, not %x\n", ctf_type_kind (child, foo), CTF_K_INTEGER);
+ exit (1);
+ }
+
+ if (ctf_member_info (child, ctype2, "c", &memb) < 0)
+ goto memb_err;
+
+ if (ctf_type_encoding (child, memb.ctm_type, &out) < 0)
+ goto encoding_err;
+
+ if (memcmp (&out, &slice_encoding, sizeof (out)) != 0)
+ {
+ fprintf (stderr, "slice encoding differs\n");
+ exit (1);
+ }
+
+ if (ctf_type_kind (child, memb.ctm_type) != CTF_K_INTEGER)
+ {
+ fprintf (stderr, "parent member slice final lookup yielded kind %x, not %x\n", ctf_type_kind (child, memb.ctm_type), CTF_K_INTEGER);
+ exit (1);
+ }
+
+ if (ctf_member_info (child, ctype2, "pfunc", &memb) < 0)
+ goto memb_err;
+
+ if (ctf_type_kind (child, memb.ctm_type) != CTF_K_FUNCTION)
+ goto func_err;
+ pfunction = memb.ctm_type;
+
+ if (ctf_member_info (child, ctype2, "cfunc", &memb) < 0)
+ goto memb_err;
+
+ if (ctf_type_kind (child, memb.ctm_type) != CTF_K_FUNCTION)
+ goto func_err;
+ cfunction = memb.ctm_type;
+
+ if (ctf_func_type_info (child, pfunction, &pfunc) < 0 ||
+ ctf_func_type_info (child, cfunction, &cfunc) < 0)
+ {
+ fprintf (stderr, "func info lookup failed: %s\n", ctf_errmsg (ctf_errno (child)));
+ exit (1);
+ }
+
+ if (memcmp (&pfunc, &cfunc, sizeof (pfunc)) != 0)
+ {
+ fprintf (stderr, "parent and child funcs differ\n");
+ exit (1);
+ }
+
+ if (ctf_type_kind (child, pfunc.ctc_return) != CTF_K_INTEGER)
+ {
+ fprintf (stderr, "func return type lookup yielded kind %x, not %x\n", ctf_type_kind (child, pfunc.ctc_return), CTF_K_INTEGER);
+ exit (1);
+ }
+
+ /* This isn't a type ID, so we're not really expecting problems here, but if
+ there are problems, rather an error message than a buffer overrun. */
+ if (pfunc.ctc_argc != 2)
+ {
+ fprintf (stderr, "func has %i args, not 2\n", pfunc.ctc_argc);
+ exit (1);
+ }
+
+ if (ctf_func_type_args (child, pfunction, pfunc.ctc_argc, pargs) < 0 ||
+ ctf_func_type_args (child, cfunction, cfunc.ctc_argc, cargs) < 0)
+ {
+ fprintf (stderr, "func arg lookup failed: %s\n", ctf_errmsg (ctf_errno (child)));
+ exit (1);
+ }
+
+ if (memcmp (pargs, cargs, sizeof (pargs)) != 0)
+ {
+ fprintf (stderr, "parent and child func args differ\n");
+ exit (1);
+ }
+ if (ctf_type_kind (child, pargs[0]) != CTF_K_POINTER ||
+ ctf_type_kind (child, pargs[1]) != CTF_K_ARRAY)
+ {
+ fprintf (stderr, "func args lookup not as expected\n");
+ exit (1);
+ }
+
+ if (ctf_member_info (child, ctype2, "self", &memb) < 0)
+ goto memb_err;
+
+ if (ctf_type_kind (child, memb.ctm_type) != CTF_K_POINTER)
+ goto memb_ptr_err;
+
+ if (ctf_type_reference (child, memb.ctm_type) != ctype2)
+ {
+ fprintf (stderr, "structure self-ref yields type %lx, not %lx as expected\n",
+ ctf_type_reference (child, memb.ctm_type), ctype2);
+ exit (1);
+ }
+ }
+
+ ctf_dict_close (child);
+ ctf_dict_close (parent);
+ free (cbuf2);
+ free (pbuf2);
+
+ return 0;
+
+ create_err:
+ fprintf (stderr, "Cannot create: %s\n", ctf_errmsg (err));
+ exit (1);
+
+ parent_write_err:
+ fprintf (stderr, "Cannot serialize parent: %s\n", ctf_errmsg (ctf_errno (parent)));
+ exit (1);
+
+ child_write_err:
+ fprintf (stderr, "Cannot serialize child: %s\n", ctf_errmsg (ctf_errno (child)));
+ exit (1);
+
+ parent_open_err:
+ fprintf (stderr, "Cannot open parent: %s\n", ctf_errmsg (err));
+ exit (1);
+
+ child_open_err:
+ fprintf (stderr, "Cannot open chile: %s\n", ctf_errmsg (err));
+ exit (1);
+
+ import_err:
+ fprintf (stderr, "Cannot import: %s\n", ctf_errmsg (ctf_errno (child)));
+ exit (1);
+
+ parent_add_err:
+ fprintf (stderr, "Cannot add parent types: %s\n", ctf_errmsg (ctf_errno (parent)));
+ exit (1);
+
+ child_add_err:
+ fprintf (stderr, "Cannot add child types: %s\n", ctf_errmsg (ctf_errno (child)));
+ exit (1);
+
+ child_add_memb_err:
+ fprintf (stderr, "Cannot add child members: %s (%i)\n", ctf_errmsg (ctf_errno (child)), ctf_errno (child));
+ exit (1);
+
+ overlapping_err:
+ fprintf (stderr, "type IDs overlap\n");
+ exit (1);
+
+ provisional_too_low_err:
+ fprintf (stderr, "provisional ID %lx is too low\n", pptr);
+ exit (1);
+
+ memb_err:
+ fprintf (stderr, "cannot look up members: %s\n", ctf_errmsg (ctf_errno (child)));
+ exit (1);
+
+ memb_ptr_err:
+ fprintf (stderr, "parent member pointer lookup yielded %lx, not %x\n", memb.ctm_type, CTF_K_POINTER);
+ exit (1);
+
+ typedef_td_err:
+ fprintf (stderr, "Cannot look up typedef td in child: %s\n", ctf_errmsg (ctf_errno (child)));
+ exit (1);
+
+ typedef_err:
+ fprintf (stderr, "Cannot look up typedef array ref in child: %s\n", ctf_errmsg (ctf_errno (child)));
+ exit (1);
+
+ array_err:
+ fprintf (stderr, "Cannot look up array in child: %s\n", ctf_errmsg (ctf_errno (child)));
+ exit (1);
+
+ type_name_err:
+ fprintf (stderr, "Cannot look up type name in child: %s\n", ctf_errmsg (ctf_errno (child)));
+ exit (1);
+
+ encoding_err:
+ fprintf (stderr, "cannot get type encoding in child: %s\n", ctf_errmsg (ctf_errno (child)));
+ exit (1);
+
+ func_err:
+ fprintf (stderr, "parent member function final lookup yielded kind %x, not %x\n", ctf_type_kind (child, memb.ctm_type), CTF_K_FUNCTION);
+ exit (1);
+}
+
+int main (void)
+{
+ test (1, 1);
+ test (1, 0);
+ test (0, 1);
+ test (0, 0);
+
+ printf ("All done.\n");
+}
--- /dev/null
+Testing with empty, unserialized parent
+Testing with empty, serialized parent
+Testing with nonempty, unserialized parent
+Testing with nonempty, serialized parent
+All done.