1/*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License, Version 1.0 only
6 * (the "License").  You may not use this file except in compliance
7 * with the License.
8 *
9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10 * or http://www.opensolaris.org/os/licensing.
11 * See the License for the specific language governing permissions
12 * and limitations under the License.
13 *
14 * When distributing Covered Code, include this CDDL HEADER in each
15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16 * If applicable, add the following below this CDDL HEADER, with the
17 * fields enclosed by brackets "[]" replaced with your own identifying
18 * information: Portions Copyright [yyyy] [name of copyright owner]
19 *
20 * CDDL HEADER END
21 */
22/*
23 * Copyright 2003 Sun Microsystems, Inc.  All rights reserved.
24 * Use is subject to license terms.
25 */
26
27#include <stdio.h>
28#include <malloc.h>
29#include <stdlib.h>
30#include <errno.h>
31#include <string.h>
32#include "xlator.h"
33#include "util.h"
34#include "bucket.h"
35#include "errlog.h"
36
37/* Statics: */
38#define	TRUE	1
39#define	FALSE	0
40#define	NLISTS	50
41#define	NPAR	25
42
43static bucket_t **Buckethead;
44static int N_lists;
45
46static int Bc = -1; /* For iterators. */
47static bucket_t *Bp;
48
49static void start_new_list(const bucket_t *);
50static void grow_lists(void);
51static bucket_t *new_bucket(const char *, int);
52static void print_iface(const Interface *);
53static void new_hashmap(void);
54static int add_to_hashmap(const char *, const bucket_t *);
55static bucket_t *find_in_hashmap(const char *);
56/*
57 * initialization interfaces.
58 */
59
60/*
61 * create_lists -- initialize the bucket list and hash map.
62 */
63void
64create_lists(void)
65{
66
67	errlog(BEGIN, "create_lists() {");
68	new_hashmap();
69	if ((Buckethead = calloc(sizeof (bucket_t *), NLISTS)) == NULL) {
70		errlog(FATAL, "out of memory creating initial "
71			"list of versions");
72
73	}
74	N_lists = NLISTS;
75	errlog(END, "}");
76}
77
78
79/*
80 * data-loading interfaces -- adding buckets to lists and
81 *	interfaces to buckets.
82 */
83
84/*
85 * add_parent -- add a parent node. Returns TRUE or FALSE.
86 *
87 * 	if *version == NULL, then
88 * 		the bucket version (eg, SUNW_1.1) hasn't
89 * 		been parsed correctly.  Die.
90 * 	if *after == NULL, then this is the ``initial case'',
91 * 		where no predecessor (child) exists.  We start a new
92 * 		tree of buckets.
93 * 	if *after != NULL, we have the normal case, and
94 * 		add to an existing tree.
95 * 	if *after is not a version name found among the buckets,
96 * 		then something got misparsed or the versions file is
97 * 		malformed. Function will print problem and
98 * 		return 0 so caller can report location of error.
99 *      If either version or after is NULL, it's a programmer error.
100 */
101int
102add_parent(const char *version, const char *after, int weak)
103{
104	bucket_t *new, *child;
105
106	/* Sanity-check parameters. */
107	assert(version != NULL, "passed a null version to add_parent");
108	assert(after != NULL, "passed a null after to add_parent");
109	errlog(BEGIN, "add_parent(%s,%s,%d) {", version, after, weak);
110	if ((new = find_in_hashmap(version)) == NULL) {
111		/* We don't have one have one yet. */
112		new = new_bucket(version, weak);
113	}
114	new->b_weak = weak;
115	if (*after == '\0') {
116		/*
117		 * This is the ``initial case'', where no
118		 * child exists.  We start a new tree of buckets.
119		 */
120		(void) add_to_hashmap(version, new);
121		start_new_list(new);
122	} else {
123		if ((child = find_in_hashmap(after)) == NULL) {
124			/*
125			 * The version in the spec doesn't appear in the
126			 * versions file.  One or the other is lying.
127			 */
128			errlog(WARNING, "set file: can't find version \"%s\","
129			    "therefor can't add it's parent, \"%s\"",
130			    after, version);
131			errlog(END, "} /* add_parent */");
132			return (FALSE);
133		}
134		(void) add_to_hashmap(version, new);
135		child->b_parent = new;
136	}
137	errlog(END, "} /* add_parent */");
138	return (TRUE);
139}
140
141/*
142 * add_uncle -- adds an uncle node
143 */
144int
145add_uncle(const char *version, const char *after, int weak)
146{
147	bucket_t *new, *child;
148	struct bucketlist *uncle;
149
150	/* Sanity-check parameters. */
151	assert(version != NULL, "passed a null version to add_uncle");
152	assert(after != NULL, "passed a null after to add_uncle");
153	errlog(BEGIN, "add_uncle(%s,%s,%d) {", version, after, weak);
154	if ((new = find_in_hashmap(version)) == NULL) {
155		/* We don't have one have one yet. */
156		new = new_bucket(version, weak);
157	}
158	if (*after == '\0') {
159		/*
160		 * This is the ``initial case'', where no
161		 * child exists.  We start a new tree of buckets.
162		 */
163		(void) add_to_hashmap(version, new);
164		start_new_list(new);
165	} else {
166		if ((child = find_in_hashmap(after)) == NULL) {
167			/*
168			 * The version in the spec doesn't appear in the
169			 * versions file.  One or the other is lying.
170			 */
171			errlog(WARNING, "set file: can't find version \"%s\","
172			    "therefor can't add it's uncle, \"%s\"",
173			    after, version);
174			errlog(END, "}");
175			return (FALSE);
176		}
177		(void) add_to_hashmap(version, new);
178		uncle =	malloc(sizeof (struct bucketlist));
179		uncle->bl_next = child->b_uncles;
180		uncle->bl_bucket = new;
181		child->b_uncles = uncle;
182	}
183	errlog(END, "}");
184	return (TRUE);
185}
186
187/*
188 * set_weak -- set a version to be a weak version
189 */
190void
191set_weak(const char *version, int weak)
192{
193	bucket_t *v;
194	if ((v = find_in_hashmap(version)) == NULL) {
195		/* We don't have one have one yet. */
196		errlog(ERROR|FATAL, "Unable to set weak. Version not found");
197	}
198	v->b_weak = weak;
199}
200
201/*
202 * add_by_name -- look up bucket and add an interface to it.
203 *      Returns 0 for success or an errno.h value for failure.
204 *
205 * 	if *version is not among the buckets, then the
206 * 		version in the spec doesn't appear in the
207 * 		set file.  One or the other is lying. Function will
208 * 		report the problem and return ENOENT
209 * 		so the front end can report and exit (or
210 * 		continue if it wants).
211 * 	if interface ore version is NULL, then
212 * 		the begin line code should
213 * 		have caught it long before, to avoid passing
214 * 		a null pointer around. Die.
215 *
216 */
217#define	ADD_EQUALS(str)	if (strchr(str, '=') == NULL) (void) strcat(str, " =")
218
219int
220add_by_name(const char *version, const Interface *interface)
221{
222	bucket_t *b;
223	char buffer[1024];
224
225	assert(version != NULL, "passed a null version to add_by_name");
226	assert(interface != NULL, "passed a null interface to add_by_name");
227
228	errlog(BEGIN, "add_by_name(%s,", version);
229	print_iface(interface);
230	errlog(TRACING, ");");
231
232	/* Sanity-check the parameters. */
233	if ((b = find_in_hashmap(version)) == NULL) {
234		/*
235		 * The version in the spec doesn't appear in the
236		 * versions file. Alas, this isn't an error.  It can
237		 * happen whenever doing just sparc, just i386
238		 * or the like.
239		 */
240		errlog(END, "}");
241		return (ENOENT);
242	}
243	/*
244	 * Add to bucket.
245	 */
246	(void) snprintf(buffer, sizeof (buffer), "%s", interface->IF_name);
247
248	if (interface->IF_filter && interface->IF_auxiliary) {
249		errlog(FATAL, "Error: cannot set both FILTER and AUXILIARY "
250		    "for an interface: %s", interface->IF_name);
251	}
252
253	if (interface->IF_filter) {
254		ADD_EQUALS(buffer);
255		if (interface->IF_type == FUNCTION) {
256			(void) strcat(buffer, " FUNCTION");
257		} else if (interface->IF_type == DATA) {
258			(void) strcat(buffer, " DATA");
259		}
260		(void) strcat(buffer, " FILTER ");
261		(void) strcat(buffer, interface->IF_filter);
262	} else if (interface->IF_auxiliary) {
263		ADD_EQUALS(buffer);
264		(void) strcat(buffer, " AUXILIARY ");
265		(void) strcat(buffer, interface->IF_auxiliary);
266	} else if (IsFilterLib) {
267		/*
268		 * For DATA types it is currently assumed that they are
269		 * handled via a minimal C file, e.g. 'data.c', in the
270		 * library's build.  Hence, we do not append '= DATA' here.
271		 */
272		if (interface->IF_type == FUNCTION) {
273			ADD_EQUALS(buffer);
274			(void) strcat(buffer, " FUNCTION");
275		}
276	}
277
278	switch (interface->IF_binding) {
279	case DIRECT:
280		ADD_EQUALS(buffer);
281		(void) strcat(buffer, " DIRECT");
282		break;
283	case NODIRECT:
284		ADD_EQUALS(buffer);
285		(void) strcat(buffer, " NODIRECT");
286		break;
287	}
288
289	if (interface->IF_binding == PROTECTED) {
290		/* Assign in case of realloc. */
291		b->b_protected_table =
292		    add_to_stringtable(b->b_protected_table, buffer);
293		b->b_has_protecteds = 1;
294		errlog(VERBOSE, "set has_protecteds on bucket 0x%p", b);
295	} else {
296		/* Assign in case of realloc. */
297		b->b_global_table = add_to_stringtable(b->b_global_table,
298			buffer);
299	}
300	errlog(END, "}");
301	return (0);
302}
303
304
305/*
306 * Processing interfaces
307 */
308
309/*
310 * sort_buckets -- sort the interfaces within the buckets into
311 *      alphabetical order.
312 */
313void
314sort_buckets(void)
315{
316	bucket_t *l, *b;
317
318	errlog(BEGIN, "sort_buckets() {");
319	for (l = first_list(); l != NULL; l = next_list()) {
320		errlog(VERBOSE, "l-bucket: %s", l->b_name);
321		for (b = first_from_list(l); b != NULL; b = next_from_list()) {
322			errlog(VERBOSE, "   b-bkt: %s", b->b_name);
323			sort_stringtable(b->b_global_table);
324			sort_stringtable(b->b_protected_table);
325			if (b->b_uncles) {
326
327				if (b->b_uncles->bl_bucket) {
328		sort_stringtable(b->b_uncles->bl_bucket->b_global_table);
329		sort_stringtable(b->b_uncles->bl_bucket->b_protected_table);
330				}
331			}
332		}
333	}
334	errlog(END, "}");
335}
336
337
338/*
339 * add_local -- set the local flag on the logically first bucket.
340 *     This decision may belong in the caller, as it is about
341 *     mapfiles, not inherent ordering or bucket contents...
342 */
343void
344add_local(void)
345{
346	bucket_t *b, *list;
347	int	done = 0;
348
349	errlog(BEGIN, "add_local() {");
350	/* Iterate across lists of buckets */
351	for (list = first_list(); list != NULL; list = next_list()) {
352		/* Traverse the list found. */
353		for (b = list; b != NULL; b = b->b_parent) {
354			if (b->b_weak != 1) {
355				/* We've found the first non-weak. */
356				b->b_has_locals = done = 1;
357				errlog(VERBOSE,
358				    "set has_locals on bucket 0x%p", b);
359				break;
360			}
361		}
362		if (b != NULL && b->b_has_locals == 1)
363			break;
364	}
365	if (done == 0) {
366		errlog(WARNING, "has_locals never set");
367	}
368	errlog(END, "}");
369}
370
371
372/*
373 * Output interfaces, mostly iterators
374 */
375
376
377/*
378 * parents_of -- return a list of all parents.
379 */
380char **
381parents_of(const bucket_t *start)
382{
383	static char *a[NPAR] = {NULL};
384	const bucket_t *b = start;
385	char **p = &a[0];
386
387	assert(start != NULL, "passed a null start to parents_of");
388	errlog(BEGIN, "parents_of() {");
389	a[0] = '\0';
390
391	/* Go to parent, print it and all its uncle. */
392	if (b->b_parent == NULL) {
393		errlog(TRACING, "returning empty string");
394		errlog(END, "}");
395		return (a);
396	}
397	b = b->b_parent;
398	*p++ = b->b_name;
399	*p = '\0';
400
401	assert(p < &a[NPAR], "p fell off the end of a in parents_of");
402	errlog(END, "}");
403	return (a);
404}
405
406/*
407 * first, next_from_bucket --iterators for bucket contents. Serially
408 *      reusable only.
409 */
410int Ic = -1;
411
412/*
413 * debugging interfaces
414 */
415void
416print_bucket(const bucket_t *b)
417{
418
419	errlog(TRACING, "bucket_t at 0x%p {", (void *)b);
420	errlog(TRACING, "    char   *b_name = \"%s\";", b->b_name);
421	errlog(TRACING, "    struct bucket_t *b_parent = 0x%p;",
422		(void *)b->b_parent);
423	errlog(TRACING, "    struct bucketlist *b_uncles = 0x%p;",
424		(void *)b->b_uncles);
425	errlog(TRACING, "    struct bucket_t *b_thread = 0x%p;",
426		(void *)b->b_thread);
427	errlog(TRACING, "    int	b_has_locals = %d;",
428		b->b_has_locals);
429	errlog(TRACING, "    int	b_has_protecteds = %d;",
430		b->b_has_protecteds);
431	errlog(TRACING, "    int        b_was_printed = %d;",
432		b->b_was_printed);
433	errlog(TRACING, "    int        b_weak = %d;",
434		b->b_weak);
435	errlog(TRACING, "    table_t  *b_global_table = 0x%p;",
436		(void *)b->b_global_table);
437	errlog(TRACING, "    table_t  *b_protected_table = 0x%p;",
438		(void *)b->b_protected_table);
439	errlog(TRACING, "}");
440}
441
442void
443print_all_buckets(void)
444{
445	bucket_t *l, *b;
446	int i = 0, j = 0;
447	char **p;
448
449	for (i = 0, l = first_list(); l != NULL; l = next_list(), ++i) {
450		errlog(TRACING, "list %d", i);
451		for (j = 0, b = first_from_list(l);
452		    b != NULL; b = next_from_list(), ++j) {
453			errlog(TRACING, "bucket %d", j);
454			print_bucket(b);
455			errlog(TRACING, "global interfaces = {");
456			print_stringtable(b->b_global_table);
457			errlog(TRACING, "}");
458			errlog(TRACING, "protected interfaces = {");
459			print_stringtable(b->b_protected_table);
460			errlog(TRACING, "}");
461
462			for (p = parents_of(b); p != NULL && *p != NULL; ++p) {
463				errlog(TRACING, " %s", *p);
464			}
465			errlog(TRACING, ";");
466
467			if (b->b_uncles) {
468				errlog(TRACING, " uncle bucket %d.1", j);
469				print_bucket(b->b_uncles->bl_bucket);
470				errlog(TRACING, "global interfaces = {");
471				print_stringtable(
472				    b->b_uncles->bl_bucket->b_global_table);
473				errlog(TRACING, "}");
474				errlog(TRACING, "protected interfaces = {");
475				print_stringtable(
476				    b->b_uncles->bl_bucket->b_protected_table);
477				errlog(TRACING, "}");
478			}
479		}
480	}
481}
482
483
484/*
485 * lower-level functions, not visible outside the file.
486 */
487
488/*
489 * new_bucket -- create a bucket for a given version. Must not fail.
490 */
491static bucket_t *
492new_bucket(const char *name, int weak)
493{
494	bucket_t *b;
495
496	if ((b = (bucket_t *)calloc(1, sizeof (bucket_t))) == NULL) {
497		errlog(FATAL, "out of memory creating a bucket "
498			"to store interfaces in");
499	}
500	if ((b->b_name = strdup(name)) == NULL) {
501		errlog(FATAL, "out of memory storing an interface "
502			"in a version bucket");
503	}
504	b->b_uncles = NULL;
505	b->b_global_table = create_stringtable(TABLE_INITIAL);
506	b->b_protected_table = create_stringtable(TABLE_INITIAL);
507	b->b_weak = weak;
508	return (b);
509}
510
511
512/*
513 * start_new_list -- start a list of buckets.
514 */
515static void
516start_new_list(const bucket_t *b)
517{
518	int i;
519
520	errlog(BEGIN, "start_new_list() {");
521	assert(Buckethead != NULL, "Buckethead null in start_new_list");
522	for (i = 0; Buckethead[i] != NULL && i < N_lists; ++i)
523		continue;
524	if (i >= N_lists) {
525		grow_lists();
526	}
527	Buckethead[i] = (bucket_t *)b;
528	errlog(END, "}");
529}
530
531/*
532 * grow_list -- make more lists.  This should never occur...
533 */
534static void
535grow_lists(void)
536{
537	int i = N_lists;
538
539	errlog(BEGIN, "grow_lists() {");
540	errlog(WARNING, "Warning: more than %d version lists "
541	    "required (< %d is normal). Check sets file "
542	    "to see why so many lines appear.",
543	    N_lists, NLISTS);
544
545	N_lists *= 2;
546	if ((Buckethead = realloc(Buckethead, sizeof (bucket_t *) * N_lists))
547		== NULL) {
548		errlog(FATAL, "out of memory growing list of "
549			"version buckets");
550	}
551	for (; i < N_lists; ++i) {
552		Buckethead[i] = NULL;
553	}
554}
555
556/*
557 * delete_lists -- clean up afterwards.
558 */
559void
560delete_lists(void)
561{
562	N_lists = 0;
563	free(Buckethead);
564	Buckethead = 0;
565}
566
567/*
568 * first_list, next_list -- an iterator for lists themselves.  Serially
569 *      reusable only.
570 */
571bucket_t *
572first_list(void)
573{
574	Bc = 0;
575	return (Buckethead[Bc]);
576}
577
578bucket_t *
579next_list(void)
580{
581	return (Buckethead[++Bc]);
582}
583
584
585/*
586 * first, next, last_from_list -- iterators for individual lists. Serially
587 *      reusable only.
588 */
589bucket_t *
590first_from_list(const bucket_t *l)
591{
592	return (Bp = (bucket_t *)l);
593}
594
595bucket_t *
596next_from_list(void)
597{
598	return (Bp = Bp->b_parent);
599}
600
601
602
603/*
604 * Iface print utility
605 */
606static void
607print_iface(const Interface * p)
608{
609
610	errlog(TRACING, "%s (%s, %s, %s %d)", p->IF_name,
611		(p->IF_type == FUNCTION) ? "function" :
612		(p->IF_type == DATA) ? "data" : "unknown type",
613		(p->IF_version) ? p->IF_version : "unknown version",
614		(p->IF_class) ? p->IF_class : "unknown class",
615		p->IF_binding);
616}
617
618
619
620#define	HASHMAPSIZE	100
621#define	ERR	(-1)
622
623static struct {
624	hashmap_t *hh_map;
625	int hh_map_size;
626	int hh_mapC;
627	hashmap_t *hh_last;
628} Hashhead = {
629	NULL, -1, -1, NULL
630};
631
632static int checksum(const char *);
633static void print_hashmap(const hashmap_t *);
634
635/*
636 * new_hashmap -- create the hash.
637 */
638static void
639new_hashmap(void)
640{
641
642	errlog(BEGIN, "new_hashmap() {");
643	if ((Hashhead.hh_map = calloc(sizeof (hashmap_t), HASHMAPSIZE))
644	    == NULL) {
645		errlog(FATAL, "out of memory creating a hash-map of "
646			"the versions");
647	}
648	Hashhead.hh_mapC = 0;
649	errlog(END, "}");
650}
651
652/*
653 * add_to_hashmap -- add a bucket to the map.  This is strictly for
654 *	use by add_parent()/add_uncle().
655 */
656static int
657add_to_hashmap(const char *version_name, const bucket_t *bucket)
658{
659	hashmap_t *p;
660
661	assert(Hashhead.hh_map != NULL,
662	    "Hashead.map was null in add_to_hashmap");
663	assert(Hashhead.hh_mapC < HASHMAPSIZE,
664	    "mapC too big in add_to_hashmap");
665	errlog(BEGIN, "add_to_hashmap(%s, %s) {", version_name, bucket);
666	if (find_in_hashmap(version_name) != NULL) {
667		/* Seen for the second time. TBD... */
668		errlog(END, "} /* add_to_hashmap */");
669		return (ERR);
670	}
671	p = &Hashhead.hh_map[Hashhead.hh_mapC++];
672	if ((p->h_version_name = strdup(version_name)) == NULL) {
673		errlog(FATAL, "out of memory storing a version name");
674
675	}
676	p->h_bucket = (bucket_t *)bucket;
677	p->h_hash = checksum(version_name);
678	Hashhead.hh_last = p;
679	print_hashmap(p);
680	errlog(END, "} /* add_to_hashmap */");
681	return (0);
682}
683
684
685/*
686 * find_in_hashmap -- find a bucket by name.  Strictly for use by addByName().
687 */
688static bucket_t *
689find_in_hashmap(const char *version_name)
690{
691	hashmap_t *current;
692	int hash = checksum(version_name);
693
694	assert(Hashhead.hh_map != NULL,
695		"Hashhead.hh_map was null in find_in_hashmap");
696	errlog(BEGIN, "find_in_hashmap(%s) {", version_name);
697	if (Hashhead.hh_last != NULL && Hashhead.hh_last->h_hash == hash &&
698	    strcmp(Hashhead.hh_last->h_version_name, version_name) == 0) {
699		errlog(END, "}");
700		return (Hashhead.hh_last->h_bucket);
701	}
702	for (current = Hashhead.hh_map;
703		current->h_version_name != NULL; ++current) {
704		if (current->h_hash == hash &&
705			strcmp(current->h_version_name, version_name) == 0) {
706			/* Found it */
707			Hashhead.hh_last = current;
708			errlog(END, "}");
709			return (current->h_bucket);
710		}
711	}
712	/* Doesn't exist, meaning version name is bogus. */
713	errlog(END, "}");
714	return (NULL);
715}
716
717/*
718 * checksum -- from sum(1), algorithm 1.
719 */
720static int
721checksum(const char *p)
722{
723	int sum;
724
725	for (sum = 0; *p != '\0'; ++p) {
726		if (sum & 01)
727			sum = (sum >> 1) + 0x8000;
728		else
729			sum >>= 1;
730		sum += *p;
731		sum &= 0xFFFF;
732	}
733	return (sum);
734}
735
736static void
737print_hashmap(const hashmap_t *h)
738{
739	errlog(VERBOSE, "struct hashmap_t at 0x4.4x {", h);
740	errlog(VERBOSE, "    int    h_hash = %d;", h->h_hash);
741	errlog(VERBOSE, "    char   *h_version_name = \"%s\";",
742		h->h_version_name);
743	errlog(VERBOSE, "    bucket_t *h_bucket = 0x%p;;",
744		(void *) h->h_bucket);
745	errlog(VERBOSE, "}");
746}
747