237b1f946390335e3b6a2170eacab9720c5f730c
[fw/sdcc] / as / asranlib / asranlib.c
1 /* asranlib.c - ranlib for asxxxx arvhives
2    version 1.0.0, April 27th, 2008
3
4    Copyright (C) 2008-2009 Borut Razem, borut dot razem at siol dot net
5
6 This program is free software; you can redistribute it and/or modify it
7 under the terms of the GNU General Public License as published by the
8 Free Software Foundation; either version 3, or (at your option) any
9 later version.
10
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14 GNU General Public License for more details.
15
16 You should have received a copy of the GNU General Public License
17 along with this program.  If not, see <http://www.gnu.org/licenses/>. */
18
19 #include <stdio.h>
20 #include <stdlib.h>
21 #include <string.h>
22 #include <assert.h>
23 #include <time.h>
24 #include "dbuf_string.h"
25 #include "lkar.h"
26
27 #ifdef _WIN32
28 #include <io.h>
29 #else
30 #include <unistd.h>
31 #endif
32
33 #define NELEM(x)  (sizeof (x) / sizeof (*x))
34
35
36 int verbose = 0;
37 int list = 0;
38 int print_index = 0;
39
40 int
41 is_ar (FILE * libfp)
42 {
43   char buf[SARMAG];
44   int ret;
45
46   if (!(ret = fread (buf, 1, sizeof (buf), libfp) == sizeof (buf) && memcmp (buf, ARMAG, SARMAG) == 0))
47     rewind (libfp);
48
49   return ret;
50 }
51
52 static char *sym_tab;
53 static int sym_tab_size;
54
55 char *
56 get_member_name (char *name, size_t *p_size, int allocate, FILE * libfp)
57 {
58   *p_size = 0;
59
60   if (0 == memcmp (name, "#1/", 3))
61     {
62       char *p;
63       size_t len = strtoul (&name [3], &p, 10);
64       if (p > &name [3])
65         {
66           /* BSD appends real file name to the file header */
67           if (p_size != NULL)
68             *p_size = len;
69
70           if (allocate)
71             {
72               char *n = (char *) malloc (len);
73               if (fread (n, 1, len, libfp) != len)
74                 {
75                   /* not an ar archive or broken ar archive */
76                   return NULL;
77                 }
78               else
79                 return n;
80             }
81           else
82             {
83               /* just advance the file pointer */
84               fseek (libfp, len, SEEK_CUR);
85               return NULL;
86             }
87         }
88       else
89         {
90           /* not an ar archive or broken ar archive */
91           return NULL;
92         }
93     }
94   else if (allocate)
95     {
96       if (name[0] == '/')
97         {
98           if (NULL != sym_tab)
99             {
100               char *p;
101
102               int name_offset = strtol (++name, &p, 0);
103               if (p != name && name_offset < sym_tab_size)
104                 {
105                   int len = p - name + 1;
106                   while (len < AR_NAME_LEN && name[len++] == ' ')
107                     ;
108                   if (len == AR_NAME_LEN)
109                     {
110                       char *n;
111
112                       /* long name: get it from the symbol table */
113                       name = &sym_tab[name_offset];
114                       for (p = name; *p != '/' && *p != '\n'; ++p)
115                         assert (p < &sym_tab[sym_tab_size]);
116
117                       if (p[0] != '/' || p[1] != '\n')
118                         while (*++p != '\n')
119                           assert (p < &sym_tab[sym_tab_size]);
120
121                       n = (char *) malloc (p - name + 1);
122                       memcpy (n, name, p - name);
123                       n[p - name] = '\0';
124                       return n;
125                     }
126                 }
127             }
128         }
129       else
130         {
131           char *p = strrchr (name, '/');
132
133           if (NULL != p)
134             {
135               int len = p - name;
136               while (name[++len] == ' ')
137                 ;
138               if (len == AR_NAME_LEN)
139                 {
140                   char *n = (char *) malloc (p - name + 1);
141                   memcpy (n, name, p - name);
142                   n[p - name] = '\0';
143                   return n;
144                 }
145             }
146           else
147             {
148               /* BSD formed member name:
149                  trim trailing spaces */
150               char *n;
151
152               p = name + AR_NAME_LEN;
153               while (*--p == ' ' && p >= name)
154                 ;
155               ++p;
156               n = (char *) malloc (p - name + 1);
157               memcpy (n, name, p - name);
158               n[p - name] = '\0';
159               return n;
160             }
161         }
162
163       /* bad formed member name:
164        just return it */
165
166       return strdup (name);
167     }
168   else
169     return NULL;
170 }
171
172 size_t
173 ar_get_header (struct ar_hdr *hdr, FILE * libfp, char **p_obj_name)
174 {
175   char header[ARHDR_LEN];
176   char buf[AR_DATE_LEN + 1];
177   char *obj_name;
178   size_t size;
179
180   if (fread (header, 1, sizeof (header), libfp) != sizeof (header)
181       || memcmp (header + AR_FMAG_OFFSET, ARFMAG, AR_FMAG_LEN) != 0)
182     {
183       /* not an ar archive */
184       return 0;
185     }
186
187   memcpy (hdr->ar_name, &header[AR_NAME_OFFSET], AR_NAME_LEN);
188   hdr->ar_name[AR_NAME_LEN] = '\0';
189
190   memcpy (buf, &header[AR_DATE_OFFSET], AR_DATE_LEN);
191   buf[AR_DATE_LEN] = '\0';
192   hdr->ar_date = strtol (buf, NULL, 0);
193
194   memcpy (buf, &header[AR_UID_OFFSET], AR_GID_LEN);
195   buf[AR_GID_LEN] = '\0';
196   hdr->ar_uid = (uid_t) strtol (buf, NULL, 0);
197
198   memcpy (buf, &header[AR_GID_OFFSET], AR_DATE_LEN);
199   buf[AR_DATE_LEN] = '\0';
200   hdr->ar_gid = (gid_t) strtol (buf, NULL, 0);
201
202   memcpy (buf, &header[AR_MODE_OFFSET], AR_MODE_LEN);
203   buf[AR_MODE_LEN] = '\0';
204   hdr->ar_mode = (mode_t) strtoul (buf, NULL, 0);
205
206   memcpy (buf, &header[AR_SIZE_OFFSET], AR_SIZE_LEN);
207   buf[AR_SIZE_LEN] = '\0';
208   hdr->ar_size = strtol (buf, NULL, 0);
209
210   obj_name = get_member_name (hdr->ar_name, &size, p_obj_name != NULL, libfp);
211
212   if (p_obj_name != NULL)
213     *p_obj_name = obj_name;
214
215   /* treat BSD appended real file name as a part of the header */
216   hdr->ar_size -= size;
217
218   return size + ARHDR_LEN;
219 }
220
221 static char *
222 get_member_name_by_offset (FILE * fp, long offset)
223 {
224   struct ar_hdr hdr;
225   char *name;
226
227   fseek (fp, offset, SEEK_SET);
228   return (ar_get_header (&hdr, fp, &name) != 0) ? name : NULL;
229 }
230
231 struct symbol_s
232   {
233     const char *name;
234     size_t offset;
235     struct symbol_s *next;
236   };
237
238 struct symbol_s *symlist, *lastsym;
239 unsigned int offset, first_member_offset;
240
241 int
242 add_symbol (const char *sym, void *param)
243 {
244   struct symbol_s *s;
245
246   if ((s = (struct symbol_s *) malloc (sizeof (struct symbol_s))) == NULL)
247     return 0;
248
249   if (verbose)
250     printf ("%s\n", sym);
251
252   s->name = strdup (sym);
253   s->offset = offset - first_member_offset;
254   s->next = NULL;
255
256   if (NULL == symlist)
257     {
258       lastsym = symlist = s;
259     }
260   else
261     {
262       lastsym->next = s;
263       lastsym = s;
264     }
265
266   return 0;
267 }
268
269
270 int
271 is_rel (FILE * libfp)
272 {
273   int c;
274   long pos = ftell (libfp);
275   int ret = 0;
276
277   /* [XDQ][HL] */
278   if (((c = getc (libfp)) == 'X' || c == 'D' || c == 'Q') && ((c = getc (libfp)) == 'H' || c == 'L'))
279     {
280       switch (getc (libfp))
281         {
282         case '\r':
283           if (getc (libfp) == '\n')
284             ret = 1;
285           break;
286
287         case '\n':
288           ret = 1;
289         }
290     }
291   else if (c == ';')
292     {
293       char buf[6];
294
295       if (fread (buf, 1, sizeof (buf), libfp) == sizeof (buf) && memcmp (buf, "!FILE ", 6) == 0)
296         ret = 1;
297     }
298   fseek (libfp, pos, SEEK_SET);
299   return ret;
300 }
301
302 int
303 enum_symbols (FILE * fp, long size, int (*func) (const char *sym, void *param), void *param)
304 {
305   long end;
306   struct dbuf_s buf;
307   struct dbuf_s symname;
308
309   assert (func != NULL);
310
311   dbuf_init (&buf, 512);
312   dbuf_init (&symname, 32);
313
314   end = (size >= 0) ? ftell (fp) + size : -1;
315
316   /*
317    * Read in the object file.  Look for lines that
318    * begin with "S" and end with "D".  These are
319    * symbol table definitions.  If we find one, see
320    * if it is our symbol.  Make sure we only read in
321    * our object file and don't go into the next one.
322    */
323
324   while (end < 0 || ftell (fp) < end)
325     {
326       const char *p;
327
328       dbuf_set_length (&buf, 0);
329       if (dbuf_getline (&buf, fp) == 0)
330         break;
331
332       p = dbuf_c_str (&buf);
333
334       if ('T' == p[0])
335         break;
336
337       /*
338        * Skip everything that's not a symbol record.
339        */
340       if ('S' == p[0] && ' ' == p[1])
341         {
342           dbuf_set_length (&symname, 0);
343
344           for (p += 2; *p && ' ' != *p; ++p)
345             dbuf_append_char (&symname, *p);
346
347           /* If it's an actual symbol, record it */
348           if (' ' == p[0] && 'D' == p[1])
349             if (func != NULL)
350               if ((*func) (dbuf_c_str (&symname), NULL))
351                 return 1;
352         }
353     }
354
355   dbuf_destroy (&buf);
356   dbuf_destroy (&symname);
357
358   return 0;
359 }
360
361
362 static int
363 process_symbol_table (struct ar_hdr *hdr, FILE *fp)
364 {
365   long pos = ftell (fp);
366
367   if (print_index)
368     {
369       char *buf, *po, *ps;
370       int i;
371       long nsym;
372
373       printf ("Archive index:\n");
374
375       buf = (char *) malloc (hdr->ar_size);
376
377       if (fread (buf, 1, hdr->ar_size, fp) != hdr->ar_size)
378         {
379           free (buf);
380           return 0;
381         }
382
383       nsym = sgetl (buf);
384
385       po = buf + 4;
386       ps = po + nsym * 4;
387
388       for (i = 0; i < nsym; ++i)
389         {
390           char *obj;
391
392           offset = sgetl (po);
393           po += 4;
394
395           obj = get_member_name_by_offset (fp, offset);  /* member name */
396           printf ("%s in %s", ps, obj);
397           if (verbose)
398             printf (" at 0x%04x\n", offset);
399           else
400             putchar ('\n');
401           free (obj);
402
403           ps += strlen(ps) + 1;
404         
405         }
406       free (buf);
407
408       fseek (fp, pos, SEEK_SET);
409
410       putchar ('\n');
411     }
412
413   /* skip the symbol table */
414   fseek (fp, pos + hdr->ar_size + (hdr->ar_size & 1), SEEK_SET);
415
416   return 1;
417 }
418
419 static int
420 process_bsd_symbol_table (struct ar_hdr *hdr, FILE *fp)
421 {
422   long pos = ftell (fp);
423
424   if (print_index)
425     {
426       char *buf, *po, *ps;
427       int i;
428       long tablesize;
429       long nsym;
430
431       printf ("Archive index:\n");
432
433       buf = (char *) malloc (hdr->ar_size);
434
435       if (fread (buf, 1, hdr->ar_size, fp) != hdr->ar_size)
436         {
437           free (buf);
438           return 0;
439         }
440
441       tablesize = sgetl (buf);
442       nsym = tablesize / 8;
443
444       po = buf + 4;
445
446       ps = po + tablesize + 4;
447
448       for (i = 0; i < nsym; ++i)
449         {
450           char *obj;
451           long sym;
452
453           sym = sgetl (po);
454           po += 4;
455           offset = sgetl (po);
456           po += 4;
457
458           printf ("%s in ", ps + sym);
459
460           obj = get_member_name_by_offset (fp, offset);  /* member name */
461           printf ("%s\n", obj);
462           free (obj);
463         }
464       free (buf);
465
466       fseek (fp, pos, SEEK_SET);
467
468       putchar ('\n');
469     }
470
471   /* skip the symbol table */
472   fseek (fp, pos + hdr->ar_size + (hdr->ar_size & 1), SEEK_SET);
473
474   return 1;
475 }
476
477 int
478 get_symbols (FILE * fp, const char *archive)
479 {
480   struct ar_hdr hdr;
481   size_t hdr_len;
482   char *name;
483
484   if (!is_ar (fp) || !(hdr_len = ar_get_header (&hdr, fp, &name)))
485     {
486       fprintf (stderr, "asranlib: %s: File format not recognized\n", archive);
487       exit (1);
488     }
489
490   if (AR_IS_SYMBOL_TABLE (name))
491     {
492       free (name);
493
494       process_symbol_table (&hdr, fp);
495       if (!(hdr_len = ar_get_header (&hdr, fp, (verbose || list) ? &name : NULL)))
496         return 1;
497     }
498   else if (AR_IS_BSD_SYMBOL_TABLE (name))
499     {
500       free (name);
501
502       process_bsd_symbol_table (&hdr, fp);
503       if (!(hdr_len = ar_get_header (&hdr, fp, (verbose || list) ? &name : NULL)))
504         return 1;
505     }
506   else if (!verbose && !list)
507     free (name);
508
509   first_member_offset = ftell (fp) - hdr_len;
510
511   /* walk trough all archive members */
512   do
513     {
514       if (is_rel (fp))
515         {
516           if (verbose || list)
517             {
518               printf ("%s%s\n", name, verbose ? ":" : "");
519               free (name);
520             }
521
522           if (!list)
523             {
524               long mdule_offset = ftell (fp);
525
526               offset = mdule_offset - hdr_len;
527
528               enum_symbols (fp, hdr.ar_size, add_symbol, NULL);
529
530               fseek (fp, mdule_offset + hdr.ar_size + (hdr.ar_size & 1), SEEK_SET);
531             }
532
533           if (verbose)
534             putchar ('\n');
535         }
536       else
537         {
538           if (verbose || list)
539             {
540               fprintf (stderr, "asranlib: %s: File format not recognized\n", name);
541               free (name);
542             }
543
544           /* skip if the member is not a .REL format */
545           fseek (fp, hdr.ar_size + (hdr.ar_size & 1), SEEK_CUR);
546         }
547     }
548   while ((hdr_len = ar_get_header (&hdr, fp, (verbose || list) ? &name : NULL)));
549
550   return 1;
551 }
552
553 void
554 do_ranlib (const char *archive)
555 {
556   FILE *infp;
557
558   if (NULL == (infp = fopen (archive, "rb")))
559     {
560       fprintf (stderr, "asranlib: %s: ", archive);
561       perror (NULL);
562       exit (1);
563     }
564
565   if (get_symbols (infp, archive))
566     {
567       FILE *outfp;
568       struct symbol_s *symp;
569       char buf[4];
570       int str_length;
571       int pad;
572       int nsym;
573       int symtab_size;
574       char tmpfile[] = "arXXXXXX";
575
576 #ifdef _WIN32
577       if (NULL == _mktemp (tmpfile) || NULL == (outfp = fopen (tmpfile, "wb")))
578         {
579           fclose (infp);
580           fprintf (stderr, "asranlib: %s: ", tmpfile);
581           perror (NULL);
582           exit (1);
583         }
584 #else
585       if ((pad = mkstemp (tmpfile)) < 0)
586         {
587           fclose (infp);
588           fprintf (stderr, "asranlib: %s: ", tmpfile);
589           perror (NULL);
590           exit (1);
591         }
592
593       if (NULL == (outfp = fdopen (pad, "wb")))
594         {
595           close (pad);
596           fclose (infp);
597           perror ("asranlib");
598           exit (1);
599         }
600 #endif
601
602       /* calculate the size of symbol table */
603       for (str_length = 0, nsym = 0, symp = symlist; symp; ++nsym, symp = symp->next)
604         {
605           str_length += strlen (symp->name) + 1;
606         }
607
608       symtab_size = 4 + 4 * nsym + str_length;
609
610       fprintf (outfp, ARMAG AR_SYMBOL_TABLE_NAME "%-12d%-6d%-6d%-8d%-10d" ARFMAG, (int) time (NULL), 0, 0, 0, symtab_size);
611
612       if (symtab_size & 1)
613         {
614           pad = 1;
615           ++symtab_size;
616         }
617       else
618         pad = 0;
619
620       symtab_size += SARMAG + ARHDR_LEN;
621
622       sputl (nsym, buf);
623       fwrite (buf, 1, sizeof (buf), outfp);
624
625       for (symp = symlist; symp; symp = symp->next)
626         {
627           sputl (symp->offset + symtab_size, buf);
628           fwrite (buf, 1, sizeof (buf), outfp);
629         }
630
631
632       for (symp = symlist; symp; symp = symp->next)
633         {
634           fputs (symp->name, outfp);
635           putc ('\0', outfp);
636         }
637
638       if (pad)
639         putc ('\n', outfp);
640
641       fseek (infp, first_member_offset, SEEK_SET);
642
643       while (EOF != (pad = getc (infp)))
644         putc (pad, outfp);
645
646       fclose (outfp);
647       fclose (infp);
648
649       if (0 != remove (archive))
650         {
651           fprintf (stderr, "asranlib: can't remove %s to %s: ", tmpfile, archive);
652           perror (NULL);
653         }
654       else if (0 != rename (tmpfile, archive))
655         {
656           fprintf (stderr, "asranlib: can't rename %s to %s: ", tmpfile, archive);
657           perror (NULL);
658         }
659     }
660   else
661     fclose (infp);
662 }
663
664 void
665 do_verbose (void)
666 {
667   verbose = 1;
668 }
669
670 void
671 print_version (void)
672 {
673   printf ("SDCC asxxxx ranlib 1.0.0 $Revision$\n");
674   exit (0);
675 }
676
677 void
678 do_list (void)
679 {
680   list = 1;
681 }
682
683 void
684 print_armap (void)
685 {
686   print_index = 1;
687 }
688
689 void usage (void);
690
691 struct opt_s
692   {
693     const char *short_opt;
694     const char *long_opt;
695     void (*optfnc) (void);
696     const char *comment;
697   }
698 opts[] =
699   {
700     { "-v", "--verbose", &do_verbose, "Be more verbose about the operation" },
701     { "-V", "--version", &print_version, "Print this help message" },
702     { "-h", "--help", &usage, "Print version information" },
703     { "-t", "--list", &do_list, "List the contents of an archive" },
704     { "-s", "--print-armap", &print_armap, "Print the archive index" },
705   };
706
707 void
708 usage (void)
709 {
710   int i;
711
712   printf ("Usage: asranlib [options] archive\n"
713     " Generate an index to speed access to archives\n"
714     " The options are:\n");
715
716   for (i = 0; i < NELEM (opts); ++i)
717     {
718       int len = 3;
719       printf ("  %s ", (NULL != opts[i].short_opt) ? opts[i].short_opt : "  ");
720       if (NULL != opts[i].long_opt)
721         {
722           printf ("%s ", opts[i].long_opt);
723           len += strlen (opts[i].long_opt);
724         }
725
726       while (len++ < 32)
727         putchar (' ');
728       printf ("%s\n", opts[i].comment);
729     }
730
731   printf ("asranlib: supported targets: asxxxx\n");
732
733   exit (1);
734 }
735
736 void
737 process_options (int argc, char *argv[])
738 {
739   char **argp;
740   int noopts = 0;
741   int narch = 0;
742   for (argp = argv + 1; *argp; ++argp)
743     {
744       if (!noopts && (*argp)[0] == '-')
745         {
746           int i;
747
748           if ((*argp)[1] == '-' && (*argp)[2] == '\0')
749             {
750               noopts = 1;
751               continue;
752             }
753
754           for (i = 0; i < NELEM (opts); ++i)
755             {
756               if (0 == strcmp (*argp, opts[i].short_opt) || 0 == strcmp (*argp, opts[i].long_opt))
757                 {
758                   if (NULL != opts[i].optfnc)
759                     {
760                       (*opts[i].optfnc) ();
761                       break;
762                     }
763                 }
764             }
765         }
766
767       else
768         {
769           do_ranlib (*argp);
770           ++narch;
771         }
772     }
773
774   if (!narch)
775     usage ();
776 }
777
778 int
779 main (int argc, char *argv[])
780 {
781   process_options (argc, argv);
782
783   return 0;
784 }