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