ldd.c revision 180235
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 180235 2008-07-03 22:30:18Z 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 <fcntl.h>
44#include <stdio.h>
45#include <stdlib.h>
46#include <unistd.h>
47
48#include "extern.h"
49
50static int	is_executable(const char *fname, int fd, int *is_shlib,
51		    int *type);
52static void	usage(void);
53
54#define	TYPE_UNKNOWN	0
55#define	TYPE_AOUT	1
56#define	TYPE_ELF	2	/* Architecture default */
57
58#define	ENV_OBJECTS		0
59#define	ENV_OBJECTS_FMT1	1
60#define	ENV_OBJECTS_FMT2	2
61#define	ENV_OBJECTS_PROGNAME	3
62#define	ENV_OBJECTS_ALL		4
63#define	ENV_LAST		5
64
65const char	*envdef[ENV_LAST] = {
66	"LD_TRACE_LOADED_OBJECTS",
67	"LD_TRACE_LOADED_OBJECTS_FMT1",
68	"LD_TRACE_LOADED_OBJECTS_FMT2",
69	"LD_TRACE_LOADED_OBJECTS_PROGNAME",
70	"LD_TRACE_LOADED_OBJECTS_ALL",
71};
72
73int
74main(int argc, char *argv[])
75{
76	char *fmt1, *fmt2;
77	int rval, c, aflag, vflag;
78
79	aflag = vflag = 0;
80	fmt1 = fmt2 = NULL;
81
82	while ((c = getopt(argc, argv, "af:v")) != -1) {
83		switch (c) {
84		case 'a':
85			aflag++;
86			break;
87		case 'f':
88			if (fmt1 != NULL) {
89				if (fmt2 != NULL)
90					errx(1, "too many formats");
91				fmt2 = optarg;
92			} else
93				fmt1 = optarg;
94			break;
95		case 'v':
96			vflag++;
97			break;
98		default:
99			usage();
100			/* NOTREACHED */
101		}
102	}
103	argc -= optind;
104	argv += optind;
105
106	if (vflag && fmt1 != NULL)
107		errx(1, "-v may not be used with -f");
108
109	if (argc <= 0) {
110		usage();
111		/* NOTREACHED */
112	}
113
114#ifdef __i386__
115	if (vflag) {
116		for (c = 0; c < argc; c++)
117			dump_file(argv[c]);
118		exit(error_count == 0 ? EXIT_SUCCESS : EXIT_FAILURE);
119	}
120#endif
121
122	rval = 0;
123	for (; argc > 0; argc--, argv++) {
124		int fd, status, is_shlib, rv, type;
125		const char **env;
126
127		env = envdef;	/* Temporary placeholder */
128
129		if ((fd = open(*argv, O_RDONLY, 0)) < 0) {
130			warn("%s", *argv);
131			rval |= 1;
132			continue;
133		}
134		rv = is_executable(*argv, fd, &is_shlib, &type);
135		close(fd);
136		if (rv == 0) {
137			rval |= 1;
138			continue;
139		}
140
141		/* ld.so magic */
142		setenv(env[ENV_OBJECTS], "yes", 1);
143		if (fmt1 != NULL)
144			setenv(env[ENV_OBJECTS_FMT1], fmt1, 1);
145		if (fmt2 != NULL)
146			setenv(env[ENV_OBJECTS_FMT2], fmt2, 1);
147
148		setenv(env[ENV_OBJECTS_PROGNAME], *argv, 1);
149		if (aflag)
150			setenv(env[ENV_OBJECTS_ALL], "1", 1);
151		else if (fmt1 == NULL && fmt2 == NULL)
152			/* Default formats */
153			printf("%s:\n", *argv);
154		fflush(stdout);
155
156		switch (fork()) {
157		case -1:
158			err(1, "fork");
159			break;
160		default:
161			if (wait(&status) <= 0) {
162				warn("wait");
163				rval |= 1;
164			} else if (WIFSIGNALED(status)) {
165				fprintf(stderr, "%s: signal %d\n", *argv,
166				    WTERMSIG(status));
167				rval |= 1;
168			} else if (WIFEXITED(status) && WEXITSTATUS(status)) {
169				fprintf(stderr, "%s: exit status %d\n", *argv,
170				    WEXITSTATUS(status));
171				rval |= 1;
172			}
173			break;
174		case 0:
175			if (is_shlib == 0) {
176				execl(*argv, *argv, (char *)NULL);
177				warn("%s", *argv);
178			} else {
179				dlopen(*argv, RTLD_TRACE);
180				warnx("%s: %s", *argv, dlerror());
181			}
182			_exit(1);
183		}
184	}
185
186	return rval;
187}
188
189static void
190usage(void)
191{
192
193	fprintf(stderr, "usage: ldd [-a] [-v] [-f format] program ...\n");
194	exit(1);
195}
196
197static int
198is_executable(const char *fname, int fd, int *is_shlib, int *type)
199{
200	union {
201		struct exec aout;
202		Elf_Ehdr elf;
203	} hdr;
204	int n;
205
206	*is_shlib = 0;
207	*type = TYPE_UNKNOWN;
208
209	if ((n = read(fd, &hdr, sizeof(hdr))) == -1) {
210		warn("%s: can't read program header", fname);
211		return (0);
212	}
213
214	if ((size_t)n >= sizeof(hdr.aout) && !N_BADMAG(hdr.aout)) {
215		/* a.out file */
216		if ((N_GETFLAG(hdr.aout) & EX_DPMASK) != EX_DYNAMIC
217#if 1 /* Compatibility */
218		    || hdr.aout.a_entry < __LDPGSZ
219#endif
220			) {
221			warnx("%s: not a dynamic executable", fname);
222			return (0);
223		}
224		*type = TYPE_AOUT;
225		return (1);
226	}
227
228	if ((size_t)n >= sizeof(hdr.elf) && IS_ELF(hdr.elf) &&
229	    hdr.elf.e_ident[EI_CLASS] == ELF_TARG_CLASS) {
230		/* Handle default ELF objects on this architecture */
231		Elf_Phdr phdr;
232		int dynamic, i;
233
234		dynamic = 0;
235		*type = TYPE_ELF;
236
237		if (lseek(fd, hdr.elf.e_phoff, SEEK_SET) == -1) {
238			warnx("%s: header too short", fname);
239			return (0);
240		}
241		for (i = 0; i < hdr.elf.e_phnum; i++) {
242			if (read(fd, &phdr, hdr.elf.e_phentsize)
243			   != sizeof(phdr)) {
244				warnx("%s: can't read program header", fname);
245				return (0);
246			}
247			if (phdr.p_type == PT_DYNAMIC) {
248				dynamic = 1;
249				break;
250			}
251		}
252
253		if (!dynamic) {
254			warnx("%s: not a dynamic ELF executable", fname);
255			return (0);
256		}
257		if (hdr.elf.e_type == ET_DYN) {
258			if (hdr.elf.e_ident[EI_OSABI] & ELFOSABI_FREEBSD) {
259				*is_shlib = 1;
260				return (1);
261			}
262			warnx("%s: not a FreeBSD ELF shared object", fname);
263			return (0);
264		}
265
266		return (1);
267	}
268
269	warnx("%s: not a dynamic executable", fname);
270	return (0);
271}
272