xref: /titanic_41/usr/src/cmd/fs.d/cachefs/cachefspack/elfrd.c (revision 70025d765b044c6d8594bb965a2247a61e991a99)
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, Version 1.0 only
6  * (the "License").  You may not use this file except in compliance
7  * with the License.
8  *
9  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10  * or http://www.opensolaris.org/os/licensing.
11  * See the License for the specific language governing permissions
12  * and limitations under the License.
13  *
14  * When distributing Covered Code, include this CDDL HEADER in each
15  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16  * If applicable, add the following below this CDDL HEADER, with the
17  * fields enclosed by brackets "[]" replaced with your own identifying
18  * information: Portions Copyright [yyyy] [name of copyright owner]
19  *
20  * CDDL HEADER END
21  */
22 /*
23  * Copyright (c) 1996-1997, by Sun Microsystems, Inc.
24  * All Rights Reserved.
25  */
26 
27 #pragma ident	"%Z%%M%	%I%	%E% SMI"
28 
29 #include <locale.h>
30 #include <sys/types.h>
31 #include <sys/stat.h>
32 #include <sys/param.h>
33 #include <stdio.h>
34 #include <fcntl.h>
35 #include <link.h>
36 #include <string.h>
37 #include <stdlib.h>
38 #include <dirent.h>
39 #include <search.h>
40 
41 #include "libelf.h"
42 #include "elfrd.h"
43 
44 extern int verbose;
45 extern char *mstrdup(const char *);
46 extern void *mmalloc(size_t size);
47 
48 /*
49  * Given the name of an executable and a function call the function for
50  * all shared objects needed to link the executable. The function will only
51  * be called once. A list of filenames for which the function has been called
52  * is maintained, this is used to exclude filenames.
53  */
54 void
55 process_executable(char *pathname, int (*func)(char *, char *, DIR *, int))
56 {
57 	struct sobj *get_share_obj(char *, struct libpath *, int);
58 	struct sobj *sop;
59 	struct sobj *psop;
60 
61 #ifdef DEBUG
62 	printf("process_executable: pathname = %s\n", pathname);
63 	fflush(stdout);
64 #endif /* debug */
65 	sop = get_share_obj(pathname, &libp_hd, GSO_ADDEXCLD);
66 #ifdef DEBUG
67 	printf("process_executable: sop = %x\n", sop);
68 	fflush(stdout);
69 #endif /* debug */
70 	if (verbose) {
71 		if ((int)sop < 0)  {
72 			fprintf(stderr,
73 			    gettext(
74 			    "cachefspack: unable to get shared objects - %s\n"),
75 			    pathname);
76 		}
77 	}
78 	if ((int)sop > 0) {
79 		while (sop->so_next != (struct sobj *)0) {
80 #ifdef DEBUG
81 			printf("process_executable: sop->so_name = %s\n",
82 			    sop->so_name);
83 			fflush(stdout);
84 #endif /* DEBUG */
85 			func_dir_path(sop->so_name, func);
86 
87 			psop = sop;
88 			sop = sop->so_next;
89 			free(psop->so_name);
90 			free(psop);
91 		}
92 	}
93 }
94 
95 /*
96  * Given the name of an executable, a list of directories to use in the
97  * library search and a list of library names to exclude, return all
98  * shared object needed by the executable.
99  *
100  * RETURNS: A pointer to a list of shared objects
101  */
102 struct sobj *
103 get_share_obj(char *fpath, struct libpath *libpath, int flag)
104 {
105 	static int name_cnt = 0;
106 	static struct sobj *so, *hd_so;
107 	static int depth = 0;
108 	static struct libpath *rpath, hd_rpath;
109 	int found_file = 0;
110 	int error;
111 	int fd;
112 	Elf *elfp;
113 	Elf32_Ehdr *Ehdr;
114 	Elf_Scn *scn;
115 	Elf32_Shdr *shdr;
116 	Elf32_Dyn *dyn;
117 	size_t	dynsz;
118 	char *name;
119 	void * get_scndata();
120 	struct sobj *alloc_sobj();
121 	struct sobj *add_so();
122 	char pathtmp[MAXPATHLEN];
123 	ENTRY hitem, *hitemp;
124 	int fileopn;
125 	int elfbgn = 0;
126 	Elf_Kind file_type;
127 	int buf;
128 
129 	/*
130 	 * Open a file and perform necessary operations to find the sections
131 	 * in an elf format file. If the specified file is not elf format
132 	 * return an error.
133 	 */
134 	depth++;
135 	if (depth == 1) {
136 		/*
137 		 * Find the ending exclude shared object element.
138 		 */
139 		rpath = &hd_rpath;
140 #ifdef DEBUG
141 		printf("dbg: rpath = %x\n", rpath);
142 #endif /* DEBUG */
143 		rpath->lp_path = " ";
144 		rpath->lp_next = (struct libpath *)0;
145 		rpath->lp_level = 0;
146 	}
147 
148 	fileopn = 0;
149 	error = ERR_NOERROR;
150 	fd = open(fpath, O_RDONLY);
151 	if (fd < 0) {
152 		error = ERR_NOFILE;
153 		goto out;
154 	}
155 	fileopn = 1;
156 /* work around */
157 /*
158  * elf_begin() core dumps when passed a file descriptor for a file
159  * which does not have read permission, but was opened RDONLY because the
160  * user doing the open was root. To avoid this problem, make sure we can
161  * read the first byte of the file. If we can't, skip the file. This is a
162  * temporary workaround until elf_begin() is fixed.
163  */
164 	if (read(fd, &buf, sizeof (buf)) < 0) {
165 #ifdef DEBUG
166 		printf("read failed\n");
167 		fflush(stdout);
168 #endif /* DEBUG */
169 		error = ERR_NOFILE;
170 		goto out;
171 	}
172 	lseek(fd, 0, SEEK_SET);
173 /* work around end */
174 	if (elf_version(EV_CURRENT) == EV_NONE) {
175 		error = ERR_NOELFVER;
176 		goto out;
177 	}
178 	elfbgn = 0;
179 	if ((elfp = elf_begin(fd,  ELF_C_READ, (Elf *)0)) == NULL) {
180 		error = ERR_NOELFBEG;
181 		goto out;
182 	}
183 	elfbgn = 1;
184 	file_type = elf_kind(elfp);
185 #ifdef DEBUG
186 	printf("file_type = %x\n", file_type);
187 	fflush(stdout);
188 #endif /* DEBUG */
189 
190 	if (file_type != ELF_K_ELF) {
191 		goto out;
192 	}
193 	if ((Ehdr = elf32_getehdr(elfp)) == NULL) {
194 		error = ERR_NOELFBEG;
195 		goto out;
196 	}
197 #ifdef DEBUG
198 	printf("dbg: depth = %d\n", depth);
199 #endif /* DEBUG */
200 	/*
201 	 * Scan all sections of the elf file to locate the dynamic section
202 	 */
203 	scn = 0;
204 	while ((scn = elf_nextscn(elfp, scn)) != 0) {
205 		if ((shdr = elf32_getshdr(scn)) == NULL) {
206 			error = ERR_NOELFSHD;
207 			goto out;
208 		}
209 		if (shdr->sh_type != SHT_DYNAMIC) {
210 			continue;
211 		}
212 		/*
213 		 * The first pass of the dynamic section locates all
214 		 * directories specified by "ld -R..". A stack is created
215 		 * for the search, this allows shared libraries, dependant
216 		 * on other shared libraries, built with "ld -R" to work
217 		 * properly.
218 		 *
219 		 */
220 		if ((dyn = (Elf32_Dyn *)get_scndata(scn, &dynsz)) == 0) {
221 			error = ERR_NOELFSDT;
222 			goto out;
223 		}
224 		while (dyn->d_tag != DT_NULL) {
225 			if (dyn->d_tag == DT_RPATH) {
226 				name = (char *)elf_strptr(elfp,
227 				    (size_t)shdr->sh_link, dyn->d_un.d_ptr);
228 #ifdef DEBUG
229 				printf("DT_RPATH: name = %s\n", name);
230 #endif /* DEBUG */
231 				rpath = stk_libpath(rpath, name, depth);
232 			}
233 			dyn++;
234 		}
235 		/*
236 		 * Find all needed shared objects. Do this recursively
237 		 * so libraries dependant on other libraries are found.
238 		 * Also, try a list of libraries to exclude. Since
239 		 * this routine is used by cachefspack, it is only neccessary
240 		 * to pack a library once. For example, libc is used by lots
241 		 * of commands, we need not return its name to cachefspack
242 		 * except the first time we find it.
243 		 */
244 		if ((dyn = (Elf32_Dyn *)get_scndata(scn, &dynsz)) == 0) {
245 			error = ERR_NOELFSDT;
246 			goto out;
247 		}
248 		for (; dyn->d_tag != DT_NULL; dyn++) {
249 			if (dyn->d_tag == DT_NEEDED) {
250 				name = (char *)elf_strptr(elfp,
251 				    (size_t)shdr->sh_link, dyn->d_un.d_ptr);
252 				if (name != 0) {
253 #ifdef DEBUG
254 					printf("chk: %s\n", name);
255 					fflush(stdout);
256 #endif /* DEBUG */
257 					found_file = libsrch(name, libpath,
258 					    pathtmp);
259 #ifdef DEBUG
260 					printf("dbg:  1 found_file = %d\n",
261 					    found_file);
262 					fflush(stdout);
263 #endif /* DEBUG */
264 					if (!found_file) {
265 						found_file = libsrch(name,
266 						    rpath, pathtmp);
267 					}
268 #ifdef DEBUG
269 					printf("dbg:  2 found_file = %d\n",
270 					    found_file);
271 					fflush(stdout);
272 #endif /* DEBUG */
273 					if (!found_file) {
274 						continue;
275 					}
276 					if (name_cnt == 0) {
277 						so = alloc_sobj();
278 						hd_so = so;
279 					}
280 					/*
281 					 * See if file already in list
282 					 */
283 					hitem.key = mstrdup(pathtmp);
284 					hitem.data = 0;
285 					hitemp = hsearch(hitem, FIND);
286 					if (hitemp != NULL) {
287 #ifdef DEBUG
288 						printf("found so: %s\n",
289 						    pathtmp);
290 						printf("hitemp.key = %s\n",
291 						    hitemp->key);
292 #endif /* DEBUG */
293 						continue;
294 					}
295 #ifdef DEBUG
296 					printf("do : %s\n", pathtmp);
297 					fflush(stdout);
298 #endif /* DEBUG */
299 					name_cnt++;
300 					so = add_so(so, pathtmp);
301 					if (flag & GSO_ADDEXCLD) {
302 #ifdef DEBUG
303 						printf("adding so: %s\n",
304 						    pathtmp);
305 #endif /* DEBUG */
306 						hitem.key = mstrdup(pathtmp);
307 						hitem.data = 0;
308 						if (hsearch(hitem, ENTER) ==
309 						    NULL) {
310 							error = ERR_HASHFULL;
311 							goto out;
312 						}
313 					}
314 					get_share_obj(pathtmp, libpath, flag);
315 				} else {
316 					if (name_cnt > 0) {
317 						goto out;
318 					} else {
319 						error = ERR_NOELFNAM;
320 						goto out;
321 					}
322 				}
323 			}
324 		}
325 	}
326 
327 out:
328 #ifdef DEBUG
329 	printf("error = %x\n", error);
330 	fflush(stdout);
331 #endif /* DEBUG */
332 	depth--;
333 #ifdef DEBUG
334 	printf("ret: depth = %d\n", depth);
335 	fflush(stdout);
336 #endif /* DEBUG */
337 	if (fileopn) {
338 		close(fd);
339 		if (elfbgn) {
340 			if ((error != ERR_NOFILE) && (error != ERR_NOELFVER)) {
341 				elf_end(elfp);
342 			}
343 		}
344 	}
345 	if (name_cnt == 0) {
346 		return ((struct sobj *)ERR_NOERROR);
347 	}
348 	while (rpath->lp_level > depth) {
349 #ifdef DEBUG
350 		printf("ret: rpath->lp_level = %d\n", rpath->lp_level);
351 		fflush(stdout);
352 #endif /* DEBUG */
353 		rpath = pop_libpath(rpath);
354 	}
355 	if (depth == 0) {
356 		name_cnt = 0;
357 	}
358 	if (error == ERR_NOERROR) {
359 		return (hd_so);
360 	} else {
361 		return ((struct sobj *)error);
362 	}
363 }
364 
365 
366 /*
367  * Get the section descriptor and set the size of the
368  * data returned.  Data is byte-order converted.
369  */
370 
371 void *
372 get_scndata(fd_scn, size)
373 Elf_Scn *fd_scn;
374 size_t    *size;
375 {
376 	Elf_Data *p_data;
377 
378 	p_data = 0;
379 	if ((p_data = elf_getdata(fd_scn, p_data)) == 0 ||
380 		p_data->d_size == 0)
381 	{
382 		return (NULL);
383 	}
384 
385 	*size = p_data->d_size;
386 	return (p_data->d_buf);
387 }
388 
389 /*
390  * Allocate a shared object structure
391  *
392  * RETURNS: A pointer to the allocated structure
393  */
394 struct sobj *
395 alloc_sobj()
396 {
397 	struct sobj *so;
398 	so = (struct sobj *)mmalloc(sizeof (struct sobj));
399 	so->so_name = " ";
400 	so->so_next = (struct sobj *)0;
401 	return (so);
402 }
403 
404 
405 /*
406  * Add an object to a shared object list
407  *
408  * RETURNS: The tail of the shared object list
409  */
410 struct sobj *
411 add_so(struct sobj *so, char *path)
412 {
413 	if (so == (struct sobj *)0) {
414 		so = alloc_sobj();
415 	}
416 	so->so_name = mstrdup(path);
417 	so->so_next = alloc_sobj();
418 	so = so->so_next;
419 	return (so);
420 }
421 
422 /*
423  * Determine if name concatenated with a library directory path yields
424  * a file name that exists.
425  *
426  * RETURNS: True(1) or False(0)
427  *	    if true - fullpath arg contains a pointer to the full path name
428  *			of the file
429  */
430 int
431 libsrch(char *name, struct libpath *libpath, char *fullpath)
432 {
433 	struct stat64 statbuf;
434 	struct libpath *lp;
435 
436 #ifdef DEBUG
437 	printf("libsrch: libpath = %x\n", libpath);
438 	fflush(stdout);
439 #endif /* DEBUG */
440 	lp = libpath;
441 	if (lp == NULL) {
442 		return (0);
443 	}
444 #ifdef DEBUG
445 	printf("libsrch: 1 lp->lp_next = %x\n", lp->lp_next);
446 	fflush(stdout);
447 #endif /* DEBUG */
448 	while (lp->lp_next != (struct libpath *)0) {
449 		strcpy(fullpath, lp->lp_path);
450 		strcat(fullpath, "/");
451 		strcat(fullpath, name);
452 		lp = lp->lp_next;
453 #ifdef DEBUG
454 		printf("libsrch: 2 lp->lp_next = %x\n", lp->lp_next);
455 		fflush(stdout);
456 #endif /* DEBUG */
457 		/*
458 		 * stat - if file break
459 		 */
460 		if (stat64(fullpath, &statbuf)
461 		    == 0) {
462 #ifdef DEBUG
463 			printf("libsrch: found - %s\n", fullpath);
464 			fflush(stdout);
465 #endif /* DEBUG */
466 			return (1);
467 		}
468 	}
469 #ifdef DEBUG
470 	printf("libsrch: NOT found - %s\n", name);
471 	fflush(stdout);
472 #endif /* DEBUG */
473 	return (0);
474 }
475 
476 /*
477  * Add path to the libpath list(add at the tail of the list).
478  *
479  * RETURNS: The new tail of the list
480  */
481 struct libpath *
482 add_libpath(struct libpath *lp, char *path, int level)
483 {
484 	char *s;
485 
486 	lp->lp_level = level;
487 	s = mstrdup(path);
488 	if (s != (char *)0) {
489 		lp->lp_path = s;
490 	}
491 	lp->lp_next = (struct libpath *)mmalloc(sizeof (struct libpath));
492 	lp = lp->lp_next;
493 	lp->lp_next = (struct libpath *)0;
494 	lp->lp_level = 0;
495 	lp->lp_path = " ";
496 	return (lp);
497 }
498 
499 /*
500  * Add directory/directories in name to libpath stack(as head of the stack)
501  * at the level specified.
502  *
503  * RETURNS: the new head of the stack
504  */
505 struct libpath *
506 stk_libpath(struct libpath *hd, char *name, int level)
507 {
508 	struct libpath *lp, *prev_lp;
509 	char *s, *t;
510 	char *tok;
511 	char *freeit;
512 
513 #ifdef DEBUG
514 	printf("stk_libpath: name = %s\n", name);
515 	fflush(stdout);
516 #endif /* DEBUG */
517 	s = mstrdup(name);
518 	freeit = s;
519 	prev_lp = hd;
520 	while (1) {
521 		tok = strtok(s, ":");
522 		if (tok == (char *)NULL)
523 		    break;
524 		s = (char *)0;
525 		lp = (struct libpath *)mmalloc(sizeof (struct libpath));
526 		lp->lp_level = level;
527 		t = mstrdup(tok);
528 		lp->lp_path = t;
529 		lp->lp_next = prev_lp;
530 		prev_lp = lp;
531 	}
532 #ifdef DEBUG
533 	printf("stk_libpath: lp = %x\n", lp);
534 	fflush(stdout);
535 #endif /* DEBUG */
536 	free(freeit);
537 	return (lp);
538 }
539 
540 /*
541  * Free up a libpath stack entry.
542  *
543  * RETURNS: the new head of the stack
544  */
545 struct libpath *
546 pop_libpath(struct libpath *lp)
547 {
548 	struct libpath *tlp;
549 
550 	tlp = lp;
551 	lp = lp->lp_next;
552 	free(tlp->lp_path);
553 	free(tlp);
554 	return (lp);
555 }
556 
557 /*
558  * Crack the LD_LIBRARY_PATH environment variable. Make a list of libraries
559  * to search.
560  */
561 void
562 get_libsrch_path(struct libpath *libhd)
563 {
564 	char *s;
565 	char *tok = (char *) 1;
566 	struct libpath *lp;
567 
568 	lp = libhd;
569 	s = getenv("LD_LIBRARY_PATH");
570 	if (s != (char *)NULL) {
571 		while (1) {
572 			tok = strtok(s, ":");
573 			if (tok == (char *) NULL)
574 				break;
575 			s = (char *) 0;
576 			lp = add_libpath(lp, tok, 0);
577 		}
578 	}
579 	add_libpath(lp, "/usr/lib", 0);
580 }
581 
582 
583 #ifdef DEBUG
584 prt_sop_lst(struct sobj *sop, char * str)
585 {
586 	printf("\n\n\n%s - sop = %x\n", str, sop);
587 	fflush(stdout);
588 	if ((int)sop < 0)  {
589 		fprintf(stderr, "get_share_obj: failed\n");
590 		exit(1);
591 	}
592 
593 	if ((int)sop > 0) {
594 		while (sop->so_next != (struct sobj *) 0) {
595 			printf("sop->so_name = %s\n", sop->so_name);
596 			sop = sop->so_next;
597 		}
598 	}
599 }
600 #endif /* DEBUG */
601