aboutsummaryrefslogtreecommitdiffstats
path: root/exec/loader-x86_64.s
blob: 70911ffd64dd689020c20c7de3b052122fdbfece (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
# Copyright (C) 2023-2026 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/>.

	.section .text
	.global _start
_start:
#	movq	$35, %rax		# SYS_nanosleep
#	leaq	timespec(%rip), %rdi
#	xorq	%rsi, %rsi
#	syscall
	popq	%r13			# original SP
	popq	%r15			# size of load area.
	movq	$-1, %r12		# r12 is the interpreter fd
next_action:
	movq	(%rsp), %r14		# action number
	movq	%r14, %r15		# original action number
	andq	$-17, %r14
	cmpq	$0, %r14		# open file?
	je	open_file
	cmpq	$3, %r14 		# jump?
	je	rest_of_exec
	cmpq	$4, %r14		# anonymous mmap?
	je	do_mmap_anon
do_mmap:
	movq	$9, %rax		# SYS_mmap
	movq	8(%rsp), %rdi		# address
	movq	16(%rsp), %r9		# offset
	movq	24(%rsp), %rdx		# protection
	movq	32(%rsp), %rsi		# length
	movq	40(%rsp), %r10		# flags
					# set r8 to the primary fd unless r15 & 16
	testq	$16, %r15
	movq	%r12, %r8
	cmovzq	%rbx, %r8
do_mmap_1:
	syscall
	cmpq	$-1, %rax		# mmap failed
	je	perror
	movq	48(%rsp), %r9		# clear
	testq	%r9, %r9
	jz	continue
	movq	8(%rsp), %r10		# start of mapping
	addq	32(%rsp), %r10		# end of mapping
	subq	%r9, %r10		# start of clear area
again:
	testq	%r9, %r9
	jz	continue
	subq	$1, %r9
	movb	$0, (%r10, %r9, 1)
	jmp	again
continue:
	leaq	56(%rsp), %rsp
	jmp	next_action
do_mmap_anon:
	movq	$9, %rax		# SYS_mmap
	movq	8(%rsp), %rdi		# address
	movq	16(%rsp), %r9		# offset
	movq	24(%rsp), %rdx		# protection
	movq	32(%rsp), %rsi		# length
	movq	40(%rsp), %r10		# flags
	movq	$-1, %r8		# -1
	jmp	do_mmap_1
open_file:
	movq	$2, %rax		# SYS_open
	leaq	8(%rsp), %rdi		# rdi = %rsp + 8
	xorq	%rsi, %rsi		# flags = O_RDONLY
	xorq	%rdx, %rdx		# mode = 0
	syscall
	cmpq	$-1, %rax		# open failed
	jle	perror
	movq	%rdi, %rsp		# rsp = start of string
	subq	$1, %rsp
	movq	%rsp, %r14		# r14 = start of string
nextc:
	addq	$1, %rsp
	movb	(%rsp), %dil		# rdi = *rsp
	cmpb	$47, %dil		# *rsp == '/'?
	jne	nextc1
	movq	%rsp, %r14		# r14 = rsp
	addq	$1, %r14		# r14 = char past separator
nextc1:
	cmpb	$0, %dil		# *rsp == 0?
	jne	nextc
	addq	$8, %rsp		# adjust past rsp prior to rounding
	andq	$-8, %rsp		# round rsp up to the next quad
	testq	$16, %r15		# r15 & 16?
	jz	primary
	movq	%rax, %r12		# otherwise, move fd to r12
	jmp	next_action
primary:
	movq	%rax, %rbx		# if not, move fd to rbx
	movq	$157, %rax		# SYS_prctl
	movq	$15, %rdi		# PR_SET_NAME
	movq	%r14, %rsi		# arg1
	xorq	%rdx, %rdx		# arg2
	xorq	%r10, %r10		# arg3
	xorq	%r8, %r8		# arg4
	xorq	%r9, %r9		# arg5
	syscall
	jmp	next_action
perror:
	movq	%rax, %r12		# error code
	negq	%r12
	movq	$1, %rax		# SYS_write
	movq	$1, %rdi		# stdout
	leaq	error(%rip), %rsi	# buffer
	movq	$24, %rdx		# count
	syscall
	movq	$60, %rax		# SYS_exit
	movq	%r12, %rdi		# code
	syscall
rest_of_exec:				# rsp now points to seven quads + string:
	movq	%rsp, %r8		# now, they are r8
	movq	%r13, %rsp		# restore SP
	popq	%r10			# argc
	leaq	8(%rsp,%r10,8), %rsp	# now at start of environ
skip_environ:
	popq	%rcx			# envp[N]
	testq	%rcx, %rcx		# envp[n]?
	jnz	skip_environ		# otherwise, rsp is now at the end of auxv
	movq	%rsp, %r11		# start of auxv
1:	testq	$-1, (%r11)		# NULL?
	leaq	16(%r11), %r11		# next entry
	jnz	1b			# otherwise copy auxv
	/* Prepare sufficient space for the new executable name at the
	   start of the auxiliary vector.  */
1:	leaq	64(%r8), %rsi		# file name
	movq	56(%r8), %r9		# name length
	leaq	-1(%r11), %r14
	subq	%r9, %r14		# destination of file name
	andq	$-16, %r14		# align destination
	/* Prepare to copy argv, environ and auxv.  */
1:	subq	%r13, %r11		# size required
	addq	$15, %r11		# align size
	andq	$-16, %r11
	negq	%r11			# subtract
	leaq	-56(%r14,%r11,1), %r11	# %r11 = destination - struct exec_jump_command
	/* Move the file name out of the way.  */
	leaq	9(%rsi,%r9,1), %r10	# end of name + 8
	cmpq	%r10, %r11		# end of name >= struct exec_jump_command - 8
	jae	1f			# save exec command
	xorq	%r10, %r10
	subq	%r9, %r10
	leaq	-9(%r11,%r10,1), %rdi	# position of new name
	movq	%rdi, %r10
	cld
	leaq	1(%r9), %rcx		# length (including termination)
  rep	movsb				# copy file name
	movq	%r10, %rsi		# file name
	/* Preserve jump command.  */
1:	cmpq	%r8, %r11		# decide copy direction
	jb	1f			# copy forward
	movq	48(%r8), %rax
	movq	%rax, 48(%r11)		# %r11->at_base
	movq	40(%r8), %rax
	movq	%rax, 40(%r11)		# %r11->at_phdr
	movq	32(%r8), %rax
	movq	%rax, 32(%r11)		# %r11->at_phnum
	movq	24(%r8), %rax
	movq	%rax, 24(%r11)		# %r11->at_phent
	movq	16(%r8), %rax
	movq	%rax, 16(%r11)		# %r11->at_entry
	movq	8(%r8), %rax
	movq	%rax, 8(%r11)		# %r11->entry
	movq	(%r8), %rax
	movq	%rax, (%r11)		# %r11->command
	movq	%r14, -8(%r11)		# destination of file name
	jmp	copy_env_and_args
1:	movq	%r14, -8(%r11)		# destination of file name
	movq	(%r8), %rax
	movq	%rax, (%r11)		# %r11->command
	movq	8(%r8), %rax
	movq	%rax, 8(%r11)		# %r11->entry
	movq	16(%r8), %rax
	movq	%rax, 16(%r11)		# %r11->at_entry
	movq	24(%r8), %rax
	movq	%rax, 24(%r11)		# %r11->at_phent
	movq	32(%r8), %rax
	movq	%rax, 32(%r11)		# %r11->at_phnum
	movq	40(%r8), %rax
	movq	%rax, 40(%r11)		# %r11->at_phdr
	movq	48(%r8), %rax
	movq	%rax, 48(%r11)		# %r11->at_base
copy_env_and_args:
	/* Copy argv and environ to their new positions.  */
	leaq	8(%r13), %r10		# src
	leaq	64(%r11), %rdi		# dest
	movq	(%r13), %rcx		# argc
	movq	%rcx, -8(%rdi)		# copy argc
1:	movq	(%r10), %rcx
	movq	%rcx, (%rdi)
	testq	%rcx, %rcx
	leaq	8(%r10), %r10		# src++
	leaq	8(%rdi), %rdi		# dst++
	jnz	1b
1:	movq	(%r10), %rcx
	movq	%rcx, (%rdi)
	testq	%rcx, %rcx
	leaq	8(%r10), %r10		# src++
	leaq	8(%rdi), %rdi		# dst++
	jnz	1b
copy_auxv:
	movq	(%r10), %rcx		# a_type
	movq	8(%r10), %rdx		# a_un.a_val
	addq	$16, %r10		# next entry
	movq	%rcx, (%rdi)
	jrcxz	cleanup			# AT_NULL
	cmpq	$3, %rcx		# AT_PHDR
	cmoveq	40(%r11), %rdx		# %r11->at_phdr
	cmpq	$4, %rcx		# AT_PHENT
	cmoveq	24(%r11), %rdx		# %r11->at_phent
	cmpq	$5, %rcx		# AT_PHNUM
	cmoveq	32(%r11), %rdx		# %r11->at_phnum
	cmpq	$9, %rcx		# AT_ENTRY
	cmoveq	16(%r11), %rdx		# %r11->at_entry
	cmpq	$7, %rcx		# AT_BASE
	cmoveq	48(%r11), %rdx		# %r11->at_base
	cmpq	$31, %rcx		# AT_EXECFN
	jne	1f
	movq	-8(%r11), %rdx		# string
1:	movq	%rdx, 8(%rdi)		# AT_NULL value
	addq	$16, %rdi		# next entry
	jmp	copy_auxv
cleanup:
	/* Copy the filename.  */
	movq	-8(%r11), %rdi		# destination of file name
	leaq	1(%r9), %rcx		# length (including termination)
  rep	movsb
	movq	%rdx, 8(%rdi)		# AT_NULL value
	leaq	56(%r11), %r13		# restore original stack pointer
	movq	$3, %rax		# SYS_close
	cmpq	$-1, %r12		# see if interpreter fd is set
	je	cleanup_1
	movq	%r12, %rdi
	syscall
	movq	$3, %rax		# SYS_close
cleanup_1:
	movq	%rbx, %rdi
	syscall
	/* Enter the program.  */
	pushq	$0
	popfq				# clear FP state
	movq	%r13, %rsp		# restore SP
	xorq	%rdx, %rdx		# clear rtld_fini
	jmpq	*-48(%rsp)		# entry

error:
	.ascii	"_start: internal error.\n"
#timespec:
#	.quad	10
#	.quad	10

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