summaryrefslogtreecommitdiff
path: root/ldso/ldso/xtensa/dl-startup.h
blob: aece0cd965b4bf49d86d594a02216376d1bd8ca2 (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
/* vi: set sw=4 ts=4: */
/*
 * Xtensa ELF code used by dl-startup.c.
 *
 * Copyright (C) 2007 Tensilica Inc.
 *
 * Licensed under the LGPL v2.1, see the file COPYING.LIB in this tarball.
 * Parts taken from glibc/sysdeps/xtensa/dl-machine.h.
 */

__asm__ (
    "	.text\n"
    "	.align  4\n"
    "   .literal_position\n"
    "	.global _start\n"
    "	.type   _start, @function\n"
    "	.hidden _start\n"
    "_start:\n"
    "	# Compute load offset in a2: the GOT has not yet been relocated\n"
    "	# but the entries for local symbols contain the relative offsets\n"
    "	# and we can explicitly add the load offset in this code.\n"
    "	_call0  0f\n"
    "	.align  4\n"
    "0:	movi    a3, _start+3\n"
    "	sub     a2, a0, a3\n"
#if defined(__XTENSA_WINDOWED_ABI__)
    "	# Make sure a0 is cleared to mark the top of stack.\n"
    "	movi    a0, 0\n"
    "	# user_entry_point = _dl_start(pointer to argument block)\n"
    "	movi    a4, _dl_start\n"
    "	mov     a6, sp\n"
    "	add     a4, a4, a2\n"
    "	callx4  a4\n"
    "	# Save user_entry_point so we can jump to it.\n"
    "	mov     a3, a6\n"
#elif defined(__XTENSA_CALL0_ABI__)
    "	# user_entry_point = _dl_start(pointer to argument block)\n"
    "	movi    a0, _dl_start\n"
    "	add     a0, a0, a2\n"
    "	mov     a2, sp\n"
    "	callx0  a0\n"
    "	# Save user_entry_point so we can jump to it.\n"
    "	mov     a3, a2\n"
#else
#error Unsupported Xtensa ABI
#endif
    "	l32i    a7, sp, 0   # load argc\n"
    "	# Load _dl_skip_args into a4.\n"
    "	movi    a4, _dl_skip_args\n"
    "	l32i    a4, a4, 0\n"
    "	bnez    a4, .Lfixup_stack\n"
    ".Lfixup_stack_ret:\n"
    "	# Pass finalizer (_dl_fini) in a2 to the user entry point.\n"
    "	movi    a2, _dl_fini\n"
    "	# Jump to user's entry point (_start).\n"
    "	jx      a3\n"
    ".Lfixup_stack:\n"
    "	# argc -= _dl_skip_args (with argc @ sp+0)\n"
    "	sub     a7, a7, a4\n"
    "	s32i    a7, sp, 0\n"
    "	# Shift everything by _dl_skip_args.\n"
    "	addi    a5, sp, 4   # a5 = destination ptr = argv\n"
    "	add     a4, a5, a4  # a4 = source ptr = argv + _dl_skip_args\n"
    "	# Shift argv.\n"
    "1:	l32i    a6, a4, 0\n"
    "	addi    a4, a4, 4\n"
    "	s32i    a6, a5, 0\n"
    "	addi    a5, a5, 4\n"
    "	bnez    a6, 1b\n"
    "	# Shift envp.\n"
    "2:	l32i    a6, a4, 0\n"
    "	addi    a4, a4, 4\n"
    "	s32i    a6, a5, 0\n"
    "	addi    a5, a5, 4\n"
    "	bnez    a6, 2b\n"
    "	# Shift auxiliary table.\n"
    "3:	l32i    a6, a4, 0\n"
    "	l32i    a8, a4, 4\n"
    "	addi    a4, a4, 8\n"
    "	s32i    a6, a5, 0\n"
    "	s32i    a8, a5, 4\n"
    "	addi    a5, a5, 8\n"
    "	bnez    a6, 3b\n"
    "	j      .Lfixup_stack_ret");

/* Get a pointer to the argv value.  */
#define GET_ARGV(ARGVP, ARGS) ARGVP = (((unsigned long *) ARGS) + 1)

/* Function calls are not safe until the GOT relocations have been done.  */
#define NO_FUNCS_BEFORE_BOOTSTRAP

#define PERFORM_BOOTSTRAP_GOT(tpnt) \
do { \
	xtensa_got_location *got_loc; \
	unsigned long l_addr = tpnt->loadaddr; \
	Elf32_Word relative_count; \
	unsigned long rel_addr; \
	Elf32_Addr prev_got_start = 0, prev_got_end = 0; \
	int x; \
\
	got_loc = (xtensa_got_location *) \
		(tpnt->dynamic_info[DT_XTENSA (GOT_LOC_OFF)] + l_addr); \
\
	for (x = 0; x < tpnt->dynamic_info[DT_XTENSA (GOT_LOC_SZ)]; x++) { \
		Elf32_Addr got_start, got_end; \
		got_start = got_loc[x].offset & ~(PAGE_SIZE - 1); \
		got_end = ((got_loc[x].offset + got_loc[x].length + PAGE_SIZE - 1) \
				   & ~(PAGE_SIZE - 1)); \
		if (got_end >= prev_got_start && got_start <= prev_got_end) { \
			if (got_end > prev_got_end) \
				prev_got_end = got_end; \
			if (got_start < prev_got_start) \
				prev_got_start = got_start; \
			continue; \
		} else if (prev_got_start != prev_got_end) { \
			_dl_mprotect ((void *)(prev_got_start + l_addr), \
						  prev_got_end - prev_got_start, \
						  PROT_READ | PROT_WRITE | PROT_EXEC); \
		} \
		prev_got_start = got_start; \
		prev_got_end = got_end; \
	} \
\
	if (prev_got_start != prev_got_end) { \
		_dl_mprotect ((void *)(prev_got_start + l_addr), \
					  prev_got_end - prev_got_start, \
					  PROT_READ | PROT_WRITE | PROT_EXEC); \
	} \
\
	/* The following is a stripped down version of the code following \
	   the invocation of PERFORM_BOOTSTRAP_GOT in dl-startup.c.	 That \
	   code is skipped when PERFORM_BOOTSTRAP_GOT is defined, so it has \
	   to be done here instead.	 */ \
	relative_count = tpnt->dynamic_info[DT_RELCONT_IDX]; \
	rel_addr = tpnt->dynamic_info[DT_RELOC_TABLE_ADDR]; \
	if (rel_addr) \
	  elf_machine_relative(load_addr, rel_addr, relative_count); \
} while (0)