1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License, Version 1.0 only 6 * (the "License"). You may not use this file except in compliance 7 * with the License. 8 * 9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 10 * or http://www.opensolaris.org/os/licensing. 11 * See the License for the specific language governing permissions 12 * and limitations under the License. 13 * 14 * When distributing Covered Code, include this CDDL HEADER in each 15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 16 * If applicable, add the following below this CDDL HEADER, with the 17 * fields enclosed by brackets "[]" replaced with your own identifying 18 * information: Portions Copyright [yyyy] [name of copyright owner] 19 * 20 * CDDL HEADER END 21 */ 22 /* 23 * Copyright 2002-2003 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 */ 26 27 #pragma ident "%Z%%M% %I% %E% SMI" 28 29 #include "abi_audit.h" 30 31 /* Variables */ 32 int Debug; 33 static const char *Numstr = "01234567689."; 34 35 /* Internal functions */ 36 static int compare_ver_inc(char *, char *); 37 static void check_ver_inc(liblist_t *); 38 static void check_lib_ver(liblist_t *, char *); 39 static void check_sym_ver(symbol_t *); 40 41 /* 42 * assign_versions() will assign vt_lib_ver and vt_sym_ver for a given release. 43 * lib_ver should be the highest version of the library and sym_ver should be 44 * the lowest version this symbol was exported. 45 * The intf_check output will be read in with the highest version of the 46 * library first. So, for a new release, the lib_ver will need to be set, 47 * whereas, on subsequent calls to this function, only the sym_ver needs to be 48 * reset. 49 */ 50 51 void 52 assign_versions(liblist_t *orig_lib, liblist_t *new_lib, int rel_num) 53 { 54 char *oldlibver; 55 char *newlibver; 56 char *newsymver; 57 58 oldlibver = get_lib_ver(orig_lib, rel_num); 59 60 /* Start of a new release */ 61 if (oldlibver == NULL) { 62 newlibver = get_lib_ver(new_lib, rel_num); 63 assign_lib_ver(orig_lib, newlibver, rel_num); 64 } 65 66 newsymver = get_sym_ver(new_lib, rel_num); 67 assign_sym_ver(orig_lib, newsymver, rel_num); 68 } 69 70 /* 71 * Returns the first release which is exported. Returns FAIL if the symbol was 72 * never exported 73 */ 74 75 int 76 find_exported_release(liblist_t *lib, int first_exported) 77 { 78 int release = 0; 79 int count = 0; 80 bvlist_t *bitmask; 81 82 bitmask = get_rel_bitmask(0); 83 84 while (count < Total_relcnt) { 85 if ((bv_and(bitmask, lib->lt_release) == TRUE) && 86 (bv_and(bitmask, lib->lt_cat->ct_unexported) != TRUE)) { 87 if (first_exported) 88 return (count); 89 release = count; 90 } 91 bitmask = bv_bitmask_rshift(bitmask); 92 count ++; 93 } 94 95 /* 96 * symbol was never exported or it was scoped local during its lifetime. 97 */ 98 if ((!get_lib_ver(lib, release)) && 99 (bv_all_zero(lib->lt_cat->ct_public) == TRUE) && 100 (bv_all_zero(lib->lt_cat->ct_private) == TRUE)) { 101 free_bv_list(bitmask); 102 return (FAIL); 103 } 104 free_bv_list(bitmask); 105 return (release); 106 } 107 108 /* 109 * Version Checker recursively checks for versioning errors in each node of 110 * the tree. 111 */ 112 113 void 114 version_checker(tree_t *rootptr) 115 { 116 if (rootptr) { 117 version_checker(rootptr->tt_left); 118 check_sym_ver(rootptr->tt_sym); 119 version_checker(rootptr->tt_right); 120 } 121 } 122 123 /* 124 * check_sym_ver() will traverse through the liblist_t and ensure 125 * versioning policies are maintained in each library of a given symbol. 126 */ 127 128 static void 129 check_sym_ver(symbol_t *symbol) 130 { 131 liblist_t *lib = symbol->st_lib; 132 133 while (lib != NULL) { 134 check_lib_ver(lib, symbol->st_sym_name); 135 lib = lib->lt_next; 136 } 137 } 138 139 /* 140 * check_lib_ver() prints error if: 141 * - base version is not maintained 142 * - base_vers is not < lib_vers 143 * - version incrementing between new releases of a lib does not 144 * follow library versioning policies of incrementing by only ".1" 145 */ 146 147 static void 148 check_lib_ver(liblist_t *lib, char *sym_name) 149 { 150 int i = 0; 151 int first_vers, prev_ver_num, var; 152 char *libver; 153 char *next_libver; 154 char *prev_sym_ver, *curr_sym_ver; 155 156 if (!lib->lt_check_me) { 157 return; 158 } 159 if ((first_vers = find_exported_release(lib, 1)) == FAIL) { 160 if (Debug) 161 (void) fprintf(stderr, 162 "%s: check_lib_ver: %s was never exported", 163 program, sym_name); 164 } 165 if (!iflag) { 166 if (get_sym_ver(lib, Total_relcnt - 2) != NULL) { 167 /* symbol existed in last Solaris Release */ 168 first_vers = Total_relcnt - 2; 169 } else { 170 /* symbol is unexported in last Solaris Release */ 171 first_vers = Total_relcnt - 1; 172 } 173 } 174 curr_sym_ver = get_sym_ver(lib, first_vers); 175 prev_ver_num = first_vers; 176 177 for (i = first_vers; i < Total_relcnt; i ++) { 178 libver = get_lib_ver(lib, i); 179 180 /* 181 * flag WARNING if SUNWobsolete->SUNWprivate_m.o 182 * flag ERROR if SUNWobsolete->SUNW_m.n.o or any new version 183 * ASSERTION check to make sure it does not happen 184 */ 185 if (libver && (strstr(libver, "SUNWobsolete") != NULL) && 186 (i < Total_relcnt - 1)) { 187 next_libver = get_lib_ver(lib, i + 1); 188 if (next_libver) { 189 if (strstr(next_libver, "SUNWprivate") != NULL) 190 (void) fprintf(Msgout, "WARNING: "); 191 else 192 (void) fprintf(Msgout, "ERROR: "); 193 (void) fprintf(Msgout, 194 "%s: %s->%s: ", lib->lt_lib_name, libver, 195 next_libver); 196 (void) fprintf(Msgout, 197 "new interface introduced to the " 198 "obsolete library\n"); 199 } 200 } 201 202 /* 203 * only do base and new version checking for public symbols. 204 * current_sym_ver can be NULL for releases in which the 205 * symbol is unexported. 206 */ 207 if (!get_sym_ver(lib, i)) { 208 continue; 209 } 210 if (!curr_sym_ver) { 211 continue; 212 } 213 prev_sym_ver = curr_sym_ver; 214 curr_sym_ver = get_sym_ver(lib, i); 215 216 if ((strstr(curr_sym_ver, "SUNW_") == NULL) || 217 (strstr(prev_sym_ver, "SUNW_") == NULL)) { 218 continue; 219 } 220 221 /* 222 * ensure the base version is maintained through all 223 * releases of a library 224 */ 225 if (strcmp(curr_sym_ver, prev_sym_ver) != 0) { 226 (void) fprintf(Msgout, 227 "ERROR: %s: %s: " 228 "base version not maintained, was %s in %s, " 229 "becomes %s in ", 230 lib->lt_lib_name, sym_name, prev_sym_ver, 231 get_rel_name(prev_ver_num), curr_sym_ver); 232 if (i != (Total_relcnt - 1)) { 233 (void) fprintf(Msgout, "%s\n", 234 get_rel_name(i)); 235 } else { 236 (void) fprintf(Msgout, "current release\n"); 237 } 238 } 239 240 /* 241 * ensure new symbols are versioned with the 242 * highest lib_version. Sym's base version must equal 243 * it's highest lib_version. 244 */ 245 if (libver && 246 ((lib->lt_scenario == SCENARIO_01) || 247 (lib->lt_scenario == SCENARIO_05))) { 248 249 /* 250 * new symbols could have been introduced in update 251 * releases and they have to be versioned in micro 252 * number if only a subset of the new symbols are 253 * backported from the market release to a update 254 * or patch release. The new version name needs 255 * to be validated. 256 */ 257 if (strcmp(libver, curr_sym_ver) != 0) { 258 if (!iflag && 259 (first_vers != Total_relcnt - 1)) { 260 continue; 261 } 262 if ((var = 263 count_num_char('.', curr_sym_ver) == 1) || 264 ((var == 2) && 265 (compare_ver_inc(curr_sym_ver, libver) 266 == FAIL))) { 267 (void) fprintf(Msgout, 268 "ERROR: %s: %s:" 269 " invalid new version, %s " 270 "should be %s in ", 271 lib->lt_lib_name, sym_name, 272 curr_sym_ver, libver); 273 if (i != (Total_relcnt - 1)) { 274 (void) fprintf(Msgout, "%s\n", 275 get_rel_name(i)); 276 } else { 277 (void) fprintf(Msgout, 278 "current release\n"); 279 } 280 } 281 break; 282 } 283 } 284 prev_ver_num = i; 285 } 286 /* 287 * ensure that the version is incremented by at most ".1" 288 * between previous and new releases of a library 289 */ 290 check_ver_inc(lib); 291 } 292 293 /* 294 * It ensures the integrity of version incrementing is maintained between 295 * multiple releases of a library. If not, an error message is printed 296 */ 297 298 static void 299 check_ver_inc(liblist_t *lib) 300 { 301 char *prev_ver; 302 int prev_ver_num; 303 char *current_ver; 304 int i; 305 int first_rel; 306 307 if ((first_rel = find_exported_release(lib, 1)) == FAIL) { 308 if (Debug) 309 (void) fprintf(stderr, 310 "%s: check_ver_inc: never exported", program); 311 } 312 /* current_ver can only be NULL when a symbol is always scoped_local */ 313 if ((current_ver = get_lib_ver(lib, first_rel)) == NULL) 314 return; 315 316 /* 317 * Only check versioning for public symbols, but skip checking of 318 * SYSVABI and SISCD standards versions 319 */ 320 if ((strstr(current_ver, "SUNW_") == NULL) || 321 (first_rel+1 >= Total_relcnt)) 322 return; 323 324 /* prev_ver refers to first release this symbol was exported */ 325 prev_ver_num = first_rel; 326 327 /* 328 * compare prev_ver to current_ver by always comparing the versions 329 * pairwise. Reset prev_ver to the last version exported. 330 */ 331 for (i = first_rel + 1; i < Total_relcnt; i ++) { 332 333 if (get_lib_ver(lib, i) != NULL) { 334 prev_ver = current_ver; 335 current_ver = get_lib_ver(lib, i); 336 337 /* 338 * check to make sure that increments between releases 339 * of a lib are no greater than .1 340 */ 341 if ((strstr(current_ver, "SUNW_") != NULL) && 342 (compare_ver_inc(prev_ver, current_ver) == FAIL)) { 343 if (!iflag && (i < Total_relcnt - 1)) { 344 continue; 345 } 346 (void) fprintf(Msgout, 347 "ERROR: %s: was %s in %s, becomes %s in ", 348 lib->lt_lib_name, prev_ver, 349 get_rel_name(prev_ver_num), current_ver); 350 if (i != (Total_relcnt - 1)) { 351 (void) fprintf(Msgout, "%s:", 352 get_rel_name(i)); 353 } else { 354 (void) fprintf(Msgout, 355 "current release:"); 356 } 357 (void) fprintf(Msgout, 358 " inconsistent increment of version\n"); 359 } 360 prev_ver_num = i; 361 } 362 } 363 } 364 365 /* 366 * compare_ver_inc() checks the major, minor and micro numbers 367 * of two public version strings to ensure that the new version hasn't been 368 * incremented by more than ".1" per release. Versioning prior to SUNW_1.1 369 * is ignored. Strings matching "SUNW_m.n.o" where "o" is optional, will 370 * be checked for this version incrementing scheme. 371 */ 372 373 static int 374 compare_ver_inc(char *old_version, char *new_version) 375 { 376 char *oversion; 377 char *nversion; 378 int omajor, nmajor, ominor, nminor; 379 int omicro = 0; 380 int nmicro = 0; 381 382 /* scoped local symbols contain no version numbering */ 383 if ((strstr(old_version, "_LOCAL_") != NULL) && 384 (strstr(new_version, "_LOCAL_") != NULL)) { 385 return (SUCCEED); 386 } 387 388 if (((oversion = strpbrk(old_version, Numstr)) == NULL) || 389 ((nversion = strpbrk(new_version, Numstr)) == NULL)) { 390 return (FAIL); 391 } 392 393 /* extracting major, minor and micro number from old version */ 394 omajor = atoi(oversion); 395 if ((oversion = strchr(oversion, '.')) == NULL) { 396 return (FAIL); 397 } 398 oversion ++; 399 ominor = atoi(oversion); 400 if ((oversion = strrchr(oversion, '.')) != NULL) { 401 oversion ++; 402 omicro = atoi(oversion); 403 } 404 405 /* extracting major, minor and micro number from new version */ 406 nmajor = atoi(nversion); 407 if ((nversion = strchr(nversion, '.')) == NULL) { 408 return (FAIL); 409 } 410 nversion ++; 411 nminor = atoi(nversion); 412 if ((nversion = strrchr(nversion, '.')) != NULL) { 413 nversion ++; 414 nmicro = atoi(nversion); 415 } 416 417 /* new symbol should be versioned with major >= 1 & minor >= 1 */ 418 if (omajor == 0 && ominor == 0 && (nmajor < 1 || nminor < 1)) 419 return (FAIL); 420 421 /* ignore all versioning prior to 1.1 */ 422 if (omajor == 0 && nmajor == 1 && nminor == 1) 423 return (SUCCEED); 424 425 /* 426 * omajor < nmajor; incompatible changes within a library 427 * i.e., SUNW_1.n.o vs. SUNW_2.n.o 428 * omajor > nmajor; impossible to have within a library 429 * i.e., SUNW_2.n.o vs. SUNW_1.n.o 430 */ 431 if (omajor < nmajor || omajor > nmajor) { 432 return (FAIL); 433 434 /* omajor == nmajor */ 435 } else if (omajor == nmajor) { 436 /* 437 * m stays, n is incremented with optional o 438 */ 439 if (ominor < nminor) { 440 /* i.e., SUNW_1.2 vs. SUNW_1.4 */ 441 if (ominor + 1 != nminor) 442 return (FAIL); 443 /* i.e., SUNW_1.2 vs. SUNW_1.3.1 */ 444 /* i.e., SUNW_1.2.1 vs. SUNW_1.3.1 */ 445 else if (nmicro != 0) 446 return (FAIL); 447 else 448 return (SUCCEED); 449 /* 450 * m & n stay the same but o is incremented 451 */ 452 } else if (ominor == nminor) { 453 /* old m.n.o equals to new m.n.o */ 454 /* i.e., SUNW_1.20.3 vs. SUNW_1.20.3 */ 455 if (omicro == nmicro) 456 return (SUCCEED); 457 /* i.e., SUNW_1.20 vs. SUNW_1.20.2 */ 458 /* i.e., SUNW_1.20.1 vs. SUNW_1.20.3 */ 459 if (omicro + 1 != nmicro) 460 return (FAIL); 461 else 462 /* i.e., SUNW_1.20 vs. SUNW_1.20.1 */ 463 /* i.e., SUNW_1.8.2 vs. SUNW_1.8.3 */ 464 return (SUCCEED); 465 /* 466 * m stays & old minor > new minor version 467 * i.e., SUNW_1.21 vs. SUNW_1.20 468 */ 469 } else if (ominor > nminor) 470 return (FAIL); 471 else 472 return (SUCCEED); 473 } else { 474 /* It's impossible to reach here but just in case */ 475 (void) fprintf(Msgout, "ERROR: verschk.c:compare_ver_inc():"); 476 (void) fprintf(Msgout, "%d.%d.%d <-> %d.%d.%d\n", 477 omajor, ominor, omicro, nmajor, nminor, nmicro); 478 } 479 return (FAIL); 480 } 481