This is the mail archive of the
binutils@sourceware.org
mailing list for the binutils project.
[gold][patch] Layout allocated sections from LTO plugin honoring original link order
- From: Cary Coutant <ccoutant at google dot com>
- To: Binutils <binutils at sourceware dot org>
- Date: Mon, 22 Dec 2008 14:10:29 -0800
- Subject: [gold][patch] Layout allocated sections from LTO plugin honoring original link order
The gold plugin interface currently lets the plugin claim IR files and
provide a list of global symbols during first (input) pass of the
link. When all files have been scanned and the symbol table is fully
built, the plugin then gets the opportunity to generate code and
provide a set of replacement files that get added to the link. The
plugin framework was designed to honor the link order as far as symbol
binding goes, but neglected to honor link order when laying out the
contents of sections. For certain sections -- .init, .fini., .ctors,
.dtors, etc. -- this is a problem because the file crtend.o typically
contributes end-of-list markers to those sections, and the new files
get added after that, resulting in a failure to run some initializers.
This patch fixes the problem by deferring the layout of any allocated
section from a normal object file once the first IR file has been
claimed by a plugin. After the plugin has added the replacement files,
the linker then performs the layout of the deferred sections.
-cary
* object.cc (Sized_relobj::layout_section): New function.
(Sized_relobj::do_layout): Defer layout of input sections until after
plugin has provided replacement files.
(Sized_relobj::do_layout_deferred_sections): New function.
* object.h (Relobj::set_section_offset): Remove virtual keyword.
(Relobj::layout_deferred_sections): New function.
(Relobj::do_layout_deferred_sections): New function.
(Sized_relobj::do_layout_deferred_sections): New function.
(Sized_relobj::layout_section): New function.
(Sized_relobj::Deferred_layout): New structure.
(Sized_relobj::deferred_layout_): New field.
* plugin.cc (Plugin_manager::finish): Renamed, was cleanup.
Change all callers. Layout deferred sections.
(class Plugin_finish): Renamed, was Plugin_cleanup. Change all
references.
(Plugin_hook::run): Move code from do_plugin_hook inline.
(Plugin_hook::do_plugin_hook): Remove.
* plugin.h (Plugin_manager::Plugin_manager): Add missing initializers.
(Plugin_manager::finish): Renamed, was cleanup.
(Plugin_manager::should_defer_layout): New function.
(Plugin_manager::add_deferred_layout_object): New function.
(Plugin_manager::Deferred_layout_list): New type.
(Plugin_manager::deferred_layout_objects_): New field.
(Plugin_hook::do_plugin_hook): Remove.
Index: object.cc
===================================================================
RCS file: /cvs/src/src/gold/object.cc,v
retrieving revision 1.78
diff -u -p -r1.78 object.cc
--- object.cc 29 Sep 2008 21:10:26 -0000 1.78
+++ object.cc 19 Dec 2008 01:36:14 -0000
@@ -37,6 +37,7 @@
#include "reloc.h"
#include "object.h"
#include "dynobj.h"
+#include "plugin.h"
namespace gold
{
@@ -784,6 +785,34 @@ Sized_relobj<size, big_endian>::include_
return include1 && include2;
}
+// Layout an input section.
+
+template<int size, bool big_endian>
+inline void
+Sized_relobj<size, big_endian>::layout_section(Layout* layout,
+ unsigned int shndx,
+ const char* name,
+ typename This::Shdr& shdr,
+ unsigned int reloc_shndx,
+ unsigned int reloc_type)
+{
+ off_t offset;
+ Output_section* os = layout->layout(this, shndx, name, shdr,
+ reloc_shndx, reloc_type, &offset);
+
+ this->output_sections()[shndx] = os;
+ if (offset == -1)
+ this->section_offsets_[shndx] = invalid_address;
+ else
+ this->section_offsets_[shndx] = convert_types<Address, off_t>(offset);
+
+ // If this section requires special handling, and if there are
+ // relocs that apply to it, then we must do the special handling
+ // before we apply the relocs.
+ if (offset == -1 && reloc_shndx != 0)
+ this->set_relocs_must_follow_section_writes();
+}
+
// Lay out the input sections. We walk through the sections and check
// whether they should be included in the link. If they should, we
// pass them to the Layout object, which will return an output section
@@ -807,6 +836,13 @@ Sized_relobj<size, big_endian>::do_layou
const unsigned char* pnamesu = sd->section_names->data();
const char* pnames = reinterpret_cast<const char*>(pnamesu);
+ // If any input files have been claimed by plugins, we need to defer
+ // actual layout until the replacement files have arrived.
+ const bool should_defer_layout =
+ (parameters->options().has_plugins()
+ && parameters->options().plugins()->should_defer_layout());
+ unsigned int num_sections_to_defer = 0;
+
// For each section, record the index of the reloc section if any.
// Use 0 to mean that there is no reloc section, -1U to mean that
// there is more than one.
@@ -818,6 +854,10 @@ Sized_relobj<size, big_endian>::do_layou
{
typename This::Shdr shdr(pshdrs);
+ // Count the number of sections whose layout will be deferred.
+ if (should_defer_layout && (shdr.get_sh_flags() & elfcpp::SHF_ALLOC))
+ ++num_sections_to_defer;
+
unsigned int sh_type = shdr.get_sh_type();
if (sh_type == elfcpp::SHT_REL || sh_type == elfcpp::SHT_RELA)
{
@@ -856,6 +896,12 @@ Sized_relobj<size, big_endian>::do_layou
return;
}
+ if (num_sections_to_defer > 0)
+ {
+ parameters->options().plugins()->add_deferred_layout_object(this);
+ this->deferred_layout_.reserve(num_sections_to_defer);
+ }
+
// Whether we've seen a .note.GNU-stack section.
bool seen_gnu_stack = false;
// The flags of a .note.GNU-stack section.
@@ -960,22 +1006,22 @@ Sized_relobj<size, big_endian>::do_layou
continue;
}
- off_t offset;
- Output_section* os = layout->layout(this, i, name, shdr,
- reloc_shndx[i], reloc_type[i],
- &offset);
-
- out_sections[i] = os;
- if (offset == -1)
- out_section_offsets[i] = invalid_address;
+ if (should_defer_layout && (shdr.get_sh_flags() & elfcpp::SHF_ALLOC))
+ {
+ this->deferred_layout_.push_back(Deferred_layout(i, name, pshdrs,
+ reloc_shndx[i],
+ reloc_type[i]));
+
+ // Put dummy values here; real values will be supplied by
+ // do_layout_deferred_sections.
+ out_sections[i] = reinterpret_cast<Output_section*>(1);
+ out_section_offsets[i] = invalid_address;
+ }
else
- out_section_offsets[i] = convert_types<Address, off_t>(offset);
-
- // If this section requires special handling, and if there are
- // relocs that apply to it, then we must do the special handling
- // before we apply the relocs.
- if (offset == -1 && reloc_shndx[i] != 0)
- this->set_relocs_must_follow_section_writes();
+ {
+ this->layout_section(layout, i, name, shdr, reloc_shndx[i],
+ reloc_type[i]);
+ }
}
layout->layout_gnu_stack(seen_gnu_stack, gnu_stack_flags);
@@ -1059,6 +1105,27 @@ Sized_relobj<size, big_endian>::do_layou
sd->section_names = NULL;
}
+// Layout sections whose layout was deferred while waiting for
+// input files from a plugin.
+
+template<int size, bool big_endian>
+void
+Sized_relobj<size, big_endian>::do_layout_deferred_sections(Layout* layout)
+{
+ typename std::vector<Deferred_layout>::iterator deferred;
+
+ for (deferred = this->deferred_layout_.begin();
+ deferred != this->deferred_layout_.end();
+ ++deferred)
+ {
+ typename This::Shdr shdr(deferred->shdr_data_);
+ this->layout_section(layout, deferred->shndx_, deferred->name_.c_str(),
+ shdr, deferred->reloc_shndx_,
deferred->reloc_type_);
+ }
+
+ this->deferred_layout_.clear();
+}
+
// Add the symbols to the symbol table.
template<int size, bool big_endian>
Index: object.h
===================================================================
RCS file: /cvs/src/src/gold/object.h,v
retrieving revision 1.66
diff -u -p -r1.66 object.h
--- object.h 29 Sep 2008 21:10:26 -0000 1.66
+++ object.h 19 Dec 2008 01:36:14 -0000
@@ -671,7 +671,7 @@ class Relobj : public Object
{ return this->do_output_section_offset(shndx); }
// Set the offset of an input section within its output section.
- virtual void
+ void
set_section_offset(unsigned int shndx, uint64_t off)
{ this->do_set_section_offset(shndx, off); }
@@ -712,6 +712,12 @@ class Relobj : public Object
return (*this->map_to_relocatable_relocs_)[reloc_shndx];
}
+ // Layout sections whose layout was deferred while waiting for
+ // input files from a plugin.
+ void
+ layout_deferred_sections(Layout* layout)
+ { this->do_layout_deferred_sections(layout); }
+
protected:
// The output section to be used for each input section, indexed by
// the input section number. The output section is NULL if the
@@ -764,6 +770,11 @@ class Relobj : public Object
virtual void
do_set_section_offset(unsigned int shndx, uint64_t off) = 0;
+ // Layout sections whose layout was deferred while waiting for
+ // input files from a plugin--implemented by child class.
+ virtual void
+ do_layout_deferred_sections(Layout*) = 0;
+
// Return the vector mapping input sections to output sections.
Output_sections&
output_sections()
@@ -1368,6 +1379,11 @@ class Sized_relobj : public Relobj
void
do_layout(Symbol_table*, Layout*, Read_symbols_data*);
+ // Layout sections whose layout was deferred while waiting for
+ // input files from a plugin.
+ void
+ do_layout_deferred_sections(Layout*);
+
// Add the symbols to the symbol table.
void
do_add_symbols(Symbol_table*, Read_symbols_data*);
@@ -1558,6 +1574,12 @@ class Sized_relobj : public Relobj
include_linkonce_section(Layout*, unsigned int, const char*,
const elfcpp::Shdr<size, big_endian>&);
+ // Layout an input section.
+ void
+ layout_section(Layout* layout, unsigned int shndx, const char* name,
+ typename This::Shdr& shdr, unsigned int reloc_shndx,
+ unsigned int reloc_type);
+
// Views and sizes when relocating.
struct View_size
{
@@ -1681,6 +1703,25 @@ class Sized_relobj : public Relobj
};
typedef Unordered_map<unsigned int, Tls_got_entry> Local_tls_got_offsets;
+ // Saved information for sections whose layout was deferred.
+ struct Deferred_layout
+ {
+ static const int shdr_size = elfcpp::Elf_sizes<size>::shdr_size;
+ Deferred_layout(unsigned int shndx, const char* name,
+ const unsigned char* pshdr,
+ unsigned int reloc_shndx, unsigned int reloc_type)
+ : shndx_(shndx), name_(name), reloc_shndx_(reloc_shndx),
+ reloc_type_(reloc_type)
+ {
+ memcpy(this->shdr_data_, pshdr, shdr_size);
+ }
+ unsigned int shndx_;
+ std::string name_;
+ unsigned int reloc_shndx_;
+ unsigned int reloc_type_;
+ unsigned char shdr_data_[shdr_size];
+ };
+
// General access to the ELF file.
elfcpp::Elf_file<size, big_endian, Object> elf_file_;
// Index of SHT_SYMTAB section.
@@ -1715,6 +1756,8 @@ class Sized_relobj : public Relobj
Comdat_group_table comdat_groups_;
// Whether this object has a GNU style .eh_frame section.
bool has_eh_frame_;
+ // The list of sections whose layout was deferred.
+ std::vector<Deferred_layout> deferred_layout_;
};
// A class to manage the list of all objects.
Index: plugin.cc
===================================================================
RCS file: /cvs/src/src/gold/plugin.cc,v
retrieving revision 1.5
diff -u -p -r1.5 plugin.cc
--- plugin.cc 16 Dec 2008 19:19:15 -0000 1.5
+++ plugin.cc 19 Dec 2008 01:36:14 -0000
@@ -306,11 +306,18 @@ Plugin_manager::all_symbols_read(Workque
*last_blocker = this->this_blocker_;
}
-// Call the cleanup handlers.
+// Layout deferred sections and call the cleanup handlers.
void
-Plugin_manager::cleanup()
+Plugin_manager::finish()
{
+ Deferred_layout_list::iterator obj;
+
+ for (obj = this->deferred_layout_objects_.begin();
+ obj != this->deferred_layout_objects_.end();
+ ++obj)
+ (*obj)->layout_deferred_sections(this->layout_);
+
for (this->current_ = this->plugins_.begin();
this->current_ != this->plugins_.end();
++this->current_)
@@ -713,17 +720,18 @@ Add_plugin_symbols::run(Workqueue*)
this->obj_->add_symbols(this->symtab_, this->layout_);
}
-// Class Plugin_cleanup. This task calls the plugin cleanup hooks once all
-// replacement files have been added.
+// Class Plugin_finish. This task runs after all replacement files have
+// been added. It calls Layout::layout for any deferred sections and
+// calls each plugin's cleanup handler.
-class Plugin_cleanup : public Task
+class Plugin_finish : public Task
{
public:
- Plugin_cleanup(Task_token* this_blocker, Task_token* next_blocker)
+ Plugin_finish(Task_token* this_blocker, Task_token* next_blocker)
: this_blocker_(this_blocker), next_blocker_(next_blocker)
{ }
- ~Plugin_cleanup()
+ ~Plugin_finish()
{
if (this->this_blocker_ != NULL)
delete this->this_blocker_;
@@ -743,11 +751,11 @@ class Plugin_cleanup : public Task
void
run(Workqueue*)
- { parameters->options().plugins()->cleanup(); }
+ { parameters->options().plugins()->finish(); }
std::string
get_name() const
- { return "Plugin_cleanup"; }
+ { return "Plugin_finish"; }
private:
Task_token* this_blocker_;
@@ -778,18 +786,10 @@ Plugin_hook::locks(Task_locker*)
{
}
-// Run a Plugin_hook task.
-
-void
-Plugin_hook::run(Workqueue* workqueue)
-{
- this->do_plugin_hook(workqueue);
-}
-
// Run the "all symbols read" plugin hook.
void
-Plugin_hook::do_plugin_hook(Workqueue* workqueue)
+Plugin_hook::run(Workqueue* workqueue)
{
gold_assert(this->options_.has_plugins());
this->options_.plugins()->all_symbols_read(workqueue,
@@ -799,8 +799,8 @@ Plugin_hook::do_plugin_hook(Workqueue* w
this->dirpath_,
this->mapfile_,
&this->this_blocker_);
- workqueue->queue_soon(new Plugin_cleanup(this->this_blocker_,
- this->next_blocker_));
+ workqueue->queue_soon(new Plugin_finish(this->this_blocker_,
+ this->next_blocker_));
}
// The C interface routines called by the plugins.
Index: plugin.h
===================================================================
RCS file: /cvs/src/src/gold/plugin.h,v
retrieving revision 1.2
diff -u -p -r1.2 plugin.h
--- plugin.h 5 Dec 2008 21:34:50 -0000 1.2
+++ plugin.h 19 Dec 2008 01:36:14 -0000
@@ -120,7 +120,8 @@ class Plugin_manager
{
public:
Plugin_manager(const General_options& options)
- : plugins_(), in_replacement_phase_(false), options_(options),
+ : plugins_(), objects_(), deferred_layout_objects_(), input_file_(NULL),
+ plugin_input_file_(), in_replacement_phase_(false), options_(options),
workqueue_(NULL), input_objects_(NULL), symtab_(NULL), layout_(NULL),
dirpath_(NULL), mapfile_(NULL), this_blocker_(NULL)
{ this->current_ = plugins_.end(); }
@@ -154,9 +155,9 @@ class Plugin_manager
Symbol_table* symtab, Layout* layout, Dirsearch* dirpath,
Mapfile* mapfile, Task_token** last_blocker);
- // Call the cleanup handlers.
+ // Run deferred layout and call the cleanup handlers.
void
- cleanup();
+ finish();
// Register a claim-file handler.
void
@@ -196,6 +197,19 @@ class Plugin_manager
return this->objects_[handle];
}
+ // Return TRUE if any input files have been claimed by a plugin
+ // and we are still in the initial input phase.
+ bool
+ should_defer_layout() const
+ { return !this->objects_.empty() && !this->in_replacement_phase_; }
+
+ // Add a regular object to the deferred layout list. These are
+ // objects whose layout has been deferred until after the
+ // replacement files have arrived.
+ void
+ add_deferred_layout_object(Relobj* obj)
+ { this->deferred_layout_objects_.push_back(obj); }
+
// Add a new input file.
ld_plugin_status
add_input_file(char *pathname);
@@ -211,6 +225,7 @@ class Plugin_manager
typedef std::list<Plugin*> Plugin_list;
typedef std::vector<Pluginobj*> Object_list;
+ typedef std::vector<Relobj*> Deferred_layout_list;
// The list of plugin libraries.
Plugin_list plugins_;
@@ -221,6 +236,9 @@ class Plugin_manager
// serves as the "handle" that we pass to the plugins.
Object_list objects_;
+ // The list of regular objects whose layout has been deferred.
+ Deferred_layout_list deferred_layout_objects_;
+
// The file currently up for claim by the plugins.
Input_file* input_file_;
struct ld_plugin_input_file plugin_input_file_;
@@ -456,10 +474,6 @@ class Plugin_hook : public Task
{ return "Plugin_hook"; }
private:
- // Call the plugin hook.
- void
- do_plugin_hook(Workqueue*);
-
const General_options& options_;
Input_objects* input_objects_;
Symbol_table* symtab_;