kolibrios/programs/other/kpack/kerpack_linux/kpack64.asm
CleverMouse cd74d1af33 switch build system to Tup
git-svn-id: svn://kolibrios.org@5098 a494cfbc-eb01-0410-851d-a64ba20cac60
2014-09-12 15:15:23 +00:00

658 lines
13 KiB
NASM

; kpack = Kolibri Packer
; Written by diamond in 2006 specially for KolibriOS
; Uses LZMA compression library by Igor Pavlov
; (for more information on LZMA and 7-Zip visit http://www.7-zip.org)
; (plain-C packer and ASM unpacker are ported by diamond)
;freebsd = 1 ;uncomment for FreeBSD-specific changes
format ELF64
public _start
.data? fix section ".bss" writeable align 4
.data fix section ".data" writeable align 4
.const fix section ".const" align 4
.code fix section ".text" executable align 16
offset fix
ptr fix
struc label type {
label . type }
extrn lzma_compress
extrn lzma_set_dict_size
.data?
infilename dq ?
outfilename dq ?
infile dq ?
outfile1 dq ?
outfile2 dq ?
outfile dq ?
outfilebest dq ?
workmem dq ?
insize dd ?
outsize dd ?
lzma_dictsize dd ?
dd ?
strucstat rq 18
if defined freebsd
st_atime_offset = 24
st_mtime_offset = 40
st_birthtime_offset = 104
st_size_offset = 72
else
st_atime_offset = 72
st_mtime_offset = 88
;st_birthtime_offset not defined
st_size_offset = 48
end if
timeval rq 2*2
public environ
environ dq ?
public __progname
__progname dq ?
ct1 db 256 dup (?)
ctn dd ?
cti db ?
.const
info_str db 'KPack - Kolibri Packer, version 0.11',13,10
db 'Uses LZMA v4.32 compression library',13,10,13,10
info_len = $ - offset info_str
usage_str db 'Written by diamond in 2006, 2007 specially for KolibriOS',13,10
db 'LZMA compression library is copyright (c) 1999-2005 by Igor Pavlov',13,10
db 13,10
db 'Usage: kpack [--nologo] <infile> [<outfile>]',13,10
usage_len = $ - offset usage_str
errload_str db 'Cannot load input file',13,10
errload_len = $ - offset errload_str
outfileerr_str db 'Cannot save output file',13,10
outfileerr_len = $ - offset outfileerr_str
nomem_str db 'No memory',13,10
nomem_len = $ - offset nomem_str
too_big_str db 'failed, output is greater than input.',13,10
too_big_len = $ - too_big_str
compressing_str db 'Compressing ... '
compressing_len = $ - compressing_str
.data
bNoLogo db 0
done_str db 'OK! Compression ratio: '
ratio dw '00'
db '%',13,10,13,10
done_len = $ - done_str
use_lzma = 1
use_no_calltrick = 0
use_calltrick1 = 40h
use_calltrick2 = 80h
method db 1
.code
; Write string from [rsi] of rdx bytes.
write_string:
; 1. Align stack on 16 bytes.
push rdi
; 2. Set rdi to 1 = descriptor for stdout.
xor edi, edi
inc edi
; 3. Do system call.
call write
; 4. Restore stack and return.
pop rdi
ret
; Write string from [rsi] of rdx bytes and exit. Note that main code jumps (not calls) here,
; so we should not align the stack.
write_exit:
; 1. Call prev func.
call write_string
; 2. Do system call for exit.
; Note that this can be used as independent proc jumped (not called) to.
doexit:
xor edi, edi
call exit
; Main procedure.
_start:
; 1. Parse command line.
; Linux: [rsp] = argc, rsp+8 = argv
; FreeBSD: [rdi] = argc, rdi+8 = argv
; 1a. Load argc and argv to registers,
; skip first argument (which is always program name)
if defined freebsd
mov ecx, [rdi] ; ecx = argc
add rdi, 16 ; rdi = &argv[1]
else
mov ecx, [rsp] ; ecx = argc
lea rdi, [rsp+16] ; rdi = &argv[1]
end if
; 1b. Test for first filename parameter. If no, goto step 2.
call get_file_name
jz usage
; 1c. We got input file name, save it.
; Assume that output file name is the same; if no, we will rewrite it in step 1d.
mov [infilename], rax
mov [outfilename], rax
; 1d. Test for second filename parameter. If yes, rewrite assumption in step 1c and check that there are no 3rd parameter.
call get_file_name
jz @f
mov [outfilename], rax
call get_file_name
jnz usage
@@:
; 1e. Parsing is done, process to step 3.
jmp short cont
; 2. No arguments or too many arguments given; write message and exit.
usage:
cmp [bNoLogo], 0
jnz doexit
push info_len + usage_len
pop rdx
mov rsi, offset info_str;usage_str
jmp write_exit
; 3. Say hello unless disabled with --nologo.
cont:
cmp [bNoLogo], 0
jnz @f
push info_len
pop rdx
mov rsi, info_str
call write_string
@@:
; 4. Load the input file.
; 4a. Do system call for stat - get file times and file size.
mov rdi, [infilename]
mov rsi, offset strucstat
mov r13, rsi
call stat
; 4b. Test result; if not 0 (0 is OK), goto 4e.
test rax, rax
jnz short infileerr
; 4c. Do system call for open.
mov rdi, [infilename]
mov rsi, offset open_mode
call fopen
; 4d. Test result; if not NULL, goto 4f.
test rax, rax
jnz short inopened
infileerr:
; 4e. Say error and abort.
push errload_len
pop rdx
mov rsi, offset errload_str
jmp write_exit
inopened:
mov r12, rax
; 4f. Check that the size is nonzero and less than 4G.
mov edi, [r13+st_size_offset]
test edi, edi
jz short infileerr
cmp dword [r13+st_size_offset+4], 0
jnz short infileerr
; 4g. Allocate memory for the input file.
mov [insize], edi
call malloc
test rax, rax
jz nomem
mov [infile], rax
; 4g. Read the input file to the allocated memory.
mov rdi, rax
push 1
pop rsi
mov edx, [r13+st_size_offset]
mov rcx, r12
call fread
; 4h. Test result; must be equal to file size.
cmp eax, [r13+st_size_offset]
jnz infileerr
; 4i. Close the input file.
mov rdi, r12
call fclose
; 5. Calculate maximum size of the output.
mov edi, [insize]
shr edi, 3
add edi, [insize]
add edi, 400h ; should be enough for header
mov r12d, edi
; 6. Allocate memory for two copies of maximum output.
; 6a. Do system call.
add edi, edi
call malloc
; 6b. Test return value. If ok, goto 6d.
test rax, rax
jnz short outmemok
; 6c. No memory; say error and exit.
nomem:
push nomem_len
pop rdx
mov rsi, offset nomem_str
jmp write_exit
; 6d. Remember allocated memory address.
outmemok:
mov [outfile], rax
mov [outfile1], rax
mov [outfilebest], rax
add rax, r12
mov [outfile2], rax
sub rax, r12
; 7. Initialize KPCK header.
mov dword ptr [rax], 'KPCK'
mov ecx, [insize]
mov dword ptr [rax+4], ecx
; 8. Determine and set lzma_dict_size.
dec ecx
bsr eax, ecx
inc eax
cmp eax, 28
jb short @f
mov eax, 28
@@:
push rax
mov edi, eax
call lzma_set_dict_size
pop rcx
mov edi, 1
shl edi, cl
mov [lzma_dictsize], edi
; 9. Allocate lzma_workmem.
imul edi, 19
shr edi, 1
add edi, 509000h
call malloc
test rax, rax
jz nomem
mov [workmem], rax
; 10. Say another 'hi'.
push compressing_len
pop rdx
mov rsi, offset compressing_str
call write_string
; 11. Do work.
mov rax, [outfile2]
mov [outfile], rax
xchg rax, rdi
mov rsi, [outfile1]
movsd
movsd
call pack_lzma
mov [outsize], eax
mov rax, [outfile]
mov [outfilebest], rax
mov [method], use_lzma
call preprocess_calltrick
test eax, eax
jz short noct1
call set_outfile
call pack_lzma
add eax, 5
cmp eax, [outsize]
jae short @f
mov [outsize], eax
mov rax, [outfile]
mov [outfilebest], rax
mov [method], use_lzma or use_calltrick1
@@:
noct1:
call set_outfile
push qword ptr [ctn]
push qword ptr [cti]
call preprocess_calltrick2
test eax, eax
jz noct2
call set_outfile
call pack_lzma
add eax, 5
cmp eax, [outsize]
jae short @f
mov [outsize], eax
mov rax, [outfile]
mov [outfilebest], rax
mov [method], use_lzma or use_calltrick2
pop rcx
pop rcx
push qword ptr [ctn]
push qword ptr [cti]
@@:
noct2:
pop rax
mov [cti], al
pop rax
mov [ctn], eax
add [outsize], 12
mov eax, [outsize]
cmp eax, [insize]
jb short packed_ok
push too_big_len
pop rdx
mov rsi, offset too_big_str
jmp write_exit
packed_ok:
; 12. Main work is done. Free lzma_workmem.
mov rdi, [workmem]
call free
; 13. Set header
movzx eax, [method]
mov rdi, [outfilebest]
mov [rdi+8], eax
test al, use_calltrick1 or use_calltrick2
jz short @f
mov ecx, [outsize]
add rcx, rdi
mov eax, [ctn]
mov [rcx-5], eax
mov al, [cti]
mov [rcx-1], al
@@:
mov eax, [outsize]
mov ecx, 100
mul ecx
div [insize]
mov cl, 10
div cl
add ax, '00'
mov [ratio], ax
push done_len
pop rdx
cmp [bNoLogo], 0
jz @f
sub dl, 2
@@:
mov rsi, offset done_str
call write_string
; 14. Save the output file.
; 14a. Do system call for open.
mov rdi, [outfilename]
mov rsi, create_mode
call fopen
; 14b. Test for success; if yes, goto 14d.
test rax, rax
jnz short @f
; 14c. Say error and exit.
outerr:
push outfileerr_len
pop rdx
mov rsi, offset outfileerr_str
jmp write_exit
; 14d. Do system call for write.
@@:
mov r12, rax
mov rdi, [outfilebest]
mov esi, [outsize]
push 1
pop rdx
mov rcx, r12
call fwrite
test eax, eax
jz short outerr
; 14e. Close output file.
mov rdi, r12
call fclose
; 14f. Set output file time from the input file.
; Do two system calls, one for birth time, one for modification time.
mov rdi, [outfilename]
mov rsi, timeval
mov rax, [r13+st_atime_offset]
mov [rsi], rax
mov rax, [r13+st_atime_offset+8]
mov [rsi+8], rax
if defined st_birthtime_offset
mov rax, [r13+st_birthtime_offset]
mov [rsi+16], rax
mov rax, [r13+st_birthtime_offset+8]
mov [rsi+24], rax
call utimes
mov rdi, [outfilename]
mov rsi, timeval
end if
mov rax, [r13+st_mtime_offset]
mov [rsi+16], rax
mov rax, [r13+st_mtime_offset+8]
mov [rsi+24], rax
call utimes
; 15. Exit.
xor edi, edi
call exit
; Scan command line, skipping possible options, and return first non-option
; ecx is number of arguments left, rdi points to first new argument (updated by func)
; After the call: ZF set if no arguments left, otherwise rax points to the arg.
get_file_name:
; 1. Test whether there are still arguments. If no, goto 5; note ZF is set.
dec ecx
jz @@end
; 2. Get the new arg, advance rdi (ecx was decreased in step 1).
mov rax, [rdi]
add rdi, 8
; 3. Test for --nologo option. If no, goto 5; note ZF is cleared.
cmp dword [rax], '--no'
jnz @@end
cmp dword [rax+4], 'logo'
jnz @@end
; 4. Remember that --nologo was given and continue from the beginning.
mov [bNoLogo], 1
jmp get_file_name
; 5. No arguments (ZF set) or normal argument (ZF cleared); return.
@@end:
ret
set_outfile:
mov rax, [outfilebest]
xor rax, [outfile1]
xor rax, [outfile2]
mov [outfile], rax
ret
pack_calltrick_fail:
xor eax, eax
xor ebx, ebx
mov [ctn], eax
ret
preprocess_calltrick:
; input preprocessing
push rax
mov edi, [insize]
add edi, edi
call malloc
pop rcx
test rax, rax
jz pack_calltrick_fail
push rax
xor eax, eax
mov rdi, offset ct1
mov ecx, 256/4
push rdi
rep stosd
pop rdi
mov ecx, [insize]
mov rsi, [infile]
xchg eax, edx
pop rax
xchg rax, rbx
push rbx
input_pre:
lodsb
sub al, 0E8h
cmp al, 1
ja short input_pre_cont
cmp ecx, 5
jb short input_pre_done
lodsd
add eax, esi
sub eax, dword ptr [infile]
cmp eax, [insize]
jae short xxx
cmp eax, 1000000h
jae short xxx
sub ecx, 4
bswap eax
mov [rsi-4], eax
inc edx
mov [rbx], rsi
add rbx, 8
jmp short input_pre_cont
xxx: sub rsi, 4
movzx eax, byte ptr [rsi]
mov byte ptr [rax+rdi], 1
input_pre_cont:
loop input_pre
input_pre_done:
mov [ctn], edx
pop rdx
xor eax, eax
mov ecx, 256
repnz scasb
jnz pack_calltrick_fail
not cl
mov [cti], cl
@@:
cmp rbx, rdx
jz @f
sub rbx, 8
mov rax, [rbx]
mov [rax-4], cl
jmp @b
@@:
push rax
mov rdi, rbx
call free
pop rax
ret
pack_lzma:
push rcx
mov rdi, [infile]
mov rsi, [outfile]
add rsi, 11
mov edx, [insize]
mov rcx, [workmem]
call lzma_compress
pop rcx
mov rcx, [outfile]
mov edx, [rcx+12]
bswap edx
mov [rcx+12], edx
dec eax
ret
preprocess_calltrick2:
; restore input
mov rsi, [infile]
mov ecx, [ctn]
jecxz pc2l2
pc2l1:
lodsb
sub al, 0E8h
cmp al, 1
ja short pc2l1
mov al, [cti]
cmp [rsi], al
jnz short pc2l1
lodsd
mov al, 0
bswap eax
sub eax, esi
add eax, dword ptr [infile]
mov [rsi-4], eax
loop pc2l1
pc2l2:
; input preprocessing
push rax
mov edi, [insize]
add edi, edi
call malloc
pop rcx
test rax, rax
jz pack_calltrick_fail
mov rdi, offset ct1
xchg rax, rbx
xor eax, eax
push rdi
mov ecx, 256/4
rep stosd
pop rdi
mov ecx, [insize]
mov rsi, [infile]
xchg eax, edx
push rbx
input_pre2:
lodsb
@@:
cmp al, 0Fh
jnz short ip1
dec ecx
jz short input_pre_done2
lodsb
cmp al, 80h
jb short @b
cmp al, 90h
jb short @f
ip1:
sub al, 0E8h
cmp al, 1
ja short input_pre_cont2
@@:
cmp ecx, 5
jb short input_pre_done2
lodsd
add eax, esi
sub eax, dword ptr [infile]
cmp eax, [insize]
jae short xxx2
cmp eax, 1000000h
jae short xxx2
sub ecx, 4
bswap eax
mov [rsi-4], eax
inc edx
mov [rbx], rsi
add rbx, 8
jmp short input_pre_cont2
xxx2: sub rsi, 4
movzx eax, byte ptr [rsi]
mov byte ptr [rax+rdi], 1
input_pre_cont2:
loop input_pre2
input_pre_done2:
mov [ctn], edx
pop rdx
xor eax, eax
mov ecx, 256
repnz scasb
jnz pack_calltrick_fail
not cl
mov [cti], cl
@@:
cmp rbx, rdx
jz @f
sub rbx, 8
mov rax, [rbx]
mov [rax-4], cl
jmp @b
@@:
push rax
mov rdi, rbx
call free
pop rax
ret
extrn exit
extrn fopen
extrn fread
extrn fwrite
extrn fclose
extrn fseek
extrn ftell
extrn malloc
extrn free
extrn write
extrn utimes
extrn stat
open_mode db "rb",0
create_mode db "wb",0