57e4efe9 |
1 | #include <stdio.h> |
2 | #include <stdlib.h> |
3 | #include <string.h> |
4 | |
5 | #include "my_assert.h" |
6 | #include "my_str.h" |
7 | |
232aca37 |
8 | #define ARRAY_SIZE(x) (sizeof(x) / sizeof(x[0])) |
3e52f54c |
9 | #define IS(w, y) !strcmp(w, y) |
232aca37 |
10 | |
57e4efe9 |
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 | |
232aca37 |
17 | rewind(fhdr); |
18 | |
57e4efe9 |
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 | |
232aca37 |
55 | // hmh.. |
57e4efe9 |
56 | static const char *known_types[] = { |
3e52f54c |
57 | "const void *", |
58 | "void *", |
59 | "char *", |
60 | "FILE *", |
232aca37 |
61 | "unsigned __int8", |
3e52f54c |
62 | "unsigned __int16", |
232aca37 |
63 | "unsigned int", |
3e52f54c |
64 | "signed int", |
65 | "char", |
66 | "__int8", |
67 | "__int16", |
68 | "int", |
69 | "bool", |
57e4efe9 |
70 | "void", |
232aca37 |
71 | "BYTE", |
72 | "WORD", |
57e4efe9 |
73 | "DWORD", |
74 | "HMODULE", |
75 | "HANDLE", |
76 | "HWND", |
232aca37 |
77 | "LPCSTR", |
78 | "size_t", |
57e4efe9 |
79 | }; |
80 | |
81 | static int check_type(const char *name) |
82 | { |
83 | int i, l; |
84 | |
232aca37 |
85 | for (i = 0; i < ARRAY_SIZE(known_types); i++) { |
57e4efe9 |
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 | |
3e52f54c |
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 | |
232aca37 |
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]; |
3e52f54c |
121 | char cconv[32]; |
232aca37 |
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 | |
3e52f54c |
141 | p = next_word(cconv, sizeof(cconv), p); |
232aca37 |
142 | p = sskip(p); |
3e52f54c |
143 | if (cconv[0] == 0) { |
232aca37 |
144 | printf("%s:%d:%ld: cconv missing\n", |
145 | hdrfn, pline, (p - protostr) + 1); |
146 | return 1; |
147 | } |
3e52f54c |
148 | if (IS(cconv, "__cdecl")) |
232aca37 |
149 | *is_stdcall = 0; |
3e52f54c |
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")) |
232aca37 |
155 | *is_stdcall = 1; |
3e52f54c |
156 | else if (IS(cconv, "__userpurge")) |
232aca37 |
157 | *is_stdcall = 1; // in all cases seen.. |
3e52f54c |
158 | else if (IS(cconv, "__usercall")) |
232aca37 |
159 | *is_stdcall = 0; // ..or is it? |
160 | else { |
232aca37 |
161 | printf("%s:%d:%ld: unhandled cconv: '%s'\n", |
3e52f54c |
162 | hdrfn, pline, (p - protostr) + 1, cconv); |
232aca37 |
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) { |
3e52f54c |
176 | if (!IS(regparm, "eax") && !IS(regparm, "ax") |
177 | && !IS(regparm, "al")) |
232aca37 |
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 | |
3e52f54c |
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]); |
232aca37 |
236 | } |
3e52f54c |
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"); |
232aca37 |
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 | |
a51421fa |
286 | fprintf(f, ".global _%s\n", sym); |
287 | fprintf(f, "_%s:\n", sym); |
232aca37 |
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) |
a51421fa |
336 | fprintf(f, "\tadd $%d,%%esp\n", args_repushed * 4); |
232aca37 |
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 | |
3e52f54c |
369 | if (!have_regs && !is_stdcall) { |
232aca37 |
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) |
a51421fa |
396 | fprintf(f, "\tadd $%d,%%esp\n", (sarg_ofs - 2) * 4); |
232aca37 |
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 | |
3e52f54c |
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 | |
57e4efe9 |
418 | int main(int argc, char *argv[]) |
419 | { |
232aca37 |
420 | FILE *fout, *fsyms_to, *fsyms_from, *fhdr; |
57e4efe9 |
421 | char protostr[256]; |
422 | char line[256]; |
423 | char sym[256]; |
232aca37 |
424 | char *reg_list[16]; |
425 | int is_stdcall = 0; |
426 | int reg_cnt = 0; |
57e4efe9 |
427 | int ret; |
428 | |
232aca37 |
429 | if (argc != 5) { |
430 | printf("usage:\n%s <bridge.s> <toasm_symf> <fromasm_symf> <hdrf>\n", |
57e4efe9 |
431 | argv[0]); |
432 | return 1; |
433 | } |
434 | |
232aca37 |
435 | hdrfn = argv[4]; |
57e4efe9 |
436 | fhdr = fopen(hdrfn, "r"); |
437 | my_assert_not(fhdr, NULL); |
438 | |
232aca37 |
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); |
57e4efe9 |
444 | |
445 | fout = fopen(argv[1], "w"); |
446 | my_assert_not(fout, NULL); |
447 | |
448 | fprintf(fout, ".text\n\n"); |
232aca37 |
449 | fprintf(fout, "# to asm\n\n"); |
57e4efe9 |
450 | |
232aca37 |
451 | while (fgets(line, sizeof(line), fsyms_to)) |
57e4efe9 |
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); |
232aca37 |
462 | goto out; |
57e4efe9 |
463 | } |
464 | |
232aca37 |
465 | ret = parse_protostr(protostr, reg_list, ®_cnt, &is_stdcall); |
466 | if (ret) |
467 | goto out; |
57e4efe9 |
468 | |
232aca37 |
469 | out_toasm_x86(fout, sym, reg_list, reg_cnt, is_stdcall); |
3e52f54c |
470 | free_reglist(reg_list, reg_cnt); |
232aca37 |
471 | } |
57e4efe9 |
472 | |
232aca37 |
473 | fprintf(fout, "# from asm\n\n"); |
57e4efe9 |
474 | |
232aca37 |
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; |
57e4efe9 |
480 | |
232aca37 |
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; |
57e4efe9 |
487 | } |
57e4efe9 |
488 | |
232aca37 |
489 | ret = parse_protostr(protostr, reg_list, ®_cnt, &is_stdcall); |
490 | if (ret) |
491 | goto out; |
57e4efe9 |
492 | |
232aca37 |
493 | out_fromasm_x86(fout, sym, reg_list, reg_cnt, is_stdcall); |
3e52f54c |
494 | free_reglist(reg_list, reg_cnt); |
57e4efe9 |
495 | } |
496 | |
232aca37 |
497 | ret = 0; |
498 | out: |
57e4efe9 |
499 | fclose(fout); |
232aca37 |
500 | fclose(fsyms_to); |
501 | fclose(fsyms_from); |
502 | fclose(fhdr); |
503 | if (ret) |
504 | remove(argv[1]); |
505 | |
506 | return ret; |
57e4efe9 |
507 | } |