1 /* $NetBSD: load.c,v 1.49 2020/09/21 16:08:57 kamil 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.49 2020/09/21 16:08:57 kamil 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); 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) 115 { 116 Obj_Entry *obj; 117 int fd = -1; 118 struct stat sb; 119 size_t pathlen = strlen(filepath); 120 121 for (obj = _rtld_objlist->next; obj != NULL; obj = obj->next) 122 if (pathlen == obj->pathlen && !strcmp(obj->path, filepath)) 123 break; 124 125 /* 126 * If we didn't find a match by pathname, open the file and check 127 * again by device and inode. This avoids false mismatches caused 128 * by multiple links or ".." in pathnames. 129 * 130 * To avoid a race, we open the file and use fstat() rather than 131 * using stat(). 132 */ 133 if (obj == NULL) { 134 if ((fd = open(filepath, O_RDONLY)) == -1) { 135 _rtld_error("Cannot open \"%s\"", filepath); 136 return NULL; 137 } 138 if (fstat(fd, &sb) == -1) { 139 _rtld_error("Cannot fstat \"%s\"", filepath); 140 close(fd); 141 return NULL; 142 } 143 for (obj = _rtld_objlist->next; obj != NULL; obj = obj->next) { 144 if (obj->ino == sb.st_ino && obj->dev == sb.st_dev) { 145 close(fd); 146 break; 147 } 148 } 149 } 150 151 #ifdef RTLD_LOADER 152 if (pathlen == _rtld_objself.pathlen && 153 strcmp(_rtld_objself.path, filepath) == 0) { 154 close(fd); 155 return &_rtld_objself; 156 } 157 #endif 158 159 if (obj == NULL) { /* First use of this object, so we must map it in */ 160 obj = _rtld_map_object(filepath, fd, &sb); 161 (void)close(fd); 162 if (obj == NULL) 163 return NULL; 164 _rtld_digest_dynamic(filepath, obj); 165 166 if (flags & _RTLD_DLOPEN) { 167 if (obj->z_noopen || (flags & _RTLD_NOLOAD)) { 168 dbg(("refusing to load non-loadable \"%s\"", 169 obj->path)); 170 _rtld_error("Cannot dlopen non-loadable %s", 171 obj->path); 172 munmap(obj->mapbase, obj->mapsize); 173 _rtld_obj_free(obj); 174 return OBJ_ERR; 175 } 176 } 177 178 *_rtld_objtail = obj; 179 _rtld_objtail = &obj->next; 180 _rtld_objcount++; 181 _rtld_objloads++; 182 #ifdef RTLD_LOADER 183 _rtld_linkmap_add(obj); /* for the debugger */ 184 #endif 185 dbg((" %p .. %p: %s", obj->mapbase, 186 obj->mapbase + obj->mapsize - 1, obj->path)); 187 if (obj->textrel) 188 dbg((" WARNING: %s has impure text", obj->path)); 189 } 190 191 ++obj->refcount; 192 #ifdef RTLD_LOADER 193 if (flags & _RTLD_MAIN && !obj->mainref) { 194 obj->mainref = 1; 195 dbg(("adding %p (%s) to _rtld_list_main", obj, obj->path)); 196 _rtld_objlist_push_tail(&_rtld_list_main, obj); 197 } 198 if (flags & _RTLD_GLOBAL && !obj->globalref) { 199 obj->globalref = 1; 200 dbg(("adding %p (%s) to _rtld_list_global", obj, obj->path)); 201 _rtld_objlist_push_tail(&_rtld_list_global, obj); 202 } 203 #endif 204 return obj; 205 } 206 207 static bool 208 _rtld_load_by_name(const char *name, Obj_Entry *obj, Needed_Entry **needed, 209 int flags) 210 { 211 Library_Xform *x = _rtld_xforms; 212 Obj_Entry *o; 213 size_t j; 214 ssize_t i; 215 bool got = false; 216 union { 217 int i; 218 u_quad_t q; 219 char s[16]; 220 } val; 221 222 dbg(("load by name %s %p", name, x)); 223 for (o = _rtld_objlist->next; o != NULL; o = o->next) 224 if (_rtld_object_match_name(o, name)) { 225 ++o->refcount; 226 (*needed)->obj = o; 227 return true; 228 } 229 230 for (; x; x = x->next) { 231 if (strcmp(x->name, name) != 0) 232 continue; 233 234 j = sizeof(val); 235 if ((i = _rtld_sysctl(x->ctlname, &val, &j)) == -1) { 236 xwarnx(_PATH_LD_HINTS ": invalid/unknown sysctl for %s (%d)", 237 name, errno); 238 break; 239 } 240 241 switch (i) { 242 case CTLTYPE_QUAD: 243 xsnprintf(val.s, sizeof(val.s), "%" PRIu64, val.q); 244 break; 245 case CTLTYPE_INT: 246 xsnprintf(val.s, sizeof(val.s), "%d", val.i); 247 break; 248 case CTLTYPE_STRING: 249 break; 250 default: 251 xwarnx("unsupported sysctl type %d", (int)i); 252 break; 253 } 254 255 dbg(("sysctl returns %s", val.s)); 256 257 for (i = 0; i < RTLD_MAX_ENTRY && x->entry[i].value != NULL; 258 i++) { 259 dbg(("entry %ld", (unsigned long)i)); 260 if (strcmp(x->entry[i].value, val.s) == 0) 261 break; 262 } 263 264 if (i == RTLD_MAX_ENTRY) { 265 xwarnx("sysctl value %s not found for lib%s", 266 val.s, name); 267 break; 268 } 269 270 for (j = 0; j < RTLD_MAX_LIBRARY && 271 x->entry[i].library[j] != NULL; j++) { 272 o = _rtld_load_library(x->entry[i].library[j], obj, 273 flags); 274 if (o == NULL) { 275 xwarnx("could not load %s for %s", 276 x->entry[i].library[j], name); 277 continue; 278 } 279 got = true; 280 if (j == 0) 281 (*needed)->obj = o; 282 else { 283 /* make a new one and put it in the chain */ 284 Needed_Entry *ne = xmalloc(sizeof(*ne)); 285 ne->name = (*needed)->name; 286 ne->obj = o; 287 ne->next = (*needed)->next; 288 (*needed)->next = ne; 289 *needed = ne; 290 } 291 292 } 293 294 } 295 296 if (got) 297 return true; 298 299 return ((*needed)->obj = _rtld_load_library(name, obj, flags)) != NULL; 300 } 301 302 303 /* 304 * Given a shared object, traverse its list of needed objects, and load 305 * each of them. Returns 0 on success. Generates an error message and 306 * returns -1 on failure. 307 */ 308 int 309 _rtld_load_needed_objects(Obj_Entry *first, int flags) 310 { 311 Obj_Entry *obj; 312 int status = 0; 313 314 for (obj = first; obj != NULL; obj = obj->next) { 315 Needed_Entry *needed; 316 317 for (needed = obj->needed; needed != NULL; 318 needed = needed->next) { 319 const char *name = obj->strtab + needed->name; 320 #ifdef RTLD_LOADER 321 Obj_Entry *nobj; 322 #endif 323 if (!_rtld_load_by_name(name, obj, &needed, 324 flags & ~_RTLD_NOLOAD)) 325 status = -1; /* FIXME - cleanup */ 326 #ifdef RTLD_LOADER 327 if (status == -1) 328 return status; 329 330 if (flags & _RTLD_MAIN) 331 continue; 332 333 nobj = needed->obj; 334 if (nobj->z_nodelete && !obj->ref_nodel) { 335 dbg(("obj %s nodelete", nobj->path)); 336 _rtld_ref_dag(nobj); 337 nobj->ref_nodel = true; 338 } 339 #endif 340 } 341 } 342 343 return status; 344 } 345 346 #ifdef RTLD_LOADER 347 int 348 _rtld_preload(const char *preload_path) 349 { 350 const char *path; 351 char *cp, *buf; 352 int status = 0; 353 354 if (preload_path != NULL && *preload_path != '\0') { 355 cp = buf = xstrdup(preload_path); 356 while ((path = strsep(&cp, " :")) != NULL && status == 0) { 357 if (!_rtld_load_object(path, _RTLD_MAIN)) 358 status = -1; 359 else 360 dbg((" preloaded \"%s\"", path)); 361 } 362 xfree(buf); 363 } 364 365 return status; 366 } 367 #endif 368