Initialize Tizen 2.3
[external/prelink.git] / src / arch-sh.c
1 /* Copyright (C) 2001, 2002, 2003, 2004, 2009 Red Hat, Inc.
2    Written by Jakub Jelinek <jakub@redhat.com>, 2001.
3
4    This program is free software; you can redistribute it and/or modify
5    it under the terms of the GNU General Public License as published by
6    the Free Software Foundation; either version 2, or (at your option)
7    any later version.
8
9    This program is distributed in the hope that it will be useful,
10    but WITHOUT ANY WARRANTY; without even the implied warranty of
11    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12    GNU General Public License for more details.
13
14    You should have received a copy of the GNU General Public License
15    along with this program; if not, write to the Free Software Foundation,
16    Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */
17
18 #include <config.h>
19 #include <assert.h>
20 #include <errno.h>
21 #include <fcntl.h>
22 #include <stdio.h>
23 #include <string.h>
24 #include <locale.h>
25 #include <error.h>
26 #include <argp.h>
27 #include <stdlib.h>
28
29 #include "prelink.h"
30
31 static int
32 sh_adjust_dyn (DSO *dso, int n, GElf_Dyn *dyn, GElf_Addr start,
33                GElf_Addr adjust)
34 {
35   if (dyn->d_tag == DT_PLTGOT)
36     {
37       int sec = addr_to_sec (dso, dyn->d_un.d_ptr);
38       Elf32_Addr data;
39
40       if (sec == -1)
41         return 0;
42
43       data = read_une32 (dso, dyn->d_un.d_ptr);
44       /* If .got.plt[0] points to _DYNAMIC, it needs to be adjusted.  */
45       if (data == dso->shdr[n].sh_addr && data >= start)
46         write_ne32 (dso, dyn->d_un.d_ptr, data + adjust);
47
48       data = read_une32 (dso, dyn->d_un.d_ptr + 4);
49       /* If .got.plt[1] points to .plt + 36, it needs to be adjusted.  */
50       if (data && data >= start)
51         {
52           int i;
53
54           for (i = 1; i < dso->ehdr.e_shnum; i++)
55             if (data == dso->shdr[i].sh_addr + 36
56                 && dso->shdr[i].sh_type == SHT_PROGBITS
57                 && strcmp (strptr (dso, dso->ehdr.e_shstrndx,
58                                         dso->shdr[i].sh_name), ".plt") == 0)
59               {
60                 write_ne32 (dso, dyn->d_un.d_ptr + 4, data + adjust);
61                 break;
62               }
63         }
64     }
65   return 0;
66 }
67
68 static int
69 sh_adjust_rel (DSO *dso, GElf_Rel *rel, GElf_Addr start,
70                GElf_Addr adjust)
71 {
72   error (0, 0, "%s: SH doesn't support REL relocs", dso->filename);
73   return 1;
74 }
75
76 static int
77 sh_adjust_rela (DSO *dso, GElf_Rela *rela, GElf_Addr start,
78                 GElf_Addr adjust)
79 {
80   Elf32_Addr data;
81
82   switch (GELF_R_TYPE (rela->r_info))
83     {
84     case R_SH_RELATIVE:
85       if (rela->r_addend && (Elf32_Addr) rela->r_addend >= start)
86         {
87           rela->r_addend += (Elf32_Sword) adjust;
88           break;
89         }
90       /* FALLTHROUGH */
91     case R_SH_JMP_SLOT:
92       data = read_une32 (dso, rela->r_offset);
93       if (data >= start)
94         write_ne32 (dso, rela->r_offset, data + adjust);
95       break;
96       break;
97     }
98   return 0;
99 }
100
101 static int
102 sh_prelink_rel (struct prelink_info *info, GElf_Rel *rel, GElf_Addr reladdr)
103 {
104   error (0, 0, "%s: SH doesn't support REL relocs", info->dso->filename);
105   return 1;
106 }
107
108 static int
109 sh_prelink_rela (struct prelink_info *info, GElf_Rela *rela,
110                  GElf_Addr relaaddr)
111 {
112   DSO *dso;
113   GElf_Addr value;
114
115   dso = info->dso;
116   if (GELF_R_TYPE (rela->r_info) == R_SH_NONE)
117     /* Fast path: nothing to do.  */
118     return 0;
119   else if (GELF_R_TYPE (rela->r_info) == R_SH_RELATIVE)
120     {
121       if (rela->r_addend)
122         write_ne32 (dso, rela->r_offset, rela->r_addend);
123       return 0;
124     }
125   value = info->resolve (info, GELF_R_SYM (rela->r_info),
126                          GELF_R_TYPE (rela->r_info));
127   value += rela->r_addend;
128   switch (GELF_R_TYPE (rela->r_info))
129     {
130     case R_SH_GLOB_DAT:
131     case R_SH_JMP_SLOT:
132     case R_SH_DIR32:
133       write_ne32 (dso, rela->r_offset, value);
134       break;
135     case R_SH_REL32:
136       write_ne32 (dso, rela->r_offset, value - rela->r_addend);
137       break;
138     case R_SH_COPY:
139       if (dso->ehdr.e_type == ET_EXEC)
140         /* COPY relocs are handled specially in generic code.  */
141         return 0;
142       error (0, 0, "%s: R_SH_COPY reloc in shared library?", dso->filename);
143       return 1;
144     default:
145       error (0, 0, "%s: Unknown sh relocation type %d", dso->filename,
146              (int) GELF_R_TYPE (rela->r_info));
147       return 1;
148     }
149   return 0;
150 }
151
152 static int
153 sh_apply_conflict_rela (struct prelink_info *info, GElf_Rela *rela,
154                         char *buf, GElf_Addr dest_addr)
155 {
156   switch (GELF_R_TYPE (rela->r_info))
157     {
158     case R_SH_GLOB_DAT:
159     case R_SH_JMP_SLOT:
160     case R_SH_DIR32:
161       buf_write_ne32 (info->dso, buf, rela->r_addend);
162       break;
163     default:
164       abort ();
165     }
166   return 0;
167 }
168
169 static int
170 sh_apply_rel (struct prelink_info *info, GElf_Rel *rel, char *buf)
171 {
172   error (0, 0, "%s: SH doesn't support REL relocs", info->dso->filename);
173   return 1;
174 }
175
176 static int
177 sh_apply_rela (struct prelink_info *info, GElf_Rela *rela, char *buf)
178 {
179   GElf_Addr value;
180
181   value = info->resolve (info, GELF_R_SYM (rela->r_info),
182                          GELF_R_TYPE (rela->r_info));
183   value += rela->r_addend;
184   switch (GELF_R_TYPE (rela->r_info))
185     {
186     case R_SH_NONE:
187       break;
188     case R_SH_GLOB_DAT:
189     case R_SH_JMP_SLOT:
190     case R_SH_DIR32:
191       buf_write_ne32 (info->dso, buf, value);
192       break;
193     case R_SH_REL32:
194       buf_write_ne32 (info->dso, buf, value - rela->r_offset);
195       break;
196     case R_SH_COPY:
197       abort ();
198     case R_SH_RELATIVE:
199       error (0, 0, "%s: R_SH_RELATIVE in ET_EXEC object?", info->dso->filename);
200       return 1;
201     default:
202       return 1;
203     }
204   return 0;
205 }
206
207 static int
208 sh_prelink_conflict_rel (DSO *dso, struct prelink_info *info, GElf_Rel *rel,
209                          GElf_Addr reladdr)
210 {
211   error (0, 0, "%s: SH doesn't support REL relocs", dso->filename);
212   return 1;
213 }
214
215 static int
216 sh_prelink_conflict_rela (DSO *dso, struct prelink_info *info,
217                           GElf_Rela *rela, GElf_Addr relaaddr)
218 {
219   GElf_Addr value;
220   struct prelink_conflict *conflict;
221   GElf_Rela *ret;
222
223   if (GELF_R_TYPE (rela->r_info) == R_SH_RELATIVE
224       || GELF_R_TYPE (rela->r_info) == R_SH_NONE
225       || info->dso == dso)
226     /* Fast path: nothing to do.  */
227     return 0;
228   conflict = prelink_conflict (info, GELF_R_SYM (rela->r_info),
229                                GELF_R_TYPE (rela->r_info));
230   if (conflict == NULL)
231     return 0;
232   else if (conflict->ifunc)
233     {
234       error (0, 0, "%s: STT_GNU_IFUNC not handled on SuperH yet",
235              dso->filename);
236       return 1;
237     }
238   value = conflict_lookup_value (conflict);
239   ret = prelink_conflict_add_rela (info);
240   if (ret == NULL)
241     return 1;
242   ret->r_offset = rela->r_offset;
243   ret->r_info = GELF_R_INFO (0, GELF_R_TYPE (rela->r_info));
244   value += rela->r_addend;
245   switch (GELF_R_TYPE (rela->r_info))
246     {
247     case R_SH_REL32:
248       value -= rela->r_offset;
249       ret->r_info = GELF_R_INFO (0, R_SH_DIR32);
250       /* FALLTHROUGH */
251     case R_SH_DIR32:
252       if ((rela->r_offset & 3) == 0)
253         ret->r_info = GELF_R_INFO (0, R_SH_GLOB_DAT);
254       /* FALLTHROUGH */
255     case R_SH_GLOB_DAT:
256     case R_SH_JMP_SLOT:
257       ret->r_addend = (Elf32_Sword) (value + rela->r_addend);
258       break;
259     case R_SH_COPY:
260       error (0, 0, "R_SH_COPY should not be present in shared libraries");
261       return 1;
262     default:
263       error (0, 0, "%s: Unknown sh relocation type %d", dso->filename,
264              (int) GELF_R_TYPE (rela->r_info));
265       return 1;
266     }
267   return 0;
268 }
269
270 static int
271 sh_rel_to_rela (DSO *dso, GElf_Rel *rel, GElf_Rela *rela)
272 {
273   return 0;
274 }
275
276 static int
277 sh_need_rel_to_rela (DSO *dso, int first, int last)
278 {
279   return 0;
280 }
281
282 static int
283 sh_arch_prelink (struct prelink_info *info)
284 {
285   DSO *dso;
286   int i;
287
288   dso = info->dso;
289   if (dso->info[DT_PLTGOT])
290     {
291       /* Write address of .plt + 36 into got[1].
292          .plt + 36 is what got[3] contains unless prelinking.  */
293       int sec = addr_to_sec (dso, dso->info[DT_PLTGOT]);
294       Elf32_Addr data;
295
296       if (sec == -1)
297         return 1;
298
299       for (i = 1; i < dso->ehdr.e_shnum; i++)
300         if (dso->shdr[i].sh_type == SHT_PROGBITS
301             && ! strcmp (strptr (dso, dso->ehdr.e_shstrndx,
302                                  dso->shdr[i].sh_name),
303                          ".plt"))
304         break;
305
306       if (i == dso->ehdr.e_shnum)
307         return 0;
308       data = dso->shdr[i].sh_addr + 36;
309       write_ne32 (dso, dso->info[DT_PLTGOT] + 4, data);
310     }
311
312   return 0;
313 }
314
315 static int
316 sh_arch_undo_prelink (DSO *dso)
317 {
318   int i;
319
320   if (dso->info[DT_PLTGOT])
321     {
322       /* Clear got[1] if it contains address of .plt + 36.  */
323       int sec = addr_to_sec (dso, dso->info[DT_PLTGOT]);
324       Elf32_Addr data;
325
326       if (sec == -1)
327         return 1;
328
329       for (i = 1; i < dso->ehdr.e_shnum; i++)
330         if (dso->shdr[i].sh_type == SHT_PROGBITS
331             && ! strcmp (strptr (dso, dso->ehdr.e_shstrndx,
332                                  dso->shdr[i].sh_name),
333                          ".plt"))
334         break;
335
336       if (i == dso->ehdr.e_shnum)
337         return 0;
338       data = read_une32 (dso, dso->info[DT_PLTGOT] + 4);
339       if (data == dso->shdr[i].sh_addr + 36)
340         write_ne32 (dso, dso->info[DT_PLTGOT] + 4, 0);
341     }
342
343   return 0;
344 }
345
346 static int
347 sh_undo_prelink_rela (DSO *dso, GElf_Rela *rela, GElf_Addr relaaddr)
348 {
349   int sec;
350   const char *name;
351
352   switch (GELF_R_TYPE (rela->r_info))
353     {
354     case R_SH_NONE:
355       break;
356     case R_SH_RELATIVE:
357       if (rela->r_addend)
358         write_le32 (dso, rela->r_offset, 0);
359       break;
360     case R_SH_JMP_SLOT:
361       sec = addr_to_sec (dso, rela->r_offset);
362       name = strptr (dso, dso->ehdr.e_shstrndx, dso->shdr[sec].sh_name);
363       if (sec == -1 || (strcmp (name, ".got") && strcmp (name, ".got.plt")))
364         {
365           error (0, 0, "%s: R_SH_JMP_SLOT not pointing into .got section",
366                  dso->filename);
367           return 1;
368         }
369       else
370         {
371           Elf32_Addr data = read_une32 (dso, dso->shdr[sec].sh_addr + 4);
372
373           assert (rela->r_offset >= dso->shdr[sec].sh_addr + 12);
374           assert (((rela->r_offset - dso->shdr[sec].sh_addr) & 3) == 0);
375           write_ne32 (dso, rela->r_offset,
376                       7 * (rela->r_offset - dso->shdr[sec].sh_addr - 12)
377                       + data);
378         }
379       break;
380     case R_SH_GLOB_DAT:
381     case R_SH_DIR32:
382     case R_SH_REL32:
383       write_ne32 (dso, rela->r_offset, 0);
384       break;
385     case R_SH_COPY:
386       if (dso->ehdr.e_type == ET_EXEC)
387         /* COPY relocs are handled specially in generic code.  */
388         return 0;
389       error (0, 0, "%s: R_SH_COPY reloc in shared library?", dso->filename);
390       return 1;
391     default:
392       error (0, 0, "%s: Unknown sh relocation type %d", dso->filename,
393              (int) GELF_R_TYPE (rela->r_info));
394       return 1;
395     }
396   return 0;
397 }
398
399 static int
400 sh_reloc_size (int reloc_type)
401 {
402   return 4;
403 }
404
405 static int
406 sh_reloc_class (int reloc_type)
407 {
408   switch (reloc_type)
409     {
410     case R_SH_COPY: return RTYPE_CLASS_COPY;
411     case R_SH_JMP_SLOT: return RTYPE_CLASS_PLT;
412     default: return RTYPE_CLASS_VALID;
413     }
414 }
415
416 PL_ARCH = {
417   .name = "SuperH",
418   .class = ELFCLASS32,
419   .machine = EM_SH,
420   .alternate_machine = { EM_NONE },
421   .R_JMP_SLOT = R_SH_JMP_SLOT,
422   .R_COPY = R_SH_COPY,
423   .R_RELATIVE = R_SH_RELATIVE,
424   .rtype_class_valid = RTYPE_CLASS_VALID,
425   .dynamic_linker = "/lib/ld-linux.so.2",
426   .adjust_dyn = sh_adjust_dyn,
427   .adjust_rel = sh_adjust_rel,
428   .adjust_rela = sh_adjust_rela,
429   .prelink_rel = sh_prelink_rel,
430   .prelink_rela = sh_prelink_rela,
431   .prelink_conflict_rel = sh_prelink_conflict_rel,
432   .prelink_conflict_rela = sh_prelink_conflict_rela,
433   .apply_conflict_rela = sh_apply_conflict_rela,
434   .apply_rel = sh_apply_rel,
435   .apply_rela = sh_apply_rela,
436   .rel_to_rela = sh_rel_to_rela,
437   .need_rel_to_rela = sh_need_rel_to_rela,
438   .reloc_size = sh_reloc_size,
439   .reloc_class = sh_reloc_class,
440   .max_reloc_size = 4,
441   .arch_prelink = sh_arch_prelink,
442   .arch_undo_prelink = sh_arch_undo_prelink,
443   .undo_prelink_rela = sh_undo_prelink_rela,
444   /* Although TASK_UNMAPPED_BASE is 0x29555000, we leave some
445      area so that mmap of /etc/ld.so.cache and ld.so's malloc
446      does not take some library's VA slot.
447      Also, if this guard area isn't too small, typically
448      even dlopened libraries will get the slots they desire.  */
449   .mmap_base = 0x30000000,
450   .mmap_end =  0x40000000,
451   .max_page_size = 0x2000,
452   .page_size = 0x1000
453 };