From: Alan Modra Date: Thu, 23 Jul 2020 01:05:56 +0000 (+0930) Subject: [GOLD] Power10 stub selection X-Git-Url: https://git.libre-soc.org/?a=commitdiff_plain;h=afd2ea23626c43886ab8b028b68b7b663d6de3c6;p=binutils-gdb.git [GOLD] Power10 stub selection gold version of commit e10a07b32dc1. * options.h (DEFINE_enum): Add optional_arg__ param, adjust all uses. (General_options): Add --power10-stubs and --no-power10-stubs. * options.cc (General_options::finalize): Handle --power10-stubs. * powerpc.cc (set_power10_stubs): Don't set when --power10-stubs=no. (power10_stubs_auto): New. (struct Plt_stub_ent): Add toc_ and tocoff_. Don't use a bitfield for indx_. (struct Branch_stub_ent): Add toc_and tocoff_. Use bitfields for iter_, notoc_ and save_res_. (add_plt_call_entry): Set toc_. Adjust resizing conditions for --power10-stubs=auto. (add_long_branch_entry): Set toc_. (add_eh_frame, define_stub_syms): No longer use const_iterators for plt and long branch stub iteration. (build_tls_opt_head, build_tls_opt_tail): Change parameters and return value. Move tests for __tls_get_addr to callers. (plt_call_size): Handle --power10-stubs=auto. (branch_stub_size): Likewise. (Stub_table::do_write): Likewise. (relocate): Likewise. --- diff --git a/gold/ChangeLog b/gold/ChangeLog index f83b602a2b6..2938cdedff7 100644 --- a/gold/ChangeLog +++ b/gold/ChangeLog @@ -1,3 +1,28 @@ +2020-07-27 Alan Modra + + * options.h (DEFINE_enum): Add optional_arg__ param, adjust + all uses. + (General_options): Add --power10-stubs and --no-power10-stubs. + * options.cc (General_options::parse_no_power10_stubs): New. + (General_options::finalize): Handle --power10-stubs. + * powerpc.cc (set_power10_stubs): Don't set when --power10-stubs=no. + (power10_stubs_auto): New. + (struct Plt_stub_ent): Add toc_ and tocoff_. Don't use a bitfield + for indx_. + (struct Branch_stub_ent): Add toc_and tocoff_. Use bitfields for + iter_, notoc_ and save_res_. + (add_plt_call_entry): Set toc_. Adjust resizing conditions for + --power10-stubs=auto. + (add_long_branch_entry): Set toc_. + (add_eh_frame, define_stub_syms): No longer use const_iterators + for plt and long branch stub iteration. + (build_tls_opt_head, build_tls_opt_tail): Change parameters and + return value. Move tests for __tls_get_addr to callers. + (plt_call_size): Handle --power10-stubs=auto. + (branch_stub_size): Likewise. + (Stub_table::do_write): Likewise. + (relocate): Likewise. + 2020-07-19 H.J. Lu * testsuite/bnd_ifunc_1.sh: Updated. diff --git a/gold/options.cc b/gold/options.cc index b13ae71ce14..6b194374c8c 100644 --- a/gold/options.cc +++ b/gold/options.cc @@ -464,6 +464,14 @@ General_options::parse_plugin_opt(const char*, const char* arg, this->add_plugin_option(arg); } +void +General_options::parse_no_power10_stubs(const char*, const char*, + Command_line*) +{ + this->set_power10_stubs("no"); + this->set_user_set_power10_stubs(); +} + void General_options::parse_R(const char* option, const char* arg, Command_line* cmdline) @@ -1183,6 +1191,27 @@ General_options::finalize() this->set_start_stop_visibility_enum(elfcpp::STV_PROTECTED); } + // Parse the --power10-stubs argument. + if (!this->user_set_power10_stubs()) + { + // --power10-stubs without an arg is equivalent to --power10-stubs=yes + // but not specifying --power10-stubs at all should be equivalent to + // --power10-stubs=auto. This doesn't fit into the notion of + // "default_value", used both as a static initializer and to provide + // a missing optional arg. Fix it here. + this->set_power10_stubs("auto"); + this->set_power10_stubs_enum(POWER10_STUBS_AUTO); + } + else + { + if (strcmp(this->power10_stubs(), "auto") == 0) + this->set_power10_stubs_enum(POWER10_STUBS_AUTO); + else if (strcmp(this->power10_stubs(), "no") == 0) + this->set_power10_stubs_enum(POWER10_STUBS_NO); + else if (strcmp(this->power10_stubs(), "yes") == 0) + this->set_power10_stubs_enum(POWER10_STUBS_YES); + } + // -M is equivalent to "-Map -". if (this->print_map() && !this->user_set_Map()) { diff --git a/gold/options.h b/gold/options.h index 3c8d25a6628..bc211151dee 100644 --- a/gold/options.h +++ b/gold/options.h @@ -481,9 +481,9 @@ struct Struct_special : public Struct_var // After helparg__ should come an initializer list, like // {"foo", "bar", "baz"} #define DEFINE_enum(varname__, dashes__, shortname__, default_value__, \ - helpstring__, helparg__, ...) \ + helpstring__, helparg__, optional_arg__, ...) \ DEFINE_var(varname__, dashes__, shortname__, default_value__, \ - default_value__, helpstring__, helparg__, false, \ + default_value__, helpstring__, helparg__, optional_arg__, \ const char*, const char*, parse_choices_##varname__, false) \ private: \ static void parse_choices_##varname__(const char* option_name, \ @@ -703,7 +703,7 @@ class General_options N_("Use DT_NEEDED for all shared libraries")); DEFINE_enum(assert, options::ONE_DASH, '\0', NULL, - N_("Ignored"), N_("[ignored]"), + N_("Ignored"), N_("[ignored]"), false, {"definitions", "nodefinitions", "nosymbolic", "pure-text"}); // b @@ -761,7 +761,7 @@ class General_options DEFINE_enum(compress_debug_sections, options::TWO_DASHES, '\0', "none", N_("Compress .debug_* sections in the output file"), - ("[none,zlib,zlib-gnu,zlib-gabi]"), + ("[none,zlib,zlib-gnu,zlib-gabi]"), false, {"none", "zlib", "zlib-gnu", "zlib-gabi"}); DEFINE_bool(copy_dt_needed_entries, options::TWO_DASHES, '\0', false, @@ -934,7 +934,7 @@ class General_options N_("FRACTION")); DEFINE_enum(hash_style, options::TWO_DASHES, '\0', DEFAULT_HASH_STYLE, - N_("Dynamic hash style"), N_("[sysv,gnu,both]"), + N_("Dynamic hash style"), N_("[sysv,gnu,both]"), false, {"sysv", "gnu", "both"}); // i @@ -946,7 +946,7 @@ class General_options N_("Identical Code Folding. " "\'--icf=safe\' Folds ctors, dtors and functions whose" " pointers are definitely not taken"), - ("[none,all,safe]"), + ("[none,all,safe]"), false, {"none", "all", "safe"}); DEFINE_uint(icf_iterations, options::TWO_DASHES , '\0', 0, @@ -1086,7 +1086,7 @@ class General_options DEFINE_enum(orphan_handling, options::TWO_DASHES, '\0', "place", N_("Orphan section handling"), N_("[place,discard,warn,error]"), - {"place", "discard", "warn", "error"}); + false, {"place", "discard", "warn", "error"}); // p @@ -1141,6 +1141,12 @@ class General_options N_("Use posix_fallocate to reserve space in the output file"), N_("Use fallocate or ftruncate to reserve space")); + DEFINE_enum(power10_stubs, options::TWO_DASHES, '\0', "yes", + N_("(PowerPC64 only) stubs use power10 insns"), + N_("[=auto,no,yes]"), true, {"auto", "no", "yes"}); + DEFINE_special(no_power10_stubs, options::TWO_DASHES, '\0', + N_("(PowerPC64 only) stubs do not use power10 insns"), NULL); + DEFINE_bool(preread_archive_symbols, options::TWO_DASHES, '\0', false, N_("Preread archive symbols when multi-threaded"), NULL); @@ -1236,7 +1242,7 @@ class General_options DEFINE_enum(sort_section, options::TWO_DASHES, '\0', "none", N_("Sort sections by name. \'--no-text-reorder\'" " will override \'--sort-section=name\' for .text"), - N_("[none,name]"), + N_("[none,name]"), false, {"none", "name"}); DEFINE_uint(spare_dynamic_tags, options::TWO_DASHES, '\0', 5, @@ -1287,7 +1293,7 @@ class General_options NULL); DEFINE_enum(target2, options::TWO_DASHES, '\0', NULL, N_("(ARM only) Set R_ARM_TARGET2 relocation type"), - N_("[rel, abs, got-rel"), + N_("[rel, abs, got-rel"), false, {"rel", "abs", "got-rel"}); DEFINE_bool(text_reorder, options::TWO_DASHES, '\0', true, @@ -1344,7 +1350,7 @@ class General_options DEFINE_enum(unresolved_symbols, options::TWO_DASHES, '\0', NULL, N_("How to handle unresolved symbols"), ("ignore-all,report-all,ignore-in-object-files," - "ignore-in-shared-libs"), + "ignore-in-shared-libs"), false, {"ignore-all", "report-all", "ignore-in-object-files", "ignore-in-shared-libs"}); @@ -1507,7 +1513,7 @@ class General_options DEFINE_enum(start_stop_visibility, options::DASH_Z, '\0', "protected", N_("ELF symbol visibility for synthesized " "__start_* and __stop_* symbols"), - ("[default,internal,hidden,protected]"), + ("[default,internal,hidden,protected]"), false, {"default", "internal", "hidden", "protected"}); DEFINE_bool(text, options::DASH_Z, '\0', false, N_("Do not permit relocations in read-only segments"), @@ -1763,6 +1769,20 @@ class General_options start_stop_visibility_enum() const { return this->start_stop_visibility_enum_; } + enum Power10_stubs + { + // Use Power10 insns on @notoc calls/branches, non-Power10 elsewhere. + POWER10_STUBS_AUTO, + // Don't use Power10 insns + POWER10_STUBS_NO, + // Always use Power10 insns + POWER10_STUBS_YES + }; + + Power10_stubs + power10_stubs_enum() const + { return this->power10_stubs_enum_; } + private: // Don't copy this structure. General_options(const General_options&); @@ -1826,6 +1846,10 @@ class General_options set_start_stop_visibility_enum(elfcpp::STV value) { this->start_stop_visibility_enum_ = value; } + void + set_power10_stubs_enum(Power10_stubs value) + { this->power10_stubs_enum_ = value; } + // These are called by finalize() to set up the search-path correctly. void add_to_library_path_with_sysroot(const std::string& arg) @@ -1895,6 +1919,8 @@ class General_options Orphan_handling orphan_handling_enum_; // Symbol visibility for __start_* / __stop_* magic symbols. elfcpp::STV start_stop_visibility_enum_; + // Power10 stubs option + Power10_stubs power10_stubs_enum_; }; // The position-dependent options. We use this to store the state of diff --git a/gold/powerpc.cc b/gold/powerpc.cc index 80f222db19b..d5736665499 100644 --- a/gold/powerpc.cc +++ b/gold/powerpc.cc @@ -1085,7 +1085,16 @@ class Target_powerpc : public Sized_target void set_power10_stubs() { - this->power10_stubs_ = true; + if (parameters->options().power10_stubs_enum() + != General_options::POWER10_STUBS_NO) + this->power10_stubs_ = true; + } + + bool + power10_stubs_auto() const + { + return (parameters->options().power10_stubs_enum() + == General_options::POWER10_STUBS_AUTO); } bool @@ -4621,26 +4630,32 @@ class Stub_table : public Output_relaxed_input_section struct Plt_stub_ent { Plt_stub_ent(unsigned int off, unsigned int indx) - : off_(off), indx_(indx), iter_(0), notoc_(0), r2save_(0), localentry0_(0) + : off_(off), indx_(indx), iter_(0), notoc_(0), toc_(0), + r2save_(0), localentry0_(0), tocoff_(0) { } unsigned int off_; - unsigned int indx_ : 28; + unsigned int indx_; unsigned int iter_ : 1; unsigned int notoc_ : 1; + unsigned int toc_ : 1; unsigned int r2save_ : 1; unsigned int localentry0_ : 1; + unsigned int tocoff_ : 8; }; struct Branch_stub_ent { Branch_stub_ent(unsigned int off, bool notoc, bool save_res) - : off_(off), iter_(false), notoc_(notoc), save_res_(save_res) + : off_(off), iter_(0), notoc_(notoc), toc_(0), save_res_(save_res), + tocoff_(0) { } unsigned int off_; - bool iter_; - bool notoc_; - bool save_res_; + unsigned int iter_ : 1; + unsigned int notoc_ : 1; + unsigned int toc_ : 1; + unsigned int save_res_ : 1; + unsigned int tocoff_ : 8; }; typedef typename elfcpp::Elf_types::Elf_Addr Address; static const Address invalid_address = static_cast
(0) - 1; @@ -4888,7 +4903,7 @@ class Stub_table : public Output_relaxed_input_section // Size of a given plt call stub. unsigned int - plt_call_size(typename Plt_stub_entries::const_iterator p) const; + plt_call_size(typename Plt_stub_entries::iterator p) const; unsigned int plt_call_align(unsigned int bytes) const @@ -4899,16 +4914,14 @@ class Stub_table : public Output_relaxed_input_section // Return long branch stub size. unsigned int - branch_stub_size(typename Branch_stub_entries::const_iterator p, + branch_stub_size(typename Branch_stub_entries::iterator p, bool* need_lt); - bool - build_tls_opt_head(unsigned char** pp, - typename Plt_stub_entries::const_iterator cs); + void + build_tls_opt_head(unsigned char** pp, bool save_lr); - bool - build_tls_opt_tail(unsigned char* p, - typename Plt_stub_entries::const_iterator cs); + void + build_tls_opt_tail(unsigned char* p); void plt_error(const Plt_stub_key& p); @@ -5073,15 +5086,22 @@ Stub_table::add_plt_call_entry( if (r_type == elfcpp::R_PPC64_REL24_NOTOC) { if (!p.second && !p.first->second.notoc_ - && !this->targ_->power10_stubs()) + && (!this->targ_->power10_stubs() + || this->targ_->power10_stubs_auto())) this->need_resize_ = true; p.first->second.notoc_ = 1; } - else if (!tocsave && !p.first->second.localentry0_) + else { - if (!p.second && !p.first->second.r2save_) + if (!p.second && !p.first->second.toc_) this->need_resize_ = true; - p.first->second.r2save_ = 1; + p.first->second.toc_ = 1; + if (!tocsave && !p.first->second.localentry0_) + { + if (!p.second && !p.first->second.r2save_) + this->need_resize_ = true; + p.first->second.r2save_ = 1; + } } } if (p.second || (this->resizing_ && !p.first->second.iter_)) @@ -5124,15 +5144,22 @@ Stub_table::add_plt_call_entry( if (r_type == elfcpp::R_PPC64_REL24_NOTOC) { if (!p.second && !p.first->second.notoc_ - && !this->targ_->power10_stubs()) + && (!this->targ_->power10_stubs() + || this->targ_->power10_stubs_auto())) this->need_resize_ = true; p.first->second.notoc_ = 1; } - else if (!tocsave && !p.first->second.localentry0_) + else { - if (!p.second && !p.first->second.r2save_) + if (!p.second && !p.first->second.toc_) this->need_resize_ = true; - p.first->second.r2save_ = 1; + p.first->second.toc_ = 1; + if (!tocsave && !p.first->second.localentry0_) + { + if (!p.second && !p.first->second.r2save_) + this->need_resize_ = true; + p.first->second.r2save_ = 1; + } } } if (p.second || (this->resizing_ && !p.first->second.iter_)) @@ -5221,11 +5248,18 @@ Stub_table::add_long_branch_entry( Branch_stub_ent ent(this->branch_size_, notoc, save_res); std::pair p = this->long_branch_stubs_.insert(std::make_pair(key, ent)); - if (notoc && !p.first->second.notoc_) + if (notoc) { - this->need_resize_ = true; + if (!p.second && !p.first->second.notoc_) + this->need_resize_ = true; p.first->second.notoc_ = true; } + else + { + if (!p.second && !p.first->second.toc_) + this->need_resize_ = true; + p.first->second.toc_ = true; + } gold_assert(save_res == p.first->second.save_res_); if (p.second || (this->resizing_ && !p.first->second.iter_)) { @@ -5320,7 +5354,7 @@ Stub_table::add_eh_frame(Layout* layout) if (!this->targ_->has_glink()) return; - typedef typename Plt_stub_entries::const_iterator plt_iter; + typedef typename Plt_stub_entries::iterator plt_iter; std::vector calls; if (!this->plt_call_stubs_.empty()) for (plt_iter cs = this->plt_call_stubs_.begin(); @@ -5622,7 +5656,7 @@ Stub_table::define_stub_syms(Symbol_table* symtab) // output .symtab ordering depends on the order in which symbols // are added to the linker symtab. We want reproducible output // so must sort the call stub symbols. - typedef typename Plt_stub_entries::const_iterator plt_iter; + typedef typename Plt_stub_entries::iterator plt_iter; std::vector sorted; sorted.resize(this->plt_call_stubs_.size()); @@ -5666,7 +5700,7 @@ Stub_table::define_stub_syms(Symbol_table* symtab) } } - typedef typename Branch_stub_entries::const_iterator branch_iter; + typedef typename Branch_stub_entries::iterator branch_iter; for (branch_iter bs = this->long_branch_stubs_.begin(); bs != this->long_branch_stubs_.end(); ++bs) @@ -5688,88 +5722,72 @@ Stub_table::define_stub_syms(Symbol_table* symtab) // Emit the start of a __tls_get_addr_opt plt call stub. template -bool -Stub_table::build_tls_opt_head( - unsigned char** pp, - typename Plt_stub_entries::const_iterator cs) +void +Stub_table::build_tls_opt_head(unsigned char** pp, + bool save_lr) { - if (this->targ_->is_tls_get_addr_opt(cs->first.sym_)) + unsigned char* p = *pp; + if (size == 64) { - unsigned char* p = *pp; - if (size == 64) - { - write_insn(p, ld_11_3 + 0); - p += 4; - write_insn(p, ld_12_3 + 8); - p += 4; - write_insn(p, mr_0_3); - p += 4; - write_insn(p, cmpdi_11_0); - p += 4; - write_insn(p, add_3_12_13); - p += 4; - write_insn(p, beqlr); - p += 4; - write_insn(p, mr_3_0); - p += 4; - if (cs->second.r2save_ && !cs->second.localentry0_) - { - write_insn(p, mflr_11); - p += 4; - write_insn(p, (std_11_1 + this->targ_->stk_linker())); - p += 4; - } - } - else + write_insn(p, ld_11_3 + 0); + p += 4; + write_insn(p, ld_12_3 + 8); + p += 4; + write_insn(p, mr_0_3); + p += 4; + write_insn(p, cmpdi_11_0); + p += 4; + write_insn(p, add_3_12_13); + p += 4; + write_insn(p, beqlr); + p += 4; + write_insn(p, mr_3_0); + p += 4; + if (save_lr) { - write_insn(p, lwz_11_3 + 0); - p += 4; - write_insn(p, lwz_12_3 + 4); + write_insn(p, mflr_11); p += 4; - write_insn(p, mr_0_3); - p += 4; - write_insn(p, cmpwi_11_0); - p += 4; - write_insn(p, add_3_12_2); - p += 4; - write_insn(p, beqlr); - p += 4; - write_insn(p, mr_3_0); - p += 4; - write_insn(p, nop); + write_insn(p, (std_11_1 + this->targ_->stk_linker())); p += 4; } - *pp = p; - return true; } - return false; -} - -// Emit the tail of a __tls_get_addr_opt plt call stub. - -template -bool -Stub_table::build_tls_opt_tail( - unsigned char* p, - typename Plt_stub_entries::const_iterator cs) -{ - if (size == 64 - && cs->second.r2save_ - && !cs->second.localentry0_ - && this->targ_->is_tls_get_addr_opt(cs->first.sym_)) + else { - write_insn(p, bctrl); + write_insn(p, lwz_11_3 + 0); p += 4; - write_insn(p, ld_2_1 + this->targ_->stk_toc()); + write_insn(p, lwz_12_3 + 4); p += 4; - write_insn(p, ld_11_1 + this->targ_->stk_linker()); + write_insn(p, mr_0_3); p += 4; - write_insn(p, mtlr_11); + write_insn(p, cmpwi_11_0); + p += 4; + write_insn(p, add_3_12_2); + p += 4; + write_insn(p, beqlr); + p += 4; + write_insn(p, mr_3_0); + p += 4; + write_insn(p, nop); p += 4; - write_insn(p, blr); - return true; } - return false; + *pp = p; +} + +// Emit the tail of a __tls_get_addr_opt plt call stub. + +template +void +Stub_table::build_tls_opt_tail(unsigned char* p) +{ + write_insn(p, bctrl); + p += 4; + write_insn(p, ld_2_1 + this->targ_->stk_toc()); + p += 4; + write_insn(p, ld_11_1 + this->targ_->stk_linker()); + p += 4; + write_insn(p, mtlr_11); + p += 4; + write_insn(p, blr); } // Emit pc-relative plt call stub code. @@ -5939,7 +5957,7 @@ build_notoc_offset(unsigned char* p, uint64_t off, bool load) template unsigned int Stub_table::plt_call_size( - typename Plt_stub_entries::const_iterator p) const + typename Plt_stub_entries::iterator p) const { if (size == 32) { @@ -5951,77 +5969,122 @@ Stub_table::plt_call_size( const Output_data_plt_powerpc* plt; uint64_t plt_addr = this->plt_off(p, &plt); plt_addr += plt->address(); - unsigned int bytes = 0; - const Symbol* gsym = p->first.sym_; - if (this->targ_->is_tls_get_addr_opt(gsym)) + if (this->targ_->power10_stubs() + && this->targ_->power10_stubs_auto()) { - if (p->second.r2save_ && !p->second.localentry0_) - bytes = 13 * 4; - else - bytes = 7 * 4; + unsigned int bytes = 0; + if (p->second.notoc_) + { + if (this->targ_->is_tls_get_addr_opt(p->first.sym_)) + bytes = 7 * 4; + uint64_t from = this->stub_address() + p->second.off_ + bytes; + uint64_t odd = from & 4; + uint64_t off = plt_addr - from; + if (off - odd + (1ULL << 33) < 1ULL << 34) + bytes += odd + 4 * 4; + else if (off - (8 - odd) + (0x20002ULL << 32) < 0x40004ULL << 32) + bytes += 7 * 4; + else + bytes += 8 * 4; + bytes = this->plt_call_align(bytes); + } + unsigned int tail = 0; + if (p->second.toc_) + { + p->second.tocoff_ = bytes; + if (this->targ_->is_tls_get_addr_opt(p->first.sym_)) + { + bytes += 7 * 4; + if (p->second.r2save_ && !p->second.localentry0_) + { + bytes += 2 * 4; + tail = 4 * 4; + } + } + if (p->second.r2save_) + bytes += 4; + uint64_t got_addr + = this->targ_->got_section()->output_section()->address(); + const Powerpc_relobj* ppcobj = static_cast + *>(p->first.object_); + got_addr += ppcobj->toc_base_offset(); + uint64_t off = plt_addr - got_addr; + bytes += 3 * 4 + 4 * (ha(off) != 0); + } + return bytes + tail; } + else + { + unsigned int bytes = 0; + unsigned int tail = 0; + if (this->targ_->is_tls_get_addr_opt(p->first.sym_)) + { + bytes = 7 * 4; + if (p->second.r2save_ && !p->second.localentry0_) + { + bytes = 9 * 4; + tail = 4 * 4; + } + } - if (p->second.r2save_) - bytes += 4; + if (p->second.r2save_) + bytes += 4; - if (this->targ_->power10_stubs()) - { - uint64_t from = this->stub_address() + p->second.off_ + bytes; - if (bytes > 8 * 4) - from -= 4 * 4; - uint64_t odd = from & 4; - uint64_t off = plt_addr - from; - if (off - odd + (1ULL << 33) < 1ULL << 34) - bytes += odd + 4 * 4; - else if (off - (8 - odd) + (0x20002ULL << 32) < 0x40004ULL << 32) - bytes += 7 * 4; - else - bytes += 8 * 4; - return bytes; - } + if (this->targ_->power10_stubs()) + { + uint64_t from = this->stub_address() + p->second.off_ + bytes; + uint64_t odd = from & 4; + uint64_t off = plt_addr - from; + if (off - odd + (1ULL << 33) < 1ULL << 34) + bytes += odd + 4 * 4; + else if (off - (8 - odd) + (0x20002ULL << 32) < 0x40004ULL << 32) + bytes += 7 * 4; + else + bytes += 8 * 4; + return bytes + tail; + } - if (p->second.notoc_) - { - uint64_t from = this->stub_address() + p->second.off_ + bytes + 2 * 4; - if (bytes > 32) - from -= 4 * 4; - uint64_t off = plt_addr - from; - if (off + 0x8000 < 0x10000) - bytes += 7 * 4; - else if (off + 0x80008000ULL < 0x100000000ULL) - bytes += 8 * 4; - else + if (p->second.notoc_) { - bytes += 8 * 4; - if (off + 0x800000000000ULL >= 0x1000000000000ULL - && ((off >> 32) & 0xffff) != 0) - bytes += 4; - if (((off >> 32) & 0xffffffffULL) != 0) - bytes += 4; - if (hi(off) != 0) - bytes += 4; - if (l(off) != 0) - bytes += 4; + uint64_t from = this->stub_address() + p->second.off_ + bytes + 2 * 4; + uint64_t off = plt_addr - from; + if (off + 0x8000 < 0x10000) + bytes += 7 * 4; + else if (off + 0x80008000ULL < 0x100000000ULL) + bytes += 8 * 4; + else + { + bytes += 8 * 4; + if (off + 0x800000000000ULL >= 0x1000000000000ULL + && ((off >> 32) & 0xffff) != 0) + bytes += 4; + if (((off >> 32) & 0xffffffffULL) != 0) + bytes += 4; + if (hi(off) != 0) + bytes += 4; + if (l(off) != 0) + bytes += 4; + } + return bytes + tail; } - return bytes; - } - uint64_t got_addr = this->targ_->got_section()->output_section()->address(); - const Powerpc_relobj* ppcobj = static_cast - *>(p->first.object_); - got_addr += ppcobj->toc_base_offset(); - uint64_t off = plt_addr - got_addr; - bytes += 3 * 4 + 4 * (ha(off) != 0); - if (this->targ_->abiversion() < 2) - { - bool static_chain = parameters->options().plt_static_chain(); - bool thread_safe = this->targ_->plt_thread_safe(); - bytes += (4 - + 4 * static_chain - + 8 * thread_safe - + 4 * (ha(off + 8 + 8 * static_chain) != ha(off))); + uint64_t got_addr = this->targ_->got_section()->output_section()->address(); + const Powerpc_relobj* ppcobj = static_cast + *>(p->first.object_); + got_addr += ppcobj->toc_base_offset(); + uint64_t off = plt_addr - got_addr; + bytes += 3 * 4 + 4 * (ha(off) != 0); + if (this->targ_->abiversion() < 2) + { + bool static_chain = parameters->options().plt_static_chain(); + bool thread_safe = this->targ_->plt_thread_safe(); + bytes += (4 + + 4 * static_chain + + 8 * thread_safe + + 4 * (ha(off + 8 + 8 * static_chain) != ha(off))); + } + return bytes + tail; } - return bytes; } // Return long branch stub size. @@ -6029,7 +6092,7 @@ Stub_table::plt_call_size( template unsigned int Stub_table::branch_stub_size( - typename Branch_stub_entries::const_iterator p, + typename Branch_stub_entries::iterator p, bool* need_lt) { Address loc = this->stub_address() + this->last_plt_size_ + p->second.off_; @@ -6043,46 +6106,56 @@ Stub_table::branch_stub_size( } uint64_t off = p->first.dest_ - loc; + unsigned int bytes = 0; if (p->second.notoc_) { if (this->targ_->power10_stubs()) { Address odd = loc & 4; if (off + (1 << 25) < 2 << 25) - return odd + 12; - if (off - odd + (1ULL << 33) < 1ULL << 34) - return odd + 16; - if (off - (8 - odd) + (0x20002ULL << 32) < 0x40004ULL << 32) - return 28; - return 32; - } - off -= 8; - if (off + 0x8000 < 0x10000) - return 24; - if (off + 0x80008000ULL < 0x100000000ULL) - { - if (off + 24 + (1 << 25) < 2 << 25) - return 28; - return 32; - } - unsigned int bytes = 32; - if (off + 0x800000000000ULL >= 0x1000000000000ULL - && ((off >> 32) & 0xffff) != 0) - bytes += 4; - if (((off >> 32) & 0xffffffffULL) != 0) - bytes += 4; - if (hi(off) != 0) - bytes += 4; - if (l(off) != 0) - bytes += 4; - return bytes; + bytes = odd + 12; + else if (off - odd + (1ULL << 33) < 1ULL << 34) + bytes = odd + 16; + else if (off - (8 - odd) + (0x20002ULL << 32) < 0x40004ULL << 32) + bytes = 28; + else + bytes = 32; + if (!(p->second.toc_ && this->targ_->power10_stubs_auto())) + return bytes; + p->second.tocoff_ = bytes; + } + else + { + off -= 8; + if (off + 0x8000 < 0x10000) + return 24; + if (off + 0x80008000ULL < 0x100000000ULL) + { + if (off + 24 + (1 << 25) < 2 << 25) + return 28; + return 32; + } + + bytes = 32; + if (off + 0x800000000000ULL >= 0x1000000000000ULL + && ((off >> 32) & 0xffff) != 0) + bytes += 4; + if (((off >> 32) & 0xffffffffULL) != 0) + bytes += 4; + if (hi(off) != 0) + bytes += 4; + if (l(off) != 0) + bytes += 4; + return bytes; + } } if (off + (1 << 25) < 2 << 25) - return 4; - if (!this->targ_->power10_stubs()) + return bytes + 4; + if (!this->targ_->power10_stubs() + || (p->second.toc_ && this->targ_->power10_stubs_auto())) *need_lt = true; - return 16; + return bytes + 16; } template @@ -6118,6 +6191,10 @@ Stub_table::do_write(Output_file* of) if (size == 64 && this->targ_->power10_stubs()) { + const Output_data_got_powerpc* got + = this->targ_->got_section(); + Address got_os_addr = got->output_section()->address(); + if (!this->plt_call_stubs_.empty()) { // Write out plt call stubs. @@ -6127,22 +6204,94 @@ Stub_table::do_write(Output_file* of) ++cs) { p = oview + cs->second.off_; - this->build_tls_opt_head(&p, cs); - if (cs->second.r2save_) - { - write_insn(p, std_2_1 + this->targ_->stk_toc()); - p += 4; - } const Output_data_plt_powerpc* plt; Address pltoff = this->plt_off(cs, &plt); Address plt_addr = pltoff + plt->address(); - Address from = this->stub_address() + (p - oview); - Address delta = plt_addr - from; - p = build_power10_offset(p, delta, from & 4, true); - write_insn(p, mtctr_12); - p += 4; - if (!this->build_tls_opt_tail(p, cs)) - write_insn(p, bctr); + if (this->targ_->power10_stubs_auto()) + { + if (cs->second.notoc_) + { + if (this->targ_->is_tls_get_addr_opt(cs->first.sym_)) + this->build_tls_opt_head(&p, false); + Address from = this->stub_address() + (p - oview); + Address delta = plt_addr - from; + p = build_power10_offset(p, delta, from & 4, + true); + write_insn(p, mtctr_12); + p += 4; + write_insn(p, bctr); + p += 4; + p = oview + this->plt_call_align(p - oview); + } + if (cs->second.toc_) + { + if (this->targ_->is_tls_get_addr_opt(cs->first.sym_)) + { + bool save_lr + = cs->second.r2save_ && !cs->second.localentry0_; + this->build_tls_opt_head(&p, save_lr); + } + const Powerpc_relobj* ppcobj + = static_cast*>( + cs->first.object_); + Address got_addr = got_os_addr + ppcobj->toc_base_offset(); + Address off = plt_addr - got_addr; + + if (off + 0x80008000 > 0xffffffff || (off & 7) != 0) + this->plt_error(cs->first); + + if (cs->second.r2save_) + { + write_insn(p, std_2_1 + this->targ_->stk_toc()); + p += 4; + } + if (ha(off) != 0) + { + write_insn(p, addis_12_2 + ha(off)); + p += 4; + write_insn(p, ld_12_12 + l(off)); + p += 4; + } + else + { + write_insn(p, ld_12_2 + l(off)); + p += 4; + } + write_insn(p, mtctr_12); + p += 4; + if (cs->second.r2save_ + && !cs->second.localentry0_ + && this->targ_->is_tls_get_addr_opt(cs->first.sym_)) + this->build_tls_opt_tail(p); + else + write_insn(p, bctr); + } + } + else + { + if (this->targ_->is_tls_get_addr_opt(cs->first.sym_)) + { + bool save_lr + = cs->second.r2save_ && !cs->second.localentry0_; + this->build_tls_opt_head(&p, save_lr); + } + if (cs->second.r2save_) + { + write_insn(p, std_2_1 + this->targ_->stk_toc()); + p += 4; + } + Address from = this->stub_address() + (p - oview); + Address delta = plt_addr - from; + p = build_power10_offset(p, delta, from & 4, true); + write_insn(p, mtctr_12); + p += 4; + if (cs->second.r2save_ + && !cs->second.localentry0_ + && this->targ_->is_tls_get_addr_opt(cs->first.sym_)) + this->build_tls_opt_tail(p); + else + write_insn(p, bctr); + } } } @@ -6158,19 +6307,76 @@ Stub_table::do_write(Output_file* of) p = oview + off; Address loc = this->stub_address() + off; Address delta = bs->first.dest_ - loc; - if (bs->second.notoc_ || delta + (1 << 25) >= 2 << 25) + if (this->targ_->power10_stubs_auto()) { - unsigned char* startp = p; - p = build_power10_offset(p, delta, loc & 4, false); - delta -= p - startp; + if (bs->second.notoc_) + { + unsigned char* startp = p; + p = build_power10_offset(p, delta, + loc & 4, false); + delta -= p - startp; + startp = p; + if (delta + (1 << 25) < 2 << 25) + write_insn(p, b | (delta & 0x3fffffc)); + else + { + write_insn(p, mtctr_12); + p += 4; + write_insn(p, bctr); + } + p += 4; + delta -= p - startp; + } + if (bs->second.toc_) + { + if (delta + (1 << 25) >= 2 << 25) + { + 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(p, ld_12_2 + l(brltoff)); + p += 4; + } + else + { + write_insn(p, addis_12_2 + ha(brltoff)); + p += 4; + write_insn(p, ld_12_12 + l(brltoff)); + p += 4; + } + } + if (delta + (1 << 25) < 2 << 25) + write_insn(p, b | (delta & 0x3fffffc)); + else + { + write_insn(p, mtctr_12); + p += 4; + write_insn(p, bctr); + } + } } - if (delta + (1 << 25) < 2 << 25) - write_insn(p, b | (delta & 0x3fffffc)); else { - write_insn(p, mtctr_12); - p += 4; - write_insn(p, bctr); + if (bs->second.notoc_ || delta + (1 << 25) >= 2 << 25) + { + unsigned char* startp = p; + p = build_power10_offset(p, delta, + loc & 4, false); + delta -= p - startp; + } + if (delta + (1 << 25) < 2 << 25) + write_insn(p, b | (delta & 0x3fffffc)); + else + { + write_insn(p, mtctr_12); + p += 4; + write_insn(p, bctr); + } } } } @@ -6194,7 +6400,11 @@ Stub_table::do_write(Output_file* of) Address plt_addr = pltoff + plt->address(); p = oview + cs->second.off_; - this->build_tls_opt_head(&p, cs); + if (this->targ_->is_tls_get_addr_opt(cs->first.sym_)) + { + bool save_lr = cs->second.r2save_ && !cs->second.localentry0_; + this->build_tls_opt_head(&p, save_lr); + } if (cs->second.r2save_) { write_insn(p, std_2_1 + this->targ_->stk_toc()); @@ -6231,7 +6441,11 @@ Stub_table::do_write(Output_file* of) } write_insn(p, mtctr_12); p += 4; - if (!this->build_tls_opt_tail(p, cs)) + if (cs->second.r2save_ + && !cs->second.localentry0_ + && this->targ_->is_tls_get_addr_opt(cs->first.sym_)) + this->build_tls_opt_tail(p); + else write_insn(p, bctr); } } @@ -6282,8 +6496,12 @@ Stub_table::do_write(Output_file* of) } p = oview + cs->second.off_; - if (this->build_tls_opt_head(&p, cs)) - use_fake_dep = thread_safe; + if (this->targ_->is_tls_get_addr_opt(cs->first.sym_)) + { + bool save_lr = cs->second.r2save_ && !cs->second.localentry0_; + this->build_tls_opt_head(&p, save_lr); + use_fake_dep = thread_safe; + } if (cs->second.r2save_) { write_insn(p, std_2_1 + this->targ_->stk_toc()); @@ -6345,8 +6563,10 @@ Stub_table::do_write(Output_file* of) write_insn(p, ld_2_2 + l(off + 8)); p += 4; } - if (this->build_tls_opt_tail(p, cs)) - ; + if (cs->second.r2save_ + && !cs->second.localentry0_ + && this->targ_->is_tls_get_addr_opt(cs->first.sym_)) + this->build_tls_opt_tail(p); else if (thread_safe && !use_fake_dep) { write_insn(p, cmpldi_2_0); @@ -6427,7 +6647,8 @@ Stub_table::do_write(Output_file* of) plt_addr += plt->address(); p = oview + cs->second.off_; - this->build_tls_opt_head(&p, cs); + if (this->targ_->is_tls_get_addr_opt(cs->first.sym_)) + this->build_tls_opt_head(&p, false); if (parameters->options().output_is_position_independent()) { Address got_addr; @@ -10087,10 +10308,17 @@ Target_powerpc::Relocate::relocate( const int reloc_size = elfcpp::Elf_sizes::rela_size; elfcpp::Shdr shdr(relinfo->reloc_shdr); size_t reloc_count = shdr.get_sh_size() / reloc_size; + if (size == 64 + && r_type != elfcpp::R_PPC64_REL24_NOTOC) + value += ent->tocoff_; if (size == 64 && ent->r2save_ && r_type == elfcpp::R_PPC64_REL24_NOTOC) - value += 4; + { + if (!(target->power10_stubs() + && target->power10_stubs_auto())) + value += 4; + } else if (size == 64 && ent->r2save_ && relnum < reloc_count - 1) @@ -10707,8 +10935,14 @@ Target_powerpc::Relocate::relocate( value = (value - target->savres_section()->address() + stub_table->branch_size()); else - value = (stub_table->stub_address() + stub_table->plt_size() - + ent->off_); + { + value = (stub_table->stub_address() + + stub_table->plt_size() + + ent->off_); + if (size == 64 + && r_type != elfcpp::R_PPC64_REL24_NOTOC) + value += ent->tocoff_; + } has_stub_value = true; } }