1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
|
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 <stdlib.h>
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% |
|