commit 4b5e6a058b8f4069d989752485eaa5003bcec94d Author: Stephen Crane Date: Fri Oct 20 14:26:30 2017 -0700 [GOLD] Add plugin API for processing plugin-added input files Gold plugins may wish to further process an input file added by a plugin. For example, the plugin may need to assign a unique segment for sections in a plugin-generated input file. This patch adds a plugin callback that is called when reading symbols from a new input file added after the all_symbols_read event (i.e. an input file added by a plugin). New test exercises the new input hook by generating a new file using the basic test plugin and then reordering its sections and assigning sections to unique segments in a second plugin. diff --git a/gold/plugin.cc b/gold/plugin.cc index 5ea23b5bdd..49212b4309 100644 --- a/gold/plugin.cc +++ b/gold/plugin.cc @@ -167,6 +167,9 @@ static enum ld_plugin_status get_input_section_size(const struct ld_plugin_section section, uint64_t* secsize); +static enum ld_plugin_status +register_new_input(ld_plugin_new_input_handler handler); + }; #endif // ENABLE_PLUGINS @@ -211,7 +214,7 @@ Plugin::load() sscanf(ver, "%d.%d", &major, &minor); // Allocate and populate a transfer vector. - const int tv_fixed_size = 29; + const int tv_fixed_size = 30; int tv_size = this->args_.size() + tv_fixed_size; ld_plugin_tv* tv = new ld_plugin_tv[tv_size]; @@ -345,6 +348,10 @@ Plugin::load() tv[i].tv_tag = LDPT_GET_INPUT_SECTION_SIZE; tv[i].tv_u.tv_get_input_section_size = get_input_section_size; + ++i; + tv[i].tv_tag = LDPT_REGISTER_NEW_INPUT_HOOK; + tv[i].tv_u.tv_register_new_input = register_new_input; + ++i; tv[i].tv_tag = LDPT_NULL; tv[i].tv_u.tv_val = 0; @@ -383,6 +390,15 @@ Plugin::all_symbols_read() (*this->all_symbols_read_handler_)(); } +// Call the new_input handler. + +inline void +Plugin::new_input(struct ld_plugin_input_file* plugin_input_file) +{ + if (this->new_input_handler_ != NULL) + (*this->new_input_handler_)(plugin_input_file); +} + // Call the cleanup handler. inline void @@ -476,8 +492,6 @@ Plugin_manager::claim_file(Input_file* input_file, off_t offset, gold_assert(lock_initialized); Hold_lock hl(*this->lock_); - if (this->in_replacement_phase_) - return NULL; unsigned int handle = this->objects_.size(); this->input_file_ = input_file; @@ -494,19 +508,28 @@ Plugin_manager::claim_file(Input_file* input_file, off_t offset, this->current_ != this->plugins_.end(); ++this->current_) { - if ((*this->current_)->claim_file(&this->plugin_input_file_)) + // If we aren't yet in replacement phase, allow plugins to claim input + // files, otherwise notify the plugin of the new input file, if needed. + if (!this->in_replacement_phase_) { - this->any_claimed_ = true; - this->in_claim_file_handler_ = false; - - if (this->objects_.size() > handle - && this->objects_[handle]->pluginobj() != NULL) - return this->objects_[handle]->pluginobj(); - - // If the plugin claimed the file but did not call the - // add_symbols callback, we need to create the Pluginobj now. - Pluginobj* obj = this->make_plugin_object(handle); - return obj; + if ((*this->current_)->claim_file(&this->plugin_input_file_)) + { + this->any_claimed_ = true; + this->in_claim_file_handler_ = false; + + if (this->objects_.size() > handle + && this->objects_[handle]->pluginobj() != NULL) + return this->objects_[handle]->pluginobj(); + + // If the plugin claimed the file but did not call the + // add_symbols callback, we need to create the Pluginobj now. + Pluginobj* obj = this->make_plugin_object(handle); + return obj; + } + } + else + { + (*this->current_)->new_input(&this->plugin_input_file_); } } @@ -1905,6 +1928,16 @@ unique_segment_for_sections(const char* segment_name, return LDPS_OK; } +// Register a new_input handler. + +static enum ld_plugin_status +register_new_input(ld_plugin_new_input_handler handler) +{ + gold_assert(parameters->options().has_plugins()); + parameters->options().plugins()->set_new_input_handler(handler); + return LDPS_OK; +} + #endif // ENABLE_PLUGINS // Allocate a Pluginobj object of the appropriate size and endianness. diff --git a/gold/plugin.h b/gold/plugin.h index 7658668502..d591d26821 100644 --- a/gold/plugin.h +++ b/gold/plugin.h @@ -60,6 +60,7 @@ class Plugin claim_file_handler_(NULL), all_symbols_read_handler_(NULL), cleanup_handler_(NULL), + new_input_handler_(NULL), cleanup_done_(false) { } @@ -78,6 +79,10 @@ class Plugin void all_symbols_read(); + // Call the new_input handler. + void + new_input(struct ld_plugin_input_file* plugin_input_file); + // Call the cleanup handler. void cleanup(); @@ -97,6 +102,11 @@ class Plugin set_cleanup_handler(ld_plugin_cleanup_handler handler) { this->cleanup_handler_ = handler; } + // Register a new_input handler. + void + set_new_input_handler(ld_plugin_new_input_handler handler) + { this->new_input_handler_ = handler; } + // Add an argument void add_option(const char* arg) @@ -118,6 +128,7 @@ class Plugin ld_plugin_claim_file_handler claim_file_handler_; ld_plugin_all_symbols_read_handler all_symbols_read_handler_; ld_plugin_cleanup_handler cleanup_handler_; + ld_plugin_new_input_handler new_input_handler_; // TRUE if the cleanup handlers have been called. bool cleanup_done_; }; @@ -218,6 +229,14 @@ class Plugin_manager (*this->current_)->set_all_symbols_read_handler(handler); } + // Register a new_input handler. + void + set_new_input_handler(ld_plugin_new_input_handler handler) + { + gold_assert(this->current_ != plugins_.end()); + (*this->current_)->set_new_input_handler(handler); + } + // Register a claim-file handler. void set_cleanup_handler(ld_plugin_cleanup_handler handler) diff --git a/gold/testsuite/Makefile.am b/gold/testsuite/Makefile.am index d9a0669fb4..ced831a935 100644 --- a/gold/testsuite/Makefile.am +++ b/gold/testsuite/Makefile.am @@ -2397,6 +2397,23 @@ plugin_section_order.so: plugin_section_order.o gcctestdir/ld plugin_section_order.o: plugin_section_order.c $(COMPILE) -O0 -c -fpic -o $@ $< +# Uses the plugin_final_layout.sh script above to avoid duplication +check_DATA += plugin_layout_new_file.stdout plugin_layout_new_file_readelf.stdout +MOSTLYCLEANFILES += plugin_layout_new_file +plugin_final_layout.o.syms: plugin_final_layout.o + $(TEST_READELF) -sW $< >$@ 2>/dev/null +plugin_layout_new_file: plugin_final_layout.o.syms plugin_test.so plugin_new_section_layout.so gcctestdir/ld + $(CXXLINK) -Bgcctestdir/ -Wl,--plugin,"./plugin_test.so" -Wl,--plugin,"./plugin_new_section_layout.so" plugin_final_layout.o.syms +plugin_layout_new_file.stdout: plugin_layout_new_file + $(TEST_NM) -n --synthetic plugin_layout_new_file > plugin_layout_new_file.stdout +plugin_layout_new_file_readelf.stdout: plugin_layout_new_file + $(TEST_READELF) -Wl plugin_layout_new_file > plugin_layout_new_file_readelf.stdout + +plugin_new_section_layout.so: plugin_new_section_layout.o gcctestdir/ld + $(LINK) -Bgcctestdir/ -shared plugin_new_section_layout.o +plugin_new_section_layout.o: plugin_new_section_layout.c + $(COMPILE) -O0 -c -fpic -o $@ $< + check_SCRIPTS += plugin_layout_with_alignment.sh check_DATA += plugin_layout_with_alignment.stdout MOSTLYCLEANFILES += plugin_layout_with_alignment diff --git a/gold/testsuite/Makefile.in b/gold/testsuite/Makefile.in index b8db70d75d..6bc1bbb360 100644 --- a/gold/testsuite/Makefile.in +++ b/gold/testsuite/Makefile.in @@ -577,11 +577,16 @@ check_PROGRAMS = $(am__EXEEXT_1) $(am__EXEEXT_2) $(am__EXEEXT_3) \ @GCC_TRUE@@NATIVE_LINKER_TRUE@@PLUGINS_TRUE@@TLS_TRUE@am__append_51 = plugin_test_tls.err @GCC_TRUE@@NATIVE_LINKER_TRUE@@PLUGINS_TRUE@am__append_52 = unused.c \ @GCC_TRUE@@NATIVE_LINKER_TRUE@@PLUGINS_TRUE@ plugin_final_layout \ +@GCC_TRUE@@NATIVE_LINKER_TRUE@@PLUGINS_TRUE@ plugin_layout_new_file \ @GCC_TRUE@@NATIVE_LINKER_TRUE@@PLUGINS_TRUE@ plugin_layout_with_alignment @GCC_TRUE@@NATIVE_LINKER_TRUE@@PLUGINS_TRUE@am__append_53 = plugin_final_layout.sh \ @GCC_TRUE@@NATIVE_LINKER_TRUE@@PLUGINS_TRUE@ plugin_layout_with_alignment.sh + +# Uses the plugin_final_layout.sh script above to avoid duplication @GCC_TRUE@@NATIVE_LINKER_TRUE@@PLUGINS_TRUE@am__append_54 = plugin_final_layout.stdout \ @GCC_TRUE@@NATIVE_LINKER_TRUE@@PLUGINS_TRUE@ plugin_final_layout_readelf.stdout \ +@GCC_TRUE@@NATIVE_LINKER_TRUE@@PLUGINS_TRUE@ plugin_layout_new_file.stdout \ +@GCC_TRUE@@NATIVE_LINKER_TRUE@@PLUGINS_TRUE@ plugin_layout_new_file_readelf.stdout \ @GCC_TRUE@@NATIVE_LINKER_TRUE@@PLUGINS_TRUE@ plugin_layout_with_alignment.stdout @GCC_TRUE@@NATIVE_LINKER_TRUE@am__append_55 = exclude_libs_test \ @GCC_TRUE@@NATIVE_LINKER_TRUE@ local_labels_test \ @@ -7032,6 +7037,19 @@ uninstall-am: @GCC_TRUE@@NATIVE_LINKER_TRUE@@PLUGINS_TRUE@ $(LINK) -Bgcctestdir/ -shared plugin_section_order.o @GCC_TRUE@@NATIVE_LINKER_TRUE@@PLUGINS_TRUE@plugin_section_order.o: plugin_section_order.c @GCC_TRUE@@NATIVE_LINKER_TRUE@@PLUGINS_TRUE@ $(COMPILE) -O0 -c -fpic -o $@ $< +@GCC_TRUE@@NATIVE_LINKER_TRUE@@PLUGINS_TRUE@plugin_final_layout.o.syms: plugin_final_layout.o +@GCC_TRUE@@NATIVE_LINKER_TRUE@@PLUGINS_TRUE@ $(TEST_READELF) -sW $< >$@ 2>/dev/null +@GCC_TRUE@@NATIVE_LINKER_TRUE@@PLUGINS_TRUE@plugin_layout_new_file: plugin_final_layout.o.syms plugin_test.so plugin_new_section_layout.so gcctestdir/ld +@GCC_TRUE@@NATIVE_LINKER_TRUE@@PLUGINS_TRUE@ $(CXXLINK) -Bgcctestdir/ -Wl,--plugin,"./plugin_test.so" -Wl,--plugin,"./plugin_new_section_layout.so" plugin_final_layout.o.syms +@GCC_TRUE@@NATIVE_LINKER_TRUE@@PLUGINS_TRUE@plugin_layout_new_file.stdout: plugin_layout_new_file +@GCC_TRUE@@NATIVE_LINKER_TRUE@@PLUGINS_TRUE@ $(TEST_NM) -n --synthetic plugin_layout_new_file > plugin_layout_new_file.stdout +@GCC_TRUE@@NATIVE_LINKER_TRUE@@PLUGINS_TRUE@plugin_layout_new_file_readelf.stdout: plugin_layout_new_file +@GCC_TRUE@@NATIVE_LINKER_TRUE@@PLUGINS_TRUE@ $(TEST_READELF) -Wl plugin_layout_new_file > plugin_layout_new_file_readelf.stdout + +@GCC_TRUE@@NATIVE_LINKER_TRUE@@PLUGINS_TRUE@plugin_new_section_layout.so: plugin_new_section_layout.o gcctestdir/ld +@GCC_TRUE@@NATIVE_LINKER_TRUE@@PLUGINS_TRUE@ $(LINK) -Bgcctestdir/ -shared plugin_new_section_layout.o +@GCC_TRUE@@NATIVE_LINKER_TRUE@@PLUGINS_TRUE@plugin_new_section_layout.o: plugin_new_section_layout.c +@GCC_TRUE@@NATIVE_LINKER_TRUE@@PLUGINS_TRUE@ $(COMPILE) -O0 -c -fpic -o $@ $< @GCC_TRUE@@NATIVE_LINKER_TRUE@@PLUGINS_TRUE@plugin_layout_with_alignment.o: plugin_layout_with_alignment.c @GCC_TRUE@@NATIVE_LINKER_TRUE@@PLUGINS_TRUE@ $(COMPILE) -O0 -c -ffunction-sections -fdata-sections -g -o $@ $< @GCC_TRUE@@NATIVE_LINKER_TRUE@@PLUGINS_TRUE@plugin_layout_with_alignment: plugin_layout_with_alignment.o plugin_section_alignment.so gcctestdir/ld diff --git a/gold/testsuite/plugin_final_layout.sh b/gold/testsuite/plugin_final_layout.sh index 54985916a7..3157beacf4 100755 --- a/gold/testsuite/plugin_final_layout.sh +++ b/gold/testsuite/plugin_final_layout.sh @@ -23,7 +23,7 @@ # MA 02110-1301, USA. # The goal of this program is to verify if --section-ordering-file works as -# intended. File final_layout.cc is in this test. +# intended. File plugin_final_layout.cc is in this test. set -e @@ -37,7 +37,7 @@ BEGIN { saw1 = 0; saw2 = 0; err = 0; } saw2 = 1; if (!saw1) { - printf \"layout of $2 and $3 is not right\\n\"; + printf \"layout of $2 and $3 is not right in file $1\\n\"; err = 1; exit 1; } @@ -45,12 +45,12 @@ BEGIN { saw1 = 0; saw2 = 0; err = 0; } END { if (!saw1 && !err) { - printf \"did not see $2\\n\"; + printf \"did not see $2 in file $1\\n\"; exit 1; } if (!saw2 && !err) { - printf \"did not see $3\\n\"; + printf \"did not see $3 in file $1\\n\"; exit 1; } }" $1 @@ -74,12 +74,12 @@ BEGIN { saw_section = 0; saw_unique = 0; } END { if (!saw_section) { - printf \"Section $2 not seen in output\\n\"; + printf \"Section $2 not seen in output file $1\\n\"; exit 1; } else if (!saw_unique) { - printf \"Unique segment not seen for: $2\\n\"; + printf \"Unique segment not seen for: $2 in file $1\\n\"; exit 1; } }" $1 @@ -88,3 +88,7 @@ END { check plugin_final_layout.stdout "_Z3foov" "_Z3barv" check plugin_final_layout.stdout "_Z3barv" "_Z3bazv" check_unique_segment plugin_final_layout_readelf.stdout ".text.plugin_created_unique" + +check plugin_layout_new_file.stdout "_Z3foov" "_Z3barv" +check plugin_layout_new_file.stdout "_Z3barv" "_Z3bazv" +check_unique_segment plugin_layout_new_file_readelf.stdout ".text.plugin_created_unique" diff --git a/gold/testsuite/plugin_new_section_layout.c b/gold/testsuite/plugin_new_section_layout.c new file mode 100644 index 0000000000..f27ad4c530 --- /dev/null +++ b/gold/testsuite/plugin_new_section_layout.c @@ -0,0 +1,181 @@ +/* plugin_new_section_layout.c -- Simple plugin to reorder function sections in + plugin-generated objects + + Copyright (C) 2017 Free Software Foundation, Inc. + Written by Stephen Crane . + + 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. */ + +/* This plugin tests the new_input API of the linker plugin interface that + * allows plugins to modify section layout and assign sections to segments for + * sections in plugin-generated object files. It assumes that another plugin is + * also in use which will add new files. In practice a plugin is likely to + * generate new input files itself in all_symbols_read and want to + * reorder/assign sections for these files in the new_input_hook callback. */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include +#include +#include +#include "plugin-api.h" +#include "elf/common.h" + +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_update_section_order update_section_order = NULL; +static ld_plugin_allow_section_ordering allow_section_ordering = NULL; +static ld_plugin_allow_unique_segment_for_sections + allow_unique_segment_for_sections = NULL; +static ld_plugin_unique_segment_for_sections unique_segment_for_sections = NULL; + +enum ld_plugin_status onload(struct ld_plugin_tv *tv); +enum ld_plugin_status new_input_hook(const struct ld_plugin_input_file *file); + +/* Plugin entry point. */ +enum ld_plugin_status +onload(struct ld_plugin_tv *tv) +{ + struct ld_plugin_tv *entry; + for (entry = tv; entry->tv_tag != LDPT_NULL; ++entry) + { + switch (entry->tv_tag) + { + 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_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_ALLOW_UNIQUE_SEGMENT_FOR_SECTIONS: + allow_unique_segment_for_sections + = *entry->tv_u.tv_allow_unique_segment_for_sections; + break; + case LDPT_UNIQUE_SEGMENT_FOR_SECTIONS: + unique_segment_for_sections + = *entry->tv_u.tv_unique_segment_for_sections; + break; + case LDPT_REGISTER_NEW_INPUT_HOOK: + assert((*entry->tv_u.tv_register_new_input) (new_input_hook) + == LDPS_OK); + break; + default: + break; + } + } + + if (get_input_section_count == NULL + || get_input_section_type == NULL + || get_input_section_name == NULL + || update_section_order == NULL + || allow_section_ordering == NULL + || allow_unique_segment_for_sections == NULL + || unique_segment_for_sections == NULL) + { + fprintf(stderr, "Some interfaces are missing\n"); + return LDPS_ERR; + } + + /* Inform the linker to prepare for section reordering. */ + (*allow_section_ordering)(); + /* Inform the linker to prepare to map some sections to unique + segments. */ + (*allow_unique_segment_for_sections)(); + + return LDPS_OK; +} + +inline static int is_prefix_of(const char *prefix, const char *str) +{ + return strncmp(prefix, str, strlen (prefix)) == 0; +} + +/* This function is called by the linker when new files are added by a plugin. + We can now tell the linker the desired function order since we have a file + handle for the newly added file. */ + +enum ld_plugin_status +new_input_hook(const struct ld_plugin_input_file *file) +{ + struct ld_plugin_section section_list[3]; + int num_entries = 0; + unsigned int count; + + if (get_input_section_count(file->handle, &count) != LDPS_OK) + return LDPS_ERR; + + for (unsigned i = 0; i < count; ++i) + { + struct ld_plugin_section section; + unsigned int type = 0; + char *name = NULL; + int position = 3; + + section.handle = file->handle; + section.shndx = i; + + if (get_input_section_type(section, &type) != LDPS_OK) + return LDPL_FATAL; + if (type != SHT_PROGBITS) + continue; + + if (get_input_section_name(section, &name)) + return LDPL_FATAL; + + /* As in plugin_section_order.c, order is foo() followed by bar() + followed by baz() */ + if (is_prefix_of(".text.", name)) + { + if (strstr(name, "_Z3foov") != NULL) + position = 0; + else if (strstr(name, "_Z3barv") != NULL) + position = 1; + else if (strstr(name, "_Z3bazv") != NULL) + position = 2; + else + position = 3; + } + if (position < 3) + { + section_list[position] = section; + num_entries++; + } + } + + if (num_entries != 3) + return LDPL_FATAL; + + update_section_order(section_list, num_entries); + unique_segment_for_sections (".text.plugin_created_unique", 0, 0x1000, + section_list, num_entries); + + return LDPS_OK; +} diff --git a/include/plugin-api.h b/include/plugin-api.h index 3a3e8b456d..f081f85dfa 100644 --- a/include/plugin-api.h +++ b/include/plugin-api.h @@ -365,6 +365,20 @@ enum ld_plugin_status (*ld_plugin_get_input_section_size) (const struct ld_plugin_section section, uint64_t *secsize); +typedef +enum ld_plugin_status +(*ld_plugin_new_input_handler) (const struct ld_plugin_input_file *file); + +/* The linker's interface for registering the "new_input" handler. This handler + will be notified when a new input file has been added after the + all_symbols_read event, allowing the plugin to, for example, set a unique + segment for sections in plugin-generated input files. */ + +typedef +enum ld_plugin_status +(*ld_plugin_register_new_input) (ld_plugin_new_input_handler handler); + + enum ld_plugin_level { LDPL_INFO, @@ -407,7 +421,8 @@ enum ld_plugin_tag LDPT_UNIQUE_SEGMENT_FOR_SECTIONS = 27, LDPT_GET_SYMBOLS_V3 = 28, LDPT_GET_INPUT_SECTION_ALIGNMENT = 29, - LDPT_GET_INPUT_SECTION_SIZE = 30 + LDPT_GET_INPUT_SECTION_SIZE = 30, + LDPT_REGISTER_NEW_INPUT_HOOK = 31 }; /* The plugin transfer vector. */ @@ -441,6 +456,7 @@ struct ld_plugin_tv ld_plugin_unique_segment_for_sections tv_unique_segment_for_sections; ld_plugin_get_input_section_alignment tv_get_input_section_alignment; ld_plugin_get_input_section_size tv_get_input_section_size; + ld_plugin_register_new_input tv_register_new_input; } tv_u; };