summaryrefslogtreecommitdiff
path: root/gold/testsuite/plugin_test.c
diff options
context:
space:
mode:
Diffstat (limited to 'gold/testsuite/plugin_test.c')
-rw-r--r--gold/testsuite/plugin_test.c427
1 files changed, 427 insertions, 0 deletions
diff --git a/gold/testsuite/plugin_test.c b/gold/testsuite/plugin_test.c
new file mode 100644
index 00000000000..c60c7a1f858
--- /dev/null
+++ b/gold/testsuite/plugin_test.c
@@ -0,0 +1,427 @@
+/* test_plugin.c -- simple linker plugin test
+
+ Copyright 2008 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. */
+
+#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;
+};
+
+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_add_input_file add_input_file = NULL;
+static ld_plugin_message message = 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);
+
+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_ADD_INPUT_FILE:
+ add_input_file = entry->tv_u.tv_add_input_file;
+ break;
+ case LDPT_MESSAGE:
+ message = entry->tv_u.tv_message;
+ 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;
+ }
+
+ return LDPS_OK;
+}
+
+enum ld_plugin_status
+claim_file_hook (const struct ld_plugin_input_file* file, int* claimed)
+{
+ int len;
+ char buf[160];
+ struct claimed_file* claimed_file;
+ struct ld_plugin_symbol* syms;
+ int nsyms = 0;
+ int maxsyms = 0;
+ FILE* irfile;
+ char *p;
+ char *pbind;
+ char *pvis;
+ char *psect;
+ int weak;
+ int def;
+ int vis;
+ int size;
+ char* name;
+ int is_comdat;
+ int i;
+
+ (*message)(LDPL_INFO,
+ "%s: claim file hook called (offset = %ld, size = %ld)",
+ file->name, (long)file->offset, (long)file->filesize);
+
+ /* Look for the beginning of output from readelf -s. */
+ irfile = fdopen(file->fd, "r");
+ (void)fseek(irfile, file->offset, SEEK_SET);
+ 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 (fgets(buf, sizeof(buf), irfile) != NULL)
+ {
+ p = buf;
+ p += strspn(p, " ");
+
+ /* Index field. */
+ p += strcspn(p, " ");
+ p += strspn(p, " ");
+
+ /* Value field. */
+ p += strcspn(p, " ");
+ p += strspn(p, " ");
+
+ /* Size field. */
+ size = atoi(p);
+ p += strcspn(p, " ");
+ p += strspn(p, " ");
+
+ /* Type field. */
+ p += strcspn(p, " ");
+ p += strspn(p, " ");
+
+ /* Binding field. */
+ pbind = p;
+ p += strcspn(p, " ");
+ p += strspn(p, " ");
+
+ /* Visibility field. */
+ pvis = p;
+ p += strcspn(p, " ");
+ p += strspn(p, " ");
+
+ /* Section field. */
+ psect = p;
+ p += strcspn(p, " ");
+ p += strspn(p, " ");
+
+ /* Name field. */
+ /* FIXME: Look for version. */
+ len = strlen(p);
+ if (p[len-1] == '\n')
+ p[--len] = '\0';
+ name = malloc(len + 1);
+ strncpy(name, p, len + 1);
+
+ /* Ignore local symbols. */
+ if (strncmp(pbind, "LOCAL", 5) == 0)
+ continue;
+
+ weak = strncmp(pbind, "WEAK", 4) == 0;
+ if (strncmp(psect, "UND", 3) == 0)
+ def = weak ? LDPK_WEAKUNDEF : LDPK_UNDEF;
+ else if (strncmp(psect, "COM", 3) == 0)
+ def = LDPK_COMMON;
+ else
+ def = weak ? LDPK_WEAKDEF : LDPK_DEF;
+
+ if (strncmp(pvis, "INTERNAL", 8) == 0)
+ vis = LDPV_INTERNAL;
+ else if (strncmp(pvis, "HIDDEN", 6) == 0)
+ vis = LDPV_HIDDEN;
+ else if (strncmp(pvis, "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 (name != NULL && strcmp(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;
+ }
+
+ syms[nsyms].name = name;
+ syms[nsyms].version = NULL;
+ syms[nsyms].def = def;
+ syms[nsyms].visibility = vis;
+ syms[nsyms].size = size;
+ syms[nsyms].comdat_key = is_comdat ? 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;
+
+ (*add_symbols)(file->handle, nsyms, syms);
+
+ *claimed = 1;
+ return LDPS_OK;
+}
+
+enum ld_plugin_status
+all_symbols_read_hook(void)
+{
+ int i;
+ const char* res;
+ struct claimed_file* claimed_file;
+ char buf[160];
+ char *p;
+
+ (*message)(LDPL_INFO, "all symbols read hook called");
+
+ if (get_symbols == NULL)
+ {
+ fprintf(stderr, "tv_get_symbols interface missing\n");
+ return LDPS_ERR;
+ }
+
+ for (claimed_file = first_claimed_file;
+ claimed_file != NULL;
+ claimed_file = claimed_file->next)
+ {
+ (*get_symbols)(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_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;
+ }
+
+ for (claimed_file = first_claimed_file;
+ claimed_file != NULL;
+ claimed_file = claimed_file->next)
+ {
+ if (strlen(claimed_file->name) >= sizeof(buf))
+ {
+ (*message)(LDPL_FATAL, "%s: filename too long", claimed_file->name);
+ return LDPS_ERR;
+ }
+ strcpy(buf, claimed_file->name);
+ p = strrchr(buf, '.');
+ if (p == NULL || strcmp(p, ".syms") != 0)
+ {
+ (*message)(LDPL_FATAL, "%s: filename must have '.syms' suffix",
+ claimed_file->name);
+ return LDPS_ERR;
+ }
+ p[1] = 'o';
+ p[2] = '\0';
+ (*add_input_file)(buf);
+ }
+
+ return LDPS_OK;
+}
+
+enum ld_plugin_status
+cleanup_hook(void)
+{
+ (*message)(LDPL_INFO, "cleanup hook called");
+ return LDPS_OK;
+}