adding disasm code, lots of breakage
[ia32rtools.git] / tools / mkbridge.c
1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <string.h>
4
5 #include "my_assert.h"
6 #include "my_str.h"
7
8 #define ARRAY_SIZE(x) (sizeof(x) / sizeof(x[0]))
9 #define IS(w, y) !strcmp(w, y)
10
11 static int find_protostr(char *dst, size_t dlen, FILE *fhdr,
12         const char *sym, int *pline)
13 {
14         int line = 0;
15         char *p;
16
17         rewind(fhdr);
18
19         while (fgets(dst, dlen, fhdr))
20         {
21                 line++;
22                 if (strstr(dst, sym) != NULL)
23                         break;
24         }
25         *pline = line;
26
27         if (feof(fhdr))
28                 return -1;
29
30         p = dst + strlen(dst);
31         for (p--; p > dst && my_isblank(*p); --p)
32                 *p = 0;
33
34         return 0;
35 }
36
37 static int get_regparm(char *dst, size_t dlen, char *p)
38 {
39         int i, o;
40
41         if (*p != '<')
42                 return 0;
43
44         for (o = 0, i = 1; o < dlen; i++) {
45                 if (p[i] == 0)
46                         return 0;
47                 if (p[i] == '>')
48                         break;
49                 dst[o++] = p[i];
50         }
51         dst[o] = 0;
52         return i + 1;
53 }
54
55 // hmh..
56 static const char *known_types[] = {
57         "const void *",
58         "void *",
59         "char *",
60         "FILE *",
61         "unsigned __int8",
62         "unsigned __int16",
63         "unsigned int",
64         "signed int",
65         "char",
66         "__int8",
67         "__int16",
68         "int",
69         "bool",
70         "void",
71         "BYTE",
72         "WORD",
73         "DWORD",
74         "HMODULE",
75         "HANDLE",
76         "HWND",
77         "LPCSTR",
78         "size_t",
79 };
80
81 static int check_type(const char *name)
82 {
83         int i, l;
84
85         for (i = 0; i < ARRAY_SIZE(known_types); i++) {
86                 l = strlen(known_types[i]);
87                 if (strncmp(known_types[i], name, l) == 0)
88                         return l;
89         }
90
91         return 0;
92 }
93
94 /* args are always expanded to 32bit */
95 static const char *map_reg(const char *reg)
96 {
97         const char *regs_f[] = { "eax", "ebx", "ecx", "edx", "esi", "edi" };
98         const char *regs_w[] = { "ax",  "bx",  "cx",  "dx",  "si",  "di" };
99         const char *regs_b[] = { "al",  "bl",  "cl",  "dl" };
100         int i;
101
102         for (i = 0; i < ARRAY_SIZE(regs_w); i++)
103                 if (IS(reg, regs_w[i]))
104                         return regs_f[i];
105
106         for (i = 0; i < ARRAY_SIZE(regs_b); i++)
107                 if (IS(reg, regs_b[i]))
108                         return regs_f[i];
109
110         return reg;
111 }
112
113 static const char *hdrfn;
114 static int pline = 0;
115
116 static int parse_protostr(char *protostr, char **reglist, int *cnt_out,
117         int *is_stdcall)
118 {
119         char regparm[16];
120         char buf[256];
121         char cconv[32];
122         int xarg = 0;
123         int ret;
124         char *p;
125
126         p = protostr;
127         if (p[0] == '/' && p[1] == '/') {
128                 //printf("warning: decl for sym '%s' is commented out\n", sym);
129                 p = sskip(p + 2);
130         }
131
132         ret = check_type(p);
133         if (ret <= 0) {
134                 printf("%s:%d:%ld: unhandled return in '%s'\n",
135                                 hdrfn, pline, (p - protostr) + 1, protostr);
136                 return 1;
137         }
138         p += ret;
139         p = sskip(p);
140
141         p = next_word(cconv, sizeof(cconv), p);
142         p = sskip(p);
143         if (cconv[0] == 0) {
144                 printf("%s:%d:%ld: cconv missing\n",
145                         hdrfn, pline, (p - protostr) + 1);
146                 return 1;
147         }
148         if      (IS(cconv, "__cdecl"))
149                 *is_stdcall = 0;
150         else if (IS(cconv, "__stdcall"))
151                 *is_stdcall = 1;
152         else if (IS(cconv, "__fastcall"))
153                 *is_stdcall = 1;
154         else if (IS(cconv, "__thiscall"))
155                 *is_stdcall = 1;
156         else if (IS(cconv, "__userpurge"))
157                 *is_stdcall = 1; // in all cases seen..
158         else if (IS(cconv, "__usercall"))
159                 *is_stdcall = 0; // ..or is it?
160         else {
161                 printf("%s:%d:%ld: unhandled cconv: '%s'\n",
162                         hdrfn, pline, (p - protostr) + 1, cconv);
163                 return 1;
164         }
165
166         p = next_idt(buf, sizeof(buf), p);
167         p = sskip(p);
168         if (buf[0] == 0) {
169                 printf("%s:%d:%ld: func name missing\n",
170                                 hdrfn, pline, (p - protostr) + 1);
171                 return 1;
172         }
173
174         ret = get_regparm(regparm, sizeof(regparm), p);
175         if (ret > 0) {
176                 if (!IS(regparm, "eax") && !IS(regparm, "ax")
177                  && !IS(regparm, "al"))
178                 {
179                         printf("%s:%d:%ld: bad regparm: %s\n",
180                                 hdrfn, pline, (p - protostr) + 1, regparm);
181                         return 1;
182                 }
183                 p += ret;
184                 p = sskip(p);
185         }
186
187         if (*p != '(') {
188                 printf("%s:%d:%ld: '(' expected, got '%c'\n",
189                                 hdrfn, pline, (p - protostr) + 1, *p);
190                 return 1;
191         }
192         p++;
193
194         while (1) {
195                 p = sskip(p);
196                 if (*p == ')')
197                         break;
198                 if (*p == ',')
199                         p = sskip(p + 1);
200
201                 xarg++;
202
203                 ret = check_type(p);
204                 if (ret <= 0) {
205                         printf("%s:%d:%ld: unhandled type for arg%d\n",
206                                         hdrfn, pline, (p - protostr) + 1, xarg);
207                         return 1;
208                 }
209                 p += ret;
210                 p = sskip(p);
211
212                 p = next_idt(buf, sizeof(buf), p);
213                 p = sskip(p);
214 #if 0
215                 if (buf[0] == 0) {
216                         printf("%s:%d:%ld: idt missing for arg%d\n",
217                                         hdrfn, pline, (p - protostr) + 1, xarg);
218                         return 1;
219                 }
220 #endif
221                 reglist[xarg - 1] = NULL;
222
223                 ret = get_regparm(regparm, sizeof(regparm), p);
224                 if (ret > 0) {
225                         p += ret;
226                         p = sskip(p);
227
228                         reglist[xarg - 1] = strdup(map_reg(regparm));
229                 }
230         }
231
232         if (xarg > 0 && (IS(cconv, "__fastcall") || IS(cconv, "__thiscall"))) {
233                 if (reglist[0] != NULL) {
234                         printf("%s:%d: %s with arg1 spec %s?\n",
235                                 hdrfn, pline, cconv, reglist[0]);
236                 }
237                 reglist[0] = strdup("ecx");
238         }
239
240         if (xarg > 1 && IS(cconv, "__fastcall")) {
241                 if (reglist[1] != NULL) {
242                         printf("%s:%d: %s with arg2 spec %s?\n",
243                                 hdrfn, pline, cconv, reglist[1]);
244                 }
245                 reglist[1] = strdup("edx");
246         }
247
248         *cnt_out = xarg;
249
250         return 0;
251 }
252
253 static int is_x86_reg_saved(const char *reg)
254 {
255         static const char *nosave_regs[] = { "eax", "edx", "ecx" };
256         int nosave = 0;
257         int r;
258
259         for (r = 0; r < ARRAY_SIZE(nosave_regs); r++)
260                 if (strcmp(reg, nosave_regs[r]) == 0)
261                         nosave = 1;
262
263         return !nosave;
264 }
265
266 static void out_toasm_x86(FILE *f, char *sym, char *reg_list[], int reg_cnt,
267         int is_stdcall)
268 {
269         int have_normal = 0; // normal args
270         int have_regs = 0;
271         int must_save = 0;
272         int sarg_ofs = 1; // stack offset to args, in DWORDs
273         int args_repushed = 0;
274         int i;
275
276         for (i = 0; i < reg_cnt; i++) {
277                 if (reg_list[i] == NULL) {
278                         have_normal++;
279                         continue;
280                 }
281
282                 have_regs++;
283                 must_save |= is_x86_reg_saved(reg_list[i]);
284         }
285
286         fprintf(f, ".global _%s\n", sym);
287         fprintf(f, "_%s:\n", sym);
288
289         if (!have_regs && !is_stdcall) {
290                 fprintf(f, "\tjmp %s\n\n", sym);
291                 return;
292         }
293
294         if (!have_normal && !must_save && !is_stdcall) {
295                 // load arg regs
296                 for (i = 0; i < reg_cnt; i++) {
297                         fprintf(f, "\tmovl %d(%%esp), %%%s\n",
298                                 (i + sarg_ofs) * 4, reg_list[i]);
299                 }
300                 fprintf(f, "\tjmp %s\n\n", sym);
301                 return;
302         }
303
304         // save the regs
305         for (i = 0; i < reg_cnt; i++) {
306                 if (reg_list[i] != NULL && is_x86_reg_saved(reg_list[i])) {
307                         fprintf(f, "\tpushl %%%s\n", reg_list[i]);
308                         sarg_ofs++;
309                 }
310         }
311
312         // reconstruct arg stack
313         for (i = reg_cnt - 1; i >= 0; i--) {
314                 if (reg_list[i] == NULL) {
315                         fprintf(f, "\tmovl %d(%%esp), %%eax\n",
316                                 (i + sarg_ofs) * 4);
317                         fprintf(f, "\tpushl %%eax\n");
318                         sarg_ofs++;
319                         args_repushed++;
320                 }
321         }
322         my_assert(args_repushed, have_normal);
323
324         // load arg regs
325         for (i = 0; i < reg_cnt; i++) {
326                 if (reg_list[i] != NULL) {
327                         fprintf(f, "\tmovl %d(%%esp), %%%s\n",
328                                 (i + sarg_ofs) * 4, reg_list[i]);
329                 }
330         }
331
332         fprintf(f, "\n\t# %s\n", is_stdcall ? "__stdcall" : "__cdecl");
333         fprintf(f, "\tcall %s\n\n", sym);
334
335         if (args_repushed && !is_stdcall)
336                 fprintf(f, "\tadd $%d,%%esp\n", args_repushed * 4);
337
338         // restore regs
339         for (i = reg_cnt - 1; i >= 0; i--) {
340                 if (reg_list[i] != NULL && is_x86_reg_saved(reg_list[i]))
341                         fprintf(f, "\tpopl %%%s\n", reg_list[i]);
342         }
343
344         fprintf(f, "\tret\n\n");
345 }
346
347 static void out_fromasm_x86(FILE *f, char *sym, char *reg_list[], int reg_cnt,
348         int is_stdcall)
349 {
350         int have_normal = 0; // normal args
351         int have_regs = 0;
352         int sarg_ofs = 1; // stack offset to args, in DWORDs
353         int stack_args;
354         int i;
355
356         for (i = 0; i < reg_cnt; i++) {
357                 if (reg_list[i] == NULL) {
358                         have_normal++;
359                         continue;
360                 }
361
362                 have_regs++;
363         }
364
365         fprintf(f, "# %s\n", is_stdcall ? "__stdcall" : "__cdecl");
366         fprintf(f, ".global %s\n", sym);
367         fprintf(f, "%s:\n", sym);
368
369         if (!have_regs && !is_stdcall) {
370                 fprintf(f, "\tjmp _%s\n\n", sym);
371                 return;
372         }
373
374         fprintf(f, "\tpushl %%edx\n"); // just in case..
375         sarg_ofs++;
376
377         // construct arg stack
378         stack_args = have_normal;
379         for (i = reg_cnt - 1; i >= 0; i--) {
380                 if (reg_list[i] == NULL) {
381                         fprintf(f, "\tmovl %d(%%esp), %%edx\n",
382                                 (sarg_ofs + stack_args - 1) * 4);
383                         fprintf(f, "\tpushl %%edx\n");
384                         stack_args--;
385                 }
386                 else {
387                         fprintf(f, "\tpushl %%%s\n", reg_list[i]);
388                 }
389                 sarg_ofs++;
390         }
391
392         // no worries about calling conventions - always __cdecl
393         fprintf(f, "\n\tcall _%s\n\n", sym);
394
395         if (sarg_ofs > 2)
396                 fprintf(f, "\tadd $%d,%%esp\n", (sarg_ofs - 2) * 4);
397
398         fprintf(f, "\tpopl %%edx\n");
399
400         if (is_stdcall && have_normal)
401                 fprintf(f, "\tret $%d\n\n", have_normal * 4);
402         else
403                 fprintf(f, "\tret\n\n");
404 }
405
406 static void free_reglist(char *reg_list[], int reg_cnt)
407 {
408         int i;
409
410         for (i = 0; i < reg_cnt; i++) {
411                 if (reg_list[i] == NULL) {
412                         free(reg_list[i]);
413                         reg_list[i] = NULL;
414                 }
415         }
416 }
417
418 int main(int argc, char *argv[])
419 {
420         FILE *fout, *fsyms_to, *fsyms_from, *fhdr;
421         char protostr[256];
422         char line[256];
423         char sym[256];
424         char *reg_list[16];
425         int is_stdcall = 0;
426         int reg_cnt = 0;
427         int ret;
428
429         if (argc != 5) {
430                 printf("usage:\n%s <bridge.s> <toasm_symf> <fromasm_symf> <hdrf>\n",
431                         argv[0]);
432                 return 1;
433         }
434
435         hdrfn = argv[4];
436         fhdr = fopen(hdrfn, "r");
437         my_assert_not(fhdr, NULL);
438
439         fsyms_from = fopen(argv[3], "r");
440         my_assert_not(fsyms_from, NULL);
441
442         fsyms_to = fopen(argv[2], "r");
443         my_assert_not(fsyms_to, NULL);
444
445         fout = fopen(argv[1], "w");
446         my_assert_not(fout, NULL);
447
448         fprintf(fout, ".text\n\n");
449         fprintf(fout, "# to asm\n\n");
450
451         while (fgets(line, sizeof(line), fsyms_to))
452         {
453                 next_word(sym, sizeof(sym), line);
454                 if (sym[0] == 0 || sym[0] == ';' || sym[0] == '#')
455                         continue;
456
457                 ret = find_protostr(protostr, sizeof(protostr), fhdr,
458                         sym, &pline);
459                 if (ret != 0) {
460                         printf("%s: sym '%s' is missing\n",
461                                 hdrfn, sym);
462                         goto out;
463                 }
464
465                 ret = parse_protostr(protostr, reg_list, &reg_cnt, &is_stdcall);
466                 if (ret)
467                         goto out;
468
469                 out_toasm_x86(fout, sym, reg_list, reg_cnt, is_stdcall);
470                 free_reglist(reg_list, reg_cnt);
471         }
472
473         fprintf(fout, "# from asm\n\n");
474
475         while (fgets(line, sizeof(line), fsyms_from))
476         {
477                 next_word(sym, sizeof(sym), line);
478                 if (sym[0] == 0 || sym[0] == ';' || sym[0] == '#')
479                         continue;
480
481                 ret = find_protostr(protostr, sizeof(protostr), fhdr,
482                         sym, &pline);
483                 if (ret != 0) {
484                         printf("%s: sym '%s' is missing\n",
485                                 hdrfn, sym);
486                         goto out;
487                 }
488
489                 ret = parse_protostr(protostr, reg_list, &reg_cnt, &is_stdcall);
490                 if (ret)
491                         goto out;
492
493                 out_fromasm_x86(fout, sym, reg_list, reg_cnt, is_stdcall);
494                 free_reglist(reg_list, reg_cnt);
495         }
496
497         ret = 0;
498 out:
499         fclose(fout);
500         fclose(fsyms_to);
501         fclose(fsyms_from);
502         fclose(fhdr);
503         if (ret)
504                 remove(argv[1]);
505
506         return ret;
507 }