| /* Test plugin for the GNU linker. |
| Copyright (C) 2010-2022 Free Software Foundation, Inc. |
| |
| This file is part of the GNU Binutils. |
| |
| 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 "sysdep.h" |
| #include "bfd.h" |
| #if BFD_SUPPORTS_PLUGINS |
| #include "plugin-api.h" |
| /* For ARRAY_SIZE macro only - we don't link the library itself. */ |
| #include "libiberty.h" |
| |
| #include <ctype.h> /* For isdigit. */ |
| |
| extern enum ld_plugin_status onload (struct ld_plugin_tv *tv); |
| static enum ld_plugin_status onclaim_file (const struct ld_plugin_input_file *file, |
| int *claimed); |
| static enum ld_plugin_status onall_symbols_read (void); |
| static enum ld_plugin_status oncleanup (void); |
| |
| /* Helper for calling plugin api message function. */ |
| #define TV_MESSAGE if (tv_message) (*tv_message) |
| |
| /* Struct for recording files to claim / files claimed. */ |
| typedef struct claim_file |
| { |
| struct claim_file *next; |
| struct ld_plugin_input_file file; |
| bool claimed; |
| struct ld_plugin_symbol *symbols; |
| int n_syms_allocated; |
| int n_syms_used; |
| } claim_file_t; |
| |
| /* Types of things that can be added at all symbols read time. */ |
| typedef enum addfile_enum |
| { |
| ADD_FILE, |
| ADD_LIB, |
| ADD_DIR |
| } addfile_enum_t; |
| |
| /* Struct for recording files to add to final link. */ |
| typedef struct add_file |
| { |
| struct add_file *next; |
| const char *name; |
| addfile_enum_t type; |
| } add_file_t; |
| |
| /* Helper macro for defining array of transfer vector tags and names. */ |
| #define ADDENTRY(tag) { tag, #tag } |
| |
| /* Struct for looking up human-readable versions of tag names. */ |
| typedef struct tag_name |
| { |
| enum ld_plugin_tag tag; |
| const char *name; |
| } tag_name_t; |
| |
| /* Array of all known tags and their names. */ |
| static const tag_name_t tag_names[] = |
| { |
| ADDENTRY(LDPT_NULL), |
| ADDENTRY(LDPT_API_VERSION), |
| ADDENTRY(LDPT_GOLD_VERSION), |
| ADDENTRY(LDPT_LINKER_OUTPUT), |
| ADDENTRY(LDPT_OPTION), |
| ADDENTRY(LDPT_REGISTER_CLAIM_FILE_HOOK), |
| ADDENTRY(LDPT_REGISTER_ALL_SYMBOLS_READ_HOOK), |
| ADDENTRY(LDPT_REGISTER_CLEANUP_HOOK), |
| ADDENTRY(LDPT_ADD_SYMBOLS), |
| ADDENTRY(LDPT_GET_SYMBOLS), |
| ADDENTRY(LDPT_GET_SYMBOLS_V2), |
| ADDENTRY(LDPT_ADD_INPUT_FILE), |
| ADDENTRY(LDPT_MESSAGE), |
| ADDENTRY(LDPT_GET_INPUT_FILE), |
| ADDENTRY(LDPT_GET_VIEW), |
| ADDENTRY(LDPT_RELEASE_INPUT_FILE), |
| ADDENTRY(LDPT_ADD_INPUT_LIBRARY), |
| ADDENTRY(LDPT_OUTPUT_NAME), |
| ADDENTRY(LDPT_SET_EXTRA_LIBRARY_PATH), |
| ADDENTRY(LDPT_GNU_LD_VERSION) |
| }; |
| |
| /* Function pointers to cache hooks passed at onload time. */ |
| static ld_plugin_register_claim_file tv_register_claim_file = 0; |
| static ld_plugin_register_all_symbols_read tv_register_all_symbols_read = 0; |
| static ld_plugin_register_cleanup tv_register_cleanup = 0; |
| static ld_plugin_add_symbols tv_add_symbols = 0; |
| static ld_plugin_get_symbols tv_get_symbols = 0; |
| static ld_plugin_get_symbols tv_get_symbols_v2 = 0; |
| static ld_plugin_add_input_file tv_add_input_file = 0; |
| static ld_plugin_message tv_message = 0; |
| static ld_plugin_get_input_file tv_get_input_file = 0; |
| static ld_plugin_get_view tv_get_view = 0; |
| static ld_plugin_release_input_file tv_release_input_file = 0; |
| static ld_plugin_add_input_library tv_add_input_library = 0; |
| static ld_plugin_set_extra_library_path tv_set_extra_library_path = 0; |
| |
| /* Other cached info from the transfer vector. */ |
| static enum ld_plugin_output_file_type linker_output; |
| static const char *output_name; |
| |
| /* Behaviour control flags set by plugin options. */ |
| static enum ld_plugin_status onload_ret = LDPS_OK; |
| static enum ld_plugin_status claim_file_ret = LDPS_OK; |
| static enum ld_plugin_status all_symbols_read_ret = LDPS_OK; |
| static enum ld_plugin_status cleanup_ret = LDPS_OK; |
| static bool register_claimfile_hook = false; |
| static bool register_allsymbolsread_hook = false; |
| static bool register_cleanup_hook = false; |
| static bool dumpresolutions = false; |
| |
| /* The master list of all claimable/claimed files. */ |
| static claim_file_t *claimfiles_list = NULL; |
| |
| /* We keep a tail pointer for easy linking on the end. */ |
| static claim_file_t **claimfiles_tail_chain_ptr = &claimfiles_list; |
| |
| /* The last claimed file added to the list, for receiving syms. */ |
| static claim_file_t *last_claimfile = NULL; |
| |
| /* The master list of all files to add to the final link. */ |
| static add_file_t *addfiles_list = NULL; |
| |
| /* We keep a tail pointer for easy linking on the end. */ |
| static add_file_t **addfiles_tail_chain_ptr = &addfiles_list; |
| |
| /* Number of bytes read in claim file before deciding if the file can be |
| claimed. */ |
| static int bytes_to_read_before_claim = 0; |
| |
| /* Add a new claimfile on the end of the chain. */ |
| static enum ld_plugin_status |
| record_claim_file (const char *file) |
| { |
| claim_file_t *newfile; |
| |
| newfile = malloc (sizeof *newfile); |
| if (!newfile) |
| return LDPS_ERR; |
| memset (newfile, 0, sizeof *newfile); |
| /* Only setup for now is remembering the name to look for. */ |
| newfile->file.name = file; |
| /* Chain it on the end of the list. */ |
| *claimfiles_tail_chain_ptr = newfile; |
| claimfiles_tail_chain_ptr = &newfile->next; |
| /* Record it as active for receiving symbols to register. */ |
| last_claimfile = newfile; |
| return LDPS_OK; |
| } |
| |
| /* How many bytes to read before claiming (or not) an input file. */ |
| static enum ld_plugin_status |
| record_read_length (const char *length) |
| { |
| const char *tmp; |
| |
| tmp = length; |
| while (*tmp != '\0' && isdigit (*tmp)) |
| ++tmp; |
| if (*tmp != '\0' || *length == '\0') |
| return LDPS_ERR; |
| |
| bytes_to_read_before_claim = atoi (length); |
| return LDPS_OK; |
| } |
| |
| /* Add a new addfile on the end of the chain. */ |
| static enum ld_plugin_status |
| record_add_file (const char *file, addfile_enum_t type) |
| { |
| add_file_t *newfile; |
| |
| newfile = malloc (sizeof *newfile); |
| if (!newfile) |
| return LDPS_ERR; |
| newfile->next = NULL; |
| newfile->name = file; |
| newfile->type = type; |
| /* Chain it on the end of the list. */ |
| *addfiles_tail_chain_ptr = newfile; |
| addfiles_tail_chain_ptr = &newfile->next; |
| return LDPS_OK; |
| } |
| |
| /* Parse a command-line argument string into a symbol definition. |
| Symbol-strings follow the colon-separated format: |
| NAME:VERSION:def:vis:size:COMDATKEY |
| where the fields in capitals are strings and those in lower |
| case are integers. We don't allow to specify a resolution as |
| doing so is not meaningful when calling the add symbols hook. */ |
| static enum ld_plugin_status |
| parse_symdefstr (const char *str, struct ld_plugin_symbol *sym) |
| { |
| int n; |
| long long size; |
| const char *colon1, *colon2, *colon5; |
| |
| /* Locate the colons separating the first two strings. */ |
| colon1 = strchr (str, ':'); |
| if (!colon1) |
| return LDPS_ERR; |
| colon2 = strchr (colon1+1, ':'); |
| if (!colon2) |
| return LDPS_ERR; |
| /* Name must not be empty (version may be). */ |
| if (colon1 == str) |
| return LDPS_ERR; |
| |
| /* The fifth colon and trailing comdat key string are optional, |
| but the intermediate ones must all be present. */ |
| colon5 = strchr (colon2+1, ':'); /* Actually only third so far. */ |
| if (!colon5) |
| return LDPS_ERR; |
| colon5 = strchr (colon5+1, ':'); /* Hopefully fourth now. */ |
| if (!colon5) |
| return LDPS_ERR; |
| colon5 = strchr (colon5+1, ':'); /* Optional fifth now. */ |
| |
| /* Finally we'll use sscanf to parse the numeric fields, then |
| we'll split out the strings which we need to allocate separate |
| storage for anyway so that we can add nul termination. */ |
| n = sscanf (colon2 + 1, "%hhi:%i:%lli", &sym->def, &sym->visibility, &size); |
| if (n != 3) |
| return LDPS_ERR; |
| |
| /* Parsed successfully, so allocate strings and fill out fields. */ |
| sym->size = size; |
| sym->unused = 0; |
| sym->section_kind = 0; |
| sym->symbol_type = 0; |
| sym->resolution = LDPR_UNKNOWN; |
| sym->name = malloc (colon1 - str + 1); |
| if (!sym->name) |
| return LDPS_ERR; |
| memcpy (sym->name, str, colon1 - str); |
| sym->name[colon1 - str] = '\0'; |
| if (colon2 > (colon1 + 1)) |
| { |
| sym->version = malloc (colon2 - colon1); |
| if (!sym->version) |
| return LDPS_ERR; |
| memcpy (sym->version, colon1 + 1, colon2 - (colon1 + 1)); |
| sym->version[colon2 - (colon1 + 1)] = '\0'; |
| } |
| else |
| sym->version = NULL; |
| if (colon5 && colon5[1]) |
| { |
| sym->comdat_key = malloc (strlen (colon5 + 1) + 1); |
| if (!sym->comdat_key) |
| return LDPS_ERR; |
| strcpy (sym->comdat_key, colon5 + 1); |
| } |
| else |
| sym->comdat_key = 0; |
| return LDPS_OK; |
| } |
| |
| /* Record a symbol to be added for the last-added claimfile. */ |
| static enum ld_plugin_status |
| record_claimed_file_symbol (const char *symdefstr) |
| { |
| struct ld_plugin_symbol sym; |
| |
| /* Can't add symbols except as belonging to claimed files. */ |
| if (!last_claimfile) |
| return LDPS_ERR; |
| |
| /* If string doesn't parse correctly, give an error. */ |
| if (parse_symdefstr (symdefstr, &sym) != LDPS_OK) |
| return LDPS_ERR; |
| |
| /* Check for enough space, resize array if needed, and add it. */ |
| if (last_claimfile->n_syms_allocated == last_claimfile->n_syms_used) |
| { |
| int new_n_syms = last_claimfile->n_syms_allocated |
| ? 2 * last_claimfile->n_syms_allocated |
| : 10; |
| last_claimfile->symbols = realloc (last_claimfile->symbols, |
| new_n_syms * sizeof *last_claimfile->symbols); |
| if (!last_claimfile->symbols) |
| return LDPS_ERR; |
| last_claimfile->n_syms_allocated = new_n_syms; |
| } |
| last_claimfile->symbols[last_claimfile->n_syms_used++] = sym; |
| |
| return LDPS_OK; |
| } |
| |
| /* Records the status to return from one of the registered hooks. */ |
| static enum ld_plugin_status |
| set_ret_val (const char *whichval, enum ld_plugin_status retval) |
| { |
| if (!strcmp ("onload", whichval)) |
| onload_ret = retval; |
| else if (!strcmp ("claimfile", whichval)) |
| claim_file_ret = retval; |
| else if (!strcmp ("allsymbolsread", whichval)) |
| all_symbols_read_ret = retval; |
| else if (!strcmp ("cleanup", whichval)) |
| cleanup_ret = retval; |
| else |
| return LDPS_ERR; |
| return LDPS_OK; |
| } |
| |
| /* Records hooks which should be registered. */ |
| static enum ld_plugin_status |
| set_register_hook (const char *whichhook, bool yesno) |
| { |
| if (!strcmp ("claimfile", whichhook)) |
| register_claimfile_hook = yesno; |
| else if (!strcmp ("allsymbolsread", whichhook)) |
| register_allsymbolsread_hook = yesno; |
| else if (!strcmp ("cleanup", whichhook)) |
| register_cleanup_hook = yesno; |
| else |
| return LDPS_ERR; |
| return LDPS_OK; |
| } |
| |
| /* Determine type of plugin option and pass to individual parsers. */ |
| static enum ld_plugin_status |
| parse_option (const char *opt) |
| { |
| if (!strncmp ("fail", opt, 4)) |
| return set_ret_val (opt + 4, LDPS_ERR); |
| else if (!strncmp ("pass", opt, 4)) |
| return set_ret_val (opt + 4, LDPS_OK); |
| else if (!strncmp ("register", opt, 8)) |
| return set_register_hook (opt + 8, true); |
| else if (!strncmp ("noregister", opt, 10)) |
| return set_register_hook (opt + 10, false); |
| else if (!strncmp ("claim:", opt, 6)) |
| return record_claim_file (opt + 6); |
| else if (!strncmp ("read:", opt, 5)) |
| return record_read_length (opt + 5); |
| else if (!strncmp ("sym:", opt, 4)) |
| return record_claimed_file_symbol (opt + 4); |
| else if (!strncmp ("add:", opt, 4)) |
| return record_add_file (opt + 4, ADD_FILE); |
| else if (!strncmp ("lib:", opt, 4)) |
| return record_add_file (opt + 4, ADD_LIB); |
| else if (!strncmp ("dir:", opt, 4)) |
| return record_add_file (opt + 4, ADD_DIR); |
| else if (!strcmp ("dumpresolutions", opt)) |
| dumpresolutions = true; |
| else |
| return LDPS_ERR; |
| return LDPS_OK; |
| } |
| |
| /* Output contents of transfer vector array entry in human-readable form. */ |
| static void |
| dump_tv_tag (size_t n, struct ld_plugin_tv *tv) |
| { |
| size_t tag; |
| char unknownbuf[40]; |
| const char *name; |
| |
| for (tag = 0; tag < ARRAY_SIZE (tag_names); tag++) |
| if (tag_names[tag].tag == tv->tv_tag) |
| break; |
| sprintf (unknownbuf, "unknown tag #%d", tv->tv_tag); |
| name = (tag < ARRAY_SIZE (tag_names)) ? tag_names[tag].name : unknownbuf; |
| switch (tv->tv_tag) |
| { |
| case LDPT_OPTION: |
| case LDPT_OUTPUT_NAME: |
| TV_MESSAGE (LDPL_INFO, "tv[%d]: %s '%s'", n, name, |
| tv->tv_u.tv_string); |
| break; |
| case LDPT_REGISTER_CLAIM_FILE_HOOK: |
| case LDPT_REGISTER_ALL_SYMBOLS_READ_HOOK: |
| case LDPT_REGISTER_CLEANUP_HOOK: |
| case LDPT_ADD_SYMBOLS: |
| case LDPT_GET_SYMBOLS: |
| case LDPT_GET_SYMBOLS_V2: |
| case LDPT_ADD_INPUT_FILE: |
| case LDPT_MESSAGE: |
| case LDPT_GET_INPUT_FILE: |
| case LDPT_GET_VIEW: |
| case LDPT_RELEASE_INPUT_FILE: |
| case LDPT_ADD_INPUT_LIBRARY: |
| case LDPT_SET_EXTRA_LIBRARY_PATH: |
| TV_MESSAGE (LDPL_INFO, "tv[%d]: %s func@0x%p", n, name, |
| (void *)(tv->tv_u.tv_message)); |
| break; |
| case LDPT_NULL: |
| case LDPT_API_VERSION: |
| case LDPT_GOLD_VERSION: |
| case LDPT_LINKER_OUTPUT: |
| case LDPT_GNU_LD_VERSION: |
| default: |
| TV_MESSAGE (LDPL_INFO, "tv[%d]: %s value %W (%d)", n, name, |
| (bfd_vma)tv->tv_u.tv_val, tv->tv_u.tv_val); |
| break; |
| } |
| } |
| |
| /* Handle/record information received in a transfer vector entry. */ |
| static enum ld_plugin_status |
| parse_tv_tag (struct ld_plugin_tv *tv) |
| { |
| #define SETVAR(x) x = tv->tv_u.x |
| switch (tv->tv_tag) |
| { |
| case LDPT_OPTION: |
| return parse_option (tv->tv_u.tv_string); |
| case LDPT_NULL: |
| case LDPT_GOLD_VERSION: |
| case LDPT_GNU_LD_VERSION: |
| case LDPT_API_VERSION: |
| default: |
| break; |
| case LDPT_OUTPUT_NAME: |
| output_name = tv->tv_u.tv_string; |
| break; |
| case LDPT_LINKER_OUTPUT: |
| linker_output = tv->tv_u.tv_val; |
| break; |
| case LDPT_REGISTER_CLAIM_FILE_HOOK: |
| SETVAR(tv_register_claim_file); |
| break; |
| case LDPT_REGISTER_ALL_SYMBOLS_READ_HOOK: |
| SETVAR(tv_register_all_symbols_read); |
| break; |
| case LDPT_REGISTER_CLEANUP_HOOK: |
| SETVAR(tv_register_cleanup); |
| break; |
| case LDPT_ADD_SYMBOLS: |
| SETVAR(tv_add_symbols); |
| break; |
| case LDPT_GET_SYMBOLS: |
| SETVAR(tv_get_symbols); |
| break; |
| case LDPT_GET_SYMBOLS_V2: |
| tv_get_symbols_v2 = tv->tv_u.tv_get_symbols; |
| break; |
| case LDPT_ADD_INPUT_FILE: |
| SETVAR(tv_add_input_file); |
| break; |
| case LDPT_MESSAGE: |
| SETVAR(tv_message); |
| break; |
| case LDPT_GET_INPUT_FILE: |
| SETVAR(tv_get_input_file); |
| break; |
| case LDPT_GET_VIEW: |
| SETVAR(tv_get_view); |
| break; |
| case LDPT_RELEASE_INPUT_FILE: |
| SETVAR(tv_release_input_file); |
| break; |
| case LDPT_ADD_INPUT_LIBRARY: |
| SETVAR(tv_add_input_library); |
| break; |
| case LDPT_SET_EXTRA_LIBRARY_PATH: |
| SETVAR(tv_set_extra_library_path); |
| break; |
| } |
| #undef SETVAR |
| return LDPS_OK; |
| } |
| |
| /* Record any useful information in transfer vector entry and display |
| it in human-readable form using the plugin API message() callback. */ |
| enum ld_plugin_status |
| parse_and_dump_tv_tag (size_t n, struct ld_plugin_tv *tv) |
| { |
| enum ld_plugin_status rv = parse_tv_tag (tv); |
| dump_tv_tag (n, tv); |
| return rv; |
| } |
| |
| /* Standard plugin API entry point. */ |
| enum ld_plugin_status |
| onload (struct ld_plugin_tv *tv) |
| { |
| size_t n = 0; |
| enum ld_plugin_status rv; |
| |
| /* This plugin does nothing but dump the tv array. It would |
| be an error if this function was called without one. */ |
| if (!tv) |
| return LDPS_ERR; |
| |
| /* First entry should always be LDPT_MESSAGE, letting us get |
| hold of it easily so we can send output straight away. */ |
| if (tv[0].tv_tag == LDPT_MESSAGE) |
| tv_message = tv[0].tv_u.tv_message; |
| |
| fflush (NULL); |
| TV_MESSAGE (LDPL_INFO, "Hello from testplugin."); |
| |
| do |
| if ((rv = parse_and_dump_tv_tag (n++, tv)) != LDPS_OK) |
| return rv; |
| while ((tv++)->tv_tag != LDPT_NULL); |
| |
| /* Register hooks only if instructed by options. */ |
| if (register_claimfile_hook) |
| { |
| if (!tv_register_claim_file) |
| { |
| TV_MESSAGE (LDPL_FATAL, "No register_claim_file hook"); |
| fflush (NULL); |
| return LDPS_ERR; |
| } |
| (*tv_register_claim_file) (onclaim_file); |
| } |
| if (register_allsymbolsread_hook) |
| { |
| if (!tv_register_all_symbols_read) |
| { |
| TV_MESSAGE (LDPL_FATAL, "No register_all_symbols_read hook"); |
| fflush (NULL); |
| return LDPS_ERR; |
| } |
| (*tv_register_all_symbols_read) (onall_symbols_read); |
| } |
| if (register_cleanup_hook) |
| { |
| if (!tv_register_cleanup) |
| { |
| TV_MESSAGE (LDPL_FATAL, "No register_cleanup hook"); |
| fflush (NULL); |
| return LDPS_ERR; |
| } |
| (*tv_register_cleanup) (oncleanup); |
| } |
| fflush (NULL); |
| return onload_ret; |
| } |
| |
| /* Standard plugin API registerable hook. */ |
| static enum ld_plugin_status |
| onclaim_file (const struct ld_plugin_input_file *file, int *claimed) |
| { |
| /* Possible read of some bytes out of the input file into a buffer. This |
| simulates a plugin that reads some file content in order to decide if |
| the file should be claimed or not. */ |
| if (bytes_to_read_before_claim > 0) |
| { |
| char *buffer = malloc (bytes_to_read_before_claim); |
| |
| if (buffer == NULL) |
| return LDPS_ERR; |
| if (read (file->fd, buffer, bytes_to_read_before_claim) < 0) |
| return LDPS_ERR; |
| free (buffer); |
| } |
| |
| /* Let's see if we want to claim this file. */ |
| claim_file_t *claimfile = claimfiles_list; |
| while (claimfile) |
| { |
| if (!strcmp (file->name, claimfile->file.name)) |
| break; |
| claimfile = claimfile->next; |
| } |
| |
| /* Inform the user/testsuite. */ |
| TV_MESSAGE (LDPL_INFO, "hook called: claim_file %s [@%ld/%ld] %s", |
| file->name, (long)file->offset, (long)file->filesize, |
| claimfile ? "CLAIMED" : "not claimed"); |
| fflush (NULL); |
| |
| /* If we decided to claim it, record that fact, and add any symbols |
| that were defined for it by plugin options. */ |
| *claimed = (claimfile != 0); |
| if (claimfile) |
| { |
| claimfile->claimed = true; |
| claimfile->file = *file; |
| if (claimfile->n_syms_used && !tv_add_symbols) |
| return LDPS_ERR; |
| else if (claimfile->n_syms_used) |
| return (*tv_add_symbols) (claimfile->file.handle, |
| claimfile->n_syms_used, claimfile->symbols); |
| } |
| |
| return claim_file_ret; |
| } |
| |
| /* Standard plugin API registerable hook. */ |
| static enum ld_plugin_status |
| onall_symbols_read (void) |
| { |
| static const char *resolutions[] = |
| { |
| "LDPR_UNKNOWN", |
| "LDPR_UNDEF", |
| "LDPR_PREVAILING_DEF", |
| "LDPR_PREVAILING_DEF_IRONLY", |
| "LDPR_PREEMPTED_REG", |
| "LDPR_PREEMPTED_IR", |
| "LDPR_RESOLVED_IR", |
| "LDPR_RESOLVED_EXEC", |
| "LDPR_RESOLVED_DYN", |
| "LDPR_PREVAILING_DEF_IRONLY_EXP", |
| }; |
| claim_file_t *claimfile = dumpresolutions ? claimfiles_list : NULL; |
| add_file_t *addfile = addfiles_list; |
| TV_MESSAGE (LDPL_INFO, "hook called: all symbols read."); |
| for ( ; claimfile; claimfile = claimfile->next) |
| { |
| enum ld_plugin_status rv; |
| int n; |
| if (claimfile->n_syms_used && !tv_get_symbols_v2) |
| return LDPS_ERR; |
| else if (!claimfile->n_syms_used) |
| continue; |
| rv = tv_get_symbols_v2 (claimfile->file.handle, claimfile->n_syms_used, |
| claimfile->symbols); |
| if (rv != LDPS_OK) |
| return rv; |
| for (n = 0; n < claimfile->n_syms_used; n++) |
| TV_MESSAGE (LDPL_INFO, "Sym: '%s%s%s' Resolution: %s", |
| claimfile->symbols[n].name, |
| claimfile->symbols[n].version ? "@" : "", |
| (claimfile->symbols[n].version |
| ? claimfile->symbols[n].version : ""), |
| resolutions[claimfile->symbols[n].resolution]); |
| } |
| for ( ; addfile ; addfile = addfile->next) |
| { |
| enum ld_plugin_status rv; |
| if (addfile->type == ADD_LIB && tv_add_input_library) |
| rv = (*tv_add_input_library) (addfile->name); |
| else if (addfile->type == ADD_FILE && tv_add_input_file) |
| rv = (*tv_add_input_file) (addfile->name); |
| else if (addfile->type == ADD_DIR && tv_set_extra_library_path) |
| rv = (*tv_set_extra_library_path) (addfile->name); |
| else |
| rv = LDPS_ERR; |
| if (rv != LDPS_OK) |
| return rv; |
| } |
| fflush (NULL); |
| return all_symbols_read_ret; |
| } |
| |
| /* Standard plugin API registerable hook. */ |
| static enum ld_plugin_status |
| oncleanup (void) |
| { |
| TV_MESSAGE (LDPL_INFO, "hook called: cleanup."); |
| fflush (NULL); |
| return cleanup_ret; |
| } |
| #endif /* BFD_SUPPORTS_PLUGINS */ |