more changes on original files
[linux-2.4.git] / arch / mips / boot / elf2ecoff.c
1 /*
2  * Copyright (c) 1995
3  *      Ted Lemon (hereinafter referred to as the author)
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  * 3. The name of the author may not be used to endorse or promote products
14  *    derived from this software without specific prior written permission.
15  *
16  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND
17  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE
20  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26  * SUCH DAMAGE.
27  */
28
29 /* elf2ecoff.c
30
31    This program converts an elf executable to an ECOFF executable.
32    No symbol table is retained.   This is useful primarily in building
33    net-bootable kernels for machines (e.g., DECstation and Alpha) which
34    only support the ECOFF object file format. */
35
36 #include <stdio.h>
37 #include <string.h>
38 #include <errno.h>
39 #include <sys/types.h>
40 #include <fcntl.h>
41 #include <unistd.h>
42 #include <elf.h>
43 #include <limits.h>
44 #include <netinet/in.h>
45 #include <stdlib.h>
46
47 #include "ecoff.h"
48
49 /*
50  * Some extra ELF definitions
51  */
52 #define PT_MIPS_REGINFO 0x70000000      /* Register usage information */
53
54 /* -------------------------------------------------------------------- */
55
56 struct sect {
57         unsigned long vaddr;
58         unsigned long len;
59 };
60
61 int *symTypeTable;
62 int must_convert_endian = 0;
63 int format_bigendian = 0;
64
65 static void copy(int out, int in, off_t offset, off_t size)
66 {
67         char ibuf[4096];
68         int remaining, cur, count;
69
70         /* Go to the start of the ELF symbol table... */
71         if (lseek(in, offset, SEEK_SET) < 0) {
72                 perror("copy: lseek");
73                 exit(1);
74         }
75
76         remaining = size;
77         while (remaining) {
78                 cur = remaining;
79                 if (cur > sizeof(ibuf))
80                         cur = sizeof(ibuf);
81                 remaining -= cur;
82                 if ((count = read(in, ibuf, cur)) != cur) {
83                         fprintf(stderr, "copy: read: %s\n",
84                                 count ? strerror(errno) :
85                                 "premature end of file");
86                         exit(1);
87                 }
88                 if ((count = write(out, ibuf, cur)) != cur) {
89                         perror("copy: write");
90                         exit(1);
91                 }
92         }
93 }
94
95 /*
96  * Combine two segments, which must be contiguous.   If pad is true, it's
97  * okay for there to be padding between.
98  */
99 static void combine(struct sect *base, struct sect *new, int pad)
100 {
101         if (!base->len)
102                 *base = *new;
103         else if (new->len) {
104                 if (base->vaddr + base->len != new->vaddr) {
105                         if (pad)
106                                 base->len = new->vaddr - base->vaddr;
107                         else {
108                                 fprintf(stderr,
109                                         "Non-contiguous data can't be converted.\n");
110                                 exit(1);
111                         }
112                 }
113                 base->len += new->len;
114         }
115 }
116
117 static int phcmp(const void *v1, const void *v2)
118 {
119         const Elf32_Phdr *h1 = v1;
120         const Elf32_Phdr *h2 = v2;
121
122         if (h1->p_vaddr > h2->p_vaddr)
123                 return 1;
124         else if (h1->p_vaddr < h2->p_vaddr)
125                 return -1;
126         else
127                 return 0;
128 }
129
130 static char *saveRead(int file, off_t offset, off_t len, char *name)
131 {
132         char *tmp;
133         int count;
134         off_t off;
135         if ((off = lseek(file, offset, SEEK_SET)) < 0) {
136                 fprintf(stderr, "%s: fseek: %s\n", name, strerror(errno));
137                 exit(1);
138         }
139         if (!(tmp = (char *) malloc(len))) {
140                 fprintf(stderr, "%s: Can't allocate %ld bytes.\n", name,
141                         len);
142                 exit(1);
143         }
144         count = read(file, tmp, len);
145         if (count != len) {
146                 fprintf(stderr, "%s: read: %s.\n",
147                         name,
148                         count ? strerror(errno) : "End of file reached");
149                 exit(1);
150         }
151         return tmp;
152 }
153
154 #define swab16(x) \
155         ((unsigned short)( \
156                 (((unsigned short)(x) & (unsigned short)0x00ffU) << 8) | \
157                 (((unsigned short)(x) & (unsigned short)0xff00U) >> 8) ))
158
159 #define swab32(x) \
160         ((unsigned int)( \
161                 (((unsigned int)(x) & (unsigned int)0x000000ffUL) << 24) | \
162                 (((unsigned int)(x) & (unsigned int)0x0000ff00UL) <<  8) | \
163                 (((unsigned int)(x) & (unsigned int)0x00ff0000UL) >>  8) | \
164                 (((unsigned int)(x) & (unsigned int)0xff000000UL) >> 24) ))
165
166 static void convert_elf_hdr(Elf32_Ehdr * e)
167 {
168         e->e_type = swab16(e->e_type);
169         e->e_machine = swab16(e->e_machine);
170         e->e_version = swab32(e->e_version);
171         e->e_entry = swab32(e->e_entry);
172         e->e_phoff = swab32(e->e_phoff);
173         e->e_shoff = swab32(e->e_shoff);
174         e->e_flags = swab32(e->e_flags);
175         e->e_ehsize = swab16(e->e_ehsize);
176         e->e_phentsize = swab16(e->e_phentsize);
177         e->e_phnum = swab16(e->e_phnum);
178         e->e_shentsize = swab16(e->e_shentsize);
179         e->e_shnum = swab16(e->e_shnum);
180         e->e_shstrndx = swab16(e->e_shstrndx);
181 }
182
183 static void convert_elf_phdrs(Elf32_Phdr * p, int num)
184 {
185         int i;
186
187         for (i = 0; i < num; i++, p++) {
188                 p->p_type = swab32(p->p_type);
189                 p->p_offset = swab32(p->p_offset);
190                 p->p_vaddr = swab32(p->p_vaddr);
191                 p->p_paddr = swab32(p->p_paddr);
192                 p->p_filesz = swab32(p->p_filesz);
193                 p->p_memsz = swab32(p->p_memsz);
194                 p->p_flags = swab32(p->p_flags);
195                 p->p_align = swab32(p->p_align);
196         }
197
198 }
199
200 static void convert_elf_shdrs(Elf32_Shdr * s, int num)
201 {
202         int i;
203
204         for (i = 0; i < num; i++, s++) {
205                 s->sh_name = swab32(s->sh_name);
206                 s->sh_type = swab32(s->sh_type);
207                 s->sh_flags = swab32(s->sh_flags);
208                 s->sh_addr = swab32(s->sh_addr);
209                 s->sh_offset = swab32(s->sh_offset);
210                 s->sh_size = swab32(s->sh_size);
211                 s->sh_link = swab32(s->sh_link);
212                 s->sh_info = swab32(s->sh_info);
213                 s->sh_addralign = swab32(s->sh_addralign);
214                 s->sh_entsize = swab32(s->sh_entsize);
215         }
216 }
217
218 static void convert_ecoff_filehdr(struct filehdr *f)
219 {
220         f->f_magic = swab16(f->f_magic);
221         f->f_nscns = swab16(f->f_nscns);
222         f->f_timdat = swab32(f->f_timdat);
223         f->f_symptr = swab32(f->f_symptr);
224         f->f_nsyms = swab32(f->f_nsyms);
225         f->f_opthdr = swab16(f->f_opthdr);
226         f->f_flags = swab16(f->f_flags);
227 }
228
229 static void convert_ecoff_aouthdr(struct aouthdr *a)
230 {
231         a->magic = swab16(a->magic);
232         a->vstamp = swab16(a->vstamp);
233         a->tsize = swab32(a->tsize);
234         a->dsize = swab32(a->dsize);
235         a->bsize = swab32(a->bsize);
236         a->entry = swab32(a->entry);
237         a->text_start = swab32(a->text_start);
238         a->data_start = swab32(a->data_start);
239         a->bss_start = swab32(a->bss_start);
240         a->gprmask = swab32(a->gprmask);
241         a->cprmask[0] = swab32(a->cprmask[0]);
242         a->cprmask[1] = swab32(a->cprmask[1]);
243         a->cprmask[2] = swab32(a->cprmask[2]);
244         a->cprmask[3] = swab32(a->cprmask[3]);
245         a->gp_value = swab32(a->gp_value);
246 }
247
248 static void convert_ecoff_esecs(struct scnhdr *s, int num)
249 {
250         int i;
251
252         for (i = 0; i < num; i++, s++) {
253                 s->s_paddr = swab32(s->s_paddr);
254                 s->s_vaddr = swab32(s->s_vaddr);
255                 s->s_size = swab32(s->s_size);
256                 s->s_scnptr = swab32(s->s_scnptr);
257                 s->s_relptr = swab32(s->s_relptr);
258                 s->s_lnnoptr = swab32(s->s_lnnoptr);
259                 s->s_nreloc = swab16(s->s_nreloc);
260                 s->s_nlnno = swab16(s->s_nlnno);
261                 s->s_flags = swab32(s->s_flags);
262         }
263 }
264
265 int main(int argc, char *argv[])
266 {
267         Elf32_Ehdr ex;
268         Elf32_Phdr *ph;
269         Elf32_Shdr *sh;
270         char *shstrtab;
271         int i, pad;
272         struct sect text, data, bss;
273         struct filehdr efh;
274         struct aouthdr eah;
275         struct scnhdr esecs[6];
276         int infile, outfile;
277         unsigned long cur_vma = ULONG_MAX;
278         int addflag = 0;
279         int nosecs;
280
281         text.len = data.len = bss.len = 0;
282         text.vaddr = data.vaddr = bss.vaddr = 0;
283
284         /* Check args... */
285         if (argc < 3 || argc > 4) {
286               usage:
287                 fprintf(stderr,
288                         "usage: elf2ecoff <elf executable> <ecoff executable> [-a]\n");
289                 exit(1);
290         }
291         if (argc == 4) {
292                 if (strcmp(argv[3], "-a"))
293                         goto usage;
294                 addflag = 1;
295         }
296
297         /* Try the input file... */
298         if ((infile = open(argv[1], O_RDONLY)) < 0) {
299                 fprintf(stderr, "Can't open %s for read: %s\n",
300                         argv[1], strerror(errno));
301                 exit(1);
302         }
303
304         /* Read the header, which is at the beginning of the file... */
305         i = read(infile, &ex, sizeof(ex));
306         if (i != sizeof(ex)) {
307                 fprintf(stderr, "ex: %s: %s.\n", argv[1],
308                         i ? strerror(errno) : "End of file reached");
309                 exit(1);
310         }
311
312         if (ex.e_ident[EI_DATA] == ELFDATA2MSB)
313                 format_bigendian = 1;
314
315         if (ntohs(0xaa55) == 0xaa55) {
316                 if (!format_bigendian)
317                         must_convert_endian = 1;
318         } else {
319                 if (format_bigendian)
320                         must_convert_endian = 1;
321         }
322         if (must_convert_endian)
323                 convert_elf_hdr(&ex);
324
325         /* Read the program headers... */
326         ph = (Elf32_Phdr *) saveRead(infile, ex.e_phoff,
327                                      ex.e_phnum * sizeof(Elf32_Phdr), "ph");
328         if (must_convert_endian)
329                 convert_elf_phdrs(ph, ex.e_phnum);
330         /* Read the section headers... */
331         sh = (Elf32_Shdr *) saveRead(infile, ex.e_shoff,
332                                      ex.e_shnum * sizeof(Elf32_Shdr),
333                                      "sh");
334         if (must_convert_endian)
335                 convert_elf_shdrs(sh, ex.e_shnum);
336         /* Read in the section string table. */
337         shstrtab = saveRead(infile, sh[ex.e_shstrndx].sh_offset,
338                             sh[ex.e_shstrndx].sh_size, "shstrtab");
339
340         /* Figure out if we can cram the program header into an ECOFF
341            header...  Basically, we can't handle anything but loadable
342            segments, but we can ignore some kinds of segments.  We can't
343            handle holes in the address space.  Segments may be out of order,
344            so we sort them first. */
345
346         qsort(ph, ex.e_phnum, sizeof(Elf32_Phdr), phcmp);
347
348         for (i = 0; i < ex.e_phnum; i++) {
349                 /* Section types we can ignore... */
350                 if (ph[i].p_type == PT_NULL || ph[i].p_type == PT_NOTE ||
351                     ph[i].p_type == PT_PHDR
352                     || ph[i].p_type == PT_MIPS_REGINFO)
353                         continue;
354                 /* Section types we can't handle... */
355                 else if (ph[i].p_type != PT_LOAD) {
356                         fprintf(stderr,
357                                 "Program header %d type %d can't be converted.\n",
358                                 ex.e_phnum, ph[i].p_type);
359                         exit(1);
360                 }
361                 /* Writable (data) segment? */
362                 if (ph[i].p_flags & PF_W) {
363                         struct sect ndata, nbss;
364
365                         ndata.vaddr = ph[i].p_vaddr;
366                         ndata.len = ph[i].p_filesz;
367                         nbss.vaddr = ph[i].p_vaddr + ph[i].p_filesz;
368                         nbss.len = ph[i].p_memsz - ph[i].p_filesz;
369
370                         combine(&data, &ndata, 0);
371                         combine(&bss, &nbss, 1);
372                 } else {
373                         struct sect ntxt;
374
375                         ntxt.vaddr = ph[i].p_vaddr;
376                         ntxt.len = ph[i].p_filesz;
377
378                         combine(&text, &ntxt, 0);
379                 }
380                 /* Remember the lowest segment start address. */
381                 if (ph[i].p_vaddr < cur_vma)
382                         cur_vma = ph[i].p_vaddr;
383         }
384
385         /* Sections must be in order to be converted... */
386         if (text.vaddr > data.vaddr || data.vaddr > bss.vaddr ||
387             text.vaddr + text.len > data.vaddr
388             || data.vaddr + data.len > bss.vaddr) {
389                 fprintf(stderr,
390                         "Sections ordering prevents a.out conversion.\n");
391                 exit(1);
392         }
393
394         /*
395          * If there's a data section but no text section, then the loader
396          * combined everything into one section.   That needs to be the text
397          * section, so just make the data section zero length following text.
398          */
399         if (data.len && !text.len) {
400                 text = data;
401                 data.vaddr = text.vaddr + text.len;
402                 data.len = 0;
403         }
404
405         /*
406          * If there is a gap between text and data, we'll fill it when we copy
407          * the data, so update the length of the text segment as represented in
408          * a.out to reflect that, since a.out doesn't allow gaps in the program
409          * address space.
410          */
411         if (text.vaddr + text.len < data.vaddr)
412                 text.len = data.vaddr - text.vaddr;
413
414         /* We now have enough information to cons up an a.out header... */
415         eah.magic = OMAGIC;
416         eah.vstamp = 200;
417         eah.tsize = text.len;
418         eah.dsize = data.len;
419         eah.bsize = bss.len;
420         eah.entry = ex.e_entry;
421         eah.text_start = text.vaddr;
422         eah.data_start = data.vaddr;
423         eah.bss_start = bss.vaddr;
424         eah.gprmask = 0xf3fffffe;
425         memset(&eah.cprmask, '\0', sizeof(eah.cprmask));
426         eah.gp_value = 0;       /* unused. */
427
428         if (format_bigendian)
429                 efh.f_magic = MIPSEBMAGIC;
430         else
431                 efh.f_magic = MIPSELMAGIC;
432         if (addflag)
433                 nosecs = 6;
434         else
435                 nosecs = 3;
436         efh.f_nscns = nosecs;
437         efh.f_timdat = 0;       /* bogus */
438         efh.f_symptr = 0;
439         efh.f_nsyms = 0;
440         efh.f_opthdr = sizeof(eah);
441         efh.f_flags = 0x100f;   /* Stripped, not sharable. */
442
443         memset(esecs, 0, sizeof(esecs));
444         strcpy(esecs[0].s_name, ".text");
445         strcpy(esecs[1].s_name, ".data");
446         strcpy(esecs[2].s_name, ".bss");
447         if (addflag) {
448                 strcpy(esecs[3].s_name, ".rdata");
449                 strcpy(esecs[4].s_name, ".sdata");
450                 strcpy(esecs[5].s_name, ".sbss");
451         }
452         esecs[0].s_paddr = esecs[0].s_vaddr = eah.text_start;
453         esecs[1].s_paddr = esecs[1].s_vaddr = eah.data_start;
454         esecs[2].s_paddr = esecs[2].s_vaddr = eah.bss_start;
455         if (addflag) {
456                 esecs[3].s_paddr = esecs[3].s_vaddr = 0;
457                 esecs[4].s_paddr = esecs[4].s_vaddr = 0;
458                 esecs[5].s_paddr = esecs[5].s_vaddr = 0;
459         }
460         esecs[0].s_size = eah.tsize;
461         esecs[1].s_size = eah.dsize;
462         esecs[2].s_size = eah.bsize;
463         if (addflag) {
464                 esecs[3].s_size = 0;
465                 esecs[4].s_size = 0;
466                 esecs[5].s_size = 0;
467         }
468         esecs[0].s_scnptr = N_TXTOFF(efh, eah);
469         esecs[1].s_scnptr = N_DATOFF(efh, eah);
470 #define ECOFF_SEGMENT_ALIGNMENT(a) 0x10
471 #define ECOFF_ROUND(s,a) (((s)+(a)-1)&~((a)-1))
472         esecs[2].s_scnptr = esecs[1].s_scnptr +
473             ECOFF_ROUND(esecs[1].s_size, ECOFF_SEGMENT_ALIGNMENT(&eah));
474         if (addflag) {
475                 esecs[3].s_scnptr = 0;
476                 esecs[4].s_scnptr = 0;
477                 esecs[5].s_scnptr = 0;
478         }
479         esecs[0].s_relptr = esecs[1].s_relptr = esecs[2].s_relptr = 0;
480         esecs[0].s_lnnoptr = esecs[1].s_lnnoptr = esecs[2].s_lnnoptr = 0;
481         esecs[0].s_nreloc = esecs[1].s_nreloc = esecs[2].s_nreloc = 0;
482         esecs[0].s_nlnno = esecs[1].s_nlnno = esecs[2].s_nlnno = 0;
483         if (addflag) {
484                 esecs[3].s_relptr = esecs[4].s_relptr
485                     = esecs[5].s_relptr = 0;
486                 esecs[3].s_lnnoptr = esecs[4].s_lnnoptr
487                     = esecs[5].s_lnnoptr = 0;
488                 esecs[3].s_nreloc = esecs[4].s_nreloc = esecs[5].s_nreloc =
489                     0;
490                 esecs[3].s_nlnno = esecs[4].s_nlnno = esecs[5].s_nlnno = 0;
491         }
492         esecs[0].s_flags = 0x20;
493         esecs[1].s_flags = 0x40;
494         esecs[2].s_flags = 0x82;
495         if (addflag) {
496                 esecs[3].s_flags = 0x100;
497                 esecs[4].s_flags = 0x200;
498                 esecs[5].s_flags = 0x400;
499         }
500
501         /* Make the output file... */
502         if ((outfile = open(argv[2], O_WRONLY | O_CREAT, 0777)) < 0) {
503                 fprintf(stderr, "Unable to create %s: %s\n", argv[2],
504                         strerror(errno));
505                 exit(1);
506         }
507
508         if (must_convert_endian)
509                 convert_ecoff_filehdr(&efh);
510         /* Write the headers... */
511         i = write(outfile, &efh, sizeof(efh));
512         if (i != sizeof efh) {
513                 perror("efh: write");
514                 exit(1);
515         }
516
517         for (i = 0; i < nosecs; i++) {
518                 printf("Section %d: %s phys %lx  size %lx  file offset %lx\n",
519                        i, esecs[i].s_name, esecs[i].s_paddr,
520                        esecs[i].s_size, esecs[i].s_scnptr);
521         }
522         fprintf(stderr, "wrote %d byte file header.\n", i);
523
524         if (must_convert_endian)
525                 convert_ecoff_aouthdr(&eah);
526         i = write(outfile, &eah, sizeof(eah));
527         if (i != sizeof(eah)) {
528                 perror("eah: write");
529                 exit(1);
530         }
531         fprintf(stderr, "wrote %d byte a.out header.\n", i);
532
533         if (must_convert_endian)
534                 convert_ecoff_esecs(&esecs[0], nosecs);
535         i = write(outfile, &esecs, nosecs * sizeof(struct scnhdr));
536         if (i != nosecs * sizeof(struct scnhdr)) {
537                 perror("esecs: write");
538                 exit(1);
539         }
540         fprintf(stderr, "wrote %d bytes of section headers.\n", i);
541
542         pad = (sizeof(efh) + sizeof(eah) + nosecs * sizeof(struct scnhdr)) & 15;
543         if (pad) {
544                 pad = 16 - pad;
545                 i = write(outfile, "\0\0\0\0\0\0\0\0\0\0\0\0\0\0", pad);
546                 if (i < 0) {
547                         perror("ipad: write");
548                         exit(1);
549                 }
550                 fprintf(stderr, "wrote %d byte pad.\n", i);
551         }
552
553         /*
554          * Copy the loadable sections.   Zero-fill any gaps less than 64k;
555          * complain about any zero-filling, and die if we're asked to zero-fill
556          * more than 64k.
557          */
558         for (i = 0; i < ex.e_phnum; i++) {
559                 /*
560                  * Unprocessable sections were handled above, so just verify
561                  * that the section can be loaded before copying.
562                  */
563                 if (ph[i].p_type != PT_LOAD || ph[i].p_filesz == 0)
564                         continue;
565
566                 if (cur_vma != ph[i].p_vaddr) {
567                         unsigned long gap = ph[i].p_vaddr - cur_vma;
568                         char obuf[1024];
569
570                         if (gap > 65536) {
571                                 fprintf(stderr, "Intersegment gap (%ld "
572                                         "bytes) too large.\n", gap);
573                                 exit(1);
574                         }
575                         fprintf(stderr, "Warning: %ld byte intersegment gap.\n",
576                                         gap);
577                         memset(obuf, 0, sizeof(obuf));
578                         while (gap) {
579                                 int count = write(outfile, obuf,
580                                    gap > sizeof(obuf) ?  sizeof(obuf) : gap);
581                                 if (count < 0) {
582                                         fprintf(stderr,
583                                                 "Error writing gap: %s\n",
584                                                 strerror(errno));
585                                         exit(1);
586                                 }
587                                 gap -= count;
588                         }
589                 }
590                 fprintf(stderr, "writing %d bytes...\n", ph[i].p_filesz);
591                 copy(outfile, infile, ph[i].p_offset, ph[i].p_filesz);
592                 cur_vma = ph[i].p_vaddr + ph[i].p_filesz;
593         }
594
595         /*
596          * Write a page of padding for boot PROMS that read entire pages.
597          * Without this, they may attempt to read past the end of the
598          * data section, incur an error, and refuse to boot.
599          */
600         {
601                 char obuf[4096];
602                 memset(obuf, 0, sizeof(obuf));
603                 if (write(outfile, obuf, sizeof(obuf)) != sizeof(obuf)) {
604                         fprintf(stderr, "Error writing PROM padding: %s\n",
605                                 strerror(errno));
606                         exit(1);
607                 }
608         }
609
610         /* Looks like we won... */
611         exit(0);
612 }