1@  arm-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
30#define ARM_OLDABI 1
31#include "arch/arm/v4a/macros.S"
32#define bkpt .long 0xe7f001f0  /* reserved instr; Linux GNU eabi breakpoint */
33
34sz_Elf32_Ehdr = 13*4
35sz_Elf32_Phdr =  8*4
36sz_l_info = 12
37sz_p_info = 12
38sz_b_info = 12
39  sz_unc= 0
40  sz_cpr= 4
41  b_method= 8
42
43MAP_ANONYMOUS= 0x20
44MAP_PRIVATE=   0x02
45MAP_FIXED=     0x10
46
47PROT_READ=     0x1
48
49O_RDONLY=       0
50
51PAGE_SHIFT= 12
52PAGE_SIZE = -(~0<<PAGE_SHIFT)
53PATHMAX=  4096
54
55PATH_MAX= 4096
56
57#ifndef DEBUG  /*{*/
58#define DEBUG 0
59#define TRACE_REGS r0-r12,r14,r15
60#endif  /*}*/
61
62#define OVERHEAD 2048
63#define MAX_ELF_HDR 512
64
65mflg_data: .int MAP_PRIVATE|MAP_ANONYMOUS  @ overwritten for QNX vs Linux
66
67SP_fd= 3*4
68/* In:
69   r4= LENX
70   r5= ADRX
71   lr= "/proc/self/exe"
72new sp/            elfaddr,fd, ADRU,LENU, f_exp,%entry, argc,argv,0,envp,0,auxv
73        (ADRX,LENX) = extent of compressed program (after moving)
74        (ADRU,LENU) = params for final munmap()
75*/
76fold_begin:  // enter here
77#if DEBUG  //{
78#define TRACE_REGS r0-r12,r14,r15
79        mov r0,sp  @ current stack pointer (extra clue)
80        stmdb sp!,{TRACE_REGS}; mov r0,#0x10; bl trace
81#endif  //}
82        ldmia sp!,{      r6,r7,r8,r9,r10,r11,r12}  @ r12= argc
83        mov r1,sp  @ src (argv)
84        sub sp,sp,#PATH_MAX
85        mov r0,sp  @ dst
86        stmdb sp!,{r4,r5,r6,r7,r8,r9,r10,r11,r12}  @ r12= argc
87F_LENX=    0*4
88F_ADRX=    1*4
89F_elfaddr= 2*4
90F_fd=      3*4
91F_ADRU=    4*4
92F_LENU=    5*4
93F_f_exp=   6*4
94  F_e_auxv=  F_f_exp
95F_entry=   7*4
96
970: // copy argv down
98        ldr r3,[r1],#4; cmp r3,#0
99        str r3,[r0],#4; bne 0b
100     mov r4,r0  @ &new_env[0]
101        str r3,[r0],#4  @ space for new_env[0]
102
1030: // copy env down
104        ldr r3,[r1],#4; cmp r3,#0
105        str r3,[r0],#4; bne 0b
106     mov r5,r1  @ &orig_auxv[0]
107
1080: // copy auxv down
109        ldmia r1!,{r2,r3}; cmp r2,#0
110        stmia r0!,{r2,r3}; bne 0b
111     mov r6,r1  @ &orig_auxv[end]; also &old_strings
112     mov r9,r0  @ &down_auxv[end]
113
114        mov r2,#PATH_MAX
115        mov r1,r0  @ buffer
116        mov r0,lr  @ arg1= "/proc/self/exe"
117        sub r2,r2,#1  @ room for null terminator
118        bl readlink
119        cmn r0,#4096
120        movcs r1,lr  @ "/proc/self/exe"
121        movcs r0,#14  @ strlen("/proc/self/exe")
122link_ok:
123        add r2,r1,r0  @ end
124        mov r0,r6  @ &old_strings
125        mov  r3,#0;        strb r3,[r0,#-1]!  @ terminate link name
1260: // copy backwards to beginning
127        ldrb r3,[r2,#-1]!; strb r3,[r0,#-1]!
128        cmp r1,r2; bne 0b
129        mov  r3,#'=';      strb r3,[r0,#-1]!
130        mov  r3,#' ';      strb r3,[r0,#-1]!
131                           strb r3,[r0,#-1]!
132                           strb r3,[r0,#-1]!
133        str r0,[r4]  @ new_env[0]
134
135// preserve 8-byte alignment of stack pointer
136        mov r0,r0,lsr #2
137        mov r0,r0,lsl #2  @ &new_strings
138        eor r3,r6,r0  @ word parity with &old_strings
139        and r3,r3,#4  @ 0 or 4
140        eor r3,r3,#4  @ we add 1 new_env[0]
141        sub r0,r0,r3  @ align mod 8
142
143// copy up auxv,env,argv
144        sub r3,r0,r6  @ &new_auxv[end] - &orig_auxv[end]
145        add r3,r3,r5  @ &new_auxv[0] = delta +  &orig_auxv[0]
146        mov r1,r9  @ &down_auxv[end]
147        ldr r5,[sp,#F_f_exp]  @ save f_exp
148        str r0,[sp,#F_e_auxv]  @ replace f_exp with &new_auxv[end]
1490:
150        ldr r2,[r1,#-4]!; cmp r1,sp
151        str r2,[r0,#-4]!; bne 0b
152        mov sp,r0
153
154#if DEBUG  //{
155        stmdb sp!,{TRACE_REGS}; mov r0,#0x12; bl trace
156#endif  //}
157
158/* Construct arglist for upx_main */
159        @ldr r5,[sp,#F_f_exp]
160        ldmia sp!,{r4,r10,r11}  @ LENX, ADRX, elfaddr
161F_delta= 3*4
162        sub sp,sp,#MAX_ELF_HDR + OVERHEAD  @ alloca
163        ldr r9,[r10,#sz_cpr]  @ xi.size  of ELF headers
164        mov r8,sp  @ xo.ptr
165        ldr r7,[r10,#sz_unc]  @ xo.size
166        adr r6,f_unfilter
167        //mov r3,r3  @ auxv
168        add r9,r9,#sz_b_info  @ for unpackExtent
169
170        stmdb sp!,{r3,r4,r5,r6,r7,r8,r9,r10,r11}
171        ldmia sp!,{r0,r1,r2,r3}
172#if DEBUG  //{
173        stmdb sp!,{TRACE_REGS}; mov r0,#0x13; bl trace
174#endif  //}
175// r0=av; r1=sz_cpr; r2=f_decompress; r3=f_unfilter;
176// xo={sz_unc, &tmp_ehdr}, xi={sz_cpr, &b_info}, elfaddr
177        bl upx_main
178        add sp,sp,#(9-4)*4
179        add sp,sp,#MAX_ELF_HDR + OVERHEAD  @ un-alloca
180#if DEBUG  //{
181        stmdb sp!,{TRACE_REGS}; mov r0,#0x14; bl trace
182#endif  //}
183        str r0,[sp,#F_entry - F_delta]  @ entry address
184
185// Map 1 page of /proc/self/exe so that it does not disappear
186        ldr r4,[sp],#4  @ pop r4,F_fd
187        mov r5,#0  @ SEEK_SET offset
188        stmdb sp!,{r4,r5}  @ arg5,arg6 calling convention
189        mov r3,#MAP_PRIVATE
190        mov r2,#PROT_READ
191        mov r1,#PAGE_SIZE
192        mov r0,#0  @ any address
193        bl mmap  @ no error check: cannot recover
194        ldmia sp!,{r0,r1}  @ fd, offset
195        bl close
196
197#if DEBUG  //{
198        stmdb sp!,{TRACE_REGS}; mov r0,#0x15; bl trace
199#endif  //}
200        ldmia sp!,{r0,r1,r2, lr}  @ ADRU,LENU,1+ &Elf32_auxv_t[AT_NULL@.a_type], entry
201        // crumb is unused: replaced by mapping /proc/self/exe into a free page
202
203#if DEBUG  /*{*/
204        ldr r3,[r2,#4 -2*4]  @ Elf32_auxv_t[AT_NULL@.a_type].a_val
205        ldr r4,[r3,#0]  @ 1st instr
206        ldr r5,[r3,#4]  @ 2nd instr
207        stmdb sp!,{TRACE_REGS}; mov r0,#0x15; bl trace
208#endif  /*}*/
209        mov r3,#0  @ clear registers: paranoia
210        mov r4,#0
211        mov r5,#0
212        mov r6,#0
213
214        mov r8,#0
215        mov r9,#0
216        mov r10,#0
217        mov r11,#0
218
219#if 1|DEBUG  //{
220/* Heuristic cache flush: sweep contiguous range to force collisions and evictions. */
221        sub r12,sp,#(1<<18)  @ limit: 1/4 MB more
222sweep:
223        ldr r7,[sp],#-(1<<5)  @ extend stack; read allocate 32 bytes
224        str r7,[sp]  @ make it dirty
225        ldr r7,[sp]  @ read alocate again in case dirtying caused COW split
226        cmp r12,sp; blo sweep
227
228        add sp,sp,#(1<<18)  @ pop stack
229#endif  //}
230
231#if defined(ARMEL_DARWIN)  /*{*/
232        mov r7,#0
233        mov r12,#0xff & __NR_munmap
234#elif defined(ARMEL_EABI4)  /*}{*/
235        mov r12,#0
236        mov r7, #0xff & __NR_munmap
237#elif defined(ARM_OLDABI)  /*{*/
238        mov r7,#0
239        mov r12,#0
240#endif  /*}*/
241        ldr pc,[r2,#4 -2*4]  @ Elf32_auxv_t[AT_NULL@.a_type].a_val
242
243proc_self_exe:
244        .ascii "/proc/self/exe"  @ no terminator
245proc_self_align:
246        .asciz ""  @ terminator
247        .balign 4
248
249f_unfilter:  @ (char *ptr, uint len, uint cto, uint fid)
250        ptr  .req r0
251        len  .req r1
252        cto  .req r2  @ unused
253        fid  .req r3
254
255        t1   .req r2
256        t2   .req r3
257
258#ifndef FILTER_ID  /*{*/
259#define FILTER_ID 0x50  /* little-endian */
260#endif  /*}*/
261        and fid,fid,#0xff
262        cmp fid,#FILTER_ID  @ last use of fid
263        movne pc,lr  @ no-op if not filter 0x50
264
265        movs  len,len,lsr #2  @ word count
266        cmpne ptr,#0
267        moveq pc,lr  @ no-op if either len or ptr is 0
268
269top_unf:
270        sub len,len,#1
271        ldr t1,[ptr,len,lsl #2]
272        and t2,t1,#0x0f<<24
273        cmp t2,   #0x0b<<24; bne tst_unf  @ not 'bl' subroutine call
274        and t2,t1,#0xff<<24  @ all the non-displacement bits
275        sub t1,t1,len  @ convert to word-relative displacement
276        bic t1,t1,#0xff<<24  @ restrict to displacement field
277        orr t1,t1,t2  @ re-combine
278        str t1,[ptr,len,lsl #2]
279tst_unf:
280        cmp len,#0
281        bne top_unf
282        ret
283
284#if DEBUG  /*{*/
285TRACE_BUFLEN=512
286trace:
287        str lr,[sp,#(-1+ 15)*4]  @ return pc; [remember: sp is not stored]
288        mov r4,sp  @ &saved_r0
289        sub sp,sp,#TRACE_BUFLEN
290        mov r2,sp  @ output string
291
292        mov r1,#'\n'; bl trace_hex  @ In: r0 as label
293        mov r1,#'>';  strb r1,[r2],#1
294
295        mov r5,#3  @ rows to print
296L600:  @ each row
297        sub r0,r4,#TRACE_BUFLEN
298        sub r0,r0,sp
299        mov r0,r0,lsr #2; mov r1,#'\n'; bl trace_hex  @ which block of 8
300
301        mov r6,#8  @ words per row
302L610:  @ each word
303        ldr r0,[r4],#4; mov r1,#' '; bl trace_hex  @ next word
304        subs r6,r6,#1; bgt L610
305
306        subs r5,r5,#1; bgt L600
307
308        mov r0,#'\n'; strb r0,[r2],#1
309        sub r2,r2,sp  @ count
310        mov r1,sp  @ buf
311        mov r0,#2  @ FD_STDERR
312#if defined(ARMEL_EABI4)  /*{*/
313        mov r7,#__NR_write
314        swi 0
315#else  /*}{*/
316        swi __NR_write
317#endif  /*}*/
318        add sp,sp,#TRACE_BUFLEN
319        ldmia sp!,{TRACE_REGS}
320
321trace_hex:  // In: r0=val, r1=punctuation before, r2=ptr; Uses: r3, ip
322        strb r1,[r2],#1  @ punctuation
323        mov r3,#4*(8 -1)  @ shift count
324        adr ip,hex
325L620:
326        mov r1,r0,lsr r3
327        and r1,r1,#0xf
328        ldrb r1,[ip, r1]
329        strb r1,[r2],#1
330        subs r3,r3,#4; bge L620
331        ret
332hex:
333        .ascii "0123456789abcdef"
334#endif  /*}*/
335        .unreq ptr
336        .unreq len
337        .unreq cto
338        .unreq fid
339
340__NR_exit  =  1 + __NR_SYSCALL_BASE
341__NR_read  =  3 + __NR_SYSCALL_BASE
342__NR_write =  4 + __NR_SYSCALL_BASE
343__NR_open  =  5 + __NR_SYSCALL_BASE
344__NR_close =  6 + __NR_SYSCALL_BASE
345__NR_unlink= 10 + __NR_SYSCALL_BASE
346__NR_getpid= 20 + __NR_SYSCALL_BASE
347__NR_brk   = 45 + __NR_SYSCALL_BASE
348__NR_readlink=85+ __NR_SYSCALL_BASE
349
350
351__NR_mmap2    = 192 + __NR_SYSCALL_BASE
352__NR_mprotect = 125 + __NR_SYSCALL_BASE
353__NR_munmap   =  91 + __NR_SYSCALL_BASE
354
355__ARM_NR_BASE       = 0x0f0000 + __NR_SYSCALL_BASE
356__ARM_NR_cacheflush = 2 + __ARM_NR_BASE
357
358        .globl my_bkpt
359my_bkpt:
360        bkpt
361        ret
362
363        .globl exit
364exit:
365        do_sys __NR_exit
366
367        .globl read
368read:
369        do_sys __NR_read; ret
370
371        .globl write
372write:
373        do_sys __NR_write; ret
374
375        .globl open
376open:
377        do_sys __NR_open; ret
378
379        .globl close
380close:
381        do_sys __NR_close; ret
382
383        .globl unlink
384unlink:
385        do_sys __NR_unlink; ret
386
387        .globl getpid
388getpid:
389        do_sys __NR_getpid; ret
390
391        .globl brk
392brk:
393        do_sys __NR_brk; ret
394
395        .globl readlink
396readlink:
397        do_sys __NR_readlink; ret
398
399        .globl munmap
400munmap:
401        do_sys __NR_munmap; ret
402
403        .globl mprotect
404mprotect:
405        do_sys __NR_mprotect; ret
406
407        .globl __clear_cache
408__clear_cache:
409        mov r2,#0
410        do_sys2 __ARM_NR_cacheflush; ret
411
412        .globl mmap
413mmap:
414        stmdb sp!,{r4,r5,lr}
415        ldr r5,[sp,#4*4]
416        ldr r4,[sp,#3*4]
417        mov r5,r5,lsr #12  @ convert to page number
418mmap_do:
419        do_sys __NR_mmap2
420        ldmia sp!,{r4,r5,pc}
421
422get_sys_munmap: .globl get_sys_munmap  // r0= system call instruction
423#if defined(ARMEL_DARWIN)  /*{*/
424        ldr r0,4*1 + munmap
425#elif defined(ARMEL_EABI4)  /*}{*/
426        ldr r0,4*2 + munmap
427#elif defined(ARM_OLDABI)  /*}{*/
428        ldr r0,4*0 + munmap
429#else  /*}{*/
430        mov r0,#0
431#endif  /*}*/
432        ret
433
434mmap_privanon: .globl mmap_privanon
435        stmdb sp!,{r4,r5,lr}
436        ldr r4,mflg_data  @ Map_PRIVATE|MAP_ANON for Linux; MAP_PRIVANON for QNX
437        mov r5,#0  @ offset= 0
438        orr r3,r3,r4  @ combine with input (such as MAP_FIXED)
439        mvn r4,#0  @ fd= -1
440        b mmap_do
441
442#if 1|DEBUG  /*{*/
443
444div10: .globl div10
445        mov ip,r0  @ extra copy used at end
446        sub r1,r1,r1  @ hi
447
448        mov r2,r0  @ copy lo
449        adds r0,r0,r0,lsl #3   @ 9*lo
450        adc  r1,r1,r1,lsl #3   @ 9*hi + C
451        add  r1,r1,r2,lsr #(32 - 3)  @ bits shifted from lo to hi
452
453        mov r2,r0  @ copy lo
454        adds r0,r0,r0,lsl #4
455        adc  r1,r1,r1,lsl #4
456        add  r1,r1,r2,lsr #(32 - 4)  @ * 0x99
457
458        mov r2,r0  @ copy lo
459        adds r0,r0,r0,lsl #8
460        adc  r1,r1,r1,lsl #8
461        add  r1,r1,r2,lsr #(32 - 8)  @ * 0x9999
462
463        mov r2,r0  @ copy lo
464        adds r0,r0,r0,lsl #16
465        adc  r1,r1,r1,lsl #16
466        add  r1,r1,r2,lsr #(32 - 16)  @ * 0x99999999
467
468        subs r0,r0,ip,lsl #(32 - 1)  @ - * 0x80000000
469        sbc  r1,r1,ip,lsr #1         @   * 0x19999999
470
471        adds r0,r0,ip
472        adc  r0,r1,#0  @ * 0x0.1999999a
473        ret
474
475#endif  /*}*/
476
477/* vim:set ts=8 sw=8 et: */
478