aboutsummaryrefslogtreecommitdiffstats
path: root/exec/loader-mipsel.s
blob: 92239614de344acc2c8d283d9e42a0f7e1d172ba (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
# 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')

## Beware: $t0-$t4 alias the syscall (and function, but they are not
## material in this context) argument registers on N32 systems, and
## mustn't be relied upon to hold arguments to `SYSCALL'.

	.set noreorder			# delay slots managed by hand
	.section .text
	.global __start
__start:
	## li	$v0, SYSCALL_nanosleep	# SYS_nanosleep
	## la	$a0, timespec		# rqtp
	## li	$a1, 0			# rmtp
	## syscall			# syscall
	lw	$s6, ($sp)		# original stack pointer
	addi	$s0, $sp, 8		# start of load area
	addi	$sp, -8			# primary fd, secondary fd
	li	$t0, -1			# secondary fd
	sw	$t0, 4($sp)		# initialize secondary fd
next_action:
	lw	$s2, ($s0)		# action number
	andi	$t0, $s2, 15		# t0 = s2 & 15
	beqz	$t0, open_file		# open file?
	li	$t1, 3			# t1 = 3, delay slot
	beq	$t0, $t1, rest_of_exec	# jump to code
	li	$t1, 4			# t1 = 4, delay slot
	beq	$t0, $t1, do_mmap_anon	# anonymous mmap
do_mmap:
	lw	$a0, 4($s0)		# vm_address, delay slot
	lw	$v1, 8($s0)		# file_offset
	lw	$a2, 12($s0)		# protection
	lw	$a1, 16($s0)		# length
	lw	$a3, 20($s0)		# flags
	lw	$v0, ($sp)		# primary fd
	andi	$t1, $s2, 16		# t1 = s2 & 16
	beqz	$t1, do_mmap_1		# secondary fd?
	nop				# delay slot
	lw	$v0, 4($sp)		# secondary fd
	nop				# delay slot
do_mmap_1:
SYSCALL(`$v0',`$v1',`$zero',`$zero')	# syscall args
	li	$v0, SYSCALL_mmap	# SYS_mmap
	syscall				# syscall
	bnez	$a3, perror		# perror
RESTORE()				# delay slot, restore sp
	lw	$s5, 24($s0)		# clear
	add	$t0, $a0, $a1		# t0 = length + vm_address, delay slot
	sub	$t1, $t0, $s5		# t1 = t0 - clear
align:
	beq	$t0, $t1, continue	# already finished?
	nop				# delay slot
	andi	$t2, $t1, 3		# t1 & 3?
	bnez	$t2, fillw		# start filling longs
	nop				# delay slot
	sb	$zero, ($t1)		# clear byte
	addi	$t1, $t1, 1		# t1++
	j	align			# continue
	nop				# delay slot
fillw:
	sub	$t2, $t0, $t1		# t2 = t0 - t1
	sltiu	$t2, $t2, 32		# r2 < 32?
	bne	$t2, $zero, fillb	# fill bytes
	nop				# delay slot
	sw	$zero, ($t1)		# zero word
	addi	$t1, $t1, 4		# next word
	sw	$zero, ($t1)		# zero word
	addi	$t1, $t1, 4		# next word
	sw	$zero, ($t1)		# zero word
	addi	$t1, $t1, 4		# next word
	sw	$zero, ($t1)		# zero word
	addi	$t1, $t1, 4		# next word
	sw	$zero, ($t1)		# zero word
	addi	$t1, $t1, 4		# next word
	sw	$zero, ($t1)		# zero word
	addi	$t1, $t1, 4		# next word
	sw	$zero, ($t1)		# zero word
	addi	$t1, $t1, 4		# next word
	sw	$zero, ($t1)		# zero word
	addi	$t1, $t1, 4		# next word
	j	fillw			# fill either word or byte
	nop				# delay slot
fillb:
	beq	$t0, $t1, continue	# already finished?
	nop				# delay slot
	sb	$zero, ($t1)		# clear byte
	addi	$t1, $t1, 1		# t1++
continue:
	addi	$s0, $s0, 28		# s0 = next action
	j	next_action		# next action
	nop				# delay slot
do_mmap_anon:
	lw	$v1, 8($s0)		# file_offset
	lw	$a2, 12($s0)		# protection
	lw	$a1, 16($s0)		# length
	lw	$a3, 20($s0)		# flags
	j	do_mmap_1		# do mmap
	li	$v0, -1			# fd, delay slot
open_file:
	li	$v0, SYSCALL_open	# SYS_open
	addi	$a0, $s0, 4		# start of name
	move	$a1, $zero		# flags = O_RDONLY
	move	$a2, $zero		# mode = 0
	syscall				# syscall
	bne	$a3, $zero, perror	# perror
	addi	$s0, $s0, 4		# start of string, delay slot
	move	$t3, $s0		# t3 = char past separator
nextc:
	lb	$t0, ($s0)		# load byte
	addi	$s0, $s0, 1		# s0++
	li	$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
	addi	$s0, $s0, 3		# adjust for round
	li	$t2, -4			# t2 = -4
	and	$s0, $s0, $t2		# mask for round
	andi	$t0, $s2, 16		# t1 = s2 & 16
	beqz	$t0, primary		# primary fd?
	move	$t0, $sp		# address of primary fd, delay slot
	addi	$t0, $t0, 4		# address of secondary fd
	j	next_action		# next action
primary:
	sw	$v0, ($t0)		# store fd, delay slot
	li	$v0, SYSCALL_prctl	# SYS_prctl
	li	$a0, 15			# PR_SET_NAME
	move	$a1, $t3		# name
	move	$a2, $zero		# arg1
	move	$a3, $zero		# arg2
SYSCALL(`$a2',`$a2',`$a2',`$a2')	# syscall args
	syscall				# syscall
RESTORE()				# restore sp
	j	next_action		# next action
	nop				# delay slot
perror:
	move	$a0, $v0		# errno
	li	$v0, SYSCALL_exit	# SYS_exit
	syscall				# syscall
rest_of_exec:
	move	$s1, $s6		# s1 = original SP
	lw	$t0, ($s1)		# argc
	sll	$t0, $t0, 2		# argc *= 4
	addi	$t0, $t0, 8		# argc += 8
	add	$s1, $s1, $t0		# s1 = start of envp
skip_environ:
	/* Locate the auxiliary vector.  */
1:	lw	$t0, ($s1)		# t0 = *s1
	bnez	$t0, 1b			# skip environment entry
	addi	$s1, $s1, 4		# s1++
	move	$s2, $s1		# $s2 = end of environment
1:	lw	$t0, ($s1)		# t0 = *s1
	bnez	$t0, 1b			# skip auxiliary vector entry
	addi	$s1, $s1, 8		# (Elf32_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.  */
	lw	$t1, 32($s0)		# length of string
	addi	$t1, $t1, 1
	addi	$t2, $s0, 36		# pointer to string
	sub	$t3, $s1, $s6		# number of bytes in vectors
	sub	$t0, $s1, $t1		# position of string
	and	$t0, $t0, -16		# align value
	sub	$t3, $t0, $t3		# position of argc
	and	$t3, $t3, -16		# align value
	/* Move the stack pointer and save required information.
	   4(FP)   = secondary/interpreter fd.
	   0(FP)   = primary/executable fd.
	   -4(FP)  = cmd->entry
	   -8(FP)  = cmd->at_entry
	   -12(FP) = cmd->at_phent
	   -16(FP) = cmd->at_phnum
	   -20(FP) = cmd->at_phdr
	   -24(FP) = cmd->at_base
	   -28(FP) = cmd->fpu_mode (only significant when N32)
	   $sp = copy of string.  */
	move	$t4, $sp		# current sp
	sub	$t5, $t3, $sp		# new argc - current sp
	blt	$t5, 8, 1f		# more than two slots apart
	addi	$sp, $t3, -8		# $sp = two slots below new argc
	j	2f			# skip copying fds
	move	$sp, $t4		# retain current sp
1:	lw	$t5, ($t4)		# old primary fd
	sw	$t5, ($sp)		# save the same
	lw	$t5, 4($t4)		# old interpreter fd
	sw	$t5, 4($sp)		# save the same
2:	move	FP, $sp			# set base pointer
	addi	$sp, $sp, -28		# command data
	lw	$t5, 4($s0)		# entry
	lw	$t6, 8($s0)		# at_entry
	sw	$t5, -4(FP)		# save entry
	sw	$t6, -8(FP)		# save at_entry
	lw	$t5, 12($s0)		# at_phent
	lw	$t6, 16($s0)		# at_phnum
	sw	$t5, -12(FP)		# save at_phent
	sw	$t6, -16(FP)		# save at_phnum
	lw	$t5, 20($s0)		# at_phdr
	lw	$t6, 24($s0)		# at_base
	sw	$t5, -20(FP)		# save at_phdr
	sw	$t6, -24(FP)		# save at_base
	lw	$t5, 28($s0)		# fpu_mode
	sw	$t5, -28(FP)		# save fpu_mode
	sub	$sp, $sp, $t1		# space for string
	/* Save the input string.  */
	add	$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
	addi	$t2, $t2, 1		# $t2++
	addi	$t6, $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:	lw	$t1, ($t5)		# $t1 = *src
	addi	$t5, $t5, 4		# src++
	addi	$t4, $t4, 4		# dst++
	bltu	$t5, $s2, 1b		# src < src_end
	sw	$t1, -4($t4)		# *(dst - 4) = $t1
copy_auxv:
	/* $t4 = destination, $t5 = first auxval.  */
2:	lw	$t1, ($t5)		# a_type
	lw	$t2, 4($t5)		# a_un.a_val
	addi	$t4, $t4, 8		# (Elf32_auxv_t *) dst++
	addi	$t5, $t5, 8		# (Elf32_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
	lw	$t2, -20(FP)
2:	b	7f
	lw	$t2, -12(FP)
3:	b	7f
	lw	$t2, -16(FP)
4:	b	7f
	lw	$t2, -8(FP)
5:	b	7f
	lw	$t2, -24(FP)
6:	b	7f
	move	$t2, $t0
7:	sw	$t1, -8($t4)		# dst->a_type
	j	copy_auxv
	sw	$t2, -4($t4)		# dst->a_un.a_val
	/* Copy the final element.  */
8:	sw	$t1, -8($t4)		# dst->a_type
	sw	$t2, -4($t4)		# dst->a_un.a_val
finish:
	/* Copy the string to its position in auxv
	   (src = $s3, dst = $t0).  */
	add	$t1, $s3, $s0		# src end
	bgeu	$s3, $t1, 2f		# there already?
	nop
1:	lb	$t2, ($s3)		# c = *src
	addi	$s3, $s3, 1		# *src++
	addi	$t0, $t0, 1		# dst++
	bltu	$s3, $t1, 1b
	sb	$t2, -1($t0)	   	# *(dst - 1) = c
	/* Save variables.  */
2:	move	$s6, $t3	   	# new stack pointer
	lw	$t4, 4(FP)		# secondary fd
	lw	$s1, (FP)		# primary fd, delay slot, preserved
	li	$t2, -1			# immediate -1
	beq	$t4, $t2, finish1	# secondary fd set?
	li	$v0, SYSCALL_close	# SYS_close, delay slot
	move	$a0, $t4		# fd
	syscall				# syscall
	li	$v0, SYSCALL_close	# SYS_close
finish1:
	move	$a0, $s1		# primary fd
	syscall				# syscall
	li	$v0, SYSCALL_prctl	# SYS_prctl
	li	$a0, 45			# PR_SET_FP_MODE
	lw	$a1, -28(FP)		# fpu_mode
	move	$a2, $zero		# arg3
	move	$a3, $zero		# arg4
SYSCALL(`$a2',`$a2',`$a2',`$a2')	# syscall args
	syscall				# syscall
RESTORE()				# restore sp
jump:
	move	$v0, $zero		# rtld_fini
	lw	$t9, -4(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?  */
	addi	$v0, $sp, -4096
	and	$v0, $v0, -4095
1:	sw	$zero, ($v0)		# copy 32 byte blocks
	sw	$zero, 4($v0)
	sw	$zero, 8($v0)
	sw	$zero, 12($v0)
	sw	$zero, 16($v0)
	sw	$zero, 20($v0)
	sw	$zero, 24($v0)
	sw	$zero, 28($v0)
	addi	$v0, $v0, 32
	sub	$t0, $sp, $v0		# remainder
	bge	$t0, 32, 1b		# test remainder
	nop				# copy 4 byte blocks
	beqz	$t0, 2f
1:	addi	$v0, $v0, 4
	bltu	$v0, $sp, 1b
	sw	$zero, -4($v0)
2:	jr	$t9			# enter
	nop				# delay slot

## timespec:
## 	.long	10
## 	.long	10

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