summaryrefslogtreecommitdiff
path: root/ldso/ldso/arc/elfinterp.c
blob: 1b5b651156e94d0ea2071b710d33658f7d8f98ab (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
296
297
298
299
300
301
302
303
304
305
306
/*
 * Copyright (C) 2013 Synopsys, Inc. (www.synopsys.com)
 *
 * Lots of code copied from ../i386/elfinterp.c, so:
 * Copyright (c) 1994-2000 Eric Youngdale, Peter MacDonald,
 *               David Engel, Hongjiu Lu and Mitch D'Souza
 * Copyright (C) 2001-2002, Erik Andersen
 * All rights reserved.
 *
 * Licensed under the LGPL v2.1 or later, see the file COPYING.LIB in this tarball.
 */
#include "ldso.h"

#ifdef __A7__
#define ARC_PLT_SIZE	12
#else
#define ARC_PLT_SIZE	16
#endif

unsigned long
_dl_linux_resolver(struct elf_resolve *tpnt, unsigned int plt_pc)
{
	ELF_RELOC *this_reloc, *rel_base;
	char *strtab, *symname, *new_addr;
	ElfW(Sym) *symtab;
	int symtab_index;
	unsigned int *got_addr;
	unsigned long plt_base;
	int plt_idx;

	/* start of .rela.plt */
	rel_base = (ELF_RELOC *)(tpnt->dynamic_info[DT_JMPREL]);

	/* starts of .plt (addr of PLT0) */
	plt_base = tpnt->dynamic_info[DT_PLTGOT];

	/*
	 * compute the idx of the yet-unresolved PLT entry in .plt
	 * Same idx will be used to find the relo entry in .rela.plt
	 */
	plt_idx = (plt_pc - plt_base)/ARC_PLT_SIZE  - 2; /* ignoring 2 dummy PLTs */

	this_reloc = rel_base + plt_idx;

	symtab_index = ELF_R_SYM(this_reloc->r_info);
	symtab = (ElfW(Sym) *)(intptr_t) (tpnt->dynamic_info[DT_SYMTAB]);
	strtab = (char *) (tpnt->dynamic_info[DT_STRTAB]);
	symname= strtab + symtab[symtab_index].st_name;

	/* relo-offset to fixup, shd be a .got entry */
	got_addr = (unsigned int *)(this_reloc->r_offset + tpnt->loadaddr);

	/* Get the address of the GOT entry */
	new_addr = _dl_find_hash(symname, &_dl_loaded_modules->symbol_scope, tpnt,
				 ELF_RTYPE_CLASS_PLT, NULL);

	if (unlikely(!new_addr)) {
		_dl_dprintf(2, "%s: can't resolve symbol '%s'\n", _dl_progname, symname);
		_dl_exit(1);
	}


#if defined __SUPPORT_LD_DEBUG__
	if (_dl_debug_bindings) {
		_dl_dprintf(_dl_debug_file, "\nresolve function: %s", symname);
		if (_dl_debug_detail)
			_dl_dprintf(_dl_debug_file, "\n\tpatched %x ==> %pc @ %pl\n",
					*got_addr, new_addr, got_addr);
	}

	if (!_dl_debug_nofixups)
		*got_addr = (unsigned int)new_addr;
#else
	/* Update the .got entry with the runtime address of symbol */
	*got_addr = (unsigned int)new_addr;
#endif

	/*
	 * Return the new addres, where the asm trampoline will jump to
	 *  after re-setting up the orig args
	 */
	return (unsigned long) new_addr;
}


static int
_dl_do_reloc(struct elf_resolve *tpnt, struct r_scope_elem *scope,
	     ELF_RELOC *rpnt, ElfW(Sym) *symtab, char *strtab)
{
	int reloc_type;
	int symtab_index;
	char *symname;
	unsigned long *reloc_addr;
	unsigned long symbol_addr;
#if defined __SUPPORT_LD_DEBUG__
	unsigned long old_val = 0;
#endif
	struct symbol_ref sym_ref;
	struct elf_resolve *tls_tpnt = NULL;

	reloc_addr   = (unsigned long *)(tpnt->loadaddr + rpnt->r_offset);
	reloc_type   = ELF_R_TYPE(rpnt->r_info);
	symtab_index = ELF_R_SYM(rpnt->r_info);
	symbol_addr  = 0;

	sym_ref.sym = &symtab[symtab_index];
	sym_ref.tpnt = NULL;

#if defined __SUPPORT_LD_DEBUG__
	if (reloc_addr)
		old_val = *reloc_addr;
#endif

	if (symtab_index) {
		symname = strtab + symtab[symtab_index].st_name;
		symbol_addr = (unsigned long) _dl_find_hash(symname, scope, tpnt,
				elf_machine_type_class(reloc_type), &sym_ref);

		/*
		 * We want to allow undefined references to weak symbols,
		 * this might have been intentional. We should not be linking
		 * local symbols here, so all bases should be covered.
		 */

		if (unlikely(!symbol_addr
		    && ELF_ST_BIND(symtab[symtab_index].st_info) != STB_WEAK
		    && ELF_ST_TYPE(symtab[symtab_index].st_info) != STT_TLS)) {
			/* Non-fatal if called from dlopen, hence different ret code */
			return 1;
		}

		tls_tpnt = sym_ref.tpnt;
	} else if (reloc_type == R_ARC_RELATIVE ) {
		*reloc_addr += tpnt->loadaddr;
		goto log_entry;
	}

#if defined USE_TLS && USE_TLS
	/* In case of a TLS reloc, tls_tpnt NULL means we have an 'anonymous'
	   symbol.  This is the case for a static tls variable, so the lookup
	   module is just that one is referencing the tls variable. */
	if (!tls_tpnt)
		tls_tpnt = tpnt;
#endif

	switch (reloc_type) {
	case R_ARC_NONE:
		break;
	case R_ARC_32:
		*reloc_addr += symbol_addr + rpnt->r_addend;
		break;
	case R_ARC_PC32:
		*reloc_addr += symbol_addr + rpnt->r_addend - (unsigned long) reloc_addr;
		break;
	case R_ARC_GLOB_DAT:
	case R_ARC_JMP_SLOT:
		*reloc_addr = symbol_addr;
		break;
	case R_ARC_COPY:
		_dl_memcpy((void *) reloc_addr,(void *) symbol_addr,
				symtab[symtab_index].st_size);
		break;
#if defined USE_TLS && USE_TLS
	case R_ARC_TLS_DTPMOD:
		*reloc_addr = tls_tpnt->l_tls_modid;
		break;
	case R_ARC_TLS_DTPOFF:
		*reloc_addr = symbol_addr;
		break;
	case R_ARC_TLS_TPOFF:
		CHECK_STATIC_TLS ((struct link_map *) tls_tpnt);
		*reloc_addr = tls_tpnt->l_tls_offset + symbol_addr + rpnt->r_addend;
		break;
#endif
	default:
		return -1;
	}

log_entry:
#if defined __SUPPORT_LD_DEBUG__
	if (_dl_debug_detail)
		_dl_dprintf(_dl_debug_file,"\tpatched: %lx ==> %lx @ %pl: addend %x ",
				old_val, *reloc_addr, reloc_addr, rpnt->r_addend);
#endif

	return 0;
}

static int
_dl_do_lazy_reloc(struct elf_resolve *tpnt, struct r_scope_elem *scope,
		  ELF_RELOC *rpnt)
{
	int reloc_type;
	unsigned long *reloc_addr;
#if defined __SUPPORT_LD_DEBUG__
	unsigned long old_val;
#endif

	reloc_addr = (unsigned long *)(tpnt->loadaddr + rpnt->r_offset);
	reloc_type = ELF_R_TYPE(rpnt->r_info);

#if defined __SUPPORT_LD_DEBUG__
	old_val = *reloc_addr;
#endif

	switch (reloc_type) {
	case R_ARC_NONE:
		break;
	case R_ARC_JMP_SLOT:
		*reloc_addr += tpnt->loadaddr;
		break;
	default:
		return -1;
	}

#if defined __SUPPORT_LD_DEBUG__
	if (_dl_debug_reloc && _dl_debug_detail)
		_dl_dprintf(_dl_debug_file, "\tpatched: %lx ==> %lx @ %pl\n",
				old_val, *reloc_addr, reloc_addr);
#endif

	return 0;
}

#define ___DO_LAZY  1
#define ___DO_NOW   2

static int _dl_parse(struct elf_resolve *tpnt, struct r_scope_elem *scope,
		     unsigned long rel_addr, unsigned long rel_size, int type)
{
	unsigned int i;
	char *strtab;
	ElfW(Sym) *symtab;
	ELF_RELOC *rpnt;
	int symtab_index;
	int res = 0;

	/* Now parse the relocation information */
	rpnt = (ELF_RELOC *)(intptr_t) (rel_addr);
	rel_size = rel_size / sizeof(ELF_RELOC);

	symtab = (ElfW(Sym) *)(intptr_t) (tpnt->dynamic_info[DT_SYMTAB]);
	strtab = (char *) (tpnt->dynamic_info[DT_STRTAB]);

	for (i = 0; i < rel_size; i++, rpnt++) {

		symtab_index = ELF_R_SYM(rpnt->r_info);

		debug_sym(symtab,strtab,symtab_index);
		debug_reloc(symtab,strtab,rpnt);

		/* constant propagation subsumes the 'if' */
		if (type == ___DO_LAZY)
			res = _dl_do_lazy_reloc(tpnt, scope, rpnt);
		else
			res = _dl_do_reloc(tpnt, scope, rpnt, symtab, strtab);

		if (res != 0)
			break;
	}

	if (unlikely(res != 0)) {
		if (res < 0) {
			int reloc_type = ELF_R_TYPE(rpnt->r_info);
			_dl_dprintf(2, "can't handle reloc type %x\n",
				    reloc_type);
			_dl_exit(-res);
		} else {
			_dl_dprintf(2, "can't resolve symbol\n");
			/* Fall thru to return res */
		}
	}

	return res;
}

void
_dl_parse_lazy_relocation_information(struct dyn_elf *rpnt,
				      unsigned long rel_addr,
				      unsigned long rel_size)
{
	/* This func is called for processing .rela.plt of loaded module(s)
	 * The relo entries handled are JMP_SLOT type for fixing up .got slots
	 * for external function calls.
	 * This function doesn't resolve the slots: that is done lazily at
	 * runtime. The build linker (at least thats what happens for ARC) had
	 * pre-init the .got slots to point to PLT0. All that is done here is
	 * to fix them up to point to load value of PLT0 (as opposed to the
	 * build value).
	 * On ARC, the loadaddr of dyn exec is zero, thus elfaddr == loadaddr
	 * Thus there is no point in adding "0" to values and un-necessarily
	 * stir up the caches and TLB.
	 * For ldso processing busybox binary, this skips over 380 relo entries
	 */
	if (rpnt->dyn->loadaddr != 0)
		_dl_parse(rpnt->dyn, NULL, rel_addr, rel_size, ___DO_LAZY);
}

int
_dl_parse_relocation_information(struct dyn_elf  *rpnt,
				 struct r_scope_elem *scope,
				 unsigned long rel_addr,
				 unsigned long rel_size)
{
	return _dl_parse(rpnt->dyn, scope, rel_addr, rel_size, ___DO_NOW);
}