[BACK]Return to elf2aout.c CVS log [TXT][DIR] Up to [local] / src / usr.bin / elf2aout

Annotation of src/usr.bin/elf2aout/elf2aout.c, Revision 1.3

1.3     ! deraadt     1: /* $OpenBSD: elf2aout.c,v 1.2 2001/01/29 01:57:56 niklas Exp $  */
1.2       niklas      2:
1.1       graichen    3: /*
                      4:  * Copyright (c) 1995
                      5:  *     Ted Lemon (hereinafter referred to as the author)
                      6:  *
                      7:  * Redistribution and use in source and binary forms, with or without
                      8:  * modification, are permitted provided that the following conditions
                      9:  * are met:
                     10:  * 1. Redistributions of source code must retain the above copyright
                     11:  *    notice, this list of conditions and the following disclaimer.
                     12:  * 2. Redistributions in binary form must reproduce the above copyright
                     13:  *    notice, this list of conditions and the following disclaimer in the
                     14:  *    documentation and/or other materials provided with the distribution.
                     15:  * 3. The name of the author may not be used to endorse or promote products
                     16:  *    derived from this software without specific prior written permission.
                     17:  *
                     18:  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND
                     19:  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
                     20:  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
                     21:  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE
                     22:  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
                     23:  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
                     24:  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
                     25:  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
                     26:  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
                     27:  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
                     28:  * SUCH DAMAGE.
                     29:  */
                     30:
                     31: #include <sys/types.h>
                     32: #include <fcntl.h>
                     33: #include <unistd.h>
                     34: #include <elf_abi.h>
                     35: #include <machine/elf_abi.h>
                     36: #include <stdio.h>
                     37: #include <a.out.h>
                     38: #include <sys/errno.h>
                     39: #include <string.h>
                     40: #include <limits.h>
                     41:
                     42: #define SHN_MIPS_ACOMMON 0xfff0
                     43:
1.3     ! deraadt    44: extern char    *__progname;
1.1       graichen   45:
                     46: struct sect {
1.3     ! deraadt    47:        unsigned long   vaddr;
        !            48:        unsigned long   len;
1.1       graichen   49: };
1.3     ! deraadt    50: int             phcmp();
        !            51: char           *saveRead(int file, off_t offset, off_t len, char *name);
        !            52: int             copy(int, int, off_t, off_t);
        !            53: int             translate_syms(int, int, off_t, off_t, off_t, off_t);
        !            54: extern int      errno;
        !            55: int            *symTypeTable;
1.1       graichen   56:
                     57: /* Symbol table entry... */
                     58: struct sym {
1.3     ! deraadt    59:        unsigned long   name;   /* Index into strtab of symbol name. */
        !            60:        unsigned long   value;  /* Section offset, virt addr or common align. */
        !            61:        unsigned long   size;   /* Size of object referenced. */
        !            62:        unsigned        type:4; /* Symbol type (e.g., function, data)... */
        !            63:        unsigned        binding:4;      /* Symbol binding (e.g., global,
        !            64:                                         * local)... */
        !            65:        unsigned char   other;  /* Unused. */
        !            66:        unsigned short  shndx;  /* Section containing symbol. */
1.1       graichen   67: };
                     68:
                     69: struct phdr {
1.3     ! deraadt    70:        unsigned long   type;   /* Segment type... */
        !            71:        unsigned long   offset; /* File offset... */
        !            72:        unsigned long   vaddr;  /* Virtual address... */
        !            73:        unsigned long   paddr;  /* Physical address... */
        !            74:        unsigned long   filesz; /* Size of segment in file... */
        !            75:        unsigned long   memsz;  /* Size of segment in memory... */
        !            76:        unsigned long   flags;  /* Segment flags... */
        !            77:        unsigned long   align;  /* Alighment, file and memory... */
1.1       graichen   78: };
                     79:
1.3     ! deraadt    80: int
        !            81: main(int argc, char *argv[])
1.1       graichen   82: {
1.3     ! deraadt    83:        Elf32_Ehdr      ex;
        !            84:        Elf32_Phdr     *ph;
        !            85:        Elf32_Shdr     *sh;
        !            86:        struct sym     *symtab;
        !            87:        char           *shstrtab;
        !            88:        int             strtabix, symtabix;
        !            89:        int             i;
        !            90:        struct sect     text, data, bss;
        !            91:        struct exec     aex;
        !            92:        int             infile, outfile;
        !            93:        unsigned long   cur_vma = ULONG_MAX;
        !            94:        int             symflag = 0;
        !            95:
        !            96:        text.len = data.len = bss.len = 0;
        !            97:        text.vaddr = data.vaddr = bss.vaddr = 0;
        !            98:
        !            99:        /* Check args... */
        !           100:        if (argc < 3 || argc > 4) {
        !           101: usage:
        !           102:                fprintf(stderr,
        !           103:                    "usage: %s elf a.out\n", __progname);
        !           104:                exit(1);
        !           105:        }
        !           106:        /* Try the input file... */
        !           107:        if ((infile = open(argv[1], O_RDONLY)) < 0) {
        !           108:                fprintf(stderr, "Can't open %s for read: %s\n",
        !           109:                        argv[1], strerror(errno));
        !           110:                exit(1);
        !           111:        }
        !           112:        /* Read the header, which is at the beginning of the file... */
        !           113:        i = read(infile, &ex, sizeof ex);
        !           114:        if (i != sizeof ex) {
        !           115:                fprintf(stderr, "ex: %s: %s.\n",
        !           116:                    argv[1], i ? strerror(errno) : "End of file reached");
        !           117:                exit(1);
        !           118:        }
        !           119:        /* Read the program headers... */
        !           120:        ph = (Elf32_Phdr *) saveRead(infile, ex.e_phoff,
        !           121:                                     ex.e_phnum * sizeof(Elf32_Phdr), "ph");
        !           122:        /* Read the section headers... */
        !           123:        sh = (Elf32_Shdr *) saveRead(infile, ex.e_shoff,
        !           124:                                     ex.e_shnum * sizeof(Elf32_Shdr), "sh");
        !           125:        /* Read in the section string table. */
        !           126:        shstrtab = saveRead(infile, sh[ex.e_shstrndx].sh_offset,
        !           127:                            sh[ex.e_shstrndx].sh_size, "shstrtab");
        !           128:
        !           129:        /*
        !           130:         * Find space for a table matching ELF section indices to a.out
        !           131:         * symbol types.
        !           132:         */
        !           133:        symTypeTable = (int *) malloc(ex.e_shnum * sizeof(int));
        !           134:        if (!symTypeTable) {
        !           135:                fprintf(stderr, "symTypeTable: can't allocate.\n");
        !           136:                exit(1);
        !           137:        }
        !           138:        memset(symTypeTable, 0, ex.e_shnum * sizeof(int));
        !           139:
        !           140:        /*
        !           141:         * Look for the symbol table and string table... Also map section
        !           142:         * indices to symbol types for a.out
        !           143:         */
        !           144:        for (i = 0; i < ex.e_shnum; i++) {
        !           145:                char           *name = shstrtab + sh[i].sh_name;
        !           146:                if (!strcmp(name, ".symtab"))
        !           147:                        symtabix = i;
        !           148:                else if (!strcmp(name, ".strtab"))
        !           149:                        strtabix = i;
        !           150:                else if (!strcmp(name, ".text") || !strcmp(name, ".rodata"))
        !           151:                        symTypeTable[i] = N_TEXT;
        !           152:                else if (!strcmp(name, ".data") || !strcmp(name, ".sdata") ||
        !           153:                         !strcmp(name, ".lit4") || !strcmp(name, ".lit8"))
        !           154:                        symTypeTable[i] = N_DATA;
        !           155:                else if (!strcmp(name, ".bss") || !strcmp(name, ".sbss"))
        !           156:                        symTypeTable[i] = N_BSS;
        !           157:        }
        !           158:
        !           159:        /*
        !           160:         * Figure out if we can cram the program header into an a.out
        !           161:         * header... Basically, we can't handle anything but loadable
        !           162:         * segments, but we can ignore some kinds of segments.   We can't
        !           163:         * handle holes in the address space, and we handle start addresses
        !           164:         * other than 0x1000 by hoping that the loader will know where to
        !           165:         * load - a.out doesn't have an explicit load address.   Segments may
        !           166:         * be out of order, so we sort them first.
        !           167:         */
        !           168:        qsort(ph, ex.e_phnum, sizeof(Elf32_Phdr), phcmp);
        !           169:        for (i = 0; i < ex.e_phnum; i++) {
        !           170:                /* Section types we can ignore... */
        !           171:                if (ph[i].p_type == PT_NULL || ph[i].p_type == PT_NOTE ||
        !           172:                 ph[i].p_type == PT_PHDR || ph[i].p_type == PT_MIPS_REGINFO)
        !           173:                        continue;
        !           174:                /* Section types we can't handle... */
        !           175:                else if (ph[i].p_type != PT_LOAD) {
        !           176:                        fprintf(stderr,
        !           177:                            "Program header %d type %d can't be converted.\n");
        !           178:                        exit(1);
        !           179:                }
        !           180:                /* Writable (data) segment? */
        !           181:                if (ph[i].p_flags & PF_W) {
        !           182:                        struct sect     ndata, nbss;
        !           183:
        !           184:                        ndata.vaddr = ph[i].p_vaddr;
        !           185:                        ndata.len = ph[i].p_filesz;
        !           186:                        nbss.vaddr = ph[i].p_vaddr + ph[i].p_filesz;
        !           187:                        nbss.len = ph[i].p_memsz - ph[i].p_filesz;
        !           188:
        !           189:                        combine(&data, &ndata, 0);
        !           190:                        combine(&bss, &nbss, 1);
        !           191:                } else {
        !           192:                        struct sect     ntxt;
        !           193:
        !           194:                        ntxt.vaddr = ph[i].p_vaddr;
        !           195:                        ntxt.len = ph[i].p_filesz;
        !           196:
        !           197:                        combine(&text, &ntxt);
1.1       graichen  198:                }
1.3     ! deraadt   199:                /* Remember the lowest segment start address. */
        !           200:                if (ph[i].p_vaddr < cur_vma)
        !           201:                        cur_vma = ph[i].p_vaddr;
        !           202:        }
        !           203:
        !           204:        /* Sections must be in order to be converted... */
        !           205:        if (text.vaddr > data.vaddr || data.vaddr > bss.vaddr ||
        !           206:            text.vaddr + text.len > data.vaddr || data.vaddr + data.len > bss.vaddr) {
        !           207:                fprintf(stderr, "Sections ordering prevents a.out conversion.\n");
        !           208:                exit(1);
        !           209:        }
        !           210:        /*
        !           211:         * If there's a data section but no text section, then the loader
        !           212:         * combined everything into one section.   That needs to be the text
        !           213:         * section, so just make the data section zero length following text.
        !           214:         */
        !           215:        if (data.len && !text.len) {
        !           216:                text = data;
        !           217:                data.vaddr = text.vaddr + text.len;
        !           218:                data.len = 0;
        !           219:        }
        !           220:        /*
        !           221:         * If there is a gap between text and data, we'll fill it when we
        !           222:         * copy the data, so update the length of the text segment as
        !           223:         * represented in a.out to reflect that, since a.out doesn't allow
        !           224:         * gaps in the program address space.
        !           225:         */
        !           226:        if (text.vaddr + text.len < data.vaddr)
        !           227:                text.len = data.vaddr - text.vaddr;
        !           228:
        !           229:        /* We now have enough information to cons up an a.out header... */
        !           230:        aex.a_midmag = htonl((symflag << 26) | (MID_PMAX << 16) | OMAGIC);
        !           231:        aex.a_text = text.len;
        !           232:        aex.a_data = data.len;
        !           233:        aex.a_bss = bss.len;
        !           234:        aex.a_entry = ex.e_entry;
        !           235:        aex.a_syms = (sizeof(struct nlist) *
        !           236:                      (symtabix != -1
        !           237:                       ? sh[symtabix].sh_size / sizeof(struct sym) : 0));
        !           238:        aex.a_trsize = 0;
        !           239:        aex.a_drsize = 0;
        !           240:
        !           241:        /* Make the output file... */
        !           242:        if ((outfile = open(argv[2], O_WRONLY | O_CREAT, 0777)) < 0) {
        !           243:                fprintf(stderr, "Unable to create %s: %s\n", argv[2], strerror(errno));
        !           244:                exit(1);
        !           245:        }
        !           246:        /* Write the header... */
        !           247:        i = write(outfile, &aex, sizeof aex);
        !           248:        if (i != sizeof aex) {
        !           249:                perror("aex: write");
        !           250:                exit(1);
        !           251:        }
        !           252:        /*
        !           253:         * Copy the loadable sections.   Zero-fill any gaps less than 64k;
        !           254:         * complain about any zero-filling, and die if we're asked to
        !           255:         * zero-fill more than 64k.
        !           256:         */
        !           257:        for (i = 0; i < ex.e_phnum; i++) {
        !           258:                /*
        !           259:                 * Unprocessable sections were handled above, so just verify
        !           260:                 * that the section can be loaded before copying.
        !           261:                 */
        !           262:                if (ph[i].p_type == PT_LOAD && ph[i].p_filesz) {
        !           263:                        if (cur_vma != ph[i].p_vaddr) {
        !           264:                                unsigned long   gap = ph[i].p_vaddr - cur_vma;
        !           265:                                char obuf[1024];
        !           266:
        !           267:                                if (gap > 65536) {
        !           268:                                        fprintf(stderr,
        !           269:                                            "Intersegment gap (%d bytes) too large.\n",
        !           270:                                            gap);
        !           271:                                        exit(1);
        !           272:                                }
        !           273:                                fprintf(stderr,
        !           274:                                    "Warning: %d byte intersegment gap.\n", gap);
        !           275:                                memset(obuf, 0, sizeof obuf);
        !           276:                                while (gap) {
        !           277:                                        int count = write(outfile, obuf,
        !           278:                                            (gap > sizeof obuf ? sizeof obuf : gap));
        !           279:                                        if (count < 0) {
        !           280:                                                fprintf(stderr,
        !           281:                                                    "Error writing gap: %s\n",
        !           282:                                                    strerror(errno));
        !           283:                                                exit(1);
        !           284:                                        }
        !           285:                                        gap -= count;
        !           286:                                }
        !           287:                        }
        !           288:                        copy(outfile, infile, ph[i].p_offset, ph[i].p_filesz);
        !           289:                        cur_vma = ph[i].p_vaddr + ph[i].p_filesz;
1.1       graichen  290:                }
1.3     ! deraadt   291:        }
        !           292:
        !           293:        /* Copy and translate the symbol table... */
        !           294:        translate_syms(outfile, infile, sh[symtabix].sh_offset,
        !           295:                       sh[symtabix].sh_size,
        !           296:                       sh[strtabix].sh_offset, sh[strtabix].sh_size);
1.1       graichen  297:
1.3     ! deraadt   298:        /* Looks like we won... */
        !           299:        exit(0);
1.1       graichen  300: }
                    301:
1.3     ! deraadt   302: /*
        !           303:  * translate_syms (out, in, offset, size)
        !           304:  *
        !           305:  * Read the ELF symbol table from in at offset; translate it into a.out nlist
        !           306:  * format and write it to out.
        !           307:  */
1.1       graichen  308:
1.3     ! deraadt   309: translate_syms(int out, int in, off_t symoff, off_t symsize, off_t stroff,
        !           310:     off_t strsize)
        !           311: {
        !           312: #define SYMS_PER_PASS  64
        !           313:        struct sym      inbuf[64];
        !           314:        struct nlist    outbuf[64];
        !           315:        int             i, remaining, cur;
        !           316:        char           *oldstrings;
        !           317:        char           *newstrings, *nsp;
        !           318:        int             newstringsize;
        !           319:
        !           320:        /* Zero the unused fields in the output buffer.. */
        !           321:        memset(outbuf, 0, sizeof outbuf);
        !           322:
        !           323:        /* Find number of symbols to process... */
        !           324:        remaining = symsize / sizeof(struct sym);
        !           325:
        !           326:        /* Suck in the old string table... */
        !           327:        oldstrings = saveRead(in, stroff, strsize, "string table");
        !           328:
        !           329:        /*
        !           330:         * Allocate space for the new one.   XXX We make the wild assumption
        !           331:         * that no two symbol table entries will point at the same place in
        !           332:         * the string table - if that assumption is bad, this could easily
        !           333:         * blow up.
        !           334:         */
        !           335:        newstringsize = strsize + remaining;
        !           336:        newstrings = (char *) malloc(newstringsize);
        !           337:        if (!newstrings) {
        !           338:                fprintf(stderr, "No memory for new string table!\n");
        !           339:                exit(1);
        !           340:        }
        !           341:        /* Initialize the table pointer... */
        !           342:        nsp = newstrings;
1.1       graichen  343:
1.3     ! deraadt   344:        /* Go the the start of the ELF symbol table... */
        !           345:        if (lseek(in, symoff, SEEK_SET) < 0) {
        !           346:                perror("translate_syms: lseek");
        !           347:                exit(1);
        !           348:        }
        !           349:        /* Translate and copy symbols... */
        !           350:        while (remaining) {
        !           351:                cur = remaining;
        !           352:                if (cur > SYMS_PER_PASS)
        !           353:                        cur = SYMS_PER_PASS;
        !           354:                remaining -= cur;
        !           355:                if ((i = read(in, inbuf, cur * sizeof(struct sym)))
        !           356:                    != cur * sizeof(struct sym)) {
        !           357:                        if (i < 0)
        !           358:                                perror("translate_syms");
        !           359:                        else
        !           360:                                fprintf(stderr,
        !           361:                                    "translate_syms: premature end of file.\n");
        !           362:                        exit(1);
        !           363:                }
        !           364:                /* Do the translation... */
        !           365:                for (i = 0; i < cur; i++) {
        !           366:                        /*
        !           367:                         * Copy the symbol into the new table, but prepend an
        !           368:                         * underscore.
        !           369:                         */
        !           370:                        *nsp = '_';
        !           371:                        strcpy(nsp + 1, oldstrings + inbuf[i].name);
        !           372:                        outbuf[i].n_un.n_strx = nsp - newstrings + 4;
        !           373:                        nsp += strlen(nsp) + 1;
        !           374:
        !           375:                        /*
        !           376:                         * Convert ELF symbol type/section/etc info into
        !           377:                         * a.out type info.
        !           378:                         */
        !           379:                        if (inbuf[i].type == STT_FILE)
        !           380:                                outbuf[i].n_type = N_FN;
        !           381:                        else if (inbuf[i].shndx == SHN_UNDEF)
        !           382:                                outbuf[i].n_type = N_UNDF;
        !           383:                        else if (inbuf[i].shndx == SHN_ABS)
        !           384:                                outbuf[i].n_type = N_ABS;
        !           385:                        else if (inbuf[i].shndx == SHN_COMMON ||
        !           386:                                 inbuf[i].shndx == SHN_MIPS_ACOMMON)
        !           387:                                outbuf[i].n_type = N_COMM;
        !           388:                        else
        !           389:                                outbuf[i].n_type = symTypeTable[inbuf[i].shndx];
        !           390:                        if (inbuf[i].binding == STB_GLOBAL)
        !           391:                                outbuf[i].n_type |= N_EXT;
        !           392:                        /* Symbol values in executables should be compatible. */
        !           393:                        outbuf[i].n_value = inbuf[i].value;
        !           394:                }
        !           395:                /* Write out the symbols... */
        !           396:                if ((i = write(out, outbuf, cur * sizeof(struct nlist)))
        !           397:                    != cur * sizeof(struct nlist)) {
        !           398:                        fprintf(stderr, "translate_syms: write: %s\n", strerror(errno));
        !           399:                        exit(1);
        !           400:                }
        !           401:        }
        !           402:        /* Write out the string table length... */
        !           403:        if (write(out, &newstringsize, sizeof newstringsize)
        !           404:            != sizeof newstringsize) {
        !           405:                fprintf(stderr,
        !           406:                    "translate_syms: newstringsize: %s\n", strerror(errno));
        !           407:                exit(1);
        !           408:        }
        !           409:        /* Write out the string table... */
        !           410:        if (write(out, newstrings, newstringsize) != newstringsize) {
        !           411:                fprintf(stderr, "translate_syms: newstrings: %s\n", strerror(errno));
        !           412:                exit(1);
        !           413:        }
1.1       graichen  414: }
1.3     ! deraadt   415:
        !           416: copy(int out, int in, off_t offset, off_t size)
1.1       graichen  417: {
1.3     ! deraadt   418:        char ibuf[4096];
        !           419:        int  remaining, cur, count;
1.1       graichen  420:
1.3     ! deraadt   421:        /* Go the the start of the ELF symbol table... */
        !           422:        if (lseek(in, offset, SEEK_SET) < 0) {
        !           423:                perror("copy: lseek");
        !           424:                exit(1);
        !           425:        }
        !           426:        remaining = size;
        !           427:        while (remaining) {
        !           428:                cur = remaining;
        !           429:                if (cur > sizeof ibuf)
        !           430:                        cur = sizeof ibuf;
        !           431:                remaining -= cur;
        !           432:                if ((count = read(in, ibuf, cur)) != cur) {
        !           433:                        fprintf(stderr, "copy: read: %s\n",
        !           434:                         count ? strerror(errno) : "premature end of file");
        !           435:                        exit(1);
        !           436:                }
        !           437:                if ((count = write(out, ibuf, cur)) != cur) {
        !           438:                        perror("copy: write");
        !           439:                        exit(1);
        !           440:                }
1.1       graichen  441:        }
                    442: }
                    443:
1.3     ! deraadt   444: /*
        !           445:  * Combine two segments, which must be contiguous.   If pad is true, it's
        !           446:  * okay for there to be padding between.
        !           447:  */
        !           448: combine(struct sect * base, struct sect * new, int pad)
1.1       graichen  449: {
1.3     ! deraadt   450:        if (!base->len)
        !           451:                *base = *new;
        !           452:        else if (new->len) {
        !           453:                if (base->vaddr + base->len != new->vaddr) {
        !           454:                        if (pad)
        !           455:                                base->len = new->vaddr - base->vaddr;
        !           456:                        else {
        !           457:                                fprintf(stderr,
        !           458:                                "Non-contiguous data can't be converted.\n");
        !           459:                                exit(1);
        !           460:                        }
        !           461:                }
        !           462:                base->len += new->len;
1.1       graichen  463:        }
                    464: }
                    465:
1.3     ! deraadt   466: phcmp(struct phdr * h1, struct phdr * h2)
1.1       graichen  467: {
1.3     ! deraadt   468:        if (h1->vaddr > h2->vaddr)
        !           469:                return 1;
        !           470:        else if (h1->vaddr < h2->vaddr)
        !           471:                return -1;
        !           472:        else
        !           473:                return 0;
1.1       graichen  474: }
                    475:
1.3     ! deraadt   476: char           *
        !           477: saveRead(int file, off_t offset, off_t len, char *name)
1.1       graichen  478: {
1.3     ! deraadt   479:        char           *tmp;
        !           480:        int             count;
        !           481:        off_t           off;
        !           482:
        !           483:        if ((off = lseek(file, offset, SEEK_SET)) < 0) {
        !           484:                fprintf(stderr, "%s: fseek: %s\n", name, strerror(errno));
        !           485:                exit(1);
        !           486:        }
        !           487:        if (!(tmp = (char *) malloc(len))) {
        !           488:                fprintf(stderr, "%s: Can't allocate %d bytes.\n", name, len);
        !           489:                exit(1);
        !           490:        }
        !           491:        count = read(file, tmp, len);
        !           492:        if (count != len) {
        !           493:                fprintf(stderr, "%s: read: %s.\n",
        !           494:                     name, count ? strerror(errno) : "End of file reached");
        !           495:                exit(1);
        !           496:        }
        !           497:        return tmp;
1.1       graichen  498: }