ldd.c revision 180236
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: head/usr.bin/ldd/ldd.c 180236 2008-07-03 22:37:51Z edwin $");
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 <unistd.h>
48
49#include "extern.h"
50
51static int	is_executable(const char *fname, int fd, int *is_shlib,
52		    int *type);
53static void	usage(void);
54
55#define	TYPE_UNKNOWN	0
56#define	TYPE_AOUT	1
57#define	TYPE_ELF	2	/* Architecture default */
58#if __ELF_WORD_SIZE > 32
59#define	TYPE_ELF32	3	/* Explicit 32 bits on architectures >32 bits */
60#endif
61
62#define	ENV_OBJECTS		0
63#define	ENV_OBJECTS_FMT1	1
64#define	ENV_OBJECTS_FMT2	2
65#define	ENV_OBJECTS_PROGNAME	3
66#define	ENV_OBJECTS_ALL		4
67#define	ENV_LAST		5
68
69const char	*envdef[ENV_LAST] = {
70	"LD_TRACE_LOADED_OBJECTS",
71	"LD_TRACE_LOADED_OBJECTS_FMT1",
72	"LD_TRACE_LOADED_OBJECTS_FMT2",
73	"LD_TRACE_LOADED_OBJECTS_PROGNAME",
74	"LD_TRACE_LOADED_OBJECTS_ALL",
75};
76#if __ELF_WORD_SIZE > 32
77const char	*env32[ENV_LAST] = {
78	"LD_32_TRACE_LOADED_OBJECTS",
79	"LD_32_TRACE_LOADED_OBJECTS_FMT1",
80	"LD_32_TRACE_LOADED_OBJECTS_FMT2",
81	"LD_32_TRACE_LOADED_OBJECTS_PROGNAME",
82	"LD_32_TRACE_LOADED_OBJECTS_ALL",
83};
84#endif
85
86int
87main(int argc, char *argv[])
88{
89	char *fmt1, *fmt2;
90	int rval, c, aflag, vflag;
91
92	aflag = vflag = 0;
93	fmt1 = fmt2 = NULL;
94
95	while ((c = getopt(argc, argv, "af:v")) != -1) {
96		switch (c) {
97		case 'a':
98			aflag++;
99			break;
100		case 'f':
101			if (fmt1 != NULL) {
102				if (fmt2 != NULL)
103					errx(1, "too many formats");
104				fmt2 = optarg;
105			} else
106				fmt1 = optarg;
107			break;
108		case 'v':
109			vflag++;
110			break;
111		default:
112			usage();
113			/* NOTREACHED */
114		}
115	}
116	argc -= optind;
117	argv += optind;
118
119	if (vflag && fmt1 != NULL)
120		errx(1, "-v may not be used with -f");
121
122	if (argc <= 0) {
123		usage();
124		/* NOTREACHED */
125	}
126
127#ifdef __i386__
128	if (vflag) {
129		for (c = 0; c < argc; c++)
130			dump_file(argv[c]);
131		exit(error_count == 0 ? EXIT_SUCCESS : EXIT_FAILURE);
132	}
133#endif
134
135	rval = 0;
136	for (; argc > 0; argc--, argv++) {
137		int fd, status, is_shlib, rv, type;
138		const char **env;
139
140		if ((fd = open(*argv, O_RDONLY, 0)) < 0) {
141			warn("%s", *argv);
142			rval |= 1;
143			continue;
144		}
145		rv = is_executable(*argv, fd, &is_shlib, &type);
146		close(fd);
147		if (rv == 0) {
148			rval |= 1;
149			continue;
150		}
151
152		switch (type) {
153		case TYPE_ELF:
154		case TYPE_AOUT:
155			env = envdef;
156			break;
157#if __ELF_WORD_SIZE > 32
158		case TYPE_ELF32:
159			env = env32;
160			break;
161#endif
162		case TYPE_UNKNOWN:
163		default:
164			/*
165			 * This shouldn't happen unless is_executable()
166			 * is broken.
167			 */
168			errx(EDOOFUS, "unknown executable type");
169		}
170
171		/* ld.so magic */
172		setenv(env[ENV_OBJECTS], "yes", 1);
173		if (fmt1 != NULL)
174			setenv(env[ENV_OBJECTS_FMT1], fmt1, 1);
175		if (fmt2 != NULL)
176			setenv(env[ENV_OBJECTS_FMT2], fmt2, 1);
177
178		setenv(env[ENV_OBJECTS_PROGNAME], *argv, 1);
179		if (aflag)
180			setenv(env[ENV_OBJECTS_ALL], "1", 1);
181		else if (fmt1 == NULL && fmt2 == NULL)
182			/* Default formats */
183			printf("%s:\n", *argv);
184		fflush(stdout);
185
186		switch (fork()) {
187		case -1:
188			err(1, "fork");
189			break;
190		default:
191			if (wait(&status) <= 0) {
192				warn("wait");
193				rval |= 1;
194			} else if (WIFSIGNALED(status)) {
195				fprintf(stderr, "%s: signal %d\n", *argv,
196				    WTERMSIG(status));
197				rval |= 1;
198			} else if (WIFEXITED(status) && WEXITSTATUS(status)) {
199				fprintf(stderr, "%s: exit status %d\n", *argv,
200				    WEXITSTATUS(status));
201				rval |= 1;
202			}
203			break;
204		case 0:
205			if (is_shlib == 0) {
206				execl(*argv, *argv, (char *)NULL);
207				warn("%s", *argv);
208			} else {
209				dlopen(*argv, RTLD_TRACE);
210				warnx("%s: %s", *argv, dlerror());
211			}
212			_exit(1);
213		}
214	}
215
216	return rval;
217}
218
219static void
220usage(void)
221{
222
223	fprintf(stderr, "usage: ldd [-a] [-v] [-f format] program ...\n");
224	exit(1);
225}
226
227static int
228is_executable(const char *fname, int fd, int *is_shlib, int *type)
229{
230	union {
231		struct exec aout;
232		Elf32_Ehdr elf32;
233		Elf_Ehdr elf;
234	} hdr;
235	int n;
236
237	*is_shlib = 0;
238	*type = TYPE_UNKNOWN;
239
240	if ((n = read(fd, &hdr, sizeof(hdr))) == -1) {
241		warn("%s: can't read program header", fname);
242		return (0);
243	}
244
245	if ((size_t)n >= sizeof(hdr.aout) && !N_BADMAG(hdr.aout)) {
246		/* a.out file */
247		if ((N_GETFLAG(hdr.aout) & EX_DPMASK) != EX_DYNAMIC
248#if 1 /* Compatibility */
249		    || hdr.aout.a_entry < __LDPGSZ
250#endif
251			) {
252			warnx("%s: not a dynamic executable", fname);
253			return (0);
254		}
255		*type = TYPE_AOUT;
256		return (1);
257	}
258
259#if __ELF_WORD_SIZE > 32
260	if ((size_t)n >= sizeof(hdr.elf32) && IS_ELF(hdr.elf32) &&
261	    hdr.elf32.e_ident[EI_CLASS] == ELFCLASS32) {
262		/* Handle 32 bit ELF objects */
263		Elf32_Phdr phdr;
264		int dynamic, i;
265
266		dynamic = 0;
267		*type = TYPE_ELF32;
268
269		if (lseek(fd, hdr.elf32.e_phoff, SEEK_SET) == -1) {
270			warnx("%s: header too short", fname);
271			return (0);
272		}
273		for (i = 0; i < hdr.elf32.e_phnum; i++) {
274			if (read(fd, &phdr, hdr.elf32.e_phentsize) !=
275			    sizeof(phdr)) {
276				warnx("%s: can't read program header", fname);
277				return (0);
278			}
279			if (phdr.p_type == PT_DYNAMIC) {
280				dynamic = 1;
281				break;
282			}
283		}
284
285		if (!dynamic) {
286			warnx("%s: not a dynamic ELF executable", fname);
287			return (0);
288		}
289		if (hdr.elf32.e_type == ET_DYN) {
290			if (hdr.elf32.e_ident[EI_OSABI] & ELFOSABI_FREEBSD) {
291				*is_shlib = 1;
292				return (1);
293			}
294			warnx("%s: not a FreeBSD ELF shared object", fname);
295			return (0);
296		}
297
298		return (1);
299	}
300#endif
301
302	if ((size_t)n >= sizeof(hdr.elf) && IS_ELF(hdr.elf) &&
303	    hdr.elf.e_ident[EI_CLASS] == ELF_TARG_CLASS) {
304		/* Handle default ELF objects on this architecture */
305		Elf_Phdr phdr;
306		int dynamic, i;
307
308		dynamic = 0;
309		*type = TYPE_ELF;
310
311		if (lseek(fd, hdr.elf.e_phoff, SEEK_SET) == -1) {
312			warnx("%s: header too short", fname);
313			return (0);
314		}
315		for (i = 0; i < hdr.elf.e_phnum; i++) {
316			if (read(fd, &phdr, hdr.elf.e_phentsize)
317			   != sizeof(phdr)) {
318				warnx("%s: can't read program header", fname);
319				return (0);
320			}
321			if (phdr.p_type == PT_DYNAMIC) {
322				dynamic = 1;
323				break;
324			}
325		}
326
327		if (!dynamic) {
328			warnx("%s: not a dynamic ELF executable", fname);
329			return (0);
330		}
331		if (hdr.elf.e_type == ET_DYN) {
332			if (hdr.elf.e_ident[EI_OSABI] & ELFOSABI_FREEBSD) {
333				*is_shlib = 1;
334				return (1);
335			}
336			warnx("%s: not a FreeBSD ELF shared object", fname);
337			return (0);
338		}
339
340		return (1);
341	}
342
343	warnx("%s: not a dynamic executable", fname);
344	return (0);
345}
346