1//  arm-darwin.macho-fold.S -- linkage to C code to process Mach-O 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 SIMULATE_ON_LINUX_EABI4 0
31
32#if SIMULATE_ON_LINUX_EABI4  /*{*/
33  #define LINUX_ARM_CACHEFLUSH 1  /* SIMULATE_ON_LINUX_EABI4 */
34  #define ARMEL_EABI4 1           /* SIMULATE_ON_LINUX_EABI4 */
35#else  /*}{ USUAL case */
36  #define DARWIN_ARM_CACHEFLUSH 1
37  #define ARMEL_DARWIN 1
38#endif  /*}*/
39
40#ifndef DEBUG  //{
41#define DEBUG 0
42#endif  //}
43#define TRACE_REGS r0-r12,r14,r15
44
45NBPW= 4
46#include "arch/arm/v5a/macros.S"
47
48arg1 .req r0
49arg2 .req r1
50arg3 .req r2
51arg4 .req r3
52arg5 .req r4
53arg6 .req r5
54arg7 .req r6
55
56sz_l_info = 12
57sz_p_info = 12
58sz_b_info = 12
59  sz_unc= 0
60  sz_cpr= 4
61  b_method= 8
62
63#if SIMULATE_ON_LINUX_EABI4  /*{*/
64__NR_brk      =  45 + __NR_SYSCALL_BASE  // 0x2d
65__NR_close    =   6 + __NR_SYSCALL_BASE  // 0x06
66__NR_exit     =   1 + __NR_SYSCALL_BASE  //  ??
67__NR_mmap     = 192 + __NR_SYSCALL_BASE  // 0xc0
68__NR_mprotect = 125 + __NR_SYSCALL_BASE  // 0x7d
69__NR_munmap   =  91 + __NR_SYSCALL_BASE  // 0x5b
70__NR_open     =   5 + __NR_SYSCALL_BASE  //  ??
71__NR_openat   = 322 + __NR_SYSCALL_BASE  // 0x142
72__NR_pread    = 180 + __NR_SYSCALL_BASE  // 0xb4
73__NR_read     =   3 + __NR_SYSCALL_BASE  // 0x03
74__NR_write    =   4 + __NR_SYSCALL_BASE  // 0x04
75#else  //}{  native darwin usual case
76__NR_brk      =  45 + __NR_SYSCALL_BASE
77__NR_close    =   6 + __NR_SYSCALL_BASE
78__NR_exit     =   1 + __NR_SYSCALL_BASE
79__NR_mmap     = 197 + __NR_SYSCALL_BASE
80__NR_mprotect =  74 + __NR_SYSCALL_BASE
81__NR_munmap   =  73 + __NR_SYSCALL_BASE
82__NR_open     =   5 + __NR_SYSCALL_BASE
83__NR_pread    = 153 + __NR_SYSCALL_BASE
84__NR_read     =   3 + __NR_SYSCALL_BASE
85__NR_write    =   4 + __NR_SYSCALL_BASE
86#endif  /*}*/
87
88_start: .globl _start  // ignored, but silence "cannot find entry symbol _start" from ld
89
90// control just falls through, after this part and compiled C code
91// are uncompressed.
92
93fold_begin:
94/* In:
95  r9= f_exp; r10= ADRX; r11= LENX
96  rsp/ fd,ADRU,LENU,%entry,&Mach_header
97*/
98r_t0   .req r12  // scratch
99r_LENX .req r11
100r_ADRX .req r10
101r_EXP  .req r9
102r_OSTK .req r8
103#if DEBUG  /*{*/
104        stmdb sp!,{TRACE_REGS}; mov r0,#0x11; bl trace
105#endif  /*}*/
106
107        ldr   arg4,[r_ADRX,#sz_unc + sz_l_info + sz_p_info]  // sz_unc of Mach_header
108        mov        r_t0,#(1<<13)
109        cmp   arg4,r_t0
110        movlo arg4,r_t0  // at least 8KiB
111        mov r_OSTK,sp  // where to un-alloca
112        sub sp,sp,arg4  // alloca
113        mov arg3,sp  // temp[sz_mhdr]
114        mov arg2,r_LENX
115        mov arg1,r_ADRX  // &{l_info; p_info; b_info}
116
117        add arg7,r_OSTK,#4*NBPW  // & &Mach_header
118        adr arg6,f_unfilter
119        mov arg5,r_EXP
120        stmdb sp!,{arg5,arg6,arg7}  // ABI: only arg1,arg2,arg3,arg4 in registers
121#if DEBUG  /*{*/
122        stmdb sp!,{TRACE_REGS}; mov r0,#0x12; bl trace
123#endif  /*}*/
124// upx_main(r0=&l_info, r1=len_cpr, r2=temp[sz_mhdr], r3=sz_mhdr, r4=f_exp, r5=f_unf, r6=mhdr **)
125        bl upx_main  // OUT: r0= &Mach_thread_state of dyld; will be in temp mhdr[]
126
127ARM_ts_pc= 14*NBPW
128        ldr r_ADRX,[r0,#ARM_ts_pc]  // dyld.entry
129        ldr r_EXP, [r0,#0]  // &hatch
130        mov sp,r_OSTK  // un-alloc
131
132        ldr r0,[sp,#0*NBPW]  // fd
133        bl close
134
135        ldr r0,[sp,#1*NBPW]  // ADRU
136        ldr r1,[sp,#2*NBPW]  // LENU
137        mov r7,#__NR_munmap
138        mov lr,r_ADRX  // dyld.entry
139        add sp,sp,#4*NBPW  // leave &Mach_header
140#if DEBUG  /*{*/
141        stmdb sp!,{TRACE_REGS}; mov r0,#0x13; bl trace
142#endif  /*}*/
143        bx r_EXP  // goto hatch: syscall.munmap(ADRU,LENU); ret
144
145f_unfilter:  // (char *ptr, uint len, uint cto, uint fid)
146        ptr  .req r0
147        len  .req r1
148        cto  .req r2  // unused
149        fid  .req r3
150
151        t1   .req r2
152        t2   .req r3
153
154#ifndef FILTER_ID  /*{*/
155#define FILTER_ID 0x50  /* little-endian */
156#endif  /*}*/
157        and fid,fid,#0xff
158        cmp fid,#FILTER_ID  // last use of fid
159        movne pc,lr  // no-op if not filter 0x50
160
161        movs  len,len,lsr #2  // word count
162        cmpne ptr,#0
163        moveq pc,lr  // no-op if either len or ptr is 0
164
165top_unf:
166        sub len,len,#1
167        ldr t1,[ptr,len,lsl #2]
168        and t2,t1,#0x0f<<24
169        cmp t2,   #0x0b<<24; bne tst_unf  // not 'bl' subroutine call
170        and t2,t1,#0xff<<24  // all the non-displacement bits
171        sub t1,t1,len  // convert to word-relative displacement
172        bic t1,t1,#0xff<<24  // restrict to displacement field
173        orr t1,t1,t2  // re-combine
174        str t1,[ptr,len,lsl #2]
175tst_unf:
176        cmp len,#0
177        bne top_unf
178        mov pc,lr
179
180        .unreq ptr
181        .unreq len
182        .unreq cto
183        .unreq fid
184
185#if DEBUG  /*{*/
186TRACE_BUFLEN=512
187trace:
188        str lr,[sp,#(-1+ 15)*4]  @ return pc; [remember: sp is not stored]
189        mov r4,sp  @ &saved_r0
190        sub sp,sp,#TRACE_BUFLEN
191        mov r2,sp  @ output string
192
193        mov r1,#'\n'; bl trace_hex  @ In: r0 as label
194        mov r1,#'>';  strb r1,[r2],#1
195
196        mov r5,#3  @ rows to print
197L600:  @ each row
198        sub r0,r4,#TRACE_BUFLEN
199        sub r0,r0,sp
200        mov r0,r0,lsr #2; mov r1,#'\n'; bl trace_hex  @ which block of 8
201
202        mov r6,#8  @ words per row
203L610:  @ each word
204        ldr r0,[r4],#4; mov r1,#' '; bl trace_hex  @ next word
205        subs r6,r6,#1; bgt L610
206
207        subs r5,r5,#1; bgt L600
208
209        mov r0,#'\n'; strb r0,[r2],#1
210        sub r2,r2,sp  @ count
211        mov r1,sp  @ buf
212        mov r0,#2  @ FD_STDERR
213#if defined(ARMEL_EABI4)  /*{*/
214        mov r7,#__NR_write
215        swi 0
216#else  /*}{*/
217        swi __NR_write
218#endif  /*}*/
219        add sp,sp,#TRACE_BUFLEN
220        ldmia sp!,{TRACE_REGS}
221
222trace_hex:  // In: r0=val, r1=punctuation before, r2=ptr; Uses: r3, ip
223        strb r1,[r2],#1  @ punctuation
224        mov r3,#4*(8 -1)  @ shift count
225        adr ip,hex
226L620:
227        mov r1,r0,lsr r3
228        and r1,r1,#0xf
229        ldrb r1,[ip, r1]
230        strb r1,[r2],#1
231        subs r3,r3,#4; bge L620
232        ret
233hex:
234        .ascii "0123456789abcdef"
235#endif  /*}*/
236spin: .globl spin
237        ret
238
239        .globl exit
240exit:
241        do_sys __NR_exit
242
243        .globl read
244read:
245        do_sys __NR_read; ret
246
247        .globl write
248write:
249        do_sys __NR_write; ret
250
251        .globl open
252open:
253        do_sys __NR_open; ret
254
255        .globl close
256close:
257        do_sys __NR_close; ret
258
259        .globl brk
260brk:
261        do_sys __NR_brk; ret
262
263        .globl munmap
264munmap:
265        do_sys __NR_munmap; ret
266
267        .globl mprotect
268mprotect:
269        do_sys __NR_mprotect; ret
270
271        .globl mmap
272mmap:
273#if SIMULATE_ON_LINUX_EABI4  /*{*/
274        stmdb sp!,{r4,r5}
275        ldr r5,[sp,#3*4]  // off_t
276        ldr r4,[sp,#2*4]  // fd
277        movs r12,r5,lsl #(32-12); bne mmap_frag  // lo 12 bits of offset
278        mov r5,r5,lsr #12  // convert to page number (avoid 64-bit argument)
279        do_sys __NR_mmap
280mmap_ret:
281        ldmia sp!,{r4,r5}
282        ret
283mmap_frag:
284EINVAL=22
285        mov r0,#-EINVAL  // offset not a multiple of page size
286        b mmap_ret
287#else  /*}{ USUAL case */
288        mov ip,sp
289        stmdb sp!,{r4,r5,r6}
290        ldmia ip ,{r4,r5 /*,r6*/}
291        mov r6,#0  // XXX: convert 32-bit unsigned off_t to 64-bits
292        do_sys __NR_mmap
293        ldmia sp!,{r4,r5,r6}
294        ret
295#endif  /*}*/
296
297        .globl pread
298pread:
299#if SIMULATE_ON_LINUX_EABI4  /*{*/
300        stmdb sp!,{r4,r5}  // EABI4 wants 64-bit off_t in even,odd register pair
301        mov r4,r3  //            32-bit off_t
302        mov r5,#0  // hi bits of 64-bit off_t
303        do_sys __NR_pread
304        ldmia sp!,{r4,r5}
305        ret
306#else  /*}{ USUAL case */
307        str r4,[sp,#-4]!  // PUSH r4
308        mov r4,#0   // convert 32-bit unsigned off_t in r3 to 64 bits in (r3,r4)
309        do_sys __NR_pread
310        ldr r4,[sp],#4  // POP r4
311        ret
312#endif  /*}*/
313
314        .globl bswap
315bswap:
316        mov ip,   #0xff
317        orr ip,ip,#0xff<<16   // ip= 0x00ff00ff
318        b bswap9
319bswap0:
320        ldr r2,[r0]           // r2= A B C D
321        and r3,ip,r2          // r3= 0 B 0 D
322        and r2,ip,r2,ror #24  // r2= 0 C 0 A
323        orr r2,r2,r3,ror # 8  // r2= D C B A
324        str r2,[r0],#4
325bswap9:
326        subs r1,r1,#4
327        bge bswap0
328        ret
329
330bad__udivsi3:
331        bkpt
332__udivsi3: .globl __udivsi3
333        cmp r1,#10
334        bne bad__udivsi3
335div10: .globl div10
336        mov ip,r0  @ extra copy used at end
337        sub r1,r1,r1  @ hi
338
339        mov r2,r0  @ copy lo
340        adds r0,r0,r0,lsl #3   @ 9*lo
341        adc  r1,r1,r1,lsl #3   @ 9*hi + C
342        add  r1,r1,r2,lsr #(32 - 3)  @ bits shifted from lo to hi
343
344        mov r2,r0  @ copy lo
345        adds r0,r0,r0,lsl #4
346        adc  r1,r1,r1,lsl #4
347        add  r1,r1,r2,lsr #(32 - 4)  @ * 0x99
348
349        mov r2,r0  @ copy lo
350        adds r0,r0,r0,lsl #8
351        adc  r1,r1,r1,lsl #8
352        add  r1,r1,r2,lsr #(32 - 8)  @ * 0x9999
353
354        mov r2,r0  @ copy lo
355        adds r0,r0,r0,lsl #16
356        adc  r1,r1,r1,lsl #16
357        add  r1,r1,r2,lsr #(32 - 16)  @ * 0x99999999
358
359        subs r0,r0,ip,lsl #(32 - 1)  @ - * 0x80000000
360        sbc  r1,r1,ip,lsr #1         @   * 0x19999999
361
362        adds r0,r0,ip
363        adc  r0,r1,#0  @ * 0x0.1999999a
364        ret
365
366/* vim:set ts=8 sw=8 et: */
367