1 /* test_plugin.c -- simple linker plugin test 2 3 Copyright (C) 2008-2014 Free Software Foundation, Inc. 4 Written by Cary Coutant <ccoutant (at) google.com>. 5 6 This file is part of gold. 7 8 This program is free software; you can redistribute it and/or modify 9 it under the terms of the GNU General Public License as published by 10 the Free Software Foundation; either version 3 of the License, or 11 (at your option) any later version. 12 13 This program is distributed in the hope that it will be useful, 14 but WITHOUT ANY WARRANTY; without even the implied warranty of 15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 GNU General Public License for more details. 17 18 You should have received a copy of the GNU General Public License 19 along with this program; if not, write to the Free Software 20 Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, 21 MA 02110-1301, USA. */ 22 23 #ifdef HAVE_CONFIG_H 24 #include "config.h" 25 #endif 26 27 #include <stdio.h> 28 #include <stdlib.h> 29 #include <string.h> 30 #include "plugin-api.h" 31 32 struct claimed_file 33 { 34 const char* name; 35 void* handle; 36 int nsyms; 37 struct ld_plugin_symbol* syms; 38 struct claimed_file* next; 39 }; 40 41 struct sym_info 42 { 43 int size; 44 char* type; 45 char* bind; 46 char* vis; 47 char* sect; 48 char* name; 49 }; 50 51 static struct claimed_file* first_claimed_file = NULL; 52 static struct claimed_file* last_claimed_file = NULL; 53 54 static ld_plugin_register_claim_file register_claim_file_hook = NULL; 55 static ld_plugin_register_all_symbols_read register_all_symbols_read_hook = NULL; 56 static ld_plugin_register_cleanup register_cleanup_hook = NULL; 57 static ld_plugin_add_symbols add_symbols = NULL; 58 static ld_plugin_get_symbols get_symbols = NULL; 59 static ld_plugin_get_symbols get_symbols_v2 = NULL; 60 static ld_plugin_add_input_file add_input_file = NULL; 61 static ld_plugin_message message = NULL; 62 static ld_plugin_get_input_file get_input_file = NULL; 63 static ld_plugin_release_input_file release_input_file = NULL; 64 static ld_plugin_get_input_section_count get_input_section_count = NULL; 65 static ld_plugin_get_input_section_type get_input_section_type = NULL; 66 static ld_plugin_get_input_section_name get_input_section_name = NULL; 67 static ld_plugin_get_input_section_contents get_input_section_contents = NULL; 68 static ld_plugin_update_section_order update_section_order = NULL; 69 static ld_plugin_allow_section_ordering allow_section_ordering = NULL; 70 71 #define MAXOPTS 10 72 73 static const char *opts[MAXOPTS]; 74 static int nopts = 0; 75 76 enum ld_plugin_status onload(struct ld_plugin_tv *tv); 77 enum ld_plugin_status claim_file_hook(const struct ld_plugin_input_file *file, 78 int *claimed); 79 enum ld_plugin_status all_symbols_read_hook(void); 80 enum ld_plugin_status cleanup_hook(void); 81 82 static void parse_readelf_line(char*, struct sym_info*); 83 84 enum ld_plugin_status 85 onload(struct ld_plugin_tv *tv) 86 { 87 struct ld_plugin_tv *entry; 88 int api_version = 0; 89 int gold_version = 0; 90 int i; 91 92 for (entry = tv; entry->tv_tag != LDPT_NULL; ++entry) 93 { 94 switch (entry->tv_tag) 95 { 96 case LDPT_API_VERSION: 97 api_version = entry->tv_u.tv_val; 98 break; 99 case LDPT_GOLD_VERSION: 100 gold_version = entry->tv_u.tv_val; 101 break; 102 case LDPT_LINKER_OUTPUT: 103 break; 104 case LDPT_OPTION: 105 if (nopts < MAXOPTS) 106 opts[nopts++] = entry->tv_u.tv_string; 107 break; 108 case LDPT_REGISTER_CLAIM_FILE_HOOK: 109 register_claim_file_hook = entry->tv_u.tv_register_claim_file; 110 break; 111 case LDPT_REGISTER_ALL_SYMBOLS_READ_HOOK: 112 register_all_symbols_read_hook = 113 entry->tv_u.tv_register_all_symbols_read; 114 break; 115 case LDPT_REGISTER_CLEANUP_HOOK: 116 register_cleanup_hook = entry->tv_u.tv_register_cleanup; 117 break; 118 case LDPT_ADD_SYMBOLS: 119 add_symbols = entry->tv_u.tv_add_symbols; 120 break; 121 case LDPT_GET_SYMBOLS: 122 get_symbols = entry->tv_u.tv_get_symbols; 123 break; 124 case LDPT_GET_SYMBOLS_V2: 125 get_symbols_v2 = entry->tv_u.tv_get_symbols; 126 break; 127 case LDPT_ADD_INPUT_FILE: 128 add_input_file = entry->tv_u.tv_add_input_file; 129 break; 130 case LDPT_MESSAGE: 131 message = entry->tv_u.tv_message; 132 break; 133 case LDPT_GET_INPUT_FILE: 134 get_input_file = entry->tv_u.tv_get_input_file; 135 break; 136 case LDPT_RELEASE_INPUT_FILE: 137 release_input_file = entry->tv_u.tv_release_input_file; 138 break; 139 case LDPT_GET_INPUT_SECTION_COUNT: 140 get_input_section_count = *entry->tv_u.tv_get_input_section_count; 141 break; 142 case LDPT_GET_INPUT_SECTION_TYPE: 143 get_input_section_type = *entry->tv_u.tv_get_input_section_type; 144 break; 145 case LDPT_GET_INPUT_SECTION_NAME: 146 get_input_section_name = *entry->tv_u.tv_get_input_section_name; 147 break; 148 case LDPT_GET_INPUT_SECTION_CONTENTS: 149 get_input_section_contents = *entry->tv_u.tv_get_input_section_contents; 150 break; 151 case LDPT_UPDATE_SECTION_ORDER: 152 update_section_order = *entry->tv_u.tv_update_section_order; 153 break; 154 case LDPT_ALLOW_SECTION_ORDERING: 155 allow_section_ordering = *entry->tv_u.tv_allow_section_ordering; 156 break; 157 default: 158 break; 159 } 160 } 161 162 if (message == NULL) 163 { 164 fprintf(stderr, "tv_message interface missing\n"); 165 return LDPS_ERR; 166 } 167 168 if (register_claim_file_hook == NULL) 169 { 170 fprintf(stderr, "tv_register_claim_file_hook interface missing\n"); 171 return LDPS_ERR; 172 } 173 174 if (register_all_symbols_read_hook == NULL) 175 { 176 fprintf(stderr, "tv_register_all_symbols_read_hook interface missing\n"); 177 return LDPS_ERR; 178 } 179 180 if (register_cleanup_hook == NULL) 181 { 182 fprintf(stderr, "tv_register_cleanup_hook interface missing\n"); 183 return LDPS_ERR; 184 } 185 186 (*message)(LDPL_INFO, "API version: %d", api_version); 187 (*message)(LDPL_INFO, "gold version: %d", gold_version); 188 189 for (i = 0; i < nopts; ++i) 190 (*message)(LDPL_INFO, "option: %s", opts[i]); 191 192 if ((*register_claim_file_hook)(claim_file_hook) != LDPS_OK) 193 { 194 (*message)(LDPL_ERROR, "error registering claim file hook"); 195 return LDPS_ERR; 196 } 197 198 if ((*register_all_symbols_read_hook)(all_symbols_read_hook) != LDPS_OK) 199 { 200 (*message)(LDPL_ERROR, "error registering all symbols read hook"); 201 return LDPS_ERR; 202 } 203 204 if ((*register_cleanup_hook)(cleanup_hook) != LDPS_OK) 205 { 206 (*message)(LDPL_ERROR, "error registering cleanup hook"); 207 return LDPS_ERR; 208 } 209 210 if (get_input_section_count == NULL) 211 { 212 fprintf(stderr, "tv_get_input_section_count interface missing\n"); 213 return LDPS_ERR; 214 } 215 216 if (get_input_section_type == NULL) 217 { 218 fprintf(stderr, "tv_get_input_section_type interface missing\n"); 219 return LDPS_ERR; 220 } 221 222 if (get_input_section_name == NULL) 223 { 224 fprintf(stderr, "tv_get_input_section_name interface missing\n"); 225 return LDPS_ERR; 226 } 227 228 if (get_input_section_contents == NULL) 229 { 230 fprintf(stderr, "tv_get_input_section_contents interface missing\n"); 231 return LDPS_ERR; 232 } 233 234 if (update_section_order == NULL) 235 { 236 fprintf(stderr, "tv_update_section_order interface missing\n"); 237 return LDPS_ERR; 238 } 239 240 if (allow_section_ordering == NULL) 241 { 242 fprintf(stderr, "tv_allow_section_ordering interface missing\n"); 243 return LDPS_ERR; 244 } 245 246 return LDPS_OK; 247 } 248 249 enum ld_plugin_status 250 claim_file_hook (const struct ld_plugin_input_file* file, int* claimed) 251 { 252 int len; 253 off_t end_offset; 254 char buf[160]; 255 struct claimed_file* claimed_file; 256 struct ld_plugin_symbol* syms; 257 int nsyms = 0; 258 int maxsyms = 0; 259 FILE* irfile; 260 struct sym_info info; 261 int weak; 262 int def; 263 int vis; 264 int is_comdat; 265 int i; 266 int irfile_was_opened = 0; 267 char syms_name[80]; 268 269 (*message)(LDPL_INFO, 270 "%s: claim file hook called (offset = %ld, size = %ld)", 271 file->name, (long)file->offset, (long)file->filesize); 272 273 /* Look for matching syms file for an archive member. */ 274 if (file->offset == 0) 275 snprintf(syms_name, sizeof(syms_name), "%s.syms", file->name); 276 else 277 snprintf(syms_name, sizeof(syms_name), "%s-%d.syms", 278 file->name, (int)file->offset); 279 irfile = fopen(syms_name, "r"); 280 if (irfile != NULL) 281 { 282 irfile_was_opened = 1; 283 end_offset = 1 << 20; 284 } 285 286 /* Otherwise, see if the file itself is a syms file. */ 287 if (!irfile_was_opened) 288 { 289 irfile = fdopen(file->fd, "r"); 290 (void)fseek(irfile, file->offset, SEEK_SET); 291 end_offset = file->offset + file->filesize; 292 } 293 294 /* Look for the beginning of output from readelf -s. */ 295 len = fread(buf, 1, 13, irfile); 296 if (len < 13 || strncmp(buf, "\nSymbol table", 13) != 0) 297 return LDPS_OK; 298 299 /* Skip the two header lines. */ 300 (void) fgets(buf, sizeof(buf), irfile); 301 (void) fgets(buf, sizeof(buf), irfile); 302 303 if (add_symbols == NULL) 304 { 305 fprintf(stderr, "tv_add_symbols interface missing\n"); 306 return LDPS_ERR; 307 } 308 309 /* Parse the output from readelf. The columns are: 310 Index Value Size Type Binding Visibility Section Name. */ 311 syms = (struct ld_plugin_symbol*)malloc(sizeof(struct ld_plugin_symbol) * 8); 312 if (syms == NULL) 313 return LDPS_ERR; 314 maxsyms = 8; 315 while (ftell(irfile) < end_offset 316 && fgets(buf, sizeof(buf), irfile) != NULL) 317 { 318 parse_readelf_line(buf, &info); 319 320 /* Ignore local symbols. */ 321 if (strncmp(info.bind, "LOCAL", 5) == 0) 322 continue; 323 324 weak = strncmp(info.bind, "WEAK", 4) == 0; 325 if (strncmp(info.sect, "UND", 3) == 0) 326 def = weak ? LDPK_WEAKUNDEF : LDPK_UNDEF; 327 else if (strncmp(info.sect, "COM", 3) == 0) 328 def = LDPK_COMMON; 329 else 330 def = weak ? LDPK_WEAKDEF : LDPK_DEF; 331 332 if (strncmp(info.vis, "INTERNAL", 8) == 0) 333 vis = LDPV_INTERNAL; 334 else if (strncmp(info.vis, "HIDDEN", 6) == 0) 335 vis = LDPV_HIDDEN; 336 else if (strncmp(info.vis, "PROTECTED", 9) == 0) 337 vis = LDPV_PROTECTED; 338 else 339 vis = LDPV_DEFAULT; 340 341 /* If the symbol is listed in the options list, special-case 342 it as a comdat symbol. */ 343 is_comdat = 0; 344 for (i = 0; i < nopts; ++i) 345 { 346 if (info.name != NULL && strcmp(info.name, opts[i]) == 0) 347 { 348 is_comdat = 1; 349 break; 350 } 351 } 352 353 if (nsyms >= maxsyms) 354 { 355 syms = (struct ld_plugin_symbol*) 356 realloc(syms, sizeof(struct ld_plugin_symbol) * maxsyms * 2); 357 if (syms == NULL) 358 return LDPS_ERR; 359 maxsyms *= 2; 360 } 361 362 if (info.name == NULL) 363 syms[nsyms].name = NULL; 364 else 365 { 366 len = strlen(info.name); 367 syms[nsyms].name = malloc(len + 1); 368 strncpy(syms[nsyms].name, info.name, len + 1); 369 } 370 syms[nsyms].version = NULL; 371 syms[nsyms].def = def; 372 syms[nsyms].visibility = vis; 373 syms[nsyms].size = info.size; 374 syms[nsyms].comdat_key = is_comdat ? syms[nsyms].name : NULL; 375 syms[nsyms].resolution = LDPR_UNKNOWN; 376 ++nsyms; 377 } 378 379 claimed_file = (struct claimed_file*) malloc(sizeof(struct claimed_file)); 380 if (claimed_file == NULL) 381 return LDPS_ERR; 382 383 claimed_file->name = file->name; 384 claimed_file->handle = file->handle; 385 claimed_file->nsyms = nsyms; 386 claimed_file->syms = syms; 387 claimed_file->next = NULL; 388 if (last_claimed_file == NULL) 389 first_claimed_file = claimed_file; 390 else 391 last_claimed_file->next = claimed_file; 392 last_claimed_file = claimed_file; 393 394 (*message)(LDPL_INFO, "%s: claiming file, adding %d symbols", 395 file->name, nsyms); 396 397 if (nsyms > 0) 398 (*add_symbols)(file->handle, nsyms, syms); 399 400 *claimed = 1; 401 if (irfile_was_opened) 402 fclose(irfile); 403 return LDPS_OK; 404 } 405 406 enum ld_plugin_status 407 all_symbols_read_hook(void) 408 { 409 int i; 410 const char* res; 411 struct claimed_file* claimed_file; 412 struct ld_plugin_input_file file; 413 FILE* irfile; 414 off_t end_offset; 415 struct sym_info info; 416 int len; 417 char buf[160]; 418 char* p; 419 const char* filename; 420 421 (*message)(LDPL_INFO, "all symbols read hook called"); 422 423 if (get_symbols_v2 == NULL) 424 { 425 fprintf(stderr, "tv_get_symbols (v2) interface missing\n"); 426 return LDPS_ERR; 427 } 428 429 for (claimed_file = first_claimed_file; 430 claimed_file != NULL; 431 claimed_file = claimed_file->next) 432 { 433 (*get_symbols_v2)(claimed_file->handle, claimed_file->nsyms, 434 claimed_file->syms); 435 436 for (i = 0; i < claimed_file->nsyms; ++i) 437 { 438 switch (claimed_file->syms[i].resolution) 439 { 440 case LDPR_UNKNOWN: 441 res = "UNKNOWN"; 442 break; 443 case LDPR_UNDEF: 444 res = "UNDEF"; 445 break; 446 case LDPR_PREVAILING_DEF: 447 res = "PREVAILING_DEF_REG"; 448 break; 449 case LDPR_PREVAILING_DEF_IRONLY: 450 res = "PREVAILING_DEF_IRONLY"; 451 break; 452 case LDPR_PREVAILING_DEF_IRONLY_EXP: 453 res = "PREVAILING_DEF_IRONLY_EXP"; 454 break; 455 case LDPR_PREEMPTED_REG: 456 res = "PREEMPTED_REG"; 457 break; 458 case LDPR_PREEMPTED_IR: 459 res = "PREEMPTED_IR"; 460 break; 461 case LDPR_RESOLVED_IR: 462 res = "RESOLVED_IR"; 463 break; 464 case LDPR_RESOLVED_EXEC: 465 res = "RESOLVED_EXEC"; 466 break; 467 case LDPR_RESOLVED_DYN: 468 res = "RESOLVED_DYN"; 469 break; 470 default: 471 res = "?"; 472 break; 473 } 474 (*message)(LDPL_INFO, "%s: %s: %s", claimed_file->name, 475 claimed_file->syms[i].name, res); 476 } 477 } 478 479 if (add_input_file == NULL) 480 { 481 fprintf(stderr, "tv_add_input_file interface missing\n"); 482 return LDPS_ERR; 483 } 484 if (get_input_file == NULL) 485 { 486 fprintf(stderr, "tv_get_input_file interface missing\n"); 487 return LDPS_ERR; 488 } 489 if (release_input_file == NULL) 490 { 491 fprintf(stderr, "tv_release_input_file interface missing\n"); 492 return LDPS_ERR; 493 } 494 495 for (claimed_file = first_claimed_file; 496 claimed_file != NULL; 497 claimed_file = claimed_file->next) 498 { 499 int irfile_was_opened = 0; 500 char syms_name[80]; 501 502 (*get_input_file) (claimed_file->handle, &file); 503 504 if (file.offset == 0) 505 snprintf(syms_name, sizeof(syms_name), "%s.syms", file.name); 506 else 507 snprintf(syms_name, sizeof(syms_name), "%s-%d.syms", 508 file.name, (int)file.offset); 509 irfile = fopen(syms_name, "r"); 510 if (irfile != NULL) 511 { 512 irfile_was_opened = 1; 513 end_offset = 1 << 20; 514 } 515 516 if (!irfile_was_opened) 517 { 518 irfile = fdopen(file.fd, "r"); 519 (void)fseek(irfile, file.offset, SEEK_SET); 520 end_offset = file.offset + file.filesize; 521 } 522 523 /* Look for the beginning of output from readelf -s. */ 524 len = fread(buf, 1, 13, irfile); 525 if (len < 13 || strncmp(buf, "\nSymbol table", 13) != 0) 526 { 527 fprintf(stderr, "%s: can't re-read original input file\n", 528 claimed_file->name); 529 return LDPS_ERR; 530 } 531 532 /* Skip the two header lines. */ 533 (void) fgets(buf, sizeof(buf), irfile); 534 (void) fgets(buf, sizeof(buf), irfile); 535 536 filename = NULL; 537 while (ftell(irfile) < end_offset 538 && fgets(buf, sizeof(buf), irfile) != NULL) 539 { 540 parse_readelf_line(buf, &info); 541 542 /* Look for file name. */ 543 if (strncmp(info.type, "FILE", 4) == 0) 544 { 545 len = strlen(info.name); 546 p = malloc(len + 1); 547 strncpy(p, info.name, len + 1); 548 filename = p; 549 break; 550 } 551 } 552 553 if (irfile_was_opened) 554 fclose(irfile); 555 556 (*release_input_file) (claimed_file->handle); 557 558 if (filename == NULL) 559 filename = claimed_file->name; 560 561 if (claimed_file->nsyms == 0) 562 continue; 563 564 if (strlen(filename) >= sizeof(buf)) 565 { 566 (*message)(LDPL_FATAL, "%s: filename too long", filename); 567 return LDPS_ERR; 568 } 569 strcpy(buf, filename); 570 p = strrchr(buf, '.'); 571 if (p == NULL 572 || (strcmp(p, ".syms") != 0 573 && strcmp(p, ".c") != 0 574 && strcmp(p, ".cc") != 0)) 575 { 576 (*message)(LDPL_FATAL, "%s: filename has unknown suffix", 577 filename); 578 return LDPS_ERR; 579 } 580 p[1] = 'o'; 581 p[2] = '\0'; 582 (*message)(LDPL_INFO, "%s: adding new input file", buf); 583 (*add_input_file)(buf); 584 } 585 586 return LDPS_OK; 587 } 588 589 enum ld_plugin_status 590 cleanup_hook(void) 591 { 592 (*message)(LDPL_INFO, "cleanup hook called"); 593 return LDPS_OK; 594 } 595 596 static void 597 parse_readelf_line(char* p, struct sym_info* info) 598 { 599 int len; 600 601 p += strspn(p, " "); 602 603 /* Index field. */ 604 p += strcspn(p, " "); 605 p += strspn(p, " "); 606 607 /* Value field. */ 608 p += strcspn(p, " "); 609 p += strspn(p, " "); 610 611 /* Size field. */ 612 info->size = atoi(p); 613 p += strcspn(p, " "); 614 p += strspn(p, " "); 615 616 /* Type field. */ 617 info->type = p; 618 p += strcspn(p, " "); 619 p += strspn(p, " "); 620 621 /* Binding field. */ 622 info->bind = p; 623 p += strcspn(p, " "); 624 p += strspn(p, " "); 625 626 /* Visibility field. */ 627 info->vis = p; 628 p += strcspn(p, " "); 629 p += strspn(p, " "); 630 631 if (*p == '[') 632 { 633 /* Skip st_other. */ 634 p += strcspn(p, "]"); 635 p += strspn(p, "] "); 636 } 637 638 /* Section field. */ 639 info->sect = p; 640 p += strcspn(p, " "); 641 p += strspn(p, " "); 642 643 /* Name field. */ 644 /* FIXME: Look for version. */ 645 len = strlen(p); 646 if (len == 0) 647 p = NULL; 648 else if (p[len-1] == '\n') 649 p[--len] = '\0'; 650 info->name = p; 651 } 652