123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652 |
- /* test_plugin.c -- simple linker plugin test
- Copyright (C) 2008-2015 Free Software Foundation, Inc.
- Written by Cary Coutant <ccoutant@google.com>.
- This file is part of gold.
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 3 of the License, or
- (at your option) any later version.
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston,
- MA 02110-1301, USA. */
- #ifdef HAVE_CONFIG_H
- #include "config.h"
- #endif
- #include <stdio.h>
- #include <stdlib.h>
- #include <string.h>
- #include "plugin-api.h"
- struct claimed_file
- {
- const char* name;
- void* handle;
- int nsyms;
- struct ld_plugin_symbol* syms;
- struct claimed_file* next;
- };
- struct sym_info
- {
- int size;
- char* type;
- char* bind;
- char* vis;
- char* sect;
- char* name;
- };
- static struct claimed_file* first_claimed_file = NULL;
- static struct claimed_file* last_claimed_file = NULL;
- static ld_plugin_register_claim_file register_claim_file_hook = NULL;
- static ld_plugin_register_all_symbols_read register_all_symbols_read_hook = NULL;
- static ld_plugin_register_cleanup register_cleanup_hook = NULL;
- static ld_plugin_add_symbols add_symbols = NULL;
- static ld_plugin_get_symbols get_symbols = NULL;
- static ld_plugin_get_symbols get_symbols_v2 = NULL;
- static ld_plugin_add_input_file add_input_file = NULL;
- static ld_plugin_message message = NULL;
- static ld_plugin_get_input_file get_input_file = NULL;
- static ld_plugin_release_input_file release_input_file = NULL;
- static ld_plugin_get_input_section_count get_input_section_count = NULL;
- static ld_plugin_get_input_section_type get_input_section_type = NULL;
- static ld_plugin_get_input_section_name get_input_section_name = NULL;
- static ld_plugin_get_input_section_contents get_input_section_contents = NULL;
- static ld_plugin_update_section_order update_section_order = NULL;
- static ld_plugin_allow_section_ordering allow_section_ordering = NULL;
- #define MAXOPTS 10
- static const char *opts[MAXOPTS];
- static int nopts = 0;
- enum ld_plugin_status onload(struct ld_plugin_tv *tv);
- enum ld_plugin_status claim_file_hook(const struct ld_plugin_input_file *file,
- int *claimed);
- enum ld_plugin_status all_symbols_read_hook(void);
- enum ld_plugin_status cleanup_hook(void);
- static void parse_readelf_line(char*, struct sym_info*);
- enum ld_plugin_status
- onload(struct ld_plugin_tv *tv)
- {
- struct ld_plugin_tv *entry;
- int api_version = 0;
- int gold_version = 0;
- int i;
- for (entry = tv; entry->tv_tag != LDPT_NULL; ++entry)
- {
- switch (entry->tv_tag)
- {
- case LDPT_API_VERSION:
- api_version = entry->tv_u.tv_val;
- break;
- case LDPT_GOLD_VERSION:
- gold_version = entry->tv_u.tv_val;
- break;
- case LDPT_LINKER_OUTPUT:
- break;
- case LDPT_OPTION:
- if (nopts < MAXOPTS)
- opts[nopts++] = entry->tv_u.tv_string;
- break;
- case LDPT_REGISTER_CLAIM_FILE_HOOK:
- register_claim_file_hook = entry->tv_u.tv_register_claim_file;
- break;
- case LDPT_REGISTER_ALL_SYMBOLS_READ_HOOK:
- register_all_symbols_read_hook =
- entry->tv_u.tv_register_all_symbols_read;
- break;
- case LDPT_REGISTER_CLEANUP_HOOK:
- register_cleanup_hook = entry->tv_u.tv_register_cleanup;
- break;
- case LDPT_ADD_SYMBOLS:
- add_symbols = entry->tv_u.tv_add_symbols;
- break;
- case LDPT_GET_SYMBOLS:
- get_symbols = entry->tv_u.tv_get_symbols;
- break;
- case LDPT_GET_SYMBOLS_V2:
- get_symbols_v2 = entry->tv_u.tv_get_symbols;
- break;
- case LDPT_ADD_INPUT_FILE:
- add_input_file = entry->tv_u.tv_add_input_file;
- break;
- case LDPT_MESSAGE:
- message = entry->tv_u.tv_message;
- break;
- case LDPT_GET_INPUT_FILE:
- get_input_file = entry->tv_u.tv_get_input_file;
- break;
- case LDPT_RELEASE_INPUT_FILE:
- release_input_file = entry->tv_u.tv_release_input_file;
- break;
- case LDPT_GET_INPUT_SECTION_COUNT:
- get_input_section_count = *entry->tv_u.tv_get_input_section_count;
- break;
- case LDPT_GET_INPUT_SECTION_TYPE:
- get_input_section_type = *entry->tv_u.tv_get_input_section_type;
- break;
- case LDPT_GET_INPUT_SECTION_NAME:
- get_input_section_name = *entry->tv_u.tv_get_input_section_name;
- break;
- case LDPT_GET_INPUT_SECTION_CONTENTS:
- get_input_section_contents = *entry->tv_u.tv_get_input_section_contents;
- break;
- case LDPT_UPDATE_SECTION_ORDER:
- update_section_order = *entry->tv_u.tv_update_section_order;
- break;
- case LDPT_ALLOW_SECTION_ORDERING:
- allow_section_ordering = *entry->tv_u.tv_allow_section_ordering;
- break;
- default:
- break;
- }
- }
- if (message == NULL)
- {
- fprintf(stderr, "tv_message interface missing\n");
- return LDPS_ERR;
- }
- if (register_claim_file_hook == NULL)
- {
- fprintf(stderr, "tv_register_claim_file_hook interface missing\n");
- return LDPS_ERR;
- }
- if (register_all_symbols_read_hook == NULL)
- {
- fprintf(stderr, "tv_register_all_symbols_read_hook interface missing\n");
- return LDPS_ERR;
- }
- if (register_cleanup_hook == NULL)
- {
- fprintf(stderr, "tv_register_cleanup_hook interface missing\n");
- return LDPS_ERR;
- }
- (*message)(LDPL_INFO, "API version: %d", api_version);
- (*message)(LDPL_INFO, "gold version: %d", gold_version);
- for (i = 0; i < nopts; ++i)
- (*message)(LDPL_INFO, "option: %s", opts[i]);
- if ((*register_claim_file_hook)(claim_file_hook) != LDPS_OK)
- {
- (*message)(LDPL_ERROR, "error registering claim file hook");
- return LDPS_ERR;
- }
- if ((*register_all_symbols_read_hook)(all_symbols_read_hook) != LDPS_OK)
- {
- (*message)(LDPL_ERROR, "error registering all symbols read hook");
- return LDPS_ERR;
- }
- if ((*register_cleanup_hook)(cleanup_hook) != LDPS_OK)
- {
- (*message)(LDPL_ERROR, "error registering cleanup hook");
- return LDPS_ERR;
- }
- if (get_input_section_count == NULL)
- {
- fprintf(stderr, "tv_get_input_section_count interface missing\n");
- return LDPS_ERR;
- }
- if (get_input_section_type == NULL)
- {
- fprintf(stderr, "tv_get_input_section_type interface missing\n");
- return LDPS_ERR;
- }
- if (get_input_section_name == NULL)
- {
- fprintf(stderr, "tv_get_input_section_name interface missing\n");
- return LDPS_ERR;
- }
- if (get_input_section_contents == NULL)
- {
- fprintf(stderr, "tv_get_input_section_contents interface missing\n");
- return LDPS_ERR;
- }
- if (update_section_order == NULL)
- {
- fprintf(stderr, "tv_update_section_order interface missing\n");
- return LDPS_ERR;
- }
- if (allow_section_ordering == NULL)
- {
- fprintf(stderr, "tv_allow_section_ordering interface missing\n");
- return LDPS_ERR;
- }
- return LDPS_OK;
- }
- enum ld_plugin_status
- claim_file_hook (const struct ld_plugin_input_file* file, int* claimed)
- {
- int len;
- off_t end_offset;
- char buf[160];
- struct claimed_file* claimed_file;
- struct ld_plugin_symbol* syms;
- int nsyms = 0;
- int maxsyms = 0;
- FILE* irfile;
- struct sym_info info;
- int weak;
- int def;
- int vis;
- int is_comdat;
- int i;
- int irfile_was_opened = 0;
- char syms_name[80];
- (*message)(LDPL_INFO,
- "%s: claim file hook called (offset = %ld, size = %ld)",
- file->name, (long)file->offset, (long)file->filesize);
- /* Look for matching syms file for an archive member. */
- if (file->offset == 0)
- snprintf(syms_name, sizeof(syms_name), "%s.syms", file->name);
- else
- snprintf(syms_name, sizeof(syms_name), "%s-%d.syms",
- file->name, (int)file->offset);
- irfile = fopen(syms_name, "r");
- if (irfile != NULL)
- {
- irfile_was_opened = 1;
- end_offset = 1 << 20;
- }
- /* Otherwise, see if the file itself is a syms file. */
- if (!irfile_was_opened)
- {
- irfile = fdopen(file->fd, "r");
- (void)fseek(irfile, file->offset, SEEK_SET);
- end_offset = file->offset + file->filesize;
- }
- /* Look for the beginning of output from readelf -s. */
- len = fread(buf, 1, 13, irfile);
- if (len < 13 || strncmp(buf, "\nSymbol table", 13) != 0)
- return LDPS_OK;
- /* Skip the two header lines. */
- (void) fgets(buf, sizeof(buf), irfile);
- (void) fgets(buf, sizeof(buf), irfile);
- if (add_symbols == NULL)
- {
- fprintf(stderr, "tv_add_symbols interface missing\n");
- return LDPS_ERR;
- }
- /* Parse the output from readelf. The columns are:
- Index Value Size Type Binding Visibility Section Name. */
- syms = (struct ld_plugin_symbol*)malloc(sizeof(struct ld_plugin_symbol) * 8);
- if (syms == NULL)
- return LDPS_ERR;
- maxsyms = 8;
- while (ftell(irfile) < end_offset
- && fgets(buf, sizeof(buf), irfile) != NULL)
- {
- parse_readelf_line(buf, &info);
- /* Ignore local symbols. */
- if (strncmp(info.bind, "LOCAL", 5) == 0)
- continue;
- weak = strncmp(info.bind, "WEAK", 4) == 0;
- if (strncmp(info.sect, "UND", 3) == 0)
- def = weak ? LDPK_WEAKUNDEF : LDPK_UNDEF;
- else if (strncmp(info.sect, "COM", 3) == 0)
- def = LDPK_COMMON;
- else
- def = weak ? LDPK_WEAKDEF : LDPK_DEF;
- if (strncmp(info.vis, "INTERNAL", 8) == 0)
- vis = LDPV_INTERNAL;
- else if (strncmp(info.vis, "HIDDEN", 6) == 0)
- vis = LDPV_HIDDEN;
- else if (strncmp(info.vis, "PROTECTED", 9) == 0)
- vis = LDPV_PROTECTED;
- else
- vis = LDPV_DEFAULT;
- /* If the symbol is listed in the options list, special-case
- it as a comdat symbol. */
- is_comdat = 0;
- for (i = 0; i < nopts; ++i)
- {
- if (info.name != NULL && strcmp(info.name, opts[i]) == 0)
- {
- is_comdat = 1;
- break;
- }
- }
- if (nsyms >= maxsyms)
- {
- syms = (struct ld_plugin_symbol*)
- realloc(syms, sizeof(struct ld_plugin_symbol) * maxsyms * 2);
- if (syms == NULL)
- return LDPS_ERR;
- maxsyms *= 2;
- }
- if (info.name == NULL)
- syms[nsyms].name = NULL;
- else
- {
- len = strlen(info.name);
- syms[nsyms].name = malloc(len + 1);
- strncpy(syms[nsyms].name, info.name, len + 1);
- }
- syms[nsyms].version = NULL;
- syms[nsyms].def = def;
- syms[nsyms].visibility = vis;
- syms[nsyms].size = info.size;
- syms[nsyms].comdat_key = is_comdat ? syms[nsyms].name : NULL;
- syms[nsyms].resolution = LDPR_UNKNOWN;
- ++nsyms;
- }
- claimed_file = (struct claimed_file*) malloc(sizeof(struct claimed_file));
- if (claimed_file == NULL)
- return LDPS_ERR;
- claimed_file->name = file->name;
- claimed_file->handle = file->handle;
- claimed_file->nsyms = nsyms;
- claimed_file->syms = syms;
- claimed_file->next = NULL;
- if (last_claimed_file == NULL)
- first_claimed_file = claimed_file;
- else
- last_claimed_file->next = claimed_file;
- last_claimed_file = claimed_file;
- (*message)(LDPL_INFO, "%s: claiming file, adding %d symbols",
- file->name, nsyms);
- if (nsyms > 0)
- (*add_symbols)(file->handle, nsyms, syms);
- *claimed = 1;
- if (irfile_was_opened)
- fclose(irfile);
- return LDPS_OK;
- }
- enum ld_plugin_status
- all_symbols_read_hook(void)
- {
- int i;
- const char* res;
- struct claimed_file* claimed_file;
- struct ld_plugin_input_file file;
- FILE* irfile;
- off_t end_offset;
- struct sym_info info;
- int len;
- char buf[160];
- char* p;
- const char* filename;
- (*message)(LDPL_INFO, "all symbols read hook called");
- if (get_symbols_v2 == NULL)
- {
- fprintf(stderr, "tv_get_symbols (v2) interface missing\n");
- return LDPS_ERR;
- }
- for (claimed_file = first_claimed_file;
- claimed_file != NULL;
- claimed_file = claimed_file->next)
- {
- (*get_symbols_v2)(claimed_file->handle, claimed_file->nsyms,
- claimed_file->syms);
- for (i = 0; i < claimed_file->nsyms; ++i)
- {
- switch (claimed_file->syms[i].resolution)
- {
- case LDPR_UNKNOWN:
- res = "UNKNOWN";
- break;
- case LDPR_UNDEF:
- res = "UNDEF";
- break;
- case LDPR_PREVAILING_DEF:
- res = "PREVAILING_DEF_REG";
- break;
- case LDPR_PREVAILING_DEF_IRONLY:
- res = "PREVAILING_DEF_IRONLY";
- break;
- case LDPR_PREVAILING_DEF_IRONLY_EXP:
- res = "PREVAILING_DEF_IRONLY_EXP";
- break;
- case LDPR_PREEMPTED_REG:
- res = "PREEMPTED_REG";
- break;
- case LDPR_PREEMPTED_IR:
- res = "PREEMPTED_IR";
- break;
- case LDPR_RESOLVED_IR:
- res = "RESOLVED_IR";
- break;
- case LDPR_RESOLVED_EXEC:
- res = "RESOLVED_EXEC";
- break;
- case LDPR_RESOLVED_DYN:
- res = "RESOLVED_DYN";
- break;
- default:
- res = "?";
- break;
- }
- (*message)(LDPL_INFO, "%s: %s: %s", claimed_file->name,
- claimed_file->syms[i].name, res);
- }
- }
- if (add_input_file == NULL)
- {
- fprintf(stderr, "tv_add_input_file interface missing\n");
- return LDPS_ERR;
- }
- if (get_input_file == NULL)
- {
- fprintf(stderr, "tv_get_input_file interface missing\n");
- return LDPS_ERR;
- }
- if (release_input_file == NULL)
- {
- fprintf(stderr, "tv_release_input_file interface missing\n");
- return LDPS_ERR;
- }
- for (claimed_file = first_claimed_file;
- claimed_file != NULL;
- claimed_file = claimed_file->next)
- {
- int irfile_was_opened = 0;
- char syms_name[80];
- (*get_input_file) (claimed_file->handle, &file);
- if (file.offset == 0)
- snprintf(syms_name, sizeof(syms_name), "%s.syms", file.name);
- else
- snprintf(syms_name, sizeof(syms_name), "%s-%d.syms",
- file.name, (int)file.offset);
- irfile = fopen(syms_name, "r");
- if (irfile != NULL)
- {
- irfile_was_opened = 1;
- end_offset = 1 << 20;
- }
- if (!irfile_was_opened)
- {
- irfile = fdopen(file.fd, "r");
- (void)fseek(irfile, file.offset, SEEK_SET);
- end_offset = file.offset + file.filesize;
- }
- /* Look for the beginning of output from readelf -s. */
- len = fread(buf, 1, 13, irfile);
- if (len < 13 || strncmp(buf, "\nSymbol table", 13) != 0)
- {
- fprintf(stderr, "%s: can't re-read original input file\n",
- claimed_file->name);
- return LDPS_ERR;
- }
- /* Skip the two header lines. */
- (void) fgets(buf, sizeof(buf), irfile);
- (void) fgets(buf, sizeof(buf), irfile);
- filename = NULL;
- while (ftell(irfile) < end_offset
- && fgets(buf, sizeof(buf), irfile) != NULL)
- {
- parse_readelf_line(buf, &info);
- /* Look for file name. */
- if (strncmp(info.type, "FILE", 4) == 0)
- {
- len = strlen(info.name);
- p = malloc(len + 1);
- strncpy(p, info.name, len + 1);
- filename = p;
- break;
- }
- }
- if (irfile_was_opened)
- fclose(irfile);
- (*release_input_file) (claimed_file->handle);
- if (filename == NULL)
- filename = claimed_file->name;
- if (claimed_file->nsyms == 0)
- continue;
- if (strlen(filename) >= sizeof(buf))
- {
- (*message)(LDPL_FATAL, "%s: filename too long", filename);
- return LDPS_ERR;
- }
- strcpy(buf, filename);
- p = strrchr(buf, '.');
- if (p == NULL
- || (strcmp(p, ".syms") != 0
- && strcmp(p, ".c") != 0
- && strcmp(p, ".cc") != 0))
- {
- (*message)(LDPL_FATAL, "%s: filename has unknown suffix",
- filename);
- return LDPS_ERR;
- }
- p[1] = 'o';
- p[2] = '\0';
- (*message)(LDPL_INFO, "%s: adding new input file", buf);
- (*add_input_file)(buf);
- }
- return LDPS_OK;
- }
- enum ld_plugin_status
- cleanup_hook(void)
- {
- (*message)(LDPL_INFO, "cleanup hook called");
- return LDPS_OK;
- }
- static void
- parse_readelf_line(char* p, struct sym_info* info)
- {
- int len;
- p += strspn(p, " ");
- /* Index field. */
- p += strcspn(p, " ");
- p += strspn(p, " ");
- /* Value field. */
- p += strcspn(p, " ");
- p += strspn(p, " ");
- /* Size field. */
- info->size = atoi(p);
- p += strcspn(p, " ");
- p += strspn(p, " ");
- /* Type field. */
- info->type = p;
- p += strcspn(p, " ");
- p += strspn(p, " ");
- /* Binding field. */
- info->bind = p;
- p += strcspn(p, " ");
- p += strspn(p, " ");
- /* Visibility field. */
- info->vis = p;
- p += strcspn(p, " ");
- p += strspn(p, " ");
- if (*p == '[')
- {
- /* Skip st_other. */
- p += strcspn(p, "]");
- p += strspn(p, "] ");
- }
- /* Section field. */
- info->sect = p;
- p += strcspn(p, " ");
- p += strspn(p, " ");
- /* Name field. */
- /* FIXME: Look for version. */
- len = strlen(p);
- if (len == 0)
- p = NULL;
- else if (p[len-1] == '\n')
- p[--len] = '\0';
- info->name = p;
- }
|