prototype.c revision 8ab151c2a1912819e77f83c154c18bc894bc5785
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); 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 enum callback_status 406add_imports_cb(struct opt_F_t *entry, void *data) 407{ 408 struct protolib_cache *self = data; 409 if (opt_F_get_kind(entry) != OPT_F_FILE) 410 return CBS_CONT; 411 412 struct protolib *new_import 413 = protolib_cache_file(self, entry->pathname); 414 415 if (new_import == NULL 416 || protolib_add_import(&self->imports, new_import) < 0) 417 /* N.B. If new_import is non-NULL, it has been already 418 * cached. We don't therefore destroy it on 419 * failures. */ 420 return CBS_STOP; 421 422 return CBS_CONT; 423} 424 425int 426protolib_cache_init(struct protolib_cache *cache, struct protolib *import) 427{ 428 DICT_INIT(&cache->protolibs, const char *, struct protolib *, 429 dict_hash_string, dict_eq_string, NULL); 430 protolib_init(&cache->imports); 431 432 /* At this point the cache is consistent. This is important, 433 * because next we will use it to cache files that we load 434 * due to -F. 435 * 436 * But we are about to construct the implicit import module, 437 * which means this module can't be itself imported to the 438 * files that we load now. So remember that we are still 439 * bootstrapping. */ 440 cache->bootstrap = 1; 441 442 if (protolib_add_import(&cache->imports, &legacy_typedefs) < 0 443 || (import != NULL 444 && protolib_add_import(&cache->imports, import) < 0) 445 || VECT_EACH(&opt_F, struct opt_F_t, NULL, 446 add_imports_cb, cache) != NULL) { 447 protolib_cache_destroy(cache); 448 return -1; 449 } 450 451 fprintf(stderr, "XXX Not looking for local and system config yet.\n"); 452 453 cache->bootstrap = 0; 454 return 0; 455} 456 457static enum callback_status 458add_import_cb(struct protolib **importp, void *data) 459{ 460 struct protolib *plib = data; 461 if (protolib_add_import(plib, *importp) < 0) 462 return CBS_STOP; 463 else 464 return CBS_CONT; 465} 466 467static struct protolib * 468build_default_config(struct protolib_cache *cache, const char *key) 469{ 470 struct protolib *new_plib = malloc(sizeof(*new_plib)); 471 if (new_plib == NULL) { 472 fprintf(stderr, "Couldn't create config module %s: %s\n", 473 key, strerror(errno)); 474 return NULL; 475 } 476 477 protolib_init(new_plib); 478 479 /* If bootstrapping, copy over imports from implicit import 480 * module to new_plib. We can't reference the implicit 481 * import module itself, because new_plib will become part of 482 * this same implicit import module itself. */ 483 if ((cache->bootstrap && each_import(&cache->imports, NULL, 484 add_import_cb, new_plib) != NULL) 485 || (!cache->bootstrap 486 && protolib_add_import(new_plib, &cache->imports) < 0)) { 487 488 fprintf(stderr, 489 "Couldn't add imports to config module %s: %s\n", 490 key, strerror(errno)); 491 protolib_destroy(new_plib); 492 free(new_plib); 493 return NULL; 494 } 495 496 return new_plib; 497} 498 499static void 500attempt_to_cache(struct protolib_cache *cache, 501 const char *key, struct protolib *plib) 502{ 503 if (protolib_cache_protolib(cache, key, plib) == 0 504 || plib == NULL) 505 /* Never mind failing to store a NULL. */ 506 return; 507 508 /* Returning a protolib that hasn't been cached would leak 509 * that protolib, but perhaps it's less bad then giving up 510 * outright. At least print an error message. */ 511 fprintf(stderr, "Couldn't cache prototype library for %s\n", key); 512} 513 514struct protolib * 515protolib_cache_search(struct protolib_cache *cache, 516 const char *key, int allow_private) 517{ 518 struct protolib *plib = NULL; 519 if (DICT_FIND_VAL(&cache->protolibs, &key, &plib) == 0) 520 return plib; 521 522 /* The order is: -F directories, private directories, system 523 * directories. If the config file is not found anywhere, 524 * build a default one. */ 525 if (load_dash_F_dirs(cache, key, &plib) < 0 526 || (plib == NULL && allow_private 527 && load_config(cache, key, 1, &plib) < 0) 528 || (plib == NULL 529 && load_config(cache, key, 0, &plib) < 0) 530 || (plib == NULL 531 && (plib = build_default_config(cache, key)) == NULL)) 532 fprintf(stderr, 533 "Error occurred when attempting to load a prototype " 534 "library for %s.\n", key); 535 536 /* Whatever came out of this (even NULL), store it in the 537 * cache. */ 538 attempt_to_cache(cache, key, plib); 539 return plib; 540} 541 542struct protolib * 543protolib_cache_file(struct protolib_cache *cache, const char *filename) 544{ 545 { 546 struct protolib *plib; 547 if (DICT_FIND_VAL(&cache->protolibs, &filename, &plib) == 0) 548 return plib; 549 } 550 551 FILE *stream = fopen(filename, "r"); 552 if (stream == NULL) 553 return NULL; 554 555 struct protolib *new_plib = build_default_config(cache, filename); 556 if (new_plib == NULL) { 557 fclose(stream); 558 return NULL; 559 } 560 561 if (read_config_file(stream, filename, new_plib) < 0) { 562 protolib_destroy(new_plib); 563 free(new_plib); 564 fclose(stream); 565 return NULL; 566 } 567 568 attempt_to_cache(cache, filename, new_plib); 569 fclose(stream); 570 return new_plib; 571} 572 573int 574protolib_cache_protolib(struct protolib_cache *cache, 575 const char *filename, struct protolib *plib) 576{ 577 return DICT_INSERT(&cache->protolibs, &filename, &plib); 578} 579 580void 581init_global_config(void) 582{ 583 protolib_init(&legacy_typedefs); 584 struct arg_type_info *void_info = type_get_simple(ARGTYPE_VOID); 585 static struct arg_type_info ptr_info; 586 type_init_pointer(&ptr_info, void_info, 0); 587 588 static struct named_type voidptr_type; 589 named_type_init(&voidptr_type, &ptr_info, 0); 590 591 if (protolib_add_named_type(&legacy_typedefs, "addr", 0, 592 &voidptr_type) < 0 593 || protolib_add_named_type(&legacy_typedefs, "file", 0, 594 &voidptr_type) < 0) { 595 fprintf(stderr, 596 "Couldn't initialize aliases `addr' and `file'.\n"); 597 exit(1); 598 } 599 600 if (protolib_cache_init(&g_protocache, NULL) < 0) { 601 fprintf(stderr, "Couldn't init prototype cache\n"); 602 abort(); 603 } 604} 605 606void 607destroy_global_config(void) 608{ 609 protolib_cache_destroy(&g_protocache); 610 protolib_destroy(&legacy_typedefs); 611} 612