xref: /illumos-gate/usr/src/lib/libctf/common/ctf_diff.c (revision 8509e9caaaa43d21ab1a18a2aa45b43322c378ac)
1 /*
2  * This file and its contents are supplied under the terms of the
3  * Common Development and Distribution License ("CDDL"), version 1.0.
4  * You may only use this file in accordance with the terms of version
5  * 1.0 of the CDDL.
6  *
7  * A full copy of the text of the CDDL should have accompanied this
8  * source.  A copy of the CDDL is also available via the Internet at
9  * http://www.illumos.org/license/CDDL.
10  */
11 
12 /*
13  * Copyright (c) 2015 Joyent, Inc.  All rights reserved.
14  */
15 
16 /*
17  * The following is a basic overview of how we diff types in containers (the
18  * generally interesting part of diff, and what's used by merge). We maintain
19  * two mapping tables, a table of forward mappings (src->dest), and a reverse
20  * mapping (dest->src). Both are initialized to contain no mapping, and can also
21  * be updated to contain a negative mapping.
22  *
23  * What we do first is iterate over each type in the src container, and compare
24  * it with a type in the destination container. This may involve doing recursive
25  * comparisons -- which can involve cycles. To deal with this, whenever we
26  * encounter something which may be cyclic, we insert a guess. In other words,
27  * we assume that it may be true. This is necessary for the classic case of the
28  * following structure:
29  *
30  * struct foo {
31  *	struct foo *foo_next;
32  * };
33  *
34  * If it turns out that we were wrong, we discard our guesses.
35  *
36  * If we find that a given type in src has no corresponding entry in dst, we
37  * then mark its map as CTF_ERR (-1) to indicate that it has *no* match, as
38  * opposed to the default value of 0, which indicates an unknown match.
39  * Once we've done the first iteration through src, we know at that point in
40  * time whether everything in dst is similar or not and can simply walk over it
41  * and don't have to do any additional checks.
42  */
43 
44 #include <libctf.h>
45 #include <ctf_impl.h>
46 #include <sys/debug.h>
47 
48 typedef struct ctf_diff_func {
49 	const char *cdf_name;
50 	ulong_t cdf_symidx;
51 	ulong_t cdf_matchidx;
52 } ctf_diff_func_t;
53 
54 typedef struct ctf_diff_obj {
55 	const char *cdo_name;
56 	ulong_t cdo_symidx;
57 	ctf_id_t cdo_id;
58 	ulong_t cdo_matchidx;
59 } ctf_diff_obj_t;
60 
61 typedef struct ctf_diff_guess {
62 	struct ctf_diff_guess *cdg_next;
63 	ctf_id_t cdg_iid;
64 	ctf_id_t cdg_oid;
65 } ctf_diff_guess_t;
66 
67 /* typedef in libctf.h */
68 struct ctf_diff {
69 	uint_t cds_flags;
70 	boolean_t cds_tvalid;	/* types valid */
71 	ctf_file_t *cds_ifp;
72 	ctf_file_t *cds_ofp;
73 	ctf_id_t *cds_forward;
74 	ctf_id_t *cds_reverse;
75 	size_t cds_fsize;
76 	size_t cds_rsize;
77 	ctf_diff_type_f cds_func;
78 	ctf_diff_guess_t *cds_guess;
79 	void *cds_arg;
80 	uint_t cds_nifuncs;
81 	uint_t cds_nofuncs;
82 	uint_t cds_nextifunc;
83 	uint_t cds_nextofunc;
84 	ctf_diff_func_t *cds_ifuncs;
85 	ctf_diff_func_t *cds_ofuncs;
86 	boolean_t cds_ffillip;
87 	boolean_t cds_fvalid;
88 	uint_t cds_niobj;
89 	uint_t cds_noobj;
90 	uint_t cds_nextiobj;
91 	uint_t cds_nextoobj;
92 	ctf_diff_obj_t *cds_iobj;
93 	ctf_diff_obj_t *cds_oobj;
94 	boolean_t cds_ofillip;
95 	boolean_t cds_ovalid;
96 };
97 
98 #define	TINDEX(tid) (tid - 1)
99 
100 /*
101  * Team Diff
102  */
103 static int ctf_diff_type(ctf_diff_t *, ctf_file_t *, ctf_id_t, ctf_file_t *,
104     ctf_id_t);
105 
106 static int
107 ctf_diff_name(ctf_file_t *ifp, ctf_id_t iid, ctf_file_t *ofp, ctf_id_t oid)
108 {
109 	const char *iname, *oname;
110 	const ctf_type_t *itp, *otp;
111 
112 	if ((itp = ctf_lookup_by_id(&ifp, iid)) == NULL)
113 		return (CTF_ERR);
114 
115 	if ((otp = ctf_lookup_by_id(&ofp, oid)) == NULL)
116 		return (ctf_set_errno(ifp, iid));
117 
118 	iname = ctf_strptr(ifp, itp->ctt_name);
119 	oname = ctf_strptr(ofp, otp->ctt_name);
120 
121 	if ((iname == NULL || oname == NULL) && (iname != oname))
122 		return (B_TRUE);
123 
124 	/* Two anonymous names are the same */
125 	if (iname == NULL && oname == NULL)
126 		return (B_FALSE);
127 
128 	return (strcmp(iname, oname) == 0 ? B_FALSE: B_TRUE);
129 }
130 
131 /*
132  * For floats and ints
133  */
134 static int
135 ctf_diff_number(ctf_file_t *ifp, ctf_id_t iid, ctf_file_t *ofp, ctf_id_t oid)
136 {
137 	ctf_encoding_t ien, den;
138 
139 	if (ctf_type_encoding(ifp, iid, &ien) != 0)
140 		return (CTF_ERR);
141 
142 	if (ctf_type_encoding(ofp, oid, &den) != 0)
143 		return (ctf_set_errno(ifp, iid));
144 
145 	if (bcmp(&ien, &den, sizeof (ctf_encoding_t)) != 0)
146 		return (B_TRUE);
147 
148 	return (B_FALSE);
149 }
150 
151 /*
152  * Two typedefs are equivalent, if after we resolve a chain of typedefs, they
153  * point to equivalent types. This means that if a size_t is defined as follows:
154  *
155  * size_t -> ulong_t -> unsigned long
156  * size_t -> unsigned long
157  *
158  * That we'll ultimately end up treating them the same.
159  */
160 static int
161 ctf_diff_typedef(ctf_diff_t *cds, ctf_file_t *ifp, ctf_id_t iid,
162     ctf_file_t *ofp, ctf_id_t oid)
163 {
164 	ctf_id_t iref = CTF_ERR, oref = CTF_ERR;
165 
166 	while (ctf_type_kind(ifp, iid) == CTF_K_TYPEDEF) {
167 		iref = ctf_type_reference(ifp, iid);
168 		if (iref == CTF_ERR)
169 			return (CTF_ERR);
170 		iid = iref;
171 	}
172 
173 	while (ctf_type_kind(ofp, oid) == CTF_K_TYPEDEF) {
174 		oref = ctf_type_reference(ofp, oid);
175 		if (oref == CTF_ERR)
176 			return (CTF_ERR);
177 		oid = oref;
178 	}
179 
180 	VERIFY(iref != CTF_ERR && oref != CTF_ERR);
181 	return (ctf_diff_type(cds, ifp, iref, ofp, oref));
182 }
183 
184 /*
185  * Two qualifiers are equivalent iff they point to two equivalent types.
186  */
187 static int
188 ctf_diff_qualifier(ctf_diff_t *cds, ctf_file_t *ifp, ctf_id_t iid,
189     ctf_file_t *ofp, ctf_id_t oid)
190 {
191 	ctf_id_t iref, oref;
192 
193 	iref = ctf_type_reference(ifp, iid);
194 	if (iref == CTF_ERR)
195 		return (CTF_ERR);
196 
197 	oref = ctf_type_reference(ofp, oid);
198 	if (oref == CTF_ERR)
199 		return (ctf_set_errno(ifp, ctf_errno(ofp)));
200 
201 	return (ctf_diff_type(cds, ifp, iref, ofp, oref));
202 }
203 
204 /*
205  * Two arrays are the same iff they have the same type for contents, the same
206  * type for the index, and the same number of elements.
207  */
208 static int
209 ctf_diff_array(ctf_diff_t *cds, ctf_file_t *ifp, ctf_id_t iid, ctf_file_t *ofp,
210     ctf_id_t oid)
211 {
212 	int ret;
213 	ctf_arinfo_t iar, oar;
214 
215 	if (ctf_array_info(ifp, iid, &iar) == CTF_ERR)
216 		return (CTF_ERR);
217 
218 	if (ctf_array_info(ofp, oid, &oar) == CTF_ERR)
219 		return (ctf_set_errno(ifp, ctf_errno(ofp)));
220 
221 	ret = ctf_diff_type(cds, ifp, iar.ctr_contents, ofp, oar.ctr_contents);
222 	if (ret != B_FALSE)
223 		return (ret);
224 
225 	if (iar.ctr_nelems != oar.ctr_nelems)
226 		return (B_TRUE);
227 
228 	/*
229 	 * If we're ignoring integer types names, then we're trying to do a bit
230 	 * of a logical diff and we don't really care about the fact that the
231 	 * index element might not be the same here, what we care about are the
232 	 * number of elements and that they're the same type.
233 	 */
234 	if ((cds->cds_flags & CTF_DIFF_F_IGNORE_INTNAMES) == 0) {
235 		ret = ctf_diff_type(cds, ifp, iar.ctr_index, ofp,
236 		    oar.ctr_index);
237 		if (ret != B_FALSE)
238 			return (ret);
239 	}
240 
241 	return (B_FALSE);
242 }
243 
244 /*
245  * Two function pointers are the same if the following is all true:
246  *
247  *   o They have the same return type
248  *   o They have the same number of arguments
249  *   o The arguments are of the same type
250  *   o They have the same flags
251  */
252 static int
253 ctf_diff_fptr(ctf_diff_t *cds, ctf_file_t *ifp, ctf_id_t iid, ctf_file_t *ofp,
254     ctf_id_t oid)
255 {
256 	int ret, i;
257 	ctf_funcinfo_t ifunc, ofunc;
258 	ctf_id_t *iids, *oids;
259 
260 	if (ctf_func_info_by_id(ifp, iid, &ifunc) == CTF_ERR)
261 		return (CTF_ERR);
262 
263 	if (ctf_func_info_by_id(ofp, oid, &ofunc) == CTF_ERR)
264 		return (ctf_set_errno(ifp, ctf_errno(ofp)));
265 
266 	if (ifunc.ctc_argc != ofunc.ctc_argc)
267 		return (B_TRUE);
268 
269 	if (ifunc.ctc_flags != ofunc.ctc_flags)
270 		return (B_TRUE);
271 
272 	ret = ctf_diff_type(cds, ifp, ifunc.ctc_return, ofp, ofunc.ctc_return);
273 	if (ret != B_FALSE)
274 		return (ret);
275 
276 	iids = ctf_alloc(sizeof (ctf_id_t) * ifunc.ctc_argc);
277 	if (iids == NULL)
278 		return (ctf_set_errno(ifp, ENOMEM));
279 
280 	oids = ctf_alloc(sizeof (ctf_id_t) * ifunc.ctc_argc);
281 	if (oids == NULL) {
282 		ctf_free(iids, sizeof (ctf_id_t) * ifunc.ctc_argc);
283 		return (ctf_set_errno(ifp, ENOMEM));
284 	}
285 
286 	if (ctf_func_args_by_id(ifp, iid, ifunc.ctc_argc, iids) == CTF_ERR) {
287 		ret = CTF_ERR;
288 		goto out;
289 	}
290 
291 	if (ctf_func_args_by_id(ofp, oid, ofunc.ctc_argc, oids) == CTF_ERR) {
292 		ret = ctf_set_errno(ifp, ctf_errno(ofp));
293 		goto out;
294 	}
295 
296 	ret = B_TRUE;
297 	for (i = 0; i < ifunc.ctc_argc; i++) {
298 		ret = ctf_diff_type(cds, ifp, iids[i], ofp, oids[i]);
299 		if (ret != B_FALSE)
300 			goto out;
301 	}
302 	ret = B_FALSE;
303 
304 out:
305 	ctf_free(iids, sizeof (ctf_id_t) * ifunc.ctc_argc);
306 	ctf_free(oids, sizeof (ctf_id_t) * ofunc.ctc_argc);
307 	return (ret);
308 }
309 
310 /*
311  * Two structures are the same if every member is identical to its corresponding
312  * type, at the same offset, and has the same name, as well as them having the
313  * same overall size.
314  */
315 static int
316 ctf_diff_struct(ctf_diff_t *cds, ctf_file_t *ifp, ctf_id_t iid, ctf_file_t *ofp,
317     ctf_id_t oid)
318 {
319 	ctf_file_t *oifp;
320 	const ctf_type_t *itp, *otp;
321 	ssize_t isize, iincr, osize, oincr;
322 	const ctf_member_t *imp, *omp;
323 	const ctf_lmember_t *ilmp, *olmp;
324 	int n;
325 	ctf_diff_guess_t *cdg;
326 
327 	oifp = ifp;
328 
329 	if ((itp = ctf_lookup_by_id(&ifp, iid)) == NULL)
330 		return (CTF_ERR);
331 
332 	if ((otp = ctf_lookup_by_id(&ofp, oid)) == NULL)
333 		return (ctf_set_errno(oifp, ctf_errno(ofp)));
334 
335 	if (ctf_type_size(ifp, iid) != ctf_type_size(ofp, oid))
336 		return (B_TRUE);
337 
338 	if (LCTF_INFO_VLEN(ifp, itp->ctt_info) !=
339 	    LCTF_INFO_VLEN(ofp, otp->ctt_info))
340 		return (B_TRUE);
341 
342 	(void) ctf_get_ctt_size(ifp, itp, &isize, &iincr);
343 	(void) ctf_get_ctt_size(ofp, otp, &osize, &oincr);
344 
345 	if (ifp->ctf_version == CTF_VERSION_1 || isize < CTF_LSTRUCT_THRESH) {
346 		imp = (const ctf_member_t *)((uintptr_t)itp + iincr);
347 		ilmp = NULL;
348 	} else {
349 		imp = NULL;
350 		ilmp = (const ctf_lmember_t *)((uintptr_t)itp + iincr);
351 	}
352 
353 	if (ofp->ctf_version == CTF_VERSION_1 || osize < CTF_LSTRUCT_THRESH) {
354 		omp = (const ctf_member_t *)((uintptr_t)otp + oincr);
355 		olmp = NULL;
356 	} else {
357 		omp = NULL;
358 		olmp = (const ctf_lmember_t *)((uintptr_t)otp + oincr);
359 	}
360 
361 	/*
362 	 * Insert our assumption that they're equal for the moment.
363 	 */
364 	cdg = ctf_alloc(sizeof (ctf_diff_guess_t));
365 	if (cdg == NULL)
366 		return (ctf_set_errno(ifp, ENOMEM));
367 	cdg->cdg_iid = iid;
368 	cdg->cdg_oid = oid;
369 	cdg->cdg_next = cds->cds_guess;
370 	cds->cds_guess = cdg;
371 	cds->cds_forward[TINDEX(iid)] = oid;
372 	cds->cds_reverse[TINDEX(oid)] = iid;
373 
374 	for (n = LCTF_INFO_VLEN(ifp, itp->ctt_info); n != 0; n--) {
375 		const char *iname, *oname;
376 		ulong_t ioff, ooff;
377 		ctf_id_t itype, otype;
378 		int ret;
379 
380 		if (imp != NULL) {
381 			iname = ctf_strptr(ifp, imp->ctm_name);
382 			ioff = imp->ctm_offset;
383 			itype = imp->ctm_type;
384 		} else {
385 			iname = ctf_strptr(ifp, ilmp->ctlm_name);
386 			ioff = CTF_LMEM_OFFSET(ilmp);
387 			itype = ilmp->ctlm_type;
388 		}
389 
390 		if (omp != NULL) {
391 			oname = ctf_strptr(ofp, omp->ctm_name);
392 			ooff = omp->ctm_offset;
393 			otype = omp->ctm_type;
394 		} else {
395 			oname = ctf_strptr(ofp, olmp->ctlm_name);
396 			ooff = CTF_LMEM_OFFSET(olmp);
397 			otype = olmp->ctlm_type;
398 		}
399 
400 		if (ioff != ooff) {
401 			return (B_TRUE);
402 		}
403 		if (strcmp(iname, oname) != 0) {
404 			return (B_TRUE);
405 		}
406 		ret = ctf_diff_type(cds, ifp, itype, ofp, otype);
407 		if (ret != B_FALSE) {
408 			return (ret);
409 		}
410 
411 		/* Advance our pointers */
412 		if (imp != NULL)
413 			imp++;
414 		if (ilmp != NULL)
415 			ilmp++;
416 		if (omp != NULL)
417 			omp++;
418 		if (olmp != NULL)
419 			olmp++;
420 	}
421 
422 	return (B_FALSE);
423 }
424 
425 /*
426  * Two unions are the same if they have the same set of members. This is similar
427  * to, but slightly different from a struct. The offsets of members don't
428  * matter. However, there is no guarantee of ordering so we have to fall back to
429  * doing an O(N^2) scan.
430  */
431 typedef struct ctf_diff_union_member {
432 	ctf_diff_t *cdum_cds;
433 	ctf_file_t *cdum_fp;
434 	ctf_file_t *cdum_iterfp;
435 	const char *cdum_name;
436 	ctf_id_t cdum_type;
437 	int cdum_ret;
438 } ctf_diff_union_member_t;
439 
440 typedef struct ctf_diff_union_fp {
441 	ctf_diff_t *cduf_cds;
442 	ctf_file_t *cduf_curfp;
443 	ctf_file_t *cduf_altfp;
444 	ctf_id_t cduf_type;
445 	int cduf_ret;
446 } ctf_diff_union_fp_t;
447 
448 /* ARGSUSED */
449 static int
450 ctf_diff_union_check_member(const char *name, ctf_id_t id, ulong_t off,
451     void *arg)
452 {
453 	int ret;
454 	ctf_diff_union_member_t *cdump = arg;
455 
456 	if (strcmp(name, cdump->cdum_name) != 0)
457 		return (0);
458 
459 	ret = ctf_diff_type(cdump->cdum_cds, cdump->cdum_fp, cdump->cdum_type,
460 	    cdump->cdum_iterfp, id);
461 	if (ret == CTF_ERR) {
462 		cdump->cdum_ret = CTF_ERR;
463 		return (1);
464 	}
465 
466 	if (ret == B_FALSE) {
467 		cdump->cdum_ret = B_FALSE;
468 		/* Return non-zero to stop iteration as we have a match */
469 		return (1);
470 	}
471 
472 	return (0);
473 }
474 
475 /* ARGSUSED */
476 static int
477 ctf_diff_union_check_fp(const char *name, ctf_id_t id, ulong_t off, void *arg)
478 {
479 	int ret;
480 	ctf_diff_union_member_t cdum;
481 	ctf_diff_union_fp_t *cdufp = arg;
482 
483 	cdum.cdum_cds = cdufp->cduf_cds;
484 	cdum.cdum_fp = cdufp->cduf_curfp;
485 	cdum.cdum_iterfp = cdufp->cduf_altfp;
486 	cdum.cdum_name = name;
487 	cdum.cdum_type = id;
488 	cdum.cdum_ret = B_TRUE;
489 
490 	ret = ctf_member_iter(cdum.cdum_iterfp, cdufp->cduf_type,
491 	    ctf_diff_union_check_member, &cdum);
492 	if (ret == 0 || cdum.cdum_ret == CTF_ERR) {
493 		/* No match found or error, terminate now */
494 		cdufp->cduf_ret = cdum.cdum_ret;
495 		return (1);
496 	} else if (ret == CTF_ERR) {
497 		(void) ctf_set_errno(cdum.cdum_fp, ctf_errno(cdum.cdum_iterfp));
498 		cdufp->cduf_ret = CTF_ERR;
499 		return (1);
500 	} else {
501 		ASSERT(cdum.cdum_ret == B_FALSE);
502 		cdufp->cduf_ret = cdum.cdum_ret;
503 		return (0);
504 	}
505 }
506 
507 static int
508 ctf_diff_union(ctf_diff_t *cds, ctf_file_t *ifp, ctf_id_t iid, ctf_file_t *ofp,
509     ctf_id_t oid)
510 {
511 	ctf_file_t *oifp;
512 	const ctf_type_t *itp, *otp;
513 	ctf_diff_union_fp_t cduf;
514 	ctf_diff_guess_t *cdg;
515 	int ret;
516 
517 	oifp = ifp;
518 	if ((itp = ctf_lookup_by_id(&ifp, iid)) == NULL)
519 		return (CTF_ERR);
520 	if ((otp = ctf_lookup_by_id(&ofp, oid)) == NULL)
521 		return (ctf_set_errno(oifp, ctf_errno(ofp)));
522 
523 	if (LCTF_INFO_VLEN(ifp, itp->ctt_info) !=
524 	    LCTF_INFO_VLEN(ofp, otp->ctt_info))
525 		return (B_TRUE);
526 
527 	cdg = ctf_alloc(sizeof (ctf_diff_guess_t));
528 	if (cdg == NULL)
529 		return (ctf_set_errno(ifp, ENOMEM));
530 	cdg->cdg_iid = iid;
531 	cdg->cdg_oid = oid;
532 	cdg->cdg_next = cds->cds_guess;
533 	cds->cds_guess = cdg;
534 	cds->cds_forward[TINDEX(iid)] = oid;
535 	cds->cds_reverse[TINDEX(oid)] = iid;
536 
537 	cduf.cduf_cds = cds;
538 	cduf.cduf_curfp = ifp;
539 	cduf.cduf_altfp = ofp;
540 	cduf.cduf_type = oid;
541 	cduf.cduf_ret = B_TRUE;
542 	ret = ctf_member_iter(ifp, iid, ctf_diff_union_check_fp, &cduf);
543 	if (ret != CTF_ERR)
544 		ret = cduf.cduf_ret;
545 
546 	return (ret);
547 }
548 
549 /*
550  * Two enums are equivalent if they share the same underlying type and they have
551  * the same set of members.
552  */
553 static int
554 ctf_diff_enum(ctf_file_t *ifp, ctf_id_t iid, ctf_file_t *ofp, ctf_id_t oid)
555 {
556 	ctf_file_t *oifp;
557 	const ctf_type_t *itp, *otp;
558 	ssize_t iincr, oincr;
559 	const ctf_enum_t *iep, *oep;
560 	int n;
561 
562 	oifp = ifp;
563 	if ((itp = ctf_lookup_by_id(&ifp, iid)) == NULL)
564 		return (CTF_ERR);
565 	if ((otp = ctf_lookup_by_id(&ofp, oid)) == NULL)
566 		return (ctf_set_errno(oifp, ctf_errno(ofp)));
567 
568 	if (LCTF_INFO_VLEN(ifp, itp->ctt_info) !=
569 	    LCTF_INFO_VLEN(ofp, otp->ctt_info))
570 		return (B_TRUE);
571 
572 	(void) ctf_get_ctt_size(ifp, itp, NULL, &iincr);
573 	(void) ctf_get_ctt_size(ofp, otp, NULL, &oincr);
574 	iep = (const ctf_enum_t *)((uintptr_t)itp + iincr);
575 	oep = (const ctf_enum_t *)((uintptr_t)otp + oincr);
576 
577 	for (n = LCTF_INFO_VLEN(ifp, itp->ctt_info); n != 0;
578 	    n--, iep++, oep++) {
579 		if (strcmp(ctf_strptr(ifp, iep->cte_name),
580 		    ctf_strptr(ofp, oep->cte_name)) != 0)
581 			return (B_TRUE);
582 
583 		if (iep->cte_value != oep->cte_value)
584 			return (B_TRUE);
585 	}
586 
587 	return (B_FALSE);
588 }
589 
590 /*
591  * Two forwards are equivalent in one of two cases. If both are forwards, than
592  * they are the same. Otherwise, they're equivalent if one is a struct or union
593  * and the other is a forward.
594  */
595 static int
596 ctf_diff_forward(ctf_file_t *ifp, ctf_id_t iid, ctf_file_t *ofp, ctf_id_t oid)
597 {
598 	int ikind, okind;
599 
600 	ikind = ctf_type_kind(ifp, iid);
601 	okind = ctf_type_kind(ofp, oid);
602 
603 	if (ikind == okind) {
604 		ASSERT(ikind == CTF_K_FORWARD);
605 		return (B_FALSE);
606 	} else if (ikind == CTF_K_FORWARD) {
607 		return (okind != CTF_K_UNION && okind != CTF_K_STRUCT);
608 	} else {
609 		return (ikind != CTF_K_UNION && ikind != CTF_K_STRUCT);
610 	}
611 }
612 
613 /*
614  * Are two types equivalent?
615  */
616 int
617 ctf_diff_type(ctf_diff_t *cds, ctf_file_t *ifp, ctf_id_t iid, ctf_file_t *ofp,
618     ctf_id_t oid)
619 {
620 	int ret, ikind, okind;
621 
622 	/* Do a quick short circuit */
623 	if (ifp == ofp && iid == oid)
624 		return (B_FALSE);
625 
626 	/*
627 	 * Check if it's something we've already encountered in a forward
628 	 * reference or forward negative table. Also double check the reverse
629 	 * table.
630 	 */
631 	if (cds->cds_forward[TINDEX(iid)] == oid)
632 		return (B_FALSE);
633 	if (cds->cds_forward[TINDEX(iid)] != 0)
634 		return (B_TRUE);
635 	if (cds->cds_reverse[TINDEX(oid)] == iid)
636 		return (B_FALSE);
637 	if ((cds->cds_flags & CTF_DIFF_F_IGNORE_INTNAMES) == 0 &&
638 	    cds->cds_reverse[TINDEX(oid)] != 0)
639 		return (B_TRUE);
640 
641 	ikind = ctf_type_kind(ifp, iid);
642 	okind = ctf_type_kind(ofp, oid);
643 
644 	if (ikind != okind &&
645 	    ikind != CTF_K_FORWARD && okind != CTF_K_FORWARD)
646 			return (B_TRUE);
647 
648 	/* Check names */
649 	if ((ret = ctf_diff_name(ifp, iid, ofp, oid)) != B_FALSE) {
650 		if (ikind != okind || ikind != CTF_K_INTEGER ||
651 		    (cds->cds_flags & CTF_DIFF_F_IGNORE_INTNAMES) == 0)
652 			return (ret);
653 	}
654 
655 	if (ikind == CTF_K_FORWARD || okind == CTF_K_FORWARD)
656 		return (ctf_diff_forward(ifp, iid, ofp, oid));
657 
658 	switch (ikind) {
659 	case CTF_K_INTEGER:
660 	case CTF_K_FLOAT:
661 		ret = ctf_diff_number(ifp, iid, ofp, oid);
662 		break;
663 	case CTF_K_ARRAY:
664 		ret = ctf_diff_array(cds, ifp, iid, ofp, oid);
665 		break;
666 	case CTF_K_FUNCTION:
667 		ret = ctf_diff_fptr(cds, ifp, iid, ofp, oid);
668 		break;
669 	case CTF_K_STRUCT:
670 		ret = ctf_diff_struct(cds, ifp, iid, ofp, oid);
671 		break;
672 	case CTF_K_UNION:
673 		ret = ctf_diff_union(cds, ifp, iid, ofp, oid);
674 		break;
675 	case CTF_K_ENUM:
676 		ret = ctf_diff_enum(ifp, iid, ofp, oid);
677 		break;
678 	case CTF_K_FORWARD:
679 		ret = ctf_diff_forward(ifp, iid, ofp, oid);
680 		break;
681 	case CTF_K_TYPEDEF:
682 		ret = ctf_diff_typedef(cds, ifp, iid, ofp, oid);
683 		break;
684 	case CTF_K_POINTER:
685 	case CTF_K_VOLATILE:
686 	case CTF_K_CONST:
687 	case CTF_K_RESTRICT:
688 		ret = ctf_diff_qualifier(cds, ifp, iid, ofp, oid);
689 		break;
690 	case CTF_K_UNKNOWN:
691 		/*
692 		 * The current CTF tools use CTF_K_UNKNOWN as a padding type. We
693 		 * always declare two instances of CTF_K_UNKNOWN as different,
694 		 * even though this leads to additional diff noise.
695 		 */
696 		ret = B_TRUE;
697 		break;
698 	default:
699 		abort();
700 	}
701 
702 	return (ret);
703 }
704 
705 /*
706  * Walk every type in the first container and try to find a match in the second.
707  * If there is a match, then update both the forward and reverse mapping tables.
708  *
709  * The self variable tells us whether or not we should be comparing the input
710  * ctf container with itself or not.
711  */
712 static int
713 ctf_diff_pass1(ctf_diff_t *cds, boolean_t self)
714 {
715 	int i, j, diff;
716 	int istart, iend, jstart, jend;
717 
718 	istart = 1;
719 	iend = cds->cds_ifp->ctf_typemax;
720 	if (cds->cds_ifp->ctf_flags & LCTF_CHILD) {
721 		istart += CTF_CHILD_START;
722 		iend += CTF_CHILD_START;
723 	}
724 
725 	jstart = 1;
726 	jend = cds->cds_ofp->ctf_typemax;
727 	if (cds->cds_ofp->ctf_flags & LCTF_CHILD) {
728 		jstart += CTF_CHILD_START;
729 		jend += CTF_CHILD_START;
730 	}
731 
732 	for (i = istart; i <= iend; i++) {
733 		diff = B_TRUE;
734 
735 		/*
736 		 * If we're doing a self diff for dedup purposes, then we want
737 		 * to ensure that we compare a type i with every type in the
738 		 * range, [ 1, i ). Yes, this does mean that when i equals 1,
739 		 * we won't compare anything.
740 		 */
741 		if (self == B_TRUE) {
742 			jstart = istart;
743 			jend = i - 1;
744 		}
745 		for (j = jstart; j <= jend; j++) {
746 			ctf_diff_guess_t *cdg, *tofree;
747 
748 			ASSERT(cds->cds_guess == NULL);
749 			diff = ctf_diff_type(cds, cds->cds_ifp, i,
750 			    cds->cds_ofp, j);
751 			if (diff == CTF_ERR)
752 				return (CTF_ERR);
753 
754 			/* Clean up our guesses */
755 			cdg = cds->cds_guess;
756 			cds->cds_guess = NULL;
757 			while (cdg != NULL) {
758 				if (diff == B_TRUE) {
759 					cds->cds_forward[TINDEX(cdg->cdg_iid)] =
760 					    0;
761 					cds->cds_reverse[TINDEX(cdg->cdg_oid)] =
762 					    0;
763 				}
764 				tofree = cdg;
765 				cdg = cdg->cdg_next;
766 				ctf_free(tofree, sizeof (ctf_diff_guess_t));
767 			}
768 
769 			/* Found a hit, update the tables */
770 			if (diff == B_FALSE) {
771 				cds->cds_forward[TINDEX(i)] = j;
772 				if (cds->cds_reverse[TINDEX(j)] == 0)
773 					cds->cds_reverse[TINDEX(j)] = i;
774 				break;
775 			}
776 		}
777 
778 		/* Call the callback at this point */
779 		if (diff == B_TRUE) {
780 			cds->cds_forward[TINDEX(i)] = CTF_ERR;
781 			cds->cds_func(cds->cds_ifp, i, B_FALSE, NULL, CTF_ERR,
782 			    cds->cds_arg);
783 		} else {
784 			cds->cds_func(cds->cds_ifp, i, B_TRUE, cds->cds_ofp, j,
785 			    cds->cds_arg);
786 		}
787 	}
788 
789 	return (0);
790 }
791 
792 /*
793  * Now we need to walk the second container and emit anything that we didn't
794  * find as common in the first pass.
795  */
796 static int
797 ctf_diff_pass2(ctf_diff_t *cds)
798 {
799 	int i, start, end;
800 
801 	start = 0x1;
802 	end = cds->cds_ofp->ctf_typemax;
803 	if (cds->cds_ofp->ctf_flags & LCTF_CHILD) {
804 		start += CTF_CHILD_START;
805 		end += CTF_CHILD_START;
806 	}
807 
808 	for (i = start; i <= end; i++) {
809 		if (cds->cds_reverse[TINDEX(i)] != 0)
810 			continue;
811 		cds->cds_func(cds->cds_ofp, i, B_FALSE, NULL, CTF_ERR,
812 		    cds->cds_arg);
813 	}
814 
815 	return (0);
816 }
817 
818 int
819 ctf_diff_init(ctf_file_t *ifp, ctf_file_t *ofp, ctf_diff_t **cdsp)
820 {
821 	ctf_diff_t *cds;
822 	size_t fsize, rsize;
823 
824 	cds = ctf_alloc(sizeof (ctf_diff_t));
825 	if (cds == NULL)
826 		return (ctf_set_errno(ifp, ENOMEM));
827 
828 	bzero(cds, sizeof (ctf_diff_t));
829 	cds->cds_ifp = ifp;
830 	cds->cds_ofp = ofp;
831 
832 	fsize = sizeof (ctf_id_t) * ifp->ctf_typemax;
833 	rsize = sizeof (ctf_id_t) * ofp->ctf_typemax;
834 	if (ifp->ctf_flags & LCTF_CHILD)
835 		fsize += CTF_CHILD_START * sizeof (ctf_id_t);
836 	if (ofp->ctf_flags & LCTF_CHILD)
837 		rsize += CTF_CHILD_START * sizeof (ctf_id_t);
838 
839 	cds->cds_forward = ctf_alloc(fsize);
840 	if (cds->cds_forward == NULL) {
841 		ctf_free(cds, sizeof (ctf_diff_t));
842 		return (ctf_set_errno(ifp, ENOMEM));
843 	}
844 	cds->cds_fsize = fsize;
845 	cds->cds_reverse = ctf_alloc(rsize);
846 	if (cds->cds_reverse == NULL) {
847 		ctf_free(cds->cds_forward, fsize);
848 		ctf_free(cds, sizeof (ctf_diff_t));
849 		return (ctf_set_errno(ifp, ENOMEM));
850 	}
851 	cds->cds_rsize = rsize;
852 	bzero(cds->cds_forward, fsize);
853 	bzero(cds->cds_reverse, rsize);
854 
855 	cds->cds_ifp->ctf_refcnt++;
856 	cds->cds_ofp->ctf_refcnt++;
857 	*cdsp = cds;
858 	return (0);
859 }
860 
861 int
862 ctf_diff_types(ctf_diff_t *cds, ctf_diff_type_f cb, void *arg)
863 {
864 	int ret;
865 
866 	cds->cds_func = cb;
867 	cds->cds_arg = arg;
868 
869 	ret = ctf_diff_pass1(cds, B_FALSE);
870 	if (ret == 0)
871 		ret = ctf_diff_pass2(cds);
872 
873 	cds->cds_func = NULL;
874 	cds->cds_arg = NULL;
875 	cds->cds_tvalid = B_TRUE;
876 	return (ret);
877 }
878 
879 /*
880  * Do a diff where we're comparing a container with itself. In other words we'd
881  * like to know what types are actually duplicates of existing types in the
882  * container.
883  *
884  * Note this should remain private to libctf and not be exported in the public
885  * mapfile for the time being.
886  */
887 int
888 ctf_diff_self(ctf_diff_t *cds, ctf_diff_type_f cb, void *arg)
889 {
890 	if (cds->cds_ifp != cds->cds_ofp)
891 		return (EINVAL);
892 
893 	cds->cds_func = cb;
894 	cds->cds_arg = arg;
895 
896 	return (ctf_diff_pass1(cds, B_TRUE));
897 }
898 
899 
900 void
901 ctf_diff_fini(ctf_diff_t *cds)
902 {
903 	ctf_diff_guess_t *cdg;
904 	size_t fsize, rsize;
905 
906 	if (cds == NULL)
907 		return;
908 
909 	cds->cds_ifp->ctf_refcnt--;
910 	cds->cds_ofp->ctf_refcnt--;
911 
912 	fsize = sizeof (ctf_id_t) * cds->cds_ifp->ctf_typemax;
913 	rsize = sizeof (ctf_id_t) * cds->cds_ofp->ctf_typemax;
914 	if (cds->cds_ifp->ctf_flags & LCTF_CHILD)
915 		fsize += CTF_CHILD_START * sizeof (ctf_id_t);
916 	if (cds->cds_ofp->ctf_flags & LCTF_CHILD)
917 		rsize += CTF_CHILD_START * sizeof (ctf_id_t);
918 
919 	if (cds->cds_ifuncs != NULL)
920 		ctf_free(cds->cds_ifuncs,
921 		    sizeof (ctf_diff_func_t) * cds->cds_nifuncs);
922 	if (cds->cds_ofuncs != NULL)
923 		ctf_free(cds->cds_ofuncs,
924 		    sizeof (ctf_diff_func_t) * cds->cds_nofuncs);
925 	if (cds->cds_iobj != NULL)
926 		ctf_free(cds->cds_iobj,
927 		    sizeof (ctf_diff_obj_t) * cds->cds_niobj);
928 	if (cds->cds_oobj != NULL)
929 		ctf_free(cds->cds_oobj,
930 		    sizeof (ctf_diff_obj_t) * cds->cds_noobj);
931 	cdg = cds->cds_guess;
932 	while (cdg != NULL) {
933 		ctf_diff_guess_t *tofree = cdg;
934 		cdg = cdg->cdg_next;
935 		ctf_free(tofree, sizeof (ctf_diff_guess_t));
936 	}
937 	if (cds->cds_forward != NULL)
938 		ctf_free(cds->cds_forward, cds->cds_fsize);
939 	if (cds->cds_reverse != NULL)
940 		ctf_free(cds->cds_reverse, cds->cds_rsize);
941 	ctf_free(cds, sizeof (ctf_diff_t));
942 }
943 
944 uint_t
945 ctf_diff_getflags(ctf_diff_t *cds)
946 {
947 	return (cds->cds_flags);
948 }
949 
950 int
951 ctf_diff_setflags(ctf_diff_t *cds, uint_t flags)
952 {
953 	if ((flags & ~CTF_DIFF_F_IGNORE_INTNAMES) != 0)
954 		return (ctf_set_errno(cds->cds_ifp, EINVAL));
955 
956 	cds->cds_flags = flags;
957 	return (0);
958 }
959 
960 static boolean_t
961 ctf_diff_symid(ctf_diff_t *cds, ctf_id_t iid, ctf_id_t oid)
962 {
963 	ctf_file_t *ifp, *ofp;
964 
965 	ifp = cds->cds_ifp;
966 	ofp = cds->cds_ofp;
967 
968 	/*
969 	 * If we have parent containers on the scene here, we need to go through
970 	 * and do a full diff check because a diff for types will not actually
971 	 * go through and check types in the parent container.
972 	 */
973 	if (iid == 0 || oid == 0)
974 		return (iid == oid ? B_FALSE: B_TRUE);
975 
976 	if (!(ifp->ctf_flags & LCTF_CHILD) && !(ofp->ctf_flags & LCTF_CHILD)) {
977 		if (cds->cds_forward[TINDEX(iid)] != oid)
978 			return (B_TRUE);
979 		return (B_FALSE);
980 	}
981 
982 	return (ctf_diff_type(cds, ifp, iid, ofp, oid));
983 }
984 
985 /* ARGSUSED */
986 static void
987 ctf_diff_void_cb(ctf_file_t *ifp, ctf_id_t iid, boolean_t same, ctf_file_t *ofp,
988     ctf_id_t oid, void *arg)
989 {
990 }
991 
992 /* ARGSUSED */
993 static int
994 ctf_diff_func_count(const char *name, ulong_t symidx, ctf_funcinfo_t *fip,
995     void *arg)
996 {
997 	uint32_t *ip = arg;
998 
999 	*ip = *ip + 1;
1000 	return (0);
1001 }
1002 
1003 /* ARGSUSED */
1004 static int
1005 ctf_diff_func_fill_cb(const char *name, ulong_t symidx, ctf_funcinfo_t *fip,
1006     void *arg)
1007 {
1008 	uint_t *next, max;
1009 	ctf_diff_func_t *funcptr;
1010 	ctf_diff_t *cds = arg;
1011 
1012 	if (cds->cds_ffillip == B_TRUE) {
1013 		max = cds->cds_nifuncs;
1014 		next = &cds->cds_nextifunc;
1015 		funcptr = cds->cds_ifuncs + *next;
1016 	} else {
1017 		max = cds->cds_nofuncs;
1018 		next = &cds->cds_nextofunc;
1019 		funcptr = cds->cds_ofuncs + *next;
1020 
1021 	}
1022 
1023 	VERIFY(*next < max);
1024 	funcptr->cdf_name = name;
1025 	funcptr->cdf_symidx = symidx;
1026 	funcptr->cdf_matchidx = ULONG_MAX;
1027 	*next = *next + 1;
1028 
1029 	return (0);
1030 }
1031 
1032 int
1033 ctf_diff_func_fill(ctf_diff_t *cds)
1034 {
1035 	int ret;
1036 	uint32_t ifcount, ofcount, idcnt, cti;
1037 	ulong_t i, j;
1038 	ctf_id_t *iids, *oids;
1039 
1040 	ifcount = 0;
1041 	ofcount = 0;
1042 	idcnt = 0;
1043 	iids = NULL;
1044 	oids = NULL;
1045 
1046 	ret = ctf_function_iter(cds->cds_ifp, ctf_diff_func_count, &ifcount);
1047 	if (ret != 0)
1048 		return (ret);
1049 	ret = ctf_function_iter(cds->cds_ofp, ctf_diff_func_count, &ofcount);
1050 	if (ret != 0)
1051 		return (ret);
1052 
1053 	cds->cds_ifuncs = ctf_alloc(sizeof (ctf_diff_func_t) * ifcount);
1054 	if (cds->cds_ifuncs == NULL)
1055 		return (ctf_set_errno(cds->cds_ifp, ENOMEM));
1056 
1057 	cds->cds_nifuncs = ifcount;
1058 	cds->cds_nextifunc = 0;
1059 
1060 	cds->cds_ofuncs = ctf_alloc(sizeof (ctf_diff_func_t) * ofcount);
1061 	if (cds->cds_ofuncs == NULL)
1062 		return (ctf_set_errno(cds->cds_ifp, ENOMEM));
1063 
1064 	cds->cds_nofuncs = ofcount;
1065 	cds->cds_nextofunc = 0;
1066 
1067 	cds->cds_ffillip = B_TRUE;
1068 	if ((ret = ctf_function_iter(cds->cds_ifp, ctf_diff_func_fill_cb,
1069 	    cds)) != 0)
1070 		return (ret);
1071 
1072 	cds->cds_ffillip = B_FALSE;
1073 	if ((ret = ctf_function_iter(cds->cds_ofp, ctf_diff_func_fill_cb,
1074 	    cds)) != 0)
1075 		return (ret);
1076 
1077 	/*
1078 	 * Everything is initialized to not match. This could probably be faster
1079 	 * with something that used a hash. But this part of the diff isn't used
1080 	 * by merge.
1081 	 */
1082 	for (i = 0; i < cds->cds_nifuncs; i++) {
1083 		for (j = 0; j < cds->cds_nofuncs; j++) {
1084 			ctf_diff_func_t *ifd, *ofd;
1085 			ctf_funcinfo_t ifip, ofip;
1086 			boolean_t match;
1087 
1088 			ifd = &cds->cds_ifuncs[i];
1089 			ofd = &cds->cds_ofuncs[j];
1090 			if (strcmp(ifd->cdf_name, ofd->cdf_name) != 0)
1091 				continue;
1092 
1093 			ret = ctf_func_info(cds->cds_ifp, ifd->cdf_symidx,
1094 			    &ifip);
1095 			if (ret != 0)
1096 				goto out;
1097 			ret = ctf_func_info(cds->cds_ofp, ofd->cdf_symidx,
1098 			    &ofip);
1099 			if (ret != 0) {
1100 				ret = ctf_set_errno(cds->cds_ifp,
1101 				    ctf_errno(cds->cds_ofp));
1102 				goto out;
1103 			}
1104 
1105 			if (ifip.ctc_argc != ofip.ctc_argc &&
1106 			    ifip.ctc_flags != ofip.ctc_flags)
1107 				continue;
1108 
1109 			/* Validate return type and arguments are the same */
1110 			if (ctf_diff_symid(cds, ifip.ctc_return,
1111 			    ofip.ctc_return))
1112 				continue;
1113 
1114 			if (ifip.ctc_argc > idcnt) {
1115 				if (iids != NULL)
1116 					ctf_free(iids,
1117 					    sizeof (ctf_id_t) * idcnt);
1118 				if (oids != NULL)
1119 					ctf_free(oids,
1120 					    sizeof (ctf_id_t) * idcnt);
1121 				iids = oids = NULL;
1122 				idcnt = ifip.ctc_argc;
1123 				iids = ctf_alloc(sizeof (ctf_id_t) * idcnt);
1124 				if (iids == NULL) {
1125 					ret = ctf_set_errno(cds->cds_ifp,
1126 					    ENOMEM);
1127 					goto out;
1128 				}
1129 				oids = ctf_alloc(sizeof (ctf_id_t) * idcnt);
1130 				if (iids == NULL) {
1131 					ret = ctf_set_errno(cds->cds_ifp,
1132 					    ENOMEM);
1133 					goto out;
1134 				}
1135 			}
1136 
1137 			if ((ret = ctf_func_args(cds->cds_ifp, ifd->cdf_symidx,
1138 			    ifip.ctc_argc, iids)) != 0)
1139 				goto out;
1140 			if ((ret = ctf_func_args(cds->cds_ofp, ofd->cdf_symidx,
1141 			    ofip.ctc_argc, oids)) != 0)
1142 				goto out;
1143 
1144 			match = B_TRUE;
1145 			for (cti = 0; cti < ifip.ctc_argc; cti++) {
1146 				if (ctf_diff_symid(cds, iids[cti], oids[cti])) {
1147 					match = B_FALSE;
1148 					break;
1149 				}
1150 			}
1151 
1152 			if (match == B_FALSE)
1153 				continue;
1154 
1155 			ifd->cdf_matchidx = j;
1156 			ofd->cdf_matchidx = i;
1157 			break;
1158 		}
1159 	}
1160 
1161 	ret = 0;
1162 
1163 out:
1164 	if (iids != NULL)
1165 		ctf_free(iids, sizeof (ctf_id_t) * idcnt);
1166 	if (oids != NULL)
1167 		ctf_free(oids, sizeof (ctf_id_t) * idcnt);
1168 
1169 	return (ret);
1170 }
1171 
1172 /*
1173  * In general, two functions are the same, if they have the same name and their
1174  * arguments have the same types, including the return type. Like types, we
1175  * basically have to do this in two passes. In the first phase we walk every
1176  * type in the first container and try to find a match in the second.
1177  */
1178 int
1179 ctf_diff_functions(ctf_diff_t *cds, ctf_diff_func_f cb, void *arg)
1180 {
1181 	int ret;
1182 	ulong_t i;
1183 
1184 	if (cds->cds_tvalid == B_FALSE) {
1185 		if ((ret = ctf_diff_types(cds, ctf_diff_void_cb, NULL)) != 0)
1186 			return (ret);
1187 	}
1188 
1189 	if (cds->cds_fvalid == B_FALSE) {
1190 		if ((ret = ctf_diff_func_fill(cds)) != 0)
1191 			return (ret);
1192 		cds->cds_fvalid = B_TRUE;
1193 	}
1194 
1195 	for (i = 0; i < cds->cds_nifuncs; i++) {
1196 		if (cds->cds_ifuncs[i].cdf_matchidx == ULONG_MAX) {
1197 			cb(cds->cds_ifp, cds->cds_ifuncs[i].cdf_symidx,
1198 			    B_FALSE, NULL, ULONG_MAX, arg);
1199 		} else {
1200 			ulong_t idx = cds->cds_ifuncs[i].cdf_matchidx;
1201 			cb(cds->cds_ifp, cds->cds_ifuncs[i].cdf_symidx, B_TRUE,
1202 			    cds->cds_ofp, cds->cds_ofuncs[idx].cdf_symidx, arg);
1203 		}
1204 	}
1205 
1206 	for (i = 0; i < cds->cds_nofuncs; i++) {
1207 		if (cds->cds_ofuncs[i].cdf_matchidx != ULONG_MAX)
1208 			continue;
1209 		cb(cds->cds_ofp, cds->cds_ofuncs[i].cdf_symidx, B_FALSE,
1210 		    NULL, ULONG_MAX, arg);
1211 	}
1212 
1213 	return (0);
1214 }
1215 
1216 static int
1217 ctf_diff_obj_fill_cb(const char *name, ctf_id_t id, ulong_t symidx, void *arg)
1218 {
1219 	uint_t *next, max;
1220 	ctf_diff_obj_t *objptr;
1221 	ctf_diff_t *cds = arg;
1222 
1223 	if (cds->cds_ofillip == B_TRUE) {
1224 		max = cds->cds_niobj;
1225 		next = &cds->cds_nextiobj;
1226 		objptr = cds->cds_iobj + *next;
1227 	} else {
1228 		max = cds->cds_noobj;
1229 		next = &cds->cds_nextoobj;
1230 		objptr = cds->cds_oobj+ *next;
1231 
1232 	}
1233 
1234 	VERIFY(*next < max);
1235 	objptr->cdo_name = name;
1236 	objptr->cdo_symidx = symidx;
1237 	objptr->cdo_id = id;
1238 	objptr->cdo_matchidx = ULONG_MAX;
1239 	*next = *next + 1;
1240 
1241 	return (0);
1242 }
1243 
1244 /* ARGSUSED */
1245 static int
1246 ctf_diff_obj_count(const char *name, ctf_id_t id, ulong_t symidx, void *arg)
1247 {
1248 	uint32_t *count = arg;
1249 
1250 	*count = *count + 1;
1251 
1252 	return (0);
1253 }
1254 
1255 
1256 static int
1257 ctf_diff_obj_fill(ctf_diff_t *cds)
1258 {
1259 	int ret;
1260 	uint32_t iocount, oocount;
1261 	ulong_t i, j;
1262 
1263 	iocount = 0;
1264 	oocount = 0;
1265 
1266 	ret = ctf_object_iter(cds->cds_ifp, ctf_diff_obj_count, &iocount);
1267 	if (ret != 0)
1268 		return (ret);
1269 
1270 	ret = ctf_object_iter(cds->cds_ofp, ctf_diff_obj_count, &oocount);
1271 	if (ret != 0)
1272 		return (ret);
1273 
1274 	cds->cds_iobj = ctf_alloc(sizeof (ctf_diff_obj_t) * iocount);
1275 	if (cds->cds_iobj == NULL)
1276 		return (ctf_set_errno(cds->cds_ifp, ENOMEM));
1277 	cds->cds_niobj = iocount;
1278 	cds->cds_nextiobj = 0;
1279 
1280 	cds->cds_oobj = ctf_alloc(sizeof (ctf_diff_obj_t) * oocount);
1281 	if (cds->cds_oobj == NULL)
1282 		return (ctf_set_errno(cds->cds_ifp, ENOMEM));
1283 	cds->cds_noobj = oocount;
1284 	cds->cds_nextoobj = 0;
1285 
1286 	cds->cds_ofillip = B_TRUE;
1287 	if ((ret = ctf_object_iter(cds->cds_ifp, ctf_diff_obj_fill_cb,
1288 	    cds)) != 0)
1289 		return (ret);
1290 
1291 	cds->cds_ofillip = B_FALSE;
1292 	if ((ret = ctf_object_iter(cds->cds_ofp, ctf_diff_obj_fill_cb,
1293 	    cds)) != 0)
1294 		return (ret);
1295 
1296 	for (i = 0; i < cds->cds_niobj; i++) {
1297 		for (j = 0; j < cds->cds_noobj; j++) {
1298 			ctf_diff_obj_t *id, *od;
1299 
1300 			id = &cds->cds_iobj[i];
1301 			od = &cds->cds_oobj[j];
1302 
1303 			if (id->cdo_name == NULL || od->cdo_name == NULL)
1304 				continue;
1305 			if (strcmp(id->cdo_name, od->cdo_name) != 0)
1306 				continue;
1307 
1308 			if (ctf_diff_symid(cds, id->cdo_id, od->cdo_id)) {
1309 				continue;
1310 			}
1311 
1312 			id->cdo_matchidx = j;
1313 			od->cdo_matchidx = i;
1314 			break;
1315 		}
1316 	}
1317 
1318 	return (0);
1319 }
1320 
1321 int
1322 ctf_diff_objects(ctf_diff_t *cds, ctf_diff_obj_f cb, void *arg)
1323 {
1324 	int ret;
1325 	ulong_t i;
1326 
1327 	if (cds->cds_tvalid == B_FALSE) {
1328 		if ((ret = ctf_diff_types(cds, ctf_diff_void_cb, NULL)) != 0)
1329 			return (ret);
1330 	}
1331 
1332 	if (cds->cds_ovalid == B_FALSE) {
1333 		if ((ret = ctf_diff_obj_fill(cds)) != 0)
1334 			return (ret);
1335 		cds->cds_ovalid = B_TRUE;
1336 	}
1337 
1338 	for (i = 0; i < cds->cds_niobj; i++) {
1339 		ctf_diff_obj_t *o = &cds->cds_iobj[i];
1340 
1341 		if (cds->cds_iobj[i].cdo_matchidx == ULONG_MAX) {
1342 			cb(cds->cds_ifp, o->cdo_symidx, o->cdo_id, B_FALSE,
1343 			    NULL, ULONG_MAX, CTF_ERR, arg);
1344 		} else {
1345 			ctf_diff_obj_t *alt = &cds->cds_oobj[o->cdo_matchidx];
1346 			cb(cds->cds_ifp, o->cdo_symidx, o->cdo_id, B_TRUE,
1347 			    cds->cds_ofp, alt->cdo_symidx, alt->cdo_id, arg);
1348 		}
1349 	}
1350 
1351 	for (i = 0; i < cds->cds_noobj; i++) {
1352 		ctf_diff_obj_t *o = &cds->cds_oobj[i];
1353 		if (o->cdo_matchidx != ULONG_MAX)
1354 			continue;
1355 		cb(cds->cds_ofp, o->cdo_symidx, o->cdo_id, B_FALSE, NULL,
1356 		    ULONG_MAX, CTF_ERR, arg);
1357 	}
1358 
1359 	return (0);
1360 }
1361