1 /*===========================================================================
2 *
3 *                            PUBLIC DOMAIN NOTICE
4 *               National Center for Biotechnology Information
5 *
6 *  This software/database is a "United States Government Work" under the
7 *  terms of the United States Copyright Act.  It was written as part of
8 *  the author's official duties as a United States Government employee and
9 *  thus cannot be copyrighted.  This software/database is freely available
10 *  to the public for use. The National Library of Medicine and the U.S.
11 *  Government have not placed any restriction on its use or reproduction.
12 *
13 *  Although all reasonable efforts have been taken to ensure the accuracy
14 *  and reliability of the software and data, the NLM and the U.S.
15 *  Government do not and cannot warrant the performance or results that
16 *  may be obtained by using this software or data. The NLM and the U.S.
17 *  Government disclaim all warranties, express or implied, including
18 *  warranties of performance, merchantability or fitness for any particular
19 *  purpose.
20 *
21 *  Please cite the author in any work or product based on this material.
22 *
23 * ===========================================================================
24 *
25 */
26 
27 #include <vdb/extern.h>
28 
29 #define TRACK_REFERENCES 0
30 
31 #include "linker-priv.h"
32 #include "schema-priv.h"
33 #include "dbmgr-priv.h"
34 
35 #include <kfs/directory.h>
36 #include <kfs/dyload.h>
37 #include <kfs/kfs-priv.h>
38 #include <klib/symbol.h>
39 #include <klib/symtab.h>
40 #include <klib/rc.h>
41 #include <sysalloc.h>
42 
43 #include <stdlib.h>
44 #include <stdio.h>
45 #include <string.h>
46 #include <assert.h>
47 
48 
49 /*--------------------------------------------------------------------------
50  * LFactory
51  *  describes an external C function factory
52  */
53 
54 /* Whack
55  */
LFactoryWhack(void * item,void * ignore)56 void CC LFactoryWhack ( void *item, void *ignore )
57 {
58     LFactory *self = item;
59 
60     /* whack the guy's factory object */
61     if ( self -> desc . whack != NULL )
62         ( * self -> desc . whack ) ( self -> desc . fself );
63 
64     /* douse the dynamic library */
65     KSymAddrRelease ( self -> addr );
66 
67     /* gone */
68     free ( self );
69 }
70 
71 
72 /*--------------------------------------------------------------------------
73  * LSpecial
74  *  describes an external C table recognition function
75  */
76 
77 /* Whack
78  */
LSpecialWhack(void * item,void * ignore)79 void CC LSpecialWhack ( void *item, void *ignore )
80 {
81     LSpecial *self = item;
82     KSymAddrRelease ( self -> addr );
83     free ( self );
84 }
85 
86 
87 /*--------------------------------------------------------------------------
88  * VLinker
89  */
90 
91 /* Whack
92  */
93 static
VLinkerWhack(VLinker * self)94 rc_t CC VLinkerWhack ( VLinker *self )
95 {
96     KRefcountWhack ( & self -> refcount, "VLinker" );
97 
98     VectorWhack ( & self -> fact, LFactoryWhack, NULL );
99     VectorWhack ( & self -> special, LSpecialWhack, NULL );
100     BSTreeWhack ( & self -> scope, KSymbolWhack, NULL );
101 
102     KDyldRelease ( self -> dl );
103     VLinkerSever ( self -> dad );
104 
105     free ( self );
106 
107     return 0;
108 }
109 
110 
111 /* StartIdx
112  *  returns starting index
113  */
114 #define VLinkerStartIdx( dad, vect, starting ) \
115     ( ( ( dad ) == NULL ) ? ( starting ) : \
116       ( VectorStart ( & ( dad ) -> vect ) + \
117         VectorLength ( & ( dad ) -> vect ) ) )
118 
119 /* VectorInit
120  *  performs vector initialization
121  */
122 #define VLinkerVectorInit( linker, dad, vect, starting, block ) \
123     VectorInit ( & ( linker ) -> vect, VLinkerStartIdx ( dad, vect, starting ), block )
124 
125 /* Make
126  *  creates an empty linker
127  */
VLinkerMake(VLinker ** lp,const VLinker * dad,struct KDyld * dl)128 rc_t VLinkerMake ( VLinker **lp, const VLinker *dad, struct KDyld *dl )
129 {
130     rc_t rc;
131 
132     VLinker *linker = malloc ( sizeof * linker );
133     if ( linker == NULL )
134         return RC ( rcVDB, rcMgr, rcConstructing, rcMemory, rcExhausted );
135 
136     rc = KDyldAddRef ( linker -> dl = dl );
137     if ( rc == 0 )
138     {
139         linker -> dad = VLinkerAttach ( dad );
140         BSTreeInit ( & linker -> scope );
141 
142         VLinkerVectorInit ( linker, dad, fact, 1, 64 );
143         VLinkerVectorInit ( linker, dad, special, 1, 8 );
144 
145         KRefcountInit ( & linker -> refcount, 1, "VLinker", "make", "vld" );
146 
147         * lp = linker;
148         return 0;
149     }
150 
151     free ( linker );
152     return rc;
153 }
154 
155 /* Release
156  */
VLinkerRelease(const VLinker * self)157 rc_t VLinkerRelease ( const VLinker *self )
158 {
159     if ( self != NULL )
160     {
161         switch ( KRefcountDrop ( & self -> refcount, "VLinker" ) )
162         {
163         case krefWhack:
164             return VLinkerWhack ( ( VLinker* ) self );
165         case krefNegative:
166             return RC ( rcVDB, rcMgr, rcAttaching, rcRange, rcExcessive );
167         }
168     }
169     return 0;
170 }
171 
172 /* Attach
173  * Sever
174  */
VLinkerAttach(const VLinker * self)175 VLinker *VLinkerAttach ( const VLinker *self )
176 {
177     if ( self != NULL )
178     {
179         switch ( KRefcountAddDep ( & self -> refcount, "VLinker" ) )
180         {
181         case krefLimit:
182             return NULL;
183         }
184     }
185     return ( VLinker* ) self;
186 }
187 
VLinkerSever(const VLinker * self)188 rc_t VLinkerSever ( const VLinker *self )
189 {
190     if ( self != NULL )
191     {
192         switch ( KRefcountDropDep ( & self -> refcount, "VLinker" ) )
193         {
194         case krefWhack:
195             return VLinkerWhack ( ( VLinker* ) self );
196         case krefNegative:
197             return RC ( rcVDB, rcMgr, rcAttaching, rcRange, rcExcessive );
198         }
199     }
200     return 0;
201 }
202 
203 
204 /* AddLoadLibraryPath
205  *  add a path[s] to loader for locating dynamic libraries
206  */
VLinkerVAddLoadLibraryPath(const VLinker * self,const char * path,va_list args)207 rc_t VLinkerVAddLoadLibraryPath ( const VLinker *self, const char *path, va_list args )
208 {
209     if ( self != NULL )
210         return KDyldVAddSearchPath ( self -> dl, path, args );
211 
212     return RC ( rcVDB, rcMgr, rcUpdating, rcSelf, rcNull );
213 }
214 
215 
216 /* Open
217  *  opens libraries for search
218  */
VLinkerOpen(const VLinker * self,struct KDlset ** libs)219 rc_t VLinkerOpen ( const VLinker *self, struct KDlset **libs )
220 {
221     rc_t rc = KDyldMakeSet ( self -> dl, libs );
222     if ( rc == 0 )
223     {
224         KDlset *set = * libs;
225 
226         rc = KDlsetAddAll ( set );
227         if ( rc == 0 )
228             return 0;
229 
230         KDlsetRelease ( set );
231         * libs = NULL;
232     }
233     return rc;
234 }
235 
236 
237 /* EnterIntoScope
238  *  pushes linker symbol tables into scope
239  */
240 static
VLinkerEnterIntoScope(const VLinker * self,KSymTable * tbl)241 rc_t CC VLinkerEnterIntoScope ( const VLinker *self, KSymTable *tbl )
242 {
243     rc_t rc;
244 
245     if ( self -> dad == NULL )
246         rc = KSymTableInit ( tbl, NULL );
247     else
248         rc = VLinkerEnterIntoScope ( self -> dad, tbl );
249 
250     if ( rc == 0 )
251         rc = KSymTablePushScope ( tbl, & ( ( VLinker* ) self ) -> scope );
252 
253     return rc;
254 }
255 
256 
257 /* MakeFQN
258  */
259 static
VLinkerMakeFQN(char * buffer,size_t bsize,const KSymbol * name)260 size_t CC VLinkerMakeFQN ( char *buffer, size_t bsize, const KSymbol *name )
261 {
262     size_t sz = 0;
263 
264     if ( name -> dad != NULL )
265     {
266         sz = VLinkerMakeFQN ( buffer, bsize, name -> dad );
267         if ( sz < bsize )
268             buffer [ sz ++ ] = '_';
269     }
270 
271     if ( sz < bsize )
272     {
273         sz += string_copy ( & buffer [ sz ], bsize - sz,
274             name -> name . addr, name -> name . size );
275     }
276 
277     return sz;
278 }
279 
280 /* ScanFactory
281  *  scans code modules for all named entrypoints
282  *  compares version numbers, retaining the highest
283  *  that satisfies stated functional interface
284  */
285 typedef struct VLinkerFactoryMatchData VLinkerFactoryMatchData;
286 struct VLinkerFactoryMatchData
287 {
288     VTransDesc desc;
289     uint32_t version;
290 };
291 
292 static
VLinkerBestMatch(const KSymAddr * sym,void * data)293 bool CC VLinkerBestMatch ( const KSymAddr *sym, void *data )
294 {
295     VLinkerFactoryMatchData *pb = data;
296 
297     rc_t rc;
298     VTransDesc desc;
299 
300     rc_t ( CC * f ) ( VTransDesc* );
301     KSymAddrAsFunc ( sym, ( fptr_t* ) & f );
302 
303     memset ( & desc, 0, sizeof desc );
304     rc = ( * f ) ( & desc );
305     if ( rc == 0 )
306     {
307         /* we don't actually expect to see two versions
308            of a factory at all, much less with the same
309            major interface version */
310         uint32_t maj = desc . itf_version >> 24;
311         if ( maj != 0 && maj == ( pb -> desc . itf_version >> 24 ) )
312         {
313             /* TBD - issue warning */
314         }
315 
316         /* prototype should have been declared with a version,
317            but if not, then any version will do */
318         if ( pb -> version == 0 ||
319              ( maj == ( pb -> version >> 24 ) &&
320                desc . itf_version >= pb -> version ) )
321         {
322             /* accept latest version */
323             if ( desc . itf_version > pb -> desc . itf_version )
324             {
325                 pb -> desc = desc;
326                 return true;
327             }
328         }
329     }
330     return false;
331 }
332 
333 static
VLinkerScanFactory(VLinker * self,const KDlset * libs,KSymTable * tbl,const KSymbol ** symp,const KSymbol * name,uint32_t version)334 rc_t VLinkerScanFactory ( VLinker *self, const KDlset *libs,
335     KSymTable *tbl, const KSymbol **symp, const KSymbol *name,
336     uint32_t version )
337 {
338     rc_t rc;
339 
340     /* convert schema fqn into legal C identifier */
341     char fqn [ 1024 ];
342     size_t sz = VLinkerMakeFQN ( fqn, sizeof fqn, name );
343     if ( sz >= sizeof fqn )
344         rc = RC ( rcVDB, rcMgr, rcResolving, rcName, rcExcessive );
345     else
346     {
347         KSymAddr *entrypoint;
348         VLinkerFactoryMatchData pb;
349         memset ( & pb, 0, sizeof pb );
350         pb . version = version;
351 
352         /* ask loader to locate fqn in library search path */
353         rc = KDlsetLastSymbol ( libs, & entrypoint,
354             fqn, VLinkerBestMatch, & pb );
355         if ( rc == 0 )
356         {
357             /* allocate factory object */
358             LFactory *fact = malloc ( sizeof * fact );
359             if ( fact == NULL )
360                 rc = RC ( rcVDB, rcMgr, rcResolving, rcMemory, rcExhausted );
361             else
362             {
363                 /* give it the exact schema name */
364                 rc = KSymTableDupSymbol ( tbl,
365                     ( KSymbol** ) & fact -> name, name, ltFactory, fact );
366                 if ( rc == 0 )
367                 {
368                     /* finally, insert the symbol into our vector */
369                     rc = VectorAppend ( & self -> fact, & fact -> id, fact );
370                     if ( rc == 0 )
371                     {
372                         /* done */
373                         fact -> addr = entrypoint;
374                         fact -> desc = pb . desc;
375                         fact -> external = true;
376                         * symp = fact -> name;
377                         return 0;
378                     }
379 
380                     KSymTableRemoveSymbol ( tbl, fact -> name );
381                 }
382 
383                 free ( fact );
384             }
385 
386             KSymAddrRelease ( entrypoint );
387         }
388     }
389 
390     return rc;
391 }
392 
393 /* ScanSpecial
394  *  scans code modules for the first named entrypoint
395  */
396 static
VLinkerScanSpecial(VLinker * self,const KDlset * libs,KSymTable * tbl,const KSymbol ** symp,const KSymbol * name,uint32_t type)397 rc_t CC VLinkerScanSpecial ( VLinker *self, const KDlset *libs,
398     KSymTable *tbl, const KSymbol **symp, const KSymbol *name, uint32_t type )
399 {
400     rc_t rc;
401 
402     /* convert schema fqn into legal C identifier */
403     char fqn [ 1024 ];
404     size_t sz = VLinkerMakeFQN ( fqn, sizeof fqn, name );
405     if ( sz >= sizeof fqn )
406         rc = RC ( rcVDB, rcMgr, rcResolving, rcName, rcExcessive );
407     else
408     {
409         /* ask loader to locate fqn in open library set */
410         KSymAddr *entrypoint;
411         rc = KDlsetSymbol ( libs, & entrypoint, fqn );
412         if ( rc == 0 )
413         {
414             /* allocate special function object */
415             LSpecial *special = malloc ( sizeof * special );
416             if ( special == NULL )
417                 rc = RC ( rcVDB, rcMgr, rcResolving, rcMemory, rcExhausted );
418             else
419             {
420                 special -> func = NULL;
421 
422                 /* give it the exact schema name */
423                 rc = KSymTableDupSymbol ( tbl,
424                     ( KSymbol** ) & special -> name, name, type, special );
425                 if ( rc == 0 )
426                 {
427                     /* finally, insert the symbol into our vector */
428                     rc = VectorAppend ( & self -> special, & special -> id, special );
429                     if ( rc == 0 )
430                     {
431                         /* done */
432                         special -> addr = entrypoint;
433                         * symp = special -> name;
434                         return 0;
435                     }
436 
437                     KSymTableRemoveSymbol ( tbl, special -> name );
438                 }
439 
440                 free ( special );
441             }
442 
443             KSymAddrRelease ( entrypoint );
444         }
445     }
446 
447     return rc;
448 }
449 
450 /* Find
451  *  find a named symbol
452  *
453  *  "func" [ OUT ] - return parameter for func pointer
454  *
455  *  "proto" [ IN ] - function prototype from schema
456  *  "min_version" [ IN ] - minimum version to accept
457  *
458  *  "external" [ OUT ] - tells whether factory is built-in or external
459  */
VLinkerFindFactory(VLinker * self,const KDlset * libs,VTransDesc * desc,const SFunction * proto,uint32_t min_version,bool * external)460 rc_t VLinkerFindFactory ( VLinker *self, const KDlset *libs,
461     VTransDesc *desc, const SFunction *proto, uint32_t min_version, bool *external )
462 {
463     KSymTable tbl;
464     rc_t rc = VLinkerEnterIntoScope ( self, & tbl );
465     if ( rc == 0 )
466     {
467         /* factory name if explicit */
468         const KSymbol *sym, *name = proto -> u . ext . fact;
469 
470         /* use simple function name as implicit factory name */
471         if ( name == NULL )
472             name = proto -> name;
473 
474         /* look for the symbol by name -
475            factory names do not support version overloading */
476         sym = KSymTableFindSymbol ( & tbl, name );
477 
478         /* if not found, search for best match */
479         if ( sym == NULL )
480         {
481             rc = VLinkerScanFactory ( self, libs, & tbl, & sym, name, proto -> version );
482             if ( rc != 0 && min_version != proto -> version )
483                 rc = VLinkerScanFactory ( self, libs, & tbl, & sym, name, min_version );
484         }
485 
486         /* otherwise ensure the found name is in fact a factory */
487         else if ( sym -> type != ltFactory )
488             rc = RC ( rcVDB, rcMgr, rcResolving, rcName, rcIncorrect );
489 
490         /* examine results */
491         if ( rc == 0 )
492         {
493             const LFactory *fact = sym -> u . obj;
494 
495             /* test for incompatible version */
496             if ( ( fact -> desc . itf_version >> 24 ) != ( min_version >> 24 ) )
497                 rc = RC ( rcVDB, rcMgr, rcResolving, rcInterface, rcIncorrect );
498             else if ( fact -> desc . itf_version < min_version )
499                 rc = RC ( rcVDB, rcMgr, rcResolving, rcInterface, rcIncorrect );
500             else
501             {
502                 * desc = fact -> desc;
503                 * external = fact -> external;
504             }
505         }
506 
507         KSymTableWhack ( & tbl );
508     }
509     return rc;
510 }
511 
512 static
VLinkerNameWhackSymbol(KSymbol * sym)513 void CC VLinkerNameWhackSymbol ( KSymbol *sym )
514 {
515     if ( sym != NULL )
516     {
517         VLinkerNameWhackSymbol ( sym -> dad );
518         free ( sym );
519     }
520 }
521 
522 static
VLinkerNameToSymbol(String * name)523 KSymbol *VLinkerNameToSymbol ( String *name )
524 {
525     KSymbol *sym, *dad = NULL;
526     const char *end = string_rchr ( name -> addr, name -> size, ':' );
527     if ( end != 0 )
528     {
529         String sub;
530         StringSubstr ( name, & sub, 0, string_len ( name -> addr, end - name -> addr ) );
531         dad = VLinkerNameToSymbol ( & sub );
532         if ( dad == NULL )
533             return NULL;
534 
535         BSTreeInit ( & dad -> u . scope );
536 
537         name -> addr += sub . size + 1;
538         name -> size -= sub . size + 1;
539         name -> len -= sub . len + 1;
540     }
541 
542     sym = malloc ( sizeof * sym );
543     if ( sym == NULL )
544     {
545         VLinkerNameWhackSymbol ( dad );
546         return NULL;
547     }
548 
549     sym -> u . obj = NULL;
550     sym -> dad = dad;
551     sym -> name = * name;
552     sym -> type = 0;
553 
554     if ( dad != NULL )
555         BSTreeInsert ( & dad -> u . scope, & sym -> n, KSymbolSort );
556 
557     return sym;
558 }
559 
VLinkerFindNamedFactory(VLinker * self,const KDlset * libs,VTransDesc * desc,const char * fact_name)560 rc_t VLinkerFindNamedFactory ( VLinker *self, const KDlset *libs,
561     VTransDesc *desc, const char *fact_name )
562 {
563     KSymTable tbl;
564     rc_t rc = VLinkerEnterIntoScope ( self, & tbl );
565     if ( rc == 0 )
566     {
567         KSymbol *name;
568         const KSymbol *sym;
569 
570         String str;
571         StringInitCString ( & str, fact_name );
572         name = VLinkerNameToSymbol ( & str );
573 
574         /* look for the symbol by name -
575            factory names do not support version overloading */
576         sym = KSymTableFindSymbol ( & tbl, name );
577 
578         /* if not found, search for best match */
579         if ( sym == NULL )
580             rc = VLinkerScanFactory ( self, libs, & tbl, & sym, name, 0 );
581 
582         /* otherwise ensure the found name is in fact a factory */
583         else if ( sym -> type != ltFactory )
584             rc = RC ( rcVDB, rcMgr, rcResolving, rcName, rcIncorrect );
585 
586         VLinkerNameWhackSymbol ( name );
587 
588         /* examine results */
589         if ( rc == 0 )
590         {
591             const LFactory *fact = sym -> u . obj;
592             * desc = fact -> desc;
593         }
594 
595         KSymTableWhack ( & tbl );
596     }
597     return rc;
598 }
599 
600 
VLinkerFindUntyped(VLinker * self,const KDlset * libs,VUntypedTableTest * func,const SFunction * proto)601 rc_t VLinkerFindUntyped ( VLinker *self, const KDlset *libs,
602     VUntypedTableTest *func, const SFunction *proto )
603 {
604     KSymTable tbl;
605     rc_t rc = VLinkerEnterIntoScope ( self, & tbl );
606     if ( rc == 0 )
607     {
608         /* untyped functions do not have factories */
609         const KSymbol *sym = KSymTableFindSymbol ( & tbl, proto -> name );
610 
611         /* if not found, search for it */
612         if ( sym == NULL )
613             rc = VLinkerScanSpecial ( self, libs, & tbl, & sym, proto -> name, ltUntyped );
614 
615         /* otherwise ensure the found name is in fact untyped */
616         else if ( sym -> type != ltUntyped )
617             rc = RC ( rcVDB, rcMgr, rcResolving, rcName, rcIncorrect );
618 
619         /* return results */
620         if ( rc == 0 )
621         {
622             const LSpecial *untyped = sym -> u . obj;
623             if ( untyped -> addr == NULL )
624                 * func = untyped -> func;
625             else
626                 KSymAddrAsFunc ( untyped -> addr, ( fptr_t* ) func );
627         }
628 
629         KSymTableWhack ( & tbl );
630     }
631     return rc;
632 }
633 
634 
635 /* ListExternalSchemaModules
636  */
VLinkerListExternalSchemaModules(const VLinker * self,struct KNamelist ** listp)637 rc_t VLinkerListExternalSchemaModules ( const VLinker *self, struct KNamelist **listp )
638 {
639     rc_t rc;
640 
641     assert ( listp != NULL );
642     if ( self == NULL )
643         rc = RC ( rcVDB, rcDylib, rcListing, rcSelf, rcNull );
644     else
645     {
646         KDlset *libs;
647         rc = VLinkerOpen ( self, & libs );
648         if ( rc == 0 )
649         {
650             rc = KDlsetList ( libs, listp );
651             KDlsetRelease ( libs );
652         }
653     }
654 
655     return rc;
656 }
657