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