1*210cc31eSderaadt /* $OpenBSD: resolve.c,v 1.102 2024/01/22 02:08:31 deraadt Exp $ */
24d11faf9Srahnds
34d11faf9Srahnds /*
44d11faf9Srahnds * Copyright (c) 1998 Per Fogelstrom, Opsycon AB
54d11faf9Srahnds *
64d11faf9Srahnds * Redistribution and use in source and binary forms, with or without
74d11faf9Srahnds * modification, are permitted provided that the following conditions
84d11faf9Srahnds * are met:
94d11faf9Srahnds * 1. Redistributions of source code must retain the above copyright
104d11faf9Srahnds * notice, this list of conditions and the following disclaimer.
114d11faf9Srahnds * 2. Redistributions in binary form must reproduce the above copyright
124d11faf9Srahnds * notice, this list of conditions and the following disclaimer in the
134d11faf9Srahnds * documentation and/or other materials provided with the distribution.
144d11faf9Srahnds *
154d11faf9Srahnds * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
164d11faf9Srahnds * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
174d11faf9Srahnds * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
184d11faf9Srahnds * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
194d11faf9Srahnds * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
204d11faf9Srahnds * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
214d11faf9Srahnds * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
224d11faf9Srahnds * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
234d11faf9Srahnds * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
244d11faf9Srahnds * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
254d11faf9Srahnds * SUCH DAMAGE.
264d11faf9Srahnds *
274d11faf9Srahnds */
284d11faf9Srahnds
294d11faf9Srahnds #define _DYN_LOADER
304d11faf9Srahnds
314d11faf9Srahnds #include <sys/types.h>
324a066defSderaadt #include <sys/mman.h>
334a066defSderaadt #include <sys/syscall.h>
344d11faf9Srahnds
35337bd349Skurt #include <limits.h>
364d11faf9Srahnds #include <link.h>
37b722ba42Sguenther
38b722ba42Sguenther #include "util.h"
39c8dfd7a0Skurt #include "path.h"
404be7d39eSrahnds #include "resolve.h"
414a066defSderaadt #include "syscall.h"
424d11faf9Srahnds
43337bd349Skurt /* substitution types */
44337bd349Skurt typedef enum {
45337bd349Skurt SUBST_UNKNOWN, SUBST_ORIGIN, SUBST_OSNAME, SUBST_OSREL, SUBST_PLATFORM
46337bd349Skurt } SUBST_TYPES;
47337bd349Skurt
48b5a21baaSguenther struct symlookup {
49b5a21baaSguenther const char *sl_name;
50143e5accSguenther struct sym_res sl_out;
51143e5accSguenther struct sym_res sl_weak_out;
52b5a21baaSguenther unsigned long sl_elf_hash;
53ac51d06cSguenther uint32_t sl_gnu_hash;
54b5a21baaSguenther int sl_flags;
55b5a21baaSguenther };
56b5a21baaSguenther
574d11faf9Srahnds elf_object_t *_dl_objects;
58bae526eeSguenther int object_count;
59bae526eeSguenther static elf_object_t *_dl_last_object;
600acae5e5Sdrahn elf_object_t *_dl_loading_object;
614d11faf9Srahnds
6270be5820Sdrahn void
_dl_handle_nodelete(elf_object_t * object)63296fbf9fSsemarie _dl_handle_nodelete(elf_object_t *object)
6470be5820Sdrahn {
652f744062Sguenther /*
662f744062Sguenther * If a .so is marked nodelete, then the entire load group that it's
672f744062Sguenther * in needs to be kept around forever, so add a reference there.
682f744062Sguenther * XXX It would be better if we tracked inter-object dependencies
692f744062Sguenther * from relocations and didn't leave dangling pointers when a load
702f744062Sguenther * group was partially unloaded. That would render this unnecessary.
712f744062Sguenther */
720fdecfd0Sguenther if (object->obj_flags & DF_1_NODELETE &&
732f744062Sguenther (object->load_object->status & STAT_NODELETE) == 0) {
740fdecfd0Sguenther DL_DEB(("objname %s is nodelete\n", object->load_name));
752f744062Sguenther object->load_object->opencount++;
762f744062Sguenther object->load_object->status |= STAT_NODELETE;
770fdecfd0Sguenther }
78296fbf9fSsemarie }
79296fbf9fSsemarie
80296fbf9fSsemarie /*
81296fbf9fSsemarie * Add a new dynamic object to the object list.
82296fbf9fSsemarie */
83296fbf9fSsemarie void
_dl_add_object(elf_object_t * object)84296fbf9fSsemarie _dl_add_object(elf_object_t *object)
85296fbf9fSsemarie {
86296fbf9fSsemarie _dl_handle_nodelete(object);
8770be5820Sdrahn
8870be5820Sdrahn /*
8970be5820Sdrahn * if this is a new object, prev will be NULL
9070be5820Sdrahn * != NULL if an object already in the list
9170be5820Sdrahn * prev == NULL for the first item in the list, but that will
9270be5820Sdrahn * be the executable.
9370be5820Sdrahn */
9470be5820Sdrahn if (object->prev != NULL)
9570be5820Sdrahn return;
9670be5820Sdrahn
9770be5820Sdrahn if (_dl_objects == NULL) { /* First object ? */
9870be5820Sdrahn _dl_last_object = _dl_objects = object;
99bae526eeSguenther object_count = 2; /* count ld.so early */
10070be5820Sdrahn } else {
10170be5820Sdrahn _dl_last_object->next = object;
10270be5820Sdrahn object->prev = _dl_last_object;
10370be5820Sdrahn _dl_last_object = object;
104bae526eeSguenther if (object->obj_type != OBJTYPE_LDR) /* see above */
105bae526eeSguenther object_count++;
10670be5820Sdrahn }
10770be5820Sdrahn }
10870be5820Sdrahn
10970be5820Sdrahn /*
110337bd349Skurt * Identify substitution sequence name.
111337bd349Skurt */
112337bd349Skurt static int
_dl_subst_name(const char * name,size_t siz)113337bd349Skurt _dl_subst_name(const char *name, size_t siz) {
114337bd349Skurt switch (siz) {
115337bd349Skurt case 5:
116337bd349Skurt if (_dl_strncmp(name, "OSREL", 5) == 0)
117337bd349Skurt return SUBST_OSREL;
118337bd349Skurt break;
119337bd349Skurt case 6:
120337bd349Skurt if (_dl_strncmp(name, "ORIGIN", 6) == 0)
121337bd349Skurt return SUBST_ORIGIN;
122337bd349Skurt if (_dl_strncmp(name, "OSNAME", 6) == 0)
123337bd349Skurt return SUBST_OSNAME;
124337bd349Skurt break;
125337bd349Skurt case 8:
126337bd349Skurt if (_dl_strncmp(name, "PLATFORM", 8) == 0)
127337bd349Skurt return SUBST_PLATFORM;
128337bd349Skurt break;
129337bd349Skurt }
130337bd349Skurt
131337bd349Skurt return (SUBST_UNKNOWN);
132337bd349Skurt }
133337bd349Skurt
134337bd349Skurt /*
135337bd349Skurt * Perform $ORIGIN substitutions on path
136337bd349Skurt */
137337bd349Skurt static void
_dl_origin_subst_path(elf_object_t * object,const char * origin_path,char ** path)138337bd349Skurt _dl_origin_subst_path(elf_object_t *object, const char *origin_path,
139337bd349Skurt char **path)
140337bd349Skurt {
141337bd349Skurt char tmp_path[PATH_MAX];
142337bd349Skurt char *new_path, *tp;
143337bd349Skurt const char *pp, *name, *value;
144337bd349Skurt static struct utsname uts;
145337bd349Skurt size_t value_len;
146337bd349Skurt int skip_brace;
147337bd349Skurt
148337bd349Skurt if (uts.sysname[0] == '\0') {
149337bd349Skurt if (_dl_uname(&uts) != 0)
150337bd349Skurt return;
151337bd349Skurt }
152337bd349Skurt
153337bd349Skurt tp = tmp_path;
154337bd349Skurt pp = *path;
155337bd349Skurt
156337bd349Skurt while (*pp != '\0' && (tp - tmp_path) < sizeof(tmp_path)) {
157337bd349Skurt
158337bd349Skurt /* copy over chars up to but not including $ */
159337bd349Skurt while (*pp != '\0' && *pp != '$' &&
160337bd349Skurt (tp - tmp_path) < sizeof(tmp_path))
161337bd349Skurt *tp++ = *pp++;
162337bd349Skurt
163337bd349Skurt /* substitution sequence detected */
164337bd349Skurt if (*pp == '$' && (tp - tmp_path) < sizeof(tmp_path)) {
165337bd349Skurt pp++;
166337bd349Skurt
167337bd349Skurt if ((skip_brace = (*pp == '{')))
168337bd349Skurt pp++;
169337bd349Skurt
170337bd349Skurt /* skip over name */
171337bd349Skurt name = pp;
1724207a9b6Sderaadt while (_dl_isalnum((unsigned char)*pp) || *pp == '_')
173337bd349Skurt pp++;
174337bd349Skurt
175337bd349Skurt switch (_dl_subst_name(name, pp - name)) {
176337bd349Skurt case SUBST_ORIGIN:
177337bd349Skurt value = origin_path;
178337bd349Skurt break;
179337bd349Skurt case SUBST_OSNAME:
180337bd349Skurt value = uts.sysname;
181337bd349Skurt break;
182337bd349Skurt case SUBST_OSREL:
183337bd349Skurt value = uts.release;
184337bd349Skurt break;
185337bd349Skurt case SUBST_PLATFORM:
186337bd349Skurt value = uts.machine;
187337bd349Skurt break;
188337bd349Skurt default:
189337bd349Skurt value = "";
190337bd349Skurt }
191337bd349Skurt
192337bd349Skurt value_len = _dl_strlen(value);
193337bd349Skurt if (value_len >= sizeof(tmp_path) - (tp - tmp_path))
194337bd349Skurt return;
195337bd349Skurt
196337bd349Skurt _dl_bcopy(value, tp, value_len);
197337bd349Skurt tp += value_len;
198337bd349Skurt
199337bd349Skurt if (skip_brace && *pp == '}')
200337bd349Skurt pp++;
201337bd349Skurt }
202337bd349Skurt }
203337bd349Skurt
204337bd349Skurt /* no substitution made if result exceeds sizeof(tmp_path) */
205337bd349Skurt if (tp - tmp_path >= sizeof(tmp_path))
206337bd349Skurt return;
207337bd349Skurt
208337bd349Skurt /* NULL terminate tmp_path */
209337bd349Skurt *tp = '\0';
210337bd349Skurt
211337bd349Skurt if (_dl_strcmp(tmp_path, *path) == 0)
212337bd349Skurt return;
213337bd349Skurt
214337bd349Skurt new_path = _dl_strdup(tmp_path);
215337bd349Skurt if (new_path == NULL)
216337bd349Skurt return;
217337bd349Skurt
218337bd349Skurt DL_DEB(("orig_path %s\n", *path));
219337bd349Skurt DL_DEB(("new_path %s\n", new_path));
220337bd349Skurt
221337bd349Skurt _dl_free(*path);
222337bd349Skurt *path = new_path;
223337bd349Skurt }
224337bd349Skurt
225337bd349Skurt /*
226337bd349Skurt * Determine origin_path from object load_name. The origin_path argument
227337bd349Skurt * must refer to a buffer capable of storing at least PATH_MAX characters.
228337bd349Skurt * Returns 0 on success.
229337bd349Skurt */
230337bd349Skurt static int
_dl_origin_path(elf_object_t * object,char * origin_path)231337bd349Skurt _dl_origin_path(elf_object_t *object, char *origin_path)
232337bd349Skurt {
233c51b9268Sgnezdo const char *dirname_path;
234337bd349Skurt
235c51b9268Sgnezdo /* syscall in ld.so returns 0/-errno, where libc returns char* */
236c51b9268Sgnezdo if (_dl___realpath(object->load_name, origin_path) < 0)
237c51b9268Sgnezdo return -1;
238c51b9268Sgnezdo
239c51b9268Sgnezdo dirname_path = _dl_dirname(origin_path);
240337bd349Skurt if (dirname_path == NULL)
241337bd349Skurt return -1;
242337bd349Skurt
243c51b9268Sgnezdo _dl_strlcpy(origin_path, dirname_path, PATH_MAX);
244337bd349Skurt
245337bd349Skurt return 0;
246337bd349Skurt }
247337bd349Skurt
248337bd349Skurt /*
24982c4222eSguenther * Perform $ORIGIN substitutions on runpath and rpath
250337bd349Skurt */
251337bd349Skurt static void
_dl_origin_subst(elf_object_t * object)252337bd349Skurt _dl_origin_subst(elf_object_t *object)
253337bd349Skurt {
254337bd349Skurt char origin_path[PATH_MAX];
255337bd349Skurt char **pp;
256337bd349Skurt
257337bd349Skurt if (_dl_origin_path(object, origin_path) != 0)
258337bd349Skurt return;
259337bd349Skurt
26082c4222eSguenther /* perform path substitutions on each segment of runpath and rpath */
261439893b0Sguenther if (object->runpath != NULL) {
262439893b0Sguenther for (pp = object->runpath; *pp != NULL; pp++)
26382c4222eSguenther _dl_origin_subst_path(object, origin_path, pp);
26482c4222eSguenther }
265439893b0Sguenther if (object->rpath != NULL) {
266439893b0Sguenther for (pp = object->rpath; *pp != NULL; pp++)
267337bd349Skurt _dl_origin_subst_path(object, origin_path, pp);
268337bd349Skurt }
269337bd349Skurt }
270337bd349Skurt
271337bd349Skurt /*
27270be5820Sdrahn * Initialize a new dynamic object.
2734d11faf9Srahnds */
2744d11faf9Srahnds elf_object_t *
_dl_finalize_object(const char * objname,Elf_Dyn * dynp,Elf_Phdr * phdrp,int phdrc,const int objtype,const long lbase,const long obase)2759f6c3316Skurt _dl_finalize_object(const char *objname, Elf_Dyn *dynp, Elf_Phdr *phdrp,
2769f6c3316Skurt int phdrc, const int objtype, const long lbase, const long obase)
2774d11faf9Srahnds {
2784d11faf9Srahnds elf_object_t *object;
279ac51d06cSguenther Elf_Addr gnu_hash = 0;
28086fa57a2Skettenis
281d198ddd1Sjasper DL_DEB(("objname [%s], dynp %p, objtype %x lbase %lx, obase %lx\n",
282d198ddd1Sjasper objname, dynp, objtype, lbase, obase));
283d198ddd1Sjasper
284c827e20bSotto object = _dl_calloc(1, sizeof(elf_object_t));
28567b06ea7Sotto if (object == NULL)
2863b50b772Sguenther _dl_oom();
28770be5820Sdrahn object->prev = object->next = NULL;
2884d11faf9Srahnds
2894d11faf9Srahnds object->load_dyn = dynp;
2904d11faf9Srahnds while (dynp->d_tag != DT_NULL) {
291fb2271a5Sderaadt if (dynp->d_tag < DT_NUM)
2924d11faf9Srahnds object->Dyn.info[dynp->d_tag] = dynp->d_un.d_val;
293fb2271a5Sderaadt else if (dynp->d_tag >= DT_LOPROC &&
294cb964ca6Skettenis dynp->d_tag < DT_LOPROC + DT_PROCNUM)
295fb2271a5Sderaadt object->Dyn.info[dynp->d_tag + DT_NUM - DT_LOPROC] =
296fb2271a5Sderaadt dynp->d_un.d_val;
2974d11faf9Srahnds if (dynp->d_tag == DT_TEXTREL)
2984d11faf9Srahnds object->dyn.textrel = 1;
2994d11faf9Srahnds if (dynp->d_tag == DT_SYMBOLIC)
3004d11faf9Srahnds object->dyn.symbolic = 1;
3014d11faf9Srahnds if (dynp->d_tag == DT_BIND_NOW)
3020fdecfd0Sguenther object->obj_flags |= DF_1_NOW;
3030fdecfd0Sguenther if (dynp->d_tag == DT_FLAGS_1)
3040fdecfd0Sguenther object->obj_flags |= dynp->d_un.d_val;
30582c4222eSguenther if (dynp->d_tag == DT_FLAGS) {
30682c4222eSguenther object->dyn.flags |= dynp->d_un.d_val;
30782c4222eSguenther if (dynp->d_un.d_val & DF_SYMBOLIC)
30882c4222eSguenther object->dyn.symbolic = 1;
30991659d32Skettenis if (dynp->d_un.d_val & DF_TEXTREL)
31091659d32Skettenis object->dyn.textrel = 1;
31182c4222eSguenther if (dynp->d_un.d_val & DF_ORIGIN)
31282c4222eSguenther object->obj_flags |= DF_1_ORIGIN;
31382c4222eSguenther if (dynp->d_un.d_val & DF_BIND_NOW)
31482c4222eSguenther object->obj_flags |= DF_1_NOW;
31582c4222eSguenther }
31688098a4dSguenther if (dynp->d_tag == DT_RELACOUNT)
31788098a4dSguenther object->relacount = dynp->d_un.d_val;
31888098a4dSguenther if (dynp->d_tag == DT_RELCOUNT)
31988098a4dSguenther object->relcount = dynp->d_un.d_val;
320ac51d06cSguenther if (dynp->d_tag == DT_GNU_HASH)
321ac51d06cSguenther gnu_hash = dynp->d_un.d_val;
3224d11faf9Srahnds dynp++;
3234d11faf9Srahnds }
3240fdecfd0Sguenther DL_DEB((" flags %s = 0x%x\n", objname, object->obj_flags));
3252880af04Skettenis object->obj_type = objtype;
3262880af04Skettenis
3272880af04Skettenis if (_dl_loading_object == NULL) {
3282880af04Skettenis /*
3292880af04Skettenis * no loading object, object is the loading object,
3302880af04Skettenis * as it is either executable, or dlopened()
3312880af04Skettenis */
3322880af04Skettenis _dl_loading_object = object;
3332880af04Skettenis }
3342880af04Skettenis
3352880af04Skettenis if ((object->obj_flags & DF_1_NOOPEN) != 0 &&
3362880af04Skettenis _dl_loading_object->obj_type == OBJTYPE_DLO &&
33710200827Sguenther !_dl_traceld) {
3382880af04Skettenis _dl_free(object);
3392880af04Skettenis _dl_errno = DL_CANT_LOAD_OBJ;
3402880af04Skettenis return(NULL);
3412880af04Skettenis }
3424d11faf9Srahnds
3434d11faf9Srahnds /*
3444d11faf9Srahnds * Now relocate all pointer to dynamic info, but only
3455d5c76edSjufi * the ones which have pointer values.
3464d11faf9Srahnds */
3474d11faf9Srahnds if (object->Dyn.info[DT_PLTGOT])
348ce11e090Skurt object->Dyn.info[DT_PLTGOT] += obase;
3494d11faf9Srahnds if (object->Dyn.info[DT_STRTAB])
350ce11e090Skurt object->Dyn.info[DT_STRTAB] += obase;
3514d11faf9Srahnds if (object->Dyn.info[DT_SYMTAB])
352ce11e090Skurt object->Dyn.info[DT_SYMTAB] += obase;
3534d11faf9Srahnds if (object->Dyn.info[DT_RELA])
354ce11e090Skurt object->Dyn.info[DT_RELA] += obase;
3554d11faf9Srahnds if (object->Dyn.info[DT_SONAME])
3560af06aafSkurt object->Dyn.info[DT_SONAME] += object->Dyn.info[DT_STRTAB];
3574d11faf9Srahnds if (object->Dyn.info[DT_RPATH])
3584be7d39eSrahnds object->Dyn.info[DT_RPATH] += object->Dyn.info[DT_STRTAB];
359d8ad95d2Sguenther if (object->Dyn.info[DT_RUNPATH])
360d8ad95d2Sguenther object->Dyn.info[DT_RUNPATH] += object->Dyn.info[DT_STRTAB];
3614d11faf9Srahnds if (object->Dyn.info[DT_REL])
362ce11e090Skurt object->Dyn.info[DT_REL] += obase;
3634d11faf9Srahnds if (object->Dyn.info[DT_INIT])
364ce11e090Skurt object->Dyn.info[DT_INIT] += obase;
3654d11faf9Srahnds if (object->Dyn.info[DT_FINI])
366ce11e090Skurt object->Dyn.info[DT_FINI] += obase;
3674be7d39eSrahnds if (object->Dyn.info[DT_JMPREL])
368ce11e090Skurt object->Dyn.info[DT_JMPREL] += obase;
36986fa57a2Skettenis if (object->Dyn.info[DT_INIT_ARRAY])
37086fa57a2Skettenis object->Dyn.info[DT_INIT_ARRAY] += obase;
37186fa57a2Skettenis if (object->Dyn.info[DT_FINI_ARRAY])
37286fa57a2Skettenis object->Dyn.info[DT_FINI_ARRAY] += obase;
37386fa57a2Skettenis if (object->Dyn.info[DT_PREINIT_ARRAY])
37486fa57a2Skettenis object->Dyn.info[DT_PREINIT_ARRAY] += obase;
375b3331980Sguenther if (object->Dyn.info[DT_RELR])
376b3331980Sguenther object->Dyn.info[DT_RELR] += obase;
3774d11faf9Srahnds
378ac51d06cSguenther if (gnu_hash) {
3799d9b0c10Sderaadt Elf_Word *hashtab = (Elf_Word *)(gnu_hash + obase);
3809d9b0c10Sderaadt Elf_Word nbuckets = hashtab[0];
3819d9b0c10Sderaadt Elf_Word nmaskwords = hashtab[2];
38282cac0f6Sart
383ac51d06cSguenther /* validity check */
384ac51d06cSguenther if (nbuckets > 0 && (nmaskwords & (nmaskwords - 1)) == 0) {
3859d9b0c10Sderaadt Elf_Word symndx = hashtab[1];
386ac51d06cSguenther int bloom_size32 = (ELFSIZE / 32) * nmaskwords;
387ac51d06cSguenther
388ac51d06cSguenther object->nbuckets = nbuckets;
389ac51d06cSguenther object->symndx_gnu = symndx;
390ac51d06cSguenther object->mask_bm_gnu = nmaskwords - 1;
391ac51d06cSguenther object->shift2_gnu = hashtab[3];
392ac51d06cSguenther object->bloom_gnu = (Elf_Addr *)(hashtab + 4);
393ac51d06cSguenther object->buckets_gnu = hashtab + 4 + bloom_size32;
394ac51d06cSguenther object->chains_gnu = object->buckets_gnu + nbuckets
395ac51d06cSguenther - symndx;
396ac51d06cSguenther
397ac51d06cSguenther /*
398ac51d06cSguenther * If the ELF hash is present, get the total symbol
399ac51d06cSguenther * count ("nchains") from there. Otherwise, count
400ac51d06cSguenther * the entries in the GNU hash chain.
401ac51d06cSguenther */
402ac51d06cSguenther if (object->Dyn.info[DT_HASH] == 0) {
4039d9b0c10Sderaadt Elf_Word n;
404ac51d06cSguenther
405ac51d06cSguenther for (n = 0; n < nbuckets; n++) {
406ac51d06cSguenther Elf_Word bkt = object->buckets_gnu[n];
4079d9b0c10Sderaadt const Elf_Word *hashval;
408ac51d06cSguenther if (bkt == 0)
409ac51d06cSguenther continue;
410ac51d06cSguenther hashval = &object->chains_gnu[bkt];
411ac51d06cSguenther do {
412ac51d06cSguenther symndx++;
413ac51d06cSguenther } while ((*hashval++ & 1U) == 0);
414ac51d06cSguenther }
415ac51d06cSguenther object->nchains = symndx;
416ac51d06cSguenther }
417ac51d06cSguenther object->status |= STAT_GNU_HASH;
418ac51d06cSguenther }
419ac51d06cSguenther }
420ac51d06cSguenther if (object->Dyn.info[DT_HASH] != 0) {
4214e986f76Sguenther Elf_Hash_Word *hashtab =
4224e986f76Sguenther (Elf_Hash_Word *)(object->Dyn.info[DT_HASH] + obase);
423ac51d06cSguenther
42482cac0f6Sart object->nchains = hashtab[1];
425ac51d06cSguenther if (object->nbuckets == 0) {
426ac51d06cSguenther object->nbuckets = hashtab[0];
427ac51d06cSguenther object->buckets_elf = hashtab + 2;
428ac51d06cSguenther object->chains_elf = object->buckets_elf +
429ac51d06cSguenther object->nbuckets;
430ac51d06cSguenther }
4314d11faf9Srahnds }
4324d11faf9Srahnds
4339f6c3316Skurt object->phdrp = phdrp;
4349f6c3316Skurt object->phdrc = phdrc;
435ce11e090Skurt object->load_base = lbase;
436ce11e090Skurt object->obj_base = obase;
43741c230b7Sdrahn object->load_name = _dl_strdup(objname);
43867b06ea7Sotto if (object->load_name == NULL)
4393b50b772Sguenther _dl_oom();
4400acae5e5Sdrahn object->load_object = _dl_loading_object;
4419652c184Smatthew if (object->load_object == object)
4429652c184Smatthew DL_DEB(("head %s\n", object->load_name));
4430acae5e5Sdrahn DL_DEB(("obj %s has %s as head\n", object->load_name,
4440acae5e5Sdrahn _dl_loading_object->load_name));
4458f55e5f1Skurt object->refcount = 0;
44650cabf59Skurt object->opencount = 0; /* # dlopen() & exe */
44750cabf59Skurt object->grprefcount = 0;
4487afb1d1dSkjell /* default dev, inode for dlopen-able objects. */
4497afb1d1dSkjell object->dev = 0;
4507afb1d1dSkjell object->inode = 0;
451b6decd50Sguenther object->grpsym_gen = 0;
45250cabf59Skurt TAILQ_INIT(&object->grpref_list);
4537afb1d1dSkjell
45482c4222eSguenther if (object->dyn.runpath)
45582c4222eSguenther object->runpath = _dl_split_path(object->dyn.runpath);
45682c4222eSguenther /*
45782c4222eSguenther * DT_RPATH is ignored if DT_RUNPATH is present...except in
45882c4222eSguenther * the exe, whose DT_RPATH is a fallback for libs that don't
45982c4222eSguenther * use DT_RUNPATH
46082c4222eSguenther */
46182c4222eSguenther if (object->dyn.rpath && (object->runpath == NULL ||
46282c4222eSguenther objtype == OBJTYPE_EXE))
463c8dfd7a0Skurt object->rpath = _dl_split_path(object->dyn.rpath);
464337bd349Skurt if ((object->obj_flags & DF_1_ORIGIN) && _dl_trust)
465337bd349Skurt _dl_origin_subst(object);
466c8dfd7a0Skurt
467ae398163Smiod _dl_trace_object_setup(object);
468ae398163Smiod
4694d11faf9Srahnds return (object);
4704d11faf9Srahnds }
4714d11faf9Srahnds
4722f86c0efSderaadt static void
_dl_tailq_free(struct dep_node * n)47350cabf59Skurt _dl_tailq_free(struct dep_node *n)
47450cabf59Skurt {
47550cabf59Skurt struct dep_node *next;
47650cabf59Skurt
47750cabf59Skurt while (n != NULL) {
47850cabf59Skurt next = TAILQ_NEXT(n, next_sib);
47950cabf59Skurt _dl_free(n);
48050cabf59Skurt n = next;
48150cabf59Skurt }
48250cabf59Skurt }
48350cabf59Skurt
484bfc90735Sguenther static elf_object_t *free_objects;
4858a1e31c9Sdrahn
4868a1e31c9Sdrahn void
_dl_cleanup_objects()4878a1e31c9Sdrahn _dl_cleanup_objects()
4888a1e31c9Sdrahn {
4898a1e31c9Sdrahn elf_object_t *nobj, *head;
49050cabf59Skurt struct dep_node *n, *next;
4912c5b63afSdrahn
49250cabf59Skurt n = TAILQ_FIRST(&_dlopened_child_list);
49350cabf59Skurt while (n != NULL) {
49450cabf59Skurt next = TAILQ_NEXT(n, next_sib);
495ca7a62b5Skurt if (OBJECT_DLREF_CNT(n->data) == 0) {
4962c5b63afSdrahn TAILQ_REMOVE(&_dlopened_child_list, n, next_sib);
4972c5b63afSdrahn _dl_free(n);
4982c5b63afSdrahn }
49950cabf59Skurt n = next;
5002c5b63afSdrahn }
5012c5b63afSdrahn
5028a1e31c9Sdrahn head = free_objects;
5038a1e31c9Sdrahn free_objects = NULL;
5048a1e31c9Sdrahn while (head != NULL) {
50550cabf59Skurt _dl_free(head->load_name);
5060af06aafSkurt _dl_free((char *)head->sod.sod_name);
50782c4222eSguenther _dl_free_path(head->runpath);
508c8dfd7a0Skurt _dl_free_path(head->rpath);
509bae526eeSguenther _dl_free(head->grpsym_vec.vec);
510d937a926Sguenther _dl_free(head->child_vec.vec);
51150cabf59Skurt _dl_tailq_free(TAILQ_FIRST(&head->grpref_list));
5128a1e31c9Sdrahn nobj = head->next;
5138a1e31c9Sdrahn _dl_free(head);
5148a1e31c9Sdrahn head = nobj;
5158a1e31c9Sdrahn }
5168a1e31c9Sdrahn }
5178a1e31c9Sdrahn
5184d11faf9Srahnds void
_dl_remove_object(elf_object_t * object)5194d11faf9Srahnds _dl_remove_object(elf_object_t *object)
5204d11faf9Srahnds {
5214d11faf9Srahnds object->prev->next = object->next;
52239b7d201Sderaadt if (object->next)
5234d11faf9Srahnds object->next->prev = object->prev;
52439b7d201Sderaadt
52539b7d201Sderaadt if (_dl_last_object == object)
526a526b250Sdrahn _dl_last_object = object->prev;
527bae526eeSguenther object_count--;
52839b7d201Sderaadt
5298a1e31c9Sdrahn object->next = free_objects;
53050cabf59Skurt free_objects = object;
5314d11faf9Srahnds }
5324d11faf9Srahnds
5332f86c0efSderaadt static int
matched_symbol(elf_object_t * obj,const Elf_Sym * sym,struct symlookup * sl)53449fb1b57Sguenther matched_symbol(elf_object_t *obj, const Elf_Sym *sym, struct symlookup *sl)
5352f86c0efSderaadt {
53649fb1b57Sguenther switch (ELF_ST_TYPE(sym->st_info)) {
53749fb1b57Sguenther case STT_FUNC:
53849fb1b57Sguenther /*
53949fb1b57Sguenther * Allow this symbol if we are referring to a function which
54049fb1b57Sguenther * has a value, even if section is UNDEF. This allows &func
54149fb1b57Sguenther * to refer to PLT as per the ELF spec. If flags has SYM_PLT
54249fb1b57Sguenther * set, we must have actual symbol, so this symbol is skipped.
5432f86c0efSderaadt */
54449fb1b57Sguenther if ((sl->sl_flags & SYM_PLT) && sym->st_shndx == SHN_UNDEF)
54549fb1b57Sguenther return 0;
54649fb1b57Sguenther if (sym->st_value == 0)
54749fb1b57Sguenther return 0;
54849fb1b57Sguenther break;
54949fb1b57Sguenther case STT_NOTYPE:
55049fb1b57Sguenther case STT_OBJECT:
55149fb1b57Sguenther if (sym->st_value == 0)
55249fb1b57Sguenther return 0;
55349fb1b57Sguenther #if 0
55449fb1b57Sguenther /* FALLTHROUGH */
55549fb1b57Sguenther case STT_TLS:
55649fb1b57Sguenther #endif
55749fb1b57Sguenther if (sym->st_shndx == SHN_UNDEF)
55849fb1b57Sguenther return 0;
55949fb1b57Sguenther break;
56049fb1b57Sguenther default:
56149fb1b57Sguenther return 0;
5622f86c0efSderaadt }
5632f86c0efSderaadt
564143e5accSguenther if (sym != sl->sl_out.sym &&
56549fb1b57Sguenther _dl_strcmp(sl->sl_name, obj->dyn.strtab + sym->st_name))
56649fb1b57Sguenther return 0;
56749fb1b57Sguenther
5682f86c0efSderaadt if (ELF_ST_BIND(sym->st_info) == STB_GLOBAL) {
569143e5accSguenther sl->sl_out.sym = sym;
570143e5accSguenther sl->sl_out.obj = obj;
5712f86c0efSderaadt return 1;
5722f86c0efSderaadt } else if (ELF_ST_BIND(sym->st_info) == STB_WEAK) {
573143e5accSguenther if (sl->sl_weak_out.sym == NULL) {
574143e5accSguenther sl->sl_weak_out.sym = sym;
575143e5accSguenther sl->sl_weak_out.obj = obj;
5762f86c0efSderaadt }
57749fb1b57Sguenther /* done with this object, but need to check other objects */
57849fb1b57Sguenther return -1;
5792f86c0efSderaadt }
58049fb1b57Sguenther return 0;
58149fb1b57Sguenther }
58249fb1b57Sguenther
58349fb1b57Sguenther static int
_dl_find_symbol_obj(elf_object_t * obj,struct symlookup * sl)58449fb1b57Sguenther _dl_find_symbol_obj(elf_object_t *obj, struct symlookup *sl)
58549fb1b57Sguenther {
58649fb1b57Sguenther const Elf_Sym *symt = obj->dyn.symtab;
58749fb1b57Sguenther
588ac51d06cSguenther if (obj->status & STAT_GNU_HASH) {
589ac51d06cSguenther uint32_t hash = sl->sl_gnu_hash;
590ac51d06cSguenther Elf_Addr bloom_word;
591ac51d06cSguenther unsigned int h1;
592ac51d06cSguenther unsigned int h2;
5939d9b0c10Sderaadt Elf_Word bucket;
5949d9b0c10Sderaadt const Elf_Word *hashval;
595ac51d06cSguenther
596ac51d06cSguenther /* pick right bitmask word from Bloom filter array */
597ac51d06cSguenther bloom_word = obj->bloom_gnu[(hash / ELFSIZE) &
598ac51d06cSguenther obj->mask_bm_gnu];
599ac51d06cSguenther
600ac51d06cSguenther /* calculate modulus ELFSIZE of gnu hash and its derivative */
601ac51d06cSguenther h1 = hash & (ELFSIZE - 1);
602ac51d06cSguenther h2 = (hash >> obj->shift2_gnu) & (ELFSIZE - 1);
603ac51d06cSguenther
604ac51d06cSguenther /* Filter out the "definitely not in set" queries */
605ac51d06cSguenther if (((bloom_word >> h1) & (bloom_word >> h2) & 1) == 0)
606ac51d06cSguenther return 0;
607ac51d06cSguenther
608ac51d06cSguenther /* Locate hash chain and corresponding value element */
609ac51d06cSguenther bucket = obj->buckets_gnu[hash % obj->nbuckets];
610ac51d06cSguenther if (bucket == 0)
611ac51d06cSguenther return 0;
612ac51d06cSguenther hashval = &obj->chains_gnu[bucket];
613ac51d06cSguenther do {
614ac51d06cSguenther if (((*hashval ^ hash) >> 1) == 0) {
615ac51d06cSguenther const Elf_Sym *sym = symt +
616ac51d06cSguenther (hashval - obj->chains_gnu);
617ac51d06cSguenther
618ac51d06cSguenther int r = matched_symbol(obj, sym, sl);
619ac51d06cSguenther if (r)
620ac51d06cSguenther return r > 0;
621ac51d06cSguenther }
622ac51d06cSguenther } while ((*hashval++ & 1U) == 0);
623ac51d06cSguenther } else {
624ac51d06cSguenther Elf_Word si;
625ac51d06cSguenther
626ac51d06cSguenther for (si = obj->buckets_elf[sl->sl_elf_hash % obj->nbuckets];
627ac51d06cSguenther si != STN_UNDEF; si = obj->chains_elf[si]) {
62849fb1b57Sguenther const Elf_Sym *sym = symt + si;
62949fb1b57Sguenther
63049fb1b57Sguenther int r = matched_symbol(obj, sym, sl);
63149fb1b57Sguenther if (r)
63249fb1b57Sguenther return r > 0;
6332f86c0efSderaadt }
634ac51d06cSguenther }
6352f86c0efSderaadt return 0;
6362f86c0efSderaadt }
6372f86c0efSderaadt
638143e5accSguenther struct sym_res
_dl_find_symbol(const char * name,int flags,const Elf_Sym * ref_sym,elf_object_t * req_obj)639143e5accSguenther _dl_find_symbol(const char *name, int flags, const Elf_Sym *ref_sym,
640143e5accSguenther elf_object_t *req_obj)
6414d11faf9Srahnds {
642ac51d06cSguenther const unsigned char *p;
643ac51d06cSguenther unsigned char c;
644b5a21baaSguenther struct symlookup sl = {
645b5a21baaSguenther .sl_name = name,
646143e5accSguenther .sl_out = { .sym = NULL },
647143e5accSguenther .sl_weak_out = { .sym = NULL },
648b5a21baaSguenther .sl_elf_hash = 0,
649ac51d06cSguenther .sl_gnu_hash = 5381,
650b5a21baaSguenther .sl_flags = flags,
651b5a21baaSguenther };
6524d11faf9Srahnds
653ac51d06cSguenther /* Calculate both hashes in one pass */
654ac51d06cSguenther for (p = (const unsigned char *)name; (c = *p) != '\0'; p++) {
655ac51d06cSguenther sl.sl_elf_hash = (sl.sl_elf_hash << 4) + c;
656d82bde86Smillert sl.sl_elf_hash ^= (sl.sl_elf_hash >> 24) & 0xf0;
657ac51d06cSguenther sl.sl_gnu_hash = sl.sl_gnu_hash * 33 + c;
6584d11faf9Srahnds }
659d82bde86Smillert sl.sl_elf_hash &= 0x0fffffff;
6604d11faf9Srahnds
66150f3e86fSdrahn if (req_obj->dyn.symbolic)
662b5a21baaSguenther if (_dl_find_symbol_obj(req_obj, &sl))
66350f3e86fSdrahn goto found;
66450f3e86fSdrahn
66525205068Sguenther if (flags & SYM_DLSYM) {
666bae526eeSguenther struct object_vector vec;
667bae526eeSguenther int i;
668bae526eeSguenther
669b5a21baaSguenther if (_dl_find_symbol_obj(req_obj, &sl))
670b5a21baaSguenther goto found;
6710acae5e5Sdrahn
672b5a21baaSguenther /* weak definition in the specified object is good enough */
673143e5accSguenther if (sl.sl_weak_out.sym != NULL)
674b5a21baaSguenther goto found;
675b5a21baaSguenther
676b5a21baaSguenther /* search dlopened obj and all children */
677bae526eeSguenther vec = req_obj->load_object->grpsym_vec;
678bae526eeSguenther for (i = 0; i < vec.len; i++) {
679bae526eeSguenther if (vec.vec[i] == req_obj)
680bae526eeSguenther continue; /* already searched */
681bae526eeSguenther if (_dl_find_symbol_obj(vec.vec[i], &sl))
682b5a21baaSguenther goto found;
6830acae5e5Sdrahn }
68491de9d58Ssthen } else {
685bae526eeSguenther struct dep_node *n;
686bae526eeSguenther struct object_vector vec;
687bae526eeSguenther int i, skip = 0;
6880acae5e5Sdrahn
6896fa405c3Sdrahn if ((flags & SYM_SEARCH_SELF) || (flags & SYM_SEARCH_NEXT))
6906fa405c3Sdrahn skip = 1;
6910acae5e5Sdrahn
6920acae5e5Sdrahn /*
6930acae5e5Sdrahn * search dlopened objects: global or req_obj == dlopened_obj
694bae526eeSguenther * and its children
6950acae5e5Sdrahn */
6960acae5e5Sdrahn TAILQ_FOREACH(n, &_dlopened_child_list, next_sib) {
6970fdecfd0Sguenther if (((n->data->obj_flags & DF_1_GLOBAL) == 0) &&
6980acae5e5Sdrahn (n->data != req_obj->load_object))
6990acae5e5Sdrahn continue;
7000acae5e5Sdrahn
701bae526eeSguenther vec = n->data->grpsym_vec;
702bae526eeSguenther for (i = 0; i < vec.len; i++) {
7036fa405c3Sdrahn if (skip == 1) {
704bae526eeSguenther if (vec.vec[i] == req_obj) {
7056fa405c3Sdrahn skip = 0;
7066fa405c3Sdrahn if (flags & SYM_SEARCH_NEXT)
7076fa405c3Sdrahn continue;
7086fa405c3Sdrahn } else
7096fa405c3Sdrahn continue;
7106fa405c3Sdrahn }
7110acae5e5Sdrahn if ((flags & SYM_SEARCH_OTHER) &&
712bae526eeSguenther (vec.vec[i] == req_obj))
7130acae5e5Sdrahn continue;
714bae526eeSguenther if (_dl_find_symbol_obj(vec.vec[i], &sl))
71597ecf04aSkurt goto found;
7160acae5e5Sdrahn }
7170acae5e5Sdrahn }
7180acae5e5Sdrahn }
71950f3e86fSdrahn
72050f3e86fSdrahn found:
721143e5accSguenther if (sl.sl_out.sym == NULL) {
722143e5accSguenther if (sl.sl_weak_out.sym != NULL)
723143e5accSguenther sl.sl_out = sl.sl_weak_out;
724143e5accSguenther else {
72506462af4Sdrahn if ((ref_sym == NULL ||
72606462af4Sdrahn (ELF_ST_BIND(ref_sym->st_info) != STB_WEAK)) &&
72706462af4Sdrahn (flags & SYM_WARNNOTFOUND))
72850f3e86fSdrahn _dl_printf("%s:%s: undefined symbol '%s'\n",
729702424caSguenther __progname, req_obj->load_name, name);
730143e5accSguenther return (struct sym_res){ NULL, NULL };
731143e5accSguenther }
73250f3e86fSdrahn }
73350f3e86fSdrahn
7346718d15cSdrahn if (ref_sym != NULL && ref_sym->st_size != 0 &&
735143e5accSguenther (ref_sym->st_size != sl.sl_out.sym->st_size) &&
736143e5accSguenther (ELF_ST_TYPE(sl.sl_out.sym->st_info) != STT_FUNC)) {
73750f3e86fSdrahn _dl_printf("%s:%s: %s : WARNING: "
73850f3e86fSdrahn "symbol(%s) size mismatch, relink your program\n",
739143e5accSguenther __progname, req_obj->load_name, sl.sl_out.obj->load_name,
740b5a21baaSguenther name);
74150f3e86fSdrahn }
74250f3e86fSdrahn
743143e5accSguenther return sl.sl_out;
74450f3e86fSdrahn }
74550f3e86fSdrahn
74676ff5b71Sotto void
_dl_debug_state(void)74776ff5b71Sotto _dl_debug_state(void)
74876ff5b71Sotto {
74976ff5b71Sotto /* Debugger stub */
75076ff5b71Sotto }
7514a066defSderaadt
7524a066defSderaadt /*
7534a066defSderaadt * Search for DT_SONAME, and check if this is libc
7544a066defSderaadt */
7554a066defSderaadt int
_dl_islibc(Elf_Dyn * _dynp,Elf_Addr loff)7564a066defSderaadt _dl_islibc(Elf_Dyn *_dynp, Elf_Addr loff)
7574a066defSderaadt {
7584a066defSderaadt Elf_Dyn *d, *dynp = (Elf_Dyn *)((unsigned long)_dynp + loff);
7594a066defSderaadt long base = 0;
7604a066defSderaadt
7614a066defSderaadt for (d = dynp; d->d_tag != DT_NULL; d++)
7624a066defSderaadt if (d->d_tag == DT_STRTAB) {
7634a066defSderaadt base = d->d_un.d_ptr + loff;
7644a066defSderaadt break;
7654a066defSderaadt }
7664a066defSderaadt if (base == 0)
7674a066defSderaadt return 0;
7684a066defSderaadt for (d = dynp; d->d_tag != DT_NULL; d++)
7694a066defSderaadt if (d->d_tag == DT_SONAME) {
7704a066defSderaadt if (_dl_strncmp((char *)(base + d->d_un.d_ptr),
7714a066defSderaadt "libc.so.", 8) == 0)
7724a066defSderaadt return 1;
7734a066defSderaadt break;
7744a066defSderaadt }
7754a066defSderaadt return 0;
7764a066defSderaadt }
7774a066defSderaadt
7784a066defSderaadt void
_dl_pin(int file,Elf_Phdr * phdp,void * base,size_t len,void * exec_base,size_t exec_size)7794a066defSderaadt _dl_pin(int file, Elf_Phdr *phdp, void *base, size_t len,
7804a066defSderaadt void *exec_base, size_t exec_size)
7814a066defSderaadt {
7824a066defSderaadt struct pinsyscalls {
7834a066defSderaadt u_int offset;
7844a066defSderaadt u_int sysno;
7854a066defSderaadt } *syscalls;
7864a066defSderaadt int npins = 0, nsyscalls, i;
7874a066defSderaadt u_int *pins = NULL;
7884a066defSderaadt vaddr_t offset;
7894a066defSderaadt
7904a066defSderaadt if (phdp->p_filesz > SYS_MAXSYSCALL * 2 * sizeof(*syscalls) ||
7914a066defSderaadt phdp->p_filesz % sizeof(*syscalls) != 0 ||
7924a066defSderaadt phdp->p_offset & 0x3)
7934a066defSderaadt return;
7944a066defSderaadt syscalls = _dl_mmap(NULL, phdp->p_filesz, PROT_READ,
7954a066defSderaadt MAP_PRIVATE|MAP_FILE, file, phdp->p_offset);
7964a066defSderaadt if (syscalls == MAP_FAILED)
7974a066defSderaadt return;
7984a066defSderaadt
7994a066defSderaadt /* Validate, and calculate pintable size */
8004a066defSderaadt nsyscalls = phdp->p_filesz / sizeof(*syscalls);
8014a066defSderaadt for (i = 0; i < nsyscalls; i++) {
8024a066defSderaadt if (syscalls[i].sysno < 0 ||
8034a066defSderaadt syscalls[i].sysno >= SYS_MAXSYSCALL ||
8044a066defSderaadt syscalls[i].offset >= len)
8054a066defSderaadt goto bad;
8064a066defSderaadt npins = MAXIMUM(npins, syscalls[i].sysno);
8074a066defSderaadt }
8084a066defSderaadt npins++;
8094a066defSderaadt
8104a066defSderaadt /*
8114a066defSderaadt * Fill pintable: 0 = invalid, -1 = accept, else offset
8124a066defSderaadt * from base, rebase to text_start while at it
8134a066defSderaadt */
8144a066defSderaadt pins = _dl_calloc(npins, sizeof(u_int));
8154a066defSderaadt offset = exec_base - base;
8164a066defSderaadt for (i = 0; i < nsyscalls; i++) {
8174a066defSderaadt if (pins[syscalls[i].sysno])
8184a066defSderaadt pins[syscalls[i].sysno] = (u_int)-1; /* duplicated */
8194a066defSderaadt else
8204a066defSderaadt pins[syscalls[i].sysno] = syscalls[i].offset - offset;
8214a066defSderaadt }
8224a066defSderaadt base += offset;
8234a066defSderaadt len = len - offset;
8244a066defSderaadt bad:
8254a066defSderaadt _dl_munmap(syscalls, phdp->p_filesz);
8264a066defSderaadt if (pins)
8274a066defSderaadt _dl_pinsyscalls(base, len, pins, npins);
8284a066defSderaadt _dl_free(pins);
8294a066defSderaadt }
830