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