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
ctf_diff_name(ctf_file_t * ifp,ctf_id_t iid,ctf_file_t * ofp,ctf_id_t oid)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
ctf_diff_number(ctf_file_t * ifp,ctf_id_t iid,ctf_file_t * ofp,ctf_id_t oid)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
ctf_diff_typedef(ctf_diff_t * cds,ctf_file_t * ifp,ctf_id_t iid,ctf_file_t * ofp,ctf_id_t oid)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
ctf_diff_qualifier(ctf_diff_t * cds,ctf_file_t * ifp,ctf_id_t iid,ctf_file_t * ofp,ctf_id_t oid)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
ctf_diff_array(ctf_diff_t * cds,ctf_file_t * ifp,ctf_id_t iid,ctf_file_t * ofp,ctf_id_t oid)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
ctf_diff_fptr(ctf_diff_t * cds,ctf_file_t * ifp,ctf_id_t iid,ctf_file_t * ofp,ctf_id_t oid)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
ctf_diff_struct(ctf_diff_t * cds,ctf_file_t * ifp,ctf_id_t iid,ctf_file_t * ofp,ctf_id_t oid)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
ctf_diff_union_check_member(const char * name,ctf_id_t id,ulong_t off,void * arg)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
ctf_diff_union_check_fp(const char * name,ctf_id_t id,ulong_t off,void * arg)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
ctf_diff_union(ctf_diff_t * cds,ctf_file_t * ifp,ctf_id_t iid,ctf_file_t * ofp,ctf_id_t oid)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
ctf_diff_enum(ctf_file_t * ifp,ctf_id_t iid,ctf_file_t * ofp,ctf_id_t oid)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
ctf_diff_forward(ctf_file_t * ifp,ctf_id_t iid,ctf_file_t * ofp,ctf_id_t oid)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
ctf_diff_type(ctf_diff_t * cds,ctf_file_t * ifp,ctf_id_t iid,ctf_file_t * ofp,ctf_id_t oid)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
ctf_diff_pass1(ctf_diff_t * cds,boolean_t self)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
ctf_diff_pass2(ctf_diff_t * cds)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
ctf_diff_init(ctf_file_t * ifp,ctf_file_t * ofp,ctf_diff_t ** cdsp)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
ctf_diff_types(ctf_diff_t * cds,ctf_diff_type_f cb,void * arg)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
ctf_diff_self(ctf_diff_t * cds,ctf_diff_type_f cb,void * arg)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
ctf_diff_fini(ctf_diff_t * cds)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
ctf_diff_getflags(ctf_diff_t * cds)945 ctf_diff_getflags(ctf_diff_t *cds)
946 {
947 return (cds->cds_flags);
948 }
949
950 int
ctf_diff_setflags(ctf_diff_t * cds,uint_t flags)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
ctf_diff_symid(ctf_diff_t * cds,ctf_id_t iid,ctf_id_t oid)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
ctf_diff_void_cb(ctf_file_t * ifp,ctf_id_t iid,boolean_t same,ctf_file_t * ofp,ctf_id_t oid,void * arg)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
ctf_diff_func_count(const char * name,ulong_t symidx,ctf_funcinfo_t * fip,void * arg)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
ctf_diff_func_fill_cb(const char * name,ulong_t symidx,ctf_funcinfo_t * fip,void * arg)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
ctf_diff_func_fill(ctf_diff_t * cds)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
ctf_diff_functions(ctf_diff_t * cds,ctf_diff_func_f cb,void * arg)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
ctf_diff_obj_fill_cb(const char * name,ctf_id_t id,ulong_t symidx,void * arg)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
ctf_diff_obj_count(const char * name,ctf_id_t id,ulong_t symidx,void * arg)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
ctf_diff_obj_fill(ctf_diff_t * cds)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
ctf_diff_objects(ctf_diff_t * cds,ctf_diff_obj_f cb,void * arg)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