Statistics
| Branch: | Revision:

root / dyngen.c @ 7d13299d

History | View | Annotate | Download (15.6 kB)

1
/*
2
 *  Generic Dynamic compiler generator
3
 * 
4
 *  Copyright (c) 2003 Fabrice Bellard
5
 *
6
 *  This program is free software; you can redistribute it and/or modify
7
 *  it under the terms of the GNU General Public License as published by
8
 *  the Free Software Foundation; either version 2 of the License, or
9
 *  (at your option) any later version.
10
 *
11
 *  This program is distributed in the hope that it will be useful,
12
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
13
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14
 *  GNU General Public License for more details.
15
 *
16
 *  You should have received a copy of the GNU General Public License
17
 *  along with this program; if not, write to the Free Software
18
 *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
19
 */
20
#include <stdlib.h>
21
#include <stdio.h>
22
#include <stdarg.h>
23
#include <inttypes.h>
24
#include <elf.h>
25
#include <unistd.h>
26
#include <fcntl.h>
27

    
28
#include "thunk.h"
29

    
30
/* all dynamically generated functions begin with this code */
31
#define OP_PREFIX "op"
32

    
33
int elf_must_swap(Elf32_Ehdr *h)
34
{
35
  union {
36
      uint32_t i;
37
      uint8_t b[4];
38
  } swaptest;
39

    
40
  swaptest.i = 1;
41
  return (h->e_ident[EI_DATA] == ELFDATA2MSB) != 
42
      (swaptest.b[0] == 0);
43
}
44
  
45
void swab16s(uint16_t *p)
46
{
47
    *p = bswap16(*p);
48
}
49

    
50
void swab32s(uint32_t *p)
51
{
52
    *p = bswap32(*p);
53
}
54

    
55
void swab64s(uint32_t *p)
56
{
57
    *p = bswap64(*p);
58
}
59

    
60
void elf_swap_ehdr(Elf32_Ehdr *h)
61
{
62
    swab16s(&h->e_type);                        /* Object file type */
63
    swab16s(&h->        e_machine);                /* Architecture */
64
    swab32s(&h->        e_version);                /* Object file version */
65
    swab32s(&h->        e_entry);                /* Entry point virtual address */
66
    swab32s(&h->        e_phoff);                /* Program header table file offset */
67
    swab32s(&h->        e_shoff);                /* Section header table file offset */
68
    swab32s(&h->        e_flags);                /* Processor-specific flags */
69
    swab16s(&h->        e_ehsize);                /* ELF header size in bytes */
70
    swab16s(&h->        e_phentsize);                /* Program header table entry size */
71
    swab16s(&h->        e_phnum);                /* Program header table entry count */
72
    swab16s(&h->        e_shentsize);                /* Section header table entry size */
73
    swab16s(&h->        e_shnum);                /* Section header table entry count */
74
    swab16s(&h->        e_shstrndx);                /* Section header string table index */
75
}
76

    
77
void elf_swap_shdr(Elf32_Shdr *h)
78
{
79
  swab32s(&h->        sh_name);                /* Section name (string tbl index) */
80
  swab32s(&h->        sh_type);                /* Section type */
81
  swab32s(&h->        sh_flags);                /* Section flags */
82
  swab32s(&h->        sh_addr);                /* Section virtual addr at execution */
83
  swab32s(&h->        sh_offset);                /* Section file offset */
84
  swab32s(&h->        sh_size);                /* Section size in bytes */
85
  swab32s(&h->        sh_link);                /* Link to another section */
86
  swab32s(&h->        sh_info);                /* Additional section information */
87
  swab32s(&h->        sh_addralign);                /* Section alignment */
88
  swab32s(&h->        sh_entsize);                /* Entry size if section holds table */
89
}
90

    
91
void elf_swap_phdr(Elf32_Phdr *h)
92
{
93
    swab32s(&h->p_type);                        /* Segment type */
94
    swab32s(&h->p_offset);                /* Segment file offset */
95
    swab32s(&h->p_vaddr);                /* Segment virtual address */
96
    swab32s(&h->p_paddr);                /* Segment physical address */
97
    swab32s(&h->p_filesz);                /* Segment size in file */
98
    swab32s(&h->p_memsz);                /* Segment size in memory */
99
    swab32s(&h->p_flags);                /* Segment flags */
100
    swab32s(&h->p_align);                /* Segment alignment */
101
}
102

    
103
int do_swap;
104
int e_machine;
105

    
106
uint16_t get16(uint16_t *p)
107
{
108
    uint16_t val;
109
    val = *p;
110
    if (do_swap)
111
        val = bswap16(val);
112
    return val;
113
}
114

    
115
uint32_t get32(uint32_t *p)
116
{
117
    uint32_t val;
118
    val = *p;
119
    if (do_swap)
120
        val = bswap32(val);
121
    return val;
122
}
123

    
124
void put16(uint16_t *p, uint16_t val)
125
{
126
    if (do_swap)
127
        val = bswap16(val);
128
    *p = val;
129
}
130

    
131
void put32(uint32_t *p, uint32_t val)
132
{
133
    if (do_swap)
134
        val = bswap32(val);
135
    *p = val;
136
}
137

    
138
void __attribute__((noreturn)) error(const char *fmt, ...)
139
{
140
    va_list ap;
141
    va_start(ap, fmt);
142
    fprintf(stderr, "dyngen: ");
143
    vfprintf(stderr, fmt, ap);
144
    fprintf(stderr, "\n");
145
    va_end(ap);
146
    exit(1);
147
}
148

    
149

    
150
Elf32_Shdr *find_elf_section(Elf32_Shdr *shdr, int shnum, const char *shstr, 
151
                             const char *name)
152
{
153
    int i;
154
    const char *shname;
155
    Elf32_Shdr *sec;
156

    
157
    for(i = 0; i < shnum; i++) {
158
        sec = &shdr[i];
159
        if (!sec->sh_name)
160
            continue;
161
        shname = shstr + sec->sh_name;
162
        if (!strcmp(shname, name))
163
            return sec;
164
    }
165
    return NULL;
166
}
167

    
168
void *load_data(int fd, long offset, unsigned int size)
169
{
170
    char *data;
171

    
172
    data = malloc(size);
173
    if (!data)
174
        return NULL;
175
    lseek(fd, offset, SEEK_SET);
176
    if (read(fd, data, size) != size) {
177
        free(data);
178
        return NULL;
179
    }
180
    return data;
181
}
182

    
183
int strstart(const char *str, const char *val, const char **ptr)
184
{
185
    const char *p, *q;
186
    p = str;
187
    q = val;
188
    while (*q != '\0') {
189
        if (*p != *q)
190
            return 0;
191
        p++;
192
        q++;
193
    }
194
    if (ptr)
195
        *ptr = p;
196
    return 1;
197
}
198

    
199
#define MAX_ARGS 3
200

    
201
/* generate op code */
202
void gen_code(const char *name, unsigned long offset, unsigned long size, 
203
              FILE *outfile, uint8_t *text, void *relocs, int nb_relocs, int reloc_sh_type,
204
              Elf32_Sym *symtab, char *strtab)
205
{
206
    int copy_size = 0;
207
    uint8_t *p_start, *p_end;
208
    int nb_args, i;
209
    uint8_t args_present[MAX_ARGS];
210
    const char *sym_name, *p;
211

    
212
    /* compute exact size excluding return instruction */
213
    p_start = text + offset;
214
    p_end = p_start + size;
215
    switch(e_machine) {
216
    case EM_386:
217
        {
218
            uint8_t *p;
219
            p = p_end - 1;
220
            if (p == p_start)
221
                error("empty code for %s", name);
222
            if (p[0] != 0xc3)
223
                error("ret expected at the end of %s", name);
224
            copy_size = p - p_start;
225
        }
226
        break;
227
    case EM_PPC:
228
        {
229
            uint8_t *p;
230
            p = (void *)(p_end - 4);
231
            /* find ret */
232
            while (p > p_start && get32((uint32_t *)p) != 0x4e800020)
233
                p -= 4;
234
            /* skip double ret */
235
            if (p > p_start && get32((uint32_t *)(p - 4)) == 0x4e800020)
236
                p -= 4;
237
            if (p == p_start)
238
                error("empty code for %s", name);
239
            copy_size = p - p_start;
240
        }
241
        break;
242
    default:
243
        error("unsupported CPU (%d)", e_machine);
244
    }
245

    
246
    /* compute the number of arguments by looking at the relocations */
247
    for(i = 0;i < MAX_ARGS; i++)
248
        args_present[i] = 0;
249

    
250
    if (reloc_sh_type == SHT_REL) {
251
        Elf32_Rel *rel;
252
        int n;
253
        for(i = 0, rel = relocs;i < nb_relocs; i++, rel++) {
254
            if (rel->r_offset >= offset && rel->r_offset < offset + copy_size) {
255
                sym_name = strtab + symtab[ELF32_R_SYM(rel->r_info)].st_name;
256
                if (strstart(sym_name, "__op_param", &p)) {
257
                    n = strtoul(p, NULL, 10);
258
                    if (n >= MAX_ARGS)
259
                        error("too many arguments in %s", name);
260
                    args_present[n - 1] = 1;
261
                } else {
262
                    fprintf(outfile, "extern char %s;\n", sym_name);
263
                }
264
            }
265
        }
266
    } else {
267
        Elf32_Rela *rel;
268
        int n;
269
        for(i = 0, rel = relocs;i < nb_relocs; i++, rel++) {
270
            if (rel->r_offset >= offset && rel->r_offset < offset + copy_size) {
271
                sym_name = strtab + symtab[ELF32_R_SYM(rel->r_info)].st_name;
272
                if (strstart(sym_name, "__op_param", &p)) {
273
                    n = strtoul(p, NULL, 10);
274
                    if (n >= MAX_ARGS)
275
                        error("too many arguments in %s", name);
276
                    args_present[n - 1] = 1;
277
                } else {
278
                    fprintf(outfile, "extern char %s;\n", sym_name);
279
                }
280
            }
281
        }
282
    }
283
    
284
    nb_args = 0;
285
    while (nb_args < MAX_ARGS && args_present[nb_args])
286
        nb_args++;
287
    for(i = nb_args; i < MAX_ARGS; i++) {
288
        if (args_present[i])
289
            error("inconsistent argument numbering in %s", name);
290
    }
291

    
292
    /* output C code */
293
    fprintf(outfile, "extern void %s();\n", name);
294
    fprintf(outfile, "static inline void gen_%s(", name);
295
    if (nb_args == 0) {
296
        fprintf(outfile, "void");
297
    } else {
298
        for(i = 0; i < nb_args; i++) {
299
            if (i != 0)
300
                fprintf(outfile, ", ");
301
            fprintf(outfile, "long param%d", i + 1);
302
        }
303
    }
304
    fprintf(outfile, ")\n");
305
    fprintf(outfile, "{\n");
306
    fprintf(outfile, "    memcpy(gen_code_ptr, &%s, %d);\n", name, copy_size);
307
    
308
    /* patch relocations */
309
    switch(e_machine) {
310
    case EM_386:
311
        {
312
            Elf32_Rel *rel;
313
            char name[256];
314
            int type;
315
            long addend;
316
            for(i = 0, rel = relocs;i < nb_relocs; i++, rel++) {
317
                if (rel->r_offset >= offset && rel->r_offset < offset + copy_size) {
318
                    sym_name = strtab + symtab[ELF32_R_SYM(rel->r_info)].st_name;
319
                    if (strstart(sym_name, "__op_param", &p)) {
320
                        snprintf(name, sizeof(name), "param%s", p);
321
                    } else {
322
                        snprintf(name, sizeof(name), "(long)(&%s)", sym_name);
323
                    }
324
                    type = ELF32_R_TYPE(rel->r_info);
325
                    addend = get32((uint32_t *)(text + rel->r_offset));
326
                    switch(type) {
327
                    case R_386_32:
328
                        fprintf(outfile, "    *(uint32_t *)(gen_code_ptr + %ld) = %s + %ld;\n", 
329
                                rel->r_offset - offset, name, addend);
330
                        break;
331
                    case R_386_PC32:
332
                        fprintf(outfile, "    *(uint32_t *)(gen_code_ptr + %ld) = %s - (long)(gen_code_ptr + %ld) + %ld;\n", 
333
                                rel->r_offset - offset, name, rel->r_offset - offset, addend);
334
                        break;
335
                    default:
336
                        error("unsupported i386 relocation (%d)", type);
337
                    }
338
                }
339
            }
340
        }
341
        break;
342
    default:
343
        error("unsupported CPU for relocations (%d)", e_machine);
344
    }
345

    
346

    
347
    fprintf(outfile, "    gen_code_ptr += %d;\n", copy_size);
348
    fprintf(outfile, "}\n\n");
349
}
350

    
351
/* load an elf object file */
352
int load_elf(const char *filename, FILE *outfile)
353
{
354
    int fd;
355
    Elf32_Ehdr ehdr;
356
    Elf32_Shdr *sec, *shdr, *symtab_sec, *strtab_sec, *text_sec;
357
    int i, j, nb_syms;
358
    Elf32_Sym *symtab, *sym;
359
    const char *cpu_name;
360
    char *shstr, *strtab;
361
    uint8_t *text;
362
    void *relocs;
363
    int nb_relocs, reloc_sh_type;
364
    
365
    fd = open(filename, O_RDONLY);
366
    if (fd < 0) 
367
        error("can't open file '%s'", filename);
368
    
369
    /* Read ELF header.  */
370
    if (read(fd, &ehdr, sizeof (ehdr)) != sizeof (ehdr))
371
        error("unable to read file header");
372

    
373
    /* Check ELF identification.  */
374
    if (ehdr.e_ident[EI_MAG0] != ELFMAG0
375
     || ehdr.e_ident[EI_MAG1] != ELFMAG1
376
     || ehdr.e_ident[EI_MAG2] != ELFMAG2
377
     || ehdr.e_ident[EI_MAG3] != ELFMAG3
378
     || ehdr.e_ident[EI_CLASS] != ELFCLASS32
379
     || ehdr.e_ident[EI_VERSION] != EV_CURRENT) {
380
        error("bad ELF header");
381
    }
382

    
383
    do_swap = elf_must_swap(&ehdr);
384
    if (do_swap)
385
        elf_swap_ehdr(&ehdr);
386
    if (ehdr.e_type != ET_REL)
387
        error("ELF object file expected");
388
    if (ehdr.e_version != EV_CURRENT)
389
        error("Invalid ELF version");
390
    e_machine = ehdr.e_machine;
391

    
392
    /* read section headers */
393
    shdr = load_data(fd, ehdr.e_shoff, ehdr.e_shnum * sizeof(Elf32_Shdr));
394
    if (do_swap) {
395
        for(i = 0; i < ehdr.e_shnum; i++) {
396
            elf_swap_shdr(&shdr[i]);
397
        }
398
    }
399

    
400
    sec = &shdr[ehdr.e_shstrndx];
401
    shstr = load_data(fd, sec->sh_offset, sec->sh_size);
402

    
403
    /* text section */
404

    
405
    text_sec = find_elf_section(shdr, ehdr.e_shnum, shstr, ".text");
406
    if (!text_sec)
407
        error("could not find .text section");
408
    text = load_data(fd, text_sec->sh_offset, text_sec->sh_size);
409

    
410
    /* find text relocations, if any */
411
    nb_relocs = 0;
412
    relocs = NULL;
413
    reloc_sh_type = 0;
414
    for(i = 0; i < ehdr.e_shnum; i++) {
415
        sec = &shdr[i];
416
        if ((sec->sh_type == SHT_REL || sec->sh_type == SHT_RELA) &&
417
            sec->sh_info == (text_sec - shdr)) {
418
            reloc_sh_type = sec->sh_type;
419
            relocs = load_data(fd, sec->sh_offset, sec->sh_size);
420
            nb_relocs = sec->sh_size / sec->sh_entsize;
421
            if (do_swap) {
422
                if (sec->sh_type == SHT_REL) {
423
                    Elf32_Rel *rel = relocs;
424
                    for(j = 0, rel = relocs; j < nb_relocs; j++, rel++) {
425
                        swab32s(&rel->r_offset);
426
                        swab32s(&rel->r_info);
427
                    }
428
                } else {
429
                    Elf32_Rela *rel = relocs;
430
                    for(j = 0, rel = relocs; j < nb_relocs; j++, rel++) {
431
                        swab32s(&rel->r_offset);
432
                        swab32s(&rel->r_info);
433
                        swab32s(&rel->r_addend);
434
                    }
435
                }
436
            }
437
            break;
438
        }
439
    }
440

    
441
    symtab_sec = find_elf_section(shdr, ehdr.e_shnum, shstr, ".symtab");
442
    if (!symtab_sec)
443
        error("could not find .symtab section");
444
    strtab_sec = &shdr[symtab_sec->sh_link];
445

    
446
    symtab = load_data(fd, symtab_sec->sh_offset, symtab_sec->sh_size);
447
    strtab = load_data(fd, strtab_sec->sh_offset, strtab_sec->sh_size);
448
    
449
    nb_syms = symtab_sec->sh_size / sizeof(Elf32_Sym);
450
    if (do_swap) {
451
        for(i = 0, sym = symtab; i < nb_syms; i++, sym++) {
452
            swab32s(&sym->st_name);
453
            swab32s(&sym->st_value);
454
            swab32s(&sym->st_size);
455
            swab16s(&sym->st_shndx);
456
        }
457
    }
458

    
459
    switch(e_machine) {
460
    case EM_386:
461
        cpu_name = "i386";
462
        break;
463
    case EM_PPC:
464
        cpu_name = "ppc";
465
        break;
466
    case EM_MIPS:
467
        cpu_name = "mips";
468
        break;
469
    case EM_ARM:
470
        cpu_name = "arm";
471
        break;
472
    case EM_SPARC:
473
        cpu_name = "sparc";
474
        break;
475
    default:
476
        error("unsupported CPU (e_machine=%d)", e_machine);
477
    }
478

    
479
    fprintf(outfile, "#include \"gen-%s.h\"\n\n", cpu_name);
480

    
481
    for(i = 0, sym = symtab; i < nb_syms; i++, sym++) {
482
        const char *name;
483
        name = strtab + sym->st_name;
484
        if (strstart(name, "op_", NULL) ||
485
            strstart(name, "op1_", NULL) ||
486
            strstart(name, "op2_", NULL) ||
487
            strstart(name, "op3_", NULL)) {
488
#if 0
489
            printf("%4d: %s pos=0x%08x len=%d\n", 
490
                   i, name, sym->st_value, sym->st_size);
491
#endif
492
            if (sym->st_shndx != (text_sec - shdr))
493
                error("invalid section for opcode (0x%x)", sym->st_shndx);
494
            gen_code(name, sym->st_value, sym->st_size, outfile, 
495
                     text, relocs, nb_relocs, reloc_sh_type, symtab, strtab);
496
        }
497
    }
498

    
499
    close(fd);
500
    return 0;
501
}
502

    
503
void usage(void)
504
{
505
    printf("dyngen (c) 2003 Fabrice Bellard\n"
506
           "usage: dyngen [-o outfile] objfile\n"
507
           "Generate a dynamic code generator from an object file\n");
508
    exit(1);
509
}
510

    
511
int main(int argc, char **argv)
512
{
513
    int c;
514
    const char *filename, *outfilename;
515
    FILE *outfile;
516

    
517
    outfilename = "out.c";
518
    for(;;) {
519
        c = getopt(argc, argv, "ho:");
520
        if (c == -1)
521
            break;
522
        switch(c) {
523
        case 'h':
524
            usage();
525
            break;
526
        case 'o':
527
            outfilename = optarg;
528
            break;
529
        }
530
    }
531
    if (optind >= argc)
532
        usage();
533
    filename = argv[optind];
534
    outfile = fopen(outfilename, "w");
535
    if (!outfile)
536
        error("could not open '%s'", outfilename);
537
    load_elf(filename, outfile);
538
    fclose(outfile);
539
    return 0;
540
}