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.
6 This is a feature required in chromeos arm development work.
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
18 Upstream-Status: Backport
19 Signed-off-by: Khem Raj <raj.khem@gmail.com>
22 gold/aarch64.cc | 2 +-
23 gold/arm.cc | 593 +++++++++++++++++++++++++++++++++++++++++++++++++++-----
25 4 files changed, 561 insertions(+), 50 deletions(-)
27 diff --git a/elfcpp/arm.h b/elfcpp/arm.h
28 index 8c6b6bf..1c13dc9 100644
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
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
50 @@ -1226,7 +1226,7 @@ class Output_data_plt_aarch64 : public Output_section_data
51 // The number of PLT entries.
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_;
59 diff --git a/gold/arm.cc b/gold/arm.cc
60 index 6c472bb..8719cc9 100644
63 @@ -2119,8 +2119,8 @@ class Target_arm : public Sized_target<32, big_endian>
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>
76 do_dynsym_value(const Symbol*) const;
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.
81 + do_plt_address_for_global(const Symbol* gsym) const
82 + { return this->plt_section()->address_for_global(gsym); }
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.
87 + do_plt_address_for_local(const Relobj* relobj, unsigned int symndx) const
88 + { return this->plt_section()->address_for_local(relobj, symndx); }
90 // Relocate a section.
92 relocate_section(const Relocate_info<32, big_endian>*,
93 @@ -2357,6 +2369,10 @@ class Target_arm : public Sized_target<32, big_endian>
95 plt_entry_size() const;
97 + // Get the section to use for IRELATIVE relocations, create it if necessary.
99 + rel_irelative_section(Layout*);
101 // Map platform-specific reloc types
103 get_real_reloc_type(unsigned int r_type);
104 @@ -2448,8 +2464,11 @@ class Target_arm : public Sized_target<32, big_endian>
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); }
116 // Make an ELF object.
118 @@ -2530,9 +2549,14 @@ class Target_arm : public Sized_target<32, big_endian>
119 do_define_standard_symbols(Symbol_table*, Layout*);
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)
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);
135 @@ -2602,6 +2626,9 @@ class Target_arm : public Sized_target<32, big_endian>
136 if (sym->is_undefined() && !parameters->options().shared())
139 + if (sym->type() == elfcpp::STT_GNU_IFUNC)
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>
147 possible_function_pointer_reloc(unsigned int r_type);
149 + // Whether a plt entry is needed for ifunc.
151 + reloc_needs_plt_for_ifunc(Sized_relobj_file<32, big_endian>*,
152 + unsigned int r_type);
154 // Whether we have issued an error about a non-PIC compilation.
155 bool issued_non_pic_error_;
157 @@ -2718,10 +2750,20 @@ class Target_arm : public Sized_target<32, big_endian>
158 return this->got_plt_;
161 + // Create the PLT section.
163 + make_plt_section(Symbol_table* symtab, Layout* layout);
165 // Create a PLT entry for a global symbol.
167 make_plt_entry(Symbol_table*, Layout*, Symbol*);
169 + // Create a PLT entry for a local STT_GNU_IFUNC symbol.
171 + make_local_ifunc_plt_entry(Symbol_table*, Layout*,
172 + Sized_relobj_file<32, big_endian>* relobj,
173 + unsigned int local_sym_index);
175 // Define the _TLS_MODULE_BASE_ symbol in the TLS segment.
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)
193 elfcpp::STV_HIDDEN, 0,
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);
207 @@ -4257,14 +4312,43 @@ Target_arm<big_endian>::rel_dyn_section(Layout* layout)
208 if (this->rel_dyn_ == NULL)
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);
222 return this->rel_dyn_;
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.
231 +template<bool big_endian>
232 +typename Target_arm<big_endian>::Reloc_section*
233 +Target_arm<big_endian>::rel_irelative_section(Layout* layout)
235 + if (this->rel_irelative_ == NULL)
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()));
244 + return this->rel_irelative_;
248 // Insn_template methods.
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
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.
263 + struct IRelative_data
265 + IRelative_data(Sized_symbol<32>* sized_symbol)
266 + : symbol_is_global_(true)
268 + u_.global = sized_symbol;
271 + IRelative_data(Sized_relobj_file<32, big_endian>* relobj,
272 + unsigned int index)
273 + : symbol_is_global_(false)
275 + u_.local.relobj = relobj;
276 + u_.local.index = index;
281 + Sized_symbol<32>* global;
285 + Sized_relobj_file<32, big_endian>* relobj;
286 + unsigned int index;
290 + bool symbol_is_global_;
293 typedef Output_data_reloc<elfcpp::SHT_REL, true, 32, big_endian>
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);
302 // Add an entry to the PLT.
304 - add_entry(Symbol* gsym);
305 + add_entry(Symbol_table* symtab, Layout* layout, Symbol* gsym);
307 + // Add the relocation for a plt entry.
309 + add_relocation(Symbol_table* symtab, Layout* layout,
310 + Symbol* gsym, unsigned int got_offset);
312 + // Add an entry to the PLT for a local STT_GNU_IFUNC symbol.
314 + add_local_ifunc_entry(Symbol_table* symtab, Layout*,
315 + Sized_relobj_file<32, big_endian>* relobj,
316 + unsigned int local_sym_index);
318 // Return the .rel.plt section data.
321 { return this->rel_; }
323 + // Return the PLT relocation container for IRELATIVE.
325 + rel_irelative(Symbol_table*, Layout*);
327 // Return the number of PLT entries.
330 - { return this->count_; }
331 + { return this->count_ + this->irelative_count_; }
333 // Return the offset of the first non-reserved PLT entry.
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(); }
339 + // Return the PLT address for globals.
341 + address_for_global(const Symbol*) const;
343 + // Return the PLT address for locals.
345 + address_for_local(const Relobj*, unsigned int symndx) const;
348 // Fill in the first PLT entry.
350 @@ -7298,19 +7446,37 @@ class Output_data_plt_arm : public Output_section_data
351 set_final_data_size()
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()));
359 // Write out the PLT data.
361 do_write(Output_file*);
363 + // Record irelative symbol data.
364 + void insert_irelative_data(const IRelative_data& idata)
365 + { irelative_data_vec_.push_back(idata); }
367 // The reloc section.
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.
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_;
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.
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)
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)
410 template<bool big_endian>
412 -Output_data_plt_arm<big_endian>::add_entry(Symbol* gsym)
413 +Output_data_plt_arm<big_endian>::add_entry(Symbol_table* symtab,
417 gold_assert(!gsym->has_plt_offset());
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;
426 + // We have 2 different types of plt entry here, normal and ifunc.
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.
431 + // For ifunc plt, the offset begins with 0. So the first offset would 0,
432 + // second 12, third 24 ... etc.
434 + // IFunc plt entries *always* come after *normal* plt entries.
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.
440 + int begin_offset = 0;
441 + if (gsym->type() == elfcpp::STT_GNU_IFUNC
442 + && gsym->can_use_relative_reloc(false))
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.
449 + // Record symbol information.
450 + this->insert_irelative_data(
451 + IRelative_data(symtab->get_sized_symbol<32>(gsym)));
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();
462 + gsym->set_plt_offset(begin_offset
463 + + (*entry_count) * this->get_plt_entry_size());
468 - section_offset_type got_offset = this->got_plt_->current_data_size();
469 + section_offset_type got_offset = got->current_data_size();
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);
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_,
481 + this->add_relocation(symtab, layout, gsym, got_offset);
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.
488 +// Add an entry to the PLT for a local STT_GNU_IFUNC symbol. Return
491 +template<bool big_endian>
493 +Output_data_plt_arm<big_endian>::add_local_ifunc_entry(
494 + Symbol_table* symtab,
496 + Sized_relobj_file<32, big_endian>* relobj,
497 + unsigned int local_sym_index)
499 + this->insert_irelative_data(IRelative_data(relobj, local_sym_index));
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_;
506 + section_offset_type got_offset = this->got_irelative_->current_data_size();
508 + // Every PLT entry needs a GOT entry which points back to the PLT
510 + this->got_irelative_->set_current_data_size(got_offset + 4);
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);
522 +// Add the relocation for a PLT entry.
524 +template<bool big_endian>
526 +Output_data_plt_arm<big_endian>::add_relocation(
527 + Symbol_table* symtab, Layout* layout, Symbol* gsym, unsigned int got_offset)
529 + if (gsym->type() == elfcpp::STT_GNU_IFUNC
530 + && gsym->can_use_relative_reloc(false))
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);
538 + gsym->set_needs_dynsym_entry();
539 + this->rel_->add_global(gsym, elfcpp::R_ARM_JUMP_SLOT, this->got_plt_,
545 +// Create the irelative relocation data.
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,
552 + if (this->irelative_rel_ == NULL)
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);
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())
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
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);
581 + return this->irelative_rel_;
585 +// Return the PLT address for a global symbol.
587 +template<bool big_endian>
589 +Output_data_plt_arm<big_endian>::address_for_global(const Symbol* gsym) const
591 + uint64_t begin_offset = 0;
592 + if (gsym->type() == elfcpp::STT_GNU_IFUNC
593 + && gsym->can_use_relative_reloc(false))
595 + begin_offset = (this->first_plt_entry_offset() +
596 + this->count_ * this->get_plt_entry_size());
598 + return this->address() + begin_offset + gsym->plt_offset();
602 +// Return the PLT address for a local symbol. These are always
603 +// IRELATIVE relocs.
605 +template<bool big_endian>
607 +Output_data_plt_arm<big_endian>::address_for_local(
608 + const Relobj* object,
609 + unsigned int r_sym) const
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));
618 template<bool big_endian>
619 class Output_data_plt_arm_standard : public Output_data_plt_arm<big_endian>
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)
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);
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,
644 unsigned char* pov = oview;
645 @@ -7505,7 +7848,8 @@ Output_data_plt_arm<big_endian>::do_write(Output_file* of)
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;
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);
659 - // Set the entry in the GOT.
660 - elfcpp::Swap<32, big_endian>::writeval(got_pov, plt_address);
662 + if (i < this->count_)
664 + // For non-irelative got entries, the value is the beginning of plt.
665 + value = plt_address;
669 + // For irelative got entries, the value is the (global/local) symbol
671 + const IRelative_data& idata =
672 + this->irelative_data_vec_[i - this->count_];
673 + if (idata.symbol_is_global_)
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();
683 + value = idata.u_.local.relobj->local_symbol_value(
684 + idata.u_.local.index, 0);
687 + elfcpp::Swap<32, big_endian>::writeval(got_pov, value);
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);
696 // Create a PLT entry for a global symbol.
698 template<bool big_endian>
699 @@ -7540,20 +7910,58 @@ Target_arm<big_endian>::make_plt_entry(Symbol_table* symtab, Layout* layout,
702 if (this->plt_ == NULL)
703 + this->make_plt_section(symtab, layout);
705 + this->plt_->add_entry(symtab, layout, gsym);
709 +// Create the PLT section.
710 +template<bool big_endian>
712 +Target_arm<big_endian>::make_plt_section(
713 + Symbol_table* symtab, Layout* layout)
715 + if (this->plt_ == NULL)
717 - // Create the GOT sections first.
718 + // Create the GOT section first.
719 this->got_section(symtab, layout);
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_);
729 layout->add_output_section_data(".plt", elfcpp::SHT_PROGBITS,
731 | elfcpp::SHF_EXECINSTR),
732 this->plt_, ORDER_PLT, false);
734 - this->plt_->add_entry(gsym);
738 +// Make a PLT entry for a local STT_GNU_IFUNC symbol.
740 +template<bool big_endian>
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)
747 + if (relobj->local_has_plt_offset(local_sym_index))
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,
754 + relobj->set_local_plt_offset(local_sym_index, plt_offset);
758 // Return the number of entries in the PLT.
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
769 @@ -7853,6 +8262,27 @@ Target_arm<big_endian>::Scan::check_non_pic(Relobj* object,
774 +// Return whether we need to make a PLT entry for a relocation of the
775 +// given type against a STT_GNU_IFUNC symbol.
777 +template<bool big_endian>
779 +Target_arm<big_endian>::Scan::reloc_needs_plt_for_ifunc(
780 + Sized_relobj_file<32, big_endian>* object,
781 + unsigned int r_type)
783 + int flags = Scan::get_reference_flags(r_type);
784 + if (flags & Symbol::TLS_REF)
786 + gold_error(_("%s: unsupported TLS reloc %u for IFUNC symbol"),
787 + object->name().c_str(), r_type);
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,
800 r_type = get_real_reloc_type(r_type);
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))
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);
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);
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);
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);
831 r_type = get_real_reloc_type(r_type);
834 @@ -8309,6 +8753,24 @@ Target_arm<big_endian>::Scan::global(Symbol_table* symtab,
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())
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());
854 + else if ((r_type == elfcpp::R_ARM_ABS32
855 + || r_type == elfcpp::R_ARM_ABS32_NOI)
856 && gsym->can_use_relative_reloc(false))
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);
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);
869 + got->add_global(gsym, GOT_TYPE_STANDARD);
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);
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.
891 + if (gsym->type() != elfcpp::STT_GNU_IFUNC)
892 + is_new = got->add_global(gsym, GOT_TYPE_STANDARD);
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();
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)))
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));
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;
921 + if (psymval->is_ifunc_symbol() && object->local_has_plt_offset(r_sym))
923 + symval.set_output_value(
924 + target->plt_address_for_local(object, r_sym));
930 @@ -9936,7 +10427,7 @@ uint64_t
931 Target_arm<big_endian>::do_dynsym_value(const Symbol* gsym) const
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);
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)))
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));
947 target_is_thumb = false;
949 @@ -12187,8 +12677,13 @@ class Target_arm_nacl : public Target_arm<big_endian>
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); }
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); }
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>
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(
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)
980 diff --git a/gold/output.h b/gold/output.h
981 index ba0cdaa..599c2b7 100644
984 @@ -1714,6 +1714,17 @@ class Output_data_reloc<elfcpp::SHT_REL, dynamic, size, big_endian>
985 address, true, true, false, false));
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)
994 + this->add(od, Output_reloc_type(relobj, local_sym_index, type, shndx,
995 + address, true, true, false,
999 // Add a local relocation which does not use a symbol for the relocation,
1000 // but which gets its addend from a symbol.