dmake: do not set MAKEFLAGS=k
[unleashed/tickless.git] / usr / src / lib / libctf / common / ctf_diff.c
blobd070488bbb7a00fa64e85112f60e234328b03ea6
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.
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.
13 * Copyright (c) 2015 Joyent, Inc. All rights reserved.
17 * The following ia 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.
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:
30 * struct foo {
31 * struct foo *foo_next;
32 * };
34 * If it turns out that we were wrong, we discard our guesses.
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.
44 #include <libctf.h>
45 #include <ctf_impl.h>
46 #include <sys/debug.h>
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;
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;
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;
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;
98 #define TINDEX(tid) (tid - 1)
101 * Team Diff
103 static int ctf_diff_type(ctf_diff_t *, ctf_file_t *, ctf_id_t, ctf_file_t *,
104 ctf_id_t);
106 static int
107 ctf_diff_name(ctf_file_t *ifp, ctf_id_t iid, ctf_file_t *ofp, ctf_id_t oid)
109 const char *iname, *oname;
110 const ctf_type_t *itp, *otp;
112 if ((itp = ctf_lookup_by_id(&ifp, iid)) == NULL)
113 return (CTF_ERR);
115 if ((otp = ctf_lookup_by_id(&ofp, oid)) == NULL)
116 return (ctf_set_errno(ifp, iid));
118 iname = ctf_strptr(ifp, itp->ctt_name);
119 oname = ctf_strptr(ofp, otp->ctt_name);
121 if ((iname == NULL || oname == NULL) && (iname != oname))
122 return (B_TRUE);
124 /* Two anonymous names are the same */
125 if (iname == NULL && oname == NULL)
126 return (B_FALSE);
128 return (strcmp(iname, oname) == 0 ? B_FALSE: B_TRUE);
132 * For floats and ints
134 static int
135 ctf_diff_number(ctf_file_t *ifp, ctf_id_t iid, ctf_file_t *ofp, ctf_id_t oid)
137 ctf_encoding_t ien, den;
139 if (ctf_type_encoding(ifp, iid, &ien) != 0)
140 return (CTF_ERR);
142 if (ctf_type_encoding(ofp, oid, &den) != 0)
143 return (ctf_set_errno(ifp, iid));
145 if (bcmp(&ien, &den, sizeof (ctf_encoding_t)) != 0)
146 return (B_TRUE);
148 return (B_FALSE);
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:
155 * size_t -> ulong_t -> unsigned long
156 * size_t -> unsigned long
158 * That we'll ultimately end up treating them the same.
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)
164 ctf_id_t iref = CTF_ERR, oref = CTF_ERR;
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;
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;
180 VERIFY(iref != CTF_ERR && oref != CTF_ERR);
181 return (ctf_diff_type(cds, ifp, iref, ofp, oref));
185 * Two qualifiers are equivalent iff they point to two equivalent types.
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)
191 ctf_id_t iref, oref;
193 iref = ctf_type_reference(ifp, iid);
194 if (iref == CTF_ERR)
195 return (CTF_ERR);
197 oref = ctf_type_reference(ofp, oid);
198 if (oref == CTF_ERR)
199 return (ctf_set_errno(ifp, ctf_errno(ofp)));
201 return (ctf_diff_type(cds, ifp, iref, ofp, oref));
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.
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)
212 int ret;
213 ctf_arinfo_t iar, oar;
215 if (ctf_array_info(ifp, iid, &iar) == CTF_ERR)
216 return (CTF_ERR);
218 if (ctf_array_info(ofp, oid, &oar) == CTF_ERR)
219 return (ctf_set_errno(ifp, ctf_errno(ofp)));
221 ret = ctf_diff_type(cds, ifp, iar.ctr_contents, ofp, oar.ctr_contents);
222 if (ret != B_FALSE)
223 return (ret);
225 if (iar.ctr_nelems != oar.ctr_nelems)
226 return (B_TRUE);
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.
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);
241 return (B_FALSE);
245 * Two function pointers are the same if the following is all true:
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
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)
256 int ret, i;
257 ctf_funcinfo_t ifunc, ofunc;
258 ctf_id_t *iids, *oids;
260 if (ctf_func_info_by_id(ifp, iid, &ifunc) == CTF_ERR)
261 return (CTF_ERR);
263 if (ctf_func_info_by_id(ofp, oid, &ofunc) == CTF_ERR)
264 return (ctf_set_errno(ifp, ctf_errno(ofp)));
266 if (ifunc.ctc_argc != ofunc.ctc_argc)
267 return (B_TRUE);
269 if (ifunc.ctc_flags != ofunc.ctc_flags)
270 return (B_TRUE);
272 ret = ctf_diff_type(cds, ifp, ifunc.ctc_return, ofp, ofunc.ctc_return);
273 if (ret != B_FALSE)
274 return (ret);
276 iids = ctf_alloc(sizeof (ctf_id_t) * ifunc.ctc_argc);
277 if (iids == NULL)
278 return (ctf_set_errno(ifp, ENOMEM));
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));
286 if (ctf_func_args_by_id(ifp, iid, ifunc.ctc_argc, iids) == CTF_ERR) {
287 ret = CTF_ERR;
288 goto out;
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;
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;
302 ret = B_FALSE;
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);
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.
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)
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;
327 oifp = ifp;
329 if ((itp = ctf_lookup_by_id(&ifp, iid)) == NULL)
330 return (CTF_ERR);
332 if ((otp = ctf_lookup_by_id(&ofp, oid)) == NULL)
333 return (ctf_set_errno(oifp, ctf_errno(ofp)));
335 if (ctf_type_size(ifp, iid) != ctf_type_size(ofp, oid))
336 return (B_TRUE);
338 if (LCTF_INFO_VLEN(ifp, itp->ctt_info) !=
339 LCTF_INFO_VLEN(ofp, otp->ctt_info))
340 return (B_TRUE);
342 (void) ctf_get_ctt_size(ifp, itp, &isize, &iincr);
343 (void) ctf_get_ctt_size(ofp, otp, &osize, &oincr);
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);
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);
362 * Insert our assumption that they're equal for the moment.
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;
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;
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;
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;
400 if (ioff != ooff) {
401 return (B_TRUE);
403 if (strcmp(iname, oname) != 0) {
404 return (B_TRUE);
406 ret = ctf_diff_type(cds, ifp, itype, ofp, otype);
407 if (ret != B_FALSE) {
408 return (ret);
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++;
422 return (B_FALSE);
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, their is no guarantee of ordering so we have to fall back to
429 * doing an O(N^2) scan.
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;
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;
448 /* ARGSUSED */
449 static int
450 ctf_diff_union_check_member(const char *name, ctf_id_t id, ulong_t off,
451 void *arg)
453 int ret;
454 ctf_diff_union_member_t *cdump = arg;
456 if (strcmp(name, cdump->cdum_name) != 0)
457 return (0);
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);
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);
472 return (0);
475 /* ARGSUSED */
476 static int
477 ctf_diff_union_check_fp(const char *name, ctf_id_t id, ulong_t off, void *arg)
479 int ret;
480 ctf_diff_union_member_t cdum;
481 ctf_diff_union_fp_t *cdufp = arg;
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;
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);
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)
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;
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)));
523 if (LCTF_INFO_VLEN(ifp, itp->ctt_info) !=
524 LCTF_INFO_VLEN(ofp, otp->ctt_info))
525 return (B_TRUE);
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;
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;
546 return (ret);
550 * Two enums are equivalent if they share the same underlying type and they have
551 * the same set of members.
553 static int
554 ctf_diff_enum(ctf_file_t *ifp, ctf_id_t iid, ctf_file_t *ofp, ctf_id_t oid)
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;
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)));
568 if (LCTF_INFO_VLEN(ifp, itp->ctt_info) !=
569 LCTF_INFO_VLEN(ofp, otp->ctt_info))
570 return (B_TRUE);
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);
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);
583 if (iep->cte_value != oep->cte_value)
584 return (B_TRUE);
587 return (B_FALSE);
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.
595 static int
596 ctf_diff_forward(ctf_file_t *ifp, ctf_id_t iid, ctf_file_t *ofp, ctf_id_t oid)
598 int ikind, okind;
600 ikind = ctf_type_kind(ifp, iid);
601 okind = ctf_type_kind(ofp, oid);
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);
614 * Are two types equivalent?
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)
620 int ret, ikind, okind;
622 /* Do a quick short circuit */
623 if (ifp == ofp && iid == oid)
624 return (B_FALSE);
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.
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);
641 ikind = ctf_type_kind(ifp, iid);
642 okind = ctf_type_kind(ofp, oid);
644 if (ikind != okind &&
645 ikind != CTF_K_FORWARD && okind != CTF_K_FORWARD)
646 return (B_TRUE);
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);
655 if (ikind == CTF_K_FORWARD || okind == CTF_K_FORWARD)
656 return (ctf_diff_forward(ifp, iid, ofp, oid));
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:
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.
696 ret = B_TRUE;
697 break;
698 default:
699 abort();
702 return (ret);
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.
709 * The self variable tells us whether or not we should be comparing the input
710 * ctf container with itself or not.
712 static int
713 ctf_diff_pass1(ctf_diff_t *cds, boolean_t self)
715 int i, j, diff;
716 int istart, iend, jstart, jend;
718 if (cds->cds_ifp->ctf_flags & LCTF_CHILD) {
719 istart = 0x8001;
720 iend = cds->cds_ifp->ctf_typemax + 0x8000;
721 } else {
722 istart = 1;
723 iend = cds->cds_ifp->ctf_typemax;
726 if (cds->cds_ofp->ctf_flags & LCTF_CHILD) {
727 jstart = 0x8001;
728 jend = cds->cds_ofp->ctf_typemax + 0x8000;
729 } else {
730 jstart = 1;
731 jend = cds->cds_ofp->ctf_typemax;
734 for (i = istart; i <= iend; i++) {
735 diff = B_TRUE;
738 * If we're doing a self diff for dedup purposes, then we want
739 * to ensure that we compare a type i with every type in the
740 * range, [ 1, i ). Yes, this does mean that when i equals 1,
741 * we won't compare anything.
743 if (self == B_TRUE) {
744 jstart = istart;
745 jend = i - 1;
747 for (j = jstart; j <= jend; j++) {
748 ctf_diff_guess_t *cdg, *tofree;
750 ASSERT(cds->cds_guess == NULL);
751 diff = ctf_diff_type(cds, cds->cds_ifp, i,
752 cds->cds_ofp, j);
753 if (diff == CTF_ERR)
754 return (CTF_ERR);
756 /* Clean up our guesses */
757 cdg = cds->cds_guess;
758 cds->cds_guess = NULL;
759 while (cdg != NULL) {
760 if (diff == B_TRUE) {
761 cds->cds_forward[TINDEX(cdg->cdg_iid)] =
763 cds->cds_reverse[TINDEX(cdg->cdg_oid)] =
766 tofree = cdg;
767 cdg = cdg->cdg_next;
768 ctf_free(tofree, sizeof (ctf_diff_guess_t));
771 /* Found a hit, update the tables */
772 if (diff == B_FALSE) {
773 cds->cds_forward[TINDEX(i)] = j;
774 if (cds->cds_reverse[TINDEX(j)] == 0)
775 cds->cds_reverse[TINDEX(j)] = i;
776 break;
780 /* Call the callback at this point */
781 if (diff == B_TRUE) {
782 cds->cds_forward[TINDEX(i)] = CTF_ERR;
783 cds->cds_func(cds->cds_ifp, i, B_FALSE, NULL, CTF_ERR,
784 cds->cds_arg);
785 } else {
786 cds->cds_func(cds->cds_ifp, i, B_TRUE, cds->cds_ofp, j,
787 cds->cds_arg);
791 return (0);
795 * Now we need to walk the second container and emit anything that we didn't
796 * find as common in the first pass.
798 static int
799 ctf_diff_pass2(ctf_diff_t *cds)
801 int i, start, end;
803 start = 0x1;
804 end = cds->cds_ofp->ctf_typemax;
805 if (cds->cds_ofp->ctf_flags & LCTF_CHILD) {
806 start += 0x8000;
807 end += 0x8000;
810 for (i = start; i <= end; i++) {
811 if (cds->cds_reverse[TINDEX(i)] != 0)
812 continue;
813 cds->cds_func(cds->cds_ofp, i, B_FALSE, NULL, CTF_ERR,
814 cds->cds_arg);
817 return (0);
821 ctf_diff_init(ctf_file_t *ifp, ctf_file_t *ofp, ctf_diff_t **cdsp)
823 ctf_diff_t *cds;
824 size_t fsize, rsize;
826 cds = ctf_alloc(sizeof (ctf_diff_t));
827 if (cds == NULL)
828 return (ctf_set_errno(ifp, ENOMEM));
830 bzero(cds, sizeof (ctf_diff_t));
831 cds->cds_ifp = ifp;
832 cds->cds_ofp = ofp;
834 fsize = sizeof (ctf_id_t) * ifp->ctf_typemax;
835 rsize = sizeof (ctf_id_t) * ofp->ctf_typemax;
836 if (ifp->ctf_flags & LCTF_CHILD)
837 fsize += 0x8000 * sizeof (ctf_id_t);
838 if (ofp->ctf_flags & LCTF_CHILD)
839 rsize += 0x8000 * sizeof (ctf_id_t);
841 cds->cds_forward = ctf_alloc(fsize);
842 if (cds->cds_forward == NULL) {
843 ctf_free(cds, sizeof (ctf_diff_t));
844 return (ctf_set_errno(ifp, ENOMEM));
846 cds->cds_fsize = fsize;
847 cds->cds_reverse = ctf_alloc(rsize);
848 if (cds->cds_reverse == NULL) {
849 ctf_free(cds->cds_forward, fsize);
850 ctf_free(cds, sizeof (ctf_diff_t));
851 return (ctf_set_errno(ifp, ENOMEM));
853 cds->cds_rsize = rsize;
854 bzero(cds->cds_forward, fsize);
855 bzero(cds->cds_reverse, rsize);
857 cds->cds_ifp->ctf_refcnt++;
858 cds->cds_ofp->ctf_refcnt++;
859 *cdsp = cds;
860 return (0);
864 ctf_diff_types(ctf_diff_t *cds, ctf_diff_type_f cb, void *arg)
866 int ret;
868 cds->cds_func = cb;
869 cds->cds_arg = arg;
871 ret = ctf_diff_pass1(cds, B_FALSE);
872 if (ret == 0)
873 ret = ctf_diff_pass2(cds);
875 cds->cds_func = NULL;
876 cds->cds_arg = NULL;
877 cds->cds_tvalid = B_TRUE;
878 return (ret);
882 * Do a diff where we're comparing a container with itself. In other words we'd
883 * like to know what types are actually duplicates of existing types in the
884 * container.
886 * Note this should remain private to libctf and not be exported in the public
887 * mapfile for the time being.
890 ctf_diff_self(ctf_diff_t *cds, ctf_diff_type_f cb, void *arg)
892 if (cds->cds_ifp != cds->cds_ofp)
893 return (EINVAL);
895 cds->cds_func = cb;
896 cds->cds_arg = arg;
898 return (ctf_diff_pass1(cds, B_TRUE));
902 void
903 ctf_diff_fini(ctf_diff_t *cds)
905 ctf_diff_guess_t *cdg;
906 size_t fsize, rsize;
908 if (cds == NULL)
909 return;
911 cds->cds_ifp->ctf_refcnt--;
912 cds->cds_ofp->ctf_refcnt--;
914 fsize = sizeof (ctf_id_t) * cds->cds_ifp->ctf_typemax;
915 rsize = sizeof (ctf_id_t) * cds->cds_ofp->ctf_typemax;
916 if (cds->cds_ifp->ctf_flags & LCTF_CHILD)
917 fsize += 0x8000 * sizeof (ctf_id_t);
918 if (cds->cds_ofp->ctf_flags & LCTF_CHILD)
919 rsize += 0x8000 * sizeof (ctf_id_t);
921 if (cds->cds_ifuncs != NULL)
922 ctf_free(cds->cds_ifuncs,
923 sizeof (ctf_diff_func_t) * cds->cds_nifuncs);
924 if (cds->cds_ofuncs != NULL)
925 ctf_free(cds->cds_ofuncs,
926 sizeof (ctf_diff_func_t) * cds->cds_nofuncs);
927 if (cds->cds_iobj != NULL)
928 ctf_free(cds->cds_iobj,
929 sizeof (ctf_diff_obj_t) * cds->cds_niobj);
930 if (cds->cds_oobj != NULL)
931 ctf_free(cds->cds_oobj,
932 sizeof (ctf_diff_obj_t) * cds->cds_noobj);
933 cdg = cds->cds_guess;
934 while (cdg != NULL) {
935 ctf_diff_guess_t *tofree = cdg;
936 cdg = cdg->cdg_next;
937 ctf_free(tofree, sizeof (ctf_diff_guess_t));
939 if (cds->cds_forward != NULL)
940 ctf_free(cds->cds_forward, cds->cds_fsize);
941 if (cds->cds_reverse != NULL)
942 ctf_free(cds->cds_reverse, cds->cds_rsize);
943 ctf_free(cds, sizeof (ctf_diff_t));
946 uint_t
947 ctf_diff_getflags(ctf_diff_t *cds)
949 return (cds->cds_flags);
953 ctf_diff_setflags(ctf_diff_t *cds, uint_t flags)
955 if ((flags & ~CTF_DIFF_F_IGNORE_INTNAMES) != 0)
956 return (ctf_set_errno(cds->cds_ifp, EINVAL));
958 cds->cds_flags = flags;
959 return (0);
962 static boolean_t
963 ctf_diff_symid(ctf_diff_t *cds, ctf_id_t iid, ctf_id_t oid)
965 ctf_file_t *ifp, *ofp;
967 ifp = cds->cds_ifp;
968 ofp = cds->cds_ofp;
971 * If we have parent containers on the scene here, we need to go through
972 * and do a full diff check because while a diff for types will not
973 * actually go through and check types in the parent container.
975 if (iid == 0 || oid == 0)
976 return (iid == oid ? B_FALSE: B_TRUE);
978 if (!(ifp->ctf_flags & LCTF_CHILD) && !(ofp->ctf_flags & LCTF_CHILD)) {
979 if (cds->cds_forward[TINDEX(iid)] != oid)
980 return (B_TRUE);
981 return (B_FALSE);
984 return (ctf_diff_type(cds, ifp, iid, ofp, oid));
987 /* ARGSUSED */
988 static void
989 ctf_diff_void_cb(ctf_file_t *ifp, ctf_id_t iid, boolean_t same, ctf_file_t *ofp,
990 ctf_id_t oid, void *arg)
994 /* ARGSUSED */
995 static int
996 ctf_diff_func_count(const char *name, ulong_t symidx, ctf_funcinfo_t *fip,
997 void *arg)
999 uint32_t *ip = arg;
1001 *ip = *ip + 1;
1002 return (0);
1005 /* ARGSUSED */
1006 static int
1007 ctf_diff_func_fill_cb(const char *name, ulong_t symidx, ctf_funcinfo_t *fip,
1008 void *arg)
1010 uint_t *next, max;
1011 ctf_diff_func_t *funcptr;
1012 ctf_diff_t *cds = arg;
1014 if (cds->cds_ffillip == B_TRUE) {
1015 max = cds->cds_nifuncs;
1016 next = &cds->cds_nextifunc;
1017 funcptr = cds->cds_ifuncs + *next;
1018 } else {
1019 max = cds->cds_nofuncs;
1020 next = &cds->cds_nextofunc;
1021 funcptr = cds->cds_ofuncs + *next;
1025 VERIFY(*next < max);
1026 funcptr->cdf_name = name;
1027 funcptr->cdf_symidx = symidx;
1028 funcptr->cdf_matchidx = ULONG_MAX;
1029 *next = *next + 1;
1031 return (0);
1035 ctf_diff_func_fill(ctf_diff_t *cds)
1037 int ret;
1038 uint32_t ifcount, ofcount, idcnt, cti;
1039 ulong_t i, j;
1040 ctf_id_t *iids, *oids;
1042 ifcount = 0;
1043 ofcount = 0;
1044 idcnt = 0;
1045 iids = NULL;
1046 oids = NULL;
1048 ret = ctf_function_iter(cds->cds_ifp, ctf_diff_func_count, &ifcount);
1049 if (ret != 0)
1050 return (ret);
1051 ret = ctf_function_iter(cds->cds_ofp, ctf_diff_func_count, &ofcount);
1052 if (ret != 0)
1053 return (ret);
1055 cds->cds_ifuncs = ctf_alloc(sizeof (ctf_diff_func_t) * ifcount);
1056 if (cds->cds_ifuncs == NULL)
1057 return (ctf_set_errno(cds->cds_ifp, ENOMEM));
1059 cds->cds_nifuncs = ifcount;
1060 cds->cds_nextifunc = 0;
1062 cds->cds_ofuncs = ctf_alloc(sizeof (ctf_diff_func_t) * ofcount);
1063 if (cds->cds_ofuncs == NULL)
1064 return (ctf_set_errno(cds->cds_ifp, ENOMEM));
1066 cds->cds_nofuncs = ofcount;
1067 cds->cds_nextofunc = 0;
1069 cds->cds_ffillip = B_TRUE;
1070 if ((ret = ctf_function_iter(cds->cds_ifp, ctf_diff_func_fill_cb,
1071 cds)) != 0)
1072 return (ret);
1074 cds->cds_ffillip = B_FALSE;
1075 if ((ret = ctf_function_iter(cds->cds_ofp, ctf_diff_func_fill_cb,
1076 cds)) != 0)
1077 return (ret);
1080 * Everything is initialized to not match. This could probably be faster
1081 * with something that used a hash. But this part of the diff isn't used
1082 * by merge.
1084 for (i = 0; i < cds->cds_nifuncs; i++) {
1085 for (j = 0; j < cds->cds_nofuncs; j++) {
1086 ctf_diff_func_t *ifd, *ofd;
1087 ctf_funcinfo_t ifip, ofip;
1088 boolean_t match;
1090 ifd = &cds->cds_ifuncs[i];
1091 ofd = &cds->cds_ofuncs[j];
1092 if (strcmp(ifd->cdf_name, ofd->cdf_name) != 0)
1093 continue;
1095 ret = ctf_func_info(cds->cds_ifp, ifd->cdf_symidx,
1096 &ifip);
1097 if (ret != 0)
1098 goto out;
1099 ret = ctf_func_info(cds->cds_ofp, ofd->cdf_symidx,
1100 &ofip);
1101 if (ret != 0) {
1102 ret = ctf_set_errno(cds->cds_ifp,
1103 ctf_errno(cds->cds_ofp));
1104 goto out;
1107 if (ifip.ctc_argc != ofip.ctc_argc &&
1108 ifip.ctc_flags != ofip.ctc_flags)
1109 continue;
1111 /* Validate return type and arguments are the same */
1112 if (ctf_diff_symid(cds, ifip.ctc_return,
1113 ofip.ctc_return))
1114 continue;
1116 if (ifip.ctc_argc > idcnt) {
1117 if (iids != NULL)
1118 ctf_free(iids,
1119 sizeof (ctf_id_t) * idcnt);
1120 if (oids != NULL)
1121 ctf_free(oids,
1122 sizeof (ctf_id_t) * idcnt);
1123 iids = oids = NULL;
1124 idcnt = ifip.ctc_argc;
1125 iids = ctf_alloc(sizeof (ctf_id_t) * idcnt);
1126 if (iids == NULL) {
1127 ret = ctf_set_errno(cds->cds_ifp,
1128 ENOMEM);
1129 goto out;
1131 oids = ctf_alloc(sizeof (ctf_id_t) * idcnt);
1132 if (iids == NULL) {
1133 ret = ctf_set_errno(cds->cds_ifp,
1134 ENOMEM);
1135 goto out;
1139 if ((ret = ctf_func_args(cds->cds_ifp, ifd->cdf_symidx,
1140 ifip.ctc_argc, iids)) != 0)
1141 goto out;
1142 if ((ret = ctf_func_args(cds->cds_ofp, ofd->cdf_symidx,
1143 ofip.ctc_argc, oids)) != 0)
1144 goto out;
1146 match = B_TRUE;
1147 for (cti = 0; cti < ifip.ctc_argc; cti++) {
1148 if (ctf_diff_symid(cds, iids[cti], oids[cti])) {
1149 match = B_FALSE;
1150 break;
1154 if (match == B_FALSE)
1155 continue;
1157 ifd->cdf_matchidx = j;
1158 ofd->cdf_matchidx = i;
1159 break;
1163 ret = 0;
1165 out:
1166 if (iids != NULL)
1167 ctf_free(iids, sizeof (ctf_id_t) * idcnt);
1168 if (oids != NULL)
1169 ctf_free(oids, sizeof (ctf_id_t) * idcnt);
1171 return (ret);
1175 * In general, two functions are the same, if they have the same name and their
1176 * arguments have the same types, including the return type. Like types, we
1177 * basically have to do this in two passes. In the first phase we walk every
1178 * type in the first container and try to find a match in the second.
1181 ctf_diff_functions(ctf_diff_t *cds, ctf_diff_func_f cb, void *arg)
1183 int ret;
1184 ulong_t i;
1186 if (cds->cds_tvalid == B_FALSE) {
1187 if ((ret = ctf_diff_types(cds, ctf_diff_void_cb, NULL)) != 0)
1188 return (ret);
1191 if (cds->cds_fvalid == B_FALSE) {
1192 if ((ret = ctf_diff_func_fill(cds)) != 0)
1193 return (ret);
1194 cds->cds_fvalid = B_TRUE;
1197 for (i = 0; i < cds->cds_nifuncs; i++) {
1198 if (cds->cds_ifuncs[i].cdf_matchidx == ULONG_MAX) {
1199 cb(cds->cds_ifp, cds->cds_ifuncs[i].cdf_symidx,
1200 B_FALSE, NULL, ULONG_MAX, arg);
1201 } else {
1202 ulong_t idx = cds->cds_ifuncs[i].cdf_matchidx;
1203 cb(cds->cds_ifp, cds->cds_ifuncs[i].cdf_symidx, B_TRUE,
1204 cds->cds_ofp, cds->cds_ofuncs[idx].cdf_symidx, arg);
1208 for (i = 0; i < cds->cds_nofuncs; i++) {
1209 if (cds->cds_ofuncs[i].cdf_matchidx != ULONG_MAX)
1210 continue;
1211 cb(cds->cds_ofp, cds->cds_ofuncs[i].cdf_symidx, B_FALSE,
1212 NULL, ULONG_MAX, arg);
1215 return (0);
1218 static int
1219 ctf_diff_obj_fill_cb(const char *name, ctf_id_t id, ulong_t symidx, void *arg)
1221 uint_t *next, max;
1222 ctf_diff_obj_t *objptr;
1223 ctf_diff_t *cds = arg;
1225 if (cds->cds_ofillip == B_TRUE) {
1226 max = cds->cds_niobj;
1227 next = &cds->cds_nextiobj;
1228 objptr = cds->cds_iobj + *next;
1229 } else {
1230 max = cds->cds_noobj;
1231 next = &cds->cds_nextoobj;
1232 objptr = cds->cds_oobj+ *next;
1236 VERIFY(*next < max);
1237 objptr->cdo_name = name;
1238 objptr->cdo_symidx = symidx;
1239 objptr->cdo_id = id;
1240 objptr->cdo_matchidx = ULONG_MAX;
1241 *next = *next + 1;
1243 return (0);
1246 /* ARGSUSED */
1247 static int
1248 ctf_diff_obj_count(const char *name, ctf_id_t id, ulong_t symidx, void *arg)
1250 uint32_t *count = arg;
1252 *count = *count + 1;
1254 return (0);
1258 static int
1259 ctf_diff_obj_fill(ctf_diff_t *cds)
1261 int ret;
1262 uint32_t iocount, oocount;
1263 ulong_t i, j;
1265 iocount = 0;
1266 oocount = 0;
1268 ret = ctf_object_iter(cds->cds_ifp, ctf_diff_obj_count, &iocount);
1269 if (ret != 0)
1270 return (ret);
1272 ret = ctf_object_iter(cds->cds_ofp, ctf_diff_obj_count, &oocount);
1273 if (ret != 0)
1274 return (ret);
1276 cds->cds_iobj = ctf_alloc(sizeof (ctf_diff_obj_t) * iocount);
1277 if (cds->cds_iobj == NULL)
1278 return (ctf_set_errno(cds->cds_ifp, ENOMEM));
1279 cds->cds_niobj = iocount;
1280 cds->cds_nextiobj = 0;
1282 cds->cds_oobj = ctf_alloc(sizeof (ctf_diff_obj_t) * oocount);
1283 if (cds->cds_oobj == NULL)
1284 return (ctf_set_errno(cds->cds_ifp, ENOMEM));
1285 cds->cds_noobj = oocount;
1286 cds->cds_nextoobj = 0;
1288 cds->cds_ofillip = B_TRUE;
1289 if ((ret = ctf_object_iter(cds->cds_ifp, ctf_diff_obj_fill_cb,
1290 cds)) != 0)
1291 return (ret);
1293 cds->cds_ofillip = B_FALSE;
1294 if ((ret = ctf_object_iter(cds->cds_ofp, ctf_diff_obj_fill_cb,
1295 cds)) != 0)
1296 return (ret);
1298 for (i = 0; i < cds->cds_niobj; i++) {
1299 for (j = 0; j < cds->cds_noobj; j++) {
1300 ctf_diff_obj_t *id, *od;
1302 id = &cds->cds_iobj[i];
1303 od = &cds->cds_oobj[j];
1305 if (id->cdo_name == NULL || od->cdo_name == NULL)
1306 continue;
1307 if (strcmp(id->cdo_name, od->cdo_name) != 0)
1308 continue;
1310 if (ctf_diff_symid(cds, id->cdo_id, od->cdo_id)) {
1311 continue;
1314 id->cdo_matchidx = j;
1315 od->cdo_matchidx = i;
1316 break;
1320 return (0);
1324 ctf_diff_objects(ctf_diff_t *cds, ctf_diff_obj_f cb, void *arg)
1326 int ret;
1327 ulong_t i;
1329 if (cds->cds_tvalid == B_FALSE) {
1330 if ((ret = ctf_diff_types(cds, ctf_diff_void_cb, NULL)) != 0)
1331 return (ret);
1334 if (cds->cds_ovalid == B_FALSE) {
1335 if ((ret = ctf_diff_obj_fill(cds)) != 0)
1336 return (ret);
1337 cds->cds_ovalid = B_TRUE;
1340 for (i = 0; i < cds->cds_niobj; i++) {
1341 ctf_diff_obj_t *o = &cds->cds_iobj[i];
1343 if (cds->cds_iobj[i].cdo_matchidx == ULONG_MAX) {
1344 cb(cds->cds_ifp, o->cdo_symidx, o->cdo_id, B_FALSE,
1345 NULL, ULONG_MAX, CTF_ERR, arg);
1346 } else {
1347 ctf_diff_obj_t *alt = &cds->cds_oobj[o->cdo_matchidx];
1348 cb(cds->cds_ifp, o->cdo_symidx, o->cdo_id, B_TRUE,
1349 cds->cds_ofp, alt->cdo_symidx, alt->cdo_id, arg);
1353 for (i = 0; i < cds->cds_noobj; i++) {
1354 ctf_diff_obj_t *o = &cds->cds_oobj[i];
1355 if (o->cdo_matchidx != ULONG_MAX)
1356 continue;
1357 cb(cds->cds_ofp, o->cdo_symidx, o->cdo_id, B_FALSE, NULL,
1358 ULONG_MAX, CTF_ERR, arg);
1361 return (0);