/* test_plugin.c -- simple linker plugin test
- Copyright 2008 Free Software Foundation, Inc.
+ Copyright (C) 2008-2020 Free Software Foundation, Inc.
Written by Cary Coutant <ccoutant@google.com>.
This file is part of gold.
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>
struct claimed_file* next;
};
+struct sym_info
+{
+ int size;
+ char* type;
+ char* bind;
+ char* vis;
+ char* sect;
+ char* name;
+ char* ver;
+};
+
static struct claimed_file* first_claimed_file = NULL;
static struct claimed_file* last_claimed_file = 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_get_symbols get_symbols_v3 = 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;
+static ld_plugin_get_wrap_symbols get_wrap_symbols = NULL;
#define MAXOPTS 10
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)
{
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_GET_SYMBOLS_V3:
+ get_symbols_v3 = 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;
+ case LDPT_GET_WRAP_SYMBOLS:
+ get_wrap_symbols = *entry->tv_u.tv_get_wrap_symbols;
+ break;
default:
break;
}
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;
+ }
+
+ if (get_wrap_symbols == NULL)
+ {
+ fprintf(stderr, "tv_get_wrap_symbols interface missing\n");
+ return LDPS_ERR;
+ }
+ else
+ {
+ const char **wrap_symbols;
+ uint64_t count = 0;
+ if (get_wrap_symbols(&count, &wrap_symbols) == LDPS_OK)
+ {
+ (*message)(LDPL_INFO, "Number of wrap symbols = %lu", count);
+ for (; count > 0; --count)
+ (*message)(LDPL_INFO, "Wrap symbol %s", wrap_symbols[count - 1]);
+ }
+ else
+ {
+ fprintf(stderr, "tv_get_wrap_symbols interface call failed\n");
+ return LDPS_ERR;
+ }
+ }
+
return LDPS_OK;
}
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;
- char *p;
- char *pbind;
- char *pvis;
- char *psect;
+ struct sym_info info;
int weak;
int def;
int vis;
- int size;
- char* name;
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. */
- 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;
if (syms == NULL)
return LDPS_ERR;
maxsyms = 8;
- while (fgets(buf, sizeof(buf), irfile) != NULL)
+ while (ftell(irfile) < end_offset
+ && 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);
+ parse_readelf_line(buf, &info);
/* Ignore local symbols. */
- if (strncmp(pbind, "LOCAL", 5) == 0)
+ if (strncmp(info.bind, "LOCAL", 5) == 0)
continue;
- weak = strncmp(pbind, "WEAK", 4) == 0;
- if (strncmp(psect, "UND", 3) == 0)
+ weak = strncmp(info.bind, "WEAK", 4) == 0;
+ if (strncmp(info.sect, "UND", 3) == 0)
def = weak ? LDPK_WEAKUNDEF : LDPK_UNDEF;
- else if (strncmp(psect, "COM", 3) == 0)
+ else if (strncmp(info.sect, "COM", 3) == 0)
def = LDPK_COMMON;
else
def = weak ? LDPK_WEAKDEF : LDPK_DEF;
- if (strncmp(pvis, "INTERNAL", 8) == 0)
+ if (strncmp(info.vis, "INTERNAL", 8) == 0)
vis = LDPV_INTERNAL;
- else if (strncmp(pvis, "HIDDEN", 6) == 0)
+ else if (strncmp(info.vis, "HIDDEN", 6) == 0)
vis = LDPV_HIDDEN;
- else if (strncmp(pvis, "PROTECTED", 9) == 0)
+ else if (strncmp(info.vis, "PROTECTED", 9) == 0)
vis = LDPV_PROTECTED;
else
vis = LDPV_DEFAULT;
is_comdat = 0;
for (i = 0; i < nopts; ++i)
{
- if (name != NULL && strcmp(name, opts[i]) == 0)
+ if (info.name != NULL && strcmp(info.name, opts[i]) == 0)
{
is_comdat = 1;
break;
maxsyms *= 2;
}
- syms[nsyms].name = name;
- syms[nsyms].version = NULL;
+ 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);
+ }
+ if (info.ver == NULL)
+ syms[nsyms].version = NULL;
+ else
+ {
+ len = strlen(info.ver);
+ syms[nsyms].version = malloc(len + 1);
+ strncpy(syms[nsyms].version, info.ver, len + 1);
+ }
syms[nsyms].def = def;
syms[nsyms].visibility = vis;
- syms[nsyms].size = size;
- syms[nsyms].comdat_key = is_comdat ? name : NULL;
+ syms[nsyms].size = info.size;
+ syms[nsyms].comdat_key = is_comdat ? syms[nsyms].name : NULL;
syms[nsyms].resolution = LDPR_UNKNOWN;
++nsyms;
}
(*add_symbols)(file->handle, nsyms, syms);
*claimed = 1;
+ if (irfile_was_opened)
+ fclose(irfile);
return LDPS_OK;
}
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;
+ char* p;
+ const char* filename;
(*message)(LDPL_INFO, "all symbols read hook called");
- if (get_symbols == NULL)
+ if (get_symbols_v3 == NULL)
{
- fprintf(stderr, "tv_get_symbols interface missing\n");
+ fprintf(stderr, "tv_get_symbols (v3) interface missing\n");
return LDPS_ERR;
}
claimed_file != NULL;
claimed_file = claimed_file->next)
{
- (*get_symbols)(claimed_file->handle, claimed_file->nsyms,
- claimed_file->syms);
+ enum ld_plugin_status status = (*get_symbols_v3)(
+ claimed_file->handle, claimed_file->nsyms, claimed_file->syms);
+ if (status == LDPS_NO_SYMS)
+ {
+ (*message)(LDPL_INFO, "%s: no symbols", claimed_file->name);
+ continue;
+ }
+
for (i = 0; i < claimed_file->nsyms; ++i)
{
switch (claimed_file->syms[i].resolution)
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;
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(claimed_file->name) >= sizeof(buf))
+
+ if (strlen(filename) >= sizeof(buf))
{
- (*message)(LDPL_FATAL, "%s: filename too long", claimed_file->name);
+ (*message)(LDPL_FATAL, "%s: filename too long", filename);
return LDPS_ERR;
}
- strcpy(buf, claimed_file->name);
+ strcpy(buf, filename);
p = strrchr(buf, '.');
- if (p == NULL || strcmp(p, ".syms") != 0)
+ if (p == NULL
+ || (strcmp(p, ".syms") != 0
+ && strcmp(p, ".c") != 0
+ && strcmp(p, ".cc") != 0))
{
- (*message)(LDPL_FATAL, "%s: filename must have '.syms' suffix",
- claimed_file->name);
+ (*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);
}
(*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. */
+ len = strcspn(p, "@\n");
+ if (len > 0 && p[len] == '@')
+ {
+ /* Get the symbol version. */
+ char* vp = p + len;
+ int vlen;
+
+ vp += strspn(vp, "@");
+ vlen = strcspn(vp, "\n");
+ vp[vlen] = '\0';
+ if (vlen > 0)
+ info->ver = vp;
+ else
+ info->ver = NULL;
+ }
+ else
+ info->ver = NULL;
+ p[len] = '\0';
+ if (len > 0)
+ info->name = p;
+ else
+ info->name = NULL;
+}