Statistics
| Branch: | Revision:

root / dyngen.c @ 927f621e

History | View | Annotate | Download (14.9 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
            /* find ret */
202
            while (p > p_start && *p != 0xc3)
203
                p--;
204
            /* skip double ret */
205
            if (p > p_start && p[-1] == 0xc3)
206
                p--;
207
            if (p == p_start)
208
                error("empty code for %s", name);
209
            copy_size = p - p_start;
210
        }
211
        break;
212
    case EM_PPC:
213
        {
214
            uint8_t *p;
215
            p = (void *)(p_end - 4);
216
            /* find ret */
217
            while (p > p_start && get32((uint32_t *)p) != 0x4e800020)
218
                p -= 4;
219
            /* skip double ret */
220
            if (p > p_start && get32((uint32_t *)(p - 4)) == 0x4e800020)
221
                p -= 4;
222
            if (p == p_start)
223
                error("empty code for %s", name);
224
            copy_size = p - p_start;
225
        }
226
        break;
227
    default:
228
        error("unsupported CPU (%d)", e_machine);
229
    }
230

    
231
    /* compute the number of arguments by looking at the relocations */
232
    for(i = 0;i < MAX_ARGS; i++)
233
        args_present[i] = 0;
234

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

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

    
331

    
332
    fprintf(outfile, "    gen_code_ptr += %d;\n", copy_size);
333
    fprintf(outfile, "}\n\n");
334
}
335

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

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

    
368
    do_swap = elf_must_swap(&ehdr);
369
    if (do_swap)
370
        elf_swap_ehdr(&ehdr);
371
    if (ehdr.e_type != ET_REL)
372
        error("ELF object file expected");
373
    if (ehdr.e_version != EV_CURRENT)
374
        error("Invalid ELF version");
375
    e_machine = ehdr.e_machine;
376

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

    
385
    sec = &shdr[ehdr.e_shstrndx];
386
    shstr = load_data(fd, sec->sh_offset, sec->sh_size);
387

    
388
    /* text section */
389

    
390
    text_sec = find_elf_section(shdr, ehdr.e_shnum, shstr, ".text");
391
    if (!text_sec)
392
        error("could not find .text section");
393
    text = load_data(fd, text_sec->sh_offset, text_sec->sh_size);
394

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

    
426
    symtab_sec = find_elf_section(shdr, ehdr.e_shnum, shstr, ".symtab");
427
    if (!symtab_sec)
428
        error("could not find .symtab section");
429
    strtab_sec = &shdr[symtab_sec->sh_link];
430

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

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

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

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

    
484
    close(fd);
485
    return 0;
486
}
487

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

    
496
int main(int argc, char **argv)
497
{
498
    int c;
499
    const char *filename, *outfilename;
500
    FILE *outfile;
501

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