e000cd081f18a12bc3477fe7a1350642d2e6391f
[platform/upstream/glibc.git] / sysdeps / nios2 / dl-machine.h
1 /* Machine-dependent ELF dynamic relocation inline functions.  Nios II version.
2    Copyright (C) 1995-2021 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, see
17    <https://www.gnu.org/licenses/>.  */
18
19 #ifndef dl_machine_h
20 #define dl_machine_h
21
22 #define ELF_MACHINE_NAME "nios2"
23
24 #include <string.h>
25 #include <link.h>
26 #include <dl-tls.h>
27
28 /* Return nonzero iff ELF header is compatible with the running host.  */
29 static inline int
30 elf_machine_matches_host (const Elf32_Ehdr *ehdr)
31 {
32   return ehdr->e_machine == EM_ALTERA_NIOS2;
33 }
34
35
36 /* Return the link-time address of _DYNAMIC.  Conveniently, this is the
37    first element of the GOT.  */
38 static inline Elf32_Addr
39 elf_machine_dynamic (void)
40 {
41   Elf32_Addr *dynamic;
42   int tmp;
43   asm ("nextpc\t%0\n\t"
44        "1: movhi\t%1, %%hiadj(_GLOBAL_OFFSET_TABLE_ - 1b)\n\t"
45        "addi\t%1, %1, %%lo(_GLOBAL_OFFSET_TABLE_ - 1b)\n\t"
46        "add\t%0, %0, %1\n"
47        : "=r" (dynamic), "=r" (tmp));
48   return *dynamic;
49 }
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   Elf32_Addr result;
57   int tmp;
58   asm ("nextpc\t%0\n\t"
59        "1: movhi\t%1, %%hiadj(1b)\n\t"
60        "addi\t%1, %1, %%lo(1b)\n\t"
61        "sub\t%0, %0, %1\n"
62        : "=r" (result), "=r" (tmp));
63   return result;
64 }
65
66 /* Set up the loaded object described by L so its unrelocated PLT
67    entries will jump to the on-demand fixup code in dl-runtime.c.  */
68
69 static inline int __attribute__ ((always_inline))
70 elf_machine_runtime_setup (struct link_map *l, int lazy, int profile)
71 {
72   extern void _dl_runtime_resolve (Elf32_Word);
73
74   if (l->l_info[DT_JMPREL] && lazy)
75     {
76       /* The GOT entries for functions in the PLT have not yet been filled
77          in.  Their initial contents will arrange when called to load r15 with
78          an offset into the .got section, load r14 with
79          _GLOBAL_OFFSET_TABLE_[1], and then jump to _GLOBAL_OFFSET_TABLE[2].
80       */
81       Elf32_Addr *got = (Elf32_Addr *) D_PTR (l, l_info[DT_PLTGOT]);
82       got[1] = (Elf32_Addr) l;  /* Identify this shared object.  */
83
84       /* This function will get called to fix up the GOT entry indicated by
85          the offset on the stack, and then jump to the resolved address.  */
86       got[2] = (Elf32_Addr) &_dl_runtime_resolve;
87     }
88
89   return lazy;
90 }
91
92 /* Initial entry point code for the dynamic linker.
93    The C function `_dl_start' is the real entry point;
94    its return value is the user program's entry point.  */
95
96 #define RTLD_START asm ("\
97 .text\n\
98 .globl _start\n\
99 .type _start, %function\n\
100 _start:\n\
101         /* At start time, all the args are on the stack.  */\n\
102         mov r4, sp\n\
103 \n\
104         /* Start the calculation of the GOT pointer.  */\n\
105         nextpc r22\n\
106 1:      movhi r8, %hiadj(_gp_got - 1b)\n\
107         addi r8, r8, %lo(_gp_got - 1b)\n\
108 \n\
109         /* Figure out where _dl_start will need to return to.  */\n\
110         movhi ra, %hiadj(2f - 1b)\n\
111         addi ra, ra, %lo(2f - 1b)\n\
112         add ra, ra, r22\n\
113 \n\
114         /* Finish the calculation of the GOT pointer.  */\n\
115         add r22, r22, r8\n\
116 \n\
117         br _dl_start\n\
118 \n\
119         /* Save the returned user entry point.  */\n\
120 2:      mov r16, r2\n\
121 \n\
122         /* Initialize gp.  */\n\
123         ldw r4, %got(_rtld_local)(r22)\n\
124         ldw r4, 0(r4)\n\
125         ldw r8, %call(_dl_nios2_get_gp_value)(r22)\n\
126         callr r8\n\
127         mov gp, r2\n\
128 \n\
129         /* Find the number of arguments to skip.  */\n\
130         ldw r8, %got(_dl_skip_args)(r22)\n\
131         ldw r8, 0(r8)\n\
132 \n\
133         /* Find the main_map from the GOT.  */\n\
134         ldw r4, %got(_rtld_local)(r22)\n\
135         ldw r4, 0(r4)\n\
136 \n\
137         /* Find argc.  */\n\
138         ldw r5, 0(sp)\n\
139         sub r5, r5, r8\n\
140         stw r5, 0(sp)\n\
141 \n\
142         /* Find the first unskipped argument.  */\n\
143         slli r8, r8, 2\n\
144         addi r6, sp, 4\n\
145         add r9, r6, r8\n\
146         mov r10, r6\n\
147 \n\
148         /* Shuffle argv down.  */\n\
149 3:      ldw r11, 0(r9)\n\
150         stw r11, 0(r10)\n\
151         addi r9, r9, 4\n\
152         addi r10, r10, 4\n\
153         bne r11, zero, 3b\n\
154 \n\
155         /* Shuffle envp down.  */\n\
156         mov r7, r10\n\
157 4:      ldw r11, 0(r9)\n\
158         stw r11, 0(r10)\n\
159         addi r9, r9, 4\n\
160         addi r10, r10, 4\n\
161         bne r11, zero, 4b\n\
162 \n\
163         /* Shuffle auxv down.  */\n\
164 5:      ldw r11, 4(r9)\n\
165         stw r11, 4(r10)\n\
166         ldw r11, 0(r9)\n\
167         stw r11, 0(r10)\n\
168         addi r9, r9, 8\n\
169         addi r10, r10, 8\n\
170         bne r11, zero, 5b\n\
171 \n\
172         /* Update _dl_argv.  */\n\
173         ldw r2, %got(_dl_argv)(r22)\n\
174         stw r6, 0(r2)\n\
175 \n\
176         /* Call _dl_init through the PLT.  */\n\
177         ldw r8, %call(_dl_init)(r22)\n\
178         callr r8\n\
179 \n\
180         /* Find the finalization function.  */\n\
181         ldw r4, %got(_dl_fini)(r22)\n\
182 \n\
183         /* Jump to the user's entry point.  */\n\
184         jmp r16\n\
185 ");
186
187 /* ELF_RTYPE_CLASS_PLT iff TYPE describes relocation of a PLT entry, so
188    PLT entries should not be allowed to define the value.
189    ELF_RTYPE_CLASS_COPY iff TYPE should not be allowed to resolve to one
190    of the main executable's symbols, as for a COPY reloc.  */
191 #define elf_machine_type_class(type)                            \
192   ((((type) == R_NIOS2_JUMP_SLOT                                \
193      || (type) == R_NIOS2_TLS_DTPMOD                            \
194      || (type) == R_NIOS2_TLS_DTPREL                            \
195      || (type) == R_NIOS2_TLS_TPREL) * ELF_RTYPE_CLASS_PLT)     \
196    | (((type) == R_NIOS2_COPY) * ELF_RTYPE_CLASS_COPY)          \
197    | (((type) == R_NIOS2_GLOB_DAT) * ELF_RTYPE_CLASS_EXTERN_PROTECTED_DATA))
198
199 /* A reloc type used for ld.so cmdline arg lookups to reject PLT entries.  */
200 #define ELF_MACHINE_JMP_SLOT  R_NIOS2_JUMP_SLOT
201
202 /* The Nios II never uses Elf32_Rel relocations.  */
203 #define ELF_MACHINE_NO_REL 1
204 #define ELF_MACHINE_NO_RELA 0
205
206 /* Fixup a PLT entry to bounce directly to the function at VALUE.  */
207
208 static inline Elf32_Addr
209 elf_machine_fixup_plt (struct link_map *map, lookup_t t,
210                        const ElfW(Sym) *refsym, const ElfW(Sym) *sym,
211                        const Elf32_Rela *reloc,
212                        Elf32_Addr *reloc_addr, Elf32_Addr value)
213 {
214   return *reloc_addr = value;
215 }
216
217 /* Return the final value of a plt relocation.  */
218 static inline Elf32_Addr
219 elf_machine_plt_value (struct link_map *map, const Elf32_Rela *reloc,
220                        Elf32_Addr value)
221 {
222   return value;
223 }
224
225 /* Names of the architecture-specific auditing callback functions.  */
226 #define ARCH_LA_PLTENTER nios2_gnu_pltenter
227 #define ARCH_LA_PLTEXIT nios2_gnu_pltexit
228
229 #endif /* dl_machine_h */
230
231 #ifdef RESOLVE_MAP
232
233 /* Perform the relocation specified by RELOC and SYM (which is fully resolved).
234    LOADADDR is the load address of the object; INFO is an array indexed
235    by DT_* of the .dynamic section info.  */
236
237 auto inline void __attribute__ ((always_inline))
238 elf_machine_rela (struct link_map *map, const ElfW(Rela) *reloc,
239                   const ElfW(Sym) *sym, const struct r_found_version *version,
240                   void *const reloc_addr_arg, int skip_ifunc)
241 {
242   Elf32_Addr *const reloc_addr = reloc_addr_arg;
243   const unsigned int r_type = ELF32_R_TYPE (reloc->r_info);
244
245   if (__glibc_unlikely (r_type == R_NIOS2_RELATIVE))
246     *reloc_addr = map->l_addr + reloc->r_addend;
247   else if (__glibc_unlikely (r_type == R_NIOS2_NONE))
248     return;
249   else
250     {
251       const Elf32_Sym *const refsym = sym;
252       struct link_map *sym_map = RESOLVE_MAP (&sym, version, r_type);
253       Elf32_Addr value = SYMBOL_ADDRESS (sym_map, sym, true);
254
255       switch (r_type)
256         {
257         case R_NIOS2_COPY:
258           if (sym == NULL)
259             /* This can happen in trace mode if an object could not be
260                found.  */
261             break;
262           if (sym->st_size > refsym->st_size
263               || (sym->st_size < refsym->st_size && GLRO(dl_verbose)))
264             {
265               const char *strtab;
266
267               strtab = (const void *) D_PTR (map, l_info[DT_STRTAB]);
268               _dl_error_printf ("\
269 %s: Symbol `%s' has different size in shared object, consider re-linking\n",
270                                 rtld_progname ?: "<program name unknown>",
271                                 strtab + refsym->st_name);
272             }
273           memcpy (reloc_addr_arg, (void *) value,
274                   MIN (sym->st_size, refsym->st_size));
275           break;
276         case R_NIOS2_GLOB_DAT:
277         case R_NIOS2_JUMP_SLOT:
278 # ifdef RTLD_BOOTSTRAP
279           /* Fix weak undefined references.  */
280           if (sym != NULL && sym->st_value == 0)
281             *reloc_addr = 0;
282           else
283 # endif
284             *reloc_addr = value;
285           break;
286 #ifndef RTLD_BOOTSTRAP
287         case R_NIOS2_TLS_DTPMOD:
288           /* Get the information from the link map returned by the
289              resolv function.  */
290           if (sym_map != NULL)
291             *reloc_addr = sym_map->l_tls_modid;
292           break;
293
294         case R_NIOS2_TLS_DTPREL:
295           *reloc_addr = reloc->r_addend + TLS_DTPREL_VALUE(sym);
296           break;
297
298         case R_NIOS2_TLS_TPREL:
299           if (sym != NULL)
300             {
301               CHECK_STATIC_TLS (map, sym_map);
302               *reloc_addr = reloc->r_addend + TLS_TPREL_VALUE(sym_map, sym);
303             }
304           break;
305 #endif
306         case R_NIOS2_BFD_RELOC_32:
307           *reloc_addr = value + reloc->r_addend;
308           break;
309
310         default:
311           _dl_reloc_bad_type (map, r_type, 0);
312           break;
313         }
314     }
315 }
316
317 auto inline void __attribute__((always_inline))
318 elf_machine_rela_relative (ElfW(Addr) l_addr, const ElfW(Rela) *reloc,
319                            void *const reloc_addr_arg)
320 {
321   Elf32_Addr *const reloc_addr = reloc_addr_arg;
322   *reloc_addr = l_addr + reloc->r_addend;
323 }
324
325 auto inline void __attribute__((always_inline))
326 elf_machine_lazy_rel (struct link_map *map,
327                       ElfW(Addr) l_addr, const ElfW(Rela) *reloc,
328                       int skip_ifunc)
329 {
330   Elf32_Addr *const reloc_addr = (void *) (l_addr + reloc->r_offset);
331   if (ELF32_R_TYPE (reloc->r_info) == R_NIOS2_JUMP_SLOT)
332     *reloc_addr += l_addr;
333   else
334     _dl_reloc_bad_type (map, ELF32_R_TYPE (reloc->r_info), 1);
335 }
336
337 #endif /* RESOLVE_MAP */