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