OSDN Git Service

Begin removing some unnecessary inlining, and fix naming
[uclinux-h8/uClibc.git] / ldso / ldso / powerpc / elfinterp.c
1 #define DEBUG
2 /* Run an ELF binary on a linux system.
3
4    Copyright (C) 1993, Eric Youngdale.
5
6    This program is free software; you can redistribute it and/or modify
7    it under the terms of the GNU General Public License as published by
8    the Free Software Foundation; either version 2, or (at your option)
9    any later version.
10
11    This program is distributed in the hope that it will be useful,
12    but WITHOUT ANY WARRANTY; without even the implied warranty of
13    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14    GNU General Public License for more details.
15
16    You should have received a copy of the GNU General Public License
17    along with this program; if not, write to the Free Software
18    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.  */
19 \f
20 #ifndef VERBOSE_DLINKER
21 #define VERBOSE_DLINKER
22 #endif
23 #ifdef VERBOSE_DLINKER
24 static char *_dl_reltypes[] =
25         { "R_PPC_NONE", "R_PPC_ADDR32", "R_PPC_ADDR24", "R_PPC_ADDR16",
26         "R_PPC_ADDR16_LO", "R_PPC_ADDR16_HI", "R_PPC_ADDR16_HA",
27         "R_PPC_ADDR14", "R_PPC_ADDR14_BRTAKEN", "R_PPC_ADDR14_BRNTAKEN",
28         "R_PPC_REL24", "R_PPC_REL14", "R_PPC_REL14_BRTAKEN",
29         "R_PPC_REL14_BRNTAKEN", "R_PPC_GOT16", "R_PPC_GOT16_LO",
30         "R_PPC_GOT16_HI", "R_PPC_GOT16_HA", "R_PPC_PLTREL24",
31         "R_PPC_COPY", "R_PPC_GLOB_DAT", "R_PPC_JMP_SLOT", "R_PPC_RELATIVE",
32         "R_PPC_LOCAL24PC", "R_PPC_UADDR32", "R_PPC_UADDR16", "R_PPC_REL32",
33         "R_PPC_PLT32", "R_PPC_PLTREL32", "R_PPC_PLT16_LO", "R_PPC_PLT16_HI",
34         "R_PPC_PLT16_HA", "R_PPC_SDAREL16", "R_PPC_SECTOFF",
35         "R_PPC_SECTOFF_LO", "R_PPC_SECTOFF_HI", "R_PPC_SECTOFF_HA",
36 };
37 #define N_RELTYPES (sizeof(_dl_reltypes)/sizeof(_dl_reltypes[0]))
38 #endif
39
40 /* Program to load an ELF binary on a linux system, and run it.
41    References to symbols in sharable libraries can be resolved by either
42    an ELF sharable library or a linux style of shared library. */
43
44 /* Disclaimer:  I have never seen any AT&T source code for SVr4, nor have
45    I ever taken any courses on internals.  This program was developed using
46    information available through the book "UNIX SYSTEM V RELEASE 4,
47    Programmers guide: Ansi C and Programming Support Tools", which did
48    a more than adequate job of explaining everything required to get this
49    working. */
50
51 #include <sys/types.h>
52 #include <errno.h>
53 #include "sysdep.h"
54 #include <elf.h>
55 #include "linuxelf.h"
56 #include "hash.h"
57 #include "syscall.h"
58 #include "string.h"
59
60 extern char *_dl_progname;
61
62 extern int _dl_linux_resolve(void);
63
64 void _dl_init_got(unsigned long *plt,struct elf_resolve *tpnt)
65 {
66         int i;
67         unsigned long target_addr = (unsigned long)_dl_linux_resolve;
68         unsigned int n_plt_entries;
69         unsigned long *tramp;
70         unsigned long data_words;
71         unsigned int rel_offset_words;
72         unsigned int offset;
73
74         _dl_dprintf(2,"init_got plt=%x, tpnt=%x\n",
75                 (unsigned long)plt,(unsigned long)tpnt);
76
77         n_plt_entries = tpnt->dynamic_info[DT_PLTRELSZ] / sizeof(ELF_RELOC);
78 _dl_dprintf(2,"n_plt_entries %d\n",n_plt_entries);
79
80 rel_offset_words = PLT_DATA_START_WORDS(n_plt_entries);
81 _dl_dprintf(2,"rel_offset_words %x\n",rel_offset_words);
82 data_words = (unsigned long)(plt + rel_offset_words);
83 _dl_dprintf(2,"data_words %x\n",data_words);
84
85         //lpnt += PLT_INITIAL_ENTRY_WORDS;
86         
87         plt[PLT_LONGBRANCH_ENTRY_WORDS] = OPCODE_ADDIS_HI(11, 11, data_words);
88         plt[PLT_LONGBRANCH_ENTRY_WORDS+1] = OPCODE_LWZ(11,data_words,11);
89
90         plt[PLT_LONGBRANCH_ENTRY_WORDS+2] = OPCODE_MTCTR(11);
91         plt[PLT_LONGBRANCH_ENTRY_WORDS+3] = OPCODE_BCTR();
92
93         tramp = plt + PLT_TRAMPOLINE_ENTRY_WORDS;
94         tramp[0] = OPCODE_ADDIS_HI(11,11,-data_words);
95         tramp[1] = OPCODE_ADDI(11,11,-data_words);
96         tramp[2] = OPCODE_SLWI(12,11,1);
97         tramp[3] = OPCODE_ADD(11,12,11);
98         tramp[4] = OPCODE_LI(12,target_addr);
99         tramp[5] = OPCODE_ADDIS_HI(12,12,target_addr);
100         tramp[6] = OPCODE_MTCTR(12);
101         tramp[7] = OPCODE_LI(12,(unsigned long)tpnt);
102         tramp[8] = OPCODE_ADDIS_HI(12,12,(unsigned long)tpnt);
103         tramp[9] = OPCODE_BCTR();
104
105         PPC_DCBST(plt);
106         PPC_DCBST(plt+4);
107         PPC_DCBST(plt+8);
108         PPC_SYNC;
109         PPC_ICBI(plt);
110         PPC_ICBI(plt+4);
111         PPC_ICBI(plt+8);
112         PPC_ISYNC;
113 }
114
115 unsigned long _dl_linux_resolver(struct elf_resolve *tpnt, int reloc_entry)
116 {
117         int reloc_type;
118         ELF_RELOC *this_reloc;
119         char *strtab;
120         Elf32_Sym *symtab;
121         ELF_RELOC *rel_addr;
122         int symtab_index;
123         char *new_addr;
124         char **got_addr;
125         unsigned long instr_addr;
126
127 _dl_dprintf(2,"linux_resolver tpnt=%x reloc_entry=%x\n",tpnt,reloc_entry);
128         rel_addr = (ELF_RELOC *) (tpnt->dynamic_info[DT_JMPREL] + tpnt->loadaddr);
129
130         this_reloc = (void *)rel_addr + reloc_entry;
131         reloc_type = ELF32_R_TYPE(this_reloc->r_info);
132         symtab_index = ELF32_R_SYM(this_reloc->r_info);
133
134         symtab = (Elf32_Sym *) (tpnt->dynamic_info[DT_SYMTAB] + tpnt->loadaddr);
135         strtab = (char *) (tpnt->dynamic_info[DT_STRTAB] + tpnt->loadaddr);
136
137
138         if (reloc_type != R_PPC_JMP_SLOT) {
139                 _dl_dprintf(2, "%s: Incorrect relocation type [%s] in jump relocations\n",
140                         _dl_progname,
141                         (reloc_type<N_RELTYPES)?_dl_reltypes[reloc_type]:"unknown");
142                 _dl_exit(1);
143         };
144
145         /* Address of dump instruction to fix up */
146         instr_addr = ((unsigned long) this_reloc->r_offset + 
147                 (unsigned long) tpnt->loadaddr);
148         got_addr = (char **) instr_addr;
149
150 #ifdef DEBUG
151         _dl_dprintf(2, "Resolving symbol %s %x --> ", 
152                 strtab + symtab[symtab_index].st_name,
153                 instr_addr);
154 #endif
155
156         /* Get the address of the GOT entry */
157         new_addr = _dl_find_hash(strtab + symtab[symtab_index].st_name, 
158                 tpnt->symbol_scope, (unsigned long) got_addr, tpnt, 0);
159         if (!new_addr) {
160                 _dl_dprintf(2, "%s: can't resolve symbol '%s'\n", 
161                         _dl_progname, strtab + symtab[symtab_index].st_name);
162                 _dl_exit(1);
163         };
164 #ifdef DEBUG
165         _dl_dprintf(2, "%x\n", new_addr);
166 #endif
167
168 /* #define DEBUG_LIBRARY */
169 #ifdef DEBUG_LIBRARY
170         if ((unsigned long) got_addr < 0x40000000) {
171                 _dl_dprintf(2, "Calling library function: %s\n", 
172                         strtab + symtab[symtab_index].st_name);
173         } else {
174                 *got_addr = new_addr;
175         }
176 #else
177         *got_addr = new_addr;
178 #endif
179         return (unsigned long) new_addr;
180 }
181
182 void _dl_parse_lazy_relocation_information(struct elf_resolve *tpnt, 
183         unsigned long rel_addr, unsigned long rel_size, int type)
184 {
185         int i;
186         char *strtab;
187         int reloc_type;
188         int symtab_index;
189         Elf32_Sym *symtab;
190         ELF_RELOC *rpnt;
191         unsigned long *reloc_addr;
192         unsigned long *plt;
193         int index;
194
195 #ifdef DEBUG
196 _dl_dprintf(2,"_dl_parse_lazy_relocation_information(tpnt=%x, rel_addr=%x, rel_size=%x, type=%d)\n",
197                 tpnt,rel_addr,rel_size,type);
198 #endif
199         /* Now parse the relocation information */
200         rpnt = (ELF_RELOC *) (rel_addr + tpnt->loadaddr);
201         rel_size = rel_size / sizeof(ELF_RELOC);
202
203         symtab =
204                 (Elf32_Sym *) (tpnt->dynamic_info[DT_SYMTAB] + tpnt->loadaddr);
205         strtab = (char *) (tpnt->dynamic_info[DT_STRTAB] + tpnt->loadaddr);
206         plt = (unsigned long *)(tpnt->dynamic_info[DT_PLTGOT] + tpnt->loadaddr);
207
208         for (i = 0; i < rel_size; i++, rpnt++) {
209                 reloc_addr = (unsigned long *) (tpnt->loadaddr + (unsigned long) rpnt->r_offset);
210                 reloc_type = ELF32_R_TYPE(rpnt->r_info);
211                 symtab_index = ELF32_R_SYM(rpnt->r_info);
212
213                 /* When the dynamic linker bootstrapped itself, it resolved some symbols.
214                    Make sure we do not do them again */
215                 if (!symtab_index && tpnt->libtype == program_interpreter)
216                         continue;
217                 if (symtab_index && tpnt->libtype == program_interpreter &&
218                         _dl_symbol(strtab + symtab[symtab_index].st_name))
219                         continue;
220
221 #ifdef DEBUG
222 _dl_dprintf(2, "L %x %s %s %x %x\n",
223         reloc_addr, _dl_reltypes[reloc_type],
224         symtab_index?strtab + symtab[symtab_index].st_name:"",0,0);
225 #endif
226
227
228                 switch (reloc_type) {
229                 case R_PPC_NONE:
230                         break;
231                 case R_PPC_JMP_SLOT:
232                         {
233                         int delta;
234                         
235                         delta = (unsigned long)(plt+PLT_TRAMPOLINE_ENTRY_WORDS+2)
236                                 - (unsigned long)(reloc_addr+1);
237
238                         index = ((unsigned long)reloc_addr -
239                                 (unsigned long)(plt+PLT_INITIAL_ENTRY_WORDS))
240                                 /sizeof(unsigned long);
241                         index /= 2;
242 #ifdef DEBUG
243 _dl_dprintf(2, "        index %x delta %x\n",index,delta);
244 #endif
245                         reloc_addr[0] = OPCODE_LI(11,index*4);
246                         reloc_addr[1] = OPCODE_B(delta);
247                         break;
248                         }
249                 default:
250                         _dl_dprintf(2, "%s: (LAZY) can't handle reloc type ", 
251                                 _dl_progname);
252 #ifdef VERBOSE_DLINKER
253                         _dl_dprintf(2, "%s ", _dl_reltypes[reloc_type]);
254 #endif
255                         if (symtab_index)
256                                 _dl_dprintf(2, "'%s'\n", strtab + symtab[symtab_index].st_name);
257                         _dl_exit(1);
258                 };
259
260                 /* instructions were modified */
261                 PPC_DCBST(reloc_addr);
262                 PPC_SYNC;
263                 PPC_ICBI(reloc_addr);
264         };
265 }
266
267 int _dl_parse_relocation_information(struct elf_resolve *tpnt, 
268         unsigned long rel_addr, unsigned long rel_size, int type)
269 {
270         int i;
271         char *strtab;
272         int reloc_type;
273         int goof = 0;
274         Elf32_Sym *symtab;
275         ELF_RELOC *rpnt;
276         unsigned long *reloc_addr;
277         unsigned long symbol_addr;
278         int symtab_index;
279         unsigned long addend;
280         unsigned long *plt;
281
282 #ifdef DEBUG
283 _dl_dprintf(2,"_dl_parse_relocation_information(tpnt=%x, rel_addr=%x, rel_size=%x, type=%d)\n",
284                 tpnt,rel_addr,rel_size,type);
285 #endif
286         /* Now parse the relocation information */
287
288         rpnt = (ELF_RELOC *) (rel_addr + tpnt->loadaddr);
289         rel_size = rel_size / sizeof(ELF_RELOC);
290
291         symtab = (Elf32_Sym *) (tpnt->dynamic_info[DT_SYMTAB] + tpnt->loadaddr);
292         strtab = (char *) (tpnt->dynamic_info[DT_STRTAB] + tpnt->loadaddr);
293         plt = (unsigned long *)(tpnt->dynamic_info[DT_PLTGOT] + tpnt->loadaddr);
294
295         for (i = 0; i < rel_size; i++, rpnt++) {
296                 reloc_addr = (unsigned long *) (tpnt->loadaddr + (unsigned long) rpnt->r_offset);
297                 reloc_type = ELF32_R_TYPE(rpnt->r_info);
298                 symtab_index = ELF32_R_SYM(rpnt->r_info);
299                 addend = rpnt->r_addend;
300                 symbol_addr = 0;
301
302                 if (!symtab_index && tpnt->libtype == program_interpreter)
303                         continue;
304
305                 if (symtab_index) {
306
307                         if (tpnt->libtype == program_interpreter &&
308                                 _dl_symbol(strtab + symtab[symtab_index].st_name))
309                                 continue;
310
311                         symbol_addr = (unsigned long) _dl_find_hash(strtab + symtab[symtab_index].st_name, 
312                                         tpnt->symbol_scope, (unsigned long) reloc_addr, 
313                                         (reloc_type == R_PPC_JMP_SLOT ? tpnt : NULL), 0);
314
315                         /*
316                          * We want to allow undefined references to weak symbols - this might
317                          * have been intentional.  We should not be linking local symbols
318                          * here, so all bases should be covered.
319                          */
320                         if (!symbol_addr &&
321                                 ELF32_ST_BIND(symtab[symtab_index].st_info) == STB_GLOBAL) {
322                                 _dl_dprintf(2, "%s: can't resolve symbol '%s'\n", 
323                                         _dl_progname, strtab + symtab[symtab_index].st_name);
324                                 goof++;
325                         }
326                 }
327 #ifdef DEBUG
328 _dl_dprintf(2, "  %x %s %s %x %x\n",
329         reloc_addr, _dl_reltypes[reloc_type],
330         symtab_index?strtab + symtab[symtab_index].st_name:"",
331         symbol_addr, addend);
332 #endif
333                 switch (reloc_type) {
334                 case R_PPC_NONE:
335                         break;
336                 case R_PPC_REL24:
337                         {
338                         int delta = symbol_addr - (unsigned long)reloc_addr;
339                         if(delta<<6>>6 != delta){
340                                 _dl_dprintf(2,"R_PPC_REL24: Reloc out of range\n");
341                                 _dl_exit(1);
342                         }
343                         *reloc_addr &= 0xfc000003;
344                         *reloc_addr |= delta&0x03fffffc;
345                         }
346                         break;
347                 case R_PPC_RELATIVE:
348                         *reloc_addr += (unsigned long)tpnt->loadaddr + addend;
349                         break;
350                 case R_PPC_ADDR32:
351                         *reloc_addr += symbol_addr;
352                         break;
353                 case R_PPC_ADDR16_HA:
354                         /* XXX is this correct? */
355                         *(short *)reloc_addr += (symbol_addr+0x8000)>>16;
356                         break;
357                 case R_PPC_ADDR16_HI:
358                         *(short *)reloc_addr += symbol_addr>>16;
359                         break;
360                 case R_PPC_ADDR16_LO:
361                         *(short *)reloc_addr += symbol_addr;
362                         break;
363                 case R_PPC_JMP_SLOT:
364                         {
365                         unsigned long targ_addr = (unsigned long)_dl_linux_resolve;
366                         int delta = targ_addr - (unsigned long)reloc_addr;
367                         if(delta<<6>>6 == delta){
368                                 *reloc_addr = OPCODE_B(delta);
369                         }else if (targ_addr <= 0x01fffffc || targ_addr >= 0xfe000000){
370                                 *reloc_addr = OPCODE_BA (targ_addr);
371                         }else{
372         {
373         int delta;
374         int index;
375         
376         delta = (unsigned long)(plt+PLT_TRAMPOLINE_ENTRY_WORDS+2)
377                 - (unsigned long)(reloc_addr+1);
378
379         index = ((unsigned long)reloc_addr -
380                 (unsigned long)(plt+PLT_INITIAL_ENTRY_WORDS))
381                 /sizeof(unsigned long);
382         index /= 2;
383 #ifdef DEBUG
384 _dl_dprintf(2, "        index %x delta %x\n",index,delta);
385 #endif
386         reloc_addr[0] = OPCODE_LI(11,index*4);
387         reloc_addr[1] = OPCODE_B(delta);
388         }
389                         }
390                         break;
391                         }
392                 default:
393                         _dl_dprintf(2, "%s: can't handle reloc type ", _dl_progname);
394 #ifdef VERBOSE_DLINKER
395                         _dl_dprintf(2, "%s ", _dl_reltypes[reloc_type]);
396 #endif
397                         if (symtab_index)
398                                 _dl_dprintf(2, "'%s'\n", strtab + symtab[symtab_index].st_name);
399                         _dl_exit(1);
400                 };
401
402                 /* instructions were modified */
403                 PPC_DCBST(reloc_addr);
404                 PPC_SYNC;
405                 PPC_ICBI(reloc_addr);
406
407 //_dl_dprintf(2,"reloc_addr %x: %x\n",reloc_addr,*reloc_addr);
408         };
409         return goof;
410 }
411
412
413 /* This is done as a separate step, because there are cases where
414    information is first copied and later initialized.  This results in
415    the wrong information being copied.  Someone at Sun was complaining about
416    a bug in the handling of _COPY by SVr4, and this may in fact be what he
417    was talking about.  Sigh. */
418
419 /* No, there are cases where the SVr4 linker fails to emit COPY relocs
420    at all */
421
422 int _dl_parse_copy_information(struct dyn_elf *xpnt, unsigned long rel_addr, 
423         unsigned long rel_size, int type)
424 {
425         int i;
426         char *strtab;
427         int reloc_type;
428         int goof = 0;
429         Elf32_Sym *symtab;
430         ELF_RELOC *rpnt;
431         unsigned long *reloc_addr;
432         unsigned long symbol_addr;
433         struct elf_resolve *tpnt;
434         int symtab_index;
435
436 _dl_dprintf(2,"parse_copy xpnt=%x rel_addr=%x rel_size=%x type=%d\n",
437                 (int)xpnt,rel_addr,rel_size,type);
438
439         /* Now parse the relocation information */
440
441         tpnt = xpnt->dyn;
442
443         rpnt = (ELF_RELOC *) (rel_addr + tpnt->loadaddr);
444         rel_size = rel_size / sizeof(ELF_RELOC);
445
446         symtab = (Elf32_Sym *) (tpnt->dynamic_info[DT_SYMTAB] + tpnt->loadaddr);
447         strtab = (char *) (tpnt->dynamic_info[DT_STRTAB] + tpnt->loadaddr);
448
449         for (i = 0; i < rel_size; i++, rpnt++) {
450                 reloc_addr = (unsigned long *) (tpnt->loadaddr + (unsigned long) rpnt->r_offset);
451                 reloc_type = ELF32_R_TYPE(rpnt->r_info);
452                 if (reloc_type != R_386_COPY)
453                         continue;
454                 symtab_index = ELF32_R_SYM(rpnt->r_info);
455                 symbol_addr = 0;
456                 if (!symtab_index && tpnt->libtype == program_interpreter)
457                         continue;
458                 if (symtab_index) {
459
460                         if (tpnt->libtype == program_interpreter &&
461                                 _dl_symbol(strtab + symtab[symtab_index].st_name))
462                                 continue;
463
464                         symbol_addr = (unsigned long) _dl_find_hash(strtab + 
465                                 symtab[symtab_index].st_name, xpnt->next, 
466                                 (unsigned long) reloc_addr, NULL, 1);
467                         if (!symbol_addr) {
468                                 _dl_dprintf(2, "%s: can't resolve symbol '%s'\n", 
469                                         _dl_progname, strtab + symtab[symtab_index].st_name);
470                                 goof++;
471                         };
472                 };
473                 if (!goof) {
474                         _dl_memcpy((char *) symtab[symtab_index].st_value, 
475                                 (char *) symbol_addr, symtab[symtab_index].st_size);
476                 }
477         };
478         return goof;
479 }