From 1418f57b9670aba88b6a79a5e8310320f2d94c54 Mon Sep 17 00:00:00 2001 From: John Reiser Date: Fri, 17 Dec 2004 23:42:16 +0000 Subject: [PATCH] Fix bugs between Filter.addvalue and ckt32 macro call: [re-]compressed Linux kernels were not booting. Explain .addvalue better. Set kernel stack pointer for boot-time decompression according to Linux kernel documentation. p_vmlinx.cpp p_vmlinz.cpp stub/l_vmlinx.asm stub/l_vmlinz.asm filter/cto.h filter/ctoj.h filter/ctok.h committer: jreiser 1103326936 +0000 --- src/filter/cto.h | 9 ++++++++- src/filter/ctoj.h | 4 +++- src/filter/ctok.h | 4 +++- src/p_vmlinx.cpp | 19 ++++++++++++++++--- src/p_vmlinz.cpp | 11 ++++++----- src/stub/l_vmlinx.asm | 9 ++++++++- src/stub/l_vmlinz.asm | 20 ++++++++++++++------ 7 files changed, 58 insertions(+), 18 deletions(-) diff --git a/src/filter/cto.h b/src/filter/cto.h index edfe1c63..9ac9c7d3 100644 --- a/src/filter/cto.h +++ b/src/filter/cto.h @@ -36,11 +36,11 @@ static int F(Filter *f) #ifdef U // filter upx_byte *b = f->buf; - const unsigned addvalue = f->addvalue; #else // scan const upx_byte *b = f->buf; #endif + const unsigned addvalue = f->addvalue; const unsigned size = f->buf_len; unsigned ic, jc, kc; @@ -52,6 +52,11 @@ static int F(Filter *f) unsigned char buf[256]; memset(buf,0,256); + // A call to a destination that is inside the buffer + // will be rewritten and marked with cto8 as first byte. + // So, a call to a destination that is outside the buffer + // must not conflict with the mark. + // Note that unsigned comparison checks both edges of buffer. for (ic = 0; ic < size - 5; ic++) if (COND(b,ic) && get_le32(b+ic+1)+ic+1 >= size) { @@ -74,6 +79,8 @@ static int F(Filter *f) // try to detect 'real' calls only if (jc < size) { + if ((1u<<24)<=(jc+addvalue)) // hi 8 bits won't be cto8 + return 1; // fail - buffer not restored #ifdef U set_be32(b+ic+1,jc+addvalue+cto); #endif diff --git a/src/filter/ctoj.h b/src/filter/ctoj.h index 9d291e62..b87fc6b1 100644 --- a/src/filter/ctoj.h +++ b/src/filter/ctoj.h @@ -40,11 +40,11 @@ static int F(Filter *f) #ifdef U // filter upx_byte *b = f->buf; - const unsigned addvalue = f->addvalue; #else // scan const upx_byte *b = f->buf; #endif + const unsigned addvalue = f->addvalue; const unsigned size = f->buf_len; unsigned ic, jc, kc; @@ -78,6 +78,8 @@ static int F(Filter *f) // try to detect 'real' calls only if (jc < size) { + if ((1u<<24)<=(jc+addvalue)) // hi 8 bits won't be cto8 + return 1; // fail - buffer not restored #ifdef U set_be32(b+ic+1,jc+addvalue+cto); #endif diff --git a/src/filter/ctok.h b/src/filter/ctok.h index e6991cfa..eff9bdfc 100644 --- a/src/filter/ctok.h +++ b/src/filter/ctok.h @@ -40,11 +40,11 @@ static int F(Filter *f) #ifdef U // filter upx_byte *b = f->buf; - const unsigned addvalue = f->addvalue; #else // scan const upx_byte *b = f->buf; #endif + const unsigned addvalue = f->addvalue; const unsigned size = f->buf_len; unsigned const id = f->id; @@ -79,6 +79,8 @@ static int F(Filter *f) // try to detect 'real' calls only if (jc < size) { + if ((1u<<24)<=(jc+addvalue)) // hi 8 bits won't be cto8 + return 1; // fail - buffer not restored #ifdef U set_be32(b+ic+1,jc+addvalue+cto); #endif diff --git a/src/p_vmlinx.cpp b/src/p_vmlinx.cpp index d1694ec9..028d5679 100644 --- a/src/p_vmlinx.cpp +++ b/src/p_vmlinx.cpp @@ -217,7 +217,7 @@ void PackVmlinuxI386::pack(OutputFile *fo) ph.filter = 0; Filter ft(ph.level); ft.buf_len = ph.u_len; - ft.addvalue = 0; + ft.addvalue = 0; // we are independent of actual runtime address; see ckt32 compressWithFilters(&ft, 1 << 20); @@ -243,6 +243,10 @@ void PackVmlinuxI386::pack(OutputFile *fo) fo->write(obuf, ph.c_len); fo_off += ph.c_len; fo->write(loader, lsize); fo_off += lsize; +#if 0 + printf("%-13s: compressed : %8u bytes\n", getName(), ph.c_len); + printf("%-13s: decompressor : %8u bytes\n", getName(), lsize); +#endif verifyOverlappingDecompression(); // .note with 1st page -------------------------------- @@ -490,8 +494,17 @@ void PackVmlinuxI386::unpack(OutputFile *fo) // cli # but if it matters, then there is a race! // // movl $ __BOOT_DS,%eax -// movl %eax,%ss; movl $0x99000,%esp # 2.6.7 setup had ss:sp of 9000:8ffe -// /* Avoid EBDA (Extended BIOS Data Area) below 0xA0000. */ +// movl %eax,%ss; leal 0x9000(%esi),%esp # 0x99000 typical +// /* Linux Documentation/i386/boot.txt "SAMPLE BOOT CONFIGURATION" says +// 0x8000-0x8FFF Stack and heap [inside the "real mode segment", +// just below the command line at offset 0x9000]. +// +// arch/i386/boot/compressed/head.S "Do the decompression ..." says +// %esi contains the "real mode pointer" [as a 32-bit addr]. +// +// In any case, avoid EBDA (Extended BIOS Data Area) below 0xA0000. +// boot.txt says 0x9A000 is the limit. LILO goes up to 0x9B000. +// */ // // pushl $0; popf # subsumes "cli; cld"; also clears NT for buggy BIOS // diff --git a/src/p_vmlinz.cpp b/src/p_vmlinz.cpp index a5083ee1..af8df745 100644 --- a/src/p_vmlinz.cpp +++ b/src/p_vmlinz.cpp @@ -38,7 +38,8 @@ static const #include "stub/l_vmlinz.h" static const unsigned kernel_entry = 0x100000; -static const unsigned stack_during_uncompression = 0x90000; +static const unsigned stack_offset_during_uncompression = 0x9000; +// add to "real mode pointer" in %esi; total 0x99000 is typical // from /usr/src/linux/arch/i386/boot/compressed/Makefile static const unsigned zimage_offset = 0x1000; @@ -273,7 +274,7 @@ void PackVmlinuzI386::pack(OutputFile *fo) // prepare filter Filter ft(ph.level); ft.buf_len = ph.u_len; - ft.addvalue = kernel_entry; + ft.addvalue = kernel_entry; // saves 4 bytes in unfilter code // compress compressWithFilters(&ft, 1 << 20); @@ -285,7 +286,7 @@ void PackVmlinuzI386::pack(OutputFile *fo) patchFilter32(loader, lsize, &ft); patch_le32(loader, lsize, "ESI1", zimage_offset + lsize); patch_le32(loader, lsize, "KEIP", kernel_entry); - patch_le32(loader, lsize, "STAK", stack_during_uncompression); + patch_le32(loader, lsize, "STAK", stack_offset_during_uncompression); boot_sect_t * const bs = (boot_sect_t *) ((unsigned char *) setup_buf); bs->sys_size = ALIGN_UP(lsize + ph.c_len, 16) / 16; @@ -348,7 +349,7 @@ void PackBvmlinuzI386::pack(OutputFile *fo) // prepare filter Filter ft(ph.level); ft.buf_len = ph.u_len; - ft.addvalue = kernel_entry; + ft.addvalue = kernel_entry; // saves 4 bytes in unfilter code // compress compressWithFilters(&ft, 512); @@ -385,7 +386,7 @@ void PackBvmlinuzI386::pack(OutputFile *fo) patch_le32(loader, e_len, "ULEN", ph.u_len); } patch_le32(loader, e_len, "KEIP", kernel_entry); - patch_le32(loader, e_len, "STAK", stack_during_uncompression); + patch_le32(loader, e_len, "STAK", stack_offset_during_uncompression); boot_sect_t * const bs = (boot_sect_t *) ((unsigned char *) setup_buf); bs->sys_size = (ALIGN_UP(lsize + clen, 16) / 16) & 0xffff; diff --git a/src/stub/l_vmlinx.asm b/src/stub/l_vmlinx.asm index 273c0b60..d7a27f72 100644 --- a/src/stub/l_vmlinx.asm +++ b/src/stub/l_vmlinx.asm @@ -79,7 +79,14 @@ start: %ifdef __LXCKLLT9__ pop edx ; MATCH04 cto pop edi ; MATCH03 src - ckt32 edi, dl + + ckt32 edi, dl ; dl has cto8 + ;edi: adjust for the difference between 0 origin of buffer at filter, + ;and actual origin of destination at unfilter. + ;Filter.addvalue is 0: destination origin is unknown at filter time. + ;The input data is still relocatable, and address is assigned later + ;[as of 2004-12-15 it is 'always' 0x100000]. + %endif; __LXDUMMY2__ %ifdef __LXCALLT9__ pop edi ; MATCH03 src diff --git a/src/stub/l_vmlinz.asm b/src/stub/l_vmlinz.asm index 82399113..c38ddac2 100644 --- a/src/stub/l_vmlinz.asm +++ b/src/stub/l_vmlinz.asm @@ -35,9 +35,9 @@ ORG 0 ; gdt segment 3 is flat data -%define __KERNEL_DS 3*8 +%define __BOOT_DS 3*8 ; gdt segment 2 is flat code -%define __KERNEL_CS 2*8 +%define __BOOT_CS 2*8 ; ============= ; ============= ENTRY POINT @@ -47,12 +47,12 @@ start: ; __LINUZ000__ cli xor eax, eax - mov al, __KERNEL_DS + mov al, __BOOT_DS mov ds, eax mov es, eax ; fs, gs set by startup_32 in arch/i386/kernel/head.S mov ss, eax - mov esp, 'STAK' ; 0x90000 + lea esp, ['STAK' + esi] ; (0x9000 + 0x90000) typical push byte 0 popf ; BIOS can leave random flags (such as NT) @@ -62,7 +62,7 @@ start: or ebp, byte -1 ; decompressor assumption mov eax, 'KEIP' ; 0x100000 : address of startup_32 - push byte __KERNEL_CS ; MATCH00 + push byte __BOOT_CS ; MATCH00 push eax ; MATCH00 entry address push edi ; MATCH01 save push esi ; MATCH02 save @@ -111,6 +111,7 @@ checka20: %include "n2b_d32.ash" %include "n2d_d32.ash" %include "n2e_d32.ash" +;;%include "cl1_d32.ash" ; ============= ; ============= UNFILTER @@ -120,7 +121,14 @@ checka20: pop ecx ; MATCH05 len pop edx ; MATCH04 cto pop edi ; MATCH03 src - ckt32 edi, dl + + ckt32 0, dl ; dl has cto8 + ;0: Filter.addvalue = kernel_entry already did the 'add' at filter time + ;[the runtime address of the destination was known], so we save 4 bytes + ;(plus 1 cycle per instance) by not doing the 'add' when unfiltering. + ;If .addvalue was 0, then use 'edi' instead of 0 in call to ckt32, + ;to compensate for difference in origin of buffer. + %endif; __LZDUMMY2__ %ifdef __LZCALLT9__ pop edi ; MATCH03 src