Home | History | Annotate | Download | only in testsuite
      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