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