summaryrefslogtreecommitdiff
path: root/ldso/include/dl-elf.h
blob: 7d514c0f54d21fa7e521a9e2009262a82a8971ef (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
/*
 * Copyright (C) 2000-2005 by Erik Andersen <andersen@codepoet.org>
 *
 * GNU Lesser General Public License version 2.1 or later.
 */

#ifndef _DL_ELF_H
#define _DL_ELF_H

#include <features.h>
#include <bits/wordsize.h>
#include <dl-string.h> /* before elf.h to get ELF_USES_RELOCA right */
#include <elf.h>
#include <link.h>
#include <dl-defs.h>
#include <dlfcn.h>

/* Forward declarations for stuff defined in dl-hash.h */
struct dyn_elf;
struct elf_resolve;
struct r_scope_elem;

#ifdef __LDSO_CACHE_SUPPORT__
extern int _dl_map_cache(void);
extern int _dl_unmap_cache(void);
#else
static __inline__ void _dl_map_cache(void) { }
static __inline__ void _dl_unmap_cache(void) { }
#endif

/* Function prototypes for non-static stuff in elfinterp.c */
extern void _dl_parse_lazy_relocation_information(struct dyn_elf *rpnt,
	unsigned long rel_addr, unsigned long rel_size);
extern int _dl_parse_relocation_information(struct dyn_elf *rpnt,
	struct r_scope_elem *scope, unsigned long rel_addr, unsigned long rel_size);
extern struct elf_resolve * _dl_load_shared_library(unsigned int rflags,
	struct dyn_elf **rpnt, struct elf_resolve *tpnt, char *full_libname,
	int trace_loaded_objects);
extern struct elf_resolve * _dl_load_elf_shared_library(unsigned int rflags,
	struct dyn_elf **rpnt, const char *libname);
extern int _dl_linux_resolve(void);
extern int _dl_fixup(struct dyn_elf *rpnt, struct r_scope_elem *scope, int flag);
extern void _dl_protect_relro (struct elf_resolve *l);

/*
 * Bitsize related settings for things ElfW()
 * does not handle already
 */
#if __WORDSIZE == 64
# define ELF_ST_BIND(val) ELF64_ST_BIND(val)
# define ELF_ST_TYPE(val) ELF64_ST_TYPE(val)
# define ELF_R_SYM(i)     ELF64_R_SYM(i)
# define ELF_R_TYPE(i)    ELF64_R_TYPE(i)
# ifndef ELF_CLASS
#  define ELF_CLASS ELFCLASS64
# endif
#else
# define ELF_ST_BIND(val) ELF32_ST_BIND(val)
# define ELF_ST_TYPE(val) ELF32_ST_TYPE(val)
# define ELF_R_SYM(i)     ELF32_R_SYM(i)
# define ELF_R_TYPE(i)    ELF32_R_TYPE(i)
# ifndef ELF_CLASS
#  define ELF_CLASS ELFCLASS32
# endif
#endif

/*
 * Datatype of a relocation on this platform
 */
#ifdef ELF_USES_RELOCA
# define ELF_RELOC	ElfW(Rela)
# define DT_RELOC_TABLE_ADDR	DT_RELA
# define DT_RELOC_TABLE_SIZE	DT_RELASZ
# define DT_RELOCCOUNT		DT_RELACOUNT
# define UNSUPPORTED_RELOC_TYPE	DT_REL
# define UNSUPPORTED_RELOC_STR	"REL"
#else
# define ELF_RELOC	ElfW(Rel)
# define DT_RELOC_TABLE_ADDR	DT_REL
# define DT_RELOC_TABLE_SIZE	DT_RELSZ
# define DT_RELOCCOUNT		DT_RELCOUNT
# define UNSUPPORTED_RELOC_TYPE	DT_RELA
# define UNSUPPORTED_RELOC_STR	"RELA"
#endif

/* OS and/or GNU dynamic extensions */

#define OS_NUM_BASE 1			/* for DT_RELOCCOUNT */

#ifdef __LDSO_GNU_HASH_SUPPORT__
# define OS_NUM_GNU_HASH	1   /* for DT_GNU_HASH entry */
#else
# define OS_NUM_GNU_HASH	0
#endif

#ifdef __LDSO_PRELINK_SUPPORT__
# define OS_NUM_PRELINK		6   /* for DT_GNU_PRELINKED entry */
#else
# define OS_NUM_PRELINK	0
#endif

#define OS_NUM	  (OS_NUM_BASE + OS_NUM_GNU_HASH + OS_NUM_PRELINK)

#ifndef ARCH_DYNAMIC_INFO
  /* define in arch specific code, if needed */
# define ARCH_NUM 0
#endif

#define DYNAMIC_SIZE (DT_NUM + OS_NUM + ARCH_NUM)
/* Keep ARCH specific entries into dynamic section at the end of the array */
#define DT_RELCONT_IDX (DYNAMIC_SIZE - OS_NUM - ARCH_NUM)

#ifdef __LDSO_GNU_HASH_SUPPORT__
/* GNU hash comes just after the relocation count */
# define DT_GNU_HASH_IDX (DT_RELCONT_IDX + 1)
#else
# define DT_GNU_HASH_IDX DT_RELCONT_IDX
#endif

#ifdef __LDSO_PRELINK_SUPPORT__
/* GNU prelink comes just after the GNU hash if present */
#define DT_GNU_PRELINKED_IDX  (DT_GNU_HASH_IDX + 1)
#define DT_GNU_CONFLICT_IDX   (DT_GNU_HASH_IDX + 2)
#define DT_GNU_CONFLICTSZ_IDX (DT_GNU_HASH_IDX + 3)
#define DT_GNU_LIBLIST_IDX    (DT_GNU_HASH_IDX + 4)
#define DT_GNU_LIBLISTSZ_IDX  (DT_GNU_HASH_IDX + 5)
#define DT_CHECKSUM_IDX       (DT_GNU_HASH_IDX + 6)
#endif

extern unsigned int _dl_parse_dynamic_info(ElfW(Dyn) *dpnt, unsigned long dynamic_info[],
                                           void *debug_addr, DL_LOADADDR_TYPE load_off);

static __always_inline
unsigned int __dl_parse_dynamic_info(ElfW(Dyn) *dpnt, unsigned long dynamic_info[],
                                     void *debug_addr, DL_LOADADDR_TYPE load_off)
{
	unsigned int rtld_flags = 0;

	for (; dpnt->d_tag; dpnt++) {
		if (dpnt->d_tag < DT_NUM) {
			dynamic_info[dpnt->d_tag] = dpnt->d_un.d_val;
#ifndef __mips__
			/* we disable for mips because normally this page is readonly
			 * and modifying the value here needlessly dirties a page.
			 * see this post for more info:
			 * http://uclibc.org/lists/uclibc/2006-April/015224.html */
			if (dpnt->d_tag == DT_DEBUG)
				dpnt->d_un.d_val = (unsigned long)debug_addr;
#endif
			if (dpnt->d_tag == DT_BIND_NOW)
				dynamic_info[DT_BIND_NOW] = 1;
			if (dpnt->d_tag == DT_FLAGS &&
			    (dpnt->d_un.d_val & DF_BIND_NOW))
				dynamic_info[DT_BIND_NOW] = 1;
			if (dpnt->d_tag == DT_TEXTREL)
				dynamic_info[DT_TEXTREL] = 1;
#ifdef __LDSO_RUNPATH__
			if (dpnt->d_tag == DT_RUNPATH)
				dynamic_info[DT_RPATH] = 0;
			if (dpnt->d_tag == DT_RPATH && dynamic_info[DT_RUNPATH])
				dynamic_info[DT_RPATH] = 0;
#endif
		} else if (dpnt->d_tag < DT_LOPROC) {
			if (dpnt->d_tag == DT_RELOCCOUNT)
				dynamic_info[DT_RELCONT_IDX] = dpnt->d_un.d_val;
			if (dpnt->d_tag == DT_FLAGS_1) {
				if (dpnt->d_un.d_val & DF_1_NOW)
					dynamic_info[DT_BIND_NOW] = 1;
				if (dpnt->d_un.d_val & DF_1_NODELETE)
					rtld_flags |= RTLD_NODELETE;
			}
#ifdef __LDSO_GNU_HASH_SUPPORT__
			if (dpnt->d_tag == DT_GNU_HASH)
				dynamic_info[DT_GNU_HASH_IDX] = dpnt->d_un.d_ptr;
#endif
#ifdef __LDSO_PRELINK_SUPPORT__
			if (dpnt->d_tag == DT_GNU_PRELINKED)
				dynamic_info[DT_GNU_PRELINKED_IDX] = dpnt->d_un.d_val;
			if (dpnt->d_tag == DT_GNU_CONFLICT)
				dynamic_info[DT_GNU_CONFLICT_IDX] = dpnt->d_un.d_ptr;
			if (dpnt->d_tag == DT_GNU_CONFLICTSZ)
				dynamic_info[DT_GNU_CONFLICTSZ_IDX] = dpnt->d_un.d_val;
			if (dpnt->d_tag == DT_GNU_LIBLIST)
				dynamic_info[DT_GNU_LIBLIST_IDX] = dpnt->d_un.d_ptr;
			if (dpnt->d_tag == DT_GNU_LIBLISTSZ)
				dynamic_info[DT_GNU_LIBLISTSZ_IDX] = dpnt->d_un.d_val;
			if (dpnt->d_tag == DT_CHECKSUM)
				dynamic_info[DT_CHECKSUM_IDX] = dpnt->d_un.d_val;
#endif
		}
#ifdef ARCH_DYNAMIC_INFO
		else {
			ARCH_DYNAMIC_INFO(dpnt, dynamic_info, debug_addr);
		}
#endif
	}
#define ADJUST_DYN_INFO(tag, load_off) \
	do { \
		if (dynamic_info[tag]) \
			dynamic_info[tag] = (unsigned long) DL_RELOC_ADDR(load_off, dynamic_info[tag]); \
	} while (0)
	/* Don't adjust .dynamic unnecessarily.  For FDPIC targets,
	   we'd have to walk all the loadsegs to find out if it was
	   actually unnecessary, so skip this optimization.  */
#if !defined __FRV_FDPIC__ && !defined __BFIN_FDPIC__ && !defined __DSBT__ && !defined __FDPIC__
	if (load_off != 0)
#endif
	{
		ADJUST_DYN_INFO(DT_HASH, load_off);
		ADJUST_DYN_INFO(DT_PLTGOT, load_off);
		ADJUST_DYN_INFO(DT_STRTAB, load_off);
		ADJUST_DYN_INFO(DT_SYMTAB, load_off);
		ADJUST_DYN_INFO(DT_RELOC_TABLE_ADDR, load_off);
		ADJUST_DYN_INFO(DT_JMPREL, load_off);
#ifdef __LDSO_GNU_HASH_SUPPORT__
		ADJUST_DYN_INFO(DT_GNU_HASH_IDX, load_off);
#endif
	}
#ifdef __DSBT__
	/* Get the mapped address of the DSBT base.  */
	ADJUST_DYN_INFO(DT_DSBT_BASE_IDX, load_off);
#endif
#undef ADJUST_DYN_INFO
	return rtld_flags;
}

/* Reloc type classes as returned by elf_machine_type_class().
   ELF_RTYPE_CLASS_PLT means this reloc should not be satisfied by
   some PLT symbol, ELF_RTYPE_CLASS_COPY means this reloc should not be
   satisfied by any symbol in the executable.  Some architectures do
   not support copy relocations.  In this case we define the macro to
   zero so that the code for handling them gets automatically optimized
   out.  */
#ifdef DL_NO_COPY_RELOCS
# define ELF_RTYPE_CLASS_COPY	(0x0)
#else
# define ELF_RTYPE_CLASS_COPY	(0x2)
#endif
#define ELF_RTYPE_CLASS_PLT	(0x1)

/* dlsym() calls _dl_find_hash with this value, that enables
   DL_FIND_HASH_VALUE to return something different than the symbol
   itself, e.g., a function descriptor.  */
#define ELF_RTYPE_CLASS_DLSYM 0x80000000


/* Convert between the Linux flags for page protections and the
   ones specified in the ELF standard. */
#define LXFLAGS(X) ( (((X) & PF_R) ? PROT_READ : 0) | \
		    (((X) & PF_W) ? PROT_WRITE : 0) | \
		    (((X) & PF_X) ? PROT_EXEC : 0))

/* FDPIC ABI don't use relative relocations */
#if !defined(__FDPIC__)
/* Apply relocations in DT_RELR format */
#define DL_DO_RELOCATE_RELR(load_addr, relr_start, relr_end) \
		do { \
			const ElfW(Relr) *relr = 0; \
			ElfW(Addr) *reloc_addr = 0; \
			for (relr = relr_start; relr < relr_end; relr++) { \
				ElfW(Relr) relr_entry = *relr; \
				if (!(relr_entry & 1)) \
				{ \
					reloc_addr = (ElfW(Addr) *)DL_RELOC_ADDR(load_addr, relr_entry); \
					*reloc_addr = (ElfW(Addr))DL_RELOC_ADDR(load_addr, reloc_addr); \
					reloc_addr++; \
				} \
				else \
				{ \
					for (long int i = 0; (relr_entry >>= 1) != 0; ++i) { \
						if ((relr_entry & 1) != 0) \
							reloc_addr[i] = (ElfW(Addr))DL_RELOC_ADDR(load_addr, reloc_addr[i]); \
					} \
					reloc_addr += CHAR_BIT * sizeof(ElfW(Relr)) - 1; \
				} \
			} \
		} while (0);

/* The macro to prepare data for the above DL_DO_RELOCATE_RELR */
#define DL_RELOCATE_RELR(dyn) \
		do { \
			if (dyn->dynamic_info[DT_RELRENT]) \
				_dl_assert(dyn->dynamic_info[DT_RELRENT] == sizeof(ElfW(Relr))); \
			if (dyn->dynamic_info[DT_RELR] && \
				dyn->dynamic_info[DT_RELRSZ]) { \
				ElfW(Relr) *relr_start = (ElfW(Relr) *)((ElfW(Addr))dyn->loadaddr + (ElfW(Addr))dyn->dynamic_info[DT_RELR]); \
				ElfW(Relr) *relr_end = (ElfW(Relr) *)((const char *)relr_start + dyn->dynamic_info[DT_RELRSZ]); \
				_dl_if_debug_dprint("Relocating DT_RELR in %s: start:%p, end:%p\n", \
						    dyn->libname, (void *)relr_start, (void *)relr_end); \
				DL_DO_RELOCATE_RELR(dyn->loadaddr, relr_start, relr_end); \
			} \
		} while (0);
#endif /* __FDPIC__ */

#endif /* _DL_ELF_H */