@@ -19,6 +19,7 @@ limitations under the License.
1919#include < vector>
2020#include " ../types.hpp"
2121#include " ../transform/AliasCodec.hpp"
22+ #include " ../transform/EXECodec.hpp"
2223#include " ../transform/FSDCodec.hpp"
2324#include " ../transform/LZCodec.hpp"
2425#include " ../transform/NullTransform.hpp"
@@ -32,6 +33,272 @@ limitations under the License.
3233using namespace std ;
3334using namespace kanzi ;
3435
36+ static void writeInt16LE (kanzi::byte buf[], int value)
37+ {
38+ buf[0 ] = kanzi::byte (value);
39+ buf[1 ] = kanzi::byte (value >> 8 );
40+ }
41+
42+ static void writeInt32LE (kanzi::byte buf[], int value)
43+ {
44+ buf[0 ] = kanzi::byte (value);
45+ buf[1 ] = kanzi::byte (value >> 8 );
46+ buf[2 ] = kanzi::byte (value >> 16 );
47+ buf[3 ] = kanzi::byte (value >> 24 );
48+ }
49+
50+ static vector<kanzi::byte> createPEBlock (int arch)
51+ {
52+ const int size = 8192 ;
53+ const int codeStart = 512 ;
54+ const int codeLen = 4096 ;
55+ const int posPE = 0x80 ;
56+ vector<kanzi::byte> data (size, kanzi::byte (0x90 ));
57+ data[0 ] = kanzi::byte (' M' );
58+ data[1 ] = kanzi::byte (' Z' );
59+ writeInt32LE (&data[60 ], posPE);
60+ data[posPE] = kanzi::byte (' P' );
61+ data[posPE + 1 ] = kanzi::byte (' E' );
62+ data[posPE + 2 ] = kanzi::byte (0 );
63+ data[posPE + 3 ] = kanzi::byte (0 );
64+ writeInt16LE (&data[posPE + 4 ], arch);
65+ writeInt32LE (&data[posPE + 28 ], codeLen);
66+ writeInt32LE (&data[posPE + 44 ], codeStart);
67+ return data;
68+ }
69+
70+ static void setPECodeLength (vector<kanzi::byte>& data, int codeLen)
71+ {
72+ writeInt32LE (&data[0x80 + 28 ], codeLen);
73+ }
74+
75+ static vector<kanzi::byte> createELF64Block (int arch)
76+ {
77+ const int size = 8192 ;
78+ const int codeStart = 512 ;
79+ const int codeLen = 4096 ;
80+ const int posSection = 0x100 ;
81+ vector<kanzi::byte> data (size, kanzi::byte (0 ));
82+ data[0 ] = kanzi::byte (0x7F );
83+ data[1 ] = kanzi::byte (' E' );
84+ data[2 ] = kanzi::byte (' L' );
85+ data[3 ] = kanzi::byte (' F' );
86+ data[4 ] = kanzi::byte (2 );
87+ data[5 ] = kanzi::byte (1 );
88+ writeInt16LE (&data[18 ], arch);
89+ writeInt16LE (&data[0x3A ], 0x40 );
90+ writeInt16LE (&data[0x3C ], 1 );
91+ writeInt32LE (&data[0x28 ], posSection);
92+ writeInt32LE (&data[posSection + 4 ], 1 );
93+ writeInt32LE (&data[posSection + 0x18 ], codeStart);
94+ writeInt32LE (&data[posSection + 0x20 ], codeLen);
95+ return data;
96+ }
97+
98+ static void fillX86Code (vector<kanzi::byte>& data, int codeStart, int codeLen)
99+ {
100+ for (int i = codeStart; i + 5 <= codeStart + codeLen; i += 5 ) {
101+ data[i] = kanzi::byte (0xE8 );
102+ data[i + 1 ] = kanzi::byte (0 );
103+ data[i + 2 ] = kanzi::byte (0 );
104+ data[i + 3 ] = kanzi::byte (0 );
105+ data[i + 4 ] = kanzi::byte (0 );
106+ }
107+ }
108+
109+ static void fillARM64Code (vector<kanzi::byte>& data, int codeStart, int codeLen)
110+ {
111+ for (int i = codeStart; i + 4 <= codeStart + codeLen; i += 4 )
112+ writeInt32LE (&data[i], 0x14000000 );
113+ }
114+
115+ static void fillX86ExpandedCode (vector<kanzi::byte>& data, int codeStart, int codeLen)
116+ {
117+ for (int i = codeStart; i + 8 <= codeStart + codeLen; i += 8 ) {
118+ const bool escaped = (((i - codeStart) >> 3 ) < 24 );
119+ data[i] = kanzi::byte (0xE8 );
120+ data[i + 1 ] = kanzi::byte (0 );
121+ data[i + 2 ] = kanzi::byte (0 );
122+ data[i + 3 ] = kanzi::byte (0 );
123+ data[i + 4 ] = kanzi::byte (0 );
124+ data[i + 5 ] = escaped ? kanzi::byte (0x9B ) : kanzi::byte (0x90 );
125+ data[i + 6 ] = kanzi::byte (0x90 );
126+ data[i + 7 ] = kanzi::byte (0x90 );
127+ }
128+ }
129+
130+ static void addX86BoundaryJCC (vector<kanzi::byte>& data, int codeStart, int codeLen)
131+ {
132+ const int idx = codeStart + codeLen - 5 ;
133+ data[idx] = kanzi::byte (0x0F );
134+ data[idx + 1 ] = kanzi::byte (0x85 );
135+ data[idx + 2 ] = kanzi::byte (0 );
136+ data[idx + 3 ] = kanzi::byte (0 );
137+ data[idx + 4 ] = kanzi::byte (0 );
138+ data[idx + 5 ] = kanzi::byte (0 );
139+ }
140+
141+ static vector<kanzi::byte> createX86BoundaryBlock ()
142+ {
143+ vector<kanzi::byte> data = createPEBlock (0x014C );
144+ const int codeStart = 512 ;
145+ const int codeLen = 85 ;
146+ setPECodeLength (data, codeLen);
147+ fillX86Code (data, codeStart, 16 * 5 );
148+ addX86BoundaryJCC (data, codeStart, codeLen);
149+ return data;
150+ }
151+
152+ static int testEXERoundTrip (const string& name, vector<kanzi::byte>& data)
153+ {
154+ cout << endl
155+ << " Correctness for " << name << endl;
156+ Context ctx;
157+ EXECodec codec (ctx);
158+ vector<kanzi::byte> encoded (codec.getMaxEncodedLength (int (data.size ())), kanzi::byte (0 ));
159+ vector<kanzi::byte> decoded (data.size (), kanzi::byte (0 ));
160+ SliceArray<kanzi::byte> input (&data[0 ], int (data.size ()), 0 );
161+ SliceArray<kanzi::byte> output (&encoded[0 ], int (encoded.size ()), 0 );
162+ SliceArray<kanzi::byte> reverse (&decoded[0 ], int (decoded.size ()), 0 );
163+
164+ if (codec.forward (input, output, int (data.size ())) == false ) {
165+ cout << " Encoding error" << endl;
166+ return 1 ;
167+ }
168+
169+ const int encodedSize = output._index ;
170+ input._index = 0 ;
171+ output._index = 0 ;
172+
173+ if (codec.inverse (output, reverse, encodedSize) == false ) {
174+ cout << " Decoding error" << endl;
175+ return 1 ;
176+ }
177+
178+ if ((reverse._index != int (data.size ())) || (memcmp (&data[0 ], &decoded[0 ], data.size ()) != 0 )) {
179+ cout << " Round-trip mismatch" << endl;
180+ return 1 ;
181+ }
182+
183+ vector<kanzi::byte> small (encodedSize - 10 , kanzi::byte (0 ));
184+ SliceArray<kanzi::byte> tooSmall (&small[0 ], int (small.size ()), 0 );
185+ output._index = 0 ;
186+
187+ if (codec.inverse (output, tooSmall, encodedSize) != false ) {
188+ cout << " Undersized output buffer should fail" << endl;
189+ return 1 ;
190+ }
191+
192+ cout << " Identical" << endl;
193+ return 0 ;
194+ }
195+
196+ static int testEXECodec ()
197+ {
198+ vector<kanzi::byte> x86 = createPEBlock (0x014C );
199+ fillX86Code (x86, 512 , 4096 );
200+
201+ if (testEXERoundTrip (" EXE-X86" , x86) != 0 )
202+ return 1 ;
203+
204+ vector<kanzi::byte> arm64 = createELF64Block (0x00B7 );
205+ fillARM64Code (arm64, 512 , 4096 );
206+
207+ if (testEXERoundTrip (" EXE-ARM64" , arm64) != 0 )
208+ return 1 ;
209+
210+ {
211+ cout << endl
212+ << " Correctness for EXE-X86-Expanded" << endl;
213+ Context ctx;
214+ EXECodec codec (ctx);
215+ vector<kanzi::byte> expanded = createPEBlock (0x014C );
216+ fillX86ExpandedCode (expanded, 512 , 4096 );
217+ vector<kanzi::byte> encoded (codec.getMaxEncodedLength (int (expanded.size ())), kanzi::byte (0 ));
218+ vector<kanzi::byte> decoded (expanded.size (), kanzi::byte (0 ));
219+ SliceArray<kanzi::byte> input (&expanded[0 ], int (expanded.size ()), 0 );
220+ SliceArray<kanzi::byte> output (&encoded[0 ], int (encoded.size ()), 0 );
221+ SliceArray<kanzi::byte> reverse (&decoded[0 ], int (decoded.size ()), 0 );
222+
223+ if (codec.forward (input, output, int (expanded.size ())) == false ) {
224+ cout << " Encoding error" << endl;
225+ return 1 ;
226+ }
227+
228+ if (output._index <= int (expanded.size ()) + 9 ) {
229+ cout << " Expected encoded block expansion beyond header" << endl;
230+ return 1 ;
231+ }
232+
233+ const int encodedSize = output._index ;
234+ output._index = 0 ;
235+ reverse._index = 0 ;
236+
237+ if (codec.inverse (output, reverse, encodedSize) == false ) {
238+ cout << " Decoding error" << endl;
239+ return 1 ;
240+ }
241+
242+ if ((reverse._index != int (expanded.size ())) ||
243+ (memcmp (&expanded[0 ], &decoded[0 ], expanded.size ()) != 0 )) {
244+ cout << " Round-trip mismatch" << endl;
245+ return 1 ;
246+ }
247+
248+ cout << " Identical" << endl;
249+ }
250+
251+ vector<kanzi::byte> boundary = createX86BoundaryBlock ();
252+
253+ if (testEXERoundTrip (" EXE-X86-Boundary-JCC" , boundary) != 0 )
254+ return 1 ;
255+
256+ {
257+ cout << endl
258+ << " Correctness for EXE-X86-Legacy-Boundary-JCC" << endl;
259+ Context ctx;
260+ EXECodec codec (ctx);
261+ vector<kanzi::byte> legacy = createX86BoundaryBlock ();
262+ vector<kanzi::byte> encoded (codec.getMaxEncodedLength (int (legacy.size ())), kanzi::byte (0 ));
263+ vector<kanzi::byte> decoded (legacy.size (), kanzi::byte (0 ));
264+ SliceArray<kanzi::byte> input (&legacy[0 ], int (legacy.size ()), 0 );
265+ SliceArray<kanzi::byte> output (&encoded[0 ], int (encoded.size ()), 0 );
266+ SliceArray<kanzi::byte> reverse (&decoded[0 ], int (decoded.size ()), 0 );
267+
268+ if (codec.forward (input, output, int (legacy.size ())) == false ) {
269+ cout << " Encoding error" << endl;
270+ return 1 ;
271+ }
272+
273+ const int encodedSize = output._index ;
274+ const int codeEnd = LittleEndian::readInt32 (&encoded[5 ]);
275+
276+ if ((codeEnd >= encodedSize) || (encoded[codeEnd] != kanzi::byte (0x0F ))) {
277+ cout << " Unexpected boundary layout" << endl;
278+ return 1 ;
279+ }
280+
281+ writeInt32LE (&encoded[5 ], codeEnd + 1 );
282+ output._index = 0 ;
283+ reverse._index = 0 ;
284+
285+ if (codec.inverse (output, reverse, encodedSize) == false ) {
286+ cout << " Decoding error" << endl;
287+ return 1 ;
288+ }
289+
290+ if ((reverse._index != int (legacy.size ())) ||
291+ (memcmp (&legacy[0 ], &decoded[0 ], legacy.size ()) != 0 )) {
292+ cout << " Round-trip mismatch" << endl;
293+ return 1 ;
294+ }
295+
296+ cout << " Identical" << endl;
297+ }
298+
299+ return 0 ;
300+ }
301+
35302static Transform<kanzi::byte>* getByteTransform (string name, Context& ctx)
36303{
37304 if (name.compare (" SRT" ) == 0 )
@@ -525,6 +792,11 @@ int TestTransforms_main(int argc, const char* argv[])
525792 int res = 0 ;
526793
527794 try {
795+ res = testEXECodec ();
796+
797+ if (res != 0 )
798+ return res;
799+
528800 vector<string> codecs;
529801 bool doPerf = true ;
530802
@@ -594,4 +866,3 @@ int TestTransforms_main(int argc, const char* argv[])
594866 cout << ((res == 0 ) ? " Success" : " Failure" ) << endl;
595867 return res;
596868}
597-
0 commit comments