1/* test_plugin.c -- simple linker plugin test 2 3 Copyright 2008, 2009 Free Software Foundation, Inc. 4 Written by Cary Coutant <ccoutant@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 32struct 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 41struct sym_info 42{ 43 int size; 44 char* type; 45 char* bind; 46 char* vis; 47 char* sect; 48 char* name; 49}; 50 51static struct claimed_file* first_claimed_file = NULL; 52static struct claimed_file* last_claimed_file = NULL; 53 54static ld_plugin_register_claim_file register_claim_file_hook = NULL; 55static ld_plugin_register_all_symbols_read register_all_symbols_read_hook = NULL; 56static ld_plugin_register_cleanup register_cleanup_hook = NULL; 57static ld_plugin_add_symbols add_symbols = NULL; 58static ld_plugin_get_symbols get_symbols = NULL; 59static ld_plugin_add_input_file add_input_file = NULL; 60static ld_plugin_message message = NULL; 61static ld_plugin_get_input_file get_input_file = NULL; 62static ld_plugin_release_input_file release_input_file = NULL; 63 64#define MAXOPTS 10 65 66static const char *opts[MAXOPTS]; 67static int nopts = 0; 68 69enum ld_plugin_status onload(struct ld_plugin_tv *tv); 70enum ld_plugin_status claim_file_hook(const struct ld_plugin_input_file *file, 71 int *claimed); 72enum ld_plugin_status all_symbols_read_hook(void); 73enum ld_plugin_status cleanup_hook(void); 74 75static void parse_readelf_line(char*, struct sym_info*); 76 77enum ld_plugin_status 78onload(struct ld_plugin_tv *tv) 79{ 80 struct ld_plugin_tv *entry; 81 int api_version = 0; 82 int gold_version = 0; 83 int i; 84 85 for (entry = tv; entry->tv_tag != LDPT_NULL; ++entry) 86 { 87 switch (entry->tv_tag) 88 { 89 case LDPT_API_VERSION: 90 api_version = entry->tv_u.tv_val; 91 break; 92 case LDPT_GOLD_VERSION: 93 gold_version = entry->tv_u.tv_val; 94 break; 95 case LDPT_LINKER_OUTPUT: 96 break; 97 case LDPT_OPTION: 98 if (nopts < MAXOPTS) 99 opts[nopts++] = entry->tv_u.tv_string; 100 break; 101 case LDPT_REGISTER_CLAIM_FILE_HOOK: 102 register_claim_file_hook = entry->tv_u.tv_register_claim_file; 103 break; 104 case LDPT_REGISTER_ALL_SYMBOLS_READ_HOOK: 105 register_all_symbols_read_hook = 106 entry->tv_u.tv_register_all_symbols_read; 107 break; 108 case LDPT_REGISTER_CLEANUP_HOOK: 109 register_cleanup_hook = entry->tv_u.tv_register_cleanup; 110 break; 111 case LDPT_ADD_SYMBOLS: 112 add_symbols = entry->tv_u.tv_add_symbols; 113 break; 114 case LDPT_GET_SYMBOLS: 115 get_symbols = entry->tv_u.tv_get_symbols; 116 break; 117 case LDPT_ADD_INPUT_FILE: 118 add_input_file = entry->tv_u.tv_add_input_file; 119 break; 120 case LDPT_MESSAGE: 121 message = entry->tv_u.tv_message; 122 break; 123 case LDPT_GET_INPUT_FILE: 124 get_input_file = entry->tv_u.tv_get_input_file; 125 break; 126 case LDPT_RELEASE_INPUT_FILE: 127 release_input_file = entry->tv_u.tv_release_input_file; 128 break; 129 default: 130 break; 131 } 132 } 133 134 if (message == NULL) 135 { 136 fprintf(stderr, "tv_message interface missing\n"); 137 return LDPS_ERR; 138 } 139 140 if (register_claim_file_hook == NULL) 141 { 142 fprintf(stderr, "tv_register_claim_file_hook interface missing\n"); 143 return LDPS_ERR; 144 } 145 146 if (register_all_symbols_read_hook == NULL) 147 { 148 fprintf(stderr, "tv_register_all_symbols_read_hook interface missing\n"); 149 return LDPS_ERR; 150 } 151 152 if (register_cleanup_hook == NULL) 153 { 154 fprintf(stderr, "tv_register_cleanup_hook interface missing\n"); 155 return LDPS_ERR; 156 } 157 158 (*message)(LDPL_INFO, "API version: %d", api_version); 159 (*message)(LDPL_INFO, "gold version: %d", gold_version); 160 161 for (i = 0; i < nopts; ++i) 162 (*message)(LDPL_INFO, "option: %s", opts[i]); 163 164 if ((*register_claim_file_hook)(claim_file_hook) != LDPS_OK) 165 { 166 (*message)(LDPL_ERROR, "error registering claim file hook"); 167 return LDPS_ERR; 168 } 169 170 if ((*register_all_symbols_read_hook)(all_symbols_read_hook) != LDPS_OK) 171 { 172 (*message)(LDPL_ERROR, "error registering all symbols read hook"); 173 return LDPS_ERR; 174 } 175 176 if ((*register_cleanup_hook)(cleanup_hook) != LDPS_OK) 177 { 178 (*message)(LDPL_ERROR, "error registering cleanup hook"); 179 return LDPS_ERR; 180 } 181 182 return LDPS_OK; 183} 184 185enum ld_plugin_status 186claim_file_hook (const struct ld_plugin_input_file* file, int* claimed) 187{ 188 int len; 189 off_t end_offset; 190 char buf[160]; 191 struct claimed_file* claimed_file; 192 struct ld_plugin_symbol* syms; 193 int nsyms = 0; 194 int maxsyms = 0; 195 FILE* irfile; 196 struct sym_info info; 197 int weak; 198 int def; 199 int vis; 200 int is_comdat; 201 int i; 202 203 (*message)(LDPL_INFO, 204 "%s: claim file hook called (offset = %ld, size = %ld)", 205 file->name, (long)file->offset, (long)file->filesize); 206 207 /* Look for the beginning of output from readelf -s. */ 208 irfile = fdopen(file->fd, "r"); 209 (void)fseek(irfile, file->offset, SEEK_SET); 210 end_offset = file->offset + file->filesize; 211 len = fread(buf, 1, 13, irfile); 212 if (len < 13 || strncmp(buf, "\nSymbol table", 13) != 0) 213 return LDPS_OK; 214 215 /* Skip the two header lines. */ 216 (void) fgets(buf, sizeof(buf), irfile); 217 (void) fgets(buf, sizeof(buf), irfile); 218 219 if (add_symbols == NULL) 220 { 221 fprintf(stderr, "tv_add_symbols interface missing\n"); 222 return LDPS_ERR; 223 } 224 225 /* Parse the output from readelf. The columns are: 226 Index Value Size Type Binding Visibility Section Name. */ 227 syms = (struct ld_plugin_symbol*)malloc(sizeof(struct ld_plugin_symbol) * 8); 228 if (syms == NULL) 229 return LDPS_ERR; 230 maxsyms = 8; 231 while (ftell(irfile) < end_offset 232 && fgets(buf, sizeof(buf), irfile) != NULL) 233 { 234 parse_readelf_line(buf, &info); 235 236 /* Ignore local symbols. */ 237 if (strncmp(info.bind, "LOCAL", 5) == 0) 238 continue; 239 240 weak = strncmp(info.bind, "WEAK", 4) == 0; 241 if (strncmp(info.sect, "UND", 3) == 0) 242 def = weak ? LDPK_WEAKUNDEF : LDPK_UNDEF; 243 else if (strncmp(info.sect, "COM", 3) == 0) 244 def = LDPK_COMMON; 245 else 246 def = weak ? LDPK_WEAKDEF : LDPK_DEF; 247 248 if (strncmp(info.vis, "INTERNAL", 8) == 0) 249 vis = LDPV_INTERNAL; 250 else if (strncmp(info.vis, "HIDDEN", 6) == 0) 251 vis = LDPV_HIDDEN; 252 else if (strncmp(info.vis, "PROTECTED", 9) == 0) 253 vis = LDPV_PROTECTED; 254 else 255 vis = LDPV_DEFAULT; 256 257 /* If the symbol is listed in the options list, special-case 258 it as a comdat symbol. */ 259 is_comdat = 0; 260 for (i = 0; i < nopts; ++i) 261 { 262 if (info.name != NULL && strcmp(info.name, opts[i]) == 0) 263 { 264 is_comdat = 1; 265 break; 266 } 267 } 268 269 if (nsyms >= maxsyms) 270 { 271 syms = (struct ld_plugin_symbol*) 272 realloc(syms, sizeof(struct ld_plugin_symbol) * maxsyms * 2); 273 if (syms == NULL) 274 return LDPS_ERR; 275 maxsyms *= 2; 276 } 277 278 if (info.name == NULL) 279 syms[nsyms].name = NULL; 280 else 281 { 282 len = strlen(info.name); 283 syms[nsyms].name = malloc(len + 1); 284 strncpy(syms[nsyms].name, info.name, len + 1); 285 } 286 syms[nsyms].version = NULL; 287 syms[nsyms].def = def; 288 syms[nsyms].visibility = vis; 289 syms[nsyms].size = info.size; 290 syms[nsyms].comdat_key = is_comdat ? syms[nsyms].name : NULL; 291 syms[nsyms].resolution = LDPR_UNKNOWN; 292 ++nsyms; 293 } 294 295 claimed_file = (struct claimed_file*) malloc(sizeof(struct claimed_file)); 296 if (claimed_file == NULL) 297 return LDPS_ERR; 298 299 claimed_file->name = file->name; 300 claimed_file->handle = file->handle; 301 claimed_file->nsyms = nsyms; 302 claimed_file->syms = syms; 303 claimed_file->next = NULL; 304 if (last_claimed_file == NULL) 305 first_claimed_file = claimed_file; 306 else 307 last_claimed_file->next = claimed_file; 308 last_claimed_file = claimed_file; 309 310 (*message)(LDPL_INFO, "%s: claiming file, adding %d symbols", 311 file->name, nsyms); 312 313 if (nsyms > 0) 314 (*add_symbols)(file->handle, nsyms, syms); 315 316 *claimed = 1; 317 return LDPS_OK; 318} 319 320enum ld_plugin_status 321all_symbols_read_hook(void) 322{ 323 int i; 324 const char* res; 325 struct claimed_file* claimed_file; 326 struct ld_plugin_input_file file; 327 FILE* irfile; 328 off_t end_offset; 329 struct sym_info info; 330 int len; 331 char buf[160]; 332 char* p; 333 const char* filename; 334 335 (*message)(LDPL_INFO, "all symbols read hook called"); 336 337 if (get_symbols == NULL) 338 { 339 fprintf(stderr, "tv_get_symbols interface missing\n"); 340 return LDPS_ERR; 341 } 342 343 for (claimed_file = first_claimed_file; 344 claimed_file != NULL; 345 claimed_file = claimed_file->next) 346 { 347 (*get_symbols)(claimed_file->handle, claimed_file->nsyms, 348 claimed_file->syms); 349 350 for (i = 0; i < claimed_file->nsyms; ++i) 351 { 352 switch (claimed_file->syms[i].resolution) 353 { 354 case LDPR_UNKNOWN: 355 res = "UNKNOWN"; 356 break; 357 case LDPR_UNDEF: 358 res = "UNDEF"; 359 break; 360 case LDPR_PREVAILING_DEF: 361 res = "PREVAILING_DEF_REG"; 362 break; 363 case LDPR_PREVAILING_DEF_IRONLY: 364 res = "PREVAILING_DEF_IRONLY"; 365 break; 366 case LDPR_PREEMPTED_REG: 367 res = "PREEMPTED_REG"; 368 break; 369 case LDPR_PREEMPTED_IR: 370 res = "PREEMPTED_IR"; 371 break; 372 case LDPR_RESOLVED_IR: 373 res = "RESOLVED_IR"; 374 break; 375 case LDPR_RESOLVED_EXEC: 376 res = "RESOLVED_EXEC"; 377 break; 378 case LDPR_RESOLVED_DYN: 379 res = "RESOLVED_DYN"; 380 break; 381 default: 382 res = "?"; 383 break; 384 } 385 (*message)(LDPL_INFO, "%s: %s: %s", claimed_file->name, 386 claimed_file->syms[i].name, res); 387 } 388 } 389 390 if (add_input_file == NULL) 391 { 392 fprintf(stderr, "tv_add_input_file interface missing\n"); 393 return LDPS_ERR; 394 } 395 if (get_input_file == NULL) 396 { 397 fprintf(stderr, "tv_get_input_file interface missing\n"); 398 return LDPS_ERR; 399 } 400 if (release_input_file == NULL) 401 { 402 fprintf(stderr, "tv_release_input_file interface missing\n"); 403 return LDPS_ERR; 404 } 405 406 for (claimed_file = first_claimed_file; 407 claimed_file != NULL; 408 claimed_file = claimed_file->next) 409 { 410 (*get_input_file) (claimed_file->handle, &file); 411 412 /* Look for the beginning of output from readelf -s. */ 413 irfile = fdopen(file.fd, "r"); 414 (void)fseek(irfile, file.offset, SEEK_SET); 415 end_offset = file.offset + file.filesize; 416 len = fread(buf, 1, 13, irfile); 417 if (len < 13 || strncmp(buf, "\nSymbol table", 13) != 0) 418 { 419 fprintf(stderr, "%s: can't re-read original input file\n", 420 claimed_file->name); 421 return LDPS_ERR; 422 } 423 424 /* Skip the two header lines. */ 425 (void) fgets(buf, sizeof(buf), irfile); 426 (void) fgets(buf, sizeof(buf), irfile); 427 428 filename = NULL; 429 while (ftell(irfile) < end_offset 430 && fgets(buf, sizeof(buf), irfile) != NULL) 431 { 432 parse_readelf_line(buf, &info); 433 434 /* Look for file name. */ 435 if (strncmp(info.type, "FILE", 4) == 0) 436 { 437 len = strlen(info.name); 438 p = malloc(len + 1); 439 strncpy(p, info.name, len + 1); 440 filename = p; 441 break; 442 } 443 } 444 445 (*release_input_file) (claimed_file->handle); 446 447 if (filename == NULL) 448 filename = claimed_file->name; 449 450 if (claimed_file->nsyms == 0) 451 continue; 452 453 if (strlen(filename) >= sizeof(buf)) 454 { 455 (*message)(LDPL_FATAL, "%s: filename too long", filename); 456 return LDPS_ERR; 457 } 458 strcpy(buf, filename); 459 p = strrchr(buf, '.'); 460 if (p == NULL 461 || (strcmp(p, ".syms") != 0 462 && strcmp(p, ".c") != 0 463 && strcmp(p, ".cc") != 0)) 464 { 465 (*message)(LDPL_FATAL, "%s: filename has unknown suffix", 466 filename); 467 return LDPS_ERR; 468 } 469 p[1] = 'o'; 470 p[2] = '\0'; 471 (*message)(LDPL_INFO, "%s: adding new input file", buf); 472 (*add_input_file)(buf); 473 } 474 475 return LDPS_OK; 476} 477 478enum ld_plugin_status 479cleanup_hook(void) 480{ 481 (*message)(LDPL_INFO, "cleanup hook called"); 482 return LDPS_OK; 483} 484 485static void 486parse_readelf_line(char* p, struct sym_info* info) 487{ 488 int len; 489 490 p += strspn(p, " "); 491 492 /* Index field. */ 493 p += strcspn(p, " "); 494 p += strspn(p, " "); 495 496 /* Value field. */ 497 p += strcspn(p, " "); 498 p += strspn(p, " "); 499 500 /* Size field. */ 501 info->size = atoi(p); 502 p += strcspn(p, " "); 503 p += strspn(p, " "); 504 505 /* Type field. */ 506 info->type = p; 507 p += strcspn(p, " "); 508 p += strspn(p, " "); 509 510 /* Binding field. */ 511 info->bind = p; 512 p += strcspn(p, " "); 513 p += strspn(p, " "); 514 515 /* Visibility field. */ 516 info->vis = p; 517 p += strcspn(p, " "); 518 p += strspn(p, " "); 519 520 /* Section field. */ 521 info->sect = p; 522 p += strcspn(p, " "); 523 p += strspn(p, " "); 524 525 /* Name field. */ 526 /* FIXME: Look for version. */ 527 len = strlen(p); 528 if (len == 0) 529 p = NULL; 530 else if (p[len-1] == '\n') 531 p[--len] = '\0'; 532 info->name = p; 533} 534