1 /* Machine-dependent ELF dynamic relocation inline functions.  C-SKY version.
2    Copyright (C) 2018-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 "csky"
23 
24 #include <sys/param.h>
25 #include <sysdep.h>
26 #include <dl-tls.h>
27 #include <dl-static-tls.h>
28 #include <dl-machine-rel.h>
29 
30 /* Return nonzero if 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_CSKY;
35 }
36 
37 /* Return the link-time address of _DYNAMIC.
38    This must be inlined in a function which uses global data.  */
39 static inline Elf32_Addr
elf_machine_dynamic(void)40 elf_machine_dynamic (void)
41 {
42   register Elf32_Addr *got __asm__ ("gb");
43   return *got;
44 }
45 
46 /* Return the run-time load address ,of the shared object.  */
47 static inline Elf32_Addr
elf_machine_load_address(void)48 elf_machine_load_address (void)
49 {
50   extern Elf32_Addr __dl_start (void *) asm ("_dl_start");
51   Elf32_Addr got_addr = (Elf32_Addr) &__dl_start;
52   Elf32_Addr pcrel_addr;
53   asm  ("grs %0,_dl_start\n" : "=r" (pcrel_addr));
54 
55   return pcrel_addr - got_addr;
56 }
57 
58 
59 /* Set up the loaded object described by L so its unrelocated PLT
60    entries will jump to the on-demand fixup code in dl-runtime.c.  */
61 
62 static inline int __attribute__ ((always_inline))
elf_machine_runtime_setup(struct link_map * l,struct r_scope_elem * scope[],int lazy,int profile)63 elf_machine_runtime_setup (struct link_map *l, struct r_scope_elem *scope[],
64 			   int lazy, int profile)
65 {
66   Elf32_Addr *got;
67   extern void _dl_runtime_resolve (Elf32_Word);
68 
69   if (l->l_info[DT_JMPREL] && lazy)
70     {
71       /* The GOT entries for functions in the PLT have not yet been
72 	 filled in.  Their initial contents will arrange when called
73 	 to push an offset into the .rela.plt section, push
74 	 _GLOBAL_OFFSET_TABLE_[1], and then jump to
75 	 _GLOBAL_OFFSET_TABLE_[2].  */
76       got = (Elf32_Addr *) D_PTR (l, l_info[DT_PLTGOT]);
77 
78       if (got[1])
79 	l->l_mach.plt = got[1] + l->l_addr;
80       got[1] = (Elf32_Addr) l; /* Identify this shared object.  */
81 
82       /* The got[2] entry contains the address of a function which gets
83 	 called to get the address of a so far unresolved function and
84 	 jump to it.  The profiling extension of the dynamic linker allows
85 	 to intercept the calls to collect information.  In this case we
86 	 don't store the address in the GOT so that all future calls also
87 	 end in this function.  */
88 	got[2] = (Elf32_Addr) &_dl_runtime_resolve;
89     }
90   return lazy;
91 }
92 
93 /* Mask identifying addresses reserved for the user program,
94    where the dynamic linker should not map anything.  */
95 #define ELF_MACHINE_USER_ADDRESS_MASK 0x80000000UL
96 
97 /* Initial entry point code for the dynamic linker.
98    The C function `_dl_start' is the real entry point;
99    its return value is the user program's entry point.  */
100 #define RTLD_START asm ("\
101 .text\n\
102 .globl _start\n\
103 .type _start, @function\n\
104 .globl _dl_start_user\n\
105 .type _dl_start_user, @function\n\
106 _start:\n\
107 	grs	gb, .Lgetpc1\n\
108 .Lgetpc1:\n\
109 	lrw	t0, .Lgetpc1@GOTPC\n\
110 	addu	gb, t0\n\
111 	mov	a0, sp\n\
112 	lrw	t1, _dl_start@GOTOFF\n\
113 	addu	t1, gb\n\
114 	jsr	t1\n\
115 _dl_start_user:\n\
116 	/* get _dl_skip_args */    \n\
117 	lrw	r11, _dl_skip_args@GOTOFF\n\
118 	addu	r11, gb\n\
119 	ldw	r11, (r11, 0)\n\
120 	/* store program entry address in r11 */ \n\
121 	mov	r10, a0\n\
122 	/* Get argc */\n\
123 	ldw	a1, (sp, 0)\n\
124 	/* Get **argv */\n\
125 	mov	a2, sp\n\
126 	addi	a2, 4\n\
127 	cmpnei	r11, 0\n\
128 	bt	.L_fixup_stack\n\
129 .L_done_fixup:\n\
130 	mov	a3, a1\n\
131 	lsli	a3, 2\n\
132 	add	a3, a2\n\
133 	addi	a3, 4\n\
134 	lrw	a0, _rtld_local@GOTOFF\n\
135 	addu	a0, gb\n\
136 	ldw	a0, (a0, 0)\n\
137 	lrw	t1, _dl_init@PLT\n\
138 	addu	t1, gb\n\
139 	ldw	t1, (t1)\n\
140 	jsr	t1\n\
141 	lrw	a0, _dl_fini@GOTOFF\n\
142 	addu	a0, gb\n\
143 	jmp	r10\n\
144 .L_fixup_stack:\n\
145 	subu	a1, r11\n\
146 	lsli	r11, 2\n\
147 	addu	sp, r11\n\
148 	stw	a1, (sp, 0)\n\
149 	mov	a2, sp\n\
150 	addi	a2, 4\n\
151 	lrw	a3, _dl_argv@GOTOFF\n\
152 	addu	a3, gb\n\
153 	stw	a2, (a3, 0)\n\
154 	br	.L_done_fixup\n\
155 ");
156 
157 /* ELF_RTYPE_CLASS_PLT iff TYPE describes relocation of a PLT entry or
158    TLS variable, so undefined references should not be allowed to
159    define the value.
160    ELF_RTYPE_CLASS_NOCOPY iff TYPE should not be allowed to resolve to one
161    of the main executable's symbols, as for a COPY reloc.  */
162 #ifndef RTLD_BOOTSTRAP
163 # define elf_machine_type_class(type) \
164   ((((type) == R_CKCORE_JUMP_SLOT || (type) == R_CKCORE_TLS_DTPMOD32	   \
165      || (type) == R_CKCORE_TLS_DTPOFF32 || (type) == R_CKCORE_TLS_TPOFF32) \
166     * ELF_RTYPE_CLASS_PLT)						   \
167    | (((type) == R_CKCORE_COPY) * ELF_RTYPE_CLASS_COPY))
168 #else
169 # define elf_machine_type_class(type) \
170   ((((type) == R_CKCORE_JUMP_SLOT     \
171    | (((type) == R_CKCORE_COPY) * ELF_RTYPE_CLASS_COPY))
172 #endif
173 
174 /* A reloc type used for ld.so cmdline arg lookups to reject PLT entries.  */
175 #define ELF_MACHINE_JMP_SLOT R_CKCORE_JUMP_SLOT
176 
177 /* We define an initialization functions.  This is called very early in
178    _dl_sysdep_start.  */
179 #define DL_PLATFORM_INIT dl_platform_init ()
180 
181 static inline void __attribute__ ((unused))
dl_platform_init(void)182 dl_platform_init (void)
183 {
184   if (GLRO(dl_platform) != NULL && *GLRO(dl_platform) == '\0')
185     /* Avoid an empty string which would disturb us.  */
186     GLRO(dl_platform) = NULL;
187 }
188 
189 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)190 elf_machine_fixup_plt (struct link_map *map, lookup_t t,
191 		       const ElfW(Sym) *refsym, const ElfW(Sym) *sym,
192 		       const Elf32_Rela *reloc,
193 		       Elf32_Addr *reloc_addr, Elf32_Addr value)
194 {
195   return *reloc_addr = value;
196 }
197 
198 /* Return the final value of a plt relocation.  On the csky the JMP_SLOT
199    relocation ignores the addend.  */
200 static inline Elf32_Addr
elf_machine_plt_value(struct link_map * map,const Elf32_Rela * reloc,Elf32_Addr value)201 elf_machine_plt_value (struct link_map *map, const Elf32_Rela *reloc,
202 		       Elf32_Addr value)
203 {
204   return value;
205 }
206 
207 /* Names of the architecture-specific auditing callback functions.  */
208 #define ARCH_LA_PLTENTER csky_gnu_pltenter
209 #define ARCH_LA_PLTEXIT csky_gnu_pltexit
210 
211 #endif /* !dl_machine_h */
212 #ifdef RESOLVE_MAP
213 
214 /* Perform the relocation specified by RELOC and SYM (which is fully resolved).
215    MAP is the object containing the reloc.  */
216 
217 static inline void __attribute__ ((unused, 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)218 elf_machine_rela (struct link_map *map, struct r_scope_elem *scope[],
219 		  const Elf32_Rela *reloc, const Elf32_Sym *sym,
220 		  const struct r_found_version *version,
221 		  void *const reloc_addr_arg, int skip_ifunc)
222 {
223   Elf32_Addr *const reloc_addr = reloc_addr_arg;
224   const unsigned int r_type = ELF32_R_TYPE (reloc->r_info);
225   unsigned short __attribute__ ((unused)) *opcode16_addr;
226   Elf32_Addr __attribute__ ((unused)) insn_opcode = 0x0;
227 
228   if (__builtin_expect (r_type == R_CKCORE_RELATIVE, 0))
229     *reloc_addr = map->l_addr + reloc->r_addend;
230   else
231     {
232       const Elf32_Sym *const refsym = sym;
233       struct link_map *sym_map = RESOLVE_MAP (map, scope, &sym, version,
234 					      r_type);
235       ElfW(Addr) value = SYMBOL_ADDRESS (sym_map, sym, true);
236       opcode16_addr = (unsigned short *)reloc_addr;
237 
238       switch (r_type)
239 	{
240 	case R_CKCORE_COPY:
241 	  if (sym == NULL)
242 	    /* This can happen in trace mode if an object could not be
243 	       found.  */
244 	    break;
245 	  if (sym->st_size > refsym->st_size
246 	      || (sym->st_size < refsym->st_size && GLRO(dl_verbose)))
247 	    {
248 	      const char *strtab;
249 
250 	      strtab = (const void *) D_PTR (map, l_info[DT_STRTAB]);
251 	      _dl_error_printf ("\
252 %s: Symbol `%s' has different size in shared object, consider re-linking\n",
253 				rtld_progname ?: "<program name unknown>",
254 				strtab + refsym->st_name);
255 	    }
256 	  memcpy (reloc_addr_arg, (void *) value,
257 		  MIN (sym->st_size, refsym->st_size));
258 	  break;
259 	case R_CKCORE_GLOB_DAT:
260 	case R_CKCORE_JUMP_SLOT:
261 	  *reloc_addr = value;
262 	  break;
263 	case R_CKCORE_ADDR32:
264 	  *reloc_addr = value + reloc->r_addend;
265 	  break;
266 	case R_CKCORE_PCREL32:
267 	  *reloc_addr = value + reloc->r_addend - (Elf32_Addr) reloc_addr;
268 	  break;
269 #if defined(__CK810__) || defined(__CK807__)
270 	case R_CKCORE_ADDR_HI16:
271 	  insn_opcode = (*opcode16_addr << 16) | (*(opcode16_addr + 1));
272 	  insn_opcode = (insn_opcode & 0xffff0000)
273 			    | (((value + reloc->r_addend) >> 16) & 0xffff);
274 	  *(opcode16_addr++) = (unsigned short)(insn_opcode >> 16);
275 	  *opcode16_addr = (unsigned short)(insn_opcode & 0xffff);
276 	  break;
277 	case R_CKCORE_ADDR_LO16:
278 	  insn_opcode = (*opcode16_addr << 16) | (*(opcode16_addr + 1));
279 	  insn_opcode = (insn_opcode & 0xffff0000)
280 			    | ((value + reloc->r_addend) & 0xffff);
281 	   *(opcode16_addr++) = (unsigned short)(insn_opcode >> 16);
282 	   *opcode16_addr = (unsigned short)(insn_opcode & 0xffff);
283 	   break;
284 	case R_CKCORE_PCREL_IMM26BY2:
285 	{
286 	  unsigned int offset = ((value + reloc->r_addend
287 				  - (unsigned int)reloc_addr) >> 1);
288 	  insn_opcode = (*opcode16_addr << 16) | (*(opcode16_addr + 1));
289 	  if (offset > 0x3ffffff){
290 	    const char *strtab;
291 	    strtab = (const void *) D_PTR (map, l_info[DT_STRTAB]);
292 
293 	    _dl_error_printf ("\
294 %s:The reloc R_CKCORE_PCREL_IMM26BY2 cannot reach the symbol '%s'.\n",
295 	      rtld_progname ?: "<program name unknown>",
296 	      strtab + refsym->st_name);
297 	    break;
298 	  }
299 	  insn_opcode = (insn_opcode & ~0x3ffffff) | offset;
300 	  *(opcode16_addr++) = (unsigned short)(insn_opcode >> 16);
301 	  *opcode16_addr = (unsigned short)(insn_opcode & 0xffff);
302 	  break;
303 	}
304 	case R_CKCORE_PCREL_JSR_IMM26BY2:
305 	  break;
306 #endif
307 #ifndef RTLD_BOOTSTRAP
308 	case R_CKCORE_TLS_DTPMOD32:
309 	/* Get the information from the link map returned by the
310 	   resolv function.  */
311 	  if (sym_map != NULL)
312 	    *reloc_addr = sym_map->l_tls_modid;
313 	  break;
314 	case R_CKCORE_TLS_DTPOFF32:
315 	  if (sym != NULL)
316 	    *reloc_addr =(sym == NULL ? 0 : sym->st_value) + reloc->r_addend;
317 	  break;
318 	case R_CKCORE_TLS_TPOFF32:
319 	  if (sym != NULL)
320 	    {
321 	      CHECK_STATIC_TLS (map, sym_map);
322 	      *reloc_addr = (sym->st_value + sym_map->l_tls_offset
323 			     + reloc->r_addend);
324 	    }
325 	  break;
326 #endif /* !RTLD_BOOTSTRAP */
327 	case R_CKCORE_NONE:
328 	  break;
329 	default:
330 	  break;
331 	}
332     }
333 }
334 
335 static inline void __attribute__ ((unused, always_inline))
elf_machine_rela_relative(Elf32_Addr l_addr,const Elf32_Rela * reloc,void * const reloc_addr_arg)336 elf_machine_rela_relative (Elf32_Addr l_addr, const Elf32_Rela *reloc,
337 			   void *const reloc_addr_arg)
338 {
339   Elf32_Addr *const reloc_addr = reloc_addr_arg;
340   *reloc_addr = l_addr + reloc->r_addend;
341 }
342 
343 static inline void __attribute__ ((unused, always_inline))
elf_machine_lazy_rel(struct link_map * map,struct r_scope_elem * scope[],Elf32_Addr l_addr,const Elf32_Rela * reloc,int skip_ifunc)344 elf_machine_lazy_rel (struct link_map *map, struct r_scope_elem *scope[],
345 		      Elf32_Addr l_addr, const Elf32_Rela *reloc,
346 		      int skip_ifunc)
347 {
348   Elf32_Addr *const reloc_addr = (void *) (l_addr + reloc->r_offset);
349   const unsigned int r_type = ELF32_R_TYPE (reloc->r_info);
350   if (ELF32_R_TYPE (reloc->r_info) == R_CKCORE_JUMP_SLOT)
351     {
352       /* Check for unexpected PLT reloc type.  */
353       if (__builtin_expect (r_type == R_CKCORE_JUMP_SLOT, 1))
354 	{
355 	  if (__builtin_expect (map->l_mach.plt, 0) == 0)
356 	    *reloc_addr = l_addr + reloc->r_addend;
357 	  else
358 	    *reloc_addr = map->l_mach.plt;
359 	}
360     }
361 }
362 
363 #endif /* RESOLVE_MAP */
364