xref: /freebsd/usr.bin/ldd/ldd.c (revision f1f890804985a1043da42a5def13c79dc005f5e9)
1 /*
2  * Copyright (c) 1993 Paul Kranenburg
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  * 3. All advertising materials mentioning features or use of this software
14  *    must display the following acknowledgement:
15  *      This product includes software developed by Paul Kranenburg.
16  * 4. The name of the author may not be used to endorse or promote products
17  *    derived from this software without specific prior written permission
18  *
19  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
20  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
21  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
22  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
23  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
24  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
28  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29  */
30 
31 #include <sys/cdefs.h>
32 __FBSDID("$FreeBSD$");
33 
34 #include <sys/wait.h>
35 
36 #include <machine/elf.h>
37 
38 #include <arpa/inet.h>
39 
40 #include <a.out.h>
41 #include <dlfcn.h>
42 #include <err.h>
43 #include <errno.h>
44 #include <fcntl.h>
45 #include <stdio.h>
46 #include <stdlib.h>
47 #include <string.h>
48 #include <unistd.h>
49 
50 #include "extern.h"
51 
52 #ifdef COMPAT_32BIT
53 #define	LD_	"LD_32_"
54 #else
55 #define	LD_	"LD_"
56 #endif
57 
58 /*
59  * 32-bit ELF data structures can only be used if the system header[s] declare
60  * them.  There is no official macro for determining whether they are declared,
61  * so check for the existence of one of the 32-macros defined in elf(5).
62  */
63 #ifdef ELF32_R_TYPE
64 #define	ELF32_SUPPORTED
65 #endif
66 
67 static int	is_executable(const char *fname, int fd, int *is_shlib,
68 		    int *type);
69 static void	usage(void);
70 
71 #define	TYPE_UNKNOWN	0
72 #define	TYPE_AOUT	1
73 #define	TYPE_ELF	2	/* Architecture default */
74 #if __ELF_WORD_SIZE > 32 && defined(ELF32_SUPPORTED)
75 #define	TYPE_ELF32	3	/* Explicit 32 bits on architectures >32 bits */
76 
77 #define	_PATH_LDD32	"/usr/bin/ldd32"
78 
79 static int
80 execldd32(char *file, char *fmt1, char *fmt2, int aflag, int vflag)
81 {
82 	char *argv[8];
83 	int i, rval, status;
84 
85 	unsetenv(LD_ "TRACE_LOADED_OBJECTS");
86 	rval = 0;
87 	i = 0;
88 	argv[i++] = strdup(_PATH_LDD32);
89 	if (aflag)
90 		argv[i++] = strdup("-a");
91 	if (vflag)
92 		argv[i++] = strdup("-v");
93 	if (fmt1 != NULL) {
94 		argv[i++] = strdup("-f");
95 		argv[i++] = strdup(fmt1);
96 	}
97 	if (fmt2 != NULL) {
98 		argv[i++] = strdup("-f");
99 		argv[i++] = strdup(fmt2);
100 	}
101 	argv[i++] = strdup(file);
102 	argv[i++] = NULL;
103 
104 	switch (fork()) {
105 	case -1:
106 		err(1, "fork");
107 		break;
108 	case 0:
109 		execv(_PATH_LDD32, argv);
110 		warn("%s", _PATH_LDD32);
111 		_exit(127);
112 		break;
113 	default:
114 		if (wait(&status) < 0)
115 			rval = 1;
116 		else if (WIFSIGNALED(status))
117 			rval = 1;
118 		else if (WIFEXITED(status) && WEXITSTATUS(status) != 0)
119 			rval = 1;
120 		break;
121 	}
122 	while (i--)
123 		free(argv[i]);
124 	setenv(LD_ "TRACE_LOADED_OBJECTS", "yes", 1);
125 	return (rval);
126 }
127 #endif
128 
129 int
130 main(int argc, char *argv[])
131 {
132 	char *fmt1, *fmt2;
133 	int rval, c, aflag, vflag;
134 
135 	aflag = vflag = 0;
136 	fmt1 = fmt2 = NULL;
137 
138 	while ((c = getopt(argc, argv, "af:v")) != -1) {
139 		switch (c) {
140 		case 'a':
141 			aflag++;
142 			break;
143 		case 'f':
144 			if (fmt1 != NULL) {
145 				if (fmt2 != NULL)
146 					errx(1, "too many formats");
147 				fmt2 = optarg;
148 			} else
149 				fmt1 = optarg;
150 			break;
151 		case 'v':
152 			vflag++;
153 			break;
154 		default:
155 			usage();
156 			/* NOTREACHED */
157 		}
158 	}
159 	argc -= optind;
160 	argv += optind;
161 
162 	if (vflag && fmt1 != NULL)
163 		errx(1, "-v may not be used with -f");
164 
165 	if (argc <= 0) {
166 		usage();
167 		/* NOTREACHED */
168 	}
169 
170 #ifdef __i386__
171 	if (vflag) {
172 		for (c = 0; c < argc; c++)
173 			dump_file(argv[c]);
174 		exit(error_count == 0 ? EXIT_SUCCESS : EXIT_FAILURE);
175 	}
176 #endif
177 
178 	rval = 0;
179 	for (; argc > 0; argc--, argv++) {
180 		int fd, status, is_shlib, rv, type;
181 
182 		if ((fd = open(*argv, O_RDONLY, 0)) < 0) {
183 			warn("%s", *argv);
184 			rval |= 1;
185 			continue;
186 		}
187 		rv = is_executable(*argv, fd, &is_shlib, &type);
188 		close(fd);
189 		if (rv == 0) {
190 			rval |= 1;
191 			continue;
192 		}
193 
194 		switch (type) {
195 		case TYPE_ELF:
196 		case TYPE_AOUT:
197 			break;
198 #if __ELF_WORD_SIZE > 32 && defined(ELF32_SUPPORTED)
199 		case TYPE_ELF32:
200 			rval |= execldd32(*argv, fmt1, fmt2, aflag, vflag);
201 			continue;
202 #endif
203 		case TYPE_UNKNOWN:
204 		default:
205 			/*
206 			 * This shouldn't happen unless is_executable()
207 			 * is broken.
208 			 */
209 			errx(EDOOFUS, "unknown executable type");
210 		}
211 
212 		/* ld.so magic */
213 		setenv(LD_ "TRACE_LOADED_OBJECTS", "yes", 1);
214 		if (fmt1 != NULL)
215 			setenv(LD_ "TRACE_LOADED_OBJECTS_FMT1", fmt1, 1);
216 		if (fmt2 != NULL)
217 			setenv(LD_ "TRACE_LOADED_OBJECTS_FMT2", fmt2, 1);
218 
219 		setenv(LD_ "TRACE_LOADED_OBJECTS_PROGNAME", *argv, 1);
220 		if (aflag)
221 			setenv(LD_ "TRACE_LOADED_OBJECTS_ALL", "1", 1);
222 		else if (fmt1 == NULL && fmt2 == NULL)
223 			/* Default formats */
224 			printf("%s:\n", *argv);
225 		fflush(stdout);
226 
227 		switch (fork()) {
228 		case -1:
229 			err(1, "fork");
230 			break;
231 		default:
232 			if (wait(&status) < 0) {
233 				warn("wait");
234 				rval |= 1;
235 			} else if (WIFSIGNALED(status)) {
236 				fprintf(stderr, "%s: signal %d\n", *argv,
237 				    WTERMSIG(status));
238 				rval |= 1;
239 			} else if (WIFEXITED(status) &&
240 			    WEXITSTATUS(status) != 0) {
241 				fprintf(stderr, "%s: exit status %d\n", *argv,
242 				    WEXITSTATUS(status));
243 				rval |= 1;
244 			}
245 			break;
246 		case 0:
247 			if (is_shlib == 0) {
248 				execl(*argv, *argv, (char *)NULL);
249 				warn("%s", *argv);
250 			} else {
251 				dlopen(*argv, RTLD_TRACE);
252 				warnx("%s: %s", *argv, dlerror());
253 			}
254 			_exit(1);
255 		}
256 	}
257 
258 	return rval;
259 }
260 
261 static void
262 usage(void)
263 {
264 
265 	fprintf(stderr, "usage: ldd [-a] [-v] [-f format] program ...\n");
266 	exit(1);
267 }
268 
269 static int
270 is_executable(const char *fname, int fd, int *is_shlib, int *type)
271 {
272 	union {
273 		struct exec aout;
274 #if __ELF_WORD_SIZE > 32 && defined(ELF32_SUPPORTED)
275 		Elf32_Ehdr elf32;
276 #endif
277 		Elf_Ehdr elf;
278 	} hdr;
279 	int n;
280 
281 	*is_shlib = 0;
282 	*type = TYPE_UNKNOWN;
283 
284 	if ((n = read(fd, &hdr, sizeof(hdr))) == -1) {
285 		warn("%s: can't read program header", fname);
286 		return (0);
287 	}
288 
289 	if ((size_t)n >= sizeof(hdr.aout) && !N_BADMAG(hdr.aout)) {
290 		/* a.out file */
291 		if ((N_GETFLAG(hdr.aout) & EX_DPMASK) != EX_DYNAMIC
292 #if 1 /* Compatibility */
293 		    || hdr.aout.a_entry < __LDPGSZ
294 #endif
295 			) {
296 			warnx("%s: not a dynamic executable", fname);
297 			return (0);
298 		}
299 		*type = TYPE_AOUT;
300 		return (1);
301 	}
302 
303 #if __ELF_WORD_SIZE > 32 && defined(ELF32_SUPPORTED)
304 	if ((size_t)n >= sizeof(hdr.elf32) && IS_ELF(hdr.elf32) &&
305 	    hdr.elf32.e_ident[EI_CLASS] == ELFCLASS32) {
306 		/* Handle 32 bit ELF objects */
307 		Elf32_Phdr phdr;
308 		int dynamic, i;
309 
310 		dynamic = 0;
311 		*type = TYPE_ELF32;
312 
313 		if (lseek(fd, hdr.elf32.e_phoff, SEEK_SET) == -1) {
314 			warnx("%s: header too short", fname);
315 			return (0);
316 		}
317 		for (i = 0; i < hdr.elf32.e_phnum; i++) {
318 			if (read(fd, &phdr, hdr.elf32.e_phentsize) !=
319 			    sizeof(phdr)) {
320 				warnx("%s: can't read program header", fname);
321 				return (0);
322 			}
323 			if (phdr.p_type == PT_DYNAMIC) {
324 				dynamic = 1;
325 				break;
326 			}
327 		}
328 
329 		if (!dynamic) {
330 			warnx("%s: not a dynamic ELF executable", fname);
331 			return (0);
332 		}
333 		if (hdr.elf32.e_type == ET_DYN) {
334 			if (hdr.elf32.e_ident[EI_OSABI] == ELFOSABI_FREEBSD) {
335 				*is_shlib = 1;
336 				return (1);
337 			}
338 			warnx("%s: not a FreeBSD ELF shared object", fname);
339 			return (0);
340 		}
341 
342 		return (1);
343 	}
344 #endif
345 
346 	if ((size_t)n >= sizeof(hdr.elf) && IS_ELF(hdr.elf) &&
347 	    hdr.elf.e_ident[EI_CLASS] == ELF_TARG_CLASS) {
348 		/* Handle default ELF objects on this architecture */
349 		Elf_Phdr phdr;
350 		int dynamic, i;
351 
352 		dynamic = 0;
353 		*type = TYPE_ELF;
354 
355 		if (lseek(fd, hdr.elf.e_phoff, SEEK_SET) == -1) {
356 			warnx("%s: header too short", fname);
357 			return (0);
358 		}
359 		for (i = 0; i < hdr.elf.e_phnum; i++) {
360 			if (read(fd, &phdr, hdr.elf.e_phentsize)
361 			   != sizeof(phdr)) {
362 				warnx("%s: can't read program header", fname);
363 				return (0);
364 			}
365 			if (phdr.p_type == PT_DYNAMIC) {
366 				dynamic = 1;
367 				break;
368 			}
369 		}
370 
371 		if (!dynamic) {
372 			warnx("%s: not a dynamic ELF executable", fname);
373 			return (0);
374 		}
375 		if (hdr.elf.e_type == ET_DYN) {
376 			if (hdr.elf.e_ident[EI_OSABI] == ELFOSABI_FREEBSD) {
377 				*is_shlib = 1;
378 				return (1);
379 			}
380 			warnx("%s: not a FreeBSD ELF shared object", fname);
381 			return (0);
382 		}
383 
384 		return (1);
385 	}
386 
387 	warnx("%s: not a dynamic executable", fname);
388 	return (0);
389 }
390