From d8d945777d591ffcc5805c46d67b9635ecde0566 Mon Sep 17 00:00:00 2001 From: Po Lu Date: Tue, 15 Apr 2025 16:14:14 +0800 Subject: [PATCH] Port recent Android changes to mips64el * exec/config-mips.m4.in (DADDI2, DADDI3): Disable at-clobbering by assembler macros expressly. * exec/loader-mips64el.s: Adapt from loader-mipsel.s. * exec/configure.ac (exec_cv_as_daddi): Properly escape reg names. * exec/exec.c (struct exec_jump_command, exec_0): Don't define or set `fpu_mode' if __LP64__. * exec/exec.h (struct exec_tracee): New field `callno'. * exec/trace.c (process_system_call): Always record the current system call number in TRACEE lest it should be required once it has been overwritten upon the syscall's completion. (seccomp_system_call): Likewise. (after_fork): Clear `tracee->callno'. (cherry picked from commit 5bf86e2be0693c579a43759fd1da1651344d401e) --- exec/config-mips.m4.in | 12 +- exec/configure.ac | 12 +- exec/exec.c | 9 +- exec/exec.h | 4 + exec/loader-mips64el.s | 317 ++++++++++++++++++++++++++++------------- exec/trace.c | 14 +- 6 files changed, 253 insertions(+), 115 deletions(-) diff --git a/exec/config-mips.m4.in b/exec/config-mips.m4.in index 1c9a4c293a6..4ab395ff2cb 100644 --- a/exec/config-mips.m4.in +++ b/exec/config-mips.m4.in @@ -38,7 +38,11 @@ define(`RESTORE', `ifelse(`@MIPS_N32@',`yes',` nop',` addi $sp, 32')') define(`FP', `ifelse(`@MIPS_N32@',`yes',`$s8',`$fp')') dnl For mips64. Some assemblers don't want to assemble `daddi'. -define(`DADDI2', `ifelse(`@DADDI_BROKEN@',`yes',` li $at, $2 -dadd $1, $1, $at',` daddi $1, $2')') -define(`DADDI3', `ifelse(`@DADDI_BROKEN@',`yes',` li $at, $3 -dadd $1, $2, $at',` daddi $1, $2, $3')') +define(`DADDI2', `ifelse(`@DADDI_BROKEN@',`yes',`.set noat +li $at, $2 +dadd $1, $1, $at +.set at',` daddi $1, $2')') +define(`DADDI3', `ifelse(`@DADDI_BROKEN@',`yes',`.set noat +li $at, $3 +dadd $1, $2, $at +.set at',` daddi $1, $2, $3')') diff --git a/exec/configure.ac b/exec/configure.ac index d91049b8d7b..d462a25b9d9 100644 --- a/exec/configure.ac +++ b/exec/configure.ac @@ -455,12 +455,12 @@ AS_CASE([$host], [x86_64-*linux*], .section text .global __start __start: - li $t0, 0 - li $t1, 0 - daddi $t0, $t1, 1 - daddi $t0, $t1, -1 - daddi $t0, -1 - daddi $t0, 1 + li \$t0, 0 + li \$t1, 0 + daddi \$t0, \$t1, 1 + daddi \$t0, \$t1, -1 + daddi \$t0, -1 + daddi \$t0, 1 _ACEOF $AS $ASFLAGS conftest.s -o conftest.$OBJEXT \ diff --git a/exec/exec.c b/exec/exec.c index b9c3e4ec92a..b83e34bc1b2 100644 --- a/exec/exec.c +++ b/exec/exec.c @@ -231,10 +231,10 @@ struct exec_jump_command /* The value of AT_BASE inside the aux vector. */ USER_WORD at_base; -#if defined __mips__ +#if defined __mips__ && !defined __LP64__ /* The FPU mode to apply. Not used when !MIPS_NABI. */ USER_WORD fpu_mode; -#endif /* defined __mips__ */ +#endif /* defined __mips__ && !defined __LP64__ */ }; @@ -918,6 +918,7 @@ exec_0 (char *name, struct exec_tracee *tracee, USER_WORD header_offset; USER_WORD name_len, aligned_len; struct exec_jump_command jump; + /* This also encompasses !__LP64__. */ #if defined __mips__ && !defined MIPS_NABI int fpu_mode; #endif /* defined __mips__ && !defined MIPS_NABI */ @@ -1130,9 +1131,9 @@ exec_0 (char *name, struct exec_tracee *tracee, fpu_mode = FP_FRE; jump.fpu_mode = fpu_mode; -#elif defined __mips__ +#elif defined __mips__ && !defined __LP64__ jump.fpu_mode = 0; -#endif /* defined __mips__ && !defined MIPS_NABI */ +#endif /* defined __mips__ && defined MIPS_NABI && !defined __LP64__ */ /* The offset used for at_phdr should be that of the first mapping. */ diff --git a/exec/exec.h b/exec/exec.h index eee48dfe2ed..d420061ff87 100644 --- a/exec/exec.h +++ b/exec/exec.h @@ -152,6 +152,10 @@ struct exec_tracee completion. */ USER_WORD sp; + /* ID of the system call that is pending completion. This value is + not available as the call number is overwritten on success. */ + USER_WORD callno; + /* Name of the executable being run. */ char *exec_file; diff --git a/exec/loader-mips64el.s b/exec/loader-mips64el.s index 1493af3c309..be978d8b18b 100644 --- a/exec/loader-mips64el.s +++ b/exec/loader-mips64el.s @@ -15,36 +15,40 @@ # You should have received a copy of the GNU General Public License # along with GNU Emacs. If not, see . - /* NOTE: this file is presently non-functional. */ - 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 - .set noat # no assembler macros .section .text .global __start __start: -dnl li $v0, 5034 # SYS_nanosleep -dnl dla $a0, .timespec # rqtp -dnl li $a1, 0 # rmtp -dnl syscall # syscall - ld $s2, ($sp) # original stack pointer + ## 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: +next_action: ld $s1, ($s0) # action number andi $t0, $s1, 15 # t0 = action number & 15 - beqz $t0, .open_file # open file? + beqz $t0, open_file # open file? nop # delay slot DADDI2( $t0, -3) # t0 -= 3 - beqz $t0, .rest_of_exec # jump to code + beqz $t0, rest_of_exec # jump to code nop # delay slot li $t1, 1 - beq $t0, $t1, .do_mmap_anon # anonymous mmap? + beq $t0, $t1, do_mmap_anon # anonymous mmap? nop # delay slot -.do_mmap: +do_mmap: ld $t0, 8($s0) # vm address ld $t1, 16($s0) # file_offset ld $t2, 24($s0) # protection @@ -52,10 +56,10 @@ dnl syscall # syscall ld $v0, 40($s0) # flags ld $v1, ($sp) # primary fd andi $s3, $s1, 16 # s1 & 16? - beqz $s3, .do_mmap_1 # secondary fd? + beqz $s3, do_mmap_1 # secondary fd? nop # delay slot ld $v1, 8($sp) # secondary fd -.do_mmap_1: +do_mmap_1: move $a0, $t0 # syscall arg move $a1, $t3 # syscall arg move $a2, $t2 # syscall arg @@ -64,21 +68,21 @@ dnl syscall # syscall move $a5, $t1 # syscall arg li $v0, 5009 # SYS_mmap syscall # syscall - bne $a3, $zero, .perror # perror? + 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 +align: + beq $t0, $t1, continue # already finished nop # delay slot andi $t2, $t1, 7 # t1 & 7? - bnez $t2, .filld # start filling longs + bnez $t2, filld # start filling longs nop # delay slot -.filld: +filld: dsub $t2, $t0, $t1 # t2 = t0 - t1 sltiu $t2, $t2, 64 # t2 < 64? - bne $t2, $zero, .fillb # fill bytes + bne $t2, $zero, fillb # fill bytes nop # delay slot sd $zero, ($t1) # zero doubleword DADDI2( $t1, 8) # next doubleword @@ -96,140 +100,255 @@ dnl syscall # syscall DADDI2( $t1, 8) # next doubleword sd $zero, ($t1) # zero doubleword DADDI2( $t1, 8) # next doubleword - j .filld # fill either doubleword or byte + j filld # fill either doubleword or byte nop # delay slot -.fillb: - beq $t0, $t1, .continue # already finished? +fillb: + beq $t0, $t1, continue # already finished? nop # delay slot sb $zero, ($t1) # clear byte DADDI2( $t1, 1) # t1++ -.continue: +continue: DADDI2( $s0, 56) # s0 = next action - j .next_action # next action + j next_action # next action nop # delay slot -.do_mmap_anon: +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 - li $v1, -1 # fd - j .do_mmap_1 # do mmap + dli $v1, -1 # fd + j do_mmap_1 # do mmap nop # branch delay slot -.open_file: - li $v0, 5002 # SYS_open +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 + bne $a3, $zero, perror # perror nop # delay slot DADDI2( $s0, 8) # start of string move $t3, $s0 # t3 = s0 -.nextc: +nextc: lb $t0, ($s0) # load byte DADDI2( $s0, 1) # s0++ - li $t1, 47 # directory separator `/' - bne $t0, $t1, .nextc1 # is separator char? + 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? +nextc1: + bnez $t0, nextc # next character? nop # delay slot DADDI2( $s0, 7) # adjust for round - li $t2, -8 # t2 = -8 + 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? + 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 + j next_action # next action nop # delay slot -.primary: +primary: sd $v0, ($t1) # store fd - li $v0, 5153 # SYS_prctl - li $a0, 15 # PR_SET_NAME + 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 + j next_action # next action nop # delay slot -.perror: +perror: move $a0, $v0 # errno - li $v0, 5058 # SYS_exit + dli $v0, 5058 # SYS_exit syscall # syscall -.rest_of_exec: - move $s1, $s2 # original SP +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 -.skipenv: - ld $t0, ($s1) # t0 = *s1 - DADDI2( $s1, 8) # s1++ - bne $t0, $zero, .skipenv # skip again - nop # delay slot - dla $t3, .auxvtab # address of auxv table -.one_auxv: - ld $t0, ($s1) # t0 = auxv type - li $t1, 10 # t1 = 10 - beqz $t0, .finish # is AT_IGNORE? - nop # delay slot - sltu $t1, $t0, $t1 # t1 = t0 < num offsets - beqz $t1, .next # next auxv - nop # delay slot - dsll $t1, $t0, 2 # t1 = t0 * 4 - dadd $t1, $t3, $t1 # t1 = .auxvtab + t1 - lw $t2, ($t1) # t2 = *t1 - beqz $t2, .next # skip auxv - nop # delay slot - dadd $t2, $s0, $t2 # t2 = s0 + t2 - ld $t2, ($t2) # t2 = *t2 - sd $t2, 8($s1) # set auxv value -.next: - DADDI2( $s1, 16) # next auxv - j .one_auxv # next auxv - nop # delay slot -.finish: - ld $t0, 8($sp) # secondary fd +skip_environ: + /* Locate the auxiliary vector. */ +1: ld $t0, ($s1) # t0 = *s1 + bnez $t0, 1b # skip environment entry + daddi $s1, $s1, 8 # s1++ + move $s2, $s1 # s2 = end of environment +1: ld $t0, ($s1) # t0 = s1->a_type + bnez $t0, 1b # skip auxiliary vector entry + daddi $s1, $s1, 16 # (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 + blt T5, 16, 1f # more than two slots apart + dadd $sp, $t3, -16 # $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, ($sp) # s1 = primary fd + ld $s1, ($fp) # s1 = primary fd + beq $t0, $t2, finish1 # secondary fd set? li $v0, 5003 # SYS_close - beq $t0, $t2, .finish1 # secondary fd set? - nop # delay slot move $a0, $t0 # secondary fd syscall # syscall li $v0, 5003 # SYS_close -.finish1: +finish1: move $a0, $s1 # primary fd syscall # syscall -.jump: +jump: move $v0, $zero # rtld_fini - ld $t0, 8($s0) # entry - move $sp, $s2 # restore stack pointer, delay slot - jr $t0 # enter - nop # delay slot + 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. -.auxvtab: - .long 0 # 0 - .long 0 # 1 - .long 0 # 2 - .long 40 # 3 AT_PHDR - .long 24 # 4 AT_PHENT - .long 32 # 5 AT_PHNUM - .long 0 # 6 - .long 48 # 7 AT_BASE - .long 0 # 8 - .long 16 # 9 AT_ENTRY + 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 +## timespec: +## .quad 10 +## .quad 10 # Local Variables: # asm-comment-char: ?# diff --git a/exec/trace.c b/exec/trace.c index 753c78994f2..61e8d5542f9 100644 --- a/exec/trace.c +++ b/exec/trace.c @@ -1237,7 +1237,11 @@ process_system_call (struct exec_tracee *tracee) set, this must be exec, whatever the value of SYSCALL_NUM_REG, which is erased when exec loads another image. */ - callno = (!tracee->exec_data ? regs.SYSCALL_NUM_REG : EXEC_SYSCALL); + callno = (!tracee->exec_data + ? (!tracee->waiting_for_syscall + ? regs.SYSCALL_NUM_REG : tracee->callno) + : EXEC_SYSCALL); + tracee->callno = callno; switch (callno) { case EXEC_SYSCALL: @@ -1644,6 +1648,11 @@ seccomp_system_call (struct exec_tracee *tracee) /* Now dispatch based on the system call. */ callno = regs.SYSCALL_NUM_REG; + + /* Record the call number, which may be required if one of the + following handlers should arrange for process_system_call to + intercede after the system call completes. */ + tracee->callno = callno; switch (callno) { case EXEC_SYSCALL: @@ -1706,7 +1715,7 @@ seccomp_system_call (struct exec_tracee *tracee) if (rc < 0) return; - tracee->waiting_for_syscall = !tracee->waiting_for_syscall; + tracee->waiting_for_syscall = true; break; default: @@ -2024,6 +2033,7 @@ after_fork (pid_t pid) return 1; tracee->pid = pid; + tracee->callno = 0; tracee->next = tracing_processes; tracee->waiting_for_syscall = false; tracee->new_child = false; -- 2.39.5