aboutsummaryrefslogtreecommitdiffstats
path: root/exec/loader-mips64el.s
blob: 2f20c2cf5d30aa6ea378287eb6fb498a19bdd326 (plain) (blame)
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
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
# Copyright (C) 2023-2025 Free Software Foundation, Inc.
#
# This file is part of GNU Emacs.
#
# GNU Emacs is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published
# by the Free Software Foundation, either version 3 of the License,
# or (at your option) any later version.
#
# GNU Emacs is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.	See the GNU
# General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with GNU Emacs.	 If not, see <https://www.gnu.org/licenses/>.

include(`config-mips.m4')

/* These "registers" alias a4-a7 and caution must be exercised not
   to overwrite them when issuing system calls.  */
define(`T4', `$a4')
define(`T5', `$a5')
define(`T6', `$a6')
define(`T7', `$a7')

	.set noreorder			# delay slots managed by hand
	.section .text
	.global __start
__start:
	## li	$v0, 5034		# SYS_nanosleep
	## dla	$a0, timespec		# rqtp
	## li	$a1, 0			# rmtp
	## syscall			# syscall
	ld	$s6, ($sp)		# original stack pointer
	DADDI3(	$s0, $sp, 16)		# start of load area
	DADDI2(	$sp, -16)		# primary fd, secondary fd
	li	$t0, -1			# secondary fd
	sd	$t0, 8($sp)		# initialize secondary fd
next_action:
	ld	$s1, ($s0)		# action number
	andi	$t0, $s1, 15		# t0 = action number & 15
	beqz	$t0, open_file		# open file?
	nop				# delay slot
	DADDI2(	$t0, -3)		# t0 -= 3
	beqz	$t0, rest_of_exec	# jump to code
	nop				# delay slot
	li	$t1, 1
	beq	$t0, $t1, do_mmap_anon	# anonymous mmap?
	nop				# delay slot
do_mmap:
	ld	$t0, 8($s0)		# vm address
	ld	$t1, 16($s0)		# file_offset
	ld	$t2, 24($s0)		# protection
	ld	$t3, 32($s0)		# length
	ld	$v0, 40($s0)		# flags
	ld	$v1, ($sp)		# primary fd
	andi	$s3, $s1, 16		# s1 & 16?
	beqz	$s3, do_mmap_1		# secondary fd?
	nop				# delay slot
	ld	$v1, 8($sp)		# secondary fd
do_mmap_1:
	move	$a0, $t0		# syscall arg
	move	$a1, $t3		# syscall arg
	move	$a2, $t2		# syscall arg
	move	$a3, $v0		# syscall arg
	move	$a4, $v1		# syscall arg
	move	$a5, $t1		# syscall arg
	li	$v0, 5009		# SYS_mmap
	syscall				# syscall
	bne	$a3, $zero, perror	# perror?
	nop				# delay slot
	ld	$t1, 48($s0)		# clear
	dadd	$t0, $a0, $a1		# t0 = end of mapping
	dsub	$t1, $t0, $t1		# t1 = t0 - clear
align:
	beq	$t0, $t1, continue	# already finished
	nop				# delay slot
	andi	$t2, $t1, 7		# t1 & 7?
	bnez	$t2, filld		# start filling longs
	nop				# delay slot
filld:
	dsub	$t2, $t0, $t1		# t2 = t0 - t1
	sltiu	$t2, $t2, 64		# t2 < 64?
	bne	$t2, $zero, fillb	# fill bytes
	nop				# delay slot
	sd	$zero, ($t1)		# zero doubleword
	DADDI2(	$t1, 8)			# next doubleword
	sd	$zero, ($t1)		# zero doubleword
	DADDI2(	$t1, 8)			# next doubleword
	sd	$zero, ($t1)		# zero doubleword
	DADDI2(	$t1, 8)			# next doubleword
	sd	$zero, ($t1)		# zero doubleword
	DADDI2(	$t1, 8)			# next doubleword
	sd	$zero, ($t1)		# zero doubleword
	DADDI2(	$t1, 8)			# next doubleword
	sd	$zero, ($t1)		# zero doubleword
	DADDI2(	$t1, 8)			# next doubleword
	sd	$zero, ($t1)		# zero doubleword
	DADDI2(	$t1, 8)			# next doubleword
	sd	$zero, ($t1)		# zero doubleword
	DADDI2(	$t1, 8)			# next doubleword
	j	filld			# fill either doubleword or byte
	nop				# delay slot
fillb:
	beq	$t0, $t1, continue	# already finished?
	nop				# delay slot
	sb	$zero, ($t1)		# clear byte
	DADDI2(	$t1, 1)			# t1++
continue:
	DADDI2(	$s0, 56)		# s0 = next action
	j	next_action		# next action
	nop				# delay slot
do_mmap_anon:
	ld	$t0, 8($s0)		# vm address
	ld	$t1, 16($s0)		# file_offset
	ld	$t2, 24($s0)		# protection
	ld	$t3, 32($s0)		# length
	ld	$v0, 40($s0)		# flags
	dli	$v1, -1			# fd
	j	do_mmap_1		# do mmap
	nop				# branch delay slot
open_file:
	dli	$v0, 5002		# SYS_open
	DADDI3(	$a0, $s0, 8)		# start of name
	move	$a1, $zero		# flags = O_RDONLY
	move	$a2, $zero		# mode = 0
	syscall				# syscall
	bne	$a3, $zero, perror	# perror
	nop				# delay slot
	DADDI2(	$s0, 8)			# start of string
	move	$t3, $s0		# t3 = s0
nextc:
	lb	$t0, ($s0)		# load byte
	DADDI2(	$s0, 1)			# s0++
	dli	$t1, 47			# directory separator `/'
	bne	$t0, $t1, nextc1	# is separator char?
	nop				# delay slot
	move	$t3, $s0		# t3 = char past separator
nextc1:
	bnez	$t0, nextc		# next character?
	nop				# delay slot
	DADDI2(	$s0, 7)			# adjust for round
	dli	$t2, -8			# t2 = -8
	and	$s0, $s0, $t2		# mask for round
	andi	$t0, $s1, 16		# t1 = s1 & 16
	move	$t1, $sp		# address of primary fd
	beqz	$t0, primary		# primary fd?
	nop				# delay slot
	DADDI2(	$t1, 8)			# address of secondary fd
	sd	$v0, ($t1)		# store fd
	j	next_action		# next action
	nop				# delay slot
primary:
	sd	$v0, ($t1)		# store fd
	dli	$v0, 5153		# SYS_prctl
	dli	$a0, 15			# PR_SET_NAME
	move	$a1, $t3		# char past separator
	move	$a2, $zero		# a2
	move	$a3, $zero		# a3
	move	$a4, $zero		# a4
	move	$a5, $zero		# a5
	syscall				# syscall
	j	next_action		# next action
	nop				# delay slot
perror:
	move	$a0, $v0		# errno
	dli	$v0, 5058		# SYS_exit
	syscall				# syscall
rest_of_exec:
	move	$s1, $s6		# original SP
	ld	$t0, ($s1)		# argc
	dsll	$t0, $t0, 3		# argc *= 8
	DADDI2(	$t0, 16)		# argc += 16
	dadd	$s1, $s1, $t0		# s1 = start of envp
skip_environ:
	/* Locate the auxiliary vector.  */
	li	$t8, 8 			# DADDI2 isn't appropriate in delay slots.
1:	ld	$t0, ($s1)		# t0 = *s1
	bnez	$t0, 1b			# skip environment entry
	dadd	$s1, $s1, $t8		# s1++
	move	$s2, $s1		# s2 = end of environment
	li	$t8, 16
1:	ld	$t0, ($s1)		# t0 = s1->a_type
	bnez	$t0, 1b			# skip auxiliary vector entry
	dadd	$s1, $s1, $t8		# (Elf64_auxv_t *) s1++
	/* Decide how many bytes must be copied and where to
	   save the file name.  Move the stack pointer to a safe
	   position below any data that must be preserved.  */
	ld	$t1, 56($s0)		# length of string
	DADDI2(	$t1, 1)
	DADDI3(	$t2, $s0, 64)		# pointer to string
	dsub	$t3, $s1, $s6		# number of bytes in vectors
	dsub	$t0, $s1, $t1		# position of string
	and	$t0, $t0, -16		# align value
	dsub	$t3, $t0, $t3		# position of argc
	and	$t3, $t3, -16		# align value
	/* Move the stack pointer and save required information.
	   8($fp)   = secondary/interpreter fd.
	   0($fp)   = primary/executable fd.
	   -8($fp)  = cmd->entry
	   -16($fp) = cmd->at_entry
	   -24($fp) = cmd->at_phent
	   -32($fp) = cmd->at_phnum
	   -40($fp) = cmd->at_phdr
	   -48($fp) = cmd->at_base
	$sp = copy of string.  */
	move	T4, $sp			# current sp
	dsub	T5, $t3, $sp		# new argc - current sp
	li	$t8, -16
	blt	T5, 16, 1f		# more than two slots apart
	dadd	$sp, $t3, $t8		# $sp = two slots below new argc
	j	2f			# skip copying fds
	move	$sp, T4			# retain current sp
1:	ld	T5, (T4)		# old primary fd
	ld	T5, ($sp)		# save the same
	ld	T5, 8(T4)		# old interpreter fd
	sd	T5, 8($sp)		# save the same
2:	move	$fp, $sp		# set base pointer
	DADDI2(	$sp, -48)		# command data
	ld	T5, 8($s0)		# entry
	ld	T6, 16($s0)		# at_entry
	ld	T7, 24($s0)		# at_phent
	ld	$t8, 32($s0)		# at_phnum
	sd	T5, -8($fp)		# save entry
	ld	T5, 40($s0)		# at_phdr
	sd	T6, -16($fp)		# save at_entry
	ld	T6, 48($s0)		# at_base
	sd	T7, -24($fp)		# save at_phent
	sd	$t8, -32($fp)		# save at_phnum
	sd	T5, -40($fp)		# save at_phdr
	sd	T6, -48($fp)		# save at_base
	dsub	$sp, $sp, $t1		# space for string
	/* Save the input string.  */
	dadd	T5, $t2, $t1		# end of source ($t2)
	move	T6, $sp			# dst
	move	$s0, $t1		# $s0 = length of string
	/* src = $t2, dst = T6 */
	bgeu	$t2, T5, 2f		# there already?
	nop
1:	lb	$t1, ($t2)		# $t1 = *$t2
	DADDI2(	$t2, 1)			# $t2++
	DADDI2(	T6, 1)			# $t6++
	bltu	$t2, T5, 1b
	sb	$t1, -1(T6)		# *(T6 - 1) = $t1
2:	move	$s3, $sp		# copy of string
	and	$sp, $sp, -16		# align stack
copy_env_and_args:
	/* Copy argc, argv, and the environment array.
	   T4 = destination, T5 = src, $s2 = src_end  */
	move	T4, $t3			# destination of argc
	move	T5, $s6			# original SP
	bgeu	T5, $s2, 2f		# there already?
	nop
1:	ld	$t1, (T5)		# $t1 = *src
	DADDI2(	T5, 8)			# src++
	DADDI2(	T4, 8)			# dst++
	bltu	T5, $s2, 1b		# src < src_end
	sd	$t1, -8(T4)		# *(dst - 8) = $t1
copy_auxv:
	/* T4 = destination, T5 = first auxval.  */
2:	ld	$t1, (T5)		# a_type
	ld	$t2, 8(T5)		# a_un.a_val
	DADDI2(	T4, 16)			# (Elf64_auxv_t *) dst++
	DADDI2(	T5, 16)			# (Elf64_auxv_t *) src
	beqz	$t1, 8f			# AT_NULL
	li	T6, 3
	beq	$t1, T6, 1f		# AT_PHDR
	li	T6, 4
	beq	$t1, T6, 2f		# AT_PHENT
	li	T6, 5
	beq	$t1, T6, 3f		# AT_PHNUM
	li	T6, 9
	beq	$t1, T6, 4f		# AT_ENTRY
	li	T6, 7
	beq	$t1, T6, 5f		# AT_BASE
	li	T6, 31
	beq	$t1, T6, 6f		# AT_EXECFN
	nop
	b	7f
	nop
1:	b	7f
	ld	$t2, -40($fp)
2:	b	7f
	ld	$t2, -24($fp)
3:	b	7f
	ld	$t2, -32($fp)
4:	b	7f
	ld	$t2, -16($fp)
5:	b	7f
	ld	$t2, -48($fp)
6:	b	7f
	move	$t2, $t0
7:	sd	$t1, -16(T4)		# dst->a_type
	j	copy_auxv
	sd	$t2, -8(T4)		# dst->a_un.a_val
	/* Copy the final element.  */
8:	sd	$t1, -16(T4)		# dst->a_type
	sd	$t2, -8(T4)		# dst->a_un.a_val
finish:
	/* Copy the string to its position in auxv
	   (src = $s3, dst = $t0).  */
	dadd	$t1, $s3, $s0		# src end
	bgeu	$s3, $t1, 2f		# there already?
	nop
1:	lb	$t2, ($s3)		# c = *src
	DADDI2(	$s3, 1)			# src++
	DADDI2(	$t0, 1)			# dst++
	bltu	$s3, $t1, 1b
	sb	$t2, -1($t0)	   	# *(dst - 1) = c
	/* Save variables.  */
2:	move	$s6, $t3		# new stack pointer
	ld	$t0, 8($fp)		# secondary fd
	li	$t1, -1			# t1 = -1
	ld	$s1, ($fp)		# s1 = primary fd
	beq	$t0, $t2, finish1	# secondary fd set?
	li	$v0, 5003		# SYS_close
	move	$a0, $t0		# secondary fd
	syscall				# syscall
	li	$v0, 5003		# SYS_close
finish1:
	move	$a0, $s1		# primary fd
	syscall				# syscall
jump:
	move	$v0, $zero		# rtld_fini
	ld	$t9, -8($fp)		# entry
	move	$sp, $s6		# restore stack pointer, delay slot
        /* Clear at least one page's worth of stack.  glibc on mipsel
	   copies certain fields from the stack to the `link_map'
	   structure representing ld.so, which are not subsequently
	   replaced if otherwise than zero.

	   XXX: report this glibc bug?  */
	DADDI3(	$v0, $sp, -4096)
	and	$v0, $v0, -4095
1:	sd	$zero, ($v0)		# copy 32 byte blocks
	sd	$zero, 8($v0)
	sd	$zero, 16($v0)
	sd	$zero, 24($v0)
	DADDI2(	$v0, 32)
	dsub	$t0, $sp, $v0		# remainder
	bge	$t0, 32, 1b		# test remainder
	nop				# copy 4 byte blocks
	beqz	$t0, 2f
	nop
1:	DADDI2(	$v0, 4)
	bltu	$v0, $sp, 1b
	sw	$zero, -4($v0)
2:	jr	$t9			# enter
	nop				# delay slot

## timespec:
## 	.quad	10
## 	.quad	10

# Local Variables:
# asm-comment-char: ?#
# End: