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