b137ffde58a45394a69d9411f4418daf3f9abdc4
[fw/sdcc] / as / link / lkar.c
1 /* lkar.c - ar library format handling
2
3    Copyright (C) 1989-1995 Alan R. Baldwin
4    721 Berkeley St., Kent, Ohio 44240
5    Copyright (C) 2008-2009 Borut Razem, borut dot razem at siol dot net
6
7 This program is free software; you can redistribute it and/or modify it
8 under the terms of the GNU General Public License as published by the
9 Free Software Foundation; either version 2, or (at your option) any
10 later version.
11
12 This program is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15 GNU General Public License for more details.
16
17 You should have received a copy of the GNU General Public License
18 along with this program; if not, write to the Free Software
19 Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
20
21 /*
22  * With contributions for the
23  * object libraries from
24  * Ken Hornstein
25  * kenh@cmf.nrl.navy.mil
26  *
27  */
28
29 /*
30  * Extensions: P. Felber
31  */
32
33 #include <stdio.h>
34 #include <stdlib.h>
35 #include <string.h>
36 #include <assert.h>
37
38 #include "aslink.h"
39 #include "lklibr.h"
40 #include "lkrel.h"
41 #include "lkar.h"
42
43
44 #ifndef max
45 # define max(a,b) ((a) > (b) ? (a) : (b))
46 #endif
47
48 #ifndef min
49 # define min(a,b) ((a) < (b) ? (a) : (b))
50 #endif
51
52
53 static int
54 is_ar (FILE * libfp)
55 {
56   char buf[SARMAG];
57   int ret;
58
59   if (!(ret = fread (buf, 1, sizeof (buf), libfp) == sizeof (buf) && memcmp (buf, ARMAG, SARMAG) == 0))
60     rewind (libfp);
61
62   return ret;
63 }
64
65 static struct ar_hdr *
66 ar_get_header (struct ar_hdr *hdr, FILE * libfp)
67 {
68   char header[ARHDR_LEN];
69   char buf[AR_DATE_LEN + 1];
70
71   if (fread (header, 1, sizeof (header), libfp) != sizeof (header)
72       || memcmp (header + AR_FMAG_OFFSET, ARFMAG, AR_FMAG_LEN) != 0)
73     {
74       /* not an ar archive */
75       return NULL;
76     }
77
78   memcpy (hdr->ar_name, &header[AR_NAME_OFFSET], AR_NAME_LEN);
79   hdr->ar_name[AR_NAME_LEN] = '\0';
80
81   memcpy (buf, &header[AR_DATE_OFFSET], AR_DATE_LEN);
82   buf[AR_DATE_LEN] = '\0';
83   hdr->ar_date = strtol (buf, NULL, 0);
84
85   memcpy (buf, &header[AR_UID_OFFSET], AR_GID_LEN);
86   buf[AR_GID_LEN] = '\0';
87   hdr->ar_uid = (uid_t) strtol (buf, NULL, 0);
88
89   memcpy (buf, &header[AR_GID_OFFSET], AR_DATE_LEN);
90   buf[AR_DATE_LEN] = '\0';
91   hdr->ar_gid = (gid_t) strtol (buf, NULL, 0);
92
93   memcpy (buf, &header[AR_MODE_OFFSET], AR_MODE_LEN);
94   buf[AR_MODE_LEN] = '\0';
95   hdr->ar_mode = (mode_t) strtoul (buf, NULL, 0);
96
97   memcpy (buf, &header[AR_SIZE_OFFSET], AR_SIZE_LEN);
98   buf[AR_SIZE_LEN] = '\0';
99   hdr->ar_size = strtol (buf, NULL, 0);
100
101   return hdr;
102 }
103
104 static char *sym_tab;
105 static int sym_tab_size;
106
107 static void
108 loadfile_ar (struct lbfile *lbfh)
109 {
110   FILE *fp;
111
112 #ifdef __CYGWIN__
113   char posix_path[PATH_MAX];
114   void cygwin_conv_to_full_posix_path (char *win_path, char *posix_path);
115   cygwin_conv_to_full_posix_path (lbfh->libspc, posix_path);
116   fp = fopen (posix_path, "rb");
117 #else
118   fp = fopen (lbfh->libspc, "rb");
119 #endif
120
121   if (fp != NULL)
122     {
123       struct ar_hdr hdr;
124
125       fseek (fp, lbfh->offset, SEEK_SET);
126       if (ar_get_header (&hdr, fp))
127         {
128           D ("Loading module %s from file %s.\n", hdr.ar_name, lbfh->libspc);
129           load_rel (fp, hdr.ar_size);
130           fclose (fp);
131         }
132       else
133         {
134           fprintf (stderr, "?ASlink-Error-Bad offset in library file %s(%s)\n", lbfh->libspc, lbfh->relfil);
135           fclose (fp);
136           lkexit (1);
137         }
138     }
139   else
140     {
141       fprintf (stderr, "?ASlink-Error-Opening library '%s'\n", lbfh->libspc);
142       lkexit (1);
143     }
144 }
145
146 #if INDEXLIB
147 char *
148 get_member_name (char *name)
149 {
150   if (name[0] == '/')
151     {
152       if (NULL != sym_tab)
153         {
154           char *p;
155
156           int name_offset = strtol (++name, &p, 0);
157           if (p != name && name_offset < sym_tab_size)
158             {
159               int len = p - name + 1;
160               while (len < AR_NAME_LEN && name[len++] == ' ')
161                 ;
162               if (len == AR_NAME_LEN)
163                 {
164                   char *n;
165
166                   /* long name: get it from the symbol table */
167                   name = &sym_tab[name_offset];
168                   for (p = name; *p != '/' && *p != '\n'; ++p)
169                     assert (p < &sym_tab[sym_tab_size]);
170
171                   if (p[0] != '/' || p[1] != '\n')
172                     while (*++p != '\n')
173                       assert (p < &sym_tab[sym_tab_size]);
174
175                   n = (char *) malloc (p - name + 1);
176                   memcpy (n, name, p - name);
177                   n[p - name] = '\0';
178                   return n;
179                 }
180             }
181         }
182     }
183   else
184     {
185       char *p = strrchr (name, '/');
186
187       if (NULL != p)
188         {
189           int len = p - name;
190           while (name[++len] == ' ')
191             ;
192           if (len == AR_NAME_LEN)
193             {
194               char *n = (char *) malloc (p - name + 1);
195               memcpy (n, name, p - name);
196               n[p - name] = '\0';
197               return n;
198             }
199         }
200     }
201
202   /* bad formed member name:
203      just return it */
204
205   return strdup (name);
206 }
207
208 static char *
209 get_member_name_by_offset (FILE * fp, long offset)
210 {
211   struct ar_hdr hdr;
212
213   fseek (fp, offset, SEEK_SET);
214
215   /* walk trough all archive members */
216   return (NULL != ar_get_header (&hdr, fp)) ? get_member_name (hdr.ar_name) : NULL;
217 }
218
219 static pmlibraryfile
220 find_member_by_offset (const char *libspc, long offset)
221 {
222   pmlibraryfile p;
223
224   for (p = libr; p; p = p->next)
225     {
226       if (0 == strcmp (libspc, p->libspc) && p->offset == offset)
227         return p;
228     }
229
230   return NULL;
231 }
232
233 static pmlibraryfile
234 buildlibraryindex_ar (struct lbname *lbnh, FILE * libfp, pmlibraryfile This, int type)
235 {
236   struct ar_hdr hdr;
237
238   /* walk trough all archive members */
239   while (ar_get_header (&hdr, libfp))
240     {
241       if (AR_IS_SYMBOL_TABLE (hdr))
242         {
243           char *buf, *po, *ps;
244           int i;
245           long nsym;
246
247           buf = (char *) new (hdr.ar_size);
248
249           if ((off_t) fread (buf, 1, hdr.ar_size, libfp) != hdr.ar_size)
250             {
251               free (buf);
252               return This;
253             }
254
255           nsym = sgetl (buf);
256
257           po = buf + 4;
258           ps = po + nsym * 4;
259
260           for (i = 0; i < nsym; ++i)
261             {
262               pmlibrarysymbol ThisSym;
263               char *sym;
264               long offset;
265               pmlibraryfile entry;
266
267               offset = sgetl (po);
268               po += 4;
269
270               sym = strdup (ps);
271               while (*ps++ != '\0')
272                 ;
273
274               if ((entry = find_member_by_offset (lbnh->libspc, offset)) != NULL)
275                 {
276                   for (ThisSym = entry->symbols; ThisSym->next != NULL; ThisSym = ThisSym->next)
277                     ;
278                 }
279               else
280                 {
281                   /* Opened OK - create a new libraryfile object for it */
282                   if (This == NULL)
283                     {
284                       assert (libr == NULL);
285                       libr = This = (pmlibraryfile) new (sizeof (mlibraryfile));
286                     }
287                   else
288                     {
289                       This->next = (pmlibraryfile) new (sizeof (mlibraryfile));
290                       This = This->next;
291                     }
292                   This->next = NULL;
293                   This->loaded = 0;
294                   This->libspc = lbnh->libspc;
295                   This->offset = offset;
296                   This->relfil = get_member_name_by_offset (libfp, offset);     /* member name */
297                   This->filspc = strdup (This->relfil); /* member file name */
298                   This->type = type;
299
300                   /* start a new linked list of symbols for this module. */
301                   This->symbols = ThisSym = NULL;
302                 }
303
304               if (ThisSym == NULL)
305                 ThisSym = This->symbols = (pmlibrarysymbol) new (sizeof (mlibrarysymbol));
306               else
307                 {
308                   ThisSym->next = (pmlibrarysymbol) new (sizeof (mlibrarysymbol));
309                   ThisSym = ThisSym->next;
310                 }
311               ThisSym->next = NULL;
312               ThisSym->name = sym;
313             }
314           free (buf);
315
316           break;
317         }
318       else if (AR_IS_STRING_TABLE (hdr))
319         {
320           if (sym_tab)
321             free (sym_tab);
322
323           sym_tab = (char *) new (hdr.ar_size);
324
325           if ((off_t) fread (sym_tab, 1, hdr.ar_size, libfp) != hdr.ar_size)
326             {
327               free (sym_tab);
328               sym_tab_size = 0;
329               return This;
330             }
331           sym_tab_size = hdr.ar_size;
332         }
333       else
334         {
335           long moduleOffset = ftell (libfp);
336
337           /* Opened OK - create a new libraryfile object for it */
338           if (This == NULL)
339             {
340               assert (libr == NULL);
341               libr = This = (pmlibraryfile) new (sizeof (mlibraryfile));
342             }
343           else
344             {
345               This->next = (pmlibraryfile) new (sizeof (mlibraryfile));
346               This = This->next;
347             }
348           This->next = NULL;
349           This->loaded = -1;
350           This->libspc = lbnh->libspc;
351           This->offset = moduleOffset - ARHDR_LEN;
352
353           This->relfil = get_member_name (hdr.ar_name); /* member name */
354           This->filspc = strdup (This->relfil); /* member file name */
355
356           D ("  Indexing module: %s\n", This->relfil);
357
358           This->type = type;
359
360           /* start a new linked list of symbols for this module. */
361           This->symbols = NULL;
362
363           add_rel_index (libfp, hdr.ar_size, This);
364
365           fseek (libfp, moduleOffset + hdr.ar_size, SEEK_SET);
366         }
367
368       if (hdr.ar_size & 1)
369         {
370           int c = getc (libfp);
371           assert (c == EOF || c == '\n');
372         }
373     }
374
375   if (NULL != sym_tab)
376     {
377       free (sym_tab);
378       sym_tab = NULL;
379       sym_tab_size = 0;
380     }
381
382   return This;
383 }
384
385 #else
386
387 #if 0
388 static int
389 load_adb (FILE * libfp, struct lbfile *lbfh)
390 {
391   struct ar_hdr hdr;
392   char *adb_name;
393
394   /* check if it is a .rel file */
395   if (0 != stricmp (&lbfh->relfil[strlen (lbfh->relfil) - 4], ".rel"))
396     return 0;
397
398
399   adb_name = (char *) new (strlen (lbfh->relfil) + 1);
400   memcpy (adb_name, lbfh->relfil, strlen (lbfh->relfil) - 4);
401   memcpy (&adb_name[strlen (lbfh->relfil) - 4], ".adb", 5);
402
403   if (!is_ar (libfp))
404     {
405       fprintf (stderr, "?ASlink-Error-%s is not an archive\n", lbfh->libspc);
406       fclose (libfp);
407       lkexit (1);
408     }
409
410
411   /* walk trough all archive members */
412   while (ar_get_header (&hdr, libfp))
413     {
414       if (AR_IS_STRING_TABLE (hdr))
415         {
416           if (sym_tab)
417             free (sym_tab);
418
419           sym_tab = (char *) new (hdr.ar_size);
420
421           if ((off_t) fread (sym_tab, 1, hdr.ar_size, libfp) != hdr.ar_size)
422             {
423               free (sym_tab);
424               sym_tab_size = 0;
425               return 0;
426             }
427           sym_tab_size = hdr.ar_size;
428         }
429       if (AR_IS_SYMBOL_TABLE (hdr) || 0 != stricmp (get_member_name (hdr.ar_name), adb_name))
430         {
431           /* skip the mamber */
432           fseek (libfp, hdr.ar_size + (hdr.ar_size & 1), SEEK_CUR);
433         }
434       else
435         {
436           long left = hdr.ar_size;
437           char buf[4096];
438
439           while (left)
440             {
441               size_t n = min (left, sizeof buf);
442
443               if (fread (buf, 1, n, libfp) != n)
444                 {
445                   assert (0);
446                 }
447
448               fwrite (buf, 1, n, dfp);
449
450               left -= n;
451             }
452
453           if (hdr.ar_size & 1)
454             {
455               int c = getc (libfp);
456               assert (c == EOF || c == '\n');
457             }
458
459           free (adb_name);
460           return 1;
461         }
462     }
463
464   free (adb_name);
465   return 0;
466 }
467 #endif
468
469 static int
470 fndsym_ar (const char *name, struct lbname *lbnh, FILE * libfp, int type)
471 {
472   struct ar_hdr hdr;
473   int ret = 0;
474
475   /* walk trough all archive members */
476   while (ar_get_header (&hdr, libfp))
477     {
478       char filspc[PATH_MAX];
479
480       if (lbnh->path != NULL)
481         {
482           strcpy (filspc, lbnh->path);
483 #ifdef  OTHERSYSTEM
484           if (*filspc != '\0' && (filspc[strlen (filspc) - 1] != '/') && (filspc[strlen (filspc) - 1] != LKDIRSEP))
485             {
486               strcat (filspc, LKDIRSEPSTR);
487             }
488 #endif
489         }
490
491       if (AR_IS_SYMBOL_TABLE (hdr))
492         {
493           char *buf, *po, *ps;
494           int i;
495           long nsym;
496
497           buf = (char *) new (hdr.ar_size);
498
499           if ((off_t) fread (buf, 1, hdr.ar_size, libfp) != hdr.ar_size)
500             {
501               free (buf);
502               return 0;
503             }
504
505           nsym = sgetl (buf);
506
507           po = buf + 4;
508           ps = po + nsym * 4;
509
510           for (i = 0; i < nsym; ++i)
511             {
512               char *sym;
513               long offset;
514
515               offset = sgetl (po);
516               po += 4;
517
518               sym = ps;
519               while (*ps++ != '\0')
520                 ;
521
522               if (0 == strcmp (name, sym))
523                 {
524                   fseek (libfp, offset, SEEK_SET);
525                   if (ar_get_header (&hdr, libfp))
526                     {
527                       sprintf (&filspc[strlen (filspc)], "%s", hdr.ar_name);
528
529                       /* If this module has been loaded already don't load it again. */
530                       if (!is_module_loaded (filspc))
531                         {
532                           struct lbfile *lbfh, *lbf;
533
534                           lbfh = (struct lbfile *) new (sizeof (struct lbfile));
535                           lbfh->libspc = strdup (lbnh->libspc);
536                           lbfh->relfil = strdup (hdr.ar_name);
537                           lbfh->filspc = strdup (filspc);
538                           lbfh->offset = offset;
539                           lbfh->type = type;
540
541                           if (lbfhead == NULL)
542                             {
543                               lbfhead = lbfh;
544                             }
545                           else
546                             {
547                               for (lbf = lbfhead; lbf->next != NULL; lbf = lbf->next)
548                                 ;
549
550                               lbf->next = lbfh;
551                             }
552
553                           D ("Loading module %s from file %s.\n", hdr.ar_name, lbfh->libspc);
554                           load_rel (libfp, hdr.ar_size);
555                           ///* if cdb information required & .adb file present */
556                           //if (dflag && dfp)
557                           //  {
558                           //    if (load_adb(FILE *libfp, struct lbfile *lbfh))
559                           //      SaveLinkedFilePath (filspc);
560                           //  }
561                           ret = 1;
562                           break;
563                         }
564                     }
565                   else
566                     {
567                       fprintf (stderr, "?ASlink-Error-Bad offset in library file %s(%s)\n", lbnh->libspc, name);
568                       fclose (libfp);
569                       lkexit (1);
570                     }
571                 }
572             }
573           free (buf);
574
575           break;
576         }
577       else if (AR_IS_STRING_TABLE (hdr))
578         {
579           if (sym_tab)
580             free (sym_tab);
581
582           sym_tab = (char *) new (hdr.ar_size);
583
584           if ((off_t) fread (sym_tab, 1, hdr.ar_size, libfp) != hdr.ar_size)
585             {
586               free (sym_tab);
587               sym_tab = NULL;
588               sym_tab_size = 0;
589               return 0;
590             }
591           sym_tab_size = hdr.ar_size;
592         }
593       else
594         {
595           long moduleOffset = ftell (libfp);
596
597           D ("  Module: %s\n", hdr.ar_name);
598
599           sprintf (&filspc[strlen (filspc)], "%s", hdr.ar_name);
600
601           /* Opened OK - create a new libraryfile object for it */
602           ret = add_rel_file (name, lbnh, hdr.ar_name, filspc, moduleOffset - ARHDR_LEN, libfp, hdr.ar_size, type);
603           ///* if cdb information required & .adb file present */
604           //if (dflag && dfp)
605           //  {
606           //    if (load_adb(FILE *libfp, struct lbfile *lbfh))
607           //      SaveLinkedFilePath (filspc);
608           //  }
609           if (ret)
610             break;
611
612           fseek (libfp, moduleOffset + hdr.ar_size, SEEK_SET);
613         }
614
615       if (hdr.ar_size & 1)
616         {
617           int c = getc (libfp);
618           assert (c == EOF || c == '\n');
619         }
620     }
621
622   if (NULL != sym_tab)
623     {
624       free (sym_tab);
625       sym_tab = NULL;
626       sym_tab_size = 0;
627     }
628
629   return ret;
630 }
631 #endif
632
633 struct aslib_target aslib_target_ar = {
634   &is_ar,
635 #ifdef INDEXLIB
636   &buildlibraryindex_ar,
637 #else
638   &fndsym_ar,
639 #endif
640   &loadfile_ar,
641 };