diff options
author | Cary Coutant <ccoutant@google.com> | 2008-09-19 22:54:57 +0000 |
---|---|---|
committer | Cary Coutant <ccoutant@google.com> | 2008-09-19 22:54:57 +0000 |
commit | 89fc34211be8b1d74b83e4e2b18cfa4b4cf65ba9 (patch) | |
tree | cd5675e9053bb0b9b68d98253003c08bc5e28bb9 /gold/testsuite/plugin_test.c | |
parent | 14fc49fb1561bd620e790080ad27ed3890780b42 (diff) | |
download | binutils-gdb-89fc34211be8b1d74b83e4e2b18cfa4b4cf65ba9.tar.gz |
Add plugin functionality for link-time optimization (LTO).
include/:
* plugin-api.h: New file.
gold/:
* configure.ac (plugins): Add --enable-plugins option.
* configure: Regenerate.
* config.in: Regenerate.
* Makefile.am (LIBDL): New variable.
(CCFILES): Add plugin.cc.
(HFILES): Add plugin.h.
(ldadd_var): Add LIBDL.
* Makefile.in: Regenerate.
* archive.cc: Include "plugin.h".
(Archive::setup): Don't preread archive symbols when using a plugin.
(Archive::get_file_and_offset): Add memsize parameter. Change callers.
(Archive::get_elf_object_for_member): Call plugin hooks for claiming
files.
(Archive::include_member): Add symbols from plugin objects.
* archive.h (Archive::get_file_and_offset): Add memsize parameter.
* descriptors.cc (Descriptors::open): Check for file descriptors
abandoned by plugins.
(Descriptors::claim_for_plugin): New function.
* descriptors.h (Descriptors::claim_for_plugin): New function.
(Open_descriptor::is_claimed): New field.
(claim_descriptor_for_plugin): New function.
* fileread.cc (File_read::claim_for_plugin): New function.
* fileread.h (File_read::claim_for_plugin): New function.
(File_read::descriptor): New function.
* gold.cc: Include "plugin.h".
(queue_initial_tasks): Add task to call plugin hooks for generating
new object files.
* main.cc: Include "plugin.h".
(main): Load plugin libraries.
* object.h (Pluginobj): Declare.
(Object::pluginobj): New function.
(Object::do_pluginobj): New function.
(Object::set_target): New function.
* options.cc: Include "plugin.h".
(General_options::parse_plugin): New function.
(General_options::General_options): Initialize plugins_ field.
(General_options::add_plugin): New function.
* options.h (Plugin_manager): Declare.
(General_options): Add --plugin option.
(General_options::has_plugins): New function.
(General_options::plugins): New function.
(General_options::add_plugin): New function.
(General_options::plugins_): New field.
* plugin.cc: New file.
* plugin.h: New file.
* readsyms.cc: Include "plugin.h".
(Read_symbols::do_read_symbols): Check for archive before checking
for ELF file. Call plugin hooks to claim files.
* resolve.cc (Symbol_table::resolve): Record when symbol is referenced
from a real object file; force override when processing replacement
files.
* symtab.cc (Symbol::init_fields): Initialize in_real_elf_ field.
(Symbol::init_base_object): Likewise.
(Symbol::init_base_output_data): Likewise.
(Symbol::init_base_output_segment): Likewise.
(Symbol::init_base_constant): Likewise.
(Symbol::init_base_undefined): Likewise.
(Symbol::output_section): Assert that object is not a plugin.
(Symbol_table::add_from_pluginobj): New function.
(Symbol_table::sized_finalize_symbol): Treat symbols from plugins as
undefined.
(Symbol_table::sized_write_globals): Likewise.
(Symbol_table::add_from_pluginobj): Instantiate template.
* symtab.h (Sized_pluginobj): Declare.
(Symbol::in_real_elf): New function.
(Symbol::set_in_real_elf): New function.
(Symbol::in_real_elf_): New field.
(Symbol_table::add_from_pluginobj): New function.
* testsuite/Makefile.am (AM_CFLAGS): New variable.
(LIBDL): New variable.
(LDADD): Add LIBDL.
(check_PROGRAMS): Add plugin_test_1 and plugin_test_2.
(check_SCRIPTS): Add plugin_test_1.sh and plugin_test_2.sh.
(check_DATA): Add plugin_test_1.err and plugin_test_2.err.
(MOSTLYCLEANFILES): Likewise.
* testsuite/Makefile.in: Regenerate.
* testsuite/plugin_test.c: New file.
* testsuite/plugin_test_1.sh: New file.
* testsuite/plugin_test_2.sh: New file.
Diffstat (limited to 'gold/testsuite/plugin_test.c')
-rw-r--r-- | gold/testsuite/plugin_test.c | 427 |
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; +} |