Subversion Repositories Kolibri OS

Rev

Go to most recent revision | Details | Last modification | View Log | RSS feed

Rev Author Line No. Line
5197 serge 1
/* ELF STT_GNU_IFUNC support.
2
   Copyright 2009-2013
3
   Free Software Foundation, Inc.
4
 
5
   This file is part of BFD, the Binary File Descriptor library.
6
 
7
   This program is free software; you can redistribute it and/or modify
8
   it under the terms of the GNU General Public License as published by
9
   the Free Software Foundation; either version 3 of the License, or
10
   (at your option) any later version.
11
 
12
   This program is distributed in the hope that it will be useful,
13
   but WITHOUT ANY WARRANTY; without even the implied warranty of
14
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15
   GNU General Public License for more details.
16
 
17
   You should have received a copy of the GNU General Public License
18
   along with this program; if not, write to the Free Software
19
   Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston,
20
   MA 02110-1301, USA.  */
21
 
22
#include "sysdep.h"
23
#include "bfd.h"
24
#include "bfdlink.h"
25
#include "libbfd.h"
26
#define ARCH_SIZE 0
27
#include "elf-bfd.h"
28
#include "safe-ctype.h"
29
#include "libiberty.h"
30
#include "objalloc.h"
31
 
32
/* Create sections needed by STT_GNU_IFUNC symbol.  */
33
 
34
bfd_boolean
35
_bfd_elf_create_ifunc_sections (bfd *abfd, struct bfd_link_info *info)
36
{
37
  flagword flags, pltflags;
38
  asection *s;
39
  const struct elf_backend_data *bed = get_elf_backend_data (abfd);
40
  struct elf_link_hash_table *htab = elf_hash_table (info);
41
 
42
  if (htab->irelifunc != NULL || htab->iplt != NULL)
43
    return TRUE;
44
 
45
  flags = bed->dynamic_sec_flags;
46
  pltflags = flags;
47
  if (bed->plt_not_loaded)
48
    /* We do not clear SEC_ALLOC here because we still want the OS to
49
       allocate space for the section; it's just that there's nothing
50
       to read in from the object file.  */
51
    pltflags &= ~ (SEC_CODE | SEC_LOAD | SEC_HAS_CONTENTS);
52
  else
53
    pltflags |= SEC_ALLOC | SEC_CODE | SEC_LOAD;
54
  if (bed->plt_readonly)
55
    pltflags |= SEC_READONLY;
56
 
57
  if (info->shared)
58
    {
59
      /* We need to create .rel[a].ifunc for shared objects.  */
60
      const char *rel_sec = (bed->rela_plts_and_copies_p
61
			     ? ".rela.ifunc" : ".rel.ifunc");
62
 
63
      s = bfd_make_section_with_flags (abfd, rel_sec,
64
				       flags | SEC_READONLY);
65
      if (s == NULL
66
	  || ! bfd_set_section_alignment (abfd, s,
67
					  bed->s->log_file_align))
68
	return FALSE;
69
      htab->irelifunc = s;
70
    }
71
  else
72
    {
73
      /* We need to create .iplt, .rel[a].iplt, .igot and .igot.plt
74
	 for static executables.   */
75
      s = bfd_make_section_with_flags (abfd, ".iplt", pltflags);
76
      if (s == NULL
77
	  || ! bfd_set_section_alignment (abfd, s, bed->plt_alignment))
78
	return FALSE;
79
      htab->iplt = s;
80
 
81
      s = bfd_make_section_with_flags (abfd,
82
				       (bed->rela_plts_and_copies_p
83
					? ".rela.iplt" : ".rel.iplt"),
84
				       flags | SEC_READONLY);
85
      if (s == NULL
86
	  || ! bfd_set_section_alignment (abfd, s,
87
					  bed->s->log_file_align))
88
	return FALSE;
89
      htab->irelplt = s;
90
 
91
      /* We don't need the .igot section if we have the .igot.plt
92
	 section.  */
93
      if (bed->want_got_plt)
94
	s = bfd_make_section_with_flags (abfd, ".igot.plt", flags);
95
      else
96
	s = bfd_make_section_with_flags (abfd, ".igot", flags);
97
      if (s == NULL
98
	  || !bfd_set_section_alignment (abfd, s,
99
					 bed->s->log_file_align))
100
	return FALSE;
101
      htab->igotplt = s;
102
    }
103
 
104
  return TRUE;
105
}
106
 
107
/* Allocate space in .plt, .got and associated reloc sections for
108
   dynamic relocs against a STT_GNU_IFUNC symbol definition.  */
109
 
110
bfd_boolean
111
_bfd_elf_allocate_ifunc_dyn_relocs (struct bfd_link_info *info,
112
				    struct elf_link_hash_entry *h,
113
				    struct elf_dyn_relocs **head,
114
				    unsigned int plt_entry_size,
115
				    unsigned int plt_header_size,
116
				    unsigned int got_entry_size)
117
{
118
  asection *plt, *gotplt, *relplt;
119
  struct elf_dyn_relocs *p;
120
  unsigned int sizeof_reloc;
121
  const struct elf_backend_data *bed;
122
  struct elf_link_hash_table *htab;
123
 
124
  /* When a shared library references a STT_GNU_IFUNC symbol defined
125
     in executable, the address of the resolved function may be used.
126
     But in non-shared executable, the address of its .plt slot may
127
     be used.  Pointer equality may not work correctly.  PIE should
128
     be used if pointer equality is required here.  */
129
  if (!info->shared
130
      && (h->dynindx != -1
131
	  || info->export_dynamic)
132
      && h->pointer_equality_needed)
133
    {
134
      info->callbacks->einfo
135
	(_("%F%P: dynamic STT_GNU_IFUNC symbol `%s' with pointer "
136
	   "equality in `%B' can not be used when making an "
137
	   "executable; recompile with -fPIE and relink with -pie\n"),
138
	 h->root.root.string,
139
	 h->root.u.def.section->owner);
140
      bfd_set_error (bfd_error_bad_value);
141
      return FALSE;
142
    }
143
 
144
  htab = elf_hash_table (info);
145
 
146
  /* When building shared library, we need to handle the case where it is
147
     marked with regular reference, but not non-GOT reference since the
148
     non-GOT reference bit may not be set here.  */
149
  if (info->shared && !h->non_got_ref && h->ref_regular)
150
    for (p = *head; p != NULL; p = p->next)
151
      if (p->count)
152
	{
153
	  h->non_got_ref = 1;
154
	  goto keep;
155
	}
156
 
157
  /* Support garbage collection against STT_GNU_IFUNC symbols.  */
158
  if (h->plt.refcount <= 0 && h->got.refcount <= 0)
159
    {
160
      h->got = htab->init_got_offset;
161
      h->plt = htab->init_plt_offset;
162
      *head = NULL;
163
      return TRUE;
164
    }
165
 
166
  /* Return and discard space for dynamic relocations against it if
167
     it is never referenced in a non-shared object.  */
168
  if (!h->ref_regular)
169
    {
170
      if (h->plt.refcount > 0
171
	  || h->got.refcount > 0)
172
	abort ();
173
      h->got = htab->init_got_offset;
174
      h->plt = htab->init_plt_offset;
175
      *head = NULL;
176
      return TRUE;
177
    }
178
 
179
keep:
180
  bed = get_elf_backend_data (info->output_bfd);
181
  if (bed->rela_plts_and_copies_p)
182
    sizeof_reloc = bed->s->sizeof_rela;
183
  else
184
    sizeof_reloc = bed->s->sizeof_rel;
185
 
186
  /* When building a static executable, use .iplt, .igot.plt and
187
     .rel[a].iplt sections for STT_GNU_IFUNC symbols.  */
188
  if (htab->splt != NULL)
189
    {
190
      plt = htab->splt;
191
      gotplt = htab->sgotplt;
192
      relplt = htab->srelplt;
193
 
194
      /* If this is the first .plt entry, make room for the special
195
	 first entry.  */
196
      if (plt->size == 0)
197
	plt->size += plt_header_size;
198
    }
199
  else
200
    {
201
      plt = htab->iplt;
202
      gotplt = htab->igotplt;
203
      relplt = htab->irelplt;
204
    }
205
 
206
  /* Don't update value of STT_GNU_IFUNC symbol to PLT.  We need
207
     the original value for R_*_IRELATIVE.  */
208
  h->plt.offset = plt->size;
209
 
210
  /* Make room for this entry in the .plt/.iplt section.  */
211
  plt->size += plt_entry_size;
212
 
213
  /* We also need to make an entry in the .got.plt/.got.iplt section,
214
     which will be placed in the .got section by the linker script.  */
215
  gotplt->size += got_entry_size;
216
 
217
  /* We also need to make an entry in the .rel[a].plt/.rel[a].iplt
218
     section.  */
219
  relplt->size += sizeof_reloc;
220
  relplt->reloc_count++;
221
 
222
  /* We need dynamic relocation for STT_GNU_IFUNC symbol only when
223
     there is a non-GOT reference in a shared object.  */
224
  if (!info->shared
225
      || !h->non_got_ref)
226
    *head = NULL;
227
 
228
  /* Finally, allocate space.  */
229
  p = *head;
230
  if (p != NULL)
231
    {
232
      bfd_size_type count = 0;
233
      do
234
	{
235
	  count += p->count;
236
	  p = p->next;
237
	}
238
      while (p != NULL);
239
      htab->irelifunc->size += count * sizeof_reloc;
240
    }
241
 
242
  /* For STT_GNU_IFUNC symbol, .got.plt has the real function address
243
     and .got has the PLT entry adddress.  We will load the GOT entry
244
     with the PLT entry in finish_dynamic_symbol if it is used.  For
245
     branch, it uses .got.plt.  For symbol value,
246
     1. Use .got.plt in a shared object if it is forced local or not
247
     dynamic.
248
     2. Use .got.plt in a non-shared object if pointer equality isn't
249
     needed.
250
     3. Use .got.plt in PIE.
251
     4. Use .got.plt if .got isn't used.
252
     5. Otherwise use .got so that it can be shared among different
253
     objects at run-time.
254
     We only need to relocate .got entry in shared object.  */
255
  if (h->got.refcount <= 0
256
      || (info->shared
257
	  && (h->dynindx == -1
258
	      || h->forced_local))
259
      || (!info->shared
260
	  && !h->pointer_equality_needed)
261
      || (info->executable && info->shared)
262
      || htab->sgot == NULL)
263
    {
264
      /* Use .got.plt.  */
265
      h->got.offset = (bfd_vma) -1;
266
    }
267
  else
268
    {
269
      h->got.offset = htab->sgot->size;
270
      htab->sgot->size += got_entry_size;
271
      if (info->shared)
272
	htab->srelgot->size += sizeof_reloc;
273
    }
274
 
275
  return TRUE;
276
}