From: Eric Biggers Date: Thu, 26 Dec 2013 02:57:17 +0000 (-0600) Subject: Share LZMS x86 filter between compressor and decompressor X-Git-Tag: v1.6.0~94 X-Git-Url: https://wimlib.net/git/?p=wimlib;a=commitdiff_plain;h=dabaf1184dfd9581804da2a14fa3617ef61a3e06 Share LZMS x86 filter between compressor and decompressor --- diff --git a/Makefile.am b/Makefile.am index dbbdff64..2602100f 100644 --- a/Makefile.am +++ b/Makefile.am @@ -38,6 +38,7 @@ libwim_la_SOURCES = \ src/integrity.c \ src/join.c \ src/lookup_table.c \ + src/lzms-common.c \ src/lzms-compress.c \ src/lzms-decompress.c \ src/lz77.c \ diff --git a/include/wimlib/lzms.h b/include/wimlib/lzms.h index de965fe7..a18c526e 100644 --- a/include/wimlib/lzms.h +++ b/include/wimlib/lzms.h @@ -45,4 +45,9 @@ #define LZMS_X86_MAX_GOOD_TARGET_OFFSET 65535 #define LZMS_X86_MAX_TRANSLATION_OFFSET 1023 +#include + +extern void +lzms_x86_filter(u8 data[], s32 size, s32 last_target_usages[], bool undo); + #endif /* _WIMLIB_LZMS_H */ diff --git a/src/lzms-common.c b/src/lzms-common.c new file mode 100644 index 00000000..946c655e --- /dev/null +++ b/src/lzms-common.c @@ -0,0 +1,160 @@ +/* + * lzms-common.c + * + * Code shared between the compressor and decompressor for the LZMS compression + * format. + */ + +/* + * Copyright (C) 2013 Eric Biggers + * + * This file is part of wimlib, a library for working with WIM files. + * + * wimlib is free software; you can redistribute it and/or modify it under the + * terms of the GNU General Public License as published by the Free + * Software Foundation; either version 3 of the License, or (at your option) + * any later version. + * + * wimlib is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + * A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License + * along with wimlib; if not, see http://www.gnu.org/licenses/. + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include "wimlib/lzms.h" +#include "wimlib/endianness.h" + +static s32 +lzms_maybe_do_x86_translation(u8 data[], s32 i, s32 num_op_bytes, + s32 *closest_target_usage_p, + s32 last_target_usages[], s32 max_trans_offset, + bool undo) +{ + u16 pos; + + if (undo) { + if (i - *closest_target_usage_p <= max_trans_offset) { + LZMS_DEBUG("Undid x86 translation at position %d " + "(opcode 0x%02x)", i, data[i]); + le32 *p32 = (le32*)&data[i + num_op_bytes]; + u32 n = le32_to_cpu(*p32); + *p32 = cpu_to_le32(n - i); + } + pos = i + le16_to_cpu(*(const le16*)&data[i + num_op_bytes]); + } else { + pos = i + le16_to_cpu(*(const le16*)&data[i + num_op_bytes]); + + if (i - *closest_target_usage_p <= max_trans_offset) { + LZMS_DEBUG("Did x86 translation at position %d " + "(opcode 0x%02x)", i, data[i]); + le32 *p32 = (le32*)&data[i + num_op_bytes]; + u32 n = le32_to_cpu(*p32); + *p32 = cpu_to_le32(n + i); + } + } + + i += num_op_bytes + sizeof(le32) - 1; + + if (i - last_target_usages[pos] <= LZMS_X86_MAX_GOOD_TARGET_OFFSET) + *closest_target_usage_p = i; + + last_target_usages[pos] = i; + + return i + 1; +} + +static s32 +lzms_may_x86_translate(const u8 p[], s32 *max_offset_ret) +{ + /* Switch on first byte of the opcode, assuming it is really an x86 + * instruction. */ + *max_offset_ret = LZMS_X86_MAX_TRANSLATION_OFFSET; + switch (p[0]) { + case 0x48: + if (p[1] == 0x8b) { + if (p[2] == 0x5 || p[2] == 0xd) { + /* Load relative (x86_64) */ + return 3; + } + } else if (p[1] == 0x8d) { + if ((p[2] & 0x7) == 0x5) { + /* Load effective address relative (x86_64) */ + return 3; + } + } + break; + + case 0x4c: + if (p[1] == 0x8d) { + if ((p[2] & 0x7) == 0x5) { + /* Load effective address relative (x86_64) */ + return 3; + } + } + break; + + case 0xe8: + /* Call relative */ + *max_offset_ret = LZMS_X86_MAX_TRANSLATION_OFFSET / 2; + return 1; + + case 0xe9: + /* Jump relative */ + *max_offset_ret = 0; + return 5; + + case 0xf0: + if (p[1] == 0x83 && p[2] == 0x05) { + /* Lock add relative */ + return 3; + } + break; + + case 0xff: + if (p[1] == 0x15) { + /* Call indirect */ + return 2; + } + break; + } + *max_offset_ret = 0; + return 1; +} + +/* + * Translate relative addresses embedded in x86 instructions into absolute + * addresses (@undo == %false), or undo this translation (@undo == %true). + * + * @last_target_usages is a temporary array of length >= 65536. + */ +void +lzms_x86_filter(u8 data[], s32 size, s32 last_target_usages[], bool undo) +{ + s32 closest_target_usage = -LZMS_X86_MAX_TRANSLATION_OFFSET - 1; + + for (s32 i = 0; i < 65536; i++) + last_target_usages[i] = -LZMS_X86_MAX_GOOD_TARGET_OFFSET - 1; + + for (s32 i = 0; i < size - 11; ) { + s32 max_trans_offset; + s32 n; + + n = lzms_may_x86_translate(data + i, &max_trans_offset); + if (max_trans_offset) { + i = lzms_maybe_do_x86_translation(data, i, n, + &closest_target_usage, + last_target_usages, + max_trans_offset, + undo); + } else { + i += n; + } + } +} diff --git a/src/lzms-compress.c b/src/lzms-compress.c index e5abf24a..3c34b52c 100644 --- a/src/lzms-compress.c +++ b/src/lzms-compress.c @@ -34,6 +34,7 @@ #include "wimlib/assert.h" #include "wimlib/compressor_ops.h" #include "wimlib/compress_common.h" +#include "wimlib/endianness.h" #include "wimlib/error.h" #include "wimlib/lzms.h" #include "wimlib/util.h" @@ -44,17 +45,9 @@ struct lzms_compressor { u8 *window; u32 window_size; u32 max_block_size; - s32 *last_target_usages; }; -static void -lzms_preprocess_data(u8 *data, s32 size, s32 *last_target_usages) -{ - for (s32 i = 0; i < size - 11; i++) { - } -} - static size_t lzms_compress(const void *uncompressed_data, size_t uncompressed_size, void *compressed_data, size_t compressed_size_avail, void *_ctx) @@ -71,8 +64,8 @@ lzms_compress(const void *uncompressed_data, size_t uncompressed_size, memcpy(ctx->window, uncompressed_data, uncompressed_size); ctx->window_size = uncompressed_size; - lzms_preprocess_data(ctx->window, ctx->window_size, - ctx->last_target_usages); + lzms_x86_filter(ctx->window, ctx->window_size, + ctx->last_target_usages, false); return 0; } diff --git a/src/lzms-decompress.c b/src/lzms-decompress.c index 40d35bf6..4d87e658 100644 --- a/src/lzms-decompress.c +++ b/src/lzms-decompress.c @@ -1112,132 +1112,6 @@ lzms_decode_items(const u8 *cdata, size_t clen, u8 *ubuf, size_t ulen, return 0; } -static s32 -lzms_try_x86_translation(u8 *ubuf, s32 i, s32 num_op_bytes, - s32 *closest_target_usage_p, s32 last_target_usages[], - s32 max_trans_offset) -{ - u16 pos; - - if (i - *closest_target_usage_p <= max_trans_offset) { - LZMS_DEBUG("Performed x86 translation at position %d " - "(opcode 0x%02x)", i, ubuf[i]); - le32 *p32 = (le32*)&ubuf[i + num_op_bytes]; - u32 n = le32_to_cpu(*p32); - *p32 = cpu_to_le32(n - i); - } - - pos = i + le16_to_cpu(*(const le16*)&ubuf[i + num_op_bytes]); - - i += num_op_bytes + sizeof(le32) - 1; - - if (i - last_target_usages[pos] <= LZMS_X86_MAX_GOOD_TARGET_OFFSET) - *closest_target_usage_p = i; - - last_target_usages[pos] = i; - - return i + 1; -} - -static s32 -lzms_process_x86_translation(u8 *ubuf, s32 i, s32 *closest_target_usage_p, - s32 last_target_usages[]) -{ - /* Switch on first byte of the opcode, assuming it is really an x86 - * instruction. */ - switch (ubuf[i]) { - case 0x48: - if (ubuf[i + 1] == 0x8b) { - if (ubuf[i + 2] == 0x5 || ubuf[i + 2] == 0xd) { - /* Load relative (x86_64) */ - return lzms_try_x86_translation(ubuf, i, 3, - closest_target_usage_p, - last_target_usages, - LZMS_X86_MAX_TRANSLATION_OFFSET); - } - } else if (ubuf[i + 1] == 0x8d) { - if ((ubuf[i + 2] & 0x7) == 0x5) { - /* Load effective address relative (x86_64) */ - return lzms_try_x86_translation(ubuf, i, 3, - closest_target_usage_p, - last_target_usages, - LZMS_X86_MAX_TRANSLATION_OFFSET); - } - } - break; - - case 0x4c: - if (ubuf[i + 1] == 0x8d) { - if ((ubuf[i + 2] & 0x7) == 0x5) { - /* Load effective address relative (x86_64) */ - return lzms_try_x86_translation(ubuf, i, 3, - closest_target_usage_p, - last_target_usages, - LZMS_X86_MAX_TRANSLATION_OFFSET); - } - } - break; - - case 0xe8: - /* Call relative */ - return lzms_try_x86_translation(ubuf, i, 1, closest_target_usage_p, - last_target_usages, - LZMS_X86_MAX_TRANSLATION_OFFSET / 2); - - case 0xe9: - /* Jump relative */ - return i + 5; - - case 0xf0: - if (ubuf[i + 1] == 0x83 && ubuf[i + 2] == 0x05) { - /* Lock add relative */ - return lzms_try_x86_translation(ubuf, i, 3, - closest_target_usage_p, - last_target_usages, - LZMS_X86_MAX_TRANSLATION_OFFSET); - } - break; - - case 0xff: - if (ubuf[i + 1] == 0x15) { - /* Call indirect */ - return lzms_try_x86_translation(ubuf, i, 2, - closest_target_usage_p, - last_target_usages, - LZMS_X86_MAX_TRANSLATION_OFFSET); - } - break; - } - return i + 1; -} - -/* Postprocess the uncompressed data by undoing the translation of relative - * addresses embedded in x86 instructions into absolute addresses. - * - * There does not appear to be any way to check to see if this postprocessing - * actually needs to be done (or to plug in alternate filters, like in LZMA), - * and the corresponding preprocessing seems to be done unconditionally. */ -static void -lzms_postprocess_data(u8 *ubuf, s32 ulen, s32 *last_target_usages) -{ - /* Offset (from beginning of buffer) of the most recent reference to a - * seemingly valid target address. */ - s32 closest_target_usage = -LZMS_X86_MAX_TRANSLATION_OFFSET - 1; - - /* Initialize the last_target_usages array. Each entry will contain the - * offset (from beginning of buffer) of the most recently used target - * address beginning with two bytes equal to the array index. */ - for (s32 i = 0; i < 65536; i++) - last_target_usages[i] = -LZMS_X86_MAX_GOOD_TARGET_OFFSET - 1; - - /* Check each byte in the buffer for an x86 opcode for which a - * translation may be possible. No translations are done on any - * instructions starting in the last 11 bytes of the buffer. */ - for (s32 i = 0; i < ulen - 11; ) - i = lzms_process_x86_translation(ubuf, i, &closest_target_usage, - last_target_usages); -} - static int lzms_decompress(const void *compressed_data, size_t compressed_size, void *uncompressed_data, size_t uncompressed_size, void *_ctx) @@ -1281,8 +1155,8 @@ lzms_decompress(const void *compressed_data, size_t compressed_size, return -1; /* Postprocess the data. */ - lzms_postprocess_data(uncompressed_data, uncompressed_size, - ctx->last_target_usages); + lzms_x86_filter(uncompressed_data, uncompressed_size, + ctx->last_target_usages, true); LZMS_DEBUG("Decompression successful."); return 0;