aboutsummaryrefslogtreecommitdiffstats
path: root/exec/loader-aarch64.s
blob: d3c565bf3f8d4a94da597b4d9bb145036f167c1f (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
// 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/>.

// Notice that aarch64 requires that sp be aligned to 16 bytes while
// accessing memory from sp, so x20 is used to chase down the load
// area.

	.section .text
	.global _start
_start:
	// mov	x8, 101			// SYS_nanosleep
	// adr	x0, timespec		// req
	// mov	x1, #0			// rem
	// svc	#0			// syscall
	mov	x20, sp			// x20 = sp
	ldr	x10, [x20]		// x10 = original SP
	add	x20, x20, #16		// x20 = start of load area
	mov	x28, #-1		// x28 = secondary fd
next_action:
	ldr	x11, [x20]		// action number
	and	x12, x11, #-17		// actual action number
	cbz	x12, open_file		// open file?
	cmp	x12, #3			// jump?
	beq	rest_of_exec
	cmp	x12, #4			// anonymous mmap?
	beq	do_mmap_anon
do_mmap:
	ldr	x0, [x20, 8]		// vm_address
	ldr	x1, [x20, 32]		// length
	ldr	x2, [x20, 24]		// protection
	ldr	x3, [x20, 40]		// flags
	tst	x11, #16		// primary fd?
	mov	x4, x29			// primary fd
	beq	do_mmap_1
	mov	x4, x28			// secondary fd
do_mmap_1:
	mov	x8, #222		// SYS_mmap
	ldr	x5, [x20, 16]		// file_offset
	svc	#0			// syscall
	ldr	x9, [x20, 8]		// length
	cmp	x0, x9			// mmap result
	bne	perror			// print error
	ldr	x3, [x20, 48]		// clear
	add	x1, x1, x0		// x1 = vm_address + end
	sub	x3, x1, x3		// x3 = x1 - clear
	mov	x0, #0			// x0 = 0
fill64:
	sub	x2, x1, x3		// x2 = x1 - x3
	cmp	x2, #63			// x2 >= 64?
	ble	fillb			// start filling bytes
	stp	x0, x0, [x3]		// x3[0] = 0, x3[1] = 0
	stp	x0, x0, [x3, 16]	// x3[2] = 0, x3[3] = 0
	stp	x0, x0, [x3, 32]	// x3[4] = 0, x3[5] = 0
	stp	x0, x0, [x3, 48]	// x3[6] = 0, x3[7] = 0
	add	x3, x3, #64		// x3 += 8
	b	fill64
fillb:
	cmp	x1, x3			// x1 == x3?
	beq	continue		// done
	strb	w0, [x3], #1		// ((char *) x3)++ = 0
	b	fillb
continue:
	add	x20, x20, #56		// next action
	b	next_action
do_mmap_anon:
	ldr	x0, [x20, 8]		// vm_address
	ldr	x1, [x20, 32]		// length
	ldr	x2, [x20, 24]		// protection
	ldr	x3, [x20, 40]		// flags
	mov	x4, #-1			// fd
	b	do_mmap_1
open_file:
	mov	x8, #56			// SYS_openat
	mov	x0, #-100		// AT_FDCWD
	add	x1, x20, #8		// file name
	mov	x2, #0			// O_RDONLY
	mov	x3, #0			// mode
	svc	#0			// syscall
	cmp	x0, #-1			// rc < 0?
	ble	perror
	mov	x19, x1			// x19 == x1
nextc:
	ldrb	w2, [x1], #1		// b = *x1++
	cmp	w2, #47			// dir separator?
	bne	nextc1			// not dir separator
	mov	x19, x1			// x19 = char past separator
nextc1:
	cbnz	w2, nextc		// b?
	add	x1, x1, #7		// round up x1
	and	x20, x1, #-8		// mask for round, set x20
	tst	x11, #16		// primary fd?
	bne	secondary		// secondary fd
	mov	x29, x0			// primary fd
	mov	x8, #167		// SYS_prctl
	mov	x0, #15			// PR_SET_NAME
	mov	x1, x19			// basename
	mov	x2, #0			// arg2
	mov	x3, #0			// arg3
	mov	x4, #0			// arg4
	mov	x5, #0			// arg5
	svc	#0			// syscall
	b	next_action		// next action
secondary:
	mov	x28, x0			// secondary fd
	b	next_action		// next action.
perror:
	mov	x8, #93			// SYS_exit
	mvn	x0, x0			// x1 = ~x0
	add	x0, x0, 1		// x1 += 1
	svc	#0			// exit
rest_of_exec:
	mov	x7, x20			// x7 = x20
	mov	x8, x10			// x8 = x10
	ldr	x9, [x8], #16		// (void *) x8 += 2
	lsl	x9, x9, #3		// argc * 8
	add	x8, x8, x9		// now past argv
skip_environ:
	ldr	x9, [x8], #8		// x9 = *envp++
	cbnz	x9, skip_environ	// x9?
	// Skip the auxiliary vector.
1:	ldp	x11, x12, [x8], #16	// a_type, a_un.a_val
	cbnz	x11, 1b			// a_type != NULL
	// Prepare sufficient space at x20 for the file name string.
	// Load the aforesaid string, and its length.
	ldr	x6, [x7, 56]		// string length
	add	x6, x6, 1
	add	x5, x7, 64		// string pointer
	sub	x4, x10, x8		// number of elements to copy
	sub	x7, x8, x6		// AT_EXECFN location
	and	x7, x7, -8		// align value
	add	x4, x7, x4		// destination argc
	and	x4, x4, -16		// align destination argc
	// Load values that must be preserved into registers x14-x19.
	// x14 = cmd->entry
	// x15 = cmd->at_entry
	// x16 = cmd->at_phent
	// x17 = cmd->at_phnum
	// x18 = cmd->at_phdr
	// x19 = cmd->at_base
	ldp	x14, x15, [x20, 8]
	ldp	x16, x17, [x20, 24]
	ldp	x18, x19, [x20, 40]
	// Move the string to a safe location, if necessary.
	sub	x3, x4, x5		// distance from dest to string
	cmp	x3, x6			// distance > length
	bge	copy_env_and_args 	// not necessary
	mov	x2, x5			// src
	sub	x5, x4, x6		// backup string
	mov	x1, x5			// dst
	add	x9, x2, x6		// src end
	cmp	x2, x9
	bcs	copy_env_and_args
1:	ldrb	w3, [x2], #1
	strb	w3, [x1], #1
	cmp	x2, x9
	blo	1b
copy_env_and_args:
	// Copy argc and the environment array.
	mov	x8, x10
	mov	x10, x4
1:	ldr	x9, [x8], #8		//  envp
	str	x9, [x4], #8
	cbnz	x9, 1b
1:	ldr	x9, [x8], #8		// environ
	str	x9, [x4], #8
	cbnz	x9, 1b
copy_auxv:
	ldp	x11, x12, [x8], #16	// a_type, a_un.a_val
	stp	x11, x12, [x4], #16	// write value
	cbz	x11, cleanup		// AT_NULL
	cmp	x11, #3			// AT_PHDR
	csel	x12, x18, x12, eq
	cmp	x11, #4			// AT_PHENT
	csel	x12, x16, x12, eq
	cmp	x11, #5			// AT_PHNUM
	csel	x12, x17, x12, eq
	cmp	x11, #9			// AT_ENTRY
	csel	x12, x15, x12, eq
	cmp	x11, #7			// AT_BASE
	csel	x12, x19, x12, eq
	cmp	x11, #31		// AT_EXECFN
	csel	x12, x7, x12, eq
	str	x12, [x4, -8]		// replace value
	b	copy_auxv
cleanup:
	// Copy the filename.
	add	x9, x5, x6		// end
	cmp	x5, x9
	bcs	2f
1:	ldrb	w3, [x5], #1
	strb	w3, [x7], #1
	cmp	x5, x9
	blo	1b
	// Close file descriptors.
2:	cmp	x28, #-1		// is secondary fd set?
	beq	cleanup1		// not set
	mov	x8, #57			// SYS_close
	mov	x0, x28			// secondary fd
	svc	#0			// syscall
cleanup1:
	mov	x8, #57			// SYS_close
	mov	x0, x29			// primary fd
	svc	#0			// syscall
enter:
	mov	sp, x10			// restore original SP
	mov	x0, #0			// clear rtld_fini
	br	x14

// timespec:
// 	.quad 10
// 	.quad 10

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