1/* mipsel-linux.elf-fold.S -- linkage to C code to process Elf binary 2* 3* This file is part of the UPX executable compressor. 4* 5* Copyright (C) 2000-2020 John F. Reiser 6* All Rights Reserved. 7* 8* UPX and the UCL library are free software; you can redistribute them 9* and/or modify them under the terms of the GNU General Public License as 10* published by the Free Software Foundation; either version 2 of 11* the License, or (at your option) any later version. 12* 13* This program is distributed in the hope that it will be useful, 14* but WITHOUT ANY WARRANTY; without even the implied warranty of 15* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16* GNU General Public License for more details. 17* 18* You should have received a copy of the GNU General Public License 19* along with this program; see the file COPYING. 20* If not, write to the Free Software Foundation, Inc., 21* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 22* 23* Markus F.X.J. Oberhumer Laszlo Molnar 24* <markus@oberhumer.com> <ezerotven+github@gmail.com> 25* 26* John F. Reiser 27* <jreiser@users.sourceforge.net> 28*/ 29 30NBPW= 4 31#include "arch/mips/r3000/macros.ash" 32#include "arch/mips/r3000/bits.ash" 33 34 .set mips1 35 .set noreorder 36 .set noat 37 .altmacro 38 39PAGE_SHIFT= 12 40PAGE_MASK= 0xffffffffffffffff<<PAGE_SHIFT 41 42sz_Ehdr= 52 43sz_Phdr= 32 44 45sz_b_info= 12 46 sz_unc= 0 47 sz_cpr= 4 48 49sz_l_info= 12 50sz_p_info= 12 51 52sz_auxv= 8 53a_type = 0 # Elf32_auxv_t 54a_val = 4 55 56__NR_Linux = 4000 57__NR_brk = 45+ __NR_Linux 58__NR_close = 6+ __NR_Linux 59__NR_exit = 1+ __NR_Linux 60__NR_mmap = 90+ __NR_Linux 61__NR_mprotect = 125+ __NR_Linux 62__NR_munmap = 91+ __NR_Linux 63__NR_open = 5+ __NR_Linux 64__NR_read = 3+ __NR_Linux 65__NR_readlink = 85+ __NR_Linux 66__NR_write = 4+ __NR_Linux 67 68PATHSIZE=4096 69OVERHEAD=2048 70MAX_ELF_HDR=512 71 72MAP_PRIVATE= 0x002 73MAP_ANONYMOUS=0x800 # not same as i386 74PROT_READ= 1 75 76get_page_mask: 77 li v0,0 // modified to PAGE_MASK >> 9 78 jr ra 79 sll v0,v0,9 80 nop 81 82sp_frame= 0x20 83F_PMASK= 4*NBPW 84F_fd= 5*NBPW 85F_ADRU= 6*NBPW 86F_LENU= 7*NBPW 87 // The above 4 registers are passed on stack to unfolded code. 88a4_sys= 4*NBPW 89a5_sys= 5*NBPW 90 91// C-language offers 8 register args; syscall offers only 4 92#define a4 t0 93#define a5 t1 94 95//ra 31 96#define r_fexp 30 /* s8 */ 97//sp 29 /* hardware */ 98#define r_PMASK 28 /* gp */ 99//k1 27 /* trashed by syscall */ 100//k0 26 /* trashed by syscall */ 101//t9, jp 25 /* trashed by syscall ? */ 102//t8 24 /* trashed by syscall ? */ 103//s7 AVAIL 23 /* s7 */ 104#define r_auxv 22 /* s6 */ 105#define r_elfa 21 /* s5 */ 106#define r_auxe 20 /* s4 */ 107#define r_LENU 19 /* s3 */ 108#define r_ADRU 18 /* s2 */ 109#define r_LENX 17 /* s1 */ 110#define r_ADRX 16 /* s0 */ 111 112/* In: 113 r_ADRX,r_LENX,r_elfa,r_auxv,r_PMASK,r_fexp 114 sp= -sp_frame{%,%,%,%,PMASK,fd,ADRU,LENU}, {argc,argv...,0,env...,0,auxv...,0,0,strings} 115*/ 116fold_begin: 117//// break 118 lw $r_ADRU,F_ADRU(sp) 119 lw $r_LENU,F_LENU(sp) 120 move v0,sp 121 addiu sp,(~0<<4)&-(NBPW+ 4+ PATHSIZE - sp_frame) # alloca: new envp[0], " =", buffer 122 123 move v1,sp 124L10: # copy until auxv 125 lw tmp,0(v0); addiu v0,NBPW 126 sw tmp,0(v1); addiu v1,NBPW 127 bne v0,$r_auxv,L10 128 addiu t1,v1,-NBPW // new envp goes here 129 sw zero,(v1); addiu v1,NBPW // new terminator for envp 130 move $r_auxv,v1 // new auxv 131L30: // copy auxv 132 lw tmp,0(v0); lw t0,NBPW(v0); addiu v0,sz_auxv 133 sw tmp,0(v1); sw t0,NBPW(v1); addiu v1,sz_auxv 134 bnez tmp,L30 # AT_NULL: stop when v0= &auxv[N] 135 move $r_auxe,v1 // end of new auxv 136 137 sw v1,0(t1) # new env var 138 li tmp,' ' 139 sb tmp,0(v1) # endian neutral! 140 sb tmp,1(v1) 141 sb tmp,2(v1) 142 li tmp,'=' 143 sb tmp,3(v1) 144 145 li a2,PATHSIZE-1 146 addiu a1,v1,4 # &buf[0] 147 bal 9f 148 move a0,ra # &path 149 .asciz "/proc/self/exe" 150 .balign 4 1519: 152 li v0,__NR_readlink 153 syscall 154 bltz a3,0f 155 addu tmp,a1,v0 156 sb $0,(tmp) # null terminate the path 1570: 158 addiu sp,-MAX_ELF_HDR # alloca 159 move t3,$r_PMASK # page_mask 160 move t2,$r_elfa # &Elf32_Ehdr of stub 161 move t1,zero # &f_unfilter 162 move t0,$r_fexp # &f_decompress 163 move a3,$r_auxv # new &auxv[0] 164 move a2,sp # &Elf32_Ehdr tmp space 165 move a1,$r_LENX # total_size 166 167BAL=0x04110000 168/* We need a position-independent call of upx_main, which is external. 169 "bal upx_main" cannot be assembled by mipsel-elf-as-20060406. 170 ".long BAL + upx_main" then changing R_MIPS_32 to R_MIPS_PC16 171 in a utility program, is botched when loaded by multiarch-ld-2.17 172 (relocates as if R_MIPS_32, changing the opcode and not 173 subtracting the current location). 174 So do it the hard way. 175*/ 176 bltzal $0,9f # ra= &9f; no branch (condition is false!) 177 li v0,%lo(9f) 1789: 179 subu v0,ra,v0 180 addiu v0,v0,%lo(upx_main) 181 jalr v0 182 move a0,$r_ADRX 183/* entry= upx_main(b_info *a0, total_size a1, Elf32_Ehdr *a2, Elf32_Auxv_t *a3, 184 f_decompr t0, f_unfilter t1, Elf32_Ehdr &t2, page_mask t3 ) 185*/ 186 addiu sp,MAX_ELF_HDR # un-alloca 187 move $r_fexp,v0 # &entry 188 189// Map 1 page of /proc/self/exe so that munmap does not remove all references 190 lw a4,F_fd(sp) 191 move a5,$0 // offset 192 sw a4,a4_sys(sp) 193 sw a5,a5_sys(sp) 194 li a3,MAP_PRIVATE 195 li a2,PROT_READ 196 neg a1,$r_PMASK // PAGE_SIZE 197 move a0,$0 // addr 198 li v0,__NR_mmap; syscall 199 200 lw a0,a4_sys(sp) // fd 201 li v0,__NR_close; syscall 202 addiu sp,sp,sp_frame 203 204/* Workaround suspected glibc bug: elf/rtld.c assumes uninit local is zero. 205 2007-11-24 openembedded.org mipsel-linux 2.6.12.6/glibc 2.3.2 206*/ 207 move tmp,sp 208 addiu sp, -300 # estimated stack bound of upx_main and below 2090: 210 addiu sp,NBPW 211 bne sp,tmp,0b 212 sw $0,-NBPW(sp) 213 214 lw tmp,-sz_auxv+ a_val($r_auxe) // last .a_val 215 move a1,$r_LENU 216 beqz tmp,L40 # could not make escape hatch 217 move a0,$r_ADRU 218 jr tmp # goto munmap escape hatch: [syscall; jr $r_fexp; nop] 219 li v0,__NR_munmap 220L40: 221 jr $r_fexp # omit munmap 222 nop 223 224#if 0 /*{ replaced by macros in include/linux.h because of 'bal' vs gcc */ 225err_syscall: 226 li a0,-1 227exit: .globl exit 228 li v0,__NR_exit 229sysgo: 230 syscall 231sysret: 232 sltiu tmp,v0,PAGE_MASK 233 addiu tmp,tmp,-1 234 j ra 235 or v0,v0,tmp 236read: .globl read 237 b sysgo; li v0,__NR_read 238write: .globl write 239 b sysgo; li v0,__NR_write 240open: .globl open 241 b sysgo; li v0,__NR_open 242close: .globl close 243 b sysgo; li v0,__NR_close 244brk: .globl brk 245 b sysgo; li v0,__NR_brk 246munmap: .globl munmap 247 b sysgo; li v0,__NR_munmap 248mprotect: .globl mprotect 249 b sysgo; li v0,__NR_mprotect 250 251mmap_privanon: .globl mmap_privanon 252 ori a3,a3,MAP_PRIVATE|MAP_ANONYMOUS 253 li t0,-1 # fd 254 li t1,0 # offset 255mmap: .globl mmap 256 addiu sp,sp,-sp_frame 257 sw a4,a4_sys(sp) 258 sw a5,a5_sys(sp) 259 li v0,__NR_mmap 260 syscall 261 b sysret 262 addiu sp,sp,sp_frame 263#endif /*}*/ 264 265/* vim:set ts=8 sw=8 et: */ 266