1 /* p_vmlinz.cpp --
2
3 This file is part of the UPX executable compressor.
4
5 Copyright (C) 1996-2020 Markus Franz Xaver Johannes Oberhumer
6 Copyright (C) 1996-2020 Laszlo Molnar
7 All Rights Reserved.
8
9 UPX and the UCL library are free software; you can redistribute them
10 and/or modify them under the terms of the GNU General Public License as
11 published by the Free Software Foundation; either version 2 of
12 the License, or (at your option) any later version.
13
14 This program is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 GNU General Public License for more details.
18
19 You should have received a copy of the GNU General Public License
20 along with this program; see the file COPYING.
21 If not, write to the Free Software Foundation, Inc.,
22 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
23
24 Markus F.X.J. Oberhumer Laszlo Molnar
25 <markus@oberhumer.com> <ezerotven+github@gmail.com>
26 */
27
28
29 #include "conf.h"
30
31 #include "p_elf.h"
32 #include "file.h"
33 #include "filter.h"
34 #include "packer.h"
35 #include "p_vmlinz.h"
36 #include "linker.h"
37 #include <zlib.h>
38
39 static const
40 #include "stub/i386-linux.kernel.vmlinuz.h"
41
42 static const unsigned stack_offset_during_uncompression = 0x9000;
43 // add to "real mode pointer" in %esi; total 0x99000 is typical
44
45 // from /usr/src/linux/arch/i386/boot/compressed/Makefile
46 static const unsigned zimage_offset = 0x1000;
47 static const unsigned bzimage_offset = 0x100000;
48
49
50 /*************************************************************************
51 //
52 **************************************************************************/
53
PackVmlinuzI386(InputFile * f)54 PackVmlinuzI386::PackVmlinuzI386(InputFile *f) :
55 super(f), physical_start(0x100000), page_offset(0), config_physical_align(0)
56 , filter_len(0)
57 {
58 bele = &N_BELE_RTP::le_policy;
59 COMPILE_TIME_ASSERT(sizeof(boot_sect_t) == 0x250);
60 }
61
62
getCompressionMethods(int method,int level) const63 const int *PackVmlinuzI386::getCompressionMethods(int method, int level) const
64 {
65 return Packer::getDefaultCompressionMethods_le32(method, level);
66 }
67
68
getFilters() const69 const int *PackVmlinuzI386::getFilters() const
70 {
71 static const int filters[] = {
72 0x26, 0x24, 0x49, 0x46, 0x16, 0x13, 0x14, 0x11,
73 FT_ULTRA_BRUTE, 0x25, 0x15, 0x12,
74 FT_END };
75 return filters;
76 }
77
getFilters() const78 const int *PackBvmlinuzI386::getFilters() const
79 {
80 // The destination buffer might be relocated at runtime.
81 static const int filters[] = {
82 0x49, 0x46,
83 FT_END };
84 return filters;
85 }
86
getStrategy(Filter &)87 int PackVmlinuzI386::getStrategy(Filter &/*ft*/)
88 {
89 // If user specified the filter, then use it (-2==filter_strategy).
90 // Else try the first two filters, and pick the better (2==filter_strategy).
91 return (opt->no_filter ? -3 : ((opt->filter > 0) ? -2 : 2));
92 }
93
94
canPack()95 bool PackVmlinuzI386::canPack()
96 {
97 return readFileHeader() == getFormat();
98 }
99
100
101 /*************************************************************************
102 // common util routines
103 **************************************************************************/
104
readFileHeader()105 int PackVmlinuzI386::readFileHeader()
106 {
107 setup_size = 0;
108
109 fi->readx(&h, sizeof(h));
110 if (h.boot_flag != 0xAA55)
111 return 0;
112 const bool hdrs = (memcmp(h.hdrs, "HdrS", 4) == 0);
113
114 setup_size = (1 + (h.setup_sects ? h.setup_sects : 4)) * 0x200;
115 if (setup_size <= 0 || setup_size >= file_size)
116 return 0;
117
118 int format = UPX_F_VMLINUZ_i386;
119 unsigned sys_size = ALIGN_UP((unsigned) file_size, 16u) - setup_size;
120
121 const unsigned char *p = (const unsigned char *) &h + 0x1e3;
122
123 if (hdrs && memcmp(p, "\x0d\x0a\x07""ELKS", 7) == 0)
124 {
125 format = UPX_F_ELKS_8086;
126 }
127 else if (hdrs && (h.load_flags & 1) != 0)
128 {
129 format = UPX_F_BVMLINUZ_i386;
130 }
131
132 if (0x204<=h.version) {
133 if ((16u * h.sys_size) != sys_size)
134 return 0;
135 }
136 else { // h.sys_size is only 2 bytes
137 if ((16u * (0xffff & h.sys_size)) != (~(~0u<<20) & sys_size))
138 return 0;
139 }
140
141 // FIXME: add more checks for a valid kernel
142
143 return format;
144 }
145
146
is_pow2(unsigned const x)147 static int is_pow2(unsigned const x)
148 {
149 return !(x & (x - 1));
150 }
151
152 // read full kernel into obuf[], gzip-decompress into ibuf[],
153 // return decompressed size
decompressKernel()154 int PackVmlinuzI386::decompressKernel()
155 {
156 // read whole kernel image
157 obuf.alloc(file_size);
158 fi->seek(0, SEEK_SET);
159 fi->readx(obuf, file_size);
160
161 {
162 const upx_byte *base = NULL;
163 unsigned relocated = 0;
164
165 // See startup_32: in linux/arch/i386/boot/compressed/head.S
166 const upx_byte *p;
167 unsigned cpa_0 = 0;
168 unsigned cpa_1 = 0;
169 int j;
170 if (0x205<=h.version) {
171 cpa_0 = h.kernel_alignment;
172 cpa_1 = 0u - cpa_0;
173 } else
174 for ((p = &obuf[setup_size]), (j= 0); j < 0x200; ++j, ++p) {
175 if (0==memcmp("\x89\xeb\x81\xc3", p, 4)
176 && 0==memcmp("\x81\xe3", 8+ p, 2)) {
177 // movl %ebp,%ebx
178 // addl $imm.w,%ebx
179 // andl $imm.w,%ebx
180 cpa_0 = 1+ get_te32( 4+ p);
181 cpa_1 = get_te32(10+ p);
182 break;
183 }
184 }
185 for ((p = &obuf[setup_size]), (j= 0); j < 0x200; ++j, ++p) {
186 if (0==memcmp("\x8d\x83", p, 2) // leal d32(%ebx),%eax
187 && 0==memcmp("\xff\xe0", 6+ p, 2) // jmp *%eax
188 ) {
189 relocated = get_te32(2+ p);
190 }
191 if (0==memcmp("\xE8\x00\x00\x00\x00\x5D", p, 6)) {
192 // "call 1f; 1f: pop %ebp" determines actual execution address.
193 // linux-2.6.21 (spring 2007) and later; upx stub needs work
194 // unless LOAD_PHYSICAL_ADDR is known.
195 // Allowed code is: linux-2.6.23/arch/x86/head_32.S 2008-01-01
196 // call 1f
197 // 1: popl %ebp
198 // subl $1b, %ebp # 32-bit immediate
199 // movl $LOAD_PHYSICAL_ADDR, %ebx
200 //
201 if (0==memcmp("\x81\xed", 6+ p, 2) // subl $imm.w,%ebp
202 && 0==memcmp("\xbb", 12+ p, 1) ) { // movl $imm.w,%ebx
203 physical_start = get_te32(13+ p);
204 } else
205 if (0==memcmp("\x81\xed", 6+ p, 2) // subl $imm.w,%ebp
206 && is_pow2(cpa_0) && (0u-cpa_0)==cpa_1) {
207 base = (5+ p) - get_te32(8+ p);
208 config_physical_align = cpa_0;
209 }
210 else {
211 throwCantPack("Unrecognized relocatable kernel");
212 }
213 }
214 // Find "ljmp $__BOOT_CS,$__PHYSICAL_START" if any.
215 if (0==memcmp("\xEA\x00\x00", p, 3) && 0==(0xf & p[3]) && 0==p[4]) {
216 /* whole megabyte < 16 MiB */
217 physical_start = get_te32(1+ p);
218 break;
219 }
220 }
221 if (base && relocated) {
222 p = base + relocated;
223 for (j = 0; j < 0x200; ++j, ++p) {
224 if (0==memcmp("\x01\x9c\x0b", p, 3) // addl %ebx,d32(%ebx,%ecx)
225 ) {
226 page_offset = 0u - get_te32(3+ p);
227 }
228 if (0==memcmp("\x89\xeb", p, 2) // movl %ebp,%ebx
229 && 0==memcmp("\x81\xeb", 2+ p, 2) // subl $imm32,%ebx
230 ) {
231 physical_start = get_te32(4+ p);
232 }
233 }
234 }
235 }
236
237 checkAlreadyPacked(obuf + setup_size, UPX_MIN(file_size - setup_size, (off_t)1024));
238
239 int gzoff = setup_size;
240 if (0x208<=h.version) {
241 gzoff += h.payload_offset;
242 }
243 for (; gzoff < file_size; gzoff++)
244 {
245 // find gzip header (2 bytes magic + 1 byte method "deflated")
246 int off = find(obuf + gzoff, file_size - gzoff, "\x1F\x8B\x08", 3);
247 if (off < 0)
248 break;
249 gzoff += off;
250 const int gzlen = (h.version < 0x208) ? (file_size - gzoff) : h.payload_length;
251 if (gzlen < 256)
252 break;
253 // check gzip flag byte
254 unsigned char flags = obuf[gzoff + 3];
255 if ((flags & 0xe0) != 0) // reserved bits set
256 continue;
257 //printf("found gzip header at offset %d\n", gzoff);
258
259 // try to decompress
260 int klen;
261 int fd;
262 off_t fd_pos;
263 for (;;)
264 {
265 klen = -1;
266 fd_pos = -1;
267 // open
268 fi->seek(gzoff, SEEK_SET);
269 fd = dup(fi->getFd());
270 if (fd < 0)
271 break;
272 gzFile zf = gzdopen(fd, "rb");
273 if (zf == NULL)
274 break;
275 // estimate gzip-decompressed kernel size & alloc buffer
276 if (ibuf.getSize() == 0)
277 ibuf.alloc(gzlen * 3);
278 // decompress
279 klen = gzread(zf, ibuf, ibuf.getSize());
280 fd_pos = lseek(fd, 0, SEEK_CUR);
281 gzclose(zf);
282 fd = -1;
283 if (klen != (int)ibuf.getSize())
284 break;
285 // realloc and try again
286 unsigned s = ibuf.getSize();
287 ibuf.dealloc();
288 ibuf.alloc(3 * s / 2);
289 }
290 if (fd >= 0)
291 (void) close(fd);
292 if (klen <= 0)
293 continue;
294
295 if (klen <= gzlen)
296 continue;
297
298 if (0x208<=h.version && 0==memcmp("\177ELF", ibuf, 4)) {
299 // Full ELF in theory; for now, try to handle as .bin at physical_start.
300 // Check for PT_LOAD.p_paddr being ascending and adjacent.
301 Elf_LE32_Ehdr const *const ehdr = (Elf_LE32_Ehdr const *)(void const *)ibuf;
302 Elf_LE32_Phdr const *phdr = (Elf_LE32_Phdr const *)(ehdr->e_phoff + (char const *)ehdr);
303 Elf_LE32_Shdr const *shdr = (Elf_LE32_Shdr const *)(ehdr->e_shoff + (char const *)ehdr);
304 unsigned hi_paddr = 0, lo_paddr = 0;
305 unsigned delta_off = 0;
306 for (unsigned j=0; j < ehdr->e_phnum; ++j, ++phdr) {
307 if (phdr->PT_LOAD==phdr->p_type) {
308 unsigned step = (hi_paddr + phdr->p_align - 1) & ~(phdr->p_align - 1);
309 if (0==hi_paddr) { // first PT_LOAD
310 if (physical_start!=phdr->p_paddr) {
311 return 0;
312 }
313 delta_off = phdr->p_paddr - phdr->p_offset;
314 lo_paddr = phdr->p_paddr;
315 hi_paddr = phdr->p_filesz + phdr->p_paddr;
316 }
317 else if (step==phdr->p_paddr
318 && delta_off==(phdr->p_paddr - phdr->p_offset)) {
319 hi_paddr = phdr->p_filesz + phdr->p_paddr;
320 }
321 else {
322 return 0; // Not equivalent to a .bin. Too complex for now.
323 }
324 }
325 }
326 // FIXME: ascending order is only a convention; might need sorting.
327 for (unsigned j=1; j < ehdr->e_shnum; ++j) {
328 if (shdr->SHT_PROGBITS==shdr->sh_type) { // SHT_REL might be intermixed
329 if (shdr->SHF_EXECINSTR & shdr[j].sh_flags) {
330 filter_len += shdr[j].sh_size; // FIXME: include sh_addralign
331 }
332 else {
333 break;
334 }
335 }
336 }
337 memmove(ibuf, (lo_paddr - delta_off) + ibuf, hi_paddr - lo_paddr); // FIXME: set_size
338 // FIXME: .bss ? Apparently handled by head.S
339 }
340
341 if (opt->force > 0)
342 return klen;
343
344 // some checks
345 if (fd_pos != file_size)
346 {
347 //printf("fd_pos: %ld, file_size: %ld\n", (long)fd_pos, (long)file_size);
348
349 // linux-2.6.21.5/arch/i386/boot/compressed/vmlinux.lds
350 // puts .data.compressed ahead of .text, .rodata, etc;
351 // so piggy.o need not be last in bzImage. Alas.
352 //throwCantPack("trailing bytes after kernel image; use option '-f' to force packing");
353 }
354
355
356 // see /usr/src/linux/arch/i386/kernel/head.S
357 // 2.4.x: [cli;] cld; mov $...,%eax
358 if (memcmp(ibuf, "\xFC\xB8", 2) == 0) goto head_ok;
359 if (memcmp(ibuf, "\xFA\xFC\xB8", 3) == 0) goto head_ok;
360 // 2.6.21.5 CONFIG_PARAVIRT mov %cs,%eax; test $3,%eax; jne ...;
361 if (memcmp(ibuf, "\x8c\xc8\xa9\x03\x00\x00\x00\x0f\x85", 9) == 0) goto head_ok;
362 if (memcmp(ibuf, "\x8c\xc8\xa8\x03\x0f\x85", 6) == 0) goto head_ok;
363 // 2.6.x: [cli;] cld; lgdt ...
364 if (memcmp(ibuf, "\xFC\x0F\x01", 3) == 0) goto head_ok;
365 if (memcmp(ibuf, "\xFA\xFC\x0F\x01", 4) == 0) goto head_ok;
366 // 2.6.x+grsecurity+strongswan+openwall+trustix: ljmp $0x10,...
367 if (ibuf[0] == 0xEA && memcmp(ibuf+5, "\x10\x00", 2) == 0) goto head_ok;
368 // x86_64 2.6.x
369 if (0xB8==ibuf[0] // mov $...,%eax
370 && 0x8E==ibuf[5] && 0xD8==ibuf[6] // mov %eax,%ds
371 && 0x0F==ibuf[7] && 0x01==ibuf[8] && 020==(070 & ibuf[9]) // lgdtl
372 && 0xB8==ibuf[14] // mov $...,%eax
373 && 0x0F==ibuf[19] && 0xA2==ibuf[20] // cpuid
374 ) goto head_ok;
375
376 // cmpw $0x207,0x206(%esi) Debian vmlinuz-2.6.24-12-generic
377 if (0==memcmp("\x66\x81\xbe\x06\x02\x00\x00\x07\x02", ibuf, 9)) goto head_ok;
378
379 // testb $0x40,0x211(%esi) Fedora vmlinuz-2.6.25-0.218.rc8.git7.fc9.i686
380 if (0==memcmp("\xf6\x86\x11\x02\x00\x00\x40", ibuf, 7)) goto head_ok;
381
382 // rex.W prefix for x86_64
383 if (0x48==ibuf[0]) throwCantPack("x86_64 bzImage is not yet supported");
384
385 throwCantPack("unrecognized kernel architecture; use option '-f' to force packing");
386 head_ok:
387
388 // FIXME: more checks for special magic bytes in ibuf ???
389 // FIXME: more checks for kernel architecture ???
390
391 return klen;
392 }
393
394 return 0;
395 }
396
397
readKernel()398 void PackVmlinuzI386::readKernel()
399 {
400 int klen = decompressKernel();
401 if (klen <= 0)
402 throwCantPack("kernel decompression failed");
403 //OutputFile::dump("kernel.img", ibuf, klen);
404
405 // copy the setup boot code
406 setup_buf.alloc(setup_size);
407 memcpy(setup_buf, obuf, setup_size);
408 //OutputFile::dump("setup.img", setup_buf, setup_size);
409
410 obuf.dealloc();
411 obuf.allocForCompression(klen);
412
413 ph.u_len = klen;
414 ph.filter = 0;
415 }
416
417
newLinker() const418 Linker* PackVmlinuzI386::newLinker() const
419 {
420 return new ElfLinkerX86;
421 }
422
423
424 /*************************************************************************
425 // vmlinuz specific
426 **************************************************************************/
427
buildLoader(const Filter * ft)428 void PackVmlinuzI386::buildLoader(const Filter *ft)
429 {
430 // prepare loader
431 initLoader(stub_i386_linux_kernel_vmlinuz, sizeof(stub_i386_linux_kernel_vmlinuz));
432 addLoader("LINUZ000",
433 ph.first_offset_found == 1 ? "LINUZ010" : "",
434 ft->id ? "LZCALLT1" : "",
435 "LZIMAGE0",
436 getDecompressorSections(),
437 NULL
438 );
439 if (ft->id)
440 {
441 assert(ft->calls > 0);
442 addLoader("LZCALLT9", NULL);
443 addFilter32(ft->id);
444 }
445 addLoader("LINUZ990,IDENTSTR,UPX1HEAD", NULL);
446 }
447
448
pack(OutputFile * fo)449 void PackVmlinuzI386::pack(OutputFile *fo)
450 {
451 readKernel();
452
453 // prepare filter
454 Filter ft(ph.level);
455 ft.buf_len = ph.u_len;
456 ft.addvalue = physical_start; // saves 4 bytes in unfilter code
457
458 // compress
459 upx_compress_config_t cconf; cconf.reset();
460 // limit stack size needed for runtime decompression
461 cconf.conf_lzma.max_num_probs = 1846 + (768 << 4); // ushort: ~28 KiB stack
462 compressWithFilters(&ft, 512, &cconf, getStrategy(ft));
463
464 const unsigned lsize = getLoaderSize();
465
466 defineDecompressorSymbols();
467 defineFilterSymbols(&ft);
468 linker->defineSymbol("src_for_decompressor", zimage_offset + lsize);
469 linker->defineSymbol("original_entry", physical_start);
470 linker->defineSymbol("stack_offset", stack_offset_during_uncompression);
471 relocateLoader();
472
473 MemBuffer loader(lsize);
474 memcpy(loader, getLoader(), lsize);
475 patchPackHeader(loader, lsize);
476
477 boot_sect_t * const bs = (boot_sect_t *) ((unsigned char *) setup_buf);
478 bs->sys_size = ALIGN_UP(lsize + ph.c_len, 16u) / 16;
479 bs->payload_length = ph.c_len;
480
481 fo->write(setup_buf, setup_buf.getSize());
482 fo->write(loader, lsize);
483 fo->write(obuf, ph.c_len);
484 #if 0
485 printf("%-13s: setup : %8ld bytes\n", getName(), (long) setup_buf.getSize());
486 printf("%-13s: loader : %8ld bytes\n", getName(), (long) lsize);
487 printf("%-13s: compressed : %8ld bytes\n", getName(), (long) ph.c_len);
488 #endif
489
490 // verify
491 verifyOverlappingDecompression();
492
493 // finally check the compression ratio
494 if (!checkFinalCompressionRatio(fo))
495 throwNotCompressible();
496 }
497
498
499 /*************************************************************************
500 // bvmlinuz specific
501 **************************************************************************/
502
buildLoader(const Filter * ft)503 void PackBvmlinuzI386::buildLoader(const Filter *ft)
504 {
505 // prepare loader
506 initLoader(stub_i386_linux_kernel_vmlinuz, sizeof(stub_i386_linux_kernel_vmlinuz));
507 if (0!=page_offset) { // relocatable kernel
508 assert(0==ft->id || 0x40==(0xf0 & ft->id)); // others assume fixed buffer address
509 addLoader("LINUZ000,LINUZ001,LINUZVGA,LINUZ101,LINUZ110",
510 ((0!=config_physical_align) ? "LINUZ120" : "LINUZ130"),
511 "LINUZ140,LZCUTPOI,LINUZ141",
512 (ft->id ? "LINUZ145" : ""),
513 (ph.first_offset_found == 1 ? "LINUZ010" : ""),
514 NULL);
515 }
516 else {
517 addLoader("LINUZ000,LINUZ001,LINUZVGA,LINUZ005",
518 ph.first_offset_found == 1 ? "LINUZ010" : "",
519 (0x40==(0xf0 & ft->id)) ? "LZCKLLT1" : (ft->id ? "LZCALLT1" : ""),
520 "LBZIMAGE,IDENTSTR",
521 "+40", // align the stuff to 4 byte boundary
522 "UPX1HEAD", // 32 byte
523 "LZCUTPOI",
524 NULL);
525 // fake alignment for the start of the decompressor
526 //linker->defineSymbol("LZCUTPOI", 0x1000);
527 }
528
529 addLoader(getDecompressorSections(), NULL);
530
531 if (ft->id)
532 {
533 assert(ft->calls > 0);
534 if (0x40==(0xf0 & ft->id)) {
535 addLoader("LZCKLLT9", NULL);
536 }
537 else {
538 addLoader("LZCALLT9", NULL);
539 }
540 addFilter32(ft->id);
541 }
542 if (0!=page_offset) {
543 addLoader("LINUZ150,IDENTSTR,+40,UPX1HEAD", NULL);
544 unsigned const l_len = getLoaderSize();
545 unsigned const c_len = ALIGN_UP(ph.c_len, 4u);
546 unsigned const e_len = getLoaderSectionStart("LINUZ141") -
547 getLoaderSectionStart("LINUZ110");
548 linker->defineSymbol("compressed_length", c_len);
549 linker->defineSymbol("load_physical_address", physical_start); // FIXME
550 if (0!=config_physical_align) {
551 linker->defineSymbol("neg_config_physical_align", 0u - config_physical_align);
552 }
553 linker->defineSymbol("neg_length_mov", 0u - ALIGN_UP(c_len + l_len, 4u));
554 linker->defineSymbol("neg_page_offset", 0u - page_offset);
555 //linker->defineSymbol("physical_start", physical_start);
556 linker->defineSymbol("unc_length", ph.u_len);
557 linker->defineSymbol("dec_offset", ph.overlap_overhead + e_len);
558 linker->defineSymbol("unc_offset", ph.overlap_overhead + ph.u_len - c_len);
559 }
560 else {
561 addLoader("LINUZ990", NULL);
562 }
563 }
564
565
pack(OutputFile * fo)566 void PackBvmlinuzI386::pack(OutputFile *fo)
567 {
568 readKernel();
569
570 // prepare filter
571 Filter ft(ph.level);
572 ft.buf_len = (filter_len ? filter_len : (ph.u_len * 3)/5);
573 // May 2008: 3/5 is heuristic to cover most .text but avoid non-instructions.
574 // Otherwise "call trick" filter cannot find a free marker byte,
575 // especially when it searches over tables of data.
576 ft.addvalue = 0; // The destination buffer might be relocated at runtime.
577
578 upx_compress_config_t cconf; cconf.reset();
579 // LINUZ001 allows most of low memory as stack for Bvmlinuz
580 cconf.conf_lzma.max_num_probs = (0x90000 - 0x10000)>>1; // ushort: 512 KiB stack
581
582 compressWithFilters(&ft, 512, &cconf, getStrategy(ft));
583
584 // align everything to dword boundary - it is easier to handle
585 unsigned c_len = ph.c_len;
586 memset(obuf + c_len, 0, 4);
587 c_len = ALIGN_UP(c_len, 4u);
588
589 const unsigned lsize = getLoaderSize();
590
591 if (M_IS_LZMA(ph.method)) {
592 const lzma_compress_result_t *res = &ph.compress_result.result_lzma;
593 upx_uint32_t properties = // lc, lp, pb, dummy
594 (res->lit_context_bits << 0) |
595 (res->lit_pos_bits << 8) |
596 (res->pos_bits << 16);
597 if (linker->bele->isBE()) // big endian - bswap32
598 acc_swab32s(&properties);
599 linker->defineSymbol("lzma_properties", properties);
600 // -2 for properties
601 linker->defineSymbol("lzma_c_len", ph.c_len - 2);
602 linker->defineSymbol("lzma_u_len", ph.u_len);
603 unsigned const stack = getDecompressorWrkmemSize();
604 linker->defineSymbol("lzma_stack_adjust", 0u - stack);
605 }
606
607 const int e_len = getLoaderSectionStart("LZCUTPOI");
608 assert(e_len > 0);
609
610 if (0==page_offset) { // not relocatable kernel
611 const unsigned d_len4 = ALIGN_UP(lsize - e_len, 4u);
612 const unsigned decompr_pos = ALIGN_UP(ph.u_len + ph.overlap_overhead, 16u);
613 const unsigned copy_size = c_len + d_len4;
614 const unsigned edi = decompr_pos + d_len4 - 4; // copy to
615 const unsigned esi = ALIGN_UP(c_len + lsize, 4u) - 4; // copy from
616
617 linker->defineSymbol("decompressor", decompr_pos - bzimage_offset + physical_start);
618 linker->defineSymbol("src_for_decompressor", physical_start + decompr_pos - c_len);
619 linker->defineSymbol("words_to_copy", copy_size / 4);
620 linker->defineSymbol("copy_dest", physical_start + edi);
621 linker->defineSymbol("copy_source", bzimage_offset + esi);
622 }
623
624 defineFilterSymbols(&ft);
625 defineDecompressorSymbols();
626 if (0==page_offset) {
627 linker->defineSymbol("original_entry", physical_start);
628 }
629 linker->defineSymbol("stack_offset", stack_offset_during_uncompression);
630 relocateLoader();
631
632 MemBuffer loader(lsize);
633 memcpy(loader, getLoader(), lsize);
634 patchPackHeader(loader, lsize);
635
636 boot_sect_t * const bs = (boot_sect_t *) ((unsigned char *) setup_buf);
637 bs->sys_size = (ALIGN_UP(lsize + c_len, 16u) / 16);
638
639 fo->write(setup_buf, setup_buf.getSize());
640
641 unsigned const e_pfx = (0==page_offset) ? 0 : getLoaderSectionStart("LINUZ110");
642 if (0!=page_offset) {
643 fo->write(loader, e_pfx);
644 }
645 else {
646 fo->write(loader, e_len);
647 }
648 fo->write(obuf, c_len);
649 if (0!=page_offset) {
650 fo->write(loader + e_pfx, e_len - e_pfx);
651 }
652 fo->write(loader + e_len, lsize - e_len);
653 #if 0
654 printf("%-13s: setup : %8ld bytes\n", getName(), (long) setup_buf.getSize());
655 printf("%-13s: entry : %8ld bytes\n", getName(), (long) e_len);
656 printf("%-13s: compressed : %8ld bytes\n", getName(), (long) c_len);
657 printf("%-13s: decompressor : %8ld bytes\n", getName(), (long) (lsize - e_len));
658 #endif
659
660 // verify
661 verifyOverlappingDecompression();
662
663 // finally check the compression ratio
664 if (!checkFinalCompressionRatio(fo))
665 throwNotCompressible();
666 }
667
668
669 /*************************************************************************
670 // unpack
671 **************************************************************************/
672
canUnpack()673 int PackVmlinuzI386::canUnpack()
674 {
675 if (readFileHeader() != getFormat())
676 return false;
677 fi->seek(setup_size, SEEK_SET);
678 return readPackHeader(1024) ? 1 : -1;
679 }
680
681
unpack(OutputFile * fo)682 void PackVmlinuzI386::unpack(OutputFile *fo)
683 {
684 // no uncompression support for this format, so that
685 // it is possible to remove the original deflate code (>10 KiB)
686
687 // FIXME: but we could write the uncompressed "vmlinux" image
688
689 ibuf.alloc(ph.c_len);
690 obuf.allocForUncompression(ph.u_len);
691
692 fi->seek(setup_size + ph.buf_offset + ph.getPackHeaderSize(), SEEK_SET);
693 fi->readx(ibuf, ph.c_len);
694
695 // decompress
696 decompress(ibuf, obuf);
697
698 // unfilter
699 Filter ft(ph.level);
700 ft.init(ph.filter, physical_start);
701 ft.cto = (unsigned char) ph.filter_cto;
702 ft.unfilter(obuf, ph.u_len);
703
704 // write decompressed file
705 if (fo)
706 {
707 throwCantUnpack("build a new kernel instead :-)");
708 //fo->write(obuf, ph.u_len);
709 }
710 }
711
712
PackVmlinuzARMEL(InputFile * f)713 PackVmlinuzARMEL::PackVmlinuzARMEL(InputFile *f) :
714 super(f), setup_size(0), filter_len(0)
715 {
716 bele = &N_BELE_RTP::le_policy;
717 }
718
getCompressionMethods(int method,int level) const719 const int *PackVmlinuzARMEL::getCompressionMethods(int method, int level) const
720 {
721 return Packer::getDefaultCompressionMethods_8(method, level);
722 }
723
getFilters() const724 const int *PackVmlinuzARMEL::getFilters() const
725 {
726 static const int f50[] = { 0x50, FT_END };
727 return f50;
728 }
729
getStrategy(Filter &)730 int PackVmlinuzARMEL::getStrategy(Filter &/*ft*/)
731 {
732 // If user specified the filter, then use it (-2==filter_strategy).
733 // Else try the first two filters, and pick the better (2==filter_strategy).
734 return (opt->no_filter ? -3 : ((opt->filter > 0) ? -2 : 2));
735 }
736
canPack()737 bool PackVmlinuzARMEL::canPack()
738 {
739 return readFileHeader() == getFormat();
740 }
741
readFileHeader()742 int PackVmlinuzARMEL::readFileHeader()
743 {
744 unsigned int hdr[8];
745
746 fi->readx(hdr, sizeof(hdr));
747 for (int j=0; j < 8; ++j) {
748 if (0xe1a00000!=get_te32(&hdr[j])) {
749 return 0;
750 }
751 }
752 return UPX_F_VMLINUZ_ARMEL;
753 }
754
decompressKernel()755 int PackVmlinuzARMEL::decompressKernel()
756 {
757 // read whole kernel image
758 obuf.alloc(file_size);
759 fi->seek(0, SEEK_SET);
760 fi->readx(obuf, file_size);
761
762 //checkAlreadyPacked(obuf + setup_size, UPX_MIN(file_size - setup_size, (off_t)1024));
763
764 // Find head.S:
765 // bl decompress_kernel # 0xeb......
766 // b call_kernel # 0xea......
767 //LC0: .word LC0 # self!
768 unsigned decompress_kernel = 0;
769 unsigned caller1 = 0;
770 unsigned caller2 = 0;
771 unsigned got_start = 0;
772 unsigned got_end = 0;
773 for (unsigned j = 0; j < 0x400; j+=4) {
774 unsigned w;
775 if (j!=get_te32(j + obuf)) {
776 continue;
777 }
778 if (0xea000000!=(0xff000000& get_te32(j - 4 + obuf))
779 || 0xeb000000!=(0xff000000&(w= get_te32(j - 8 + obuf))) ) {
780 continue;
781 }
782 caller1 = j - 8;
783 decompress_kernel = ((0x00ffffff & w)<<2) + 8+ caller1;
784 for (unsigned k = 12; k<=128; k+=4) {
785 w = get_te32(j - k + obuf);
786 if (0xeb000000==(0xff000000 & w)
787 && decompress_kernel==(((0x00ffffff & w)<<2) + 8+ j - k) ) {
788 caller2 = j - k;
789 break;
790 }
791 }
792 got_start = get_te32(5*4 + j + obuf);
793 got_end = get_te32(6*4 + j + obuf);
794 #if 0 /*{*/
795 printf("decompress_kernel=0x%x got_start=0x%x got_end=0x%x\n",
796 decompress_kernel, got_start, got_end);
797 #endif /*}*/
798 break;
799 }
800 if (0==decompress_kernel) {
801 return 0;
802 }
803
804 // Find first subroutine that is called by decompress_kernel,
805 // which we will consider to be the start of the gunzip module
806 // and the end of the non-gunzip modules.
807 for (unsigned j = decompress_kernel; j < (unsigned)file_size; j+=4) {
808 unsigned w = get_te32(j + obuf);
809 if (0xeb800000==(0xff800000 & w)) {
810 setup_size = 8+ ((0xff000000 | w)<<2) + j;
811 // Move the GlobalOffsetTable.
812 for (unsigned k = got_start; k < got_end; k+=4) {
813 w = get_te32(k + obuf);
814 // FIXME: must relocate w
815 set_te32(k - got_start + setup_size + obuf, w);
816 }
817 setup_size += got_end - got_start;
818 set_te32(&obuf[caller1], 0xeb000000 |
819 (0x00ffffff & ((setup_size - (8+ caller1))>>2)) );
820 set_te32(&obuf[caller2], 0xeb000000 |
821 (0x00ffffff & ((setup_size - (8+ caller2))>>2)) );
822 break;
823 }
824 }
825
826 for (int gzoff = 0; gzoff < 0x4000; gzoff+=4) {
827 // find gzip header (2 bytes magic + 1 byte method "deflated")
828 int off = find(obuf + gzoff, file_size - gzoff, "\x1F\x8B\x08", 3);
829 if (off < 0 || 0!=(3u & off))
830 break; // not found, or not word-aligned
831 gzoff += off;
832 const int gzlen = file_size - gzoff;
833 if (gzlen < 256)
834 break;
835 // check gzip flag byte
836 unsigned char flags = obuf[gzoff + 3];
837 if ((flags & 0xe0) != 0) // reserved bits set
838 continue;
839 //printf("found gzip header at offset %d\n", gzoff);
840
841 // try to decompress
842 int klen;
843 int fd;
844 off_t fd_pos;
845 for (;;)
846 {
847 klen = -1;
848 fd_pos = -1;
849 // open
850 fi->seek(gzoff, SEEK_SET);
851 fd = dup(fi->getFd());
852 if (fd < 0)
853 break;
854 gzFile zf = gzdopen(fd, "rb");
855 if (zf == NULL)
856 break;
857 // estimate gzip-decompressed kernel size & alloc buffer
858 if (ibuf.getSize() == 0)
859 ibuf.alloc(gzlen * 3);
860 // decompress
861 klen = gzread(zf, ibuf, ibuf.getSize());
862 fd_pos = lseek(fd, 0, SEEK_CUR);
863 gzclose(zf);
864 fd = -1;
865 if (klen != (int)ibuf.getSize())
866 break;
867 // realloc and try again
868 unsigned const s = ibuf.getSize();
869 ibuf.dealloc();
870 ibuf.alloc(3 * s / 2);
871 }
872 if (fd >= 0)
873 (void) close(fd);
874 if (klen <= 0)
875 continue;
876
877 if (klen <= gzlen)
878 continue;
879
880 if (opt->force > 0)
881 return klen;
882
883 // some checks
884 if (fd_pos != file_size) {
885 //printf("fd_pos: %ld, file_size: %ld\n", (long)fd_pos, (long)file_size);
886 }
887
888 //head_ok:
889
890 // FIXME: more checks for special magic bytes in ibuf ???
891 // FIXME: more checks for kernel architecture ???
892
893 return klen;
894 }
895
896 return 0;
897 }
898
readKernel()899 void PackVmlinuzARMEL::readKernel()
900 {
901 int klen = decompressKernel();
902 if (klen <= 0)
903 throwCantPack("kernel decompression failed");
904 //OutputFile::dump("kernel.img", ibuf, klen);
905
906 // copy the setup boot code
907 setup_buf.alloc(setup_size);
908 memcpy(setup_buf, obuf, setup_size);
909 //OutputFile::dump("setup.img", setup_buf, setup_size);
910
911 obuf.dealloc();
912 obuf.allocForCompression(klen);
913
914 ph.u_len = klen;
915 ph.filter = 0;
916 }
917
newLinker() const918 Linker* PackVmlinuzARMEL::newLinker() const
919 {
920 return new ElfLinkerArmLE;
921 }
922
923 static const
924 #include "stub/arm.v5a-linux.kernel.vmlinux.h"
925 static const
926 #include "stub/arm.v5a-linux.kernel.vmlinuz-head.h"
927
buildLoader(const Filter * ft)928 void PackVmlinuzARMEL::buildLoader(const Filter *ft)
929 {
930 // prepare loader; same as vmlinux (with 'x')
931 initLoader(stub_arm_v5a_linux_kernel_vmlinux, sizeof(stub_arm_v5a_linux_kernel_vmlinux));
932 addLoader("LINUX000", NULL);
933 if (ft->id) {
934 assert(ft->calls > 0);
935 addLoader("LINUX010", NULL);
936 }
937 addLoader("LINUX020", NULL);
938 if (ft->id) {
939 addFilter32(ft->id);
940 }
941 addLoader("LINUX030", NULL);
942 if (ph.method == M_NRV2E_8) addLoader("NRV2E", NULL);
943 else if (ph.method == M_NRV2B_8) addLoader("NRV2B", NULL);
944 else if (ph.method == M_NRV2D_8) addLoader("NRV2D", NULL);
945 else if (M_IS_LZMA(ph.method)) addLoader("LZMA_ELF00",
946 (opt->small ? "LZMA_DEC10" : "LZMA_DEC20"), "LZMA_DEC30", NULL);
947 else throwBadLoader();
948 addLoader("IDENTSTR,UPX1HEAD", NULL);
949
950 // To debug (2008-09-14):
951 // Build gdb-6.8-21.fc9.src.rpm; ./configure --target=arm-none-elf; make
952 // Contains the fix for http://bugzilla.redhat.com/show_bug.cgi?id=436037
953 // Install qemu-0.9.1-6.fc9.i386.rpm
954 // qemu-system-arm -s -S -kernel <file> -nographic
955 // (gdb) target remote localhost:1234
956 // A very small boot loader runs at pc=0x0; the kernel is at 0x10000 (64KiB).
957 }
958
defineDecompressorSymbols()959 void PackVmlinuzARMEL::defineDecompressorSymbols()
960 {
961 super::defineDecompressorSymbols();
962 linker->defineSymbol( "COMPRESSED_LENGTH", ph.c_len);
963 linker->defineSymbol("UNCOMPRESSED_LENGTH", ph.u_len);
964 linker->defineSymbol("METHOD", ph.method);
965 }
966
write_vmlinuz_head(OutputFile * fo)967 unsigned PackVmlinuzARMEL::write_vmlinuz_head(OutputFile *fo)
968 { // First word from vmlinuz-head.S
969 fo->write(&stub_arm_v5a_linux_kernel_vmlinuz_head[0], 4);
970
971 // Second word
972 upx_uint32_t tmp_u32;
973 unsigned const t = (0xff000000 &
974 get_te32(&stub_arm_v5a_linux_kernel_vmlinuz_head[4]))
975 | (0x00ffffff & (0u - 1 + ((3+ ph.c_len)>>2)));
976 set_te32(&tmp_u32, t);
977 fo->write(&tmp_u32, 4);
978
979 return sizeof(stub_arm_v5a_linux_kernel_vmlinuz_head);
980 }
981
pack(OutputFile * fo)982 void PackVmlinuzARMEL::pack(OutputFile *fo)
983 {
984 readKernel();
985
986 // prepare filter
987 Filter ft(ph.level);
988 ft.buf_len = ph.u_len;
989 ft.addvalue = 0;
990
991 // compress
992 upx_compress_config_t cconf; cconf.reset();
993 // limit stack size needed for runtime decompression
994 cconf.conf_lzma.max_num_probs = 1846 + (768 << 5); // ushort: 52,844 byte stack
995 compressWithFilters(&ft, 512, &cconf, getStrategy(ft));
996
997 const unsigned lsize = getLoaderSize();
998
999 defineDecompressorSymbols();
1000 defineFilterSymbols(&ft);
1001 relocateLoader();
1002
1003 MemBuffer loader(lsize);
1004 memcpy(loader, getLoader(), lsize);
1005 patchPackHeader(loader, lsize);
1006
1007 // boot_sect_t * const bs = (boot_sect_t *) ((unsigned char *) setup_buf);
1008 // bs->sys_size = ALIGN_UP(lsize + ph.c_len, 16u) / 16;
1009 // bs->payload_length = ph.c_len;
1010
1011 fo->write(setup_buf, setup_buf.getSize());
1012 write_vmlinuz_head(fo);
1013 fo->write(obuf, ph.c_len);
1014 unsigned const zero = 0;
1015 fo->write((void const *)&zero, 3u & (0u - ph.c_len));
1016 fo->write(loader, lsize);
1017 #if 0
1018 printf("%-13s: setup : %8ld bytes\n", getName(), (long) setup_buf.getSize());
1019 printf("%-13s: loader : %8ld bytes\n", getName(), (long) lsize);
1020 printf("%-13s: compressed : %8ld bytes\n", getName(), (long) ph.c_len);
1021 #endif
1022
1023 // verify
1024 verifyOverlappingDecompression();
1025
1026 // finally check the compression ratio
1027 if (!checkFinalCompressionRatio(fo))
1028 throwNotCompressible();
1029 }
1030
canUnpack()1031 int PackVmlinuzARMEL::canUnpack()
1032 {
1033 if (readFileHeader() != getFormat())
1034 return false;
1035 fi->seek(setup_size, SEEK_SET);
1036 return readPackHeader(1024) ? 1 : -1;
1037 }
1038
unpack(OutputFile * fo)1039 void PackVmlinuzARMEL::unpack(OutputFile *fo)
1040 {
1041 // no uncompression support for this format, so that
1042 // it is possible to remove the original deflate code (>10 KiB)
1043
1044 // FIXME: but we could write the uncompressed "vmlinux" image
1045
1046 ibuf.alloc(ph.c_len);
1047 obuf.allocForUncompression(ph.u_len);
1048
1049 fi->seek(setup_size + ph.buf_offset + ph.getPackHeaderSize(), SEEK_SET);
1050 fi->readx(ibuf, ph.c_len);
1051
1052 // decompress
1053 decompress(ibuf, obuf);
1054
1055 // unfilter
1056 Filter ft(ph.level);
1057 ft.init(ph.filter, 0);
1058 ft.cto = (unsigned char) ph.filter_cto;
1059 ft.unfilter(obuf, ph.u_len);
1060
1061 // write decompressed file
1062 if (fo)
1063 {
1064 throwCantUnpack("build a new kernel instead :-)");
1065 //fo->write(obuf, ph.u_len);
1066 }
1067 }
1068
1069 /* vim:set ts=4 sw=4 et: */
1070