c59becceaf0c1d0eb4094e3b52772e99bd2a0766
[fw/sdcc] / as / link / lklibr.c
1 /* lklibr.c */
2
3 /*
4  * (C) Copyright 1989-1995
5  * All Rights Reserved
6  *
7  * Alan R. Baldwin
8  * 721 Berkeley St.
9  * Kent, Ohio  44240
10  *
11  * With contributions for the
12  * object libraries from
13  * Ken Hornstein
14  * kenh@cmf.nrl.navy.mil
15  *
16  */
17
18 /*
19  * Extensions: P. Felber
20  */
21
22 #define EQ(A,B) !strcmp((A),(B))
23
24 #include <stdio.h>
25 #include <stdlib.h>
26 #include <string.h>
27 #include <ctype.h>
28 #include <assert.h>
29
30 #include "aslink.h"
31 #include "lkrel.h"
32 #include "lklibr.h"
33
34 /*)Module   lklibr.c
35  *
36  *  The module lklibr.c contains the functions which
37  *  (1) specify the path(s) to library files [.LIB]
38  *  (2) specify the library file(s) [.LIB] to search
39  *  (3) search the library files for specific symbols
40  *      and link the module containing this symbol
41  *
42  *  lklibr.c contains the following functions:
43  *      VOID    addpath()
44  *      VOID    addlib()
45  *      VOID    addfile()
46  *      VOID    search()
47  *      VOID    fndsym()
48  *      VOID    library()
49  *      VOID    loadfile()
50  *
51  */
52
53 #define NELEM(x) (sizeof (x) / sizeof (*x))
54
55 #ifdef INDEXLIB
56 /* First entry in the library object symbol cache */
57 pmlibraryfile libr = NULL;
58
59 int buildlibraryindex (void);
60 void freelibraryindex (void);
61 #endif /* INDEXLIB */
62
63 struct aslib_target *aslib_targets[] = {
64   &aslib_target_sdcclib,
65   &aslib_target_lib,
66 };
67
68 /*)Function VOID    addpath()
69  *
70  *  The function addpath() creates a linked structure containing
71  *  the paths to various object module library files.
72  *
73  *  local variables:
74  *      lbpath  *lbph       pointer to new path structure
75  *      lbpath  *lbp        temporary pointer
76  *
77  *  global variables:
78  *      lbpath  *lbphead    The pointer to the first
79  *                          path structure
80  *
81  *   functions called:
82  *      char    getnb()     lklex.c
83  *      VOID *  new()       lksym.c
84  *      int     strlen()    c_library
85  *      char *  strcpy()    c_library
86  *      VOID    unget()     lklex.c
87  *
88  *  side effects:
89  *      An lbpath structure may be created.
90  */
91
92 VOID
93 addpath (void)
94 {
95   struct lbpath *lbph, *lbp;
96
97   lbph = (struct lbpath *) new (sizeof (struct lbpath));
98   if (lbphead == NULL)
99     {
100       lbphead = lbph;
101     }
102   else
103     {
104       lbp = lbphead;
105       while (lbp->next)
106         {
107           lbp = lbp->next;
108         }
109       lbp->next = lbph;
110     }
111   unget (getnb ());
112   lbph->path = strdup (ip);
113 }
114
115 /*)Function VOID    addlib()
116  *
117  *  The function addlib() tests for the existance of a
118  *  library path structure to determine the method of
119  *  adding this library file to the library search structure.
120  *
121  *  This function calls the function addfile() to actually
122  *  add the library file to the search list.
123  *
124  *  local variables:
125  *      lbpath  *lbph       pointer to path structure
126  *
127  *  global variables:
128  *      lbpath  *lbphead    The pointer to the first
129  *                          path structure
130  *      ip a pointer to the library name
131  *
132  *   functions called:
133  *      VOID    addfile()   lklibr.c
134  *      char    getnb()     lklex.c
135  *      VOID    unget()     lklex.c
136  *
137  *  side effects:
138  *      The function addfile() may add the file to
139  *      the library search list.
140  */
141
142 VOID
143 addlib (void)
144 {
145   struct lbpath *lbph;
146   int foundcount = 0;
147
148   unget (getnb ());
149
150   if (lbphead == NULL)
151     {
152       foundcount = addfile (NULL, ip);
153     }
154   else
155     {
156       for (lbph = lbphead; lbph; lbph = lbph->next)
157         {
158           foundcount += addfile (lbph->path, ip);
159         }
160     }
161   if (foundcount == 0)
162     {
163       fprintf (stderr, "?ASlink-Warning-Couldn't find library '%s'\n", ip);
164     }
165 }
166
167 /*)Function int addfile(path,libfil)
168  *
169  *      char    *path       library path specification
170  *      char    *libfil     library file specification
171  *
172  *  The function addfile() searches for the library file
173  *  by concatenating the path and libfil specifications.
174  *  if the library is found, an lbname structure is created
175  *  and linked to any previously defined structures.  This
176  *  linked list is used by the function fndsym() to attempt
177  *  to find any undefined symbols.
178  *
179  *  The function does not give report an error on invalid
180  *  path / file specifications or if the file is not found.
181  *
182  *  local variables:
183  *      lbname  *lbnh       pointer to new name structure
184  *      lbname  *lbn        temporary pointer
185  *
186  *  global variables:
187  *      lbname  *lbnhead    The pointer to the first
188  *                          path structure
189  *
190  *   functions called:
191  *      char    getnb()     lklex.c
192  *      VOID *  new()       lksym.c
193  *      int     strlen()    c_library
194  *      char *  strcpy()    c_library
195  *      VOID    unget()     lklex.c
196  *
197  *  side effects:
198  *      An lbname structure may be created.
199  *
200  *  return:
201  *      1: the library was found
202  *      0: the library was not found
203  */
204
205 int
206 addfile (char *path, char *libfil)
207 {
208   FILE *fp;
209   char *str;
210   struct lbname *lbnh, *lbn;
211 #ifdef  OTHERSYSTEM
212   int libfilinc = 0;
213 #endif
214
215   if (path != NULL)
216     {
217       str = (char *) new (strlen (path) + strlen (libfil) + 6);
218       strcpy (str, path);
219 #ifdef  OTHERSYSTEM
220       if (strlen (str) && (str[strlen (str) - 1] != '/') && (str[strlen (str) - 1] != LKDIRSEP))
221         {
222           strcat (str, LKDIRSEPSTR);
223         }
224 #endif
225     }
226   else
227     {
228       str = (char *) new (strlen (libfil) + 5);
229     }
230
231 #ifdef  OTHERSYSTEM
232   if ((libfil[0] == '/') || (libfil[0] == LKDIRSEP))
233     {
234       libfil++;
235       libfilinc = 1;
236     }
237 #endif
238
239   strcat (str, libfil);
240   if (strchr (libfil, FSEPX) == NULL)
241     {
242       sprintf (&str[strlen (str)], "%clib", FSEPX);
243     }
244
245   fp = fopen (str, "rb");
246   if (fp == NULL)
247     {
248       /*Ok, that didn't work.  Try with the 'libfil' name only */
249 #ifdef  OTHERSYSTEM
250       if (libfilinc)
251         libfil--;
252 #endif
253       fp = fopen (libfil, "rb");
254       if (fp != NULL)
255         {
256           /*Bingo!  'libfil' is the absolute path of the library */
257           strcpy (str, libfil);
258           path = NULL;          /*This way 'libfil' and 'path' will be rebuilt from 'str' */
259         }
260     }
261
262   if (path == NULL)
263     {
264       /*'path' can not be null since it is needed to find the object files associated with
265          the library.  So, get 'path' from 'str' and then chop it off and recreate 'libfil'.
266          That way putting 'path' and 'libfil' together will result into the original filepath
267          as contained in 'str'. */
268       int j;
269       path = strdup (str);
270       for (j = strlen (path) - 1; j >= 0; j--)
271         {
272           if ((path[j] == '/') || (path[j] == LKDIRSEP))
273             {
274               strcpy (libfil, &path[j + 1]);
275               path[j + 1] = 0;
276               break;
277             }
278         }
279       if (j <= 0)
280         path[0] = 0;
281     }
282
283   if (fp != NULL)
284     {
285       fclose (fp);
286       lbnh = (struct lbname *) new (sizeof (struct lbname));
287       if (lbnhead == NULL)
288         {
289           lbnhead = lbnh;
290         }
291       else
292         {
293           lbn = lbnhead;
294           while (lbn->next)
295             {
296               lbn = lbn->next;
297             }
298           lbn->next = lbnh;
299         }
300
301       lbnh->path = path;
302       lbnh->libfil = strdup (libfil);
303       lbnh->libspc = str;
304       return 1;
305     }
306   else
307     {
308       free (str);
309       return 0;
310     }
311 }
312
313 /*)Function VOID    search()
314  *
315  *  The function search() looks through all the symbol tables
316  *  at the end of pass 1.  If any undefined symbols are found
317  *  then the function fndsym() is called. Function fndsym()
318  *  searches any specified library files to automagically
319  *  import the object modules containing the needed symbol.
320  *
321  *  After a symbol is found and imported by the function
322  *  fndsym() the symbol tables are again searched.  The
323  *  symbol tables are search until no more symbols can be
324  *  resolved within the library files.  This ensures that
325  *  back references from one library module to another are
326  *  also resolved.
327  *
328  *  local variables:
329  *      int     i           temporary counter
330  *      sym     *sp         pointer to a symbol structure
331  *      int     symfnd      found a symbol flag
332  *
333  *  global variables:
334  *      sym     *symhash[]  array of pointers to symbol tables
335  *
336  *   functions called:
337  *      int     fndsym()    lklibr.c
338  *
339  *  side effects:
340  *      If a symbol is found then the library object module
341  *      containing the symbol will be imported and linked.
342  */
343
344 VOID
345 search (void)
346 {
347   register struct sym *sp;
348   register int i, symfnd;
349
350   /*
351    * Look for undefined symbols.  Keep
352    * searching until no more symbols are resolved.
353    */
354   symfnd = 1;
355   while (symfnd)
356     {
357       symfnd = 0;
358       /*
359        * Look through all the symbols
360        */
361       for (i = 0; i < NHASH; ++i)
362         {
363           sp = symhash[i];
364           while (sp)
365             {
366               /* If we find an undefined symbol
367                * (one where S_DEF is not set), then
368                * try looking for it.  If we find it
369                * in any of the libraries then
370                * increment symfnd.  This will force
371                * another pass of symbol searching and
372                * make sure that back references work.
373                */
374               if ((sp->s_type & S_DEF) == 0)
375                 {
376                   if (fndsym (sp->s_id))
377                     {
378                       symfnd++;
379                     }
380                 }
381               sp = sp->s_sp;
382             }
383         }
384     }
385 }
386
387 /*)Function VOID    fndsym(name)
388  *
389  *      char    *name       symbol name to find
390  *
391  *  The function fndsym() searches through all combinations of the
392  *  library path specifications (input by the -k option) and the
393  *  library file specifications (input by the -l option) that
394  *  lead to an existing file.
395  *
396  *  The file specicifation may be formed in one of two ways:
397  *
398  *  (1) If the library file contained an absolute
399  *      path/file specification then this becomes filspc.
400  *      (i.e. C:\...)
401  *
402  *  (2) If the library file contains a relative path/file
403  *      specification then the concatenation of the path
404  *      and this file specification becomes filspc.
405  *      (i.e. \...)
406  *
407  *  The structure lbfile is created for the first library
408  *  object file which contains the definition for the
409  *  specified undefined symbol.
410  *
411  *  If the library file [.LIB] contains file specifications for
412  *  non existant files, no errors are returned.
413  *
414  *  local variables:
415  *      char    buf[]       [.REL] file input line
416  *      char    c           [.REL] file input character
417  *      FILE    *fp         file handle for object file
418  *      lbfile  *lbf        temporary pointer
419  *      lbfile  *lbfh       pointer to lbfile structure
420  *      FILE    *libfp      file handle for library file
421  *      lbname  *lbnh       pointer to lbname structure
422  *      char    *path       file specification path
423  *      char    relfil[]    [.REL] file specification
424  *      char    *str        combined path and file specification
425  *      char    symname[]   [.REL] file symbol string
426  *
427  *  global variables:
428  *      lbname  *lbnhead    The pointer to the first
429  *                          name structure
430  *      lbfile  *lbfhead    The pointer to the first
431  *                          file structure
432  *
433  *   functions called:
434  *      int     fclose()    c_library
435  *      FILE    *fopen()    c_library
436  *      VOID    free()      c_library
437  *      char    getnb()     lklex.c
438  *      VOID    lkexit()    lkmain.c
439  *      VOID    loadfile()  lklibr.c
440  *      VOID *  new()       lksym.c
441  *      char *  sprintf()   c_library
442  *      int     sscanf()    c_library
443  *      char *  strcat()    c_library
444  *      char *  strchr()    c_library
445  *      char *  strcpy()    c_library
446  *      int     strlen()    c_library
447  *      int     strncmp()   c_library
448  *      VOID    unget()     lklex.c
449  *
450  *  side effects:
451  *      If the symbol is found then a new lbfile structure
452  *      is created and added to the linked list of lbfile
453  *      structures.  The file containing the found symbol
454  *      is linked.
455  */
456
457 #ifdef INDEXLIB
458 int
459 fndsym (char *name)
460 {
461   struct lbfile *lbfh, *lbf;
462   pmlibraryfile ThisLibr;
463   pmlibrarysymbol ThisSym = NULL;
464
465   pmlibraryfile FirstFound;
466   int numfound = 0;
467
468   /* Build the index if this is the first call to fndsym */
469   if (libr == NULL)
470     buildlibraryindex ();
471
472   /* Iterate through all library object files */
473   ThisLibr = libr;
474   FirstFound = libr;            /*So gcc stops whining */
475   while (ThisLibr)
476     {
477       /* Iterate through all symbols in an object file */
478       ThisSym = ThisLibr->symbols;
479
480       while (ThisSym)
481         {
482           if (!strcmp (ThisSym->name, name))
483             {
484               if ((!ThisLibr->loaded) && (numfound == 0))
485                 {
486                   /* Object file is not loaded - add it to the list */
487                   lbfh = (struct lbfile *) new (sizeof (struct lbfile));
488                   if (lbfhead == NULL)
489                     {
490                       lbfhead = lbfh;
491                     }
492                   else
493                     {
494                       lbf = lbfhead;
495                       while (lbf->next)
496                         {
497                           lbf = lbf->next;
498                         }
499                       lbf->next = lbfh;
500                     }
501                   lbfh->libspc = ThisLibr->libspc;
502                   lbfh->filspc = ThisLibr->filename;
503                   lbfh->relfil = strdup (ThisLibr->relfil);
504                   lbfh->offset = ThisLibr->offset;
505                   lbfh->type = ThisLibr->type;
506
507                   (*aslib_targets[lbfh->type]->loadfile) (lbfh);
508
509                   ThisLibr->loaded = 1;
510                 }
511
512               if (numfound == 0)
513                 {
514                   numfound++;
515                   FirstFound = ThisLibr;
516                 }
517               else
518                 {
519                   char absPath1[PATH_MAX];
520                   char absPath2[PATH_MAX];
521 #if defined(__BORLANDC__) || defined(_MSC_VER) || defined(__MINGW32__)
522                   int j;
523
524                   _fullpath (absPath1, FirstFound->libspc, PATH_MAX);
525                   _fullpath (absPath2, ThisLibr->libspc, PATH_MAX);
526                   for (j = 0; absPath1[j] != 0; j++)
527                     absPath1[j] = tolower ((unsigned char) absPath1[j]);
528                   for (j = 0; absPath2[j] != 0; j++)
529                     absPath2[j] = tolower ((unsigned char) absPath2[j]);
530 #else
531                   realpath (FirstFound->libspc, absPath1);
532                   realpath (ThisLibr->libspc, absPath2);
533 #endif
534                   if (!(EQ (absPath1, absPath2) && EQ (FirstFound->relfil, ThisLibr->relfil)))
535                     {
536                       if (numfound == 1)
537                         {
538                           fprintf (stderr, "?ASlink-Warning-Definition of public symbol '%s'" " found more than once:\n", name);
539                           fprintf (stderr, "   Library: '%s', Module: '%s'\n", FirstFound->libspc, FirstFound->relfil);
540                         }
541                       fprintf (stderr, "   Library: '%s', Module: '%s'\n", ThisLibr->libspc, ThisLibr->relfil);
542                       numfound++;
543                     }
544                 }
545             }
546           ThisSym = ThisSym->next;      /* Next sym in library */
547         }
548       ThisLibr = ThisLibr->next;        /* Next library in list */
549     }
550   return numfound;
551 }
552
553 struct add_sym_s
554 {
555   pmlibraryfile plf;
556   pmlibrarysymbol pls;
557 };
558
559 static int
560 add_sybmol (const char *sym, void *param)
561 {
562   struct add_sym_s *as = (struct add_sym_s *) param;
563   pmlibrarysymbol ps = (pmlibrarysymbol) new (sizeof (mlibrarysymbol));
564
565   as->plf->loaded = 0;
566   ps->next = NULL;
567   ps->name = strdup (sym);
568
569   if (as->pls == NULL)
570     {
571       as->pls = as->plf->symbols = ps;
572     }
573   else
574     {
575       as->pls->next = ps;
576       as->pls = as->pls->next;
577     }
578
579   return 0;
580 }
581
582 pmlibrarysymbol
583 add_rel_index (FILE * fp, long size, pmlibraryfile This)
584 {
585   struct add_sym_s as;
586   as.plf = This;
587   as.pls = This->symbols;
588
589   assert (This->symbols == NULL);
590
591   enum_symbols (fp, size, &add_sybmol, &as);
592
593   return as.pls;
594 }
595
596 /* buildlibraryindex - build an in-memory cache of the symbols contained in
597  *                     the libraries
598  */
599 int
600 buildlibraryindex (void)
601 {
602   pmlibraryfile This = NULL;
603   struct lbname *lbnh;
604
605   /*
606    * Search through every library in the linked list "lbnhead".
607    */
608   for (lbnh = lbnhead; lbnh; lbnh = lbnh->next)
609     {
610       FILE *libfp;
611       int i;
612
613       if ((libfp = fopen (lbnh->libspc, "rb")) == NULL)
614         {
615           fprintf (stderr, "?ASlink-Error-Cannot open library file %s\n", lbnh->libspc);
616           lkexit (1);
617         }
618
619       for (i = 0; i < NELEM (aslib_targets); ++i)
620         {
621           if ((*aslib_targets[i]->is_lib) (libfp))
622             {
623               This = (*aslib_targets[i]->buildlibraryindex) (lbnh, libfp, This, i);
624               break;
625             }
626         }
627
628       if (i >= NELEM (aslib_targets))
629         fprintf (stderr, "?ASlink-Error-Unknown library file format %s\n", lbnh->libspc);
630
631       fclose (libfp);
632     }
633
634   return 0;
635 }
636
637 /*Release all memory allocated for the in-memory library index*/
638 void
639 freelibraryindex (void)
640 {
641   pmlibraryfile ThisLibr, ThisLibr2Free;
642   pmlibrarysymbol ThisSym, ThisSym2Free;
643
644   ThisLibr = libr;
645
646   while (ThisLibr)
647     {
648       ThisSym = ThisLibr->symbols;
649
650       while (ThisSym)
651         {
652           free (ThisSym->name);
653           ThisSym2Free = ThisSym;
654           ThisSym = ThisSym->next;
655           free (ThisSym2Free);
656         }
657       free (ThisLibr->filename);
658       free (ThisLibr->relfil);
659       ThisLibr2Free = ThisLibr;
660       ThisLibr = ThisLibr->next;
661       free (ThisLibr2Free);
662     }
663
664   libr = NULL;
665 }
666
667 #else /* INDEXLIB */
668
669 struct load_sym_s
670 {
671   const char *name;
672   struct lbname *lbnh;
673   const char *relfil;
674   const char *filspc;
675   int offset;
676   int type;
677 };
678
679 static int
680 load_sybmol (const char *sym, void *params)
681 {
682   struct load_sym_s *ls = (struct load_sym_s *) params;
683
684   if (strcmp (ls->name, sym) == 0)
685     {
686       struct lbfile *lbfh, *lbf;
687
688       lbfh = (struct lbfile *) new (sizeof (struct lbfile));
689       lbfh->libspc = ls->lbnh->libspc;
690       lbfh->relfil = strdup (ls->relfil);
691       lbfh->filspc = strdup (ls->filspc);
692       lbfh->offset = ls->offset;
693       lbfh->type = ls->type;
694
695       if (lbfhead == NULL)
696         {
697           lbfhead = lbfh;
698         }
699       else
700         {
701           lbf = lbfhead;
702           while (lbf->next)
703             {
704               lbf = lbf->next;
705             }
706           lbf->next = lbfh;
707         }
708
709       (*aslib_targets[ls->type]->loadfile) (lbfh);
710
711       return 1;
712     }
713   else
714     return 0;
715 }
716
717 int
718 add_rel_file (const char *name, struct lbname *lbnh, const char *relfil,
719               const char *filspc, int offset, FILE * fp, long size, int type)
720 {
721   struct load_sym_s ls;
722   ls.name = name;
723   ls.lbnh = lbnh;
724   ls.relfil = relfil;
725   ls.filspc = filspc;
726   ls.offset = offset;
727   ls.type = type;
728
729   return enum_symbols (fp, size, &load_sybmol, &ls);
730 }
731
732 int
733 fndsym (const char *name)
734 {
735   FILE *libfp;
736   struct lbname *lbnh;
737   int i;
738
739   /*
740    * Search through every library in the linked list "lbnhead".
741    */
742   for (lbnh = lbnhead; lbnh; lbnh = lbnh->next)
743     {
744       int ret;
745
746       if ((libfp = fopen (lbnh->libspc, "rb")) == NULL)
747         {
748           fprintf (stderr, "?ASlink-Error-Cannot open library file %s\n", lbnh->libspc);
749           lkexit (1);
750         }
751
752       for (i = 0; i < NELEM (aslib_targets); ++i)
753         {
754           if ((*aslib_targets[i]->is_lib) (libfp))
755             {
756               ret = (*aslib_targets[i]->fndsym) (name, lbnh, libfp, i);
757               break;
758             }
759         }
760
761       if (i >= NELEM (aslib_targets))
762         fprintf (stderr, "?ASlink-Error-Unknown library file format %s\n", lbnh->libspc);
763
764       fclose (libfp);
765
766       if (!ret)
767         break;
768
769     }                           /* Ends good open of libr file */
770   return 0;
771 }
772 #endif /* INDEXLIB */
773
774 /*)Function VOID    library()
775  *
776  *  The function library() links all the library object files
777  *  contained in the lbfile structures.
778  *
779  *  local variables:
780  *      lbfile  *lbfh       pointer to lbfile structure
781  *
782  *  global variables:
783  *      lbfile  *lbfhead    pointer to first lbfile structure
784  *
785  *   functions called:
786  *      VOID    loadfile    lklibr.c
787  *
788  *  side effects:
789  *      Links all files contained in the lbfile structures.
790  */
791
792 VOID
793 library (void)
794 {
795   struct lbfile *lbfh;
796
797   for (lbfh = lbfhead; lbfh; lbfh = lbfh->next)
798     (*aslib_targets[lbfh->type]->loadfile) (lbfh);
799
800 #ifdef INDEXLIB
801   freelibraryindex ();
802 #endif
803 }