Update.
[platform/upstream/glibc.git] / elf / dl-reloc.c
1 /* Relocate a shared object and resolve its references to other loaded objects.
2    Copyright (C) 1995,96,97,98,99,2000,2001,2002 Free Software Foundation, Inc.
3    This file is part of the GNU C Library.
4
5    The GNU C Library is free software; you can redistribute it and/or
6    modify it under the terms of the GNU Lesser General Public
7    License as published by the Free Software Foundation; either
8    version 2.1 of the License, or (at your option) any later version.
9
10    The GNU C Library is distributed in the hope that it will be useful,
11    but WITHOUT ANY WARRANTY; without even the implied warranty of
12    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13    Lesser General Public License for more details.
14
15    You should have received a copy of the GNU Lesser General Public
16    License along with the GNU C Library; if not, write to the Free
17    Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
18    02111-1307 USA.  */
19
20 #include <errno.h>
21 #include <libintl.h>
22 #include <stdlib.h>
23 #include <unistd.h>
24 #include <ldsodefs.h>
25 #include <sys/mman.h>
26 #include <sys/param.h>
27 #include <sys/types.h>
28 #include "dynamic-link.h"
29
30 /* Statistics function.  */
31 #ifdef SHARED
32 # define bump_num_cache_relocations() ++GL(dl_num_cache_relocations)
33 #else
34 # define bump_num_cache_relocations() ((void) 0)
35 #endif
36
37
38 void
39 _dl_relocate_object (struct link_map *l, struct r_scope_elem *scope[],
40                      int lazy, int consider_profiling)
41 {
42   struct textrels
43   {
44     caddr_t start;
45     size_t len;
46     int prot;
47     struct textrels *next;
48   } *textrels = NULL;
49   /* Initialize it to make the compiler happy.  */
50   const char *errstring = NULL;
51
52   if (l->l_relocated)
53     return;
54
55   /* If DT_BIND_NOW is set relocate all references in this object.  We
56      do not do this if we are profiling, of course.  */
57   if (!consider_profiling
58       && __builtin_expect (l->l_info[DT_BIND_NOW] != NULL, 0))
59     lazy = 0;
60
61   if (__builtin_expect (GL(dl_debug_mask) & DL_DEBUG_RELOC, 0))
62     INT(_dl_debug_printf) ("\nrelocation processing: %s%s\n",
63                            l->l_name[0] ? l->l_name : _dl_argv[0],
64                            lazy ? " (lazy)" : "");
65
66   /* DT_TEXTREL is now in level 2 and might phase out at some time.
67      But we rewrite the DT_FLAGS entry to a DT_TEXTREL entry to make
68      testing easier and therefore it will be available at all time.  */
69   if (__builtin_expect (l->l_info[DT_TEXTREL] != NULL, 0))
70     {
71       /* Bletch.  We must make read-only segments writable
72          long enough to relocate them.  */
73       const ElfW(Phdr) *ph;
74       for (ph = l->l_phdr; ph < &l->l_phdr[l->l_phnum]; ++ph)
75         if (ph->p_type == PT_LOAD && (ph->p_flags & PF_W) == 0)
76           {
77             struct textrels *newp;
78
79             newp = (struct textrels *) alloca (sizeof (*newp));
80             newp->len = (((ph->p_vaddr + ph->p_memsz + GL(dl_pagesize) - 1)
81                           & ~(GL(dl_pagesize) - 1))
82                          - (ph->p_vaddr & ~(GL(dl_pagesize) - 1)));
83             newp->start = ((ph->p_vaddr & ~(GL(dl_pagesize) - 1))
84                            + (caddr_t) l->l_addr);
85
86             if (__mprotect (newp->start, newp->len, PROT_READ|PROT_WRITE) < 0)
87               {
88                 errstring = N_("cannot make segment writable for relocation");
89               call_error:
90                 INT(_dl_signal_error) (errno, l->l_name, NULL, errstring);
91               }
92
93 #if (PF_R | PF_W | PF_X) == 7 && (PROT_READ | PROT_WRITE | PROT_EXEC) == 7
94             newp->prot = (PF_TO_PROT
95                           >> ((ph->p_flags & (PF_R | PF_W | PF_X)) * 4)) & 0xf;
96 #else
97             newp->prot = 0;
98             if (ph->p_flags & PF_R)
99               newp->prot |= PROT_READ;
100             if (ph->p_flags & PF_W)
101               newp->prot |= PROT_WRITE;
102             if (ph->p_flags & PF_X)
103               newp->prot |= PROT_EXEC;
104 #endif
105             newp->next = textrels;
106             textrels = newp;
107           }
108     }
109
110   {
111     /* Do the actual relocation of the object's GOT and other data.  */
112
113     /* String table object symbols.  */
114     const char *strtab = (const void *) D_PTR (l, l_info[DT_STRTAB]);
115
116     /* This macro is used as a callback from the ELF_DYNAMIC_RELOCATE code.  */
117 #define RESOLVE_MAP(ref, version, r_type) \
118     (ELFW(ST_BIND) ((*ref)->st_info) != STB_LOCAL                             \
119      ? ((__builtin_expect ((*ref) == l->l_lookup_cache.sym, 0)                \
120          && elf_machine_type_class (r_type) == l->l_lookup_cache.type_class)  \
121         ? (bump_num_cache_relocations (),                                     \
122            (*ref) = l->l_lookup_cache.ret,                                    \
123            l->l_lookup_cache.value)                                           \
124         : ({ lookup_t _lr;                                                    \
125              int _tc = elf_machine_type_class (r_type);                       \
126              l->l_lookup_cache.type_class = _tc;                              \
127              l->l_lookup_cache.sym = (*ref);                                  \
128              _lr = ((version) != NULL && (version)->hash != 0                 \
129                     ? INT(_dl_lookup_versioned_symbol) (strtab                \
130                                                         + (*ref)->st_name,    \
131                                                         l, (ref), scope,      \
132                                                         (version), _tc, 0)    \
133                     : INT(_dl_lookup_symbol) (strtab + (*ref)->st_name, l,    \
134                                               (ref), scope, _tc, 0));         \
135              l->l_lookup_cache.ret = (*ref);                                  \
136              l->l_lookup_cache.value = _lr; }))                               \
137      : l)
138 #define RESOLVE(ref, version, r_type) \
139     (ELFW(ST_BIND) ((*ref)->st_info) != STB_LOCAL                             \
140      ? ((__builtin_expect ((*ref) == l->l_lookup_cache.sym, 0)                \
141          && elf_machine_type_class (r_type) == l->l_lookup_cache.type_class)  \
142                 ? (bump_num_cache_relocations (),                             \
143            (*ref) = l->l_lookup_cache.ret,                                    \
144            l->l_lookup_cache.value)                                           \
145         : ({ lookup_t _lr;                                                    \
146              int _tc = elf_machine_type_class (r_type);                       \
147              l->l_lookup_cache.type_class = _tc;                              \
148              l->l_lookup_cache.sym = (*ref);                                  \
149              _lr = ((version) != NULL && (version)->hash != 0                 \
150                     ? INT(_dl_lookup_versioned_symbol) (strtab                \
151                                                         + (*ref)->st_name,    \
152                                                         l, (ref), scope,      \
153                                                         (version), _tc, 0)    \
154                     : INT(_dl_lookup_symbol) (strtab + (*ref)->st_name, l,    \
155                                               (ref), scope, _tc, 0));         \
156              l->l_lookup_cache.ret = (*ref);                                  \
157              l->l_lookup_cache.value = _lr; }))                               \
158      : l->l_addr)
159
160 #include "dynamic-link.h"
161
162     ELF_DYNAMIC_RELOCATE (l, lazy, consider_profiling);
163
164     if (__builtin_expect (consider_profiling, 0))
165       {
166         /* Allocate the array which will contain the already found
167            relocations.  If the shared object lacks a PLT (for example
168            if it only contains lead function) the l_info[DT_PLTRELSZ]
169            will be NULL.  */
170         if (l->l_info[DT_PLTRELSZ] == NULL)
171           {
172             errstring = N_("%s: profiler found no PLTREL in object %s\n");
173           fatal:
174             _dl_fatal_printf (errstring,
175                               _dl_argv[0] ?: "<program name unknown>",
176                               l->l_name);
177           }
178
179         l->l_reloc_result =
180           (ElfW(Addr) *) calloc (sizeof (ElfW(Addr)),
181                                  l->l_info[DT_PLTRELSZ]->d_un.d_val);
182         if (l->l_reloc_result == NULL)
183           {
184             errstring = N_("\
185 %s: profiler out of memory shadowing PLTREL of %s\n");
186             goto fatal;
187           }
188       }
189   }
190
191   /* Mark the object so we know this work has been done.  */
192   l->l_relocated = 1;
193
194   /* Undo the segment protection changes.  */
195   while (__builtin_expect (textrels != NULL, 0))
196     {
197       if (__mprotect (textrels->start, textrels->len, textrels->prot) < 0)
198         {
199           errstring = N_("cannot restore segment prot after reloc");
200           goto call_error;
201         }
202
203       textrels = textrels->next;
204     }
205 }
206 INTDEF (_dl_relocate_object)
207
208
209 void
210 internal_function
211 _dl_reloc_bad_type (struct link_map *map, unsigned int type, int plt)
212 {
213   extern const char _itoa_lower_digits[];
214 #define DIGIT(b)        _itoa_lower_digits[(b) & 0xf];
215
216   /* XXX We cannot translate these messages.  */
217   static const char msg[2][32] = { "unexpected reloc type 0x",
218                                    "unexpected PLT reloc type 0x" };
219   char msgbuf[sizeof (msg[0])];
220   char *cp;
221
222   cp = __stpcpy (msgbuf, msg[plt]);
223   *cp++ = DIGIT (type >> 4);
224   *cp = DIGIT (type);
225
226   INT(_dl_signal_error) (0, map->l_name, NULL, msgbuf);
227 }