]> code.ossystems Code Review - openembedded-core.git/blob
dcac308bccc0ed03ca691ea0832aae5fd58d41c8
[openembedded-core.git] /
1 From b780c9e06cabe6d8e301aaf46f33f116f3224021 Mon Sep 17 00:00:00 2001
2 From: Han Shen <shenhan@google.com>
3 Date: Thu, 29 Jan 2015 10:00:46 -0800
4 Subject: [PATCH] This patch adds IFUNC support for arm gold backend.
5
6 This is a feature required in chromeos arm development work.
7
8 Tested:
9 1) Built passed all-gold on x86_64 machine
10 2) Tested with basic gold aarch64 ifunc unittests -
11    a) global ifunc, statically/non-statically linked
12    b) local ifunc, statically/non-statically linked
13    c) global/local, other shared library routine mixed,
14    statically/non-statically linked
15    d) arm/thumb mode ifunc
16    e) linking chrome browser passed
17 ---
18 Upstream-Status: Backport
19 Signed-off-by: Khem Raj <raj.khem@gmail.com>
20
21  elfcpp/arm.h    |   5 +-
22  gold/aarch64.cc |   2 +-
23  gold/arm.cc     | 593 +++++++++++++++++++++++++++++++++++++++++++++++++++-----
24  gold/output.h   |  11 ++
25  4 files changed, 561 insertions(+), 50 deletions(-)
26
27 diff --git a/elfcpp/arm.h b/elfcpp/arm.h
28 index 8c6b6bf..1c13dc9 100644
29 --- a/elfcpp/arm.h
30 +++ b/elfcpp/arm.h
31 @@ -192,11 +192,12 @@ enum
32    R_ARM_PRIVATE_14 = 126,
33    R_ARM_PRIVATE_15 = 127,
34    R_ARM_ME_TOO = 128,          // Obsolete
35 -  R_ARM_THM_TLS_DESCSEQ16 = 129,// Static      Thumb16 
36 +  R_ARM_THM_TLS_DESCSEQ16 = 129,// Static      Thumb16
37    R_ARM_THM_TLS_DESCSEQ32 = 130,// Static      Thumb32
38    // 131 - 139                 Unallocated
39    // 140 - 159                 Dynamic         Reserved for future allocation
40 -  // 160 - 255                 Unallocated
41 +  R_ARM_IRELATIVE = 160,       // Dynamic
42 +  // 161 - 255                 Unallocated
43  };
44  
45  // e_flags values used for ARM.  We only support flags defined in AAELF.
46 diff --git a/gold/aarch64.cc b/gold/aarch64.cc
47 index afb9024..7fbbdbd 100644
48 --- a/gold/aarch64.cc
49 +++ b/gold/aarch64.cc
50 @@ -1226,7 +1226,7 @@ class Output_data_plt_aarch64 : public Output_section_data
51    // The number of PLT entries.
52    unsigned int count_;
53  
54 -  // Number of PLT entries with R_X86_64_IRELATIVE relocs.  These
55 +  // Number of PLT entries with R_AARCH64_IRELATIVE relocs.  These
56    // follow the regular PLT entries.
57    unsigned int irelative_count_;
58  
59 diff --git a/gold/arm.cc b/gold/arm.cc
60 index 6c472bb..8719cc9 100644
61 --- a/gold/arm.cc
62 +++ b/gold/arm.cc
63 @@ -2119,8 +2119,8 @@ class Target_arm : public Sized_target<32, big_endian>
64  
65    Target_arm(const Target::Target_info* info = &arm_info)
66      : Sized_target<32, big_endian>(info),
67 -      got_(NULL), plt_(NULL), got_plt_(NULL), rel_dyn_(NULL),
68 -      copy_relocs_(elfcpp::R_ARM_COPY),
69 +      got_(NULL), plt_(NULL), got_plt_(NULL), got_irelative_(NULL),
70 +      rel_dyn_(NULL), rel_irelative_(NULL), copy_relocs_(elfcpp::R_ARM_COPY),
71        got_mod_index_offset_(-1U), tls_base_symbol_defined_(false),
72        stub_tables_(), stub_factory_(Stub_factory::get_instance()),
73        should_force_pic_veneer_(false),
74 @@ -2258,6 +2258,18 @@ class Target_arm : public Sized_target<32, big_endian>
75    uint64_t
76    do_dynsym_value(const Symbol*) const;
77  
78 +  // Return the plt address for globals. Since we have irelative plt entries,
79 +  // address calculation is not as straightforward as plt_address + plt_offset.
80 +  uint64_t
81 +  do_plt_address_for_global(const Symbol* gsym) const
82 +  { return this->plt_section()->address_for_global(gsym); }
83 +
84 +  // Return the plt address for locals. Since we have irelative plt entries,
85 +  // address calculation is not as straightforward as plt_address + plt_offset.
86 +  uint64_t
87 +  do_plt_address_for_local(const Relobj* relobj, unsigned int symndx) const
88 +  { return this->plt_section()->address_for_local(relobj, symndx); }
89 +
90    // Relocate a section.
91    void
92    relocate_section(const Relocate_info<32, big_endian>*,
93 @@ -2357,6 +2369,10 @@ class Target_arm : public Sized_target<32, big_endian>
94    unsigned int
95    plt_entry_size() const;
96  
97 +  // Get the section to use for IRELATIVE relocations, create it if necessary.
98 +  Reloc_section*
99 +  rel_irelative_section(Layout*);
100 +
101    // Map platform-specific reloc types
102    static unsigned int
103    get_real_reloc_type(unsigned int r_type);
104 @@ -2448,8 +2464,11 @@ class Target_arm : public Sized_target<32, big_endian>
105   protected:
106    // Make the PLT-generator object.
107    Output_data_plt_arm<big_endian>*
108 -  make_data_plt(Layout* layout, Output_data_space* got_plt)
109 -  { return this->do_make_data_plt(layout, got_plt); }
110 +  make_data_plt(Layout* layout,
111 +               Arm_output_data_got<big_endian>* got,
112 +               Output_data_space* got_plt,
113 +               Output_data_space* got_irelative)
114 +  { return this->do_make_data_plt(layout, got, got_plt, got_irelative); }
115  
116    // Make an ELF object.
117    Object*
118 @@ -2530,9 +2549,14 @@ class Target_arm : public Sized_target<32, big_endian>
119    do_define_standard_symbols(Symbol_table*, Layout*);
120  
121    virtual Output_data_plt_arm<big_endian>*
122 -  do_make_data_plt(Layout* layout, Output_data_space* got_plt)
123 +  do_make_data_plt(Layout* layout,
124 +                  Arm_output_data_got<big_endian>* got,
125 +                  Output_data_space* got_plt,
126 +                  Output_data_space* got_irelative)
127    {
128 -    return new Output_data_plt_arm_standard<big_endian>(layout, got_plt);
129 +    gold_assert(got_plt != NULL && got_irelative != NULL);
130 +    return new Output_data_plt_arm_standard<big_endian>(
131 +       layout, got, got_plt, got_irelative);
132    }
133  
134   private:
135 @@ -2602,6 +2626,9 @@ class Target_arm : public Sized_target<32, big_endian>
136        if (sym->is_undefined() && !parameters->options().shared())
137         return false;
138  
139 +      if (sym->type() == elfcpp::STT_GNU_IFUNC)
140 +       return true;
141 +
142        return (!parameters->doing_static_link()
143               && (sym->type() == elfcpp::STT_FUNC
144                   || sym->type() == elfcpp::STT_ARM_TFUNC)
145 @@ -2613,6 +2640,11 @@ class Target_arm : public Sized_target<32, big_endian>
146      inline bool
147      possible_function_pointer_reloc(unsigned int r_type);
148  
149 +    // Whether a plt entry is needed for ifunc.
150 +    bool
151 +    reloc_needs_plt_for_ifunc(Sized_relobj_file<32, big_endian>*,
152 +                             unsigned int r_type);
153 +
154      // Whether we have issued an error about a non-PIC compilation.
155      bool issued_non_pic_error_;
156    };
157 @@ -2718,10 +2750,20 @@ class Target_arm : public Sized_target<32, big_endian>
158      return this->got_plt_;
159    }
160  
161 +  // Create the PLT section.
162 +  void
163 +  make_plt_section(Symbol_table* symtab, Layout* layout);
164 +
165    // Create a PLT entry for a global symbol.
166    void
167    make_plt_entry(Symbol_table*, Layout*, Symbol*);
168  
169 +  // Create a PLT entry for a local STT_GNU_IFUNC symbol.
170 +  void
171 +  make_local_ifunc_plt_entry(Symbol_table*, Layout*,
172 +                            Sized_relobj_file<32, big_endian>* relobj,
173 +                            unsigned int local_sym_index);
174 +
175    // Define the _TLS_MODULE_BASE_ symbol in the TLS segment.
176    void
177    define_tls_base_symbol(Symbol_table*, Layout*);
178 @@ -2903,8 +2945,12 @@ class Target_arm : public Sized_target<32, big_endian>
179    Output_data_plt_arm<big_endian>* plt_;
180    // The GOT PLT section.
181    Output_data_space* got_plt_;
182 +  // The GOT section for IRELATIVE relocations.
183 +  Output_data_space* got_irelative_;
184    // The dynamic reloc section.
185    Reloc_section* rel_dyn_;
186 +  // The section to use for IRELATIVE relocs.
187 +  Reloc_section* rel_irelative_;
188    // Relocs saved to avoid a COPY reloc.
189    Copy_relocs<elfcpp::SHT_REL, 32, big_endian> copy_relocs_;
190    // Offset of the GOT entry for the TLS module index.
191 @@ -4244,6 +4290,15 @@ Target_arm<big_endian>::got_section(Symbol_table* symtab, Layout* layout)
192                                     elfcpp::STB_LOCAL,
193                                     elfcpp::STV_HIDDEN, 0,
194                                     false, false);
195 +
196 +      // If there are any IRELATIVE relocations, they get GOT entries
197 +      // in .got.plt after the jump slot entries.
198 +      this->got_irelative_ = new Output_data_space(4, "** GOT IRELATIVE PLT");
199 +      layout->add_output_section_data(".got", elfcpp::SHT_PROGBITS,
200 +                                     (elfcpp::SHF_ALLOC | elfcpp::SHF_WRITE),
201 +                                     this->got_irelative_,
202 +                                     got_order, is_got_relro);
203 +
204      }
205    return this->got_;
206  }
207 @@ -4257,14 +4312,43 @@ Target_arm<big_endian>::rel_dyn_section(Layout* layout)
208    if (this->rel_dyn_ == NULL)
209      {
210        gold_assert(layout != NULL);
211 +      // Create both relocation sections in the same place, so as to ensure
212 +      // their relative order in the output section.
213        this->rel_dyn_ = new Reloc_section(parameters->options().combreloc());
214 +      this->rel_irelative_ = new Reloc_section(false);
215        layout->add_output_section_data(".rel.dyn", elfcpp::SHT_REL,
216                                       elfcpp::SHF_ALLOC, this->rel_dyn_,
217                                       ORDER_DYNAMIC_RELOCS, false);
218 +      layout->add_output_section_data(".rel.dyn", elfcpp::SHT_REL,
219 +                                     elfcpp::SHF_ALLOC, this->rel_irelative_,
220 +                                     ORDER_DYNAMIC_RELOCS, false);
221      }
222    return this->rel_dyn_;
223  }
224  
225 +
226 +// Get the section to use for IRELATIVE relocs, creating it if necessary.  These
227 +// go in .rela.dyn, but only after all other dynamic relocations.  They need to
228 +// follow the other dynamic relocations so that they can refer to global
229 +// variables initialized by those relocs.
230 +
231 +template<bool big_endian>
232 +typename Target_arm<big_endian>::Reloc_section*
233 +Target_arm<big_endian>::rel_irelative_section(Layout* layout)
234 +{
235 +  if (this->rel_irelative_ == NULL)
236 +    {
237 +      // Delegate the creation to rel_dyn_section so as to ensure their order in
238 +      // the output section.
239 +      this->rel_dyn_section(layout);
240 +      gold_assert(this->rel_irelative_ != NULL
241 +                 && (this->rel_dyn_->output_section()
242 +                     == this->rel_irelative_->output_section()));
243 +    }
244 +  return this->rel_irelative_;
245 +}
246 +
247 +
248  // Insn_template methods.
249  
250  // Return byte size of an instruction template.
251 @@ -7221,24 +7305,80 @@ template<bool big_endian>
252  class Output_data_plt_arm : public Output_section_data
253  {
254   public:
255 +  // Unlike aarch64, which records symbol value in "addend" field of relocations
256 +  // and could be done at the same time an IRelative reloc is created for the
257 +  // symbol, arm puts the symbol value into "GOT" table, which, however, is
258 +  // issued later in Output_data_plt_arm::do_write(). So we have a struct here
259 +  // to keep necessary symbol information for later use in do_write. We usually
260 +  // have only a very limited number of ifuncs, so the extra data required here
261 +  // is also limited.
262 +
263 +  struct IRelative_data
264 +  {
265 +    IRelative_data(Sized_symbol<32>* sized_symbol)
266 +      : symbol_is_global_(true)
267 +    {
268 +      u_.global = sized_symbol;
269 +    }
270 +
271 +    IRelative_data(Sized_relobj_file<32, big_endian>* relobj,
272 +                  unsigned int index)
273 +      : symbol_is_global_(false)
274 +    {
275 +      u_.local.relobj = relobj;
276 +      u_.local.index = index;
277 +    }
278 +
279 +    union
280 +    {
281 +      Sized_symbol<32>* global;
282 +
283 +      struct
284 +      {
285 +       Sized_relobj_file<32, big_endian>* relobj;
286 +       unsigned int index;
287 +      } local;
288 +    } u_;
289 +
290 +    bool symbol_is_global_;
291 +  };
292 +
293    typedef Output_data_reloc<elfcpp::SHT_REL, true, 32, big_endian>
294      Reloc_section;
295  
296 -  Output_data_plt_arm(Layout*, uint64_t addralign, Output_data_space*);
297 +  Output_data_plt_arm(Layout* layout, uint64_t addralign,
298 +                     Arm_output_data_got<big_endian>* got,
299 +                     Output_data_space* got_plt,
300 +                     Output_data_space* got_irelative);
301  
302    // Add an entry to the PLT.
303    void
304 -  add_entry(Symbol* gsym);
305 +  add_entry(Symbol_table* symtab, Layout* layout, Symbol* gsym);
306 +
307 +  // Add the relocation for a plt entry.
308 +  void
309 +  add_relocation(Symbol_table* symtab, Layout* layout,
310 +                Symbol* gsym, unsigned int got_offset);
311 +
312 +  // Add an entry to the PLT for a local STT_GNU_IFUNC symbol.
313 +  unsigned int
314 +  add_local_ifunc_entry(Symbol_table* symtab, Layout*,
315 +                       Sized_relobj_file<32, big_endian>* relobj,
316 +                       unsigned int local_sym_index);
317  
318    // Return the .rel.plt section data.
319    const Reloc_section*
320    rel_plt() const
321    { return this->rel_; }
322  
323 +  // Return the PLT relocation container for IRELATIVE.
324 +  Reloc_section*
325 +  rel_irelative(Symbol_table*, Layout*);
326 +
327    // Return the number of PLT entries.
328    unsigned int
329    entry_count() const
330 -  { return this->count_; }
331 +  { return this->count_ + this->irelative_count_; }
332  
333    // Return the offset of the first non-reserved PLT entry.
334    unsigned int
335 @@ -7250,6 +7390,14 @@ class Output_data_plt_arm : public Output_section_data
336    get_plt_entry_size() const
337    { return this->do_get_plt_entry_size(); }
338  
339 +  // Return the PLT address for globals.
340 +  uint32_t
341 +  address_for_global(const Symbol*) const;
342 +
343 +  // Return the PLT address for locals.
344 +  uint32_t
345 +  address_for_local(const Relobj*, unsigned int symndx) const;
346 +
347   protected:
348    // Fill in the first PLT entry.
349    void
350 @@ -7298,19 +7446,37 @@ class Output_data_plt_arm : public Output_section_data
351    set_final_data_size()
352    {
353      this->set_data_size(this->first_plt_entry_offset()
354 -                       + this->count_ * this->get_plt_entry_size());
355 +                       + ((this->count_ + this->irelative_count_)
356 +                          * this->get_plt_entry_size()));
357    }
358  
359    // Write out the PLT data.
360    void
361    do_write(Output_file*);
362  
363 +  // Record irelative symbol data.
364 +  void insert_irelative_data(const IRelative_data& idata)
365 +  { irelative_data_vec_.push_back(idata); }
366 +
367    // The reloc section.
368    Reloc_section* rel_;
369 +  // The IRELATIVE relocs, if necessary.  These must follow the
370 +  // regular PLT relocations.
371 +  Reloc_section* irelative_rel_;
372 +  // The .got section.
373 +  Arm_output_data_got<big_endian>* got_;
374    // The .got.plt section.
375    Output_data_space* got_plt_;
376 +  // The part of the .got.plt section used for IRELATIVE relocs.
377 +  Output_data_space* got_irelative_;
378    // The number of PLT entries.
379    unsigned int count_;
380 +  // Number of PLT entries with R_ARM_IRELATIVE relocs.  These
381 +  // follow the regular PLT entries.
382 +  unsigned int irelative_count_;
383 +  // Vector for irelative data.
384 +  typedef std::vector<IRelative_data> IRelative_data_vec;
385 +  IRelative_data_vec irelative_data_vec_;
386  };
387  
388  // Create the PLT section.  The ordinary .got section is an argument,
389 @@ -7318,10 +7484,14 @@ class Output_data_plt_arm : public Output_section_data
390  // section just for PLT entries.
391  
392  template<bool big_endian>
393 -Output_data_plt_arm<big_endian>::Output_data_plt_arm(Layout* layout,
394 -                                                    uint64_t addralign,
395 -                                                    Output_data_space* got_plt)
396 -  : Output_section_data(addralign), got_plt_(got_plt), count_(0)
397 +Output_data_plt_arm<big_endian>::Output_data_plt_arm(
398 +    Layout* layout, uint64_t addralign,
399 +    Arm_output_data_got<big_endian>* got,
400 +    Output_data_space* got_plt,
401 +    Output_data_space* got_irelative)
402 +  : Output_section_data(addralign), irelative_rel_(NULL),
403 +    got_(got), got_plt_(got_plt), got_irelative_(got_irelative),
404 +    count_(0), irelative_count_(0)
405  {
406    this->rel_ = new Reloc_section(false);
407    layout->add_output_section_data(".rel.plt", elfcpp::SHT_REL,
408 @@ -7340,40 +7510,210 @@ Output_data_plt_arm<big_endian>::do_adjust_output_section(Output_section* os)
409  
410  template<bool big_endian>
411  void
412 -Output_data_plt_arm<big_endian>::add_entry(Symbol* gsym)
413 +Output_data_plt_arm<big_endian>::add_entry(Symbol_table* symtab,
414 +                                          Layout* layout,
415 +                                          Symbol* gsym)
416  {
417    gold_assert(!gsym->has_plt_offset());
418  
419 -  // Note that when setting the PLT offset we skip the initial
420 -  // reserved PLT entry.
421 -  gsym->set_plt_offset((this->count_) * this->get_plt_entry_size()
422 -                      + this->first_plt_entry_offset());
423 +  unsigned int* entry_count;
424 +  Output_section_data_build* got;
425 +
426 +  // We have 2 different types of plt entry here, normal and ifunc.
427 +
428 +  // For normal plt, the offset begins with first_plt_entry_offset(20), and the
429 +  // 1st entry offset would be 20, the second 32, third 44 ... etc.
430 +
431 +  // For ifunc plt, the offset begins with 0. So the first offset would 0,
432 +  // second 12, third 24 ... etc.
433 +
434 +  // IFunc plt entries *always* come after *normal* plt entries.
435 +
436 +  // Notice, when computing the plt address of a certain symbol, "plt_address +
437 +  // plt_offset" is no longer correct. Use target->plt_address_for_global() or
438 +  // target->plt_address_for_local() instead.
439 +
440 +  int begin_offset = 0;
441 +  if (gsym->type() == elfcpp::STT_GNU_IFUNC
442 +      && gsym->can_use_relative_reloc(false))
443 +    {
444 +      entry_count = &this->irelative_count_;
445 +      got = this->got_irelative_;
446 +      // For irelative plt entries, offset is relative to the end of normal plt
447 +      // entries, so it starts from 0.
448 +      begin_offset = 0;
449 +      // Record symbol information.
450 +      this->insert_irelative_data(
451 +         IRelative_data(symtab->get_sized_symbol<32>(gsym)));
452 +    }
453 +  else
454 +    {
455 +      entry_count = &this->count_;
456 +      got = this->got_plt_;
457 +      // Note that for normal plt entries, when setting the PLT offset we skip
458 +      // the initial reserved PLT entry.
459 +      begin_offset = this->first_plt_entry_offset();
460 +    }
461 +
462 +  gsym->set_plt_offset(begin_offset
463 +                      + (*entry_count) * this->get_plt_entry_size());
464  
465 -  ++this->count_;
466 +  ++(*entry_count);
467  
468 -  section_offset_type got_offset = this->got_plt_->current_data_size();
469 +  section_offset_type got_offset = got->current_data_size();
470  
471    // Every PLT entry needs a GOT entry which points back to the PLT
472    // entry (this will be changed by the dynamic linker, normally
473    // lazily when the function is called).
474 -  this->got_plt_->set_current_data_size(got_offset + 4);
475 +  got->set_current_data_size(got_offset + 4);
476  
477    // Every PLT entry needs a reloc.
478 -  gsym->set_needs_dynsym_entry();
479 -  this->rel_->add_global(gsym, elfcpp::R_ARM_JUMP_SLOT, this->got_plt_,
480 -                        got_offset);
481 +  this->add_relocation(symtab, layout, gsym, got_offset);
482  
483    // Note that we don't need to save the symbol.  The contents of the
484    // PLT are independent of which symbols are used.  The symbols only
485    // appear in the relocations.
486  }
487  
488 +// Add an entry to the PLT for a local STT_GNU_IFUNC symbol.  Return
489 +// the PLT offset.
490 +
491 +template<bool big_endian>
492 +unsigned int
493 +Output_data_plt_arm<big_endian>::add_local_ifunc_entry(
494 +    Symbol_table* symtab,
495 +    Layout* layout,
496 +    Sized_relobj_file<32, big_endian>* relobj,
497 +    unsigned int local_sym_index)
498 +{
499 +  this->insert_irelative_data(IRelative_data(relobj, local_sym_index));
500 +
501 +  // Notice, when computingthe plt entry address, "plt_address + plt_offset" is
502 +  // no longer correct. Use target->plt_address_for_local() instead.
503 +  unsigned int plt_offset = this->irelative_count_ * this->get_plt_entry_size();
504 +  ++this->irelative_count_;
505 +
506 +  section_offset_type got_offset = this->got_irelative_->current_data_size();
507 +
508 +  // Every PLT entry needs a GOT entry which points back to the PLT
509 +  // entry.
510 +  this->got_irelative_->set_current_data_size(got_offset + 4);
511 +
512 +
513 +  // Every PLT entry needs a reloc.
514 +  Reloc_section* rel = this->rel_irelative(symtab, layout);
515 +  rel->add_symbolless_local_addend(relobj, local_sym_index,
516 +                                  elfcpp::R_ARM_IRELATIVE,
517 +                                  this->got_irelative_, got_offset);
518 +  return plt_offset;
519 +}
520 +
521 +
522 +// Add the relocation for a PLT entry.
523 +
524 +template<bool big_endian>
525 +void
526 +Output_data_plt_arm<big_endian>::add_relocation(
527 +    Symbol_table* symtab, Layout* layout, Symbol* gsym, unsigned int got_offset)
528 +{
529 +  if (gsym->type() == elfcpp::STT_GNU_IFUNC
530 +      && gsym->can_use_relative_reloc(false))
531 +    {
532 +      Reloc_section* rel = this->rel_irelative(symtab, layout);
533 +      rel->add_symbolless_global_addend(gsym, elfcpp::R_ARM_IRELATIVE,
534 +                                       this->got_irelative_, got_offset);
535 +    }
536 +  else
537 +    {
538 +      gsym->set_needs_dynsym_entry();
539 +      this->rel_->add_global(gsym, elfcpp::R_ARM_JUMP_SLOT, this->got_plt_,
540 +                            got_offset);
541 +    }
542 +}
543 +
544 +
545 +// Create the irelative relocation data.
546 +
547 +template<bool big_endian>
548 +typename Output_data_plt_arm<big_endian>::Reloc_section*
549 +Output_data_plt_arm<big_endian>::rel_irelative(Symbol_table* symtab,
550 +                                               Layout* layout)
551 +{
552 +  if (this->irelative_rel_ == NULL)
553 +    {
554 +      // Since irelative relocations goes into 'rel.dyn', we delegate the
555 +      // creation of irelative_rel_ to where rel_dyn section gets created.
556 +      Target_arm<big_endian>* arm_target =
557 +         Target_arm<big_endian>::default_target();
558 +      this->irelative_rel_ = arm_target->rel_irelative_section(layout);
559 +
560 +      // Make sure we have a place for the TLSDESC relocations, in
561 +      // case we see any later on.
562 +      // this->rel_tlsdesc(layout);
563 +      if (parameters->doing_static_link())
564 +       {
565 +         // A statically linked executable will only have a .rel.plt section to
566 +         // hold R_ARM_IRELATIVE relocs for STT_GNU_IFUNC symbols.  The library
567 +         // will use these symbols to locate the IRELATIVE relocs at program
568 +         // startup time.
569 +         symtab->define_in_output_data("__rel_iplt_start", NULL,
570 +                                       Symbol_table::PREDEFINED,
571 +                                       this->irelative_rel_, 0, 0,
572 +                                       elfcpp::STT_NOTYPE, elfcpp::STB_GLOBAL,
573 +                                       elfcpp::STV_HIDDEN, 0, false, true);
574 +         symtab->define_in_output_data("__rel_iplt_end", NULL,
575 +                                       Symbol_table::PREDEFINED,
576 +                                       this->irelative_rel_, 0, 0,
577 +                                       elfcpp::STT_NOTYPE, elfcpp::STB_GLOBAL,
578 +                                       elfcpp::STV_HIDDEN, 0, true, true);
579 +       }
580 +    }
581 +  return this->irelative_rel_;
582 +}
583 +
584 +
585 +// Return the PLT address for a global symbol.
586 +
587 +template<bool big_endian>
588 +uint32_t
589 +Output_data_plt_arm<big_endian>::address_for_global(const Symbol* gsym) const
590 +{
591 +  uint64_t begin_offset = 0;
592 +  if (gsym->type() == elfcpp::STT_GNU_IFUNC
593 +      && gsym->can_use_relative_reloc(false))
594 +    {
595 +      begin_offset = (this->first_plt_entry_offset() +
596 +                     this->count_ * this->get_plt_entry_size());
597 +    }
598 +  return this->address() + begin_offset + gsym->plt_offset();
599 +}
600 +
601 +
602 +// Return the PLT address for a local symbol.  These are always
603 +// IRELATIVE relocs.
604 +
605 +template<bool big_endian>
606 +uint32_t
607 +Output_data_plt_arm<big_endian>::address_for_local(
608 +    const Relobj* object,
609 +    unsigned int r_sym) const
610 +{
611 +  return (this->address()
612 +         + this->first_plt_entry_offset()
613 +         + this->count_ * this->get_plt_entry_size()
614 +         + object->local_plt_offset(r_sym));
615 +}
616 +
617 +
618  template<bool big_endian>
619  class Output_data_plt_arm_standard : public Output_data_plt_arm<big_endian>
620  {
621   public:
622 -  Output_data_plt_arm_standard(Layout* layout, Output_data_space* got_plt)
623 -    : Output_data_plt_arm<big_endian>(layout, 4, got_plt)
624 +  Output_data_plt_arm_standard(Layout* layout,
625 +                              Arm_output_data_got<big_endian>* got,
626 +                              Output_data_space* got_plt,
627 +                              Output_data_space* got_irelative)
628 +    : Output_data_plt_arm<big_endian>(layout, 4, got, got_plt, got_irelative)
629    { }
630  
631   protected:
632 @@ -7485,8 +7825,11 @@ Output_data_plt_arm<big_endian>::do_write(Output_file* of)
633    unsigned char* const oview = of->get_output_view(offset, oview_size);
634  
635    const off_t got_file_offset = this->got_plt_->offset();
636 +  gold_assert(got_file_offset + this->got_plt_->data_size()
637 +             == this->got_irelative_->offset());
638    const section_size_type got_size =
639 -    convert_to_section_size_type(this->got_plt_->data_size());
640 +    convert_to_section_size_type(this->got_plt_->data_size()
641 +                                + this->got_irelative_->data_size());
642    unsigned char* const got_view = of->get_output_view(got_file_offset,
643                                                       got_size);
644    unsigned char* pov = oview;
645 @@ -7505,7 +7848,8 @@ Output_data_plt_arm<big_endian>::do_write(Output_file* of)
646  
647    unsigned int plt_offset = this->first_plt_entry_offset();
648    unsigned int got_offset = 12;
649 -  const unsigned int count = this->count_;
650 +  const unsigned int count = this->count_ + this->irelative_count_;
651 +  gold_assert(this->irelative_count_ == this->irelative_data_vec_.size());
652    for (unsigned int i = 0;
653         i < count;
654         ++i,
655 @@ -7518,8 +7862,33 @@ Output_data_plt_arm<big_endian>::do_write(Output_file* of)
656        this->fill_plt_entry(pov, got_address, plt_address,
657                            got_offset, plt_offset);
658  
659 -      // Set the entry in the GOT.
660 -      elfcpp::Swap<32, big_endian>::writeval(got_pov, plt_address);
661 +      Arm_address value;
662 +      if (i < this->count_)
663 +       {
664 +         // For non-irelative got entries, the value is the beginning of plt.
665 +         value = plt_address;
666 +       }
667 +      else
668 +       {
669 +         // For irelative got entries, the value is the (global/local) symbol
670 +         // address.
671 +         const IRelative_data& idata =
672 +             this->irelative_data_vec_[i - this->count_];
673 +         if (idata.symbol_is_global_)
674 +           {
675 +             // Set the entry in the GOT for irelative symbols.  The content is
676 +             // the address of the ifunc, not the address of plt start.
677 +             const Sized_symbol<32>* sized_symbol = idata.u_.global;
678 +             gold_assert(sized_symbol->type() == elfcpp::STT_GNU_IFUNC);
679 +             value = sized_symbol->value();
680 +           }
681 +         else
682 +           {
683 +             value = idata.u_.local.relobj->local_symbol_value(
684 +                 idata.u_.local.index, 0);
685 +           }
686 +       }
687 +      elfcpp::Swap<32, big_endian>::writeval(got_pov, value);
688      }
689  
690    gold_assert(static_cast<section_size_type>(pov - oview) == oview_size);
691 @@ -7529,6 +7898,7 @@ Output_data_plt_arm<big_endian>::do_write(Output_file* of)
692    of->write_output_view(got_file_offset, got_size, got_view);
693  }
694  
695 +
696  // Create a PLT entry for a global symbol.
697  
698  template<bool big_endian>
699 @@ -7540,20 +7910,58 @@ Target_arm<big_endian>::make_plt_entry(Symbol_table* symtab, Layout* layout,
700      return;
701  
702    if (this->plt_ == NULL)
703 +    this->make_plt_section(symtab, layout);
704 +
705 +  this->plt_->add_entry(symtab, layout, gsym);
706 +}
707 +
708 +
709 +// Create the PLT section.
710 +template<bool big_endian>
711 +void
712 +Target_arm<big_endian>::make_plt_section(
713 +  Symbol_table* symtab, Layout* layout)
714 +{
715 +  if (this->plt_ == NULL)
716      {
717 -      // Create the GOT sections first.
718 +      // Create the GOT section first.
719        this->got_section(symtab, layout);
720  
721 -      this->plt_ = this->make_data_plt(layout, this->got_plt_);
722 +      // GOT for irelatives is create along with got.plt.
723 +      gold_assert(this->got_ != NULL
724 +                 && this->got_plt_ != NULL
725 +                 && this->got_irelative_ != NULL);
726 +      this->plt_ = this->make_data_plt(layout, this->got_, this->got_plt_,
727 +                                      this->got_irelative_);
728  
729        layout->add_output_section_data(".plt", elfcpp::SHT_PROGBITS,
730                                       (elfcpp::SHF_ALLOC
731                                        | elfcpp::SHF_EXECINSTR),
732                                       this->plt_, ORDER_PLT, false);
733      }
734 -  this->plt_->add_entry(gsym);
735  }
736  
737 +
738 +// Make a PLT entry for a local STT_GNU_IFUNC symbol.
739 +
740 +template<bool big_endian>
741 +void
742 +Target_arm<big_endian>::make_local_ifunc_plt_entry(
743 +    Symbol_table* symtab, Layout* layout,
744 +    Sized_relobj_file<32, big_endian>* relobj,
745 +    unsigned int local_sym_index)
746 +{
747 +  if (relobj->local_has_plt_offset(local_sym_index))
748 +    return;
749 +  if (this->plt_ == NULL)
750 +    this->make_plt_section(symtab, layout);
751 +  unsigned int plt_offset = this->plt_->add_local_ifunc_entry(symtab, layout,
752 +                                                             relobj,
753 +                                                             local_sym_index);
754 +  relobj->set_local_plt_offset(local_sym_index, plt_offset);
755 +}
756 +
757 +
758  // Return the number of entries in the PLT.
759  
760  template<bool big_endian>
761 @@ -7823,6 +8231,7 @@ Target_arm<big_endian>::Scan::check_non_pic(Relobj* object,
762      case elfcpp::R_ARM_JUMP_SLOT:
763      case elfcpp::R_ARM_ABS32:
764      case elfcpp::R_ARM_ABS32_NOI:
765 +    case elfcpp::R_ARM_IRELATIVE:
766      case elfcpp::R_ARM_PC24:
767      // FIXME: The following 3 types are not supported by Android's dynamic
768      // linker.
769 @@ -7853,6 +8262,27 @@ Target_arm<big_endian>::Scan::check_non_pic(Relobj* object,
770      }
771  }
772  
773 +
774 +// Return whether we need to make a PLT entry for a relocation of the
775 +// given type against a STT_GNU_IFUNC symbol.
776 +
777 +template<bool big_endian>
778 +bool
779 +Target_arm<big_endian>::Scan::reloc_needs_plt_for_ifunc(
780 +    Sized_relobj_file<32, big_endian>* object,
781 +    unsigned int r_type)
782 +{
783 +  int flags = Scan::get_reference_flags(r_type);
784 +  if (flags & Symbol::TLS_REF)
785 +    {
786 +      gold_error(_("%s: unsupported TLS reloc %u for IFUNC symbol"),
787 +                object->name().c_str(), r_type);
788 +      return false;
789 +    }
790 +  return flags != 0;
791 +}
792 +
793 +
794  // Scan a relocation for a local symbol.
795  // FIXME: This only handles a subset of relocation types used by Android
796  // on ARM v5te devices.
797 @@ -7874,6 +8304,15 @@ Target_arm<big_endian>::Scan::local(Symbol_table* symtab,
798      return;
799  
800    r_type = get_real_reloc_type(r_type);
801 +
802 +  // A local STT_GNU_IFUNC symbol may require a PLT entry.
803 +  bool is_ifunc = lsym.get_st_type() == elfcpp::STT_GNU_IFUNC;
804 +  if (is_ifunc && this->reloc_needs_plt_for_ifunc(object, r_type))
805 +    {
806 +      unsigned int r_sym = elfcpp::elf_r_sym<32>(reloc.get_r_info());
807 +      target->make_local_ifunc_plt_entry(symtab, layout, object, r_sym);
808 +    }
809 +
810    switch (r_type)
811      {
812      case elfcpp::R_ARM_NONE:
813 @@ -7898,7 +8337,7 @@ Target_arm<big_endian>::Scan::local(Symbol_table* symtab,
814           // we need to add check_non_pic(object, r_type) here.
815           rel_dyn->add_local_relative(object, r_sym, elfcpp::R_ARM_RELATIVE,
816                                       output_section, data_shndx,
817 -                                     reloc.get_r_offset());
818 +                                     reloc.get_r_offset(), is_ifunc);
819         }
820        break;
821  
822 @@ -8265,6 +8704,11 @@ Target_arm<big_endian>::Scan::global(Symbol_table* symtab,
823        && strcmp(gsym->name(), "_GLOBAL_OFFSET_TABLE_") == 0)
824      target->got_section(symtab, layout);
825  
826 +  // A STT_GNU_IFUNC symbol may require a PLT entry.
827 +  if (gsym->type() == elfcpp::STT_GNU_IFUNC
828 +      && this->reloc_needs_plt_for_ifunc(object, r_type))
829 +    target->make_plt_entry(symtab, layout, gsym);
830 +
831    r_type = get_real_reloc_type(r_type);
832    switch (r_type)
833      {
834 @@ -8309,6 +8753,24 @@ Target_arm<big_endian>::Scan::global(Symbol_table* symtab,
835               }
836             else if ((r_type == elfcpp::R_ARM_ABS32
837                       || r_type == elfcpp::R_ARM_ABS32_NOI)
838 +                    && gsym->type() == elfcpp::STT_GNU_IFUNC
839 +                    && gsym->can_use_relative_reloc(false)
840 +                    && !gsym->is_from_dynobj()
841 +                    && !gsym->is_undefined()
842 +                    && !gsym->is_preemptible())
843 +             {
844 +               // Use an IRELATIVE reloc for a locally defined STT_GNU_IFUNC
845 +               // symbol. This makes a function address in a PIE executable
846 +               // match the address in a shared library that it links against.
847 +               Reloc_section* rel_irelative =
848 +                   target->rel_irelative_section(layout);
849 +               unsigned int r_type = elfcpp::R_ARM_IRELATIVE;
850 +               rel_irelative->add_symbolless_global_addend(
851 +                   gsym, r_type, output_section, object,
852 +                   data_shndx, reloc.get_r_offset());
853 +             }
854 +           else if ((r_type == elfcpp::R_ARM_ABS32
855 +                     || r_type == elfcpp::R_ARM_ABS32_NOI)
856                      && gsym->can_use_relative_reloc(false))
857               {
858                 Reloc_section* rel_dyn = target->rel_dyn_section(layout);
859 @@ -8442,7 +8904,13 @@ Target_arm<big_endian>::Scan::global(Symbol_table* symtab,
860         Arm_output_data_got<big_endian>* got =
861           target->got_section(symtab, layout);
862         if (gsym->final_value_is_known())
863 -         got->add_global(gsym, GOT_TYPE_STANDARD);
864 +         {
865 +           // For a STT_GNU_IFUNC symbol we want the PLT address.
866 +           if (gsym->type() == elfcpp::STT_GNU_IFUNC)
867 +             got->add_global_plt(gsym, GOT_TYPE_STANDARD);
868 +           else
869 +             got->add_global(gsym, GOT_TYPE_STANDARD);
870 +         }
871         else
872           {
873             // If this symbol is not fully resolved, we need to add a
874 @@ -8452,12 +8920,29 @@ Target_arm<big_endian>::Scan::global(Symbol_table* symtab,
875                 || gsym->is_undefined()
876                 || gsym->is_preemptible()
877                 || (gsym->visibility() == elfcpp::STV_PROTECTED
878 -                   && parameters->options().shared()))
879 +                   && parameters->options().shared())
880 +               || (gsym->type() == elfcpp::STT_GNU_IFUNC
881 +                   && parameters->options().output_is_position_independent()))
882               got->add_global_with_rel(gsym, GOT_TYPE_STANDARD,
883                                        rel_dyn, elfcpp::R_ARM_GLOB_DAT);
884             else
885               {
886 -               if (got->add_global(gsym, GOT_TYPE_STANDARD))
887 +               // For a STT_GNU_IFUNC symbol we want to write the PLT
888 +               // offset into the GOT, so that function pointer
889 +               // comparisons work correctly.
890 +               bool is_new;
891 +               if (gsym->type() != elfcpp::STT_GNU_IFUNC)
892 +                 is_new = got->add_global(gsym, GOT_TYPE_STANDARD);
893 +               else
894 +                 {
895 +                   is_new = got->add_global_plt(gsym, GOT_TYPE_STANDARD);
896 +                   // Tell the dynamic linker to use the PLT address
897 +                   // when resolving relocations.
898 +                   if (gsym->is_from_dynobj()
899 +                       && !parameters->options().shared())
900 +                     gsym->set_needs_dynsym_value();
901 +                 }
902 +               if (is_new)
903                   rel_dyn->add_global_relative(
904                       gsym, elfcpp::R_ARM_RELATIVE, got,
905                       gsym->got_offset(GOT_TYPE_STANDARD));
906 @@ -8919,8 +9404,7 @@ Target_arm<big_endian>::Relocate::relocate(
907           if (gsym->use_plt_offset(Scan::get_reference_flags(r_type)))
908             {
909               // This uses a PLT, change the symbol value.
910 -             symval.set_output_value(target->plt_section()->address()
911 -                                     + gsym->plt_offset());
912 +             symval.set_output_value(target->plt_address_for_global(gsym));
913               psymval = &symval;
914             }
915           else if (gsym->is_weak_undefined())
916 @@ -8958,6 +9442,13 @@ Target_arm<big_endian>::Relocate::relocate(
917           elfcpp::Elf_types<32>::Elf_WXword r_info = rel.get_r_info();
918           unsigned int r_sym = elfcpp::elf_r_sym<32>(r_info);
919           thumb_bit = object->local_symbol_is_thumb_function(r_sym) ? 1 : 0;
920 +
921 +         if (psymval->is_ifunc_symbol() && object->local_has_plt_offset(r_sym))
922 +           {
923 +             symval.set_output_value(
924 +                 target->plt_address_for_local(object, r_sym));
925 +             psymval = &symval;
926 +           }
927         }
928      }
929    else
930 @@ -9936,7 +10427,7 @@ uint64_t
931  Target_arm<big_endian>::do_dynsym_value(const Symbol* gsym) const
932  {
933    gold_assert(gsym->is_from_dynobj() && gsym->has_plt_offset());
934 -  return this->plt_section()->address() + gsym->plt_offset();
935 +  return this->plt_address_for_global(gsym);
936  }
937  
938  // Map platform-specific relocs to real relocs
939 @@ -11083,8 +11574,7 @@ Target_arm<big_endian>::scan_reloc_for_stub(
940        if (gsym->use_plt_offset(Scan::get_reference_flags(r_type)))
941         {
942           // This uses a PLT, change the symbol value.
943 -         symval.set_output_value(this->plt_section()->address()
944 -                                 + gsym->plt_offset());
945 +         symval.set_output_value(this->plt_address_for_global(gsym));
946           psymval = &symval;
947           target_is_thumb = false;
948         }
949 @@ -12187,8 +12677,13 @@ class Target_arm_nacl : public Target_arm<big_endian>
950  
951   protected:
952    virtual Output_data_plt_arm<big_endian>*
953 -  do_make_data_plt(Layout* layout, Output_data_space* got_plt)
954 -  { return new Output_data_plt_arm_nacl<big_endian>(layout, got_plt); }
955 +  do_make_data_plt(
956 +                  Layout* layout,
957 +                  Arm_output_data_got<big_endian>* got,
958 +                  Output_data_space* got_plt,
959 +                  Output_data_space* got_irelative)
960 +  { return new Output_data_plt_arm_nacl<big_endian>(
961 +      layout, got, got_plt, got_irelative); }
962  
963   private:
964    static const Target::Target_info arm_nacl_info;
965 @@ -12225,8 +12720,12 @@ template<bool big_endian>
966  class Output_data_plt_arm_nacl : public Output_data_plt_arm<big_endian>
967  {
968   public:
969 -  Output_data_plt_arm_nacl(Layout* layout, Output_data_space* got_plt)
970 -    : Output_data_plt_arm<big_endian>(layout, 16, got_plt)
971 +  Output_data_plt_arm_nacl(
972 +      Layout* layout,
973 +      Arm_output_data_got<big_endian>* got,
974 +      Output_data_space* got_plt,
975 +      Output_data_space* got_irelative)
976 +    : Output_data_plt_arm<big_endian>(layout, 16, got, got_plt, got_irelative)
977    { }
978  
979   protected:
980 diff --git a/gold/output.h b/gold/output.h
981 index ba0cdaa..599c2b7 100644
982 --- a/gold/output.h
983 +++ b/gold/output.h
984 @@ -1714,6 +1714,17 @@ class Output_data_reloc<elfcpp::SHT_REL, dynamic, size, big_endian>
985                                     address, true, true, false, false));
986    }
987  
988 +  void
989 +  add_local_relative(Sized_relobj<size, big_endian>* relobj,
990 +                    unsigned int local_sym_index, unsigned int type,
991 +                    Output_data* od, unsigned int shndx, Address address,
992 +                    bool use_plt_offset)
993 +  {
994 +    this->add(od, Output_reloc_type(relobj, local_sym_index, type, shndx,
995 +                                   address, true, true, false,
996 +                                   use_plt_offset));
997 +  }
998 +
999    // Add a local relocation which does not use a symbol for the relocation,
1000    // but which gets its addend from a symbol.
1001  
1002 -- 
1003 2.7.0
1004