diff options
author | Alan Modra <amodra@gmail.com> | 2012-12-03 05:30:59 +0000 |
---|---|---|
committer | Alan Modra <amodra@gmail.com> | 2012-12-03 05:30:59 +0000 |
commit | ec661b9dca95a55df264edaaa430dd6183ad2adf (patch) | |
tree | 3fe1dd21cc67d3af02e77b2a761128f3f5052e4b | |
parent | 0f6752b51fa8d4e6119c662eb17520f7ee5a6007 (diff) | |
download | gdb-ec661b9dca95a55df264edaaa430dd6183ad2adf.zip gdb-ec661b9dca95a55df264edaaa430dd6183ad2adf.tar.gz gdb-ec661b9dca95a55df264edaaa430dd6183ad2adf.tar.bz2 |
* layout.h (Layout::get_executable_sections): Declare.
* layout.cc (Layout::get_executable_sections): New function.
* arm.cc (Target_arm::group_sections): Use it.
(Arm_output_section::group_sections): Delete now redundant test.
* output.cc (Output_reloc::Output_reloc): Add is_relative.
param to handle relative relocs.
* output.h (Output_reloc::Output_reloc <absolute reloc>): Likewise.
(Output_data_reloc::add_absolute): Adjust.
(Output_data_reloc::add_relative): New function.
(Output_data::reset_data_size): New function.
(Output_relaxed_input_section::set_relobj, set_shndx): New functions.
(Output_section::set_addralign): New function.
(Output_section::checkpoint_set_addralign): New function.
(Output_section::clear_section_offsets_need_adjustment): New function.
(Output_section::input_sections): Make public.
* powerpc.cc (class Output_data_brlt_powerpc): New.
(class Stub_table, class Stub_control): New.
(Powerpc_relobj::has14_, set_has_14bit_branch, has_14bit_branch,
stub_table_, set_stub_table, stub_table): New vectors and accessor
functions.
(Target_powerpc::do_may_relax, do_relax, push_branch,
new_stub_table, stub_tables, brlt_section, group_sections,
add_branch_lookup_table, find_branch_lookup_table,
write_branch_lookup_table, make_brlt_section): New functions.
(Target_powerpc::struct Sort_sections, class Branch_info): New.
(Target_powerpc::brlt_section_, stub_tables_, branch_lookup_table_,
branch_info_): New vars.
(Target_powerpc::make_plt_entry, make_local_ifunc_plt_entry): Don't
make call stubs here.
(Output_data_glink): Remove all call stub handling from this class.
(Target_powerpc::Scan::local, global): Save interesting branch
relocs and relocs for ifunc. Adjust calls to plt entry functions.
(Target_powerpc::do_finalize_sections): Only make reg save/restore
functions on final link.
(Target_powerpc::Relocate::relocate): Adjust lookup of call stubs.
Handle long branch destinations too.
(Target_powerpc::do_dynsym_value, do_plt_address_for_global,
do_plt_address_for_local): Adjust lookup of plt call stubs.
-rw-r--r-- | gold/ChangeLog | 41 | ||||
-rw-r--r-- | gold/arm.cc | 6 | ||||
-rw-r--r-- | gold/layout.cc | 13 | ||||
-rw-r--r-- | gold/layout.h | 4 | ||||
-rw-r--r-- | gold/output.cc | 12 | ||||
-rw-r--r-- | gold/output.h | 104 | ||||
-rw-r--r-- | gold/powerpc.cc | 1761 |
7 files changed, 1572 insertions, 369 deletions
diff --git a/gold/ChangeLog b/gold/ChangeLog index 92cbe14..933ee95 100644 --- a/gold/ChangeLog +++ b/gold/ChangeLog @@ -1,3 +1,44 @@ +i2012-12-03 Alan Modra <amodra@gmail.com> + + * layout.h (Layout::get_executable_sections): Declare. + * layout.cc (Layout::get_executable_sections): New function. + * arm.cc (Target_arm::group_sections): Use it. + (Arm_output_section::group_sections): Delete now redundant test. + * output.cc (Output_reloc::Output_reloc): Add is_relative. + param to handle relative relocs. + * output.h (Output_reloc::Output_reloc <absolute reloc>): Likewise. + (Output_data_reloc::add_absolute): Adjust. + (Output_data_reloc::add_relative): New function. + (Output_data::reset_data_size): New function. + (Output_relaxed_input_section::set_relobj, set_shndx): New functions. + (Output_section::set_addralign): New function. + (Output_section::checkpoint_set_addralign): New function. + (Output_section::clear_section_offsets_need_adjustment): New function. + (Output_section::input_sections): Make public. + * powerpc.cc (class Output_data_brlt_powerpc): New. + (class Stub_table, class Stub_control): New. + (Powerpc_relobj::has14_, set_has_14bit_branch, has_14bit_branch, + stub_table_, set_stub_table, stub_table): New vectors and accessor + functions. + (Target_powerpc::do_may_relax, do_relax, push_branch, + new_stub_table, stub_tables, brlt_section, group_sections, + add_branch_lookup_table, find_branch_lookup_table, + write_branch_lookup_table, make_brlt_section): New functions. + (Target_powerpc::struct Sort_sections, class Branch_info): New. + (Target_powerpc::brlt_section_, stub_tables_, branch_lookup_table_, + branch_info_): New vars. + (Target_powerpc::make_plt_entry, make_local_ifunc_plt_entry): Don't + make call stubs here. + (Output_data_glink): Remove all call stub handling from this class. + (Target_powerpc::Scan::local, global): Save interesting branch + relocs and relocs for ifunc. Adjust calls to plt entry functions. + (Target_powerpc::do_finalize_sections): Only make reg save/restore + functions on final link. + (Target_powerpc::Relocate::relocate): Adjust lookup of call stubs. + Handle long branch destinations too. + (Target_powerpc::do_dynsym_value, do_plt_address_for_global, + do_plt_address_for_local): Adjust lookup of plt call stubs. + 2012-11-30 Alan Modra <amodra@gmail.com> * powerpc.c (Target_powerpc::Scan::global): Don't emit relative diff --git a/gold/arm.cc b/gold/arm.cc index a98004b..736a4ae 100644 --- a/gold/arm.cc +++ b/gold/arm.cc @@ -5644,10 +5644,6 @@ Arm_output_section<big_endian>::group_sections( Target_arm<big_endian>* target, const Task* task) { - // We only care about sections containing code. - if ((this->flags() & elfcpp::SHF_EXECINSTR) == 0) - return; - // States for grouping. typedef enum { @@ -11442,7 +11438,7 @@ Target_arm<big_endian>::group_sections( { // Group input sections and insert stub table Layout::Section_list section_list; - layout->get_allocated_sections(§ion_list); + layout->get_executable_sections(§ion_list); for (Layout::Section_list::const_iterator p = section_list.begin(); p != section_list.end(); ++p) diff --git a/gold/layout.cc b/gold/layout.cc index 022f5a9..60fabb8 100644 --- a/gold/layout.cc +++ b/gold/layout.cc @@ -5004,6 +5004,19 @@ Layout::get_allocated_sections(Section_list* section_list) const section_list->push_back(*p); } +// Store the executable sections into the section list. + +void +Layout::get_executable_sections(Section_list* section_list) const +{ + for (Section_list::const_iterator p = this->section_list_.begin(); + p != this->section_list_.end(); + ++p) + if (((*p)->flags() & (elfcpp::SHF_ALLOC | elfcpp::SHF_EXECINSTR)) + == (elfcpp::SHF_ALLOC | elfcpp::SHF_EXECINSTR)) + section_list->push_back(*p); +} + // Create an output segment. Output_segment* diff --git a/gold/layout.h b/gold/layout.h index 94745a6..cf41f32 100644 --- a/gold/layout.h +++ b/gold/layout.h @@ -917,6 +917,10 @@ class Layout void get_allocated_sections(Section_list*) const; + // Store the executable sections into the section list. + void + get_executable_sections(Section_list*) const; + // Make a section for a linker script to hold data. Output_section* make_output_section_for_script(const char* name, diff --git a/gold/output.cc b/gold/output.cc index b47ad4d..9e3b0b4 100644 --- a/gold/output.cc +++ b/gold/output.cc @@ -851,15 +851,16 @@ Output_reloc<elfcpp::SHT_REL, dynamic, size, big_endian>::Output_reloc( os->set_needs_symtab_index(); } -// An absolute relocation. +// An absolute or relative relocation. template<bool dynamic, int size, bool big_endian> Output_reloc<elfcpp::SHT_REL, dynamic, size, big_endian>::Output_reloc( unsigned int type, Output_data* od, - Address address) + Address address, + bool is_relative) : address_(address), local_sym_index_(0), type_(type), - is_relative_(false), is_symbolless_(false), + is_relative_(is_relative), is_symbolless_(false), is_section_symbol_(false), use_plt_offset_(false), shndx_(INVALID_CODE) { // this->type_ is a bitfield; make sure TYPE fits. @@ -873,9 +874,10 @@ Output_reloc<elfcpp::SHT_REL, dynamic, size, big_endian>::Output_reloc( unsigned int type, Sized_relobj<size, big_endian>* relobj, unsigned int shndx, - Address address) + Address address, + bool is_relative) : address_(address), local_sym_index_(0), type_(type), - is_relative_(false), is_symbolless_(false), + is_relative_(is_relative), is_symbolless_(false), is_section_symbol_(false), use_plt_offset_(false), shndx_(shndx) { gold_assert(shndx != INVALID_CODE); diff --git a/gold/output.h b/gold/output.h index d52d075..138712d 100644 --- a/gold/output.h +++ b/gold/output.h @@ -103,8 +103,8 @@ class Output_data return this->offset_; } - // Reset the address and file offset. This essentially disables the - // sanity testing about duplicate and unknown settings. + // Reset the address, file offset and data size. This essentially + // disables the sanity testing about duplicate and unknown settings. void reset_address_and_file_offset() { @@ -115,6 +115,14 @@ class Output_data this->do_reset_address_and_file_offset(); } + // As above, but just for data size. + void + reset_data_size() + { + if (!this->is_data_size_fixed_) + this->is_data_size_valid_ = false; + } + // Return true if address and file offset already have reset values. In // other words, calling reset_address_and_file_offset will not change them. bool @@ -1052,12 +1060,13 @@ class Output_reloc<elfcpp::SHT_REL, dynamic, size, big_endian> Sized_relobj<size, big_endian>* relobj, unsigned int shndx, Address address, bool is_relative); - // An absolute relocation with no symbol. + // An absolute or relative relocation with no symbol. - Output_reloc(unsigned int type, Output_data* od, Address address); + Output_reloc(unsigned int type, Output_data* od, Address address, + bool is_relative); Output_reloc(unsigned int type, Sized_relobj<size, big_endian>* relobj, - unsigned int shndx, Address address); + unsigned int shndx, Address address, bool is_relative); // A target specific relocation. The target will be called to get // the symbol index, passing ARG. The type and offset will be set @@ -1307,16 +1316,17 @@ class Output_reloc<elfcpp::SHT_RELA, dynamic, size, big_endian> : rel_(os, type, relobj, shndx, address, is_relative), addend_(addend) { } - // An absolute relocation with no symbol. + // An absolute or relative relocation with no symbol. Output_reloc(unsigned int type, Output_data* od, Address address, - Addend addend) - : rel_(type, od, address), addend_(addend) + Addend addend, bool is_relative) + : rel_(type, od, address, is_relative), addend_(addend) { } Output_reloc(unsigned int type, Sized_relobj<size, big_endian>* relobj, - unsigned int shndx, Address address, Addend addend) - : rel_(type, relobj, shndx, address), addend_(addend) + unsigned int shndx, Address address, Addend addend, + bool is_relative) + : rel_(type, relobj, shndx, address, is_relative), addend_(addend) { } // A target specific relocation. The target will be called to get @@ -1797,13 +1807,25 @@ class Output_data_reloc<elfcpp::SHT_REL, dynamic, size, big_endian> void add_absolute(unsigned int type, Output_data* od, Address address) - { this->add(od, Output_reloc_type(type, od, address)); } + { this->add(od, Output_reloc_type(type, od, address, false)); } void add_absolute(unsigned int type, Output_data* od, Sized_relobj<size, big_endian>* relobj, unsigned int shndx, Address address) - { this->add(od, Output_reloc_type(type, relobj, shndx, address)); } + { this->add(od, Output_reloc_type(type, relobj, shndx, address, false)); } + + // Add a relative relocation + + void + add_relative(unsigned int type, Output_data* od, Address address) + { this->add(od, Output_reloc_type(type, od, address, true)); } + + void + add_relative(unsigned int type, Output_data* od, + Sized_relobj<size, big_endian>* relobj, + unsigned int shndx, Address address) + { this->add(od, Output_reloc_type(type, relobj, shndx, address, true)); } // Add a target specific relocation. A target which calls this must // define the reloc_symbol_index and reloc_addend virtual functions. @@ -2095,13 +2117,28 @@ class Output_data_reloc<elfcpp::SHT_RELA, dynamic, size, big_endian> void add_absolute(unsigned int type, Output_data* od, Address address, Addend addend) - { this->add(od, Output_reloc_type(type, od, address, addend)); } + { this->add(od, Output_reloc_type(type, od, address, addend, false)); } void add_absolute(unsigned int type, Output_data* od, Sized_relobj<size, big_endian>* relobj, unsigned int shndx, Address address, Addend addend) - { this->add(od, Output_reloc_type(type, relobj, shndx, address, addend)); } + { this->add(od, Output_reloc_type(type, relobj, shndx, address, addend, + false)); } + + // Add a relative relocation + + void + add_relative(unsigned int type, Output_data* od, Address address, + Addend addend) + { this->add(od, Output_reloc_type(type, od, address, addend, true)); } + + void + add_relative(unsigned int type, Output_data* od, + Sized_relobj<size, big_endian>* relobj, + unsigned int shndx, Address address, Addend addend) + { this->add(od, Output_reloc_type(type, relobj, shndx, address, addend, + true)); } // Add a target specific relocation. A target which calls this must // define the reloc_symbol_index and reloc_addend virtual functions. @@ -2700,6 +2737,15 @@ class Output_relaxed_input_section : public Output_section_data_build shndx() const { return this->shndx_; } + protected: + void + set_relobj(Relobj* relobj) + { this->relobj_ = relobj; } + + void + set_shndx(unsigned int shndx) + { this->shndx_ = shndx; } + private: Relobj* relobj_; unsigned int shndx_; @@ -3162,6 +3208,13 @@ class Output_section : public Output_data set_addralign(uint64_t v) { this->addralign_ = v; } + void + checkpoint_set_addralign(uint64_t val) + { + if (this->checkpoint_ != NULL) + this->checkpoint_->set_addralign(val); + } + // Whether the output section index has been set. bool has_out_shndx() const @@ -3794,6 +3847,11 @@ class Output_section : public Output_data set_section_offsets_need_adjustment() { this->section_offsets_need_adjustment_ = true; } + // Set section_offsets_need_adjustment to be false. + void + clear_section_offsets_need_adjustment() + { this->section_offsets_need_adjustment_ = false; } + // Adjust section offsets of input sections in this. This is // requires if relaxation caused some input sections to change sizes. void @@ -3848,6 +3906,13 @@ class Output_section : public Output_data off_t allocate(off_t len, uint64_t addralign); + typedef std::vector<Input_section> Input_section_list; + + // Allow access to the input sections. + const Input_section_list& + input_sections() const + { return this->input_sections_; } + protected: // Return the output section--i.e., the object itself. Output_section* @@ -3970,13 +4035,6 @@ class Output_section : public Output_data void write_to_postprocessing_buffer(); - typedef std::vector<Input_section> Input_section_list; - - // Allow a child class to access the input sections. - const Input_section_list& - input_sections() const - { return this->input_sections_; } - // Whether this always keeps an input section list bool always_keeps_input_sections() const @@ -4015,6 +4073,10 @@ class Output_section : public Output_data addralign() const { return this->addralign_; } + void + set_addralign(uint64_t val) + { this->addralign_ = val; } + // Return the section flags. elfcpp::Elf_Xword flags() const diff --git a/gold/powerpc.cc b/gold/powerpc.cc index a3bb9f2..3f3041f 100644 --- a/gold/powerpc.cc +++ b/gold/powerpc.cc @@ -23,6 +23,7 @@ #include "gold.h" +#include <algorithm> #include "elfcpp.h" #include "parameters.h" #include "reloc.h" @@ -48,12 +49,18 @@ template<int size, bool big_endian> class Output_data_plt_powerpc; template<int size, bool big_endian> +class Output_data_brlt_powerpc; + +template<int size, bool big_endian> class Output_data_got_powerpc; template<int size, bool big_endian> class Output_data_glink; template<int size, bool big_endian> +class Stub_table; + +template<int size, bool big_endian> class Powerpc_relobj : public Sized_relobj_file<size, big_endian> { public: @@ -225,6 +232,34 @@ public: toc_base_offset() const { return 0x8000; } + void + set_has_14bit_branch(unsigned int shndx) + { + if (shndx >= this->has14_.size()) + this->has14_.resize(shndx + 1); + this->has14_[shndx] = true; + } + + bool + has_14bit_branch(unsigned int shndx) const + { return shndx < this->has14_.size() && this->has14_[shndx]; } + + void + set_stub_table(unsigned int shndx, Stub_table<size, big_endian>* stub_table) + { + if (shndx >= this->stub_table_.size()) + this->stub_table_.resize(shndx + 1); + this->stub_table_[shndx] = stub_table; + } + + Stub_table<size, big_endian>* + stub_table(unsigned int shndx) + { + if (shndx < this->stub_table_.size()) + return this->stub_table_[shndx]; + return NULL; + } + private: struct Opd_ent { @@ -268,6 +303,12 @@ private: // gc_process_relocs for another object, before the opd_ent_ vector // is valid for this object. Access_from access_from_map_; + + // Whether input section has a 14-bit branch reloc. + std::vector<bool> has14_; + + // The stub table to use for a given input section. + std::vector<Stub_table<size, big_endian>*> stub_table_; }; template<int size, bool big_endian> @@ -285,9 +326,10 @@ class Target_powerpc : public Sized_target<size, big_endian> Target_powerpc() : Sized_target<size, big_endian>(&powerpc_info), - got_(NULL), plt_(NULL), iplt_(NULL), glink_(NULL), rela_dyn_(NULL), - copy_relocs_(elfcpp::R_POWERPC_COPY), - dynbss_(NULL), tlsld_got_offset_(-1U) + got_(NULL), plt_(NULL), iplt_(NULL), brlt_section_(NULL), + glink_(NULL), rela_dyn_(NULL), copy_relocs_(elfcpp::R_POWERPC_COPY), + dynbss_(NULL), tlsld_got_offset_(-1U), + stub_tables_(), branch_lookup_table_(), branch_info_() { } @@ -336,6 +378,31 @@ class Target_powerpc : public Sized_target<size, big_endian> void define_save_restore_funcs(Layout*, Symbol_table*); + // No stubs unless a final link. + bool + do_may_relax() const + { return !parameters->options().relocatable(); } + + bool + do_relax(int, const Input_objects*, Symbol_table*, Layout*, const Task*); + + // Stash info about branches, for stub generation. + void + push_branch(Powerpc_relobj<size, big_endian>* ppc_object, + unsigned int data_shndx, Address r_offset, + unsigned int r_type, unsigned int r_sym, Address addend) + { + Branch_info info(ppc_object, data_shndx, r_offset, r_type, r_sym, addend); + this->branch_info_.push_back(info); + if (r_type == elfcpp::R_POWERPC_REL14 + || r_type == elfcpp::R_POWERPC_REL14_BRTAKEN + || r_type == elfcpp::R_POWERPC_REL14_BRNTAKEN) + ppc_object->set_has_14bit_branch(data_shndx); + } + + Stub_table<size, big_endian>* + new_stub_table(); + // Finalize the sections. void do_finalize_sections(Layout*, const Input_objects*, Symbol_table*); @@ -503,6 +570,42 @@ class Target_powerpc : public Sized_target<size, big_endian> unsigned int dst_shndx, Address dst_off) const; + typedef std::vector<Stub_table<size, big_endian>*> Stub_tables; + const Stub_tables& + stub_tables() const + { return this->stub_tables_; } + + const Output_data_brlt_powerpc<size, big_endian>* + brlt_section() const + { return this->brlt_section_; } + + void + add_branch_lookup_table(Address to) + { + unsigned int off = this->branch_lookup_table_.size() * (size / 8); + this->branch_lookup_table_.insert(std::make_pair(to, off)); + } + + Address + find_branch_lookup_table(Address to) + { + typename Branch_lookup_table::const_iterator p + = this->branch_lookup_table_.find(to); + return p == this->branch_lookup_table_.end() ? invalid_address : p->second; + } + + void + write_branch_lookup_table(unsigned char *oview) + { + for (typename Branch_lookup_table::const_iterator p + = this->branch_lookup_table_.begin(); + p != this->branch_lookup_table_.end(); + ++p) + { + elfcpp::Swap<32, big_endian>::writeval(oview + p->second, p->first); + } + } + private: // The class which scans relocations. @@ -716,17 +819,19 @@ class Target_powerpc : public Sized_target<size, big_endian> void make_iplt_section(Symbol_table*, Layout*); + void + make_brlt_section(Layout*); + // Create a PLT entry for a global symbol. void - make_plt_entry(Symbol_table*, Layout*, Symbol*, - const elfcpp::Rela<size, big_endian>&, - const Sized_relobj_file<size, big_endian>* object); + make_plt_entry(Symbol_table*, Layout*, Symbol*); // Create a PLT entry for a local IFUNC symbol. void make_local_ifunc_plt_entry(Symbol_table*, Layout*, - const elfcpp::Rela<size, big_endian>&, - Sized_relobj_file<size, big_endian>*); + Sized_relobj_file<size, big_endian>*, + unsigned int); + // Create a GOT entry for local dynamic __tls_get_addr. unsigned int @@ -756,6 +861,51 @@ class Target_powerpc : public Sized_target<size, big_endian> reloc, this->rela_dyn_section(layout)); } + // Look over all the input sections, deciding where to place stub. + void + group_sections(Layout*, const Task*); + + // Sort output sections by address. + struct Sort_sections + { + bool + operator()(const Output_section* sec1, const Output_section* sec2) + { return sec1->address() < sec2->address(); } + }; + + class Branch_info + { + public: + Branch_info(Powerpc_relobj<size, big_endian>* ppc_object, + unsigned int data_shndx, + Address r_offset, + unsigned int r_type, + unsigned int r_sym, + Address addend) + : object_(ppc_object), shndx_(data_shndx), offset_(r_offset), + r_type_(r_type), r_sym_(r_sym), addend_(addend) + { } + + ~Branch_info() + { } + + // If this branch needs a plt call stub, or a long branch stub, make one. + void + make_stub(Stub_table<size, big_endian>*, + Stub_table<size, big_endian>*, + Symbol_table*) const; + + private: + // The branch location.. + Powerpc_relobj<size, big_endian>* object_; + unsigned int shndx_; + Address offset_; + // ..and the branch type and destination. + unsigned int r_type_; + unsigned int r_sym_; + Address addend_; + }; + // Information about this specific target which we pass to the // general Target structure. static Target::Target_info powerpc_info; @@ -772,15 +922,17 @@ class Target_powerpc : public Sized_target<size, big_endian> GOT_TYPE_TPREL // entry for @got@tprel }; - // The GOT output section. + // The GOT section. Output_data_got_powerpc<size, big_endian>* got_; - // The PLT output section. + // The PLT section. Output_data_plt_powerpc<size, big_endian>* plt_; - // The IPLT output section. + // The IPLT section. Output_data_plt_powerpc<size, big_endian>* iplt_; - // The .glink output section. + // Section holding long branch destinations. + Output_data_brlt_powerpc<size, big_endian>* brlt_section_; + // The .glink section. Output_data_glink<size, big_endian>* glink_; - // The dynamic reloc output section. + // The dynamic reloc section. Reloc_section* rela_dyn_; // Relocs saved to avoid a COPY reloc. Copy_relocs<elfcpp::SHT_RELA, size, big_endian> copy_relocs_; @@ -788,6 +940,13 @@ class Target_powerpc : public Sized_target<size, big_endian> Output_data_space* dynbss_; // Offset of the GOT entry for local dynamic __tls_get_addr calls. unsigned int tlsld_got_offset_; + + Stub_tables stub_tables_; + typedef Unordered_map<Address, unsigned int> Branch_lookup_table; + Branch_lookup_table branch_lookup_table_; + + typedef std::vector<Branch_info> Branches; + Branches branch_info_; }; template<> @@ -1642,6 +1801,403 @@ Target_powerpc<size, big_endian>::rela_dyn_section(Layout* layout) return this->rela_dyn_; } +class Stub_control +{ + public: + // Determine the stub group size. The group size is the absolute + // value of the parameter --stub-group-size. If --stub-group-size + // is passed a negative value, we restrict stubs to be always before + // the stubbed branches. + Stub_control(int32_t size) + : state_(NO_GROUP), stub_group_size_(abs(size)), + stub14_group_size_(abs(size)), + stubs_always_before_branch_(size < 0), suppress_size_errors_(false), + group_end_addr_(0), owner_(NULL), output_section_(NULL) + { + if (stub_group_size_ == 1) + { + // Default values. + if (stubs_always_before_branch_) + { + stub_group_size_ = 0x1e00000; + stub14_group_size_ = 0x7800; + } + else + { + stub_group_size_ = 0x1c00000; + stub14_group_size_ = 0x7000; + } + suppress_size_errors_ = true; + } + } + + // Return true iff input section can be handled by current stub + // group. + bool + can_add_to_stub_group(Output_section* o, + const Output_section::Input_section* i, + bool has14); + + const Output_section::Input_section* + owner() + { return owner_; } + + Output_section* + output_section() + { return output_section_; } + + private: + typedef enum + { + NO_GROUP, + FINDING_STUB_SECTION, + HAS_STUB_SECTION + } State; + + State state_; + uint32_t stub_group_size_; + uint32_t stub14_group_size_; + bool stubs_always_before_branch_; + bool suppress_size_errors_; + uint64_t group_end_addr_; + const Output_section::Input_section* owner_; + Output_section* output_section_; +}; + +// Return true iff input section can be handled by current stub/ +// group. + +bool +Stub_control::can_add_to_stub_group(Output_section* o, + const Output_section::Input_section* i, + bool has14) +{ + uint32_t group_size + = has14 ? this->stub14_group_size_ : this->stub_group_size_; + bool whole_sec = o->order() == ORDER_INIT || o->order() == ORDER_FINI; + uint64_t this_size; + uint64_t start_addr = o->address(); + + if (whole_sec) + // .init and .fini sections are pasted together to form a single + // function. We can't be adding stubs in the middle of the function. + this_size = o->data_size(); + else + { + start_addr += i->relobj()->output_section_offset(i->shndx()); + this_size = i->data_size(); + } + uint64_t end_addr = start_addr + this_size; + bool toobig = this_size > group_size; + + if (toobig && !this->suppress_size_errors_) + gold_warning(_("%s:%s exceeds group size"), + i->relobj()->name().c_str(), + i->relobj()->section_name(i->shndx()).c_str()); + + if (this->state_ != HAS_STUB_SECTION + && (!whole_sec || this->output_section_ != o)) + { + this->owner_ = i; + this->output_section_ = o; + } + + if (this->state_ == NO_GROUP) + { + this->state_ = FINDING_STUB_SECTION; + this->group_end_addr_ = end_addr; + } + else if (this->group_end_addr_ - start_addr < group_size) + ; + // Adding this section would make the group larger than GROUP_SIZE. + else if (this->state_ == FINDING_STUB_SECTION + && !this->stubs_always_before_branch_ + && !toobig) + { + // But wait, there's more! Input sections up to GROUP_SIZE + // bytes before the stub table can be handled by it too. + this->state_ = HAS_STUB_SECTION; + this->group_end_addr_ = end_addr; + } + else + { + this->state_ = NO_GROUP; + return false; + } + return true; +} + +// Look over all the input sections, deciding where to place stubs. + +template<int size, bool big_endian> +void +Target_powerpc<size, big_endian>::group_sections(Layout* layout, + const Task*) +{ + Stub_control stub_control(parameters->options().stub_group_size()); + + // Group input sections and insert stub table + Stub_table<size, big_endian>* stub_table = NULL; + Layout::Section_list section_list; + layout->get_executable_sections(§ion_list); + std::stable_sort(section_list.begin(), section_list.end(), Sort_sections()); + for (Layout::Section_list::reverse_iterator o = section_list.rbegin(); + o != section_list.rend(); + ++o) + { + typedef Output_section::Input_section_list Input_section_list; + for (Input_section_list::const_reverse_iterator i + = (*o)->input_sections().rbegin(); + i != (*o)->input_sections().rend(); + ++i) + { + if (i->is_input_section()) + { + Powerpc_relobj<size, big_endian>* ppcobj = static_cast + <Powerpc_relobj<size, big_endian>*>(i->relobj()); + bool has14 = ppcobj->has_14bit_branch(i->shndx()); + if (!stub_control.can_add_to_stub_group(*o, &*i, has14)) + { + stub_table->init(stub_control.owner(), + stub_control.output_section()); + stub_table = NULL; + } + if (stub_table == NULL) + stub_table = this->new_stub_table(); + ppcobj->set_stub_table(i->shndx(), stub_table); + } + } + } + if (stub_table != NULL) + stub_table->init(stub_control.owner(), stub_control.output_section()); +} + +// If this branch needs a plt call stub, or a long branch stub, make one. + +template<int size, bool big_endian> +void +Target_powerpc<size, big_endian>::Branch_info::make_stub( + Stub_table<size, big_endian>* stub_table, + Stub_table<size, big_endian>* ifunc_stub_table, + Symbol_table* symtab) const +{ + Symbol* sym = this->object_->global_symbol(this->r_sym_); + if (sym != NULL && sym->is_forwarder()) + sym = symtab->resolve_forwards(sym); + const Sized_symbol<size>* gsym = static_cast<const Sized_symbol<size>*>(sym); + if (gsym != NULL + ? use_plt_offset<size>(gsym, Scan::get_reference_flags(this->r_type_)) + : this->object_->local_has_plt_offset(this->r_sym_)) + { + if (stub_table == NULL) + stub_table = this->object_->stub_table(this->shndx_); + if (stub_table == NULL) + { + // This is a ref from a data section to an ifunc symbol. + stub_table = ifunc_stub_table; + } + gold_assert(stub_table != NULL); + if (gsym != NULL) + stub_table->add_plt_call_entry(this->object_, gsym, + this->r_type_, this->addend_); + else + stub_table->add_plt_call_entry(this->object_, this->r_sym_, + this->r_type_, this->addend_); + } + else + { + unsigned int max_branch_offset; + if (this->r_type_ == elfcpp::R_POWERPC_REL14 + || this->r_type_ == elfcpp::R_POWERPC_REL14_BRTAKEN + || this->r_type_ == elfcpp::R_POWERPC_REL14_BRNTAKEN) + max_branch_offset = 1 << 15; + else if (this->r_type_ == elfcpp::R_POWERPC_REL24 + || this->r_type_ == elfcpp::R_PPC_PLTREL24 + || this->r_type_ == elfcpp::R_PPC_LOCAL24PC) + max_branch_offset = 1 << 25; + else + return; + Address from = this->object_->get_output_section_offset(this->shndx_); + gold_assert(from != invalid_address); + from += (this->object_->output_section(this->shndx_)->address() + + this->offset_); + Address to; + if (gsym != NULL) + { + switch (gsym->source()) + { + case Symbol::FROM_OBJECT: + { + Object* symobj = gsym->object(); + if (symobj->is_dynamic() + || symobj->pluginobj() != NULL) + return; + bool is_ordinary; + unsigned int shndx = gsym->shndx(&is_ordinary); + if (shndx == elfcpp::SHN_UNDEF) + return; + } + break; + + case Symbol::IS_UNDEFINED: + return; + + default: + break; + } + Symbol_table::Compute_final_value_status status; + to = symtab->compute_final_value<size>(gsym, &status); + if (status != Symbol_table::CFVS_OK) + return; + } + else + { + const Symbol_value<size>* psymval + = this->object_->local_symbol(this->r_sym_); + Symbol_value<size> symval; + typedef Sized_relobj_file<size, big_endian> ObjType; + typename ObjType::Compute_final_local_value_status status + = this->object_->compute_final_local_value(this->r_sym_, psymval, + &symval, symtab); + if (status != ObjType::CFLV_OK + || !symval.has_output_value()) + return; + to = symval.value(this->object_, 0); + } + if (stub_table == NULL) + stub_table = this->object_->stub_table(this->shndx_); + gold_assert(stub_table != NULL); + if (size == 64 && is_branch_reloc(this->r_type_)) + { + unsigned int dest_shndx; + to = stub_table->targ()->symval_for_branch(to, gsym, this->object_, + &dest_shndx); + } + Address delta = to - from; + if (delta + max_branch_offset >= 2 * max_branch_offset) + { + stub_table->add_long_branch_entry(this->object_, to); + } + } +} + +// Relaxation hook. This is where we do stub generation. + +template<int size, bool big_endian> +bool +Target_powerpc<size, big_endian>::do_relax(int pass, + const Input_objects*, + Symbol_table* symtab, + Layout* layout, + const Task* task) +{ + unsigned int prev_brlt_size = 0; + if (pass == 1) + this->group_sections(layout, task); + else + { + prev_brlt_size = this->branch_lookup_table_.size(); + this->branch_lookup_table_.clear(); + for (typename Stub_tables::iterator p = this->stub_tables_.begin(); + p != this->stub_tables_.end(); + ++p) + { + (*p)->clear_long_branch_stubs(); + } + } + + // We need address of stub tables valid for make_stub. + for (typename Stub_tables::iterator p = this->stub_tables_.begin(); + p != this->stub_tables_.end(); + ++p) + { + const Powerpc_relobj<size, big_endian>* object + = static_cast<const Powerpc_relobj<size, big_endian>*>((*p)->relobj()); + Address off = object->get_output_section_offset((*p)->shndx()); + gold_assert(off != invalid_address); + Output_section* os = (*p)->output_section(); + (*p)->set_address_and_size(os, off); + } + + Stub_table<size, big_endian>* ifunc_stub_table + = this->stub_tables_.size() == 0 ? NULL : this->stub_tables_[0]; + Stub_table<size, big_endian>* one_stub_table + = this->stub_tables_.size() != 1 ? NULL : ifunc_stub_table; + for (typename Branches::const_iterator b = this->branch_info_.begin(); + b != this->branch_info_.end(); + b++) + { + b->make_stub(one_stub_table, ifunc_stub_table, symtab); + } + + unsigned int num_huge_branches = this->branch_lookup_table_.size(); + bool again = num_huge_branches != prev_brlt_size; + if (size == 64 && num_huge_branches != 0) + this->make_brlt_section(layout); + if (size == 64 && again) + this->brlt_section_->set_current_size(num_huge_branches); + + typedef Unordered_set<Output_section*> Output_sections; + Output_sections os_need_update; + for (typename Stub_tables::iterator p = this->stub_tables_.begin(); + p != this->stub_tables_.end(); + ++p) + { + if ((*p)->size_update()) + { + again = true; + os_need_update.insert((*p)->output_section()); + } + } + + for (typename Output_sections::iterator p = os_need_update.begin(); + p != os_need_update.end(); + p++) + { + Output_section* os = *p; + Address off = 0; + typedef Output_section::Input_section_list Input_section_list; + for (Input_section_list::const_iterator i = os->input_sections().begin(); + i != os->input_sections().end(); + ++i) + { + off = align_address(off, i->addralign()); + if (i->is_input_section() || i->is_relaxed_input_section()) + i->relobj()->set_section_offset(i->shndx(), off); + if (i->is_relaxed_input_section()) + { + Stub_table<size, big_endian>* stub_table + = static_cast<Stub_table<size, big_endian>*>( + i->relaxed_input_section()); + off += stub_table->set_address_and_size(os, off); + } + else + off += i->data_size(); + } + // If .brlt is part of this output section, then we have just + // done the offset adjustment. + os->clear_section_offsets_need_adjustment(); + } + + if (size == 64 + && !again + && num_huge_branches != 0 + && parameters->options().output_is_position_independent()) + { + // Fill in the BRLT relocs. + this->brlt_section_->reset_data_size(); + for (typename Branch_lookup_table::const_iterator p + = this->branch_lookup_table_.begin(); + p != this->branch_lookup_table_.end(); + ++p) + { + this->brlt_section_->add_reloc(p->first, p->second); + } + this->brlt_section_->finalize_data_size(); + } + return again; +} + // A class to handle the PLT data. template<int size, bool big_endian> @@ -1735,7 +2291,7 @@ Output_data_plt_powerpc<size, big_endian>::add_entry(Symbol* gsym) { if (!gsym->has_plt_offset()) { - off_t off = this->current_data_size(); + section_size_type off = this->current_data_size(); if (off == 0) off += this->first_plt_entry_offset(); gsym->set_plt_offset(off); @@ -1755,7 +2311,7 @@ Output_data_plt_powerpc<size, big_endian>::add_ifunc_entry(Symbol* gsym) { if (!gsym->has_plt_offset()) { - off_t off = this->current_data_size(); + section_size_type off = this->current_data_size(); gsym->set_plt_offset(off); unsigned int dynrel = elfcpp::R_POWERPC_IRELATIVE; if (size == 64) @@ -1776,7 +2332,7 @@ Output_data_plt_powerpc<size, big_endian>::add_local_ifunc_entry( { if (!relobj->local_has_plt_offset(local_sym_index)) { - off_t off = this->current_data_size(); + section_size_type off = this->current_data_size(); relobj->set_local_plt_offset(local_sym_index, off); unsigned int dynrel = elfcpp::R_POWERPC_IRELATIVE; if (size == 64) @@ -1838,6 +2394,7 @@ static const uint32_t mflr_11 = 0x7d6802a6; static const uint32_t mflr_12 = 0x7d8802a6; static const uint32_t mtctr_0 = 0x7c0903a6; static const uint32_t mtctr_11 = 0x7d6903a6; +static const uint32_t mtctr_12 = 0x7d8903a6; static const uint32_t mtlr_0 = 0x7c0803a6; static const uint32_t mtlr_12 = 0x7d8803a6; static const uint32_t nop = 0x60000000; @@ -1857,7 +2414,7 @@ Output_data_plt_powerpc<size, big_endian>::do_write(Output_file* of) { if (size == 32) { - const off_t offset = this->offset(); + const section_size_type offset = this->offset(); const section_size_type oview_size = convert_to_section_size_type(this->data_size()); unsigned char* const oview = of->get_output_view(offset, oview_size); @@ -1867,8 +2424,7 @@ Output_data_plt_powerpc<size, big_endian>::do_write(Output_file* of) // The address of the .glink branch table const Output_data_glink<size, big_endian>* glink = this->targ_->glink_section(); - elfcpp::Elf_types<32>::Elf_Addr branch_tab - = glink->address() + glink->pltresolve(); + elfcpp::Elf_types<32>::Elf_Addr branch_tab = glink->address(); while (pov < endpov) { @@ -1942,117 +2498,340 @@ Target_powerpc<size, big_endian>::make_iplt_section(Symbol_table* symtab, } } -// A class to handle .glink. +// A section for huge long branch addresses, similar to plt section. template<int size, bool big_endian> -class Output_data_glink : public Output_section_data +class Output_data_brlt_powerpc : public Output_section_data_build { public: - static const int pltresolve_size = 16*4; + typedef typename elfcpp::Elf_types<size>::Elf_Addr Address; + typedef Output_data_reloc<elfcpp::SHT_RELA, true, + size, big_endian> Reloc_section; - Output_data_glink(Target_powerpc<size, big_endian>*); + Output_data_brlt_powerpc(Target_powerpc<size, big_endian>* targ, + Reloc_section* brlt_rel) + : Output_section_data_build(size == 32 ? 4 : 8), + rel_(brlt_rel), + targ_(targ) + { } - // Add an entry + // Add a reloc for an entry in the BRLT. void - add_entry(const Sized_relobj_file<size, big_endian>*, - const Symbol*, - const elfcpp::Rela<size, big_endian>&); + add_reloc(Address to, unsigned int off) + { this->rel_->add_relative(elfcpp::R_POWERPC_RELATIVE, this, off, to); } + // Update section and reloc section size. void - add_entry(const Sized_relobj_file<size, big_endian>*, - unsigned int, - const elfcpp::Rela<size, big_endian>&); + set_current_size(unsigned int num_branches) + { + this->reset_address_and_file_offset(); + this->set_current_data_size(num_branches * 16); + this->finalize_data_size(); + Output_section* os = this->output_section(); + os->set_section_offsets_need_adjustment(); + if (this->rel_ != NULL) + { + unsigned int reloc_size + = Reloc_types<elfcpp::SHT_RELA, size, big_endian>::reloc_size; + this->rel_->reset_address_and_file_offset(); + this->rel_->set_current_data_size(num_branches * reloc_size); + this->rel_->finalize_data_size(); + Output_section* os = this->rel_->output_section(); + os->set_section_offsets_need_adjustment(); + } + } - unsigned int - find_entry(const Symbol*) const; + protected: + void + do_adjust_output_section(Output_section* os) + { + os->set_entsize(0); + } - unsigned int - find_entry(const Sized_relobj_file<size, big_endian>*, unsigned int) const; + // Write to a map file. + void + do_print_to_mapfile(Mapfile* mapfile) const + { mapfile->print_output_data(this, "** BRLT"); } - unsigned int - find_entry(const Sized_relobj_file<size, big_endian>*, - const Symbol*, - const elfcpp::Rela<size, big_endian>&) const; + private: + // Write out the BRLT data. + void + do_write(Output_file*); - unsigned int - find_entry(const Sized_relobj_file<size, big_endian>*, - unsigned int, - const elfcpp::Rela<size, big_endian>&) const; + // The reloc section. + Reloc_section* rel_; + Target_powerpc<size, big_endian>* targ_; +}; - unsigned int - glink_entry_size() const +// Make the branch lookup table section. + +template<int size, bool big_endian> +void +Target_powerpc<size, big_endian>::make_brlt_section(Layout* layout) +{ + if (size == 64 && this->brlt_section_ == NULL) + { + Reloc_section* brlt_rel = NULL; + bool is_pic = parameters->options().output_is_position_independent(); + if (is_pic) + { + // When PIC we can't fill in .brlt (like .plt it can be a + // bss style section) but must initialise at runtime via + // dynamic relocats. + this->rela_dyn_section(layout); + brlt_rel = new Reloc_section(false); + this->rela_dyn_->output_section()->add_output_section_data(brlt_rel); + } + this->brlt_section_ + = new Output_data_brlt_powerpc<size, big_endian>(this, brlt_rel); + if (this->plt_ && is_pic) + this->plt_->output_section() + ->add_output_section_data(this->brlt_section_); + else + layout->add_output_section_data(".brlt", + (is_pic ? elfcpp::SHT_NOBITS + : elfcpp::SHT_PROGBITS), + elfcpp::SHF_ALLOC | elfcpp::SHF_WRITE, + this->brlt_section_, + (is_pic ? ORDER_SMALL_BSS + : ORDER_SMALL_DATA), + false); + } +} + +// Write out .brlt when non-PIC. + +template<int size, bool big_endian> +void +Output_data_brlt_powerpc<size, big_endian>::do_write(Output_file* of) +{ + if (size == 64 && !parameters->options().output_is_position_independent()) + { + const section_size_type offset = this->offset(); + const section_size_type oview_size + = convert_to_section_size_type(this->data_size()); + unsigned char* const oview = of->get_output_view(offset, oview_size); + + this->targ_->write_branch_lookup_table(oview); + of->write_output_view(offset, oview_size, oview); + } +} + +// Stub_table holds information about plt and long branch stubs. +// Stubs are built in an area following some input section determined +// by group_sections(). This input section is converted to a relaxed +// input section allowing it to be resized to accommodate the stubs + +template<int size, bool big_endian> +class Stub_table : public Output_relaxed_input_section +{ + public: + typedef typename elfcpp::Elf_types<size>::Elf_Addr Address; + static const Address invalid_address = static_cast<Address>(0) - 1; + + Stub_table(Target_powerpc<size, big_endian>* targ) + : Output_relaxed_input_section(NULL, 0, 0), + targ_(targ), plt_call_stubs_(), long_branch_stubs_(), + orig_data_size_(0), plt_size_(0), branch_size_(0), prev_size_(0) + { } + + // Delayed Output_relaxed_input_section init. + void + init(const Output_section::Input_section*, Output_section*); + + // Add a plt call stub. + void + add_plt_call_entry(const Sized_relobj_file<size, big_endian>*, + const Symbol*, + unsigned int, + Address); + + void + add_plt_call_entry(const Sized_relobj_file<size, big_endian>*, + unsigned int, + unsigned int, + Address); + + // Find a given plt call stub. + Address + find_plt_call_entry(const Symbol*) const; + + Address + find_plt_call_entry(const Sized_relobj_file<size, big_endian>*, + unsigned int) const; + + Address + find_plt_call_entry(const Sized_relobj_file<size, big_endian>*, + const Symbol*, + unsigned int, + Address) const; + + Address + find_plt_call_entry(const Sized_relobj_file<size, big_endian>*, + unsigned int, + unsigned int, + Address) const; + + // Add a long branch stub. + void + add_long_branch_entry(const Powerpc_relobj<size, big_endian>*, Address); + + Address + find_long_branch_entry(const Powerpc_relobj<size, big_endian>*, Address); + + void + clear_long_branch_stubs() { - if (size == 32) - return 4 * 4; - else - // FIXME: We should be using multiple glink sections for - // stubs to support > 33M applications. - return 8 * 4; + this->long_branch_stubs_.clear(); + this->branch_size_ = 0; } - off_t - pltresolve() const + Address + set_address_and_size(const Output_section* os, Address off) { - return this->pltresolve_; + Address start_off = off; + off += this->orig_data_size_; + Address my_size = this->plt_size_ + this->branch_size_; + if (my_size != 0) + off = align_address(off, this->stub_align()); + // Include original section size and alignment padding in size + my_size += off - start_off; + this->reset_address_and_file_offset(); + this->set_current_data_size(my_size); + this->set_address_and_file_offset(os->address() + start_off, + os->offset() + start_off); + return my_size; } - protected: - // Write to a map file. + Address + stub_address() + { + return align_address(this->address() + this->orig_data_size_, + this->stub_align()); + } + + Address + stub_offset() + { + return align_address(this->offset() + this->orig_data_size_, + this->stub_align()); + } + + section_size_type + plt_size() const + { return this->plt_size_; } + + bool + size_update() + { + Output_section* os = this->output_section(); + if (os->addralign() < this->stub_align()) + { + os->set_addralign(this->stub_align()); + // FIXME: get rid of the insane checkpointing. + // We can't increase alignment of the input section to which + // stubs are attached; The input section may be .init which + // is pasted together with other .init sections to form a + // function. Aligning might insert zero padding resulting in + // sigill. However we do need to increase alignment of the + // output section so that the align_address() on offset in + // set_address_and_size() adds the same padding as the + // align_address() on address in stub_address(). + // What's more, we need this alignment for the layout done in + // relaxation_loop_body() so that the output section starts at + // a suitably aligned address. + os->checkpoint_set_addralign(this->stub_align()); + } + if (this->prev_size_ != this->plt_size_ + this->branch_size_) + { + this->prev_size_ = this->plt_size_ + this->branch_size_; + return true; + } + return false; + } + + section_size_type + prev_size() const + { return this->prev_size_; } + void - do_print_to_mapfile(Mapfile* mapfile) const - { mapfile->print_output_data(this, _("** glink")); } + set_prev_size(section_size_type val) + { this->prev_size_ = val; } + + Target_powerpc<size, big_endian>* + targ() const + { return targ_; } private: - void - set_final_data_size(); + unsigned int + stub_align() + { return size == 32 ? 16 : 32; } - // Write out .glink + // We keep plt stubs aligned, so no fancy sizing. + unsigned int + plt_call_size() const + { return size == 32 ? 16 : 32; } + + // Return long branch stub size. + unsigned int + branch_stub_size(Address to) + { + Address loc = this->stub_address() + this->plt_size_ + this->branch_size_; + if (loc - to + (1 << 25) < 2 << 25) + return 4; + if (size == 64 || !parameters->options().output_is_position_independent()) + return 16; + return 32; + } + + // Write out stubs. void do_write(Output_file*); - class Glink_sym_ent + // Plt call stub keys. + class Plt_stub_ent { public: - Glink_sym_ent(const Symbol* sym) + Plt_stub_ent(const Symbol* sym) : sym_(sym), object_(0), addend_(0), locsym_(0) { } - Glink_sym_ent(const Sized_relobj_file<size, big_endian>* object, - unsigned int locsym_index) + Plt_stub_ent(const Sized_relobj_file<size, big_endian>* object, + unsigned int locsym_index) : sym_(NULL), object_(object), addend_(0), locsym_(locsym_index) { } - Glink_sym_ent(const Sized_relobj_file<size, big_endian>* object, - const Symbol* sym, - const elfcpp::Rela<size, big_endian>& reloc) + Plt_stub_ent(const Sized_relobj_file<size, big_endian>* object, + const Symbol* sym, + unsigned int r_type, + Address addend) : sym_(sym), object_(0), addend_(0), locsym_(0) { if (size != 32) - this->addend_ = reloc.get_r_addend(); + this->addend_ = addend; else if (parameters->options().output_is_position_independent() - && (elfcpp::elf_r_type<size>(reloc.get_r_info()) - == elfcpp::R_PPC_PLTREL24)) + && r_type == elfcpp::R_PPC_PLTREL24) { - this->addend_ = reloc.get_r_addend(); + this->addend_ = addend; if (this->addend_ >= 32768) this->object_ = object; } } - Glink_sym_ent(const Sized_relobj_file<size, big_endian>* object, - unsigned int locsym_index, - const elfcpp::Rela<size, big_endian>& reloc) + Plt_stub_ent(const Sized_relobj_file<size, big_endian>* object, + unsigned int locsym_index, + unsigned int r_type, + Address addend) : sym_(NULL), object_(object), addend_(0), locsym_(locsym_index) { if (size != 32) - this->addend_ = reloc.get_r_addend(); + this->addend_ = addend; else if (parameters->options().output_is_position_independent() - && (elfcpp::elf_r_type<size>(reloc.get_r_info()) - == elfcpp::R_PPC_PLTREL24)) - this->addend_ = reloc.get_r_addend(); + && r_type == elfcpp::R_PPC_PLTREL24) + this->addend_ = addend; } - bool operator==(const Glink_sym_ent& that) const + bool operator==(const Plt_stub_ent& that) const { return (this->sym_ == that.sym_ && this->object_ == that.object_ @@ -2066,10 +2845,10 @@ class Output_data_glink : public Output_section_data unsigned int locsym_; }; - class Glink_sym_ent_hash + class Plt_stub_ent_hash { public: - size_t operator()(const Glink_sym_ent& ent) const + size_t operator()(const Plt_stub_ent& ent) const { return (reinterpret_cast<uintptr_t>(ent.sym_) ^ reinterpret_cast<uintptr_t>(ent.object_) @@ -2078,117 +2857,237 @@ class Output_data_glink : public Output_section_data } }; - // Map sym/object/addend to index. - typedef Unordered_map<Glink_sym_ent, unsigned int, - Glink_sym_ent_hash> Glink_entries; - Glink_entries glink_entries_; + // Long branch stub keys. + class Branch_stub_ent + { + public: + Branch_stub_ent(const Powerpc_relobj<size, big_endian>* obj, Address to) + : dest_(to), toc_base_off_(0) + { + if (size == 64) + toc_base_off_ = obj->toc_base_offset(); + } + + bool operator==(const Branch_stub_ent& that) const + { + return (this->dest_ == that.dest_ + && (size == 32 + || this->toc_base_off_ == that.toc_base_off_)); + } - // Offset of pltresolve stub (actually, branch table for 32-bit) - off_t pltresolve_; + Address dest_; + unsigned int toc_base_off_; + }; - // Allows access to .got and .plt for do_write. + class Branch_stub_ent_hash + { + public: + size_t operator()(const Branch_stub_ent& ent) const + { return ent.dest_ ^ ent.toc_base_off_; } + }; + + // In a sane world this would be a global. Target_powerpc<size, big_endian>* targ_; + // Map sym/object/addend to stub offset. + typedef Unordered_map<Plt_stub_ent, unsigned int, + Plt_stub_ent_hash> Plt_stub_entries; + Plt_stub_entries plt_call_stubs_; + // Map destination address to stub offset. + typedef Unordered_map<Branch_stub_ent, unsigned int, + Branch_stub_ent_hash> Branch_stub_entries; + Branch_stub_entries long_branch_stubs_; + // size of input section + section_size_type orig_data_size_; + // size of stubs + section_size_type plt_size_, branch_size_, prev_size_; }; -// Create the glink section. +// Make a new stub table, and record. + +template<int size, bool big_endian> +Stub_table<size, big_endian>* +Target_powerpc<size, big_endian>::new_stub_table() +{ + Stub_table<size, big_endian>* stub_table + = new Stub_table<size, big_endian>(this); + this->stub_tables_.push_back(stub_table); + return stub_table; +} + +// Delayed stub table initialisation, because we create the stub table +// before we know to which section it will be attached. template<int size, bool big_endian> -Output_data_glink<size, big_endian>::Output_data_glink( - Target_powerpc<size, big_endian>* targ) - : Output_section_data(16), - pltresolve_(0), targ_(targ) +void +Stub_table<size, big_endian>::init( + const Output_section::Input_section* owner, + Output_section* output_section) { + this->set_relobj(owner->relobj()); + this->set_shndx(owner->shndx()); + this->set_addralign(this->relobj()->section_addralign(this->shndx())); + this->set_output_section(output_section); + this->orig_data_size_ = owner->current_data_size(); + + std::vector<Output_relaxed_input_section*> new_relaxed; + new_relaxed.push_back(this); + output_section->convert_input_sections_to_relaxed_sections(new_relaxed); } -// Add an entry to glink, if we do not already have one for this +// Add a plt call stub, if we do not already have one for this // sym/object/addend combo. template<int size, bool big_endian> void -Output_data_glink<size, big_endian>::add_entry( +Stub_table<size, big_endian>::add_plt_call_entry( const Sized_relobj_file<size, big_endian>* object, const Symbol* gsym, - const elfcpp::Rela<size, big_endian>& reloc) + unsigned int r_type, + Address addend) { - Glink_sym_ent ent(object, gsym, reloc); - unsigned int indx = this->glink_entries_.size(); - this->glink_entries_.insert(std::make_pair(ent, indx)); + Plt_stub_ent ent(object, gsym, r_type, addend); + Address off = this->plt_size_; + if (this->plt_call_stubs_.insert(std::make_pair(ent, off)).second) + this->plt_size_ = off + this->plt_call_size(); } template<int size, bool big_endian> void -Output_data_glink<size, big_endian>::add_entry( +Stub_table<size, big_endian>::add_plt_call_entry( const Sized_relobj_file<size, big_endian>* object, unsigned int locsym_index, - const elfcpp::Rela<size, big_endian>& reloc) + unsigned int r_type, + Address addend) { - Glink_sym_ent ent(object, locsym_index, reloc); - unsigned int indx = this->glink_entries_.size(); - this->glink_entries_.insert(std::make_pair(ent, indx)); + Plt_stub_ent ent(object, locsym_index, r_type, addend); + Address off = this->plt_size_; + if (this->plt_call_stubs_.insert(std::make_pair(ent, off)).second) + this->plt_size_ = off + this->plt_call_size(); } +// Find a plt call stub. + template<int size, bool big_endian> -unsigned int -Output_data_glink<size, big_endian>::find_entry( +typename elfcpp::Elf_types<size>::Elf_Addr +Stub_table<size, big_endian>::find_plt_call_entry( const Sized_relobj_file<size, big_endian>* object, const Symbol* gsym, - const elfcpp::Rela<size, big_endian>& reloc) const + unsigned int r_type, + Address addend) const { - Glink_sym_ent ent(object, gsym, reloc); - typename Glink_entries::const_iterator p = this->glink_entries_.find(ent); - gold_assert(p != this->glink_entries_.end()); - return p->second; + Plt_stub_ent ent(object, gsym, r_type, addend); + typename Plt_stub_entries::const_iterator p = this->plt_call_stubs_.find(ent); + return p == this->plt_call_stubs_.end() ? invalid_address : p->second; } template<int size, bool big_endian> -unsigned int -Output_data_glink<size, big_endian>::find_entry(const Symbol* gsym) const +typename elfcpp::Elf_types<size>::Elf_Addr +Stub_table<size, big_endian>::find_plt_call_entry(const Symbol* gsym) const { - Glink_sym_ent ent(gsym); - typename Glink_entries::const_iterator p = this->glink_entries_.find(ent); - gold_assert(p != this->glink_entries_.end()); - return p->second; + Plt_stub_ent ent(gsym); + typename Plt_stub_entries::const_iterator p = this->plt_call_stubs_.find(ent); + return p == this->plt_call_stubs_.end() ? invalid_address : p->second; } template<int size, bool big_endian> -unsigned int -Output_data_glink<size, big_endian>::find_entry( +typename elfcpp::Elf_types<size>::Elf_Addr +Stub_table<size, big_endian>::find_plt_call_entry( const Sized_relobj_file<size, big_endian>* object, unsigned int locsym_index, - const elfcpp::Rela<size, big_endian>& reloc) const + unsigned int r_type, + Address addend) const { - Glink_sym_ent ent(object, locsym_index, reloc); - typename Glink_entries::const_iterator p = this->glink_entries_.find(ent); - gold_assert(p != this->glink_entries_.end()); - return p->second; + Plt_stub_ent ent(object, locsym_index, r_type, addend); + typename Plt_stub_entries::const_iterator p = this->plt_call_stubs_.find(ent); + return p == this->plt_call_stubs_.end() ? invalid_address : p->second; } template<int size, bool big_endian> -unsigned int -Output_data_glink<size, big_endian>::find_entry( +typename elfcpp::Elf_types<size>::Elf_Addr +Stub_table<size, big_endian>::find_plt_call_entry( const Sized_relobj_file<size, big_endian>* object, unsigned int locsym_index) const { - Glink_sym_ent ent(object, locsym_index); - typename Glink_entries::const_iterator p = this->glink_entries_.find(ent); - gold_assert(p != this->glink_entries_.end()); - return p->second; + Plt_stub_ent ent(object, locsym_index); + typename Plt_stub_entries::const_iterator p = this->plt_call_stubs_.find(ent); + return p == this->plt_call_stubs_.end() ? invalid_address : p->second; } +// Add a long branch stub if we don't already have one to given +// destination. + +template<int size, bool big_endian> +void +Stub_table<size, big_endian>::add_long_branch_entry( + const Powerpc_relobj<size, big_endian>* object, + Address to) +{ + Branch_stub_ent ent(object, to); + Address off = this->branch_size_; + if (this->long_branch_stubs_.insert(std::make_pair(ent, off)).second) + { + unsigned int stub_size = this->branch_stub_size(to); + this->branch_size_ = off + stub_size; + if (size == 64 && stub_size != 4) + this->targ_->add_branch_lookup_table(to); + } +} + +// Find long branch stub. + +template<int size, bool big_endian> +typename elfcpp::Elf_types<size>::Elf_Addr +Stub_table<size, big_endian>::find_long_branch_entry( + const Powerpc_relobj<size, big_endian>* object, + Address to) +{ + Branch_stub_ent ent(object, to); + typename Branch_stub_entries::const_iterator p + = this->long_branch_stubs_.find(ent); + return p == this->long_branch_stubs_.end() ? invalid_address : p->second; +} + +// A class to handle .glink. + +template<int size, bool big_endian> +class Output_data_glink : public Output_section_data +{ + public: + static const int pltresolve_size = 16*4; + + Output_data_glink(Target_powerpc<size, big_endian>* targ) + : Output_section_data(16), targ_(targ) + { } + + protected: + // Write to a map file. + void + do_print_to_mapfile(Mapfile* mapfile) const + { mapfile->print_output_data(this, _("** glink")); } + + private: + void + set_final_data_size(); + + // Write out .glink + void + do_write(Output_file*); + + // Allows access to .got and .plt for do_write. + Target_powerpc<size, big_endian>* targ_; +}; + template<int size, bool big_endian> void Output_data_glink<size, big_endian>::set_final_data_size() { - unsigned int count = this->glink_entries_.size(); - off_t total = count; + unsigned int count = this->targ_->plt_entry_count(); + section_size_type total = 0; if (count != 0) { if (size == 32) { - total *= 16; - this->pltresolve_ = total; - // space for branch table total += 4 * (count - 1); @@ -2197,8 +3096,6 @@ Output_data_glink<size, big_endian>::set_final_data_size() } else { - total *= 32; - this->pltresolve_ = total; total += this->pltresolve_size; // space for branch table @@ -2236,107 +3133,293 @@ write_insn(unsigned char* p, uint32_t v) elfcpp::Swap<32, big_endian>::writeval(p, v); } -// Write out .glink. +// Write out plt and long branch stub code. template<int size, bool big_endian> void -Output_data_glink<size, big_endian>::do_write(Output_file* of) +Stub_table<size, big_endian>::do_write(Output_file* of) { - const off_t off = this->offset(); + if (this->plt_call_stubs_.empty() + && this->long_branch_stubs_.empty()) + return; + + const section_size_type start_off = this->offset(); + const section_size_type off = this->stub_offset(); const section_size_type oview_size = - convert_to_section_size_type(this->data_size()); + convert_to_section_size_type(this->data_size() - (off - start_off)); unsigned char* const oview = of->get_output_view(off, oview_size); unsigned char* p; - // The base address of the .plt section. typedef typename elfcpp::Elf_types<size>::Elf_Addr Address; static const Address invalid_address = static_cast<Address>(0) - 1; - Address plt_base = this->targ_->plt_section()->address(); - Address iplt_base = invalid_address; - - const Output_data_got_powerpc<size, big_endian>* got - = this->targ_->got_section(); if (size == 64) { + const Output_data_got_powerpc<size, big_endian>* got + = this->targ_->got_section(); Address got_os_addr = got->output_section()->address(); - // Write out call stubs. - typename Glink_entries::const_iterator g; - for (g = this->glink_entries_.begin(); - g != this->glink_entries_.end(); - ++g) + if (!this->plt_call_stubs_.empty()) { - Address plt_addr; - bool is_ifunc; - const Symbol* gsym = g->first.sym_; - if (gsym != NULL) + // The base address of the .plt section. + Address plt_base = this->targ_->plt_section()->address(); + Address iplt_base = invalid_address; + + // Write out plt call stubs. + typename Plt_stub_entries::const_iterator cs; + for (cs = this->plt_call_stubs_.begin(); + cs != this->plt_call_stubs_.end(); + ++cs) { - is_ifunc = (gsym->type() == elfcpp::STT_GNU_IFUNC - && gsym->can_use_relative_reloc(false)); - plt_addr = gsym->plt_offset(); - } - else - { - is_ifunc = true; - const Sized_relobj_file<size, big_endian>* relobj - = g->first.object_; - unsigned int local_sym_index = g->first.locsym_; - plt_addr = relobj->local_plt_offset(local_sym_index); - } - if (is_ifunc) - { - if (iplt_base == invalid_address) - iplt_base = this->targ_->iplt_section()->address(); - plt_addr += iplt_base; + Address plt_addr; + bool is_ifunc; + const Symbol* gsym = cs->first.sym_; + if (gsym != NULL) + { + is_ifunc = (gsym->type() == elfcpp::STT_GNU_IFUNC + && gsym->can_use_relative_reloc(false)); + plt_addr = gsym->plt_offset(); + } + else + { + is_ifunc = true; + const Sized_relobj_file<size, big_endian>* relobj + = cs->first.object_; + unsigned int local_sym_index = cs->first.locsym_; + plt_addr = relobj->local_plt_offset(local_sym_index); + } + if (is_ifunc) + { + if (iplt_base == invalid_address) + iplt_base = this->targ_->iplt_section()->address(); + plt_addr += iplt_base; + } + else + plt_addr += plt_base; + const Powerpc_relobj<size, big_endian>* ppcobj = static_cast + <const Powerpc_relobj<size, big_endian>*>(cs->first.object_); + Address got_addr = got_os_addr + ppcobj->toc_base_offset(); + Address pltoff = plt_addr - got_addr; + + if (pltoff + 0x80008000 > 0xffffffff || (pltoff & 7) != 0) + gold_error(_("%s: linkage table error against `%s'"), + cs->first.object_->name().c_str(), + cs->first.sym_->demangled_name().c_str()); + + p = oview + cs->second; + if (ha(pltoff) != 0) + { + write_insn<big_endian>(p, addis_12_2 + ha(pltoff)), p += 4; + write_insn<big_endian>(p, std_2_1 + 40), p += 4; + write_insn<big_endian>(p, ld_11_12 + l(pltoff)), p += 4; + if (ha(pltoff + 16) != ha(pltoff)) + { + write_insn<big_endian>(p, addi_12_12 + l(pltoff)),p += 4; + pltoff = 0; + } + write_insn<big_endian>(p, mtctr_11), p += 4; + write_insn<big_endian>(p, ld_2_12 + l(pltoff + 8)), p += 4; + write_insn<big_endian>(p, ld_11_12 + l(pltoff + 16)), p += 4; + write_insn<big_endian>(p, bctr); + } + else + { + write_insn<big_endian>(p, std_2_1 + 40), p += 4; + write_insn<big_endian>(p, ld_11_2 + l(pltoff)), p += 4; + if (ha(pltoff + 16) != ha(pltoff)) + { + write_insn<big_endian>(p, addi_2_2 + l(pltoff)), p += 4; + pltoff = 0; + } + write_insn<big_endian>(p, mtctr_11), p += 4; + write_insn<big_endian>(p, ld_11_2 + l(pltoff + 16)), p += 4; + write_insn<big_endian>(p, ld_2_2 + l(pltoff + 8)), p += 4; + write_insn<big_endian>(p, bctr); + } } + } + + // Write out long branch stubs. + typename Branch_stub_entries::const_iterator bs; + for (bs = this->long_branch_stubs_.begin(); + bs != this->long_branch_stubs_.end(); + ++bs) + { + p = oview + this->plt_size_ + bs->second; + Address loc = this->stub_address() + this->plt_size_ + bs->second; + Address delta = bs->first.dest_ - loc; + if (delta + (1 << 25) < 2 << 25) + write_insn<big_endian>(p, b | (delta & 0x3fffffc)); else - plt_addr += plt_base; - const Powerpc_relobj<size, big_endian>* ppcobj = static_cast - <const Powerpc_relobj<size, big_endian>*>(g->first.object_); - Address got_addr = got_os_addr + ppcobj->toc_base_offset(); - Address pltoff = plt_addr - got_addr; - - if (pltoff + 0x80008000 > 0xffffffff || (pltoff & 7) != 0) - gold_error(_("%s: linkage table error against `%s'"), - g->first.object_->name().c_str(), - g->first.sym_->demangled_name().c_str()); - - p = oview + g->second * this->glink_entry_size(); - if (ha(pltoff) != 0) { - write_insn<big_endian>(p, addis_12_2 + ha(pltoff)), p += 4; - write_insn<big_endian>(p, std_2_1 + 40), p += 4; - write_insn<big_endian>(p, ld_11_12 + l(pltoff)), p += 4; - if (ha(pltoff + 16) != ha(pltoff)) + Address brlt_addr + = this->targ_->find_branch_lookup_table(bs->first.dest_); + gold_assert(brlt_addr != invalid_address); + brlt_addr += this->targ_->brlt_section()->address(); + Address got_addr = got_os_addr + bs->first.toc_base_off_; + Address brltoff = brlt_addr - got_addr; + if (ha(brltoff) == 0) { - write_insn<big_endian>(p, addi_12_12 + l(pltoff)), p += 4; - pltoff = 0; + write_insn<big_endian>(p, ld_11_2 + l(brltoff)), p += 4; + } + else + { + write_insn<big_endian>(p, addis_12_2 + ha(brltoff)), p += 4; + write_insn<big_endian>(p, ld_11_12 + l(brltoff)), p += 4; } write_insn<big_endian>(p, mtctr_11), p += 4; - write_insn<big_endian>(p, ld_2_12 + l(pltoff + 8)), p += 4; - write_insn<big_endian>(p, ld_11_12 + l(pltoff + 16)), p += 4; - write_insn<big_endian>(p, bctr), p += 4; + write_insn<big_endian>(p, bctr); } - else + } + } + else + { + if (!this->plt_call_stubs_.empty()) + { + // The base address of the .plt section. + Address plt_base = this->targ_->plt_section()->address(); + Address iplt_base = invalid_address; + // The address of _GLOBAL_OFFSET_TABLE_. + Address g_o_t = invalid_address; + + // Write out plt call stubs. + typename Plt_stub_entries::const_iterator cs; + for (cs = this->plt_call_stubs_.begin(); + cs != this->plt_call_stubs_.end(); + ++cs) { - write_insn<big_endian>(p, std_2_1 + 40), p += 4; - write_insn<big_endian>(p, ld_11_2 + l(pltoff)), p += 4; - if (ha(pltoff + 16) != ha(pltoff)) + Address plt_addr; + bool is_ifunc; + const Symbol* gsym = cs->first.sym_; + if (gsym != NULL) { - write_insn<big_endian>(p, addi_2_2 + l(pltoff)), p += 4; - pltoff = 0; + is_ifunc = (gsym->type() == elfcpp::STT_GNU_IFUNC + && gsym->can_use_relative_reloc(false)); + plt_addr = gsym->plt_offset(); } - write_insn<big_endian>(p, mtctr_11), p += 4; - write_insn<big_endian>(p, ld_11_2 + l(pltoff + 16)), p += 4; - write_insn<big_endian>(p, ld_2_2 + l(pltoff + 8)), p += 4; - write_insn<big_endian>(p, bctr), p += 4; + else + { + is_ifunc = true; + const Sized_relobj_file<size, big_endian>* relobj + = cs->first.object_; + unsigned int local_sym_index = cs->first.locsym_; + plt_addr = relobj->local_plt_offset(local_sym_index); + } + if (is_ifunc) + { + if (iplt_base == invalid_address) + iplt_base = this->targ_->iplt_section()->address(); + plt_addr += iplt_base; + } + else + plt_addr += plt_base; + + p = oview + cs->second; + if (parameters->options().output_is_position_independent()) + { + Address got_addr; + const Powerpc_relobj<size, big_endian>* ppcobj + = (static_cast<const Powerpc_relobj<size, big_endian>*> + (cs->first.object_)); + if (ppcobj != NULL && cs->first.addend_ >= 32768) + { + unsigned int got2 = ppcobj->got2_shndx(); + got_addr = ppcobj->get_output_section_offset(got2); + gold_assert(got_addr != invalid_address); + got_addr += (ppcobj->output_section(got2)->address() + + cs->first.addend_); + } + else + { + if (g_o_t == invalid_address) + { + const Output_data_got_powerpc<size, big_endian>* got + = this->targ_->got_section(); + g_o_t = got->address() + got->g_o_t(); + } + got_addr = g_o_t; + } + + Address pltoff = plt_addr - got_addr; + if (ha(pltoff) == 0) + { + write_insn<big_endian>(p + 0, lwz_11_30 + l(pltoff)); + write_insn<big_endian>(p + 4, mtctr_11); + write_insn<big_endian>(p + 8, bctr); + } + else + { + write_insn<big_endian>(p + 0, addis_11_30 + ha(pltoff)); + write_insn<big_endian>(p + 4, lwz_11_11 + l(pltoff)); + write_insn<big_endian>(p + 8, mtctr_11); + write_insn<big_endian>(p + 12, bctr); + } + } + else + { + write_insn<big_endian>(p + 0, lis_11 + ha(plt_addr)); + write_insn<big_endian>(p + 4, lwz_11_11 + l(plt_addr)); + write_insn<big_endian>(p + 8, mtctr_11); + write_insn<big_endian>(p + 12, bctr); + } + } + } + + // Write out long branch stubs. + typename Branch_stub_entries::const_iterator bs; + for (bs = this->long_branch_stubs_.begin(); + bs != this->long_branch_stubs_.end(); + ++bs) + { + p = oview + this->plt_size_ + bs->second; + Address loc = this->stub_address() + this->plt_size_ + bs->second; + Address delta = bs->first.dest_ - loc; + if (delta + (1 << 25) < 2 << 25) + write_insn<big_endian>(p, b | (delta & 0x3fffffc)); + else if (!parameters->options().output_is_position_independent()) + { + write_insn<big_endian>(p + 0, lis_12 + ha(bs->first.dest_)); + write_insn<big_endian>(p + 4, addi_12_12 + l(bs->first.dest_)); + write_insn<big_endian>(p + 8, mtctr_12); + write_insn<big_endian>(p + 12, bctr); + } + else + { + delta -= 8; + write_insn<big_endian>(p + 0, mflr_0); + write_insn<big_endian>(p + 4, bcl_20_31); + write_insn<big_endian>(p + 8, mflr_12); + write_insn<big_endian>(p + 12, addis_12_12 + ha(delta)); + write_insn<big_endian>(p + 16, addi_12_12 + l(delta)); + write_insn<big_endian>(p + 20, mtlr_0); + write_insn<big_endian>(p + 24, mtctr_12); + write_insn<big_endian>(p + 28, bctr); } } + } +} + +// Write out .glink. + +template<int size, bool big_endian> +void +Output_data_glink<size, big_endian>::do_write(Output_file* of) +{ + const section_size_type off = this->offset(); + const section_size_type oview_size = + convert_to_section_size_type(this->data_size()); + unsigned char* const oview = of->get_output_view(off, oview_size); + unsigned char* p; + + // The base address of the .plt section. + typedef typename elfcpp::Elf_types<size>::Elf_Addr Address; + Address plt_base = this->targ_->plt_section()->address(); + if (size == 64) + { // Write pltresolve stub. - p = oview + this->pltresolve_; - Address after_bcl = this->address() + this->pltresolve_ + 16; + p = oview; + Address after_bcl = this->address() + 16; Address pltoff = plt_base - after_bcl; elfcpp::Swap<64, big_endian>::writeval(p, pltoff), p += 8; @@ -2352,7 +3435,7 @@ Output_data_glink<size, big_endian>::do_write(Output_file* of) write_insn<big_endian>(p, mtctr_11), p += 4; write_insn<big_endian>(p, ld_11_12 + 16), p += 4; write_insn<big_endian>(p, bctr), p += 4; - while (p < oview + this->pltresolve_ + this->pltresolve_size) + while (p < oview + this->pltresolve_size) write_insn<big_endian>(p, nop), p += 4; // Write lazy link call stubs. @@ -2368,91 +3451,20 @@ Output_data_glink<size, big_endian>::do_write(Output_file* of) write_insn<big_endian>(p, lis_0_0 + hi(indx)), p += 4; write_insn<big_endian>(p, ori_0_0_0 + l(indx)), p += 4; } - uint32_t branch_off = this->pltresolve_ + 8 - (p - oview); + uint32_t branch_off = 8 - (p - oview); write_insn<big_endian>(p, b + (branch_off & 0x3fffffc)), p += 4; indx++; } } else { + const Output_data_got_powerpc<size, big_endian>* got + = this->targ_->got_section(); // The address of _GLOBAL_OFFSET_TABLE_. Address g_o_t = got->address() + got->g_o_t(); - // Write out call stubs. - typename Glink_entries::const_iterator g; - for (g = this->glink_entries_.begin(); - g != this->glink_entries_.end(); - ++g) - { - Address plt_addr; - bool is_ifunc; - const Symbol* gsym = g->first.sym_; - if (gsym != NULL) - { - is_ifunc = (gsym->type() == elfcpp::STT_GNU_IFUNC - && gsym->can_use_relative_reloc(false)); - plt_addr = gsym->plt_offset(); - } - else - { - is_ifunc = true; - const Sized_relobj_file<size, big_endian>* relobj - = g->first.object_; - unsigned int local_sym_index = g->first.locsym_; - plt_addr = relobj->local_plt_offset(local_sym_index); - } - if (is_ifunc) - { - if (iplt_base == invalid_address) - iplt_base = this->targ_->iplt_section()->address(); - plt_addr += iplt_base; - } - else - plt_addr += plt_base; - - p = oview + g->second * this->glink_entry_size(); - if (parameters->options().output_is_position_independent()) - { - Address got_addr; - const Powerpc_relobj<size, big_endian>* object = static_cast - <const Powerpc_relobj<size, big_endian>*>(g->first.object_); - if (object != NULL && g->first.addend_ >= 32768) - { - unsigned int got2 = object->got2_shndx(); - got_addr = g->first.object_->get_output_section_offset(got2); - gold_assert(got_addr != invalid_address); - got_addr += (g->first.object_->output_section(got2)->address() - + g->first.addend_); - } - else - got_addr = g_o_t; - - Address pltoff = plt_addr - got_addr; - if (ha(pltoff) == 0) - { - write_insn<big_endian>(p + 0, lwz_11_30 + l(pltoff)); - write_insn<big_endian>(p + 4, mtctr_11); - write_insn<big_endian>(p + 8, bctr); - } - else - { - write_insn<big_endian>(p + 0, addis_11_30 + ha(pltoff)); - write_insn<big_endian>(p + 4, lwz_11_11 + l(pltoff)); - write_insn<big_endian>(p + 8, mtctr_11); - write_insn<big_endian>(p + 12, bctr); - } - } - else - { - write_insn<big_endian>(p + 0, lis_11 + ha(plt_addr)); - write_insn<big_endian>(p + 4, lwz_11_11 + l(plt_addr)); - write_insn<big_endian>(p + 8, mtctr_11); - write_insn<big_endian>(p + 12, bctr); - } - } - // Write out pltresolve branch table. - p = oview + this->pltresolve_; + p = oview; unsigned int the_end = oview_size - this->pltresolve_size; unsigned char* end_p = oview + the_end; while (p < end_p - 8 * 4) @@ -2463,7 +3475,7 @@ Output_data_glink<size, big_endian>::do_write(Output_file* of) // Write out pltresolve call stub. if (parameters->options().output_is_position_independent()) { - Address res0_off = this->pltresolve_; + Address res0_off = 0; Address after_bcl_off = the_end + 12; Address bcl_res0 = after_bcl_off - res0_off; @@ -2497,7 +3509,7 @@ Output_data_glink<size, big_endian>::do_write(Output_file* of) } else { - Address res0 = this->pltresolve_ + this->address(); + Address res0 = this->address(); write_insn<big_endian>(p + 0, lis_12 + ha(g_o_t + 4)); write_insn<big_endian>(p + 4, addis_11_11 + ha(-res0)); @@ -2825,13 +3837,13 @@ Output_data_save_res<size, big_endian>::savres_define( if (this->contents_ == NULL) this->contents_ = new unsigned char[this->savres_max]; - off_t value = this->current_data_size(); + section_size_type value = this->current_data_size(); unsigned char* p = this->contents_ + value; if (i != hi) p = write_ent(p, i); else p = write_tail(p, i); - off_t cur_size = p - this->contents_; + section_size_type cur_size = p - this->contents_; this->set_current_data_size(cur_size); if (refd) symtab->define_in_output_data(sym, NULL, Symbol_table::PREDEFINED, @@ -2848,7 +3860,7 @@ template<int size, bool big_endian> void Output_data_save_res<size, big_endian>::do_write(Output_file* of) { - const off_t off = this->offset(); + const section_size_type off = this->offset(); const section_size_type oview_size = convert_to_section_size_type(this->data_size()); unsigned char* const oview = of->get_output_view(off, oview_size); @@ -2876,12 +3888,9 @@ Target_powerpc<size, big_endian>::make_glink_section(Layout* layout) template<int size, bool big_endian> void -Target_powerpc<size, big_endian>::make_plt_entry( - Symbol_table* symtab, - Layout* layout, - Symbol* gsym, - const elfcpp::Rela<size, big_endian>& reloc, - const Sized_relobj_file<size, big_endian>* object) +Target_powerpc<size, big_endian>::make_plt_entry(Symbol_table* symtab, + Layout* layout, + Symbol* gsym) { if (gsym->type() == elfcpp::STT_GNU_IFUNC && gsym->can_use_relative_reloc(false)) @@ -2896,7 +3905,6 @@ Target_powerpc<size, big_endian>::make_plt_entry( this->make_plt_section(symtab, layout); this->plt_->add_entry(gsym); } - this->glink_->add_entry(object, gsym, reloc); } // Make a PLT entry for a local STT_GNU_IFUNC symbol. @@ -2906,14 +3914,12 @@ void Target_powerpc<size, big_endian>::make_local_ifunc_plt_entry( Symbol_table* symtab, Layout* layout, - const elfcpp::Rela<size, big_endian>& reloc, - Sized_relobj_file<size, big_endian>* relobj) + Sized_relobj_file<size, big_endian>* relobj, + unsigned int r_sym) { if (this->iplt_ == NULL) this->make_iplt_section(symtab, layout); - unsigned int r_sym = elfcpp::elf_r_sym<size>(reloc.get_r_info()); this->iplt_->add_local_ifunc_entry(relobj, r_sym); - this->glink_->add_entry(relobj, r_sym, reloc); } // Return the number of entries in the PLT. @@ -3264,7 +4270,10 @@ Target_powerpc<size, big_endian>::Scan::local( bool is_ifunc = lsym.get_st_type() == elfcpp::STT_GNU_IFUNC; if (is_ifunc && this->reloc_needs_plt_for_ifunc(object, r_type)) { - target->make_local_ifunc_plt_entry(symtab, layout, reloc, object); + unsigned int r_sym = elfcpp::elf_r_sym<size>(reloc.get_r_info()); + target->push_branch(ppc_object, data_shndx, reloc.get_r_offset(), + r_type, r_sym, reloc.get_r_addend()); + target->make_local_ifunc_plt_entry(symtab, layout, object, r_sym); } switch (r_type) @@ -3353,18 +4362,28 @@ Target_powerpc<size, big_endian>::Scan::local( } break; - case elfcpp::R_PPC64_REL64: - case elfcpp::R_POWERPC_REL32: case elfcpp::R_POWERPC_REL24: case elfcpp::R_PPC_PLTREL24: case elfcpp::R_PPC_LOCAL24PC: + target->push_branch(ppc_object, data_shndx, reloc.get_r_offset(), + r_type, elfcpp::elf_r_sym<size>(reloc.get_r_info()), + reloc.get_r_addend()); + break; + + case elfcpp::R_POWERPC_REL14: + case elfcpp::R_POWERPC_REL14_BRTAKEN: + case elfcpp::R_POWERPC_REL14_BRNTAKEN: + target->push_branch(ppc_object, data_shndx, reloc.get_r_offset(), + r_type, elfcpp::elf_r_sym<size>(reloc.get_r_info()), + reloc.get_r_addend()); + break; + + case elfcpp::R_PPC64_REL64: + case elfcpp::R_POWERPC_REL32: case elfcpp::R_POWERPC_REL16: case elfcpp::R_POWERPC_REL16_LO: case elfcpp::R_POWERPC_REL16_HI: case elfcpp::R_POWERPC_REL16_HA: - case elfcpp::R_POWERPC_REL14: - case elfcpp::R_POWERPC_REL14_BRTAKEN: - case elfcpp::R_POWERPC_REL14_BRNTAKEN: case elfcpp::R_POWERPC_SECTOFF: case elfcpp::R_POWERPC_TPREL16: case elfcpp::R_POWERPC_DTPREL16: @@ -3575,7 +4594,12 @@ Target_powerpc<size, big_endian>::Scan::global( // A STT_GNU_IFUNC symbol may require a PLT entry. if (gsym->type() == elfcpp::STT_GNU_IFUNC && this->reloc_needs_plt_for_ifunc(object, r_type)) - target->make_plt_entry(symtab, layout, gsym, reloc, object); + { + target->push_branch(ppc_object, data_shndx, reloc.get_r_offset(), + r_type, elfcpp::elf_r_sym<size>(reloc.get_r_info()), + reloc.get_r_addend()); + target->make_plt_entry(symtab, layout, gsym); + } switch (r_type) { @@ -3645,7 +4669,11 @@ Target_powerpc<size, big_endian>::Scan::global( // Make a PLT entry if necessary. if (gsym->needs_plt_entry()) { - target->make_plt_entry(symtab, layout, gsym, reloc, 0); + target->push_branch(ppc_object, data_shndx, reloc.get_r_offset(), + r_type, + elfcpp::elf_r_sym<size>(reloc.get_r_info()), + reloc.get_r_addend()); + target->make_plt_entry(symtab, layout, gsym); // Since this is not a PC-relative relocation, we may be // taking the address of a function. In that case we need to // set the entry in the dynamic symbol table to the address of @@ -3700,12 +4728,15 @@ Target_powerpc<size, big_endian>::Scan::global( case elfcpp::R_PPC_PLTREL24: case elfcpp::R_POWERPC_REL24: + target->push_branch(ppc_object, data_shndx, reloc.get_r_offset(), + r_type, elfcpp::elf_r_sym<size>(reloc.get_r_info()), + reloc.get_r_addend()); if (gsym->needs_plt_entry() || (!gsym->final_value_is_known() && (gsym->is_undefined() || gsym->is_from_dynobj() || gsym->is_preemptible()))) - target->make_plt_entry(symtab, layout, gsym, reloc, object); + target->make_plt_entry(symtab, layout, gsym); // Fall thru case elfcpp::R_PPC64_REL64: @@ -3730,13 +4761,18 @@ Target_powerpc<size, big_endian>::Scan::global( } break; + case elfcpp::R_POWERPC_REL14: + case elfcpp::R_POWERPC_REL14_BRTAKEN: + case elfcpp::R_POWERPC_REL14_BRNTAKEN: + target->push_branch(ppc_object, data_shndx, reloc.get_r_offset(), + r_type, elfcpp::elf_r_sym<size>(reloc.get_r_info()), + reloc.get_r_addend()); + break; + case elfcpp::R_POWERPC_REL16: case elfcpp::R_POWERPC_REL16_LO: case elfcpp::R_POWERPC_REL16_HI: case elfcpp::R_POWERPC_REL16_HA: - case elfcpp::R_POWERPC_REL14: - case elfcpp::R_POWERPC_REL14_BRTAKEN: - case elfcpp::R_POWERPC_REL14_BRNTAKEN: case elfcpp::R_POWERPC_SECTOFF: case elfcpp::R_POWERPC_TPREL16: case elfcpp::R_POWERPC_DTPREL16: @@ -4221,7 +5257,17 @@ Target_powerpc<size, big_endian>::do_finalize_sections( { typedef Global_symbol_visitor_opd<big_endian> Symbol_visitor; symtab->for_all_symbols<64, Symbol_visitor>(Symbol_visitor()); - this->define_save_restore_funcs(layout, symtab); + + if (!parameters->options().relocatable()) + { + this->define_save_restore_funcs(layout, symtab); + + // Annoyingly, we need to make these sections now whether or + // not we need them. If we delay until do_relax then we + // need to mess with the relaxation machinery checkpointing. + this->got_section(symtab, layout); + this->make_brlt_section(layout); + } } // Fill in some more dynamic tags. @@ -4250,8 +5296,7 @@ Target_powerpc<size, big_endian>::do_finalize_sections( this->glink_->finalize_data_size(); odyn->add_section_plus_offset(elfcpp::DT_PPC64_GLINK, this->glink_, - (this->glink_->pltresolve() - + this->glink_->pltresolve_size + (this->glink_->pltresolve_size - 32)); } } @@ -4320,7 +5365,6 @@ Target_powerpc<size, big_endian>::Relocate::relocate( Address address, section_size_type view_size) { - bool is_tls_call = ((r_type == elfcpp::R_POWERPC_REL24 || r_type == elfcpp::R_PPC_PLTREL24) && gsym != NULL @@ -4350,14 +5394,24 @@ Target_powerpc<size, big_endian>::Relocate::relocate( ? use_plt_offset<size>(gsym, Scan::get_reference_flags(r_type)) : object->local_has_plt_offset(r_sym)) { - const Output_data_glink<size, big_endian>* glink - = target->glink_section(); - unsigned int glink_index; + Stub_table<size, big_endian>* stub_table + = object->stub_table(relinfo->data_shndx); + if (stub_table == NULL) + { + // This is a ref from a data section to an ifunc symbol. + if (target->stub_tables().size() != 0) + stub_table = target->stub_tables()[0]; + } + gold_assert(stub_table != NULL); + Address off; if (gsym != NULL) - glink_index = glink->find_entry(object, gsym, rela); + off = stub_table->find_plt_call_entry(object, gsym, r_type, + rela.get_r_addend()); else - glink_index = glink->find_entry(object, r_sym, rela); - value = glink->address() + glink_index * glink->glink_entry_size(); + off = stub_table->find_plt_call_entry(object, r_sym, r_type, + rela.get_r_addend()); + gold_assert(off != invalid_address); + value = stub_table->stub_address() + off; has_plt_value = true; } @@ -4695,6 +5749,25 @@ Target_powerpc<size, big_endian>::Relocate::relocate( value = psymval->value(object, addend); if (size == 64 && is_branch_reloc(r_type)) value = target->symval_for_branch(value, gsym, object, &dest_shndx); + unsigned int max_branch_offset = 0; + if (r_type == elfcpp::R_POWERPC_REL24 + || r_type == elfcpp::R_PPC_PLTREL24 + || r_type == elfcpp::R_PPC_LOCAL24PC) + max_branch_offset = 1 << 25; + else if (r_type == elfcpp::R_POWERPC_REL14 + || r_type == elfcpp::R_POWERPC_REL14_BRTAKEN + || r_type == elfcpp::R_POWERPC_REL14_BRNTAKEN) + max_branch_offset = 1 << 15; + if (max_branch_offset != 0 + && value - address + max_branch_offset >= 2 * max_branch_offset) + { + Stub_table<size, big_endian>* stub_table + = object->stub_table(relinfo->data_shndx); + gold_assert(stub_table != NULL); + Address off = stub_table->find_long_branch_entry(object, value); + if (off != invalid_address) + value = stub_table->stub_address() + stub_table->plt_size() + off; + } } switch (r_type) @@ -5503,7 +6576,7 @@ Target_powerpc<size, big_endian>::relocate_relocs( == reloc_view_size); } -// Return the value to use for a dynamic which requires special +// Return the value to use for a dynamic symbol which requires special // treatment. This is how we support equality comparisons of function // pointers across shared library boundaries, as described in the // processor specific ABI supplement. @@ -5515,12 +6588,16 @@ Target_powerpc<size, big_endian>::do_dynsym_value(const Symbol* gsym) const if (size == 32) { gold_assert(gsym->is_from_dynobj() && gsym->has_plt_offset()); - const Output_data_glink<size, big_endian>* glink = this->glink_section(); - unsigned int glink_index = glink->find_entry(gsym); - return glink->address() + glink_index * glink->glink_entry_size(); + for (typename Stub_tables::const_iterator p = this->stub_tables_.begin(); + p != this->stub_tables_.end(); + ++p) + { + Address off = (*p)->find_plt_call_entry(gsym); + if (off != invalid_address) + return (*p)->stub_address() + off; + } } - else - gold_unreachable(); + gold_unreachable(); } // Return the PLT address to use for a local symbol. @@ -5534,13 +6611,17 @@ Target_powerpc<size, big_endian>::do_plt_address_for_local( { const Sized_relobj<size, big_endian>* relobj = static_cast<const Sized_relobj<size, big_endian>*>(object); - const Output_data_glink<size, big_endian>* glink = this->glink_section(); - unsigned int glink_index = glink->find_entry(relobj->sized_relobj(), - symndx); - return glink->address() + glink_index * glink->glink_entry_size(); + for (typename Stub_tables::const_iterator p = this->stub_tables_.begin(); + p != this->stub_tables_.end(); + ++p) + { + Address off = (*p)->find_plt_call_entry(relobj->sized_relobj(), + symndx); + if (off != invalid_address) + return (*p)->stub_address() + off; + } } - else - gold_unreachable(); + gold_unreachable(); } // Return the PLT address to use for a global symbol. @@ -5551,12 +6632,16 @@ Target_powerpc<size, big_endian>::do_plt_address_for_global( { if (size == 32) { - const Output_data_glink<size, big_endian>* glink = this->glink_section(); - unsigned int glink_index = glink->find_entry(gsym); - return glink->address() + glink_index * glink->glink_entry_size(); + for (typename Stub_tables::const_iterator p = this->stub_tables_.begin(); + p != this->stub_tables_.end(); + ++p) + { + Address off = (*p)->find_plt_call_entry(gsym); + if (off != invalid_address) + return (*p)->stub_address() + off; + } } - else - gold_unreachable(); + gold_unreachable(); } // Return the offset to use for the GOT_INDX'th got entry which is |