resolve.c revision 1.93
1/*	$OpenBSD: resolve.c,v 1.93 2019/10/03 06:10:54 guenther Exp $ */
2
3/*
4 * Copyright (c) 1998 Per Fogelstrom, Opsycon AB
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 * 1. Redistributions of source code must retain the above copyright
10 *    notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 *    notice, this list of conditions and the following disclaimer in the
13 *    documentation and/or other materials provided with the distribution.
14 *
15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
16 * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
17 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
19 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25 * SUCH DAMAGE.
26 *
27 */
28
29#define _DYN_LOADER
30
31#include <sys/types.h>
32
33#include <limits.h>
34#include <nlist.h>
35#include <link.h>
36#include "syscall.h"
37#include "archdep.h"
38#include "path.h"
39#include "resolve.h"
40
41/* substitution types */
42typedef enum {
43	SUBST_UNKNOWN, SUBST_ORIGIN, SUBST_OSNAME, SUBST_OSREL, SUBST_PLATFORM
44} SUBST_TYPES;
45
46struct symlookup {
47	const char		*sl_name;
48	struct sym_res		sl_out;
49	struct sym_res		sl_weak_out;
50	unsigned long		sl_elf_hash;
51	uint32_t		sl_gnu_hash;
52	int			sl_flags;
53};
54
55elf_object_t *_dl_objects;
56int object_count;
57static elf_object_t *_dl_last_object;
58elf_object_t *_dl_loading_object;
59
60/*
61 * Add a new dynamic object to the object list.
62 */
63void
64_dl_add_object(elf_object_t *object)
65{
66	/*
67	 * If a .so is marked nodelete, then the entire load group that it's
68	 * in needs to be kept around forever, so add a reference there.
69	 * XXX It would be better if we tracked inter-object dependencies
70	 * from relocations and didn't leave dangling pointers when a load
71	 * group was partially unloaded.  That would render this unnecessary.
72	 */
73	if (object->obj_flags & DF_1_NODELETE &&
74	    (object->load_object->status & STAT_NODELETE) == 0) {
75		DL_DEB(("objname %s is nodelete\n", object->load_name));
76		object->load_object->opencount++;
77		object->load_object->status |= STAT_NODELETE;
78	}
79
80	/*
81	 * if this is a new object, prev will be NULL
82	 * != NULL if an object already in the list
83	 * prev == NULL for the first item in the list, but that will
84	 * be the executable.
85	 */
86	if (object->prev != NULL)
87		return;
88
89	if (_dl_objects == NULL) {			/* First object ? */
90		_dl_last_object = _dl_objects = object;
91		object_count = 2;			/* count ld.so early */
92	} else {
93		_dl_last_object->next = object;
94		object->prev = _dl_last_object;
95		_dl_last_object = object;
96		if (object->obj_type != OBJTYPE_LDR)	/* see above */
97			object_count++;
98	}
99}
100
101/*
102 * Identify substitution sequence name.
103 */
104static int
105_dl_subst_name(const char *name, size_t siz) {
106	switch (siz) {
107	case 5:
108		if (_dl_strncmp(name, "OSREL", 5) == 0)
109			return SUBST_OSREL;
110		break;
111	case 6:
112		if (_dl_strncmp(name, "ORIGIN", 6) == 0)
113			return SUBST_ORIGIN;
114		if (_dl_strncmp(name, "OSNAME", 6) == 0)
115			return SUBST_OSNAME;
116		break;
117	case 8:
118		if (_dl_strncmp(name, "PLATFORM", 8) == 0)
119			return SUBST_PLATFORM;
120		break;
121	}
122
123	return (SUBST_UNKNOWN);
124}
125
126/*
127 * Perform $ORIGIN substitutions on path
128 */
129static void
130_dl_origin_subst_path(elf_object_t *object, const char *origin_path,
131    char **path)
132{
133	char tmp_path[PATH_MAX];
134	char *new_path, *tp;
135	const char *pp, *name, *value;
136	static struct utsname uts;
137	size_t value_len;
138	int skip_brace;
139
140	if (uts.sysname[0] == '\0') {
141		if (_dl_uname(&uts) != 0)
142			return;
143	}
144
145	tp = tmp_path;
146	pp = *path;
147
148	while (*pp != '\0' && (tp - tmp_path) < sizeof(tmp_path)) {
149
150		/* copy over chars up to but not including $ */
151		while (*pp != '\0' && *pp != '$' &&
152		    (tp - tmp_path) < sizeof(tmp_path))
153			*tp++ = *pp++;
154
155		/* substitution sequence detected */
156		if (*pp == '$' && (tp - tmp_path) < sizeof(tmp_path)) {
157			pp++;
158
159			if ((skip_brace = (*pp == '{')))
160				pp++;
161
162			/* skip over name */
163			name = pp;
164			while (_dl_isalnum((unsigned char)*pp) || *pp == '_')
165				pp++;
166
167			switch (_dl_subst_name(name, pp - name)) {
168			case SUBST_ORIGIN:
169				value = origin_path;
170				break;
171			case SUBST_OSNAME:
172				value = uts.sysname;
173				break;
174			case SUBST_OSREL:
175				value = uts.release;
176				break;
177			case SUBST_PLATFORM:
178				value = uts.machine;
179				break;
180			default:
181				value = "";
182			}
183
184			value_len = _dl_strlen(value);
185			if (value_len >= sizeof(tmp_path) - (tp - tmp_path))
186				return;
187
188			_dl_bcopy(value, tp, value_len);
189			tp += value_len;
190
191			if (skip_brace && *pp == '}')
192				pp++;
193		}
194	}
195
196	/* no substitution made if result exceeds sizeof(tmp_path) */
197	if (tp - tmp_path >= sizeof(tmp_path))
198		return;
199
200	/* NULL terminate tmp_path */
201	*tp = '\0';
202
203	if (_dl_strcmp(tmp_path, *path) == 0)
204		return;
205
206	new_path = _dl_strdup(tmp_path);
207	if (new_path == NULL)
208		return;
209
210	DL_DEB(("orig_path %s\n", *path));
211	DL_DEB(("new_path  %s\n", new_path));
212
213	_dl_free(*path);
214	*path = new_path;
215}
216
217/*
218 * Determine origin_path from object load_name. The origin_path argument
219 * must refer to a buffer capable of storing at least PATH_MAX characters.
220 * Returns 0 on success.
221 */
222static int
223_dl_origin_path(elf_object_t *object, char *origin_path)
224{
225	const char *dirname_path = _dl_dirname(object->load_name);
226
227	if (dirname_path == NULL)
228		return -1;
229
230	/* syscall in ld.so returns 0/-errno, where libc returns char* */
231	if (_dl___realpath(dirname_path, origin_path) < 0)
232		return -1;
233
234	return 0;
235}
236
237/*
238 * Perform $ORIGIN substitutions on runpath and rpath
239 */
240static void
241_dl_origin_subst(elf_object_t *object)
242{
243	char origin_path[PATH_MAX];
244	char **pp;
245
246	if (_dl_origin_path(object, origin_path) != 0)
247		return;
248
249	/* perform path substitutions on each segment of runpath and rpath */
250	if (object->runpath != NULL) {
251		for (pp = object->runpath; *pp != NULL; pp++)
252			_dl_origin_subst_path(object, origin_path, pp);
253	}
254	if (object->rpath != NULL) {
255		for (pp = object->rpath; *pp != NULL; pp++)
256			_dl_origin_subst_path(object, origin_path, pp);
257	}
258}
259
260/*
261 * Initialize a new dynamic object.
262 */
263elf_object_t *
264_dl_finalize_object(const char *objname, Elf_Dyn *dynp, Elf_Phdr *phdrp,
265    int phdrc, const int objtype, const long lbase, const long obase)
266{
267	elf_object_t *object;
268	Elf_Addr gnu_hash = 0;
269
270#if 0
271	_dl_printf("objname [%s], dynp %p, objtype %x lbase %lx, obase %lx\n",
272	    objname, dynp, objtype, lbase, obase);
273#endif
274	object = _dl_calloc(1, sizeof(elf_object_t));
275	if (object == NULL)
276		_dl_oom();
277	object->prev = object->next = NULL;
278
279	object->load_dyn = dynp;
280	while (dynp->d_tag != DT_NULL) {
281		if (dynp->d_tag < DT_NUM)
282			object->Dyn.info[dynp->d_tag] = dynp->d_un.d_val;
283		else if (dynp->d_tag >= DT_LOPROC &&
284		    dynp->d_tag < DT_LOPROC + DT_PROCNUM)
285			object->Dyn.info[dynp->d_tag + DT_NUM - DT_LOPROC] =
286			    dynp->d_un.d_val;
287		if (dynp->d_tag == DT_TEXTREL)
288			object->dyn.textrel = 1;
289		if (dynp->d_tag == DT_SYMBOLIC)
290			object->dyn.symbolic = 1;
291		if (dynp->d_tag == DT_BIND_NOW)
292			object->obj_flags |= DF_1_NOW;
293		if (dynp->d_tag == DT_FLAGS_1)
294			object->obj_flags |= dynp->d_un.d_val;
295		if (dynp->d_tag == DT_FLAGS) {
296			object->dyn.flags |= dynp->d_un.d_val;
297			if (dynp->d_un.d_val & DF_SYMBOLIC)
298				object->dyn.symbolic = 1;
299			if (dynp->d_un.d_val & DF_TEXTREL)
300				object->dyn.textrel = 1;
301			if (dynp->d_un.d_val & DF_ORIGIN)
302				object->obj_flags |= DF_1_ORIGIN;
303			if (dynp->d_un.d_val & DF_BIND_NOW)
304				object->obj_flags |= DF_1_NOW;
305		}
306		if (dynp->d_tag == DT_RELACOUNT)
307			object->relacount = dynp->d_un.d_val;
308		if (dynp->d_tag == DT_RELCOUNT)
309			object->relcount = dynp->d_un.d_val;
310		if (dynp->d_tag == DT_GNU_HASH)
311			gnu_hash = dynp->d_un.d_val;
312		dynp++;
313	}
314	DL_DEB((" flags %s = 0x%x\n", objname, object->obj_flags ));
315	object->obj_type = objtype;
316
317	if (_dl_loading_object == NULL) {
318		/*
319		 * no loading object, object is the loading object,
320		 * as it is either executable, or dlopened()
321		 */
322		_dl_loading_object = object;
323	}
324
325	if ((object->obj_flags & DF_1_NOOPEN) != 0 &&
326	    _dl_loading_object->obj_type == OBJTYPE_DLO &&
327	    !_dl_traceld) {
328		_dl_free(object);
329		_dl_errno = DL_CANT_LOAD_OBJ;
330		return(NULL);
331	}
332
333	/*
334	 *  Now relocate all pointer to dynamic info, but only
335	 *  the ones which have pointer values.
336	 */
337	if (object->Dyn.info[DT_PLTGOT])
338		object->Dyn.info[DT_PLTGOT] += obase;
339	if (object->Dyn.info[DT_STRTAB])
340		object->Dyn.info[DT_STRTAB] += obase;
341	if (object->Dyn.info[DT_SYMTAB])
342		object->Dyn.info[DT_SYMTAB] += obase;
343	if (object->Dyn.info[DT_RELA])
344		object->Dyn.info[DT_RELA] += obase;
345	if (object->Dyn.info[DT_SONAME])
346		object->Dyn.info[DT_SONAME] += object->Dyn.info[DT_STRTAB];
347	if (object->Dyn.info[DT_RPATH])
348		object->Dyn.info[DT_RPATH] += object->Dyn.info[DT_STRTAB];
349	if (object->Dyn.info[DT_RUNPATH])
350		object->Dyn.info[DT_RUNPATH] += object->Dyn.info[DT_STRTAB];
351	if (object->Dyn.info[DT_REL])
352		object->Dyn.info[DT_REL] += obase;
353	if (object->Dyn.info[DT_INIT])
354		object->Dyn.info[DT_INIT] += obase;
355	if (object->Dyn.info[DT_FINI])
356		object->Dyn.info[DT_FINI] += obase;
357	if (object->Dyn.info[DT_JMPREL])
358		object->Dyn.info[DT_JMPREL] += obase;
359	if (object->Dyn.info[DT_INIT_ARRAY])
360		object->Dyn.info[DT_INIT_ARRAY] += obase;
361	if (object->Dyn.info[DT_FINI_ARRAY])
362		object->Dyn.info[DT_FINI_ARRAY] += obase;
363	if (object->Dyn.info[DT_PREINIT_ARRAY])
364		object->Dyn.info[DT_PREINIT_ARRAY] += obase;
365
366	if (gnu_hash) {
367		Elf_Word *hashtab = (Elf_Word *)(gnu_hash + obase);
368		Elf_Word nbuckets = hashtab[0];
369		Elf_Word nmaskwords = hashtab[2];
370
371		/* validity check */
372		if (nbuckets > 0 && (nmaskwords & (nmaskwords - 1)) == 0) {
373			Elf_Word symndx = hashtab[1];
374			int bloom_size32 = (ELFSIZE / 32) * nmaskwords;
375
376			object->nbuckets = nbuckets;
377			object->symndx_gnu = symndx;
378			object->mask_bm_gnu = nmaskwords - 1;
379			object->shift2_gnu = hashtab[3];
380			object->bloom_gnu = (Elf_Addr *)(hashtab + 4);
381			object->buckets_gnu = hashtab + 4 + bloom_size32;
382			object->chains_gnu = object->buckets_gnu + nbuckets
383			    - symndx;
384
385			/*
386			 * If the ELF hash is present, get the total symbol
387			 * count ("nchains") from there.  Otherwise, count
388			 * the entries in the GNU hash chain.
389			 */
390			if (object->Dyn.info[DT_HASH] == 0) {
391				Elf_Word n;
392
393				for (n = 0; n < nbuckets; n++) {
394					Elf_Word bkt = object->buckets_gnu[n];
395					const Elf_Word *hashval;
396					if (bkt == 0)
397						continue;
398					hashval = &object->chains_gnu[bkt];
399					do {
400						symndx++;
401					} while ((*hashval++ & 1U) == 0);
402				}
403				object->nchains = symndx;
404			}
405			object->status |= STAT_GNU_HASH;
406		}
407	}
408	if (object->Dyn.info[DT_HASH] != 0) {
409		Elf_Hash_Word *hashtab =
410		    (Elf_Hash_Word *)(object->Dyn.info[DT_HASH] + obase);
411
412		object->nchains = hashtab[1];
413		if (object->nbuckets == 0) {
414			object->nbuckets = hashtab[0];
415			object->buckets_elf = hashtab + 2;
416			object->chains_elf = object->buckets_elf +
417			    object->nbuckets;
418		}
419	}
420
421	object->phdrp = phdrp;
422	object->phdrc = phdrc;
423	object->load_base = lbase;
424	object->obj_base = obase;
425	object->load_name = _dl_strdup(objname);
426	if (object->load_name == NULL)
427		_dl_oom();
428	object->load_object = _dl_loading_object;
429	if (object->load_object == object)
430		DL_DEB(("head %s\n", object->load_name));
431	DL_DEB(("obj %s has %s as head\n", object->load_name,
432	    _dl_loading_object->load_name ));
433	object->refcount = 0;
434	TAILQ_INIT(&object->child_list);
435	object->opencount = 0;	/* # dlopen() & exe */
436	object->grprefcount = 0;
437	/* default dev, inode for dlopen-able objects. */
438	object->dev = 0;
439	object->inode = 0;
440	object->grpsym_gen = 0;
441	TAILQ_INIT(&object->grpref_list);
442
443	if (object->dyn.runpath)
444		object->runpath = _dl_split_path(object->dyn.runpath);
445	/*
446	 * DT_RPATH is ignored if DT_RUNPATH is present...except in
447	 * the exe, whose DT_RPATH is a fallback for libs that don't
448	 * use DT_RUNPATH
449	 */
450	if (object->dyn.rpath && (object->runpath == NULL ||
451	    objtype == OBJTYPE_EXE))
452		object->rpath = _dl_split_path(object->dyn.rpath);
453	if ((object->obj_flags & DF_1_ORIGIN) && _dl_trust)
454		_dl_origin_subst(object);
455
456	_dl_trace_object_setup(object);
457
458	return (object);
459}
460
461static void
462_dl_tailq_free(struct dep_node *n)
463{
464	struct dep_node *next;
465
466	while (n != NULL) {
467		next = TAILQ_NEXT(n, next_sib);
468		_dl_free(n);
469		n = next;
470	}
471}
472
473static elf_object_t *free_objects;
474
475void
476_dl_cleanup_objects()
477{
478	elf_object_t *nobj, *head;
479	struct dep_node *n, *next;
480
481	n = TAILQ_FIRST(&_dlopened_child_list);
482	while (n != NULL) {
483		next = TAILQ_NEXT(n, next_sib);
484		if (OBJECT_DLREF_CNT(n->data) == 0) {
485			TAILQ_REMOVE(&_dlopened_child_list, n, next_sib);
486			_dl_free(n);
487		}
488		n = next;
489	}
490
491	head = free_objects;
492	free_objects = NULL;
493	while (head != NULL) {
494		_dl_free(head->load_name);
495		_dl_free((char *)head->sod.sod_name);
496		_dl_free_path(head->runpath);
497		_dl_free_path(head->rpath);
498		_dl_free(head->grpsym_vec.vec);
499		_dl_tailq_free(TAILQ_FIRST(&head->child_list));
500		_dl_tailq_free(TAILQ_FIRST(&head->grpref_list));
501		nobj = head->next;
502		_dl_free(head);
503		head = nobj;
504	}
505}
506
507void
508_dl_remove_object(elf_object_t *object)
509{
510	object->prev->next = object->next;
511	if (object->next)
512		object->next->prev = object->prev;
513
514	if (_dl_last_object == object)
515		_dl_last_object = object->prev;
516	object_count--;
517
518	object->next = free_objects;
519	free_objects = object;
520}
521
522static int
523matched_symbol(elf_object_t *obj, const Elf_Sym *sym, struct symlookup *sl)
524{
525	switch (ELF_ST_TYPE(sym->st_info)) {
526	case STT_FUNC:
527		/*
528		 * Allow this symbol if we are referring to a function which
529		 * has a value, even if section is UNDEF.  This allows &func
530		 * to refer to PLT as per the ELF spec.  If flags has SYM_PLT
531		 * set, we must have actual symbol, so this symbol is skipped.
532		 */
533		if ((sl->sl_flags & SYM_PLT) && sym->st_shndx == SHN_UNDEF)
534			return 0;
535		if (sym->st_value == 0)
536			return 0;
537		break;
538	case STT_NOTYPE:
539	case STT_OBJECT:
540		if (sym->st_value == 0)
541			return 0;
542#if 0
543		/* FALLTHROUGH */
544	case STT_TLS:
545#endif
546		if (sym->st_shndx == SHN_UNDEF)
547			return 0;
548		break;
549	default:
550		return 0;
551	}
552
553	if (sym != sl->sl_out.sym &&
554	    _dl_strcmp(sl->sl_name, obj->dyn.strtab + sym->st_name))
555		return 0;
556
557	if (ELF_ST_BIND(sym->st_info) == STB_GLOBAL) {
558		sl->sl_out.sym = sym;
559		sl->sl_out.obj = obj;
560		return 1;
561	} else if (ELF_ST_BIND(sym->st_info) == STB_WEAK) {
562		if (sl->sl_weak_out.sym == NULL) {
563			sl->sl_weak_out.sym = sym;
564			sl->sl_weak_out.obj = obj;
565		}
566		/* done with this object, but need to check other objects */
567		return -1;
568	}
569	return 0;
570}
571
572static int
573_dl_find_symbol_obj(elf_object_t *obj, struct symlookup *sl)
574{
575	const Elf_Sym	*symt = obj->dyn.symtab;
576
577	if (obj->status & STAT_GNU_HASH) {
578		uint32_t hash = sl->sl_gnu_hash;
579		Elf_Addr bloom_word;
580		unsigned int h1;
581		unsigned int h2;
582		Elf_Word bucket;
583		const Elf_Word *hashval;
584
585		/* pick right bitmask word from Bloom filter array */
586		bloom_word = obj->bloom_gnu[(hash / ELFSIZE) &
587		    obj->mask_bm_gnu];
588
589		/* calculate modulus ELFSIZE of gnu hash and its derivative */
590		h1 = hash & (ELFSIZE - 1);
591		h2 = (hash >> obj->shift2_gnu) & (ELFSIZE - 1);
592
593		/* Filter out the "definitely not in set" queries */
594		if (((bloom_word >> h1) & (bloom_word >> h2) & 1) == 0)
595			return 0;
596
597		/* Locate hash chain and corresponding value element */
598		bucket = obj->buckets_gnu[hash % obj->nbuckets];
599		if (bucket == 0)
600			return 0;
601		hashval = &obj->chains_gnu[bucket];
602		do {
603			if (((*hashval ^ hash) >> 1) == 0) {
604				const Elf_Sym *sym = symt +
605				    (hashval - obj->chains_gnu);
606
607				int r = matched_symbol(obj, sym, sl);
608				if (r)
609					return r > 0;
610			}
611		} while ((*hashval++ & 1U) == 0);
612	} else {
613		Elf_Word si;
614
615		for (si = obj->buckets_elf[sl->sl_elf_hash % obj->nbuckets];
616		    si != STN_UNDEF; si = obj->chains_elf[si]) {
617			const Elf_Sym *sym = symt + si;
618
619			int r = matched_symbol(obj, sym, sl);
620			if (r)
621				return r > 0;
622		}
623	}
624	return 0;
625}
626
627struct sym_res
628_dl_find_symbol(const char *name, int flags, const Elf_Sym *ref_sym,
629    elf_object_t *req_obj)
630{
631	const unsigned char *p;
632	unsigned char c;
633	struct symlookup sl = {
634		.sl_name = name,
635		.sl_out = { .sym = NULL },
636		.sl_weak_out = { .sym = NULL },
637		.sl_elf_hash = 0,
638		.sl_gnu_hash = 5381,
639		.sl_flags = flags,
640	};
641
642	/* Calculate both hashes in one pass */
643	for (p = (const unsigned char *)name; (c = *p) != '\0'; p++) {
644		unsigned long g;
645		sl.sl_elf_hash = (sl.sl_elf_hash << 4) + c;
646		if ((g = sl.sl_elf_hash & 0xf0000000))
647			sl.sl_elf_hash ^= g >> 24;
648		sl.sl_elf_hash &= ~g;
649		sl.sl_gnu_hash = sl.sl_gnu_hash * 33 + c;
650	}
651
652	if (req_obj->dyn.symbolic)
653		if (_dl_find_symbol_obj(req_obj, &sl))
654			goto found;
655
656	if (flags & SYM_DLSYM) {
657		struct object_vector vec;
658		int i;
659
660		if (_dl_find_symbol_obj(req_obj, &sl))
661			goto found;
662
663		/* weak definition in the specified object is good enough */
664		if (sl.sl_weak_out.sym != NULL)
665			goto found;
666
667		/* search dlopened obj and all children */
668		vec = req_obj->load_object->grpsym_vec;
669		for (i = 0; i < vec.len; i++) {
670			if (vec.vec[i] == req_obj)
671				continue;		/* already searched */
672			if (_dl_find_symbol_obj(vec.vec[i], &sl))
673				goto found;
674		}
675	} else {
676		struct dep_node *n;
677		struct object_vector vec;
678		int i, skip = 0;
679
680		if ((flags & SYM_SEARCH_SELF) || (flags & SYM_SEARCH_NEXT))
681			skip = 1;
682
683		/*
684		 * search dlopened objects: global or req_obj == dlopened_obj
685		 * and its children
686		 */
687		TAILQ_FOREACH(n, &_dlopened_child_list, next_sib) {
688			if (((n->data->obj_flags & DF_1_GLOBAL) == 0) &&
689			    (n->data != req_obj->load_object))
690				continue;
691
692			vec = n->data->grpsym_vec;
693			for (i = 0; i < vec.len; i++) {
694				if (skip == 1) {
695					if (vec.vec[i] == req_obj) {
696						skip = 0;
697						if (flags & SYM_SEARCH_NEXT)
698							continue;
699					} else
700						continue;
701				}
702				if ((flags & SYM_SEARCH_OTHER) &&
703				    (vec.vec[i] == req_obj))
704					continue;
705				if (_dl_find_symbol_obj(vec.vec[i], &sl))
706					goto found;
707			}
708		}
709	}
710
711found:
712	if (sl.sl_out.sym == NULL) {
713		if (sl.sl_weak_out.sym != NULL)
714			sl.sl_out = sl.sl_weak_out;
715		else {
716			if ((ref_sym == NULL ||
717			    (ELF_ST_BIND(ref_sym->st_info) != STB_WEAK)) &&
718			    (flags & SYM_WARNNOTFOUND))
719				_dl_printf("%s:%s: undefined symbol '%s'\n",
720				    __progname, req_obj->load_name, name);
721			return (struct sym_res){ NULL, NULL };
722		}
723	}
724
725	if (ref_sym != NULL && ref_sym->st_size != 0 &&
726	    (ref_sym->st_size != sl.sl_out.sym->st_size) &&
727	    (ELF_ST_TYPE(sl.sl_out.sym->st_info) != STT_FUNC) ) {
728		_dl_printf("%s:%s: %s : WARNING: "
729		    "symbol(%s) size mismatch, relink your program\n",
730		    __progname, req_obj->load_name, sl.sl_out.obj->load_name,
731		    name);
732	}
733
734	return sl.sl_out;
735}
736
737void
738_dl_debug_state(void)
739{
740	/* Debugger stub */
741}
742