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