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