ldd.c revision 181161
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 181161 2008-08-02 12:04:59Z jhb $");
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
67static int	is_executable(const char *fname, int fd, int *is_shlib,
68		    int *type);
69static 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
79static int
80execldd32(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
129int
130main(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
261static void
262usage(void)
263{
264
265	fprintf(stderr, "usage: ldd [-a] [-v] [-f format] program ...\n");
266	exit(1);
267}
268
269static int
270is_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