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