prototype.c revision f9f3df4c9e232dc06b769daa5edf489675c5cdfc
1/* 2 * This file is part of ltrace. 3 * Copyright (C) 2012 Petr Machata, Red Hat Inc. 4 * 5 * This program is free software; you can redistribute it and/or 6 * modify it under the terms of the GNU General Public License as 7 * published by the Free Software Foundation; either version 2 of the 8 * License, or (at your option) any later version. 9 * 10 * This program is distributed in the hope that it will be useful, but 11 * WITHOUT ANY WARRANTY; without even the implied warranty of 12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 13 * General Public License for more details. 14 * 15 * You should have received a copy of the GNU General Public License 16 * along with this program; if not, write to the Free Software 17 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 18 * 02110-1301 USA 19 */ 20 21#include <alloca.h> 22#include <errno.h> 23#include <stdlib.h> 24#include <string.h> 25#include <stdio.h> 26 27#include "callback.h" 28#include "param.h" 29#include "prototype.h" 30#include "type.h" 31#include "options.h" 32#include "read_config_file.h" 33#include "backend.h" 34 35struct protolib_cache g_protocache; 36static struct protolib legacy_typedefs; 37 38void 39prototype_init(struct prototype *proto) 40{ 41 VECT_INIT(&proto->params, struct param); 42 43 proto->return_info = NULL; 44 proto->own_return_info = 0; 45} 46 47static void 48param_destroy_cb(struct param *param, void *data) 49{ 50 param_destroy(param); 51} 52 53void 54prototype_destroy(struct prototype *proto) 55{ 56 if (proto == NULL) 57 return; 58 if (proto->own_return_info) { 59 type_destroy(proto->return_info); 60 free(proto->return_info); 61 } 62 63 VECT_DESTROY(&proto->params, struct param, ¶m_destroy_cb, NULL); 64} 65 66int 67prototype_push_param(struct prototype *proto, struct param *param) 68{ 69 return VECT_PUSHBACK(&proto->params, param); 70} 71 72size_t 73prototype_num_params(struct prototype *proto) 74{ 75 return vect_size(&proto->params); 76} 77 78void 79prototype_destroy_nth_param(struct prototype *proto, size_t n) 80{ 81 assert(n < prototype_num_params(proto)); 82 VECT_ERASE(&proto->params, struct param, n, n+1, 83 ¶m_destroy_cb, NULL); 84} 85 86struct param * 87prototype_get_nth_param(struct prototype *proto, size_t n) 88{ 89 assert(n < prototype_num_params(proto)); 90 return VECT_ELEMENT(&proto->params, struct param, n); 91} 92 93struct each_param_data { 94 struct prototype *proto; 95 enum callback_status (*cb)(struct prototype *, struct param *, void *); 96 void *data; 97}; 98 99static enum callback_status 100each_param_cb(struct param *param, void *data) 101{ 102 struct each_param_data *cb_data = data; 103 return (cb_data->cb)(cb_data->proto, param, cb_data->data); 104} 105 106struct param * 107prototype_each_param(struct prototype *proto, struct param *start_after, 108 enum callback_status (*cb)(struct prototype *, 109 struct param *, void *), 110 void *data) 111{ 112 struct each_param_data cb_data = { proto, cb, data }; 113 return VECT_EACH(&proto->params, struct param, start_after, 114 &each_param_cb, &cb_data); 115} 116 117void 118named_type_init(struct named_type *named, 119 struct arg_type_info *info, int own_type) 120{ 121 named->info = info; 122 named->own_type = own_type; 123 named->forward = 0; 124} 125 126void 127named_type_destroy(struct named_type *named) 128{ 129 if (named->own_type) { 130 type_destroy(named->info); 131 free(named->info); 132 } 133} 134 135void 136protolib_init(struct protolib *plib) 137{ 138 DICT_INIT(&plib->prototypes, const char *, struct prototype, 139 dict_hash_string, dict_eq_string, NULL); 140 141 DICT_INIT(&plib->named_types, const char *, struct named_type, 142 dict_hash_string, dict_eq_string, NULL); 143 144 VECT_INIT(&plib->imports, struct protolib *); 145 146 plib->refs = 0; 147} 148 149static void 150destroy_prototype_cb(struct prototype *proto, void *data) 151{ 152 prototype_destroy(proto); 153} 154 155static void 156destroy_named_type_cb(struct named_type *named, void *data) 157{ 158 named_type_destroy(named); 159} 160 161void 162protolib_destroy(struct protolib *plib) 163{ 164 assert(plib->refs == 0); 165 166 VECT_DESTROY(&plib->imports, struct prototype *, NULL, NULL); 167 168 DICT_DESTROY(&plib->prototypes, const char *, struct prototype, 169 dict_dtor_string, destroy_prototype_cb, NULL); 170 171 DICT_DESTROY(&plib->named_types, const char *, struct named_type, 172 dict_dtor_string, destroy_named_type_cb, NULL); 173} 174 175static struct protolib ** 176each_import(struct protolib *plib, struct protolib **start_after, 177 enum callback_status (*cb)(struct protolib **, void *), void *data) 178{ 179 assert(plib != NULL); 180 return VECT_EACH(&plib->imports, struct protolib *, 181 start_after, cb, data); 182} 183 184static enum callback_status 185is_or_imports(struct protolib **plibp, void *data) 186{ 187 assert(plibp != NULL); 188 assert(*plibp != NULL); 189 struct protolib *import = data; 190 if (*plibp == import 191 || each_import(*plibp, NULL, &is_or_imports, import) != NULL) 192 return CBS_STOP; 193 else 194 return CBS_CONT; 195} 196 197int 198protolib_add_import(struct protolib *plib, struct protolib *import) 199{ 200 assert(plib != NULL); 201 assert(import != NULL); 202 if (is_or_imports(&import, plib) == CBS_STOP) { 203 fprintf(stderr, "Recursive import rejected.\n"); 204 return -2; 205 } 206 207 return VECT_PUSHBACK(&plib->imports, &import) < 0 ? -1 : 0; 208} 209 210static int 211clone_if_not_own(const char **strp, int own) 212{ 213 assert(*strp != NULL); 214 215 if (!own) { 216 *strp = strdup(*strp); 217 if (*strp == NULL) 218 return -1; 219 } 220 221 return 0; 222} 223 224static int 225bailout(const char *name, int own) 226{ 227 int save_errno = errno; 228 if (own) 229 free((char *)name); 230 errno = save_errno; 231 return -1; 232} 233 234int 235protolib_add_prototype(struct protolib *plib, const char *name, int own_name, 236 struct prototype *proto) 237{ 238 assert(plib != NULL); 239 if (clone_if_not_own(&name, own_name) < 0) 240 return -1; 241 if (DICT_INSERT(&plib->prototypes, &name, proto) < 0) 242 return bailout(name, own_name); 243 return 0; 244} 245 246int 247protolib_add_named_type(struct protolib *plib, const char *name, int own_name, 248 struct named_type *named) 249{ 250 assert(plib != NULL); 251 if (clone_if_not_own(&name, own_name) < 0) 252 return -1; 253 if (DICT_INSERT(&plib->named_types, &name, named) < 0) 254 return bailout(name, own_name); 255 return 0; 256} 257 258struct lookup { 259 const char *name; 260 void *result; 261 struct dict *(*getter)(struct protolib *plib); 262}; 263 264static struct dict * 265get_prototypes(struct protolib *plib) 266{ 267 assert(plib != NULL); 268 return &plib->prototypes; 269} 270 271static struct dict * 272get_named_types(struct protolib *plib) 273{ 274 assert(plib != NULL); 275 return &plib->named_types; 276} 277 278static enum callback_status 279protolib_lookup_rec(struct protolib **plibp, void *data) 280{ 281 assert(plibp != NULL); 282 assert(*plibp != NULL); 283 struct lookup *lookup = data; 284 struct dict *dict = (*lookup->getter)(*plibp); 285 286 lookup->result = dict_find(dict, &lookup->name); 287 if (lookup->result != NULL) 288 return CBS_STOP; 289 290 if (each_import(*plibp, NULL, &protolib_lookup_rec, lookup) != NULL) { 291 assert(lookup->result != NULL); 292 return CBS_STOP; 293 } 294 295 return CBS_CONT; 296} 297 298static void * 299protolib_lookup(struct protolib *plib, const char *name, 300 struct dict *(*getter)(struct protolib *)) 301{ 302 assert(plib != NULL); 303 struct lookup lookup = { name, NULL, getter }; 304 if (protolib_lookup_rec(&plib, &lookup) == CBS_STOP) 305 assert(lookup.result != NULL); 306 else 307 assert(lookup.result == NULL); 308 return lookup.result; 309} 310 311struct prototype * 312protolib_lookup_prototype(struct protolib *plib, const char *name) 313{ 314 assert(plib != NULL); 315 return protolib_lookup(plib, name, &get_prototypes); 316} 317 318struct named_type * 319protolib_lookup_type(struct protolib *plib, const char *name) 320{ 321 assert(plib != NULL); 322 return protolib_lookup(plib, name, &get_named_types); 323} 324 325static void 326destroy_protolib_cb(struct protolib **plibp, void *data) 327{ 328 assert(plibp != NULL); 329 assert(*plibp != NULL); 330 331 if (--(*plibp)->refs == 0) { 332 protolib_destroy(*plibp); 333 free(*plibp); 334 } 335} 336 337void 338protolib_cache_destroy(struct protolib_cache *cache) 339{ 340 DICT_DESTROY(&cache->protolibs, const char *, struct protolib *, 341 dict_dtor_string, destroy_protolib_cb, NULL); 342} 343 344struct load_config_data { 345 struct protolib_cache *self; 346 const char *key; 347 struct protolib *result; 348}; 349 350static struct protolib * 351consider_config_dir(struct protolib_cache *cache, 352 const char *path, const char *key) 353{ 354 size_t len = sizeof ".conf"; 355 char slash[2] = {'/'}; 356 char *buf = alloca(strlen(path) + 1 + strlen(key) + len); 357 strcpy(stpcpy(stpcpy(stpcpy(buf, path), slash), key), ".conf"); 358 359 return protolib_cache_file(cache, buf, 0); 360} 361 362static enum callback_status 363consider_confdir_cb(struct opt_F_t *entry, void *d) 364{ 365 if (opt_F_get_kind(entry) != OPT_F_DIR) 366 return CBS_CONT; 367 struct load_config_data *data = d; 368 369 data->result = consider_config_dir(data->self, 370 entry->pathname, data->key); 371 return data->result != NULL ? CBS_STOP : CBS_CONT; 372} 373 374static int 375load_dash_F_dirs(struct protolib_cache *cache, 376 const char *key, struct protolib **retp) 377{ 378 struct load_config_data data = {cache, key}; 379 380 if (VECT_EACH(&opt_F, struct opt_F_t, NULL, 381 consider_confdir_cb, &data) == NULL) 382 /* Not found. That's fine. */ 383 return 0; 384 385 if (data.result == NULL) 386 /* There were errors. */ 387 return -1; 388 389 *retp = data.result; 390 return 0; 391} 392 393static int 394load_config(struct protolib_cache *cache, 395 const char *key, int private, struct protolib **retp) 396{ 397 const char **dirs = NULL; 398 if (os_get_config_dirs(private, &dirs) < 0 399 || dirs == NULL) 400 return -1; 401 402 for (; *dirs != NULL; ++dirs) { 403 struct protolib *plib = consider_config_dir(cache, *dirs, key); 404 if (plib != NULL) { 405 *retp = plib; 406 break; 407 } 408 } 409 410 return 0; 411} 412 413static int 414add_ltrace_conf(struct protolib_cache *cache) 415{ 416 /* Look into private config directories for .ltrace.conf and 417 * into system config directories for ltrace.conf. If it's 418 * found, add it to implicit import module. */ 419 struct protolib *plib = NULL; 420 const char *home = NULL; 421 if (os_get_ltrace_conf_filename(&home) < 0 422 || (home != NULL 423 && (plib = consider_config_dir(cache, home, ".ltrace")) != NULL 424 && protolib_add_import(&cache->imports, plib) < 0) 425 || (plib == NULL && load_config(cache, "ltrace", 0, &plib) < 0) 426 || (plib != NULL && protolib_add_import(&cache->imports, plib) < 0)) 427 /* N.B. If plib is non-NULL, it has been already 428 * cached. We don't therefore destroy it on 429 * failures. */ 430 return -1; 431 432 /* Never mind whether we've found anything. It's fine if the 433 * config is absent. */ 434 return 0; 435} 436 437static enum callback_status 438add_imports_cb(struct opt_F_t *entry, void *data) 439{ 440 struct protolib_cache *self = data; 441 if (opt_F_get_kind(entry) != OPT_F_FILE) 442 return CBS_CONT; 443 444 struct protolib *new_import 445 = protolib_cache_file(self, entry->pathname, 0); 446 447 if (new_import == NULL 448 || protolib_add_import(&self->imports, new_import) < 0) 449 /* N.B. If new_import is non-NULL, it has been already 450 * cached. We don't therefore destroy it on 451 * failures. */ 452 return CBS_STOP; 453 454 return CBS_CONT; 455} 456 457int 458protolib_cache_init(struct protolib_cache *cache, struct protolib *import) 459{ 460 DICT_INIT(&cache->protolibs, const char *, struct protolib *, 461 dict_hash_string, dict_eq_string, NULL); 462 protolib_init(&cache->imports); 463 464 /* At this point the cache is consistent. This is important, 465 * because next we will use it to cache files that we load 466 * due to -F. 467 * 468 * But we are about to construct the implicit import module, 469 * which means this module can't be itself imported to the 470 * files that we load now. So remember that we are still 471 * bootstrapping. */ 472 cache->bootstrap = 1; 473 474 if (protolib_add_import(&cache->imports, &legacy_typedefs) < 0 475 || (import != NULL 476 && protolib_add_import(&cache->imports, import) < 0) 477 || add_ltrace_conf(cache) < 0 478 || VECT_EACH(&opt_F, struct opt_F_t, NULL, 479 add_imports_cb, cache) != NULL) { 480 protolib_cache_destroy(cache); 481 return -1; 482 } 483 484 cache->bootstrap = 0; 485 return 0; 486} 487 488static enum callback_status 489add_import_cb(struct protolib **importp, void *data) 490{ 491 struct protolib *plib = data; 492 if (protolib_add_import(plib, *importp) < 0) 493 return CBS_STOP; 494 else 495 return CBS_CONT; 496} 497 498static struct protolib * 499build_default_config(struct protolib_cache *cache, const char *key) 500{ 501 struct protolib *new_plib = malloc(sizeof(*new_plib)); 502 if (new_plib == NULL) { 503 fprintf(stderr, "Couldn't create config module %s: %s\n", 504 key, strerror(errno)); 505 return NULL; 506 } 507 508 protolib_init(new_plib); 509 510 /* If bootstrapping, copy over imports from implicit import 511 * module to new_plib. We can't reference the implicit 512 * import module itself, because new_plib will become part of 513 * this same implicit import module itself. */ 514 if ((cache->bootstrap && each_import(&cache->imports, NULL, 515 add_import_cb, new_plib) != NULL) 516 || (!cache->bootstrap 517 && protolib_add_import(new_plib, &cache->imports) < 0)) { 518 519 fprintf(stderr, 520 "Couldn't add imports to config module %s: %s\n", 521 key, strerror(errno)); 522 protolib_destroy(new_plib); 523 free(new_plib); 524 return NULL; 525 } 526 527 return new_plib; 528} 529 530static void 531attempt_to_cache(struct protolib_cache *cache, 532 const char *key, struct protolib *plib) 533{ 534 if (protolib_cache_protolib(cache, key, 1, plib) == 0 535 || plib == NULL) 536 /* Never mind failing to store a NULL. */ 537 return; 538 539 /* Returning a protolib that hasn't been cached would leak 540 * that protolib, but perhaps it's less bad then giving up 541 * outright. At least print an error message. */ 542 fprintf(stderr, "Couldn't cache prototype library for %s\n", key); 543} 544 545struct protolib * 546protolib_cache_search(struct protolib_cache *cache, 547 const char *key, int own_key, int allow_private) 548{ 549 struct protolib *plib = NULL; 550 if (DICT_FIND_VAL(&cache->protolibs, &key, &plib) == 0) 551 return plib; 552 553 if (clone_if_not_own(&key, own_key) < 0) { 554 fprintf(stderr, "Couldn't cache %s: %s\n", 555 key, strerror(errno)); 556 return NULL; 557 } 558 559 /* The order is: -F directories, private directories, system 560 * directories. If the config file is not found anywhere, 561 * build a default one. */ 562 if (load_dash_F_dirs(cache, key, &plib) < 0 563 || (plib == NULL && allow_private 564 && load_config(cache, key, 1, &plib) < 0) 565 || (plib == NULL 566 && load_config(cache, key, 0, &plib) < 0) 567 || (plib == NULL 568 && (plib = build_default_config(cache, key)) == NULL)) 569 fprintf(stderr, 570 "Error occurred when attempting to load a prototype " 571 "library for %s.\n", key); 572 573 /* Whatever came out of this (even NULL), store it in the 574 * cache. */ 575 attempt_to_cache(cache, key, plib); 576 return plib; 577} 578 579struct protolib * 580protolib_cache_file(struct protolib_cache *cache, 581 const char *filename, int own_filename) 582{ 583 { 584 struct protolib *plib; 585 if (DICT_FIND_VAL(&cache->protolibs, &filename, &plib) == 0) 586 return plib; 587 } 588 589 FILE *stream = fopen(filename, "r"); 590 if (stream == NULL) 591 return NULL; 592 593 if (clone_if_not_own(&filename, own_filename) < 0) { 594 fprintf(stderr, "Couldn't cache %s: %s\n", 595 filename, strerror(errno)); 596 fclose(stream); 597 return NULL; 598 } 599 600 struct protolib *new_plib = build_default_config(cache, filename); 601 if (new_plib == NULL 602 || read_config_file(stream, filename, new_plib) < 0) { 603 fclose(stream); 604 if (own_filename) 605 free((char *)filename); 606 if (new_plib != NULL) { 607 protolib_destroy(new_plib); 608 free(new_plib); 609 } 610 return NULL; 611 } 612 613 attempt_to_cache(cache, filename, new_plib); 614 fclose(stream); 615 return new_plib; 616} 617 618int 619protolib_cache_protolib(struct protolib_cache *cache, 620 const char *filename, int own_filename, 621 struct protolib *plib) 622{ 623 if (clone_if_not_own(&filename, own_filename) < 0) { 624 fprintf(stderr, "Couldn't cache %s: %s\n", 625 filename, strerror(errno)); 626 return -1; 627 } 628 629 int rc = DICT_INSERT(&cache->protolibs, &filename, &plib); 630 if (rc < 0 && own_filename) 631 free((char *)filename); 632 if (rc == 0) 633 plib->refs++; 634 return rc; 635} 636 637void 638init_global_config(void) 639{ 640 protolib_init(&legacy_typedefs); 641 struct arg_type_info *void_info = type_get_simple(ARGTYPE_VOID); 642 static struct arg_type_info ptr_info; 643 type_init_pointer(&ptr_info, void_info, 0); 644 645 static struct named_type voidptr_type; 646 named_type_init(&voidptr_type, &ptr_info, 0); 647 648 /* Build legacy typedefs first. This is used by 649 * protolib_cache_init call below. */ 650 if (protolib_add_named_type(&legacy_typedefs, "addr", 0, 651 &voidptr_type) < 0 652 || protolib_add_named_type(&legacy_typedefs, "file", 0, 653 &voidptr_type) < 0) { 654 fprintf(stderr, 655 "Couldn't initialize aliases `addr' and `file'.\n"); 656 657 /* Since global config was not yet initialized, do an 658 * early exit. */ 659 fflush(stderr); 660 _Exit(1); 661 } 662 663 if (protolib_cache_init(&g_protocache, NULL) < 0) { 664 fprintf(stderr, "Couldn't init prototype cache\n"); 665 fflush(stderr); 666 _Exit(1); 667 } 668} 669 670void 671destroy_global_config(void) 672{ 673 protolib_cache_destroy(&g_protocache); 674 protolib_destroy(&legacy_typedefs); 675} 676