1 /* Machine-dependent ELF dynamic relocation inline functions.  ARC version.
2    Copyright (C) 2020-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 "arc"
23 
24 #include <entry.h>
25 
26 #ifndef ENTRY_POINT
27 # error ENTRY_POINT needs to be defined for ARC
28 #endif
29 
30 #include <string.h>
31 #include <link.h>
32 #include <dl-tls.h>
33 #include <dl-static-tls.h>
34 #include <dl-machine-rel.h>
35 
36 /* Dynamic Linking ABI for ARCv2 ISA.
37 
38                         PLT
39           --------------------------------	<---- DT_PLTGOT
40           |  ld r11, [pcl, off-to-GOT[1] |  0
41           |                              |  4
42    plt0   |  ld r10, [pcl, off-to-GOT[2] |  8
43           |                              | 12
44           |  j [r10]                     | 16
45           --------------------------------
46           |    Base address of GOT       | 20
47           --------------------------------
48           |  ld r12, [pcl, off-to-GOT[3] | 24
49    plt1   |                              |
50           |  j.d    [r12]                | 32
51           |  mov    r12, pcl             | 36
52           --------------------------------
53           |                              | 40
54           ~                              ~
55           ~                              ~
56           |                              |
57           --------------------------------
58 
59                .got
60           --------------
61           |    [0]     |
62           |    ...     |  Runtime address for data symbols
63           |    [n]     |
64           --------------
65 
66             .got.plt
67           --------------
68           |    [0]     |  Build address of .dynamic
69           --------------
70           |    [1]     |  Module info - setup by ld.so
71           --------------
72           |    [2]     |  resolver entry point
73           --------------
74           |    [3]     |
75           |    ...     |  Runtime address for function symbols
76           |    [f]     |
77           --------------
78 
79    For ARCompact, the PLT is 12 bytes due to short instructions
80 
81           --------------------------------
82           |  ld r12, [pcl, off-to-GOT[3] | 24   (12 bytes each)
83    plt1   |                              |
84           |  j_s.d  [r12]                | 32
85           |  mov_s  r12, pcl             | 34
86           --------------------------------
87           |                              | 36  */
88 
89 /* Return nonzero iff ELF header is compatible with the running host.  */
90 static inline int
elf_machine_matches_host(const ElfW (Ehdr)* ehdr)91 elf_machine_matches_host (const ElfW(Ehdr) *ehdr)
92 {
93   return (ehdr->e_machine == EM_ARCV2		 /* ARC HS.  */
94 	  || ehdr->e_machine == EM_ARC_COMPACT); /* ARC 700.  */
95 }
96 
97 /* Get build time address of .dynamic as setup in GOT[0]
98    This is called very early in _dl_start so it has not been relocated to
99    runtime value.  */
100 static inline ElfW(Addr)
elf_machine_dynamic(void)101 elf_machine_dynamic (void)
102 {
103   extern const ElfW(Addr) _GLOBAL_OFFSET_TABLE_[] attribute_hidden;
104   return _GLOBAL_OFFSET_TABLE_[0];
105 }
106 
107 
108 /* Return the run-time load address of the shared object.  */
109 static inline ElfW(Addr)
elf_machine_load_address(void)110 elf_machine_load_address (void)
111 {
112   ElfW(Addr) build_addr, run_addr;
113 
114   /* For build address, below generates
115      ld  r0, [pcl, _GLOBAL_OFFSET_TABLE_@pcl].  */
116   build_addr = elf_machine_dynamic ();
117   __asm__ ("add %0, pcl, _DYNAMIC@pcl	\n" : "=r" (run_addr));
118 
119   return run_addr - build_addr;
120 }
121 
122 /* Set up the loaded object described by L so its unrelocated PLT
123    entries will jump to the on-demand fixup code in dl-runtime.c.  */
124 
125 static inline int
126 __attribute__ ((always_inline))
elf_machine_runtime_setup(struct link_map * l,struct r_scope_elem * scope[],int lazy,int profile)127 elf_machine_runtime_setup (struct link_map *l, struct r_scope_elem *scope[],
128 			   int lazy, int profile)
129 {
130   extern void _dl_runtime_resolve (void);
131 
132   if (l->l_info[DT_JMPREL] && lazy)
133     {
134       /* On ARC DT_PLTGOT point to .plt whose 5th word (after the PLT header)
135          contains the address of .got.  */
136       ElfW(Addr) *plt_base = (ElfW(Addr) *) D_PTR (l, l_info[DT_PLTGOT]);
137       ElfW(Addr) *got = (ElfW(Addr) *) (plt_base[5] + l->l_addr);
138 
139       got[1] = (ElfW(Addr)) l;	/* Identify this shared object.  */
140 
141       /* This function will get called to fix up the GOT entry indicated by
142 	 the offset on the stack, and then jump to the resolved address.  */
143       got[2] = (ElfW(Addr)) &_dl_runtime_resolve;
144     }
145 
146   return lazy;
147 }
148 
149 /* What this code does:
150     -ldso starts execution here when kernel returns from execve
151     -calls into generic ldso entry point _dl_start
152     -optionally adjusts argc for executable if exec passed as cmd
153     -calls into app main with address of finaliser.  */
154 
155 #define RTLD_START asm ("\
156 .text									\n\
157 .globl __start								\n\
158 .type __start, @function						\n\
159 __start:								\n\
160 	/* (1). bootstrap ld.so.  */					\n\
161 	bl.d    _dl_start                                       	\n\
162 	mov_s   r0, sp  /* pass ptr to aux vector tbl.    */    	\n\
163 	mov r13, r0	/* safekeep app elf entry point.  */		\n\
164 									\n\
165 	/* (2). If ldso ran with executable as arg.       */		\n\
166 	/*      skip the extra args calc by dl_start.     */		\n\
167 	ld_s    r1, [sp]       /* orig argc.  */			\n\
168 	ld      r12, [pcl, _dl_skip_args@pcl]                   	\n\
169 	breq	r12, 0, 1f						\n\
170 									\n\
171 	add2    sp, sp, r12 /* discard argv entries from stack.  */	\n\
172 	sub_s   r1, r1, r12 /* adjusted argc on stack.  */      	\n\
173 	st_s    r1, [sp]                                        	\n\
174 	add	r2, sp, 4						\n\
175 	/* intermediate LD for ST emcoding limitations.  */		\n\
176 	ld	r3, [pcl, _dl_argv@gotpc]    				\n\
177 	st	r2, [r3]						\n\
178 1:									\n\
179 	/* (3). call preinit stuff.  */					\n\
180 	ld	r0, [pcl, _rtld_local@pcl]				\n\
181 	add	r2, sp, 4	; argv					\n\
182 	add2	r3, r2, r1						\n\
183 	add	r3, r3, 4	; env					\n\
184 	bl	_dl_init@plt						\n\
185 									\n\
186 	/* (4) call app elf entry point.  */				\n\
187 	add     r0, pcl, _dl_fini@pcl					\n\
188 	j	[r13]							\n\
189 									\n\
190 	.size  __start,.-__start                               		\n\
191 	.previous                                               	\n\
192 ");
193 
194 /* ELF_RTYPE_CLASS_PLT iff TYPE describes relocation of a PLT entry, so
195    PLT entries should not be allowed to define the value.
196    ELF_RTYPE_CLASS_NOCOPY iff TYPE should not be allowed to resolve to one
197    of the main executable's symbols, as for a COPY reloc.  */
198 #define elf_machine_type_class(type)				\
199   ((((type) == R_ARC_JUMP_SLOT					\
200      || (type) == R_ARC_TLS_DTPMOD				\
201      || (type) == R_ARC_TLS_DTPOFF				\
202      || (type) == R_ARC_TLS_TPOFF) * ELF_RTYPE_CLASS_PLT)	\
203    | (((type) == R_ARC_COPY) * ELF_RTYPE_CLASS_COPY))
204 
205 /* A reloc type used for ld.so cmdline arg lookups to reject PLT entries.  */
206 #define ELF_MACHINE_JMP_SLOT  R_ARC_JUMP_SLOT
207 
208 /* Fixup a PLT entry to bounce directly to the function at VALUE.  */
209 
210 static inline ElfW(Addr)
elf_machine_fixup_plt(struct link_map * map,lookup_t t,const ElfW (Sym)* refsym,const ElfW (Sym)* sym,const ElfW (Rela)* reloc,ElfW (Addr)* reloc_addr,ElfW (Addr)value)211 elf_machine_fixup_plt (struct link_map *map, lookup_t t,
212 		       const ElfW(Sym) *refsym, const ElfW(Sym) *sym,
213 		       const ElfW(Rela) *reloc,
214 		       ElfW(Addr) *reloc_addr, ElfW(Addr) value)
215 {
216   return *reloc_addr = value;
217 }
218 
219 /* Return the final value of a plt relocation.  */
220 #define elf_machine_plt_value(map, reloc, value) (value)
221 
222 /* Names of the architecture-specific auditing callback functions.  */
223 #define ARCH_LA_PLTENTER arc_gnu_pltenter
224 #define ARCH_LA_PLTEXIT arc_gnu_pltexit
225 
226 #endif /* dl_machine_h */
227 
228 #ifdef RESOLVE_MAP
229 
230 static inline void
231 __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)232 elf_machine_rela (struct link_map *map, struct r_scope_elem *scope[],
233 		  const ElfW(Rela) *reloc, const ElfW(Sym) *sym,
234 		  const struct r_found_version *version,
235                   void *const reloc_addr_arg, int skip_ifunc)
236 {
237   ElfW(Addr) r_info = reloc->r_info;
238   const unsigned long int r_type = ELFW (R_TYPE) (r_info);
239   ElfW(Addr) *const reloc_addr = reloc_addr_arg;
240 
241   if (__glibc_unlikely (r_type == R_ARC_RELATIVE))
242     *reloc_addr += map->l_addr;
243   else if (__glibc_unlikely (r_type == R_ARC_NONE))
244     return;
245   else
246     {
247       const ElfW(Sym) *const refsym = sym;
248       struct link_map *sym_map = RESOLVE_MAP (map, scope, &sym, version,
249 					      r_type);
250       ElfW(Addr) value = SYMBOL_ADDRESS (sym_map, sym, true);
251 
252       switch (r_type)
253         {
254         case R_ARC_COPY:
255           if (__glibc_unlikely (sym == NULL))
256             /* This can happen in trace mode if an object could not be
257                found.  */
258             break;
259 
260           size_t size = sym->st_size;
261           if (__glibc_unlikely (size != refsym->st_size))
262             {
263               const char *strtab = (const void *) D_PTR (map, l_info[DT_STRTAB]);
264               if (sym->st_size > refsym->st_size)
265                 size = refsym->st_size;
266               if (sym->st_size > refsym->st_size || GLRO(dl_verbose))
267                 _dl_error_printf ("\
268   %s: Symbol `%s' has different size in shared object, consider re-linking\n",
269                                   rtld_progname ?: "<program name unknown>",
270                                   strtab + refsym->st_name);
271             }
272 
273           memcpy (reloc_addr_arg, (void *) value, size);
274           break;
275 
276         case R_ARC_GLOB_DAT:
277         case R_ARC_JUMP_SLOT:
278             *reloc_addr = value;
279           break;
280 
281         case R_ARC_TLS_DTPMOD:
282           if (sym_map != NULL)
283             /* Get the information from the link map returned by the
284                resolv function.  */
285             *reloc_addr = sym_map->l_tls_modid;
286           break;
287 
288         case R_ARC_TLS_DTPOFF:
289           if (sym != NULL)
290             /* Offset set by the linker in the GOT entry would be overwritten
291                by dynamic loader instead of added to the symbol location.
292                Other target have the same approach on DTPOFF relocs.  */
293             *reloc_addr += sym->st_value;
294           break;
295 
296         case R_ARC_TLS_TPOFF:
297           if (sym != NULL)
298             {
299               CHECK_STATIC_TLS (map, sym_map);
300               *reloc_addr = sym_map->l_tls_offset + sym->st_value + reloc->r_addend;
301             }
302           break;
303 
304         case R_ARC_32:
305           *reloc_addr += value + reloc->r_addend;
306           break;
307 
308         case R_ARC_PC32:
309           *reloc_addr += value + reloc->r_addend - (unsigned long int) reloc_addr;
310           break;
311 
312         default:
313           _dl_reloc_bad_type (map, r_type, 0);
314           break;
315         }
316     }
317 }
318 
319 static inline void
320 __attribute__ ((always_inline))
elf_machine_rela_relative(ElfW (Addr)l_addr,const ElfW (Rela)* reloc,void * const reloc_addr_arg)321 elf_machine_rela_relative (ElfW(Addr) l_addr, const ElfW(Rela) *reloc,
322                            void *const reloc_addr_arg)
323 {
324   ElfW(Addr) *const reloc_addr = reloc_addr_arg;
325   *reloc_addr += l_addr;
326 }
327 
328 static inline void
329 __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)330 elf_machine_lazy_rel (struct link_map *map, struct r_scope_elem *scope[],
331 		      ElfW(Addr) l_addr, const ElfW(Rela) *reloc,
332 		      int skip_ifunc)
333 {
334   ElfW(Addr) *const reloc_addr = (void *) (l_addr + reloc->r_offset);
335   const unsigned int r_type = ELFW (R_TYPE) (reloc->r_info);
336 
337   if (r_type == R_ARC_JUMP_SLOT)
338     *reloc_addr += l_addr;
339   else
340     _dl_reloc_bad_type (map, r_type, 1);
341 }
342 
343 #endif /* RESOLVE_MAP */
344