848e82210636b8f497991e507f207a729c62ce05
[platform/upstream/linaro-glibc.git] / sysdeps / microblaze / dl-machine.h
1 /* Copyright (C) 1995-2014 Free Software Foundation, Inc.
2
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 License as
7    published by the Free Software Foundation; either version 2.1 of the
8    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, see
17    <http://www.gnu.org/licenses/>.  */
18
19 #ifndef dl_machine_h
20 #define dl_machine_h
21
22 #define ELF_MACHINE_NAME "microblaze"
23
24 #include <sys/param.h>
25 #include <tls.h>
26
27 /* Return nonzero iff ELF header is compatible with the running host.  */
28 static inline int
29 elf_machine_matches_host (const Elf32_Ehdr *ehdr)
30 {
31   return (ehdr->e_machine == EM_MICROBLAZE);
32 }
33
34 /* Return the link-time address of _DYNAMIC.  Conveniently, this is the
35    first element of the GOT.  This must be inlined in a function which
36    uses global data.  */
37 static inline Elf32_Addr
38 elf_machine_dynamic (void)
39 {
40   /* This produces a GOTOFF reloc that resolves to zero at link time, so in
41      fact just loads from the GOT register directly.  By doing it without
42      an asm we can let the compiler choose any register.  */
43
44   Elf32_Addr got_entry_0;
45   __asm__ __volatile__(
46     "lwi %0,r20,0"
47     :"=r"(got_entry_0)
48     );
49   return got_entry_0;
50 }
51
52 /* Return the run-time load address of the shared object.  */
53 static inline Elf32_Addr
54 elf_machine_load_address (void)
55 {
56   /* Compute the difference between the runtime address of _DYNAMIC as seen
57      by a GOTOFF reference, and the link-time address found in the special
58      unrelocated first GOT entry.  */
59
60   Elf32_Addr dyn;
61   __asm__ __volatile__ (
62     "addik %0,r20,_DYNAMIC@GOTOFF"
63     : "=r"(dyn)
64     );
65   return dyn - elf_machine_dynamic ();
66 }
67
68 /* Set up the loaded object described by L so its unrelocated PLT
69    entries will jump to the on-demand fixup code in dl-runtime.c.  */
70
71 static inline int __attribute__ ((always_inline))
72 elf_machine_runtime_setup (struct link_map *l, int lazy, int profile)
73 {
74   extern void _dl_runtime_resolve (Elf32_Word);
75   extern void _dl_runtime_profile (Elf32_Word);
76
77   return lazy;
78 }
79
80 /* The PLT uses Elf32_Rela relocs.  */
81 #define elf_machine_relplt elf_machine_rela
82
83 /* Mask identifying addresses reserved for the user program,
84    where the dynamic linker should not map anything.  */
85 #define ELF_MACHINE_USER_ADDRESS_MASK   0x80000000UL
86
87 /* Initial entry point code for the dynamic linker.
88    The C function `_dl_start' is the real entry point;
89    its return value is the user program's entry point.  */
90
91 #define RTLD_START asm ("\
92         .text\n\
93         .globl _start\n\
94         .type _start,@function\n\
95 _start:\n\
96         addk  r5,r0,r1\n\
97         addk  r3,r0,r0\n\
98 1:\n\
99         addik r5,r5,4\n\
100         lw    r4,r5,r0\n\
101         bneid r4,1b\n\
102         addik r3,r3,1\n\
103         addik r3,r3,-1\n\
104         addk  r5,r0,r1\n\
105         sw    r3,r5,r0\n\
106         addik r1,r1,-24\n\
107         sw    r15,r1,r0\n\
108         brlid r15,_dl_start\n\
109         nop\n\
110         /* FALLTHRU.  */\n\
111 \n\
112         .globl _dl_start_user\n\
113         .type _dl_start_user,@function\n\
114 _dl_start_user:\n\
115         mfs   r20,rpc\n\
116         addik r20,r20,_GLOBAL_OFFSET_TABLE_+8\n\
117         lwi   r4,r20,_dl_skip_args@GOTOFF\n\
118         lwi   r5,r1,24\n\
119         rsubk r5,r4,r5\n\
120         addk  r4,r4,r4\n\
121         addk  r4,r4,r4\n\
122         addk  r1,r1,r4\n\
123         swi   r5,r1,24\n\
124         swi   r3,r1,20\n\
125         addk  r6,r5,r0\n\
126         addk  r5,r5,r5\n\
127         addk  r5,r5,r5\n\
128         addik r7,r1,28\n\
129         addk  r8,r7,r5\n\
130         addik r8,r8,4\n\
131         lwi   r5,r20,_rtld_local@GOTOFF\n\
132         brlid r15,_dl_init_internal\n\
133         nop\n\
134         lwi   r5,r1,24\n\
135         lwi   r3,r1,20\n\
136         addk  r4,r5,r5\n\
137         addk  r4,r4,r4\n\
138         addik r6,r1,28\n\
139         addk  r7,r6,r4\n\
140         addik r7,r7,4\n\
141         addik r15,r20,_dl_fini@GOTOFF\n\
142         addik r15,r15,-8\n\
143         brad  r3\n\
144         addik r1,r1,24\n\
145         nop\n\
146         .size _dl_start_user, . - _dl_start_user\n\
147         .previous");
148
149 /* ELF_RTYPE_CLASS_PLT iff TYPE describes relocation of a PLT entry or
150    TLS variable, so undefined references should not be allowed to
151    define the value.
152    ELF_RTYPE_CLASS_NOCOPY iff TYPE should not be allowed to resolve to one
153    of the main executable's symbols, as for a COPY reloc.  */
154 #ifndef RTLD_BOOTSTRAP
155 # define elf_machine_type_class(type) \
156   (((type) == R_MICROBLAZE_JUMP_SLOT || \
157     (type) == R_MICROBLAZE_TLSDTPREL32 || \
158     (type) == R_MICROBLAZE_TLSDTPMOD32 || \
159     (type) == R_MICROBLAZE_TLSTPREL32) \
160     * ELF_RTYPE_CLASS_PLT \
161    | ((type) == R_MICROBLAZE_COPY) * ELF_RTYPE_CLASS_COPY)
162 #else
163 # define elf_machine_type_class(type) \
164   (((type) == R_MICROBLAZE_JUMP_SLOT) * ELF_RTYPE_CLASS_PLT \
165    | ((type) == R_MICROBLAZE_COPY) * ELF_RTYPE_CLASS_COPY)
166 #endif
167
168 /* A reloc type used for ld.so cmdline arg lookups to reject PLT entries.  */
169 #define ELF_MACHINE_JMP_SLOT    R_MICROBLAZE_JUMP_SLOT
170
171 /* The microblaze never uses Elf32_Rel relocations.  */
172 #define ELF_MACHINE_NO_REL 1
173
174 static inline Elf32_Addr
175 elf_machine_fixup_plt (struct link_map *map, lookup_t t,
176                        const Elf32_Rela *reloc,
177                        Elf32_Addr *reloc_addr, Elf32_Addr value)
178 {
179   return *reloc_addr = value;
180 }
181
182 /* Return the final value of a plt relocation. Ignore the addend.  */
183 static inline Elf32_Addr
184 elf_machine_plt_value (struct link_map *map, const Elf32_Rela *reloc,
185                        Elf32_Addr value)
186 {
187   return value;
188 }
189
190 #endif /* !dl_machine_h.  */
191
192 /* Names of the architecture-specific auditing callback functions.  */
193 #define ARCH_LA_PLTENTER microblaze_gnu_pltenter
194 #define ARCH_LA_PLTEXIT microblaze_gnu_pltexit
195
196 #ifdef RESOLVE_MAP
197
198 /* Perform the relocation specified by RELOC and SYM (which is fully resolved).
199    MAP is the object containing the reloc.  */
200
201 /* Macro to put 32-bit relocation value into 2 words.  */
202 #define PUT_REL_64(rel_addr,val) \
203   do { \
204     ((unsigned short *)(rel_addr))[1] = (val) >> 16; \
205     ((unsigned short *)(rel_addr))[3] = (val) & 0xffff; \
206   } while (0)
207
208 auto inline void __attribute__ ((always_inline))
209 elf_machine_rela (struct link_map *map, const Elf32_Rela *reloc,
210                   const Elf32_Sym *sym, const struct r_found_version *version,
211                   void *const reloc_addr_arg, int skip_ifunc)
212 {
213   Elf32_Addr *const reloc_addr = reloc_addr_arg;
214   const int r_type = ELF32_R_TYPE (reloc->r_info);
215
216   if (__builtin_expect (r_type == R_MICROBLAZE_64_PCREL, 0))
217     PUT_REL_64 (reloc_addr, map->l_addr + reloc->r_addend);
218   else if (r_type == R_MICROBLAZE_REL)
219     *reloc_addr = map->l_addr + reloc->r_addend;
220   else
221     {
222       const Elf32_Sym *const refsym = sym;
223       struct link_map *sym_map = RESOLVE_MAP (&sym, version, r_type);
224       Elf32_Addr value = sym == NULL ? 0 : sym_map->l_addr + sym->st_value;
225
226       value += reloc->r_addend;
227       if (r_type == R_MICROBLAZE_GLOB_DAT ||
228           r_type == R_MICROBLAZE_JUMP_SLOT ||
229           r_type == R_MICROBLAZE_32)
230         {
231           *reloc_addr = value;
232         }
233       else if (r_type == R_MICROBLAZE_COPY)
234         {
235           if (sym != NULL && (sym->st_size > refsym->st_size
236               || (sym->st_size < refsym->st_size && GLRO (dl_verbose))) )
237             {
238               const char *strtab;
239
240               strtab = (const void *) D_PTR (map, l_info[DT_STRTAB]);
241               _dl_error_printf ("\
242 %s: Symbol `%s' has different size in shared object, consider re-linking\n",
243                                 RTLD_PROGNAME, strtab + refsym->st_name);
244             }
245           memcpy (reloc_addr_arg, (void *) value,
246                   MIN (sym->st_size, refsym->st_size));
247         }
248       else if (r_type == R_MICROBLAZE_NONE)
249         {
250         }
251 #if !defined RTLD_BOOTSTRAP
252       else if (r_type == R_MICROBLAZE_TLSDTPMOD32)
253         {
254           if (sym_map != NULL)
255             *reloc_addr = sym_map->l_tls_modid;
256         }
257       else if (r_type == R_MICROBLAZE_TLSDTPREL32)
258         {
259           if (sym != NULL)
260             *reloc_addr = sym->st_value + reloc->r_addend;
261         }
262       else if (r_type == R_MICROBLAZE_TLSTPREL32)
263         {
264           if (sym != NULL)
265             {
266               CHECK_STATIC_TLS (map, sym_map);
267               *reloc_addr = sym->st_value + sym_map->l_tls_offset + reloc->r_addend;
268             }
269         }
270 #endif
271       else
272         {
273           _dl_reloc_bad_type (map, r_type, 0);
274         }
275     }
276 }
277
278 auto inline void
279 elf_machine_rela_relative (Elf32_Addr l_addr, const Elf32_Rela *reloc,
280                            void *const reloc_addr_arg)
281 {
282   Elf32_Addr *const reloc_addr = reloc_addr_arg;
283   PUT_REL_64 (reloc_addr, l_addr + reloc->r_addend);
284 }
285
286 auto inline void
287 elf_machine_lazy_rel (struct link_map *map,
288                       Elf32_Addr l_addr, const Elf32_Rela *reloc,
289                       int skip_ifunc)
290 {
291   Elf32_Addr *const reloc_addr = (void *) (l_addr + reloc->r_offset);
292   if (ELF32_R_TYPE (reloc->r_info) == R_MICROBLAZE_JUMP_SLOT)
293     *reloc_addr += l_addr;
294   else
295     _dl_reloc_bad_type (map, ELF32_R_TYPE (reloc->r_info), 1);
296 }
297
298 #endif /* RESOLVE_MAP.  */