search.c revision 1.1
1/*	$NetBSD: search.c,v 1.1 1996/12/16 20:38:05 cgd Exp $	*/
2
3/*
4 * Copyright 1996 Matt Thomas <matt@3am-software.com>
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 *    notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 *    notice, this list of conditions and the following disclaimer in the
14 *    documentation and/or other materials provided with the distribution.
15 * 3. All advertising materials mentioning features or use of this software
16 *    must display the following acknowledgement:
17 *      This product includes software developed by John Polstra.
18 * 4. The name of the author may not be used to endorse or promote products
19 *    derived from this software without specific prior written permission.
20 *
21 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
22 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
23 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
24 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
25 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
26 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
27 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
28 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
30 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31 */
32
33/*
34 * Dynamic linker for ELF.
35 *
36 * John Polstra <jdp@polstra.com>.
37 */
38
39#include <err.h>
40#include <errno.h>
41#include <fcntl.h>
42#include <stdarg.h>
43#include <stdio.h>
44#include <stdlib.h>
45#include <string.h>
46#include <unistd.h>
47#include <sys/types.h>
48#include <sys/mman.h>
49#include <sys/stat.h>
50#include <dirent.h>
51
52#include "debug.h"
53#include "rtld.h"
54
55#define CONCAT(x,y)     __CONCAT(x,y)
56#define ELFNAME(x)      CONCAT(elf,CONCAT(ELFSIZE,CONCAT(_,x)))
57#define ELFNAME2(x,y)   CONCAT(x,CONCAT(_elf,CONCAT(ELFSIZE,CONCAT(_,y))))
58#define ELFNAMEEND(x)   CONCAT(x,CONCAT(_elf,ELFSIZE))
59#define ELFDEFNNAME(x)  CONCAT(ELF,CONCAT(ELFSIZE,CONCAT(_,x)))
60
61/*
62 * Data declarations.
63 */
64
65typedef struct {
66    const char *si_name;
67    const char *si_best_name;
68    char *si_best_fullpath;
69    const Search_Path *si_best_path;
70    size_t si_namelen;
71    int si_desired_major;
72    int si_desired_minor;
73    int si_best_major;
74    int si_best_minor;
75    unsigned si_exact : 1;
76} Search_Info;
77
78typedef enum {
79    Search_FoundNothing,
80    Search_FoundLower,
81    Search_FoundHigher,
82    Search_FoundExact
83} Search_Result;
84
85static bool
86_rtld_check_library(
87    const Search_Path *sp,
88    const char *name,
89    size_t namelen,
90    char **fullpath_p)
91{
92    struct stat mystat;
93    char *fullpath;
94    Elf_Ehdr ehdr;
95    int fd;
96
97    fullpath = xmalloc(sp->sp_pathlen + 1 + namelen + 1);
98    strncpy(fullpath, sp->sp_path, sp->sp_pathlen);
99    fullpath[sp->sp_pathlen] = '/';
100    strcpy(&fullpath[sp->sp_pathlen + 1], name);
101
102    dbg("  Trying \"%s\"", fullpath);
103    if (stat(fullpath, &mystat) >= 0 && S_ISREG(mystat.st_mode)) {
104	if ((fd = open(fullpath, O_RDONLY)) >= 0) {
105	    if (read(fd, &ehdr, sizeof(ehdr)) != sizeof(ehdr))
106		goto lose;
107
108	    /* Elf_e_ident includes class */
109	    if (memcmp(Elf_e_ident, ehdr.e_ident, Elf_e_siz) != 0)
110		goto lose;
111
112	    switch (ehdr.e_machine) {
113	    ELFDEFNNAME(MACHDEP_ID_CASES)
114
115	    default:
116		goto lose;
117	    }
118
119	    if (ehdr.e_ident[Elf_ei_version] != Elf_ev_current ||
120	      ehdr.e_version != Elf_ev_current ||
121	      ehdr.e_ident[Elf_ei_data] != ELFDEFNNAME(MACHDEP_ENDIANNESS) ||
122	      ehdr.e_type != Elf_et_dyn)
123		goto lose;
124
125	    if (*fullpath_p != NULL)
126		free(*fullpath_p);
127	    *fullpath_p = fullpath;
128	    return true;
129
130lose:
131	    close(fd);
132	}
133    }
134
135    free(fullpath);
136    return false;
137}
138
139static Search_Result
140_rtld_search_directory(
141    const Search_Path *sp,
142    Search_Info *si)
143{
144    struct dirent *entry;
145    DIR *dirp;
146    Search_Result result = Search_FoundNothing;
147
148dbg("_rtld_search_directory");
149    if (sp->sp_path == NULL || sp->sp_path[0] == '\0')
150	return result;
151
152dbg("_rtld_search_directory 2");
153    if ((dirp = opendir(sp->sp_path)) == NULL) {
154	dbg("_rtld_search_directory 2.1");
155	return result;
156    }
157
158dbg("_rtld_search_directory 3");
159    while ((entry = readdir(dirp)) != NULL) {
160	long major = -1;
161	long minor = -1;
162	if (strncmp(entry->d_name, si->si_name, si->si_namelen))
163	    continue;
164	/*
165	 * We are matching libfoo.so only (no more info).  Only take
166	 * it as a last resort.
167	 */
168	if (si->si_exact) {
169	    if (strcmp(entry->d_name, si->si_name))
170		continue;
171#ifdef notyet
172	} else if (entry->d_namlen == si->si_namelen) {
173	    if (si->si_best_path != NULL || si->si_best_major != -1)
174		continue;
175#endif
176	} else {
177	    char *cp;
178	    /*
179	     * We expect (demand!) that it be of the form
180	     * "libfoo.so.<something>"
181	     */
182	    if (entry->d_name[si->si_namelen] != '.')
183		continue;
184	    /*
185	     * This file has a least a major number (well, maybe not if it
186	     * has a name of "libfoo.so." but treat that as equivalent to 0.
187	     * It had better match what we are looking for.
188	     */
189	    major = strtol(&entry->d_name[si->si_namelen+1], &cp, 10);
190	    if (major < 0 || (cp[0] != '\0' && cp[0] != '.')
191		    || &entry->d_name[si->si_namelen+1] == cp)
192		continue;
193	    if (cp[0] == '.') {
194		char *cp2;
195		minor = strtol(&cp[1], &cp2, 10);
196		if (minor < 0 || cp2[0] != '\0' || cp == cp2)
197		    continue;
198	    } else {
199		minor = 0;
200	    }
201	    if (major != si->si_desired_major || minor <= si->si_best_minor)
202		continue;
203	}
204	/*
205	 * We have a better candidate...
206	 */
207	if (!_rtld_check_library(sp, entry->d_name, entry->d_namlen,
208			   &si->si_best_fullpath))
209	    continue;
210
211	si->si_best_name = &si->si_best_fullpath[sp->sp_pathlen + 1];
212	si->si_best_major = major;
213	si->si_best_minor = minor;
214	si->si_best_path = sp;
215
216	if (si->si_exact || si->si_best_minor == si->si_desired_minor)
217	    result = Search_FoundExact;
218	else if (si->si_best_minor > si->si_desired_minor)
219	    result = Search_FoundHigher;
220	else
221	    result = Search_FoundLower;
222
223	/*
224	 * We were looking for, and found, an exact match.  We're done.
225	 */
226	if (si->si_exact)
227	    break;
228    }
229
230    dbg("found %s (%d.%d) match for %s (%d.%d) -> %s",
231	result == Search_FoundNothing ? "no"
232	: result == Search_FoundLower ? "lower"
233	: result == Search_FoundExact ? "exact" : "higher",
234	si->si_best_major, si->si_best_minor,
235	si->si_name,
236	si->si_desired_major, si->si_desired_minor,
237	si->si_best_fullpath ? si->si_best_fullpath : sp->sp_path);
238
239    closedir(dirp);
240    return result;
241}
242
243static char *
244_rtld_search_library_paths(
245    const char *name,
246    Search_Path *paths,
247    const Search_Path *rpaths)
248{
249    Search_Info info;
250    Search_Path *path;
251    const char *cp;
252    Search_Result result = Search_FoundNothing;
253
254    memset(&info, 0, sizeof(info));
255    info.si_name = name;
256    info.si_desired_major = -1;
257    info.si_desired_minor = -1;
258    info.si_best_major = -1;
259    info.si_best_minor = -1;
260
261    cp = strstr(name, ".so");
262    if (cp == NULL) {
263	info.si_exact = true;
264    } else {
265	cp += sizeof(".so") - 1;
266	info.si_namelen = cp - name;
267	if (cp[0] != '.') {
268	    info.si_exact = true;
269	} else {
270	    info.si_desired_major = atoi(&cp[1]);
271	    if ((cp = strchr(&cp[1], '.')) != NULL) {
272		info.si_desired_minor = atoi(&cp[1]);
273	    } else {
274		info.si_desired_minor = 0;
275	    }
276	}
277    }
278
279    if (rpaths != NULL && result < Search_FoundHigher) {	/* Exact? */
280	dbg("  checking rpaths..");
281	for (; rpaths != NULL; rpaths = rpaths->sp_next) {
282	    dbg("   in \"%s\"", rpaths->sp_path);
283	    result = _rtld_search_directory(rpaths, &info);
284	    if (result >= Search_FoundHigher)	/* Exact? */
285		break;
286	}
287    }
288    if (result < Search_FoundHigher) {	/* Exact? */
289	dbg("  checking default paths..");
290	for (path = paths; path != NULL; path = path->sp_next) {
291	    dbg("   in \"%s\"", path->sp_path);
292	    result = _rtld_search_directory(path, &info);
293	    if (result >= Search_FoundHigher)	/* Exact? */
294		break;
295	}
296    }
297
298    if (result >= Search_FoundHigher)
299	return info.si_best_fullpath;
300
301    if (info.si_best_fullpath != NULL)
302	free(info.si_best_fullpath);
303    return NULL;
304}
305
306/*
307 * Find the library with the given name, and return its full pathname.
308 * The returned string is dynamically allocated.  Generates an error
309 * message and returns NULL if the library cannot be found.
310 *
311 * If the second argument is non-NULL, then it refers to an already-
312 * loaded shared object, whose library search path will be searched.
313 */
314char *
315_rtld_find_library(
316    const char *name,
317    const Obj_Entry *refobj)
318{
319    char *pathname;
320
321    if (strchr(name, '/') != NULL) {	/* Hard coded pathname */
322	if (name[0] != '/' && !_rtld_trust) {
323	    _rtld_error("Absolute pathname required for shared object \"%s\"",
324			name);
325	    return NULL;
326	}
327#ifdef SVR4_LIBDIR
328	if (strncmp(name, SVR4_LIBDIR, SVR4_LIBDIRLEN) == 0
329	        && name[SVR4_LIBDIRLEN] == '/') {	/* In "/usr/lib" */
330	    /* Map hard-coded "/usr/lib" onto our ELF library directory. */
331	    pathname = xmalloc(strlen(name) + LIBDIRLEN - SVR4_LIBDIRLEN + 1);
332	    strcpy(pathname, LIBDIR);
333	    strcpy(pathname + LIBDIRLEN, name + SVR4_LIBDIRLEN);
334	    return pathname;
335	}
336#endif /* SVR4_LIBDIR */
337	return xstrdup(name);
338    }
339
340    dbg(" Searching for \"%s\" (%p)", name, refobj);
341
342    pathname = _rtld_search_library_paths(name, _rtld_paths,
343					  refobj ? refobj->rpaths : NULL);
344    if (pathname == NULL)
345	_rtld_error("Shared object \"%s\" not found", name);
346    return pathname;
347}
348