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