1 | /* $NetBSD: rtld.h,v 1.136 2018/12/30 01:48:37 christos Exp $ */ |
2 | |
3 | /* |
4 | * Copyright 1996 John D. Polstra. |
5 | * Copyright 1996 Matt Thomas <matt@3am-software.com> |
6 | * All rights reserved. |
7 | * |
8 | * Redistribution and use in source and binary forms, with or without |
9 | * modification, are permitted provided that the following conditions |
10 | * are met: |
11 | * 1. Redistributions of source code must retain the above copyright |
12 | * notice, this list of conditions and the following disclaimer. |
13 | * 2. Redistributions in binary form must reproduce the above copyright |
14 | * notice, this list of conditions and the following disclaimer in the |
15 | * documentation and/or other materials provided with the distribution. |
16 | * 3. All advertising materials mentioning features or use of this software |
17 | * must display the following acknowledgement: |
18 | * This product includes software developed by John Polstra. |
19 | * 4. The name of the author may not be used to endorse or promote products |
20 | * derived from this software without specific prior written permission. |
21 | * |
22 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR |
23 | * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES |
24 | * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. |
25 | * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, |
26 | * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT |
27 | * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
28 | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
29 | * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
30 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF |
31 | * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
32 | */ |
33 | |
34 | #ifndef RTLD_H |
35 | #define RTLD_H |
36 | |
37 | #include <dlfcn.h> |
38 | #include <signal.h> |
39 | #include <stdbool.h> |
40 | #include <stddef.h> |
41 | #include <sys/param.h> |
42 | #include <sys/types.h> |
43 | #include <sys/queue.h> |
44 | #include <sys/exec_elf.h> |
45 | #include <sys/tls.h> |
46 | #include "rtldenv.h" |
47 | #include "link.h" |
48 | |
49 | #if defined(_RTLD_SOURCE) |
50 | |
51 | #if defined(__ARM_EABI__) && !defined(__ARM_DWARF_EH__) |
52 | #include "unwind.h" |
53 | #endif |
54 | |
55 | #ifndef RTLD_DEFAULT_LIBRARY_PATH |
56 | #define RTLD_DEFAULT_LIBRARY_PATH "/usr/lib" |
57 | #endif |
58 | #define _PATH_LD_HINTS "/etc/ld.so.conf" |
59 | |
60 | extern size_t _rtld_pagesz; |
61 | |
62 | #define round_down(x) ((x) & ~(_rtld_pagesz - 1)) |
63 | #define round_up(x) round_down((x) + _rtld_pagesz - 1) |
64 | |
65 | #define NEW(type) ((type *) xmalloc(sizeof(type))) |
66 | #define CNEW(type) ((type *) xcalloc(sizeof(type))) |
67 | |
68 | /* |
69 | * Fill in a DoneList with an allocation large enough to hold all of |
70 | * the currently-loaded objects. Keep this in a macro since it calls |
71 | * alloca and we want that to occur within the scope of the caller. |
72 | */ |
73 | #define _rtld_donelist_init(dlp) \ |
74 | ((dlp)->num_alloc = _rtld_objcount, \ |
75 | (dlp)->objs = alloca((dlp)->num_alloc * sizeof((dlp)->objs[0])), \ |
76 | assert((dlp)->objs != NULL), \ |
77 | (dlp)->num_used = 0) |
78 | |
79 | #endif /* _RTLD_SOURCE */ |
80 | |
81 | /* |
82 | * C++ has mandated the use of the following keywords for its new boolean |
83 | * type. We might as well follow their lead. |
84 | */ |
85 | struct Struct_Obj_Entry; |
86 | |
87 | typedef struct Struct_Objlist_Entry { |
88 | SIMPLEQ_ENTRY(Struct_Objlist_Entry) link; |
89 | struct Struct_Obj_Entry *obj; |
90 | } Objlist_Entry; |
91 | |
92 | typedef SIMPLEQ_HEAD(Struct_Objlist, Struct_Objlist_Entry) Objlist; |
93 | |
94 | typedef struct Struct_Name_Entry { |
95 | SIMPLEQ_ENTRY(Struct_Name_Entry) link; |
96 | char name[1]; |
97 | } Name_Entry; |
98 | |
99 | typedef struct Struct_Needed_Entry { |
100 | struct Struct_Needed_Entry *next; |
101 | struct Struct_Obj_Entry *obj; |
102 | unsigned long name; /* Offset of name in string table */ |
103 | } Needed_Entry; |
104 | |
105 | typedef struct _rtld_search_path_t { |
106 | struct _rtld_search_path_t *sp_next; |
107 | const char *sp_path; |
108 | size_t sp_pathlen; |
109 | } Search_Path; |
110 | |
111 | typedef struct Struct_Ver_Entry { |
112 | Elf_Word hash; |
113 | u_int flags; |
114 | const char *name; |
115 | const char *file; |
116 | } Ver_Entry; |
117 | |
118 | /* Ver_Entry.flags */ |
119 | #define VER_INFO_HIDDEN 0x01 |
120 | |
121 | #define RTLD_MAX_ENTRY 10 |
122 | #define RTLD_MAX_LIBRARY 4 |
123 | #define RTLD_MAX_CTL 2 |
124 | typedef struct _rtld_library_xform_t { |
125 | struct _rtld_library_xform_t *next; |
126 | char *name; |
127 | const char *ctlname; |
128 | struct { |
129 | char *value; |
130 | char *library[RTLD_MAX_LIBRARY]; |
131 | } entry[RTLD_MAX_ENTRY]; |
132 | } Library_Xform; |
133 | |
134 | /* |
135 | * Shared object descriptor. |
136 | * |
137 | * Items marked with "(%)" are dynamically allocated, and must be freed |
138 | * when the structure is destroyed. |
139 | */ |
140 | |
141 | typedef void (*fptr_t)(void); |
142 | |
143 | typedef struct Struct_Obj_Entry { |
144 | struct Struct_Obj_Entry *next; |
145 | char *path; /* Pathname of underlying file (%) */ |
146 | int refcount; |
147 | int dl_refcount; /* Number of times loaded by dlopen */ |
148 | |
149 | /* These items are computed by map_object() or by digest_phdr(). */ |
150 | caddr_t mapbase; /* Base address of mapped region */ |
151 | size_t mapsize; /* Size of mapped region in bytes */ |
152 | size_t textsize; /* Size of text segment in bytes */ |
153 | Elf_Addr vaddrbase; /* Base address in shared object file */ |
154 | caddr_t relocbase; /* Reloc const = mapbase - *vaddrbase */ |
155 | Elf_Dyn *dynamic; /* Dynamic section */ |
156 | caddr_t entry; /* Entry point */ |
157 | const Elf_Phdr *phdr; /* Program header (may be xmalloc'ed) */ |
158 | size_t phsize; /* Size of program header in bytes */ |
159 | |
160 | /* Items from the dynamic section. */ |
161 | Elf_Addr *pltgot; /* PLTGOT table */ |
162 | const Elf_Rel *rel; /* Relocation entries */ |
163 | const Elf_Rel *rellim; /* Limit of Relocation entries */ |
164 | const Elf_Rela *rela; /* Relocation entries */ |
165 | const Elf_Rela *relalim; /* Limit of Relocation entries */ |
166 | const Elf_Rel *pltrel; /* PLT relocation entries */ |
167 | const Elf_Rel *pltrellim; /* Limit of PLT relocation entries */ |
168 | const Elf_Rela *pltrela; /* PLT relocation entries */ |
169 | const Elf_Rela *pltrelalim; /* Limit of PLT relocation entries */ |
170 | const Elf_Sym *symtab; /* Symbol table */ |
171 | const char *strtab; /* String table */ |
172 | unsigned long strsize; /* Size in bytes of string table */ |
173 | #if defined(__mips__) || defined(__riscv__) |
174 | Elf_Word local_gotno; /* Number of local GOT entries */ |
175 | Elf_Word symtabno; /* Number of dynamic symbols */ |
176 | Elf_Word gotsym; /* First dynamic symbol in GOT */ |
177 | #endif |
178 | |
179 | const Elf_Symindx *buckets; /* Hash table buckets array */ |
180 | unsigned long unused1; /* Used to be nbuckets */ |
181 | const Elf_Symindx *chains; /* Hash table chain array */ |
182 | unsigned long nchains; /* Number of chains */ |
183 | |
184 | Search_Path *rpaths; /* Search path specified in object */ |
185 | Needed_Entry *needed; /* Shared objects needed by this (%) */ |
186 | |
187 | Elf_Addr init; /* Initialization function to call */ |
188 | Elf_Addr fini; /* Termination function to call */ |
189 | |
190 | u_int32_t mainprog:1, /* True if this is the main program */ |
191 | rtld:1, /* True if this is the dynamic linker */ |
192 | textrel:1, /* True if there are relocations to |
193 | * text seg */ |
194 | symbolic:1, /* True if generated with |
195 | * "-Bsymbolic" */ |
196 | printed:1, /* True if ldd has printed it */ |
197 | isdynamic:1, /* True if this is a pure PIC object */ |
198 | mainref:1, /* True if on _rtld_list_main */ |
199 | globalref:1, /* True if on _rtld_list_global */ |
200 | init_done:1, /* True if .init has been added */ |
201 | init_called:1, /* True if .init function has been |
202 | * called */ |
203 | fini_called:1, /* True if .fini function has been |
204 | * called */ |
205 | z_now:1, /* True if object's symbols should be |
206 | bound immediately */ |
207 | z_nodelete:1, /* True if object should never be |
208 | unloaded */ |
209 | z_initfirst:1, /* True if object's .init/.fini take |
210 | * priority over others */ |
211 | z_noopen:1, /* True if object should never be |
212 | dlopen'ed */ |
213 | phdr_loaded:1, /* Phdr is loaded and doesn't need to |
214 | * be freed. */ |
215 | #if defined(__HAVE_TLS_VARIANT_I) || defined(__HAVE_TLS_VARIANT_II) |
216 | tls_done:1, /* True if static TLS offset |
217 | * has been allocated */ |
218 | #endif |
219 | ref_nodel:1; /* Refcount increased to prevent dlclose */ |
220 | |
221 | struct link_map linkmap; /* for GDB */ |
222 | |
223 | /* These items are computed by map_object() or by digest_phdr(). */ |
224 | const char *interp; /* Pathname of the interpreter, if any */ |
225 | Objlist dldags; /* Object belongs to these dlopened DAGs (%) */ |
226 | Objlist dagmembers; /* DAG has these members (%) */ |
227 | dev_t dev; /* Object's filesystem's device */ |
228 | ino_t ino; /* Object's inode number */ |
229 | |
230 | void *ehdr; |
231 | |
232 | uint32_t nbuckets; /* Number of buckets */ |
233 | uint32_t nbuckets_m; /* Precomputed for fast remainder */ |
234 | uint8_t nbuckets_s1; |
235 | uint8_t nbuckets_s2; |
236 | size_t pathlen; /* Pathname length */ |
237 | SIMPLEQ_HEAD(, Struct_Name_Entry) names; /* List of names for this |
238 | * object we know about. */ |
239 | |
240 | #ifdef __powerpc__ |
241 | #ifdef _LP64 |
242 | Elf_Addr glink; /* global linkage */ |
243 | #else |
244 | Elf_Addr *gotptr; /* GOT table (secure-plt only) */ |
245 | #endif |
246 | #endif |
247 | |
248 | #if defined(__HAVE_TLS_VARIANT_I) || defined(__HAVE_TLS_VARIANT_II) |
249 | /* Thread Local Storage support for this module */ |
250 | size_t tlsindex; /* Index in DTV */ |
251 | void *tlsinit; /* Base address of TLS init block */ |
252 | size_t tlsinitsize; /* Size of TLS init block */ |
253 | size_t tlssize; /* Size of TLS block */ |
254 | size_t tlsoffset; /* Offset in the static TLS block */ |
255 | size_t tlsalign; /* Needed alignment for static TLS */ |
256 | #endif |
257 | |
258 | #ifdef GNU_RELRO |
259 | /* relocation readonly */ |
260 | void *relro_page; |
261 | size_t relro_size; |
262 | #endif |
263 | |
264 | /* symbol versioning */ |
265 | const Elf_Verneed *verneed; /* Required versions. */ |
266 | Elf_Word verneednum; /* Number of entries in verneed table */ |
267 | const Elf_Verdef *verdef; /* Provided versions. */ |
268 | Elf_Word verdefnum; /* Number of entries in verdef table */ |
269 | const Elf_Versym *versyms; /* Symbol versions table */ |
270 | |
271 | Ver_Entry *vertab; /* Versions required/defined by this |
272 | * object */ |
273 | int vertabnum; /* Number of entries in vertab */ |
274 | |
275 | /* init_array/fini_array */ |
276 | Elf_Addr *init_array; /* start of init array */ |
277 | size_t init_arraysz; /* # of entries in it */ |
278 | Elf_Addr *fini_array; /* start of fini array */ |
279 | size_t fini_arraysz; /* # of entries in it */ |
280 | /* IRELATIVE relocations */ |
281 | size_t ifunc_remaining; |
282 | #if defined(__sparc__) || defined(__powerpc__) || defined(__arm__) || \ |
283 | defined(__i386__) || defined(__x86_64__) |
284 | #define IFUNC_NONPLT |
285 | /* On SPARC, the PLT variant is called JMP_IREL and counted above. */ |
286 | size_t ifunc_remaining_nonplt; |
287 | #endif |
288 | size_t cxa_refcount; /* For TLS destructors. */ |
289 | #ifdef __ARM_EABI__ |
290 | void *exidx_start; |
291 | size_t exidx_sz; |
292 | #endif |
293 | } Obj_Entry; |
294 | |
295 | typedef struct Struct_DoneList { |
296 | const Obj_Entry **objs; /* Array of object pointers */ |
297 | unsigned int num_alloc; /* Allocated size of the array */ |
298 | unsigned int num_used; /* Number of array slots used */ |
299 | } DoneList; |
300 | |
301 | |
302 | #if defined(_RTLD_SOURCE) |
303 | |
304 | extern struct r_debug _rtld_debug; |
305 | extern Search_Path *_rtld_default_paths; |
306 | extern Obj_Entry *_rtld_objlist; |
307 | extern Obj_Entry **_rtld_objtail; |
308 | extern u_int _rtld_objcount; |
309 | extern u_int _rtld_objloads; |
310 | extern const uintptr_t _rtld_compat_obj[]; |
311 | extern Obj_Entry *_rtld_objmain; |
312 | extern Obj_Entry _rtld_objself; |
313 | extern Search_Path *_rtld_paths; |
314 | extern Library_Xform *_rtld_xforms; |
315 | extern bool _rtld_trust; |
316 | extern Objlist _rtld_list_global; |
317 | extern Objlist _rtld_list_main; |
318 | extern Elf_Sym _rtld_sym_zero; |
319 | extern u_int _rtld_objgen; |
320 | |
321 | #define RTLD_MODEMASK 0x3 |
322 | |
323 | /* Flags to be passed into _rtld_symlook_ family of functions. */ |
324 | #define SYMLOOK_IN_PLT 0x01 /* Lookup for PLT symbol */ |
325 | #define SYMLOOK_DLSYM 0x02 /* Return newest versioned symbol. |
326 | Used by dlsym. */ |
327 | |
328 | /* Flags for _rtld_load_object() and friends. */ |
329 | #define _RTLD_GLOBAL 0x01 /* Add object to global DAG. */ |
330 | #define _RTLD_MAIN 0x02 |
331 | #define _RTLD_NOLOAD 0x04 /* dlopen() specified RTLD_NOLOAD. */ |
332 | #define _RTLD_DLOPEN 0x08 /* Load_object() called from dlopen(). */ |
333 | |
334 | /* Preallocation for static TLS model */ |
335 | #define RTLD_STATIC_TLS_RESERVATION 64 |
336 | |
337 | /* rtld.c */ |
338 | __dso_public char *dlerror(void); |
339 | __dso_public void *dlopen(const char *, int); |
340 | __dso_public void *dlsym(void *, const char *); |
341 | __dso_public int dlclose(void *); |
342 | __dso_public int dladdr(const void *, Dl_info *); |
343 | __dso_public int dlinfo(void *, int, void *); |
344 | __dso_public int dl_iterate_phdr(int (*)(struct dl_phdr_info *, size_t, void *), |
345 | void *); |
346 | |
347 | __dso_public void *_dlauxinfo(void) __pure; |
348 | __dso_public void __dl_cxa_refcount(void *addr, ssize_t delta); |
349 | |
350 | #if defined(__ARM_EABI__) && !defined(__ARM_DWARF_EH__) |
351 | /* |
352 | * This is used by libgcc to find the start and length of the exception table |
353 | * associated with a PC. |
354 | */ |
355 | __dso_public _Unwind_Ptr __gnu_Unwind_Find_exidx(_Unwind_Ptr, int *); |
356 | #endif |
357 | |
358 | /* These aren't exported */ |
359 | void _rtld_error(const char *, ...) __printflike(1,2); |
360 | void _rtld_die(void) __dead; |
361 | void *_rtld_objmain_sym(const char *); |
362 | __dso_public void _rtld_debug_state(void) __noinline; |
363 | void _rtld_linkmap_add(Obj_Entry *); |
364 | void _rtld_linkmap_delete(Obj_Entry *); |
365 | void _rtld_objlist_push_head(Objlist *, Obj_Entry *); |
366 | void _rtld_objlist_push_tail(Objlist *, Obj_Entry *); |
367 | Objlist_Entry *_rtld_objlist_find(Objlist *, const Obj_Entry *); |
368 | void _rtld_ref_dag(Obj_Entry *); |
369 | |
370 | void _rtld_shared_enter(void); |
371 | void _rtld_shared_exit(void); |
372 | void _rtld_exclusive_enter(sigset_t *); |
373 | void _rtld_exclusive_exit(sigset_t *); |
374 | |
375 | int _rtld_relro(const Obj_Entry *, bool); |
376 | |
377 | /* expand.c */ |
378 | size_t _rtld_expand_path(char *, size_t, const char *, const char *,\ |
379 | const char *); |
380 | |
381 | /* headers.c */ |
382 | void _rtld_digest_dynamic(const char *, Obj_Entry *); |
383 | Obj_Entry *_rtld_digest_phdr(const Elf_Phdr *, int, caddr_t); |
384 | |
385 | /* load.c */ |
386 | Obj_Entry *_rtld_load_object(const char *, int); |
387 | int _rtld_load_needed_objects(Obj_Entry *, int); |
388 | int _rtld_preload(const char *); |
389 | |
390 | #define OBJ_ERR (Obj_Entry *)(-1) |
391 | /* path.c */ |
392 | void _rtld_add_paths(const char *, Search_Path **, const char *); |
393 | void _rtld_process_hints(const char *, Search_Path **, Library_Xform **, |
394 | const char *); |
395 | int _rtld_sysctl(const char *, void *, size_t *); |
396 | |
397 | /* reloc.c */ |
398 | int _rtld_do_copy_relocations(const Obj_Entry *); |
399 | int _rtld_relocate_objects(Obj_Entry *, bool); |
400 | int _rtld_relocate_nonplt_objects(Obj_Entry *); |
401 | int _rtld_relocate_plt_lazy(Obj_Entry *); |
402 | int _rtld_relocate_plt_objects(const Obj_Entry *); |
403 | void _rtld_setup_pltgot(const Obj_Entry *); |
404 | Elf_Addr _rtld_resolve_ifunc(const Obj_Entry *, const Elf_Sym *); |
405 | Elf_Addr _rtld_resolve_ifunc2(const Obj_Entry *, Elf_Addr); |
406 | |
407 | void _rtld_call_ifunc(Obj_Entry *, sigset_t *, u_int); |
408 | |
409 | /* search.c */ |
410 | Obj_Entry *_rtld_load_library(const char *, const Obj_Entry *, int); |
411 | |
412 | /* symbol.c */ |
413 | unsigned long _rtld_elf_hash(const char *); |
414 | const Elf_Sym *_rtld_symlook_obj(const char *, unsigned long, |
415 | const Obj_Entry *, u_int, const Ver_Entry *); |
416 | const Elf_Sym *_rtld_find_symdef(unsigned long, const Obj_Entry *, |
417 | const Obj_Entry **, u_int); |
418 | const Elf_Sym *_rtld_find_plt_symdef(unsigned long, const Obj_Entry *, |
419 | const Obj_Entry **, bool); |
420 | |
421 | const Elf_Sym *_rtld_symlook_list(const char *, unsigned long, |
422 | const Objlist *, const Obj_Entry **, u_int, const Ver_Entry *, DoneList *); |
423 | const Elf_Sym *_rtld_symlook_default(const char *, unsigned long, |
424 | const Obj_Entry *, const Obj_Entry **, u_int, const Ver_Entry *); |
425 | const Elf_Sym *_rtld_symlook_needed(const char *, unsigned long, |
426 | const Needed_Entry *, const Obj_Entry **, u_int, const Ver_Entry *, |
427 | DoneList *, DoneList *); |
428 | |
429 | /* symver.c */ |
430 | void _rtld_object_add_name(Obj_Entry *, const char *); |
431 | int _rtld_object_match_name(const Obj_Entry *, const char *); |
432 | int _rtld_verify_object_versions(Obj_Entry *); |
433 | |
434 | static __inline const Ver_Entry * |
435 | _rtld_fetch_ventry(const Obj_Entry *obj, unsigned long symnum) |
436 | { |
437 | Elf_Half vernum; |
438 | |
439 | if (obj->vertab) { |
440 | vernum = VER_NDX(obj->versyms[symnum].vs_vers); |
441 | if (vernum >= obj->vertabnum) { |
442 | _rtld_error("%s: symbol %s has wrong verneed value %d" , |
443 | obj->path, &obj->strtab[symnum], vernum); |
444 | } else if (obj->vertab[vernum].hash) { |
445 | return &obj->vertab[vernum]; |
446 | } |
447 | } |
448 | return NULL; |
449 | } |
450 | |
451 | #if defined(__HAVE_TLS_VARIANT_I) || defined(__HAVE_TLS_VARIANT_II) |
452 | /* tls.c */ |
453 | void *_rtld_tls_get_addr(void *, size_t, size_t); |
454 | void _rtld_tls_initial_allocation(void); |
455 | void *_rtld_tls_module_allocate(size_t index); |
456 | int _rtld_tls_offset_allocate(Obj_Entry *); |
457 | void _rtld_tls_offset_free(Obj_Entry *); |
458 | |
459 | extern size_t _rtld_tls_dtv_generation; |
460 | extern size_t _rtld_tls_max_index; |
461 | |
462 | __dso_public extern void *__tls_get_addr(void *); |
463 | #ifdef __i386__ |
464 | __dso_public extern void *___tls_get_addr(void *) |
465 | __attribute__((__regparm__(1))); |
466 | #endif |
467 | #endif |
468 | |
469 | /* map_object.c */ |
470 | struct stat; |
471 | Obj_Entry *_rtld_map_object(const char *, int, const struct stat *); |
472 | void _rtld_obj_free(Obj_Entry *); |
473 | Obj_Entry *_rtld_obj_new(void); |
474 | |
475 | #ifdef RTLD_LOADER |
476 | /* function descriptors */ |
477 | #ifdef __HAVE_FUNCTION_DESCRIPTORS |
478 | Elf_Addr _rtld_function_descriptor_alloc(const Obj_Entry *, |
479 | const Elf_Sym *, Elf_Addr); |
480 | const void *_rtld_function_descriptor_function(const void *); |
481 | |
482 | void _rtld_call_function_void(const Obj_Entry *, Elf_Addr); |
483 | Elf_Addr _rtld_call_function_addr(const Obj_Entry *, Elf_Addr); |
484 | #else |
485 | static inline void |
486 | _rtld_call_function_void(const Obj_Entry *obj, Elf_Addr addr) |
487 | { |
488 | ((void (*)(void))addr)(); |
489 | } |
490 | static inline Elf_Addr |
491 | _rtld_call_function_addr(const Obj_Entry *obj, Elf_Addr addr) |
492 | { |
493 | return ((Elf_Addr(*)(void))addr)(); |
494 | } |
495 | #endif /* __HAVE_FUNCTION_DESCRIPTORS */ |
496 | #endif /* RTLD_LOADER */ |
497 | |
498 | #endif /* _RTLD_SOURCE */ |
499 | |
500 | #endif /* RTLD_H */ |
501 | |