]> wimlib.net Git - wimlib/blob - src/lzms-common.c
946c655e7afc14ae112a0b0a5981610e42aacc21
[wimlib] / src / lzms-common.c
1 /*
2  * lzms-common.c
3  *
4  * Code shared between the compressor and decompressor for the LZMS compression
5  * format.
6  */
7
8 /*
9  * Copyright (C) 2013 Eric Biggers
10  *
11  * This file is part of wimlib, a library for working with WIM files.
12  *
13  * wimlib is free software; you can redistribute it and/or modify it under the
14  * terms of the GNU General Public License as published by the Free
15  * Software Foundation; either version 3 of the License, or (at your option)
16  * any later version.
17  *
18  * wimlib is distributed in the hope that it will be useful, but WITHOUT ANY
19  * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
20  * A PARTICULAR PURPOSE. See the GNU General Public License for more
21  * details.
22  *
23  * You should have received a copy of the GNU General Public License
24  * along with wimlib; if not, see http://www.gnu.org/licenses/.
25  */
26
27 #ifdef HAVE_CONFIG_H
28 #  include "config.h"
29 #endif
30
31 #include "wimlib/lzms.h"
32 #include "wimlib/endianness.h"
33
34 static s32
35 lzms_maybe_do_x86_translation(u8 data[], s32 i, s32 num_op_bytes,
36                               s32 *closest_target_usage_p,
37                               s32 last_target_usages[], s32 max_trans_offset,
38                               bool undo)
39 {
40         u16 pos;
41
42         if (undo) {
43                 if (i - *closest_target_usage_p <= max_trans_offset) {
44                         LZMS_DEBUG("Undid x86 translation at position %d "
45                                    "(opcode 0x%02x)", i, data[i]);
46                         le32 *p32 = (le32*)&data[i + num_op_bytes];
47                         u32 n = le32_to_cpu(*p32);
48                         *p32 = cpu_to_le32(n - i);
49                 }
50                 pos = i + le16_to_cpu(*(const le16*)&data[i + num_op_bytes]);
51         } else {
52                 pos = i + le16_to_cpu(*(const le16*)&data[i + num_op_bytes]);
53
54                 if (i - *closest_target_usage_p <= max_trans_offset) {
55                         LZMS_DEBUG("Did x86 translation at position %d "
56                                    "(opcode 0x%02x)", i, data[i]);
57                         le32 *p32 = (le32*)&data[i + num_op_bytes];
58                         u32 n = le32_to_cpu(*p32);
59                         *p32 = cpu_to_le32(n + i);
60                 }
61         }
62
63         i += num_op_bytes + sizeof(le32) - 1;
64
65         if (i - last_target_usages[pos] <= LZMS_X86_MAX_GOOD_TARGET_OFFSET)
66                 *closest_target_usage_p = i;
67
68         last_target_usages[pos] = i;
69
70         return i + 1;
71 }
72
73 static s32
74 lzms_may_x86_translate(const u8 p[], s32 *max_offset_ret)
75 {
76         /* Switch on first byte of the opcode, assuming it is really an x86
77          * instruction.  */
78         *max_offset_ret = LZMS_X86_MAX_TRANSLATION_OFFSET;
79         switch (p[0]) {
80         case 0x48:
81                 if (p[1] == 0x8b) {
82                         if (p[2] == 0x5 || p[2] == 0xd) {
83                                 /* Load relative (x86_64)  */
84                                 return 3;
85                         }
86                 } else if (p[1] == 0x8d) {
87                         if ((p[2] & 0x7) == 0x5) {
88                                 /* Load effective address relative (x86_64)  */
89                                 return 3;
90                         }
91                 }
92                 break;
93
94         case 0x4c:
95                 if (p[1] == 0x8d) {
96                         if ((p[2] & 0x7) == 0x5) {
97                                 /* Load effective address relative (x86_64)  */
98                                 return 3;
99                         }
100                 }
101                 break;
102
103         case 0xe8:
104                 /* Call relative  */
105                 *max_offset_ret = LZMS_X86_MAX_TRANSLATION_OFFSET / 2;
106                 return 1;
107
108         case 0xe9:
109                 /* Jump relative  */
110                 *max_offset_ret = 0;
111                 return 5;
112
113         case 0xf0:
114                 if (p[1] == 0x83 && p[2] == 0x05) {
115                         /* Lock add relative  */
116                         return 3;
117                 }
118                 break;
119
120         case 0xff:
121                 if (p[1] == 0x15) {
122                         /* Call indirect  */
123                         return 2;
124                 }
125                 break;
126         }
127         *max_offset_ret = 0;
128         return 1;
129 }
130
131 /*
132  * Translate relative addresses embedded in x86 instructions into absolute
133  * addresses (@undo == %false), or undo this translation (@undo == %true).
134  *
135  * @last_target_usages is a temporary array of length >= 65536.
136  */
137 void
138 lzms_x86_filter(u8 data[], s32 size, s32 last_target_usages[], bool undo)
139 {
140         s32 closest_target_usage = -LZMS_X86_MAX_TRANSLATION_OFFSET - 1;
141
142         for (s32 i = 0; i < 65536; i++)
143                 last_target_usages[i] = -LZMS_X86_MAX_GOOD_TARGET_OFFSET - 1;
144
145         for (s32 i = 0; i < size - 11; ) {
146                 s32 max_trans_offset;
147                 s32 n;
148
149                 n = lzms_may_x86_translate(data + i, &max_trans_offset);
150                 if (max_trans_offset) {
151                         i = lzms_maybe_do_x86_translation(data, i, n,
152                                                           &closest_target_usage,
153                                                           last_target_usages,
154                                                           max_trans_offset,
155                                                           undo);
156                 } else {
157                         i += n;
158                 }
159         }
160 }