emacs/exec/loader-mips64el.s

358 lines
10 KiB
ArmAsm

# 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: