Statistics
| Branch: | Revision:

root / dyngen.c @ 4b74fe1f

History | View | Annotate | Download (14.8 kB)

1
#include <stdlib.h>
2
#include <stdio.h>
3
#include <stdarg.h>
4
#include <inttypes.h>
5
#include <elf.h>
6
#include <unistd.h>
7
#include <fcntl.h>
8

    
9
#include "thunk.h"
10

    
11
/* all dynamically generated functions begin with this code */
12
#define OP_PREFIX "op"
13

    
14
int elf_must_swap(Elf32_Ehdr *h)
15
{
16
  union {
17
      uint32_t i;
18
      uint8_t b[4];
19
  } swaptest;
20

    
21
  swaptest.i = 1;
22
  return (h->e_ident[EI_DATA] == ELFDATA2MSB) != 
23
      (swaptest.b[0] == 0);
24
}
25
  
26
void swab16s(uint16_t *p)
27
{
28
    *p = bswap16(*p);
29
}
30

    
31
void swab32s(uint32_t *p)
32
{
33
    *p = bswap32(*p);
34
}
35

    
36
void swab64s(uint32_t *p)
37
{
38
    *p = bswap64(*p);
39
}
40

    
41
void elf_swap_ehdr(Elf32_Ehdr *h)
42
{
43
    swab16s(&h->e_type);                        /* Object file type */
44
    swab16s(&h->        e_machine);                /* Architecture */
45
    swab32s(&h->        e_version);                /* Object file version */
46
    swab32s(&h->        e_entry);                /* Entry point virtual address */
47
    swab32s(&h->        e_phoff);                /* Program header table file offset */
48
    swab32s(&h->        e_shoff);                /* Section header table file offset */
49
    swab32s(&h->        e_flags);                /* Processor-specific flags */
50
    swab16s(&h->        e_ehsize);                /* ELF header size in bytes */
51
    swab16s(&h->        e_phentsize);                /* Program header table entry size */
52
    swab16s(&h->        e_phnum);                /* Program header table entry count */
53
    swab16s(&h->        e_shentsize);                /* Section header table entry size */
54
    swab16s(&h->        e_shnum);                /* Section header table entry count */
55
    swab16s(&h->        e_shstrndx);                /* Section header string table index */
56
}
57

    
58
void elf_swap_shdr(Elf32_Shdr *h)
59
{
60
  swab32s(&h->        sh_name);                /* Section name (string tbl index) */
61
  swab32s(&h->        sh_type);                /* Section type */
62
  swab32s(&h->        sh_flags);                /* Section flags */
63
  swab32s(&h->        sh_addr);                /* Section virtual addr at execution */
64
  swab32s(&h->        sh_offset);                /* Section file offset */
65
  swab32s(&h->        sh_size);                /* Section size in bytes */
66
  swab32s(&h->        sh_link);                /* Link to another section */
67
  swab32s(&h->        sh_info);                /* Additional section information */
68
  swab32s(&h->        sh_addralign);                /* Section alignment */
69
  swab32s(&h->        sh_entsize);                /* Entry size if section holds table */
70
}
71

    
72
void elf_swap_phdr(Elf32_Phdr *h)
73
{
74
    swab32s(&h->p_type);                        /* Segment type */
75
    swab32s(&h->p_offset);                /* Segment file offset */
76
    swab32s(&h->p_vaddr);                /* Segment virtual address */
77
    swab32s(&h->p_paddr);                /* Segment physical address */
78
    swab32s(&h->p_filesz);                /* Segment size in file */
79
    swab32s(&h->p_memsz);                /* Segment size in memory */
80
    swab32s(&h->p_flags);                /* Segment flags */
81
    swab32s(&h->p_align);                /* Segment alignment */
82
}
83

    
84
int do_swap;
85
int e_machine;
86

    
87
uint16_t get16(uint16_t *p)
88
{
89
    uint16_t val;
90
    val = *p;
91
    if (do_swap)
92
        val = bswap16(val);
93
    return val;
94
}
95

    
96
uint32_t get32(uint32_t *p)
97
{
98
    uint32_t val;
99
    val = *p;
100
    if (do_swap)
101
        val = bswap32(val);
102
    return val;
103
}
104

    
105
void put16(uint16_t *p, uint16_t val)
106
{
107
    if (do_swap)
108
        val = bswap16(val);
109
    *p = val;
110
}
111

    
112
void put32(uint32_t *p, uint32_t val)
113
{
114
    if (do_swap)
115
        val = bswap32(val);
116
    *p = val;
117
}
118

    
119
void __attribute__((noreturn)) error(const char *fmt, ...)
120
{
121
    va_list ap;
122
    va_start(ap, fmt);
123
    fprintf(stderr, "dyngen: ");
124
    vfprintf(stderr, fmt, ap);
125
    fprintf(stderr, "\n");
126
    va_end(ap);
127
    exit(1);
128
}
129

    
130

    
131
Elf32_Shdr *find_elf_section(Elf32_Shdr *shdr, int shnum, const char *shstr, 
132
                             const char *name)
133
{
134
    int i;
135
    const char *shname;
136
    Elf32_Shdr *sec;
137

    
138
    for(i = 0; i < shnum; i++) {
139
        sec = &shdr[i];
140
        if (!sec->sh_name)
141
            continue;
142
        shname = shstr + sec->sh_name;
143
        if (!strcmp(shname, name))
144
            return sec;
145
    }
146
    return NULL;
147
}
148

    
149
void *load_data(int fd, long offset, unsigned int size)
150
{
151
    char *data;
152

    
153
    data = malloc(size);
154
    if (!data)
155
        return NULL;
156
    lseek(fd, offset, SEEK_SET);
157
    if (read(fd, data, size) != size) {
158
        free(data);
159
        return NULL;
160
    }
161
    return data;
162
}
163

    
164
int strstart(const char *str, const char *val, const char **ptr)
165
{
166
    const char *p, *q;
167
    p = str;
168
    q = val;
169
    while (*q != '\0') {
170
        if (*p != *q)
171
            return 0;
172
        p++;
173
        q++;
174
    }
175
    if (ptr)
176
        *ptr = p;
177
    return 1;
178
}
179

    
180
#define MAX_ARGS 3
181

    
182
/* generate op code */
183
void gen_code(const char *name, unsigned long offset, unsigned long size, 
184
              FILE *outfile, uint8_t *text, void *relocs, int nb_relocs, int reloc_sh_type,
185
              Elf32_Sym *symtab, char *strtab)
186
{
187
    int copy_size = 0;
188
    uint8_t *p_start, *p_end;
189
    int nb_args, i;
190
    uint8_t args_present[MAX_ARGS];
191
    const char *sym_name, *p;
192

    
193
    /* compute exact size excluding return instruction */
194
    p_start = text + offset;
195
    p_end = p_start + size;
196
    switch(e_machine) {
197
    case EM_386:
198
        {
199
            uint8_t *p;
200
            p = p_end - 1;
201
            if (p == p_start)
202
                error("empty code for %s", name);
203
            if (p[0] != 0xc3)
204
                error("ret expected at the end of %s", name);
205
            copy_size = p - p_start;
206
        }
207
        break;
208
    case EM_PPC:
209
        {
210
            uint8_t *p;
211
            p = (void *)(p_end - 4);
212
            /* find ret */
213
            while (p > p_start && get32((uint32_t *)p) != 0x4e800020)
214
                p -= 4;
215
            /* skip double ret */
216
            if (p > p_start && get32((uint32_t *)(p - 4)) == 0x4e800020)
217
                p -= 4;
218
            if (p == p_start)
219
                error("empty code for %s", name);
220
            copy_size = p - p_start;
221
        }
222
        break;
223
    default:
224
        error("unsupported CPU (%d)", e_machine);
225
    }
226

    
227
    /* compute the number of arguments by looking at the relocations */
228
    for(i = 0;i < MAX_ARGS; i++)
229
        args_present[i] = 0;
230

    
231
    if (reloc_sh_type == SHT_REL) {
232
        Elf32_Rel *rel;
233
        int n;
234
        for(i = 0, rel = relocs;i < nb_relocs; i++, rel++) {
235
            if (rel->r_offset >= offset && rel->r_offset < offset + copy_size) {
236
                sym_name = strtab + symtab[ELF32_R_SYM(rel->r_info)].st_name;
237
                if (strstart(sym_name, "__op_param", &p)) {
238
                    n = strtoul(p, NULL, 10);
239
                    if (n >= MAX_ARGS)
240
                        error("too many arguments in %s", name);
241
                    args_present[n - 1] = 1;
242
                } else {
243
                    fprintf(outfile, "extern char %s;\n", sym_name);
244
                }
245
            }
246
        }
247
    } else {
248
        Elf32_Rela *rel;
249
        int n;
250
        for(i = 0, rel = relocs;i < nb_relocs; i++, rel++) {
251
            if (rel->r_offset >= offset && rel->r_offset < offset + copy_size) {
252
                sym_name = strtab + symtab[ELF32_R_SYM(rel->r_info)].st_name;
253
                if (strstart(sym_name, "__op_param", &p)) {
254
                    n = strtoul(p, NULL, 10);
255
                    if (n >= MAX_ARGS)
256
                        error("too many arguments in %s", name);
257
                    args_present[n - 1] = 1;
258
                } else {
259
                    fprintf(outfile, "extern char %s;\n", sym_name);
260
                }
261
            }
262
        }
263
    }
264
    
265
    nb_args = 0;
266
    while (nb_args < MAX_ARGS && args_present[nb_args])
267
        nb_args++;
268
    for(i = nb_args; i < MAX_ARGS; i++) {
269
        if (args_present[i])
270
            error("inconsistent argument numbering in %s", name);
271
    }
272

    
273
    /* output C code */
274
    fprintf(outfile, "extern void %s();\n", name);
275
    fprintf(outfile, "static inline void gen_%s(", name);
276
    if (nb_args == 0) {
277
        fprintf(outfile, "void");
278
    } else {
279
        for(i = 0; i < nb_args; i++) {
280
            if (i != 0)
281
                fprintf(outfile, ", ");
282
            fprintf(outfile, "long param%d", i + 1);
283
        }
284
    }
285
    fprintf(outfile, ")\n");
286
    fprintf(outfile, "{\n");
287
    fprintf(outfile, "    memcpy(gen_code_ptr, &%s, %d);\n", name, copy_size);
288
    
289
    /* patch relocations */
290
    switch(e_machine) {
291
    case EM_386:
292
        {
293
            Elf32_Rel *rel;
294
            char name[256];
295
            int type;
296
            long addend;
297
            for(i = 0, rel = relocs;i < nb_relocs; i++, rel++) {
298
                if (rel->r_offset >= offset && rel->r_offset < offset + copy_size) {
299
                    sym_name = strtab + symtab[ELF32_R_SYM(rel->r_info)].st_name;
300
                    if (strstart(sym_name, "__op_param", &p)) {
301
                        snprintf(name, sizeof(name), "param%s", p);
302
                    } else {
303
                        snprintf(name, sizeof(name), "(long)(&%s)", sym_name);
304
                    }
305
                    type = ELF32_R_TYPE(rel->r_info);
306
                    addend = get32((uint32_t *)(text + rel->r_offset));
307
                    switch(type) {
308
                    case R_386_32:
309
                        fprintf(outfile, "    *(uint32_t *)(gen_code_ptr + %ld) = %s + %ld;\n", 
310
                                rel->r_offset - offset, name, addend);
311
                        break;
312
                    case R_386_PC32:
313
                        fprintf(outfile, "    *(uint32_t *)(gen_code_ptr + %ld) = %s - (long)(gen_code_ptr + %ld) + %ld;\n", 
314
                                rel->r_offset - offset, name, rel->r_offset - offset, addend);
315
                        break;
316
                    default:
317
                        error("unsupported i386 relocation (%d)", type);
318
                    }
319
                }
320
            }
321
        }
322
        break;
323
    default:
324
        error("unsupported CPU for relocations (%d)", e_machine);
325
    }
326

    
327

    
328
    fprintf(outfile, "    gen_code_ptr += %d;\n", copy_size);
329
    fprintf(outfile, "}\n\n");
330
}
331

    
332
/* load an elf object file */
333
int load_elf(const char *filename, FILE *outfile)
334
{
335
    int fd;
336
    Elf32_Ehdr ehdr;
337
    Elf32_Shdr *sec, *shdr, *symtab_sec, *strtab_sec, *text_sec;
338
    int i, j, nb_syms;
339
    Elf32_Sym *symtab, *sym;
340
    const char *cpu_name;
341
    char *shstr, *strtab;
342
    uint8_t *text;
343
    void *relocs;
344
    int nb_relocs, reloc_sh_type;
345
    
346
    fd = open(filename, O_RDONLY);
347
    if (fd < 0) 
348
        error("can't open file '%s'", filename);
349
    
350
    /* Read ELF header.  */
351
    if (read(fd, &ehdr, sizeof (ehdr)) != sizeof (ehdr))
352
        error("unable to read file header");
353

    
354
    /* Check ELF identification.  */
355
    if (ehdr.e_ident[EI_MAG0] != ELFMAG0
356
     || ehdr.e_ident[EI_MAG1] != ELFMAG1
357
     || ehdr.e_ident[EI_MAG2] != ELFMAG2
358
     || ehdr.e_ident[EI_MAG3] != ELFMAG3
359
     || ehdr.e_ident[EI_CLASS] != ELFCLASS32
360
     || ehdr.e_ident[EI_VERSION] != EV_CURRENT) {
361
        error("bad ELF header");
362
    }
363

    
364
    do_swap = elf_must_swap(&ehdr);
365
    if (do_swap)
366
        elf_swap_ehdr(&ehdr);
367
    if (ehdr.e_type != ET_REL)
368
        error("ELF object file expected");
369
    if (ehdr.e_version != EV_CURRENT)
370
        error("Invalid ELF version");
371
    e_machine = ehdr.e_machine;
372

    
373
    /* read section headers */
374
    shdr = load_data(fd, ehdr.e_shoff, ehdr.e_shnum * sizeof(Elf32_Shdr));
375
    if (do_swap) {
376
        for(i = 0; i < ehdr.e_shnum; i++) {
377
            elf_swap_shdr(&shdr[i]);
378
        }
379
    }
380

    
381
    sec = &shdr[ehdr.e_shstrndx];
382
    shstr = load_data(fd, sec->sh_offset, sec->sh_size);
383

    
384
    /* text section */
385

    
386
    text_sec = find_elf_section(shdr, ehdr.e_shnum, shstr, ".text");
387
    if (!text_sec)
388
        error("could not find .text section");
389
    text = load_data(fd, text_sec->sh_offset, text_sec->sh_size);
390

    
391
    /* find text relocations, if any */
392
    nb_relocs = 0;
393
    relocs = NULL;
394
    reloc_sh_type = 0;
395
    for(i = 0; i < ehdr.e_shnum; i++) {
396
        sec = &shdr[i];
397
        if ((sec->sh_type == SHT_REL || sec->sh_type == SHT_RELA) &&
398
            sec->sh_info == (text_sec - shdr)) {
399
            reloc_sh_type = sec->sh_type;
400
            relocs = load_data(fd, sec->sh_offset, sec->sh_size);
401
            nb_relocs = sec->sh_size / sec->sh_entsize;
402
            if (do_swap) {
403
                if (sec->sh_type == SHT_REL) {
404
                    Elf32_Rel *rel = relocs;
405
                    for(j = 0, rel = relocs; j < nb_relocs; j++, rel++) {
406
                        swab32s(&rel->r_offset);
407
                        swab32s(&rel->r_info);
408
                    }
409
                } else {
410
                    Elf32_Rela *rel = relocs;
411
                    for(j = 0, rel = relocs; j < nb_relocs; j++, rel++) {
412
                        swab32s(&rel->r_offset);
413
                        swab32s(&rel->r_info);
414
                        swab32s(&rel->r_addend);
415
                    }
416
                }
417
            }
418
            break;
419
        }
420
    }
421

    
422
    symtab_sec = find_elf_section(shdr, ehdr.e_shnum, shstr, ".symtab");
423
    if (!symtab_sec)
424
        error("could not find .symtab section");
425
    strtab_sec = &shdr[symtab_sec->sh_link];
426

    
427
    symtab = load_data(fd, symtab_sec->sh_offset, symtab_sec->sh_size);
428
    strtab = load_data(fd, strtab_sec->sh_offset, strtab_sec->sh_size);
429
    
430
    nb_syms = symtab_sec->sh_size / sizeof(Elf32_Sym);
431
    if (do_swap) {
432
        for(i = 0, sym = symtab; i < nb_syms; i++, sym++) {
433
            swab32s(&sym->st_name);
434
            swab32s(&sym->st_value);
435
            swab32s(&sym->st_size);
436
            swab16s(&sym->st_shndx);
437
        }
438
    }
439

    
440
    switch(e_machine) {
441
    case EM_386:
442
        cpu_name = "i386";
443
        break;
444
    case EM_PPC:
445
        cpu_name = "ppc";
446
        break;
447
    case EM_MIPS:
448
        cpu_name = "mips";
449
        break;
450
    case EM_ARM:
451
        cpu_name = "arm";
452
        break;
453
    case EM_SPARC:
454
        cpu_name = "sparc";
455
        break;
456
    default:
457
        error("unsupported CPU (e_machine=%d)", e_machine);
458
    }
459

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

    
462
    for(i = 0, sym = symtab; i < nb_syms; i++, sym++) {
463
        const char *name;
464
        name = strtab + sym->st_name;
465
        if (strstart(name, "op_", NULL) ||
466
            strstart(name, "op1_", NULL) ||
467
            strstart(name, "op2_", NULL) ||
468
            strstart(name, "op3_", NULL)) {
469
#if 0
470
            printf("%4d: %s pos=0x%08x len=%d\n", 
471
                   i, name, sym->st_value, sym->st_size);
472
#endif
473
            if (sym->st_shndx != (text_sec - shdr))
474
                error("invalid section for opcode (0x%x)", sym->st_shndx);
475
            gen_code(name, sym->st_value, sym->st_size, outfile, 
476
                     text, relocs, nb_relocs, reloc_sh_type, symtab, strtab);
477
        }
478
    }
479

    
480
    close(fd);
481
    return 0;
482
}
483

    
484
void usage(void)
485
{
486
    printf("dyngen (c) 2003 Fabrice Bellard\n"
487
           "usage: dyngen [-o outfile] objfile\n"
488
           "Generate a dynamic code generator from an object file\n");
489
    exit(1);
490
}
491

    
492
int main(int argc, char **argv)
493
{
494
    int c;
495
    const char *filename, *outfilename;
496
    FILE *outfile;
497

    
498
    outfilename = "out.c";
499
    for(;;) {
500
        c = getopt(argc, argv, "ho:");
501
        if (c == -1)
502
            break;
503
        switch(c) {
504
        case 'h':
505
            usage();
506
            break;
507
        case 'o':
508
            outfilename = optarg;
509
            break;
510
        }
511
    }
512
    if (optind >= argc)
513
        usage();
514
    filename = argv[optind];
515
    outfile = fopen(outfilename, "w");
516
    if (!outfile)
517
        error("could not open '%s'", outfilename);
518
    load_elf(filename, outfile);
519
    fclose(outfile);
520
    return 0;
521
}