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