title: ARM64 make tiny binaries keywords:raspi5,asm,c,kernel,arm64 # ARM64 make tiny binaries ## Intro This notes based on [Making C executable smaller](/writeup/making_c_executables_smaller.md). Now time came to create version for ARM. One example using C and one example using assembler. Examples printout hello world message and exit, nothing else. ## Minimal C program Two minimal C programs one that using libc other that doesn't ### Source code without libc ___mainc.c___ ```c char str[] = "Hello world\n"; unsigned int str_len = 12; void _start() { //write asm(\ "mov x0, #1\n" "mov x1, %0\n" "mov x2, %1\n" "mov w8, #64\n" "svc #0\n" : :"r"(str),"r"(str_len) ); //exit asm(\ "mov x0, #0\n" /* status := 0 */ "mov w8, #93\n" /* exit is syscall #1 */ "svc #0\n" ); } ``` Compiles with ```bash gcc -Os -c mainc.c mainc.o ld mainc.o -o mainc ``` ### Source code with libc __mainc2.c__ ```c #include void _start() { write(1,"Hello world\n",12); exit(0); } ``` Compiles with ```bash gcc -Os -c mainc2.c mainc2.o ld mainc2.o -lc -o mainc2 ``` ## Minimal Assembler program Both examples will work with raspberry pi 4 as such. One example for ARMv7 and other for ARMv8 ### Source code for ARMv7 __main7.S__ ```c .text .global _start _start: mov r0, #1 ldr r1, =message ldr r2, =len mov r7, #4 swi 0 mov r7, #1 swi 0 .data message: .asciz "hello world\n" len = .-message ``` ```bash arm-linux-gnueabi-gcc -Os -c main7.S main7.o arm-linux-gnueabi-ld -s main7.o -o main7 ``` ### Source code for ARMv8 __main.S__ ```c .data /* Data segment: define our message string and calculate its length. */ helloworld: .ascii "Hello, ARM64!\n" helloworld_len = . - helloworld .text /* Our application's entry point. */ .globl _start _start: /* syscall write(int fd, const void *buf, size_t count) */ mov x0, #1 /* fd := STDOUT_FILENO */ ldr x1, =helloworld /* buf := msg */ ldr x2, =helloworld_len /* count := len */ mov w8, #64 /* write is syscall #64 */ svc #0 /* invoke syscall */ /* syscall exit(int status) */ mov x0, #0 /* status := 0 */ mov w8, #93 /* exit is syscall #1 */ svc #0 ``` ```bash gcc -Os -c main.S -o main.o ld main.o -o main ``` ## Binary size before compression After all examples compiled lets compare sizes | Binary | Size, bytes | |---|---| | C, libc| 67976 | | C, no libc | 66680 | | Asm, ARMv7 | 432 | | Asm, ARMv8 | 1152 | ## Packing the binaries into self extracting archive Binaries are stripped with ```bash strip ``` Then compressed with ```bash 7z a -tGZip -mx=9 main.gz main > /dev/null ``` Then self unpacking header is added __unpack.header__ ```bash a=/tmp/I;tail -n+2 $0|zcat>$a;chmod +x $a;$a;rm $a;exit ``` ```bash chmod a+x main ``` Now the binaries can run as regular executable. ## Binary sizes of compression Lets compare sizes after packing up the binaries | Binary | Compressed size, bytes | Original size, bytes | Size of original | |---|---|---|---| | C, libc| 1049 | 67976 | 1.5% | | C, no libc | 517 | 66680 | 0.7% | | Asm, ARMv7 | 291 | 432 | 67% | | Asm, ARMv8 | 250 | 1152 | 21% |