resolve.c revision 1.94
1/*	$OpenBSD: resolve.c,v 1.94 2019/10/04 17:42:16 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	object->opencount = 0;	/* # dlopen() & exe */
435	object->grprefcount = 0;
436	/* default dev, inode for dlopen-able objects. */
437	object->dev = 0;
438	object->inode = 0;
439	object->grpsym_gen = 0;
440	TAILQ_INIT(&object->grpref_list);
441
442	if (object->dyn.runpath)
443		object->runpath = _dl_split_path(object->dyn.runpath);
444	/*
445	 * DT_RPATH is ignored if DT_RUNPATH is present...except in
446	 * the exe, whose DT_RPATH is a fallback for libs that don't
447	 * use DT_RUNPATH
448	 */
449	if (object->dyn.rpath && (object->runpath == NULL ||
450	    objtype == OBJTYPE_EXE))
451		object->rpath = _dl_split_path(object->dyn.rpath);
452	if ((object->obj_flags & DF_1_ORIGIN) && _dl_trust)
453		_dl_origin_subst(object);
454
455	_dl_trace_object_setup(object);
456
457	return (object);
458}
459
460static void
461_dl_tailq_free(struct dep_node *n)
462{
463	struct dep_node *next;
464
465	while (n != NULL) {
466		next = TAILQ_NEXT(n, next_sib);
467		_dl_free(n);
468		n = next;
469	}
470}
471
472static elf_object_t *free_objects;
473
474void
475_dl_cleanup_objects()
476{
477	elf_object_t *nobj, *head;
478	struct dep_node *n, *next;
479
480	n = TAILQ_FIRST(&_dlopened_child_list);
481	while (n != NULL) {
482		next = TAILQ_NEXT(n, next_sib);
483		if (OBJECT_DLREF_CNT(n->data) == 0) {
484			TAILQ_REMOVE(&_dlopened_child_list, n, next_sib);
485			_dl_free(n);
486		}
487		n = next;
488	}
489
490	head = free_objects;
491	free_objects = NULL;
492	while (head != NULL) {
493		_dl_free(head->load_name);
494		_dl_free((char *)head->sod.sod_name);
495		_dl_free_path(head->runpath);
496		_dl_free_path(head->rpath);
497		_dl_free(head->grpsym_vec.vec);
498		_dl_free(head->child_vec.vec);
499		_dl_tailq_free(TAILQ_FIRST(&head->grpref_list));
500		nobj = head->next;
501		_dl_free(head);
502		head = nobj;
503	}
504}
505
506void
507_dl_remove_object(elf_object_t *object)
508{
509	object->prev->next = object->next;
510	if (object->next)
511		object->next->prev = object->prev;
512
513	if (_dl_last_object == object)
514		_dl_last_object = object->prev;
515	object_count--;
516
517	object->next = free_objects;
518	free_objects = object;
519}
520
521static int
522matched_symbol(elf_object_t *obj, const Elf_Sym *sym, struct symlookup *sl)
523{
524	switch (ELF_ST_TYPE(sym->st_info)) {
525	case STT_FUNC:
526		/*
527		 * Allow this symbol if we are referring to a function which
528		 * has a value, even if section is UNDEF.  This allows &func
529		 * to refer to PLT as per the ELF spec.  If flags has SYM_PLT
530		 * set, we must have actual symbol, so this symbol is skipped.
531		 */
532		if ((sl->sl_flags & SYM_PLT) && sym->st_shndx == SHN_UNDEF)
533			return 0;
534		if (sym->st_value == 0)
535			return 0;
536		break;
537	case STT_NOTYPE:
538	case STT_OBJECT:
539		if (sym->st_value == 0)
540			return 0;
541#if 0
542		/* FALLTHROUGH */
543	case STT_TLS:
544#endif
545		if (sym->st_shndx == SHN_UNDEF)
546			return 0;
547		break;
548	default:
549		return 0;
550	}
551
552	if (sym != sl->sl_out.sym &&
553	    _dl_strcmp(sl->sl_name, obj->dyn.strtab + sym->st_name))
554		return 0;
555
556	if (ELF_ST_BIND(sym->st_info) == STB_GLOBAL) {
557		sl->sl_out.sym = sym;
558		sl->sl_out.obj = obj;
559		return 1;
560	} else if (ELF_ST_BIND(sym->st_info) == STB_WEAK) {
561		if (sl->sl_weak_out.sym == NULL) {
562			sl->sl_weak_out.sym = sym;
563			sl->sl_weak_out.obj = obj;
564		}
565		/* done with this object, but need to check other objects */
566		return -1;
567	}
568	return 0;
569}
570
571static int
572_dl_find_symbol_obj(elf_object_t *obj, struct symlookup *sl)
573{
574	const Elf_Sym	*symt = obj->dyn.symtab;
575
576	if (obj->status & STAT_GNU_HASH) {
577		uint32_t hash = sl->sl_gnu_hash;
578		Elf_Addr bloom_word;
579		unsigned int h1;
580		unsigned int h2;
581		Elf_Word bucket;
582		const Elf_Word *hashval;
583
584		/* pick right bitmask word from Bloom filter array */
585		bloom_word = obj->bloom_gnu[(hash / ELFSIZE) &
586		    obj->mask_bm_gnu];
587
588		/* calculate modulus ELFSIZE of gnu hash and its derivative */
589		h1 = hash & (ELFSIZE - 1);
590		h2 = (hash >> obj->shift2_gnu) & (ELFSIZE - 1);
591
592		/* Filter out the "definitely not in set" queries */
593		if (((bloom_word >> h1) & (bloom_word >> h2) & 1) == 0)
594			return 0;
595
596		/* Locate hash chain and corresponding value element */
597		bucket = obj->buckets_gnu[hash % obj->nbuckets];
598		if (bucket == 0)
599			return 0;
600		hashval = &obj->chains_gnu[bucket];
601		do {
602			if (((*hashval ^ hash) >> 1) == 0) {
603				const Elf_Sym *sym = symt +
604				    (hashval - obj->chains_gnu);
605
606				int r = matched_symbol(obj, sym, sl);
607				if (r)
608					return r > 0;
609			}
610		} while ((*hashval++ & 1U) == 0);
611	} else {
612		Elf_Word si;
613
614		for (si = obj->buckets_elf[sl->sl_elf_hash % obj->nbuckets];
615		    si != STN_UNDEF; si = obj->chains_elf[si]) {
616			const Elf_Sym *sym = symt + si;
617
618			int r = matched_symbol(obj, sym, sl);
619			if (r)
620				return r > 0;
621		}
622	}
623	return 0;
624}
625
626struct sym_res
627_dl_find_symbol(const char *name, int flags, const Elf_Sym *ref_sym,
628    elf_object_t *req_obj)
629{
630	const unsigned char *p;
631	unsigned char c;
632	struct symlookup sl = {
633		.sl_name = name,
634		.sl_out = { .sym = NULL },
635		.sl_weak_out = { .sym = NULL },
636		.sl_elf_hash = 0,
637		.sl_gnu_hash = 5381,
638		.sl_flags = flags,
639	};
640
641	/* Calculate both hashes in one pass */
642	for (p = (const unsigned char *)name; (c = *p) != '\0'; p++) {
643		unsigned long g;
644		sl.sl_elf_hash = (sl.sl_elf_hash << 4) + c;
645		if ((g = sl.sl_elf_hash & 0xf0000000))
646			sl.sl_elf_hash ^= g >> 24;
647		sl.sl_elf_hash &= ~g;
648		sl.sl_gnu_hash = sl.sl_gnu_hash * 33 + c;
649	}
650
651	if (req_obj->dyn.symbolic)
652		if (_dl_find_symbol_obj(req_obj, &sl))
653			goto found;
654
655	if (flags & SYM_DLSYM) {
656		struct object_vector vec;
657		int i;
658
659		if (_dl_find_symbol_obj(req_obj, &sl))
660			goto found;
661
662		/* weak definition in the specified object is good enough */
663		if (sl.sl_weak_out.sym != NULL)
664			goto found;
665
666		/* search dlopened obj and all children */
667		vec = req_obj->load_object->grpsym_vec;
668		for (i = 0; i < vec.len; i++) {
669			if (vec.vec[i] == req_obj)
670				continue;		/* already searched */
671			if (_dl_find_symbol_obj(vec.vec[i], &sl))
672				goto found;
673		}
674	} else {
675		struct dep_node *n;
676		struct object_vector vec;
677		int i, skip = 0;
678
679		if ((flags & SYM_SEARCH_SELF) || (flags & SYM_SEARCH_NEXT))
680			skip = 1;
681
682		/*
683		 * search dlopened objects: global or req_obj == dlopened_obj
684		 * and its children
685		 */
686		TAILQ_FOREACH(n, &_dlopened_child_list, next_sib) {
687			if (((n->data->obj_flags & DF_1_GLOBAL) == 0) &&
688			    (n->data != req_obj->load_object))
689				continue;
690
691			vec = n->data->grpsym_vec;
692			for (i = 0; i < vec.len; i++) {
693				if (skip == 1) {
694					if (vec.vec[i] == req_obj) {
695						skip = 0;
696						if (flags & SYM_SEARCH_NEXT)
697							continue;
698					} else
699						continue;
700				}
701				if ((flags & SYM_SEARCH_OTHER) &&
702				    (vec.vec[i] == req_obj))
703					continue;
704				if (_dl_find_symbol_obj(vec.vec[i], &sl))
705					goto found;
706			}
707		}
708	}
709
710found:
711	if (sl.sl_out.sym == NULL) {
712		if (sl.sl_weak_out.sym != NULL)
713			sl.sl_out = sl.sl_weak_out;
714		else {
715			if ((ref_sym == NULL ||
716			    (ELF_ST_BIND(ref_sym->st_info) != STB_WEAK)) &&
717			    (flags & SYM_WARNNOTFOUND))
718				_dl_printf("%s:%s: undefined symbol '%s'\n",
719				    __progname, req_obj->load_name, name);
720			return (struct sym_res){ NULL, NULL };
721		}
722	}
723
724	if (ref_sym != NULL && ref_sym->st_size != 0 &&
725	    (ref_sym->st_size != sl.sl_out.sym->st_size) &&
726	    (ELF_ST_TYPE(sl.sl_out.sym->st_info) != STT_FUNC) ) {
727		_dl_printf("%s:%s: %s : WARNING: "
728		    "symbol(%s) size mismatch, relink your program\n",
729		    __progname, req_obj->load_name, sl.sl_out.obj->load_name,
730		    name);
731	}
732
733	return sl.sl_out;
734}
735
736void
737_dl_debug_state(void)
738{
739	/* Debugger stub */
740}
741