1 /* $NetBSD: load.c,v 1.52 2026/07/07 14:10:23 riastradh Exp $ */ 2 3 /* 4 * Copyright 1996 John D. Polstra. 5 * Copyright 1996 Matt Thomas <matt (at) 3am-software.com> 6 * Copyright 2002 Charles M. Hannum <root (at) ihack.net> 7 * All rights reserved. 8 * 9 * Redistribution and use in source and binary forms, with or without 10 * modification, are permitted provided that the following conditions 11 * are met: 12 * 1. Redistributions of source code must retain the above copyright 13 * notice, this list of conditions and the following disclaimer. 14 * 2. Redistributions in binary form must reproduce the above copyright 15 * notice, this list of conditions and the following disclaimer in the 16 * documentation and/or other materials provided with the distribution. 17 * 3. All advertising materials mentioning features or use of this software 18 * must display the following acknowledgement: 19 * This product includes software developed by John Polstra. 20 * 4. The name of the author may not be used to endorse or promote products 21 * derived from this software without specific prior written permission. 22 * 23 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 24 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 25 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 26 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 27 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 28 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 29 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 30 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 31 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 32 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 33 */ 34 35 /* 36 * Dynamic linker for ELF. 37 * 38 * John Polstra <jdp (at) polstra.com>. 39 */ 40 41 #include <sys/cdefs.h> 42 #ifndef lint 43 __RCSID("$NetBSD: load.c,v 1.52 2026/07/07 14:10:23 riastradh Exp $"); 44 #endif /* not lint */ 45 46 #include <sys/types.h> 47 #include <sys/param.h> 48 #include <sys/mman.h> 49 #include <sys/sysctl.h> 50 #include <sys/stat.h> 51 52 #include <err.h> 53 #include <errno.h> 54 #include <fcntl.h> 55 #include <stdarg.h> 56 #include <stdio.h> 57 #include <stdlib.h> 58 #include <string.h> 59 #include <unistd.h> 60 #include <dirent.h> 61 62 #include "debug.h" 63 #include "rtld.h" 64 65 static bool _rtld_load_by_name(const char *, Obj_Entry *, Needed_Entry **, 66 int, sigset_t *); 67 68 #ifdef RTLD_LOADER 69 Objlist _rtld_list_main = /* Objects loaded at program startup */ 70 SIMPLEQ_HEAD_INITIALIZER(_rtld_list_main); 71 Objlist _rtld_list_global = /* Objects dlopened with RTLD_GLOBAL */ 72 SIMPLEQ_HEAD_INITIALIZER(_rtld_list_global); 73 74 void 75 _rtld_objlist_push_head(Objlist *list, Obj_Entry *obj) 76 { 77 Objlist_Entry *elm; 78 79 elm = NEW(Objlist_Entry); 80 elm->obj = obj; 81 SIMPLEQ_INSERT_HEAD(list, elm, link); 82 } 83 84 void 85 _rtld_objlist_push_tail(Objlist *list, Obj_Entry *obj) 86 { 87 Objlist_Entry *elm; 88 89 elm = NEW(Objlist_Entry); 90 elm->obj = obj; 91 SIMPLEQ_INSERT_TAIL(list, elm, link); 92 } 93 94 Objlist_Entry * 95 _rtld_objlist_find(Objlist *list, const Obj_Entry *obj) 96 { 97 Objlist_Entry *elm; 98 99 SIMPLEQ_FOREACH(elm, list, link) { 100 if (elm->obj == obj) 101 return elm; 102 } 103 return NULL; 104 } 105 #endif 106 107 /* 108 * Load a shared object into memory, if it is not already loaded. 109 * 110 * Returns a pointer to the Obj_Entry for the object. Returns NULL 111 * on failure. 112 */ 113 Obj_Entry * 114 _rtld_load_object(const char *filepath, int flags, sigset_t *mask) 115 { 116 Obj_Entry *obj; 117 int fd = -1; 118 struct stat sb; 119 size_t pathlen = strlen(filepath); 120 121 restart: 122 /* 123 * Search the list of objects for a matching path. If we find 124 * a match, but it's concurrently running destructors, wait for 125 * it with the rtld exclusive lock dropped and start over. 126 */ 127 for (obj = _rtld_objlist->next; obj != NULL; obj = obj->next) { 128 if (pathlen == obj->pathlen && !strcmp(obj->path, filepath)) { 129 if (__predict_false(_rtld_wait_for_fini(&obj, mask))) 130 goto restart; 131 assert(obj->refcount > 0); 132 break; 133 } 134 } 135 136 /* 137 * If we didn't find a match by pathname, open the file and check 138 * again by device and inode. This avoids false mismatches caused 139 * by multiple links or ".." in pathnames. 140 * 141 * To avoid a race, we open the file and use fstat() rather than 142 * using stat(). 143 */ 144 if (obj == NULL) { 145 if ((fd = open(filepath, O_RDONLY)) == -1) { 146 _rtld_error("Cannot open \"%s\"", filepath); 147 return NULL; 148 } 149 if (fstat(fd, &sb) == -1) { 150 _rtld_error("Cannot fstat \"%s\"", filepath); 151 close(fd); 152 return NULL; 153 } 154 for (obj = _rtld_objlist->next; obj != NULL; obj = obj->next) { 155 if (obj->ino == sb.st_ino && obj->dev == sb.st_dev) { 156 close(fd); 157 if (__predict_false(_rtld_wait_for_fini(&obj, 158 mask))) 159 goto restart; 160 assert(obj->refcount > 0); 161 break; 162 } 163 } 164 } 165 166 #ifdef RTLD_LOADER 167 if (pathlen == _rtld_objself.pathlen && 168 strcmp(_rtld_objself.path, filepath) == 0) { 169 close(fd); 170 assert(_rtld_objself.refcount > 0); 171 return &_rtld_objself; 172 } 173 #endif 174 175 if (obj == NULL) { /* First use of this object, so we must map it in */ 176 obj = _rtld_map_object(filepath, fd, &sb); 177 (void)close(fd); 178 if (obj == NULL) 179 return NULL; 180 _rtld_digest_dynamic(filepath, obj); 181 182 if (flags & _RTLD_DLOPEN) { 183 if (obj->z_noopen || (flags & _RTLD_NOLOAD)) { 184 dbg(("refusing to load non-loadable \"%s\"", 185 obj->path)); 186 _rtld_error("Cannot dlopen non-loadable %s", 187 obj->path); 188 munmap(obj->mapbase, obj->mapsize); 189 _rtld_obj_free(obj); 190 return OBJ_ERR; 191 } 192 } 193 194 *_rtld_objtail = obj; 195 _rtld_objtail = &obj->next; 196 _rtld_objgen++; 197 _rtld_objcount++; 198 _rtld_objloads++; 199 _rtld_objrelocpending++; 200 #ifdef RTLD_LOADER 201 _rtld_linkmap_add(obj); /* for the debugger */ 202 #endif 203 dbg((" %p .. %p: %s", obj->mapbase, 204 obj->mapbase + obj->mapsize - 1, obj->path)); 205 if (obj->textrel) 206 dbg((" WARNING: %s has impure text", obj->path)); 207 } else { 208 assert(obj->refcount > 0); 209 } 210 211 ++obj->refcount; 212 #ifdef RTLD_LOADER 213 if (flags & _RTLD_MAIN && !obj->mainref) { 214 obj->mainref = 1; 215 dbg(("adding %p (%s) to _rtld_list_main", obj, obj->path)); 216 _rtld_objlist_push_tail(&_rtld_list_main, obj); 217 } 218 if (flags & _RTLD_GLOBAL && !obj->globalref) { 219 obj->globalref = 1; 220 dbg(("adding %p (%s) to _rtld_list_global", obj, obj->path)); 221 _rtld_objlist_push_tail(&_rtld_list_global, obj); 222 } 223 #endif 224 assert(obj->refcount > 0); 225 return obj; 226 } 227 228 static bool 229 _rtld_load_by_name(const char *name, Obj_Entry *obj, Needed_Entry **needed, 230 int flags, sigset_t *mask) 231 { 232 Library_Xform *x = _rtld_xforms; 233 Obj_Entry *o; 234 size_t j; 235 ssize_t i; 236 bool got = false; 237 union { 238 int i; 239 u_quad_t q; 240 char s[16]; 241 } val; 242 243 /* 244 * Caller must hold a reference to prevent concurrent dlclose 245 * from unloading obj until we're done, even if we 246 * unlock/wait/relock to wait for a dependency that is being 247 * concurrently unloaded. 248 */ 249 assert(obj->neededrefcount); 250 251 dbg(("load by name %s %p", name, x)); 252 restart: 253 /* 254 * Search the list of objects for a matching path. If we find 255 * a match, but it's concurrently running destructors, wait for 256 * it with the rtld exclusive lock dropped and start over. 257 */ 258 for (o = _rtld_objlist->next; o != NULL; o = o->next) { 259 if (_rtld_object_match_name(o, name)) { 260 if (__predict_false(_rtld_wait_for_fini(&o, mask))) 261 goto restart; 262 assert(o->refcount > 0); 263 ++o->refcount; 264 (*needed)->obj = o; 265 return true; 266 } 267 } 268 269 for (; x; x = x->next) { 270 if (strcmp(x->name, name) != 0) 271 continue; 272 273 j = sizeof(val); 274 if ((i = _rtld_sysctl(x->ctlname, &val, &j)) == -1) { 275 xwarnx(_PATH_LD_HINTS ": invalid/unknown sysctl for %s (%d)", 276 name, errno); 277 break; 278 } 279 280 switch (i) { 281 case CTLTYPE_QUAD: 282 xsnprintf(val.s, sizeof(val.s), "%" PRIu64, val.q); 283 break; 284 case CTLTYPE_INT: 285 xsnprintf(val.s, sizeof(val.s), "%d", val.i); 286 break; 287 case CTLTYPE_STRING: 288 break; 289 default: 290 xwarnx("unsupported sysctl type %d", (int)i); 291 break; 292 } 293 294 dbg(("sysctl returns %s", val.s)); 295 296 for (i = 0; i < RTLD_MAX_ENTRY && x->entry[i].value != NULL; 297 i++) { 298 dbg(("entry %ld", (unsigned long)i)); 299 if (strcmp(x->entry[i].value, val.s) == 0) 300 break; 301 } 302 303 if (i == RTLD_MAX_ENTRY) { 304 xwarnx("sysctl value %s not found for lib%s", 305 val.s, name); 306 break; 307 } 308 309 for (j = 0; j < RTLD_MAX_LIBRARY && 310 x->entry[i].library[j] != NULL; j++) { 311 o = _rtld_load_library(x->entry[i].library[j], obj, 312 flags, mask); 313 if (o == NULL) { 314 xwarnx("could not load %s for %s", 315 x->entry[i].library[j], name); 316 continue; 317 } 318 assert(o->refcount > 0); 319 got = true; 320 if (j == 0) 321 (*needed)->obj = o; 322 else { 323 /* make a new one and put it in the chain */ 324 Needed_Entry *ne = xmalloc(sizeof(*ne)); 325 ne->name = (*needed)->name; 326 ne->obj = o; 327 ne->next = (*needed)->next; 328 (*needed)->next = ne; 329 *needed = ne; 330 } 331 332 } 333 334 } 335 336 if (got) 337 return true; 338 339 assert((*needed)->obj == NULL); 340 (*needed)->obj = _rtld_load_library(name, obj, flags, mask); 341 assert((*needed)->obj == NULL || (*needed)->obj->refcount > 0); 342 return ((*needed)->obj != NULL); 343 } 344 345 346 /* 347 * Given a shared object, traverse its list of needed objects, and load 348 * each of them. Returns 0 on success. Generates an error message and 349 * returns -1 on failure. 350 */ 351 int 352 _rtld_load_needed_objects(Obj_Entry *first, int flags, sigset_t *mask) 353 { 354 Obj_Entry *obj; 355 int status = 0; 356 357 for (obj = first; obj != NULL; obj = obj->next) { 358 Needed_Entry *needed; 359 360 /* 361 * If obj is already being unloaded, there is no need 362 * to load its dependencies, so just skip it. 363 */ 364 if (__predict_false(obj->refcount == 0)) 365 continue; 366 367 /* 368 * Prevent obj from being concurrently unloaded until 369 * we're done. We hold the rtld exclusive lock right 370 * now, but _rtld_load_by_name may drop it. 371 */ 372 _rtld_load_needed_enter(obj); 373 374 for (needed = obj->needed; needed != NULL; 375 needed = needed->next) { 376 const char *name = obj->strtab + needed->name; 377 #ifdef RTLD_LOADER 378 Obj_Entry *nobj; 379 #endif 380 if (__predict_false(needed->obj != NULL)) 381 continue; 382 if (!_rtld_load_by_name(name, obj, &needed, 383 flags & ~_RTLD_NOLOAD, mask)) 384 status = -1; /* FIXME - cleanup */ 385 #ifdef RTLD_LOADER 386 if (status == -1) 387 break; 388 389 if (flags & _RTLD_MAIN) 390 continue; 391 392 nobj = needed->obj; 393 if (nobj->z_nodelete && !obj->ref_nodel) { 394 dbg(("obj %s nodelete", nobj->path)); 395 _rtld_ref_dag(nobj); 396 nobj->ref_nodel = true; 397 } 398 #endif 399 } 400 401 /* 402 * Allow obj to be concurrently unloaded now that we're 403 * done loading its dependencies. 404 */ 405 _rtld_load_needed_exit(obj); 406 407 #ifdef RTLD_LOADER 408 if (status == -1) 409 break; 410 #endif 411 } 412 413 return status; 414 } 415 416 #ifdef RTLD_LOADER 417 int 418 _rtld_preload(const char *preload_path, sigset_t *mask) 419 { 420 const char *path; 421 char *cp, *buf; 422 int status = 0; 423 424 if (preload_path != NULL && *preload_path != '\0') { 425 cp = buf = xstrdup(preload_path); 426 while ((path = strsep(&cp, " :")) != NULL && status == 0) { 427 if (!_rtld_load_object(path, _RTLD_MAIN, mask)) 428 status = -1; 429 else 430 dbg((" preloaded \"%s\"", path)); 431 } 432 xfree(buf); 433 } 434 435 return status; 436 } 437 #endif 438