xref: /illumos-gate/usr/src/cmd/sgs/libld/common/ldlibs.c (revision b533f56bf95137d3de6666bd923e15ec373ea611)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 
22 /*
23  *	Copyright (c) 1988 AT&T
24  *	  All Rights Reserved
25  *
26  * Copyright (c) 1992, 2010, Oracle and/or its affiliates. All rights reserved.
27  */
28 /*
29  * Copyright (c) 2012, Joyent, Inc.  All rights reserved.
30  */
31 
32 /*
33  * Library processing
34  */
35 #include	<stdio.h>
36 #include	<unistd.h>
37 #include	<fcntl.h>
38 #include	<string.h>
39 #include	<limits.h>
40 #include	<errno.h>
41 #include	<debug.h>
42 #include	<sys/sysmacros.h>
43 #include	"msg.h"
44 #include	"_libld.h"
45 
46 /*
47  * Define a list index for "-L" processing.  By default, "-L" search paths are
48  * inserted at the beginning of the associated search list.  However, should a
49  * ";" be discovered in a LD_LIBRARY_PATH listing, then any new "-L" search
50  * paths are inserted following the ";".
51  */
52 static Aliste	Lidx = 0;
53 
54 /*
55  * Function to handle -YL and -YU substitutions in LIBPATH.  It's probably
56  * very unlikely that the link-editor will ever see this, as any use of these
57  * options is normally processed by the compiler driver first and the finished
58  * -YP string is sent to us.  The fact that these two options are not even
59  * documented anymore makes it even more unlikely this processing will occur.
60  */
61 static char *
compat_YL_YU(Ofl_desc * ofl,char * path,int index)62 compat_YL_YU(Ofl_desc *ofl, char *path, int index)
63 {
64 	if (index == YLDIR) {
65 		if (Llibdir) {
66 			/*
67 			 * User supplied "-YL,libdir", this is the pathname that
68 			 * corresponds for compatibility to -YL (as defined in
69 			 * sgs/include/paths.h)
70 			 */
71 			DBG_CALL(Dbg_libs_ylu(ofl->ofl_lml, Llibdir,
72 			    path, index));
73 			return (Llibdir);
74 		}
75 	} else if (index == YUDIR) {
76 		if (Ulibdir) {
77 			/*
78 			 * User supplied "-YU,libdir", this is the pathname that
79 			 * corresponds for compatibility to -YU (as defined in
80 			 * sgs/include/paths.h)
81 			 */
82 			DBG_CALL(Dbg_libs_ylu(ofl->ofl_lml, Ulibdir,
83 			    path, index));
84 			return (Ulibdir);
85 		}
86 	}
87 	return (path);
88 }
89 
90 static char *
process_lib_path(Ofl_desc * ofl,APlist ** apl,char * path,Boolean subsflag)91 process_lib_path(Ofl_desc *ofl, APlist **apl, char *path, Boolean subsflag)
92 {
93 	int	i;
94 	char	*cp;
95 	Boolean	seenflg = FALSE;
96 	char	*dot = (char *)MSG_ORIG(MSG_STR_DOT);
97 
98 	for (i = YLDIR; i; i++) {
99 		cp = strpbrk(path, MSG_ORIG(MSG_STR_PATHTOK));
100 		if (cp == NULL) {
101 			if (*path == '\0') {
102 				if (seenflg)
103 					if (aplist_append(apl, (subsflag ?
104 					    compat_YL_YU(ofl, dot, i) : dot),
105 					    AL_CNT_OFL_LIBDIRS) == NULL)
106 						return ((char *)S_ERROR);
107 
108 			} else if (aplist_append(apl, (subsflag ?
109 			    compat_YL_YU(ofl, path, i) : path),
110 			    AL_CNT_OFL_LIBDIRS) == NULL) {
111 				return ((char *)S_ERROR);
112 			}
113 			return (cp);
114 		}
115 
116 		if (*cp == ':') {
117 			*cp = '\0';
118 			if (cp == path) {
119 				if (aplist_append(apl, (subsflag ?
120 				    compat_YL_YU(ofl, dot, i) : dot),
121 				    AL_CNT_OFL_LIBDIRS) == NULL)
122 					return ((char *)S_ERROR);
123 
124 			} else if (aplist_append(apl, (subsflag ?
125 			    compat_YL_YU(ofl, path, i) : path),
126 			    AL_CNT_OFL_LIBDIRS) == NULL) {
127 				return ((char *)S_ERROR);
128 			}
129 			path = cp + 1;
130 			seenflg = TRUE;
131 			continue;
132 		}
133 
134 		/* case ";" */
135 
136 		if (cp != path) {
137 			if (aplist_append(apl, (subsflag ?
138 			    compat_YL_YU(ofl, path, i) : path),
139 			    AL_CNT_OFL_LIBDIRS) == NULL)
140 				return ((char *)S_ERROR);
141 		} else {
142 			if (seenflg)
143 				if (aplist_append(apl, (subsflag ?
144 				    compat_YL_YU(ofl, dot, i) : dot),
145 				    AL_CNT_OFL_LIBDIRS) == NULL)
146 					return ((char *)S_ERROR);
147 		}
148 		return (cp);
149 	}
150 	/* NOTREACHED */
151 	return (NULL);	/* keep gcc happy */
152 }
153 
154 /*
155  * adds the indicated path to those to be searched for libraries.
156  */
157 uintptr_t
ld_add_libdir(Ofl_desc * ofl,const char * path)158 ld_add_libdir(Ofl_desc *ofl, const char *path)
159 {
160 	if (aplist_insert(&ofl->ofl_ulibdirs, path,
161 	    AL_CNT_OFL_LIBDIRS, Lidx++) == NULL)
162 		return (S_ERROR);
163 
164 	/*
165 	 * As -l and -L options can be interspersed, print the library
166 	 * search paths each time a new path is added.
167 	 */
168 	DBG_CALL(Dbg_libs_update(ofl->ofl_lml, ofl->ofl_ulibdirs,
169 	    ofl->ofl_dlibdirs));
170 	return (1);
171 }
172 
173 /*
174  * Process a required library.  Combine the directory and filename, and then
175  * append either a `.so' or `.a' suffix and try opening the associated pathname.
176  */
177 static uintptr_t
find_lib_name(const char * dir,const char * file,Ofl_desc * ofl,Rej_desc * rej,ofl_flag_t flags)178 find_lib_name(const char *dir, const char *file, Ofl_desc *ofl, Rej_desc *rej,
179     ofl_flag_t flags)
180 {
181 	int		fd;
182 	size_t		dlen;
183 	char		*_path, path[PATH_MAX + 2];
184 	const char	*_dir = dir;
185 	uintptr_t	open_ret;
186 
187 	/*
188 	 * Determine the size of the directory.  The directory and filename are
189 	 * concatenated into the local buffer which is purposely larger than
190 	 * PATH_MAX.  Should a pathname be created that exceeds the system
191 	 * limit, the open() will catch it, and a suitable rejection message is
192 	 * saved.
193 	 */
194 	if ((dlen = strlen(dir)) == 0) {
195 		_dir = (char *)MSG_ORIG(MSG_STR_DOT);
196 		dlen = 1;
197 	}
198 	dlen++;
199 
200 	/*
201 	 * If we are in dynamic mode try and open the associated shared object.
202 	 */
203 	if (ofl->ofl_flags & FLG_OF_DYNLIBS) {
204 		(void) snprintf(path, (PATH_MAX + 2), MSG_ORIG(MSG_STR_LIB_SO),
205 		    _dir, file);
206 		DBG_CALL(Dbg_libs_l(ofl->ofl_lml, file, path));
207 		if ((fd = open(path, O_RDONLY)) != -1) {
208 
209 			if ((_path = libld_malloc(strlen(path) + 1)) == NULL)
210 				return (S_ERROR);
211 			(void) strcpy(_path, path);
212 
213 			open_ret = ld_process_open(_path, &_path[dlen], &fd,
214 			    ofl, FLG_IF_NEEDED, rej, NULL);
215 			if (fd != -1)
216 				(void) close(fd);
217 			if (open_ret != 0 && (flags & FLG_OF_ADEFLIB))
218 				ld_eprintf(ofl, ERR_WARNING,
219 				    MSG_INTL(MSG_ARG_ASSDEFLIB_FOUND), dir,
220 				    file);
221 			return (open_ret);
222 
223 		} else if (errno != ENOENT) {
224 			/*
225 			 * If the open() failed for anything other than the
226 			 * file not existing, record the error condition.
227 			 */
228 			rej->rej_type = SGS_REJ_STR;
229 			rej->rej_str = strerror(errno);
230 			rej->rej_name = strdup(path);
231 		}
232 	}
233 
234 	/*
235 	 * If we are not in dynamic mode, or a shared object could not be
236 	 * located, try and open the associated archive.
237 	 */
238 	(void) snprintf(path, (PATH_MAX + 2), MSG_ORIG(MSG_STR_LIB_A),
239 	    _dir, file);
240 	DBG_CALL(Dbg_libs_l(ofl->ofl_lml, file, path));
241 	if ((fd = open(path, O_RDONLY)) != -1) {
242 
243 		if ((_path = libld_malloc(strlen(path) + 1)) == NULL)
244 			return (S_ERROR);
245 		(void) strcpy(_path, path);
246 
247 		open_ret = ld_process_open(_path, &_path[dlen], &fd, ofl,
248 		    FLG_IF_NEEDED, rej, NULL);
249 		if (fd != -1)
250 			(void) close(fd);
251 		return (open_ret);
252 
253 	} else if (errno != ENOENT) {
254 		/*
255 		 * If the open() failed for anything other than the
256 		 * file not existing, record the error condition.
257 		 */
258 		rej->rej_type = SGS_REJ_STR;
259 		rej->rej_str = strerror(errno);
260 		rej->rej_name = strdup(path);
261 	}
262 
263 	return (0);
264 }
265 
266 /*
267  * Take the abbreviated name of a library file (from -lfoo) and searches for the
268  * library.  The search path rules are:
269  *
270  *	o	use any user supplied paths, i.e. LD_LIBRARY_PATH and -L, then
271  *
272  *	o	use the default directories, i.e. LIBPATH or -YP.
273  *
274  * If we are in dynamic mode and -Bstatic is not in effect, first look for a
275  * shared object with full name: path/libfoo.so; then [or else] look for an
276  * archive with name: path/libfoo.a.  If no file is found, it's a fatal error,
277  * otherwise process the file appropriately depending on its type.
278  *
279  * If we end up using the default directories and -z assert-deflib has been
280  * turned on, then we pass that information down into find_lib_name which will
281  * warn appropriately if we find a shared object.
282  */
283 uintptr_t
ld_find_library(const char * name,Ofl_desc * ofl)284 ld_find_library(const char *name, Ofl_desc *ofl)
285 {
286 	Aliste		idx;
287 	char		*path;
288 	uintptr_t	open_ret;
289 	Rej_desc	rej = { 0 };
290 	ofl_flag_t	flags = 0;
291 
292 	/*
293 	 * Search for this file in any user defined directories.
294 	 */
295 	for (APLIST_TRAVERSE(ofl->ofl_ulibdirs, idx, path)) {
296 		Rej_desc	_rej = { 0 };
297 
298 		if ((open_ret = find_lib_name(path, name, ofl, &_rej,
299 		    flags)) == 0) {
300 			if (_rej.rej_type && (rej.rej_type == 0))
301 				rej = _rej;
302 			continue;
303 		}
304 		return (open_ret);
305 	}
306 
307 	if (ofl->ofl_flags & FLG_OF_ADEFLIB) {
308 		flags |= FLG_OF_ADEFLIB;
309 		for (APLIST_TRAVERSE(ofl->ofl_assdeflib, idx, path)) {
310 			if (strncmp(name, path + MSG_STR_LIB_SIZE,
311 			    MAX(strlen(path + MSG_STR_LIB_SIZE) -
312 			    MSG_STR_SOEXT_SIZE, strlen(name))) == 0) {
313 				flags &= ~FLG_OF_ADEFLIB;
314 				break;
315 			}
316 		}
317 	}
318 
319 	/*
320 	 * Finally try the default library search directories.
321 	 */
322 	for (APLIST_TRAVERSE(ofl->ofl_dlibdirs, idx, path)) {
323 		Rej_desc	_rej = { 0 };
324 
325 		if ((open_ret = find_lib_name(path, name, ofl, &_rej,
326 		    flags)) == 0) {
327 			if (_rej.rej_type && (rej.rej_type == 0))
328 				rej = _rej;
329 			continue;
330 		}
331 
332 		return (open_ret);
333 	}
334 
335 	/*
336 	 * If we've got this far we haven't found a shared object or archive.
337 	 * If an object was found, but was rejected for some reason, print a
338 	 * diagnostic to that effect, otherwise generate a generic "not found"
339 	 * diagnostic.
340 	 */
341 	if (rej.rej_type) {
342 		Conv_reject_desc_buf_t rej_buf;
343 
344 		ld_eprintf(ofl, ERR_FATAL, MSG_INTL(reject[rej.rej_type]),
345 		    rej.rej_name ? rej.rej_name : MSG_INTL(MSG_STR_UNKNOWN),
346 		    conv_reject_desc(&rej, &rej_buf, ld_targ.t_m.m_mach));
347 	} else {
348 		ld_eprintf(ofl, ERR_FATAL, MSG_INTL(MSG_LIB_NOTFOUND), name);
349 	}
350 
351 	return (0);
352 }
353 
354 /*
355  * Inspect the LD_LIBRARY_PATH variable (if the -i options has not been
356  * specified), and set up the directory list from which to search for
357  * libraries.  From the man page:
358  *
359  *	LD_LIBRARY_PATH=dirlist1;dirlist2
360  * and
361  *	ld ... -Lpath1 ... -Lpathn ...
362  *
363  * results in a search order of:
364  *
365  *	dirlist1 path1 ... pathn dirlist2 LIBPATH
366  *
367  * If LD_LIBRARY_PATH has no `;' specified, the pathname(s) supplied are
368  * all taken as dirlist2.
369  */
370 uintptr_t
ld_lib_setup(Ofl_desc * ofl)371 ld_lib_setup(Ofl_desc *ofl)
372 {
373 	char	*path, *cp = NULL;
374 
375 	/*
376 	 * Determine whether an LD_LIBRARY_PATH setting is in effect.
377 	 */
378 	if (!(ofl->ofl_flags & FLG_OF_IGNENV)) {
379 #if	defined(_ELF64)
380 		if ((cp = getenv(MSG_ORIG(MSG_LD_LIBPATH_64))) == NULL)
381 #else
382 		if ((cp = getenv(MSG_ORIG(MSG_LD_LIBPATH_32))) == NULL)
383 #endif
384 			cp  = getenv(MSG_ORIG(MSG_LD_LIBPATH));
385 	}
386 
387 	if (cp && cp[0]) {
388 		if ((path = libld_malloc(strlen(cp) + 1)) == NULL)
389 			return (S_ERROR);
390 		(void) strcpy(path, cp);
391 		DBG_CALL(Dbg_libs_path(ofl->ofl_lml, path, LA_SER_DEFAULT, 0));
392 
393 		/*
394 		 * Process the first path string (anything up to a null or
395 		 * a `;');
396 		 */
397 		path = process_lib_path(ofl, &ofl->ofl_ulibdirs, path, FALSE);
398 
399 
400 		/*
401 		 * By default, -L paths are prepended to the library search
402 		 * path list, because Lidx == 0.  If a ';' is seen within an
403 		 * LD_LIBRARY_PATH string, change the insert index so that -L
404 		 * paths are added following the ';'.
405 		 */
406 		if (path) {
407 			Lidx = aplist_nitems(ofl->ofl_ulibdirs);
408 			*path = '\0';
409 			++path;
410 			cp = process_lib_path(ofl, &ofl->ofl_ulibdirs, path,
411 			    FALSE);
412 			if (cp == (char *)S_ERROR)
413 				return (S_ERROR);
414 			else if (cp)
415 				ld_eprintf(ofl, ERR_WARNING,
416 				    MSG_INTL(MSG_LIB_MALFORM));
417 		}
418 	}
419 
420 	/*
421 	 * Add the default LIBPATH or any -YP supplied path.
422 	 */
423 	DBG_CALL(Dbg_libs_yp(ofl->ofl_lml, Plibpath));
424 	cp = process_lib_path(ofl, &ofl->ofl_dlibdirs, Plibpath, TRUE);
425 	if (cp == (char *)S_ERROR)
426 		return (S_ERROR);
427 	else if (cp) {
428 		ld_eprintf(ofl, ERR_FATAL, MSG_INTL(MSG_LIB_BADYP));
429 		return (S_ERROR);
430 	}
431 	DBG_CALL(Dbg_libs_init(ofl->ofl_lml, ofl->ofl_ulibdirs,
432 	    ofl->ofl_dlibdirs));
433 	return (1);
434 }
435