Initial commit
[dis-ebc.git] / dis-ebc.c
1 /* todo: copyright/gpl stuff. */
2
3 #ifndef __i386__
4 #error "use it on your own risk"
5 #endif
6
7 #include <stdio.h>
8 #include <stdlib.h>
9 #include <unistd.h>
10 #include <sys/mman.h>
11 #include <fcntl.h>
12 #include <sys/types.h>
13 #include <string.h>
14 #include <assert.h>
15
16 #define PS "%-10s: "
17
18 typedef unsigned char u8;
19 typedef unsigned short u16;
20 typedef unsigned int u32;
21 typedef unsigned long long u64;
22
23 static int file_len;
24 static u8 *data, *data_start;
25 static u32 base_data, base_code, size_code;
26
27 static char *imm_chw[] = {
28         "b", /* 08 bits */ "w", /* 16 bits */
29         "d", /* 32 bits */ "q", /* 64 bits */
30         "n" /* unsigned int. hax */ ,
31         "sn" /* signed int. hax */
32 };
33
34 static char *vm_reg[] = {
35         "FLAGS", "IP", "RESERVED", "RESERVED",
36         "RESERVED", "RESERVED", "RESERVED", "RESERVED"
37 };
38
39 static void print_debug(const char *s)
40 {
41         fprintf(stderr, PS "0x%08x (0x%04x)\n", s, (u32) data,
42                 data - data_start);
43 }
44
45 static void pspace(void) { printf(" "); }
46
47 static void pcomma(void) { printf(", "); }
48
49 static void preg(u8 reg)
50 {
51         if (reg > 7) { assert(0); }
52         printf("r%1d", reg);
53 }
54
55 static void pvmreg(u8 reg)
56 {
57         if (reg > 7) { assert(0); }
58         printf("%s", vm_reg[reg]);
59 }
60
61 static void pdref(u8 x)
62 {
63         printf("%s", x ? "@" : "");
64 }
65
66 #define PIMM_READ(size) \
67         static void pimm##size(void) \
68         { \
69                 printf(size != 64 ? "%x" : "%llx", *((u##size *) data)); \
70                 data += sizeof (u##size); \
71         }
72
73 PIMM_READ(8);
74 PIMM_READ(16);
75 PIMM_READ(32);
76 PIMM_READ(64);
77
78 static void pimm(u8 mod)
79 {
80         switch (mod) {
81         case 1: pimm16(); break;
82         case 2: pimm32(); break;
83         case 3: pimm64(); break;
84         case 0: default: assert(0);
85         }
86 }
87
88 static void pimmc(u8 imm_type)
89 {
90         if (imm_type > 5) { assert(0); }
91         printf("%s", imm_chw[imm_type]);
92 }
93
94 static void pflags(u8 flag)
95 {
96         switch (flag) {
97         case 0x0: printf("eq"); break;
98         case 0x1: printf("lte"); break;
99         case 0x2: printf("gte"); break;
100         case 0x3: printf("ulte"); break;
101         case 0x4: printf("ugte"); break;
102         }
103 }
104
105 static const char *breakopts[] = {
106         "Runaway program break",
107         "Get virtual machine version",
108         "undefined. WTF?",      // "Skip"?
109         "Debug breakpoint",
110         "System call",
111         "Create thunk",
112         "Set compiler version"
113 };
114
115 static void pbreak(void)
116 {
117         u8 i = *data++;
118         pspace();
119         if (i > 6) { printf("unknown break!"); assert(0);
120         } else printf("\"%s\"", breakopts[i]);
121 }
122
123 static void pinsn(void)
124 {
125         u8 insn = *data++;
126         u8 opc = insn & 0x3f;
127
128         switch (opc) {
129         case 0x00: printf("BREAK"); pbreak(); break;
130         case 0x01: case 0x03:{
131                         u8 opindex = insn & 0x80;
132                         u8 c3264 = insn & 0x40;
133
134                         u8 b1 = *data++;
135                         u8 cond = b1 & 0x80;
136                         u8 flag = b1 & 0x40;
137                         u8 ebcnative = b1 & 0x20;
138                         u8 relabs = b1 & 0x10;
139                         u8 op1 = b1 & 0x7;
140                         u8 dref1 = b1 & 0x8;
141
142                         if (opc == 0x01) {
143                                 printf("JMP%d%s", c3264 ? 64 : 32,
144                                        cond ? (flag ? "cs" : "cc") : "");
145                         } else if (opc == 0x03) {
146                                 printf("CALL%d%s", c3264 ? 64 : 32,
147                                        ebcnative ? "EX" : "");
148                         }
149                         printf("%s", relabs ? "a" : ""); pspace();
150
151                         if (!c3264) {   // 32bit
152                                 pdref(dref1);
153                                 preg(op1);
154                                 if (opindex) {
155                                         pspace(); pimm32();
156                                 }
157                         } else {        // 64bit
158                                 pimm64();
159                         }
160                 }
161                 break;
162         case 0x02:{
163                         u8 cond = insn & 0x80;
164                         u8 flag = insn & 0x40;
165                         printf("JMP8%s", cond ? (flag ? "cs" : "cc") : "");
166                         pspace(); pimm8();
167                 }
168                 break;
169         case 0x04: printf("RET"); data++; break;
170         case 0x05 ... 0x09:{
171                         u8 opindex = insn & 0x80;
172                         u8 c3264 = insn & 0x40;
173
174                         u8 b1 = *data++;
175                         u8 op1 = b1 & 0x7;
176                         u8 op2 = (b1 & 0x70) >> 4;
177                         u8 dref2 = b1 & 0x80;
178
179                         printf("CMP%d", !c3264 ? 32 : 64);
180                         pflags(opc - 0x05); pspace();
181                         preg(op1); pcomma();
182                         pdref(dref2); preg(op2);
183                         if (opindex) {
184                                 pspace(); pimm16();
185                         }
186                 }
187                 break;
188         case 0x0a ... 0x19:{
189                         u8 opindex = insn & 0x80;
190                         u8 c3264 = insn & 0x40;
191
192                         u8 b1 = *data++;
193                         u8 op1 = b1 & 0x7;
194                         u8 dref1 = b1 & 0x8;
195                         u8 op2 = (b1 & 0x70) >> 4;
196                         u8 dref2 = b1 & 0x80;
197                         switch (opc) {
198                         case 0x0a: printf("NOT"); break;
199                         case 0x0b: printf("NEG"); break;
200                         case 0x0c: printf("ADD"); break;
201                         case 0x0d: printf("SUB"); break;
202                         case 0x0e: printf("MUL"); break;
203                         case 0x0f: printf("MULU"); break;
204                         case 0x10: printf("DIV"); break;
205                         case 0x11: printf("DIVU"); break;
206                         case 0x12: printf("MOD"); break;
207                         case 0x13: printf("MODU"); break;
208                         case 0x14: printf("AND"); break;
209                         case 0x15: printf("OR"); break;
210                         case 0x16: printf("XOR"); break;
211                         case 0x17: printf("SHL"); break;
212                         case 0x18: printf("SHR"); break;
213                         case 0x19: printf("ASHR"); break;
214                         default:
215                                 printf("\nopcode: %x\n", opc);
216                                 assert(0);
217                         }
218                         printf("%d", c3264 ? 64 : 32); pspace();
219                         pdref(dref1); preg(op1); pcomma();
220                         pdref(dref2); preg(op2);
221                         if (opindex) {
222                                 pspace(); pimm16();
223                         }
224                 }
225                 break;
226         case 0x1a ... 0x1c:{
227                         u8 opindex = insn & 0x80;
228                         u8 c3264 = insn & 0x40;
229                         u8 opmod = opc - 0x1a;  // 0b, 1w, 2d
230
231                         u8 b1 = *data++;
232                         u8 op1 = b1 & 0x7;
233                         u8 dref1 = b1 & 0x8;
234                         u8 op2 = (b1 & 0x70) >> 4;
235                         u8 dref2 = b1 & 0x80;
236
237                         printf("EXTND"); pimmc(opmod);
238                         printf("%d", c3264 ? 64 : 32); pspace();
239                         pdref(dref1); preg(op1); pcomma();
240                         pdref(dref2); preg(op2);
241                         if (opindex) {
242                                 pspace(); pimm16();
243                         }
244                 }
245                 break;
246         case 0x2a:{
247                         u8 b1 = *data++;
248                         u8 op1 = b1 & 0x7;
249                         u8 op2 = (b1 & 0x70) >> 4;
250
251                         printf("STORESP"); pspace();
252                         preg(op1); pcomma(); pvmreg(op2);
253                 }
254                 break;
255         case 0x2d ... 0x31:{
256                         u8 c3264 = insn & 0x40;
257                         u8 i1632 = !!(insn & 0x80);
258
259                         u8 b1 = *data++;
260                         u8 op1 = b1 & 0x7;
261                         u8 dref1 = b1 & 0x8;
262                         u8 op1index = b1 & 0x10;
263
264                         printf("CMPI%d", !c3264 ? 32 : 64);
265                         pimmc(i1632 + 1);
266                         pflags(opc - 0x2d); pspace();
267                         pdref(dref1); preg(op1);
268                         if (op1index) {
269                                 pspace(); pimm16();
270                         }
271                         pcomma();
272                         if (i1632) pimm32();
273                         else pimm16();
274                 }
275                 break;
276         case 0x1d ... 0x28:
277         case 0x32 ... 0x33:{
278                         u8 op1mod = 0;
279                         u8 op2mod = 0;
280                         switch (opc) {
281                         case 0x1d: op1mod = 0; op2mod = 1; break;       // MOVbw
282                         case 0x1e: op1mod = 1; op2mod = 1; break;       // MOVww
283                         case 0x1f: op1mod = 2; op2mod = 1; break;       // MOVdw
284                         case 0x20: op1mod = 3; op2mod = 1; break;       // MOVqw
285                         case 0x21: op1mod = 0; op2mod = 2; break;       // MOVbd
286                         case 0x22: op1mod = 1; op2mod = 2; break;       // MOVwd
287                         case 0x23: op1mod = 2; op2mod = 2; break;       // MOVdd
288                         case 0x24: op1mod = 3; op2mod = 2; break;       // MOVqd
289
290                         case 0x25: op1mod = 5; op2mod = 1; break;       // MOVsnw
291                         case 0x26: op1mod = 5; op2mod = 2; break;       // MOVsnq
292
293                         case 0x28: op1mod = 3; op2mod = 3; break;       // MOVqq
294
295                         case 0x32: op1mod = 4; op2mod = 1; break;       // MOVnw
296                         case 0x33: op1mod = 4; op2mod = 2; break;       // MOVnd
297                         default:
298                                 fprintf(stderr, "wtfopcode: %x\n", opc);
299                                 assert(0);
300                         }
301                         u8 mod = (insn & 0xc0) >> 6;
302                         u8 op1index = mod & 0x2;
303                         u8 op2index = mod & 0x1;
304
305                         u8 b1 = *data++;
306                         u8 op1 = b1 & 0x7;
307                         u8 dref1 = b1 & 0x8;
308                         u8 op2 = (b1 & 0x70) >> 4;
309                         u8 dref2 = b1 & 0x80;
310
311                         printf("MOV"); pimmc(op1mod); pimmc(op2mod); pspace();
312                         pdref(dref1); preg(op1);
313                         // op2mod defines index width for *both* indexes
314                         if (op1index) {
315                                 pspace(); pimm(op2mod);
316                         }
317                         pcomma(); pdref(dref2); preg(op2);
318                         if (op2index) {
319                                 pspace(); pimm(op2mod);
320                         }
321                 }
322                 break;
323         case 0x37:{
324                         u8 mod = (insn & 0xc0) >> 6;
325                         u8 b1 = *data++;
326                         u8 op1 = b1 & 0x7;
327                         u8 dref = b1 & 0x8;
328                         u8 width = (b1 & 0x30) >> 4;
329                         u8 op1index = b1 & 0x40;
330
331                         printf("MOVI"); pimmc(width); pimmc(mod); pspace();
332                         pdref(dref); preg(op1);
333                         if (op1index) {
334                                 pspace(); pimm16();
335                         }
336                         pcomma(); pimm(mod);
337                 }
338                 break;
339         case 0x38:{
340                         u8 mod = (insn & 0xc0) >> 6;
341
342                         u8 b1 = *data++;
343                         u8 op1 = b1 & 0x7;
344                         u8 dref = b1 & 0x8;
345                         u8 op1index = (b1 & 0x40) >> 6;
346
347                         printf("MOVIn"); pimmc(mod); pspace();
348                         pdref(dref); preg(op1);
349                         if (op1index) {
350                                 pspace(); pimm16();
351                         }
352                         pcomma(); pimm(mod);
353                 }
354                 break;
355         case 0x39:{
356                         u8 mod = (insn & 0xc0) >> 6;
357                         u8 b1 = *data++;
358                         u8 op1 = b1 & 0x7;
359                         u8 dref = b1 & 0x8;
360                         u8 op1index = b1 & 0x40;
361
362                         printf("MOVREL"); pimmc(mod); pspace();
363                         pdref(dref); preg(op1);
364                         if (op1index) {
365                                 pspace(); pimm16();
366                         }
367                         pcomma(); pimm(mod);
368                 }
369                 break;
370         default:
371                 fprintf(stderr, "\nunknown opcode: 0x%02x\n", opc);
372                 assert(0);
373         }
374 }
375
376 static void pheader(void)
377 {
378         printf("\n%08x: ", data - data_start);
379 }
380
381 int main(int argc, const char **argv)
382 {
383         if (argc != 2) {
384                 fprintf(stderr, "usage: %s <pe-ebc>\n", argv[0]);
385                 exit(1);
386         }
387
388         int fd = open(argv[1], O_RDONLY);
389         file_len = lseek(fd, 0, SEEK_END);
390         data_start = data = mmap(0, file_len, PROT_READ, MAP_SHARED, fd, 0);
391         close(fd);
392
393         if (memcmp(data, "MZ", 2) != 0) {
394                 fprintf(stderr, "wrong DOS header: %c%c\n", *data, *(data + 1));
395                 exit(2);
396         } else {
397                 print_debug("data");
398         }
399
400         int found = 0;
401         while (!found && ++data < (data + file_len)) {
402                 if (memcmp(data, "PE", 2) == 0) {
403                         print_debug("PE");
404                         found = 1;
405                 }
406         }
407
408         if (!found) {
409                 fprintf(stderr, "no PE header found\n");
410                 exit(3);
411         }
412
413         data += 4;
414         if (memcmp(data, "\xbc\x0e", 2) != 0) { // read '0xebc'
415                 fprintf(stderr, "not an EBC image: 0x%02x%02x\n", *data,
416                         *(data + 1));
417                 exit(4);
418         }
419
420         found = 0;
421         while (!found && ++data < (data + file_len)) {
422                 if (memcmp(data, "\x0b\x01", 2) == 0) { // read '0x10b'
423                         print_debug("PE-opt");
424                         found = 1;
425                 }
426         }
427
428         if (!found) {
429                 fprintf(stderr, "no PE-opt header found\n");
430                 exit(3);
431         }
432
433         fprintf(stderr, PS "0x%04x\n", "magic", *((u16 *) data));
434         data += 2;              //u16
435
436         fprintf(stderr, PS "0x%02x\n", "majorver", *((u8 *) data));
437         data++;                 //u8
438         fprintf(stderr, PS "0x%02x\n", "minorver", *((u8 *) data));
439         data++;                 //u8
440
441         // TODO: not sure if this is correct :/
442         size_code = *((u32 *) data);
443         print_debug("size_code");
444         fprintf(stderr, PS "0x%08x\n", "size_code", size_code);
445         data += 4 + 4 + 4;      //u32, u32, u32
446
447         fprintf(stderr, PS "0x%08x\n", "entry_point", *((u32 *) data));
448         data += 4;              //u32
449
450         base_code = *((u32 *) data);
451         print_debug("base_code");
452         fprintf(stderr, PS "0x%08x\n", "base_code", base_code);
453         data += 4;
454         base_data = *((u32 *) data);
455         print_debug("base_data");
456         fprintf(stderr, PS "0x%08x\n", "base_data", base_data);
457
458         data += 4;              //u32
459         fprintf(stderr, PS "0x%08x\n", "???", *((u32 *) data));
460
461         data = data_start + base_code;
462         while (data <= (data_start + size_code)) {
463                 pheader();
464                 pinsn();
465         }
466         printf("\n");
467
468         return 0;
469 }