1696Spaul/*
2696Spaul * Copyright (c) 1993 Paul Kranenburg
3696Spaul * All rights reserved.
4696Spaul *
5696Spaul * Redistribution and use in source and binary forms, with or without
6696Spaul * modification, are permitted provided that the following conditions
7696Spaul * are met:
8696Spaul * 1. Redistributions of source code must retain the above copyright
9696Spaul *    notice, this list of conditions and the following disclaimer.
10696Spaul * 2. Redistributions in binary form must reproduce the above copyright
11696Spaul *    notice, this list of conditions and the following disclaimer in the
12696Spaul *    documentation and/or other materials provided with the distribution.
13696Spaul * 3. All advertising materials mentioning features or use of this software
14696Spaul *    must display the following acknowledgement:
15696Spaul *      This product includes software developed by Paul Kranenburg.
16696Spaul * 4. The name of the author may not be used to endorse or promote products
171153Sjkh *    derived from this software without specific prior written permission
18696Spaul *
19696Spaul * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
20696Spaul * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
21696Spaul * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
22696Spaul * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
23696Spaul * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
24696Spaul * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25696Spaul * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26696Spaul * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27696Spaul * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
28696Spaul * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29696Spaul */
30696Spaul
3195648Smarkm#include <sys/cdefs.h>
3295648Smarkm__FBSDID("$FreeBSD$");
3369827Scharnier
34696Spaul#include <sys/wait.h>
3595648Smarkm
3676224Sobrien#include <machine/elf.h>
3795648Smarkm
3895153Smike#include <arpa/inet.h>
3995648Smarkm
40696Spaul#include <a.out.h>
4190172Ssobomax#include <dlfcn.h>
421741Srich#include <err.h>
43180236Sedwin#include <errno.h>
441741Srich#include <fcntl.h>
451741Srich#include <stdio.h>
461741Srich#include <stdlib.h>
47181136Sjhb#include <string.h>
481741Srich#include <unistd.h>
49696Spaul
5095648Smarkm#include "extern.h"
5118600Speter
52180646Sedwin/*
53180877Sedwin * 32-bit ELF data structures can only be used if the system header[s] declare
54180877Sedwin * them.  There is no official macro for determining whether they are declared,
55180877Sedwin * so check for the existence of one of the 32-macros defined in elf(5).
56180646Sedwin */
57180877Sedwin#ifdef ELF32_R_TYPE
58180877Sedwin#define	ELF32_SUPPORTED
59180646Sedwin#endif
60180646Sedwin
61254018Smarkj#define	LDD_SETENV(name, value, overwrite) do {		\
62254018Smarkj	setenv("LD_" name, value, overwrite);		\
63254018Smarkj	setenv("LD_32_" name, value, overwrite);	\
64254018Smarkj} while (0)
65254018Smarkj
66254018Smarkj#define	LDD_UNSETENV(name) do {		\
67254018Smarkj	unsetenv("LD_" name);		\
68254018Smarkj	unsetenv("LD_32_" name);	\
69254018Smarkj} while (0)
70254018Smarkj
71180235Sedwinstatic int	is_executable(const char *fname, int fd, int *is_shlib,
72180235Sedwin		    int *type);
73180235Sedwinstatic void	usage(void);
74180234Sedwin
75180235Sedwin#define	TYPE_UNKNOWN	0
76180235Sedwin#define	TYPE_AOUT	1
77180235Sedwin#define	TYPE_ELF	2	/* Architecture default */
78180646Sedwin#if __ELF_WORD_SIZE > 32 && defined(ELF32_SUPPORTED)
79180236Sedwin#define	TYPE_ELF32	3	/* Explicit 32 bits on architectures >32 bits */
80696Spaul
81181136Sjhb#define	_PATH_LDD32	"/usr/bin/ldd32"
82180235Sedwin
83181136Sjhbstatic int
84181136Sjhbexecldd32(char *file, char *fmt1, char *fmt2, int aflag, int vflag)
85181136Sjhb{
86300458Struckman	char *argv[9];
87181136Sjhb	int i, rval, status;
88181136Sjhb
89254018Smarkj	LDD_UNSETENV("TRACE_LOADED_OBJECTS");
90181136Sjhb	rval = 0;
91181136Sjhb	i = 0;
92181136Sjhb	argv[i++] = strdup(_PATH_LDD32);
93181136Sjhb	if (aflag)
94181136Sjhb		argv[i++] = strdup("-a");
95181136Sjhb	if (vflag)
96181136Sjhb		argv[i++] = strdup("-v");
97181161Sjhb	if (fmt1 != NULL) {
98181136Sjhb		argv[i++] = strdup("-f");
99181136Sjhb		argv[i++] = strdup(fmt1);
100181136Sjhb	}
101181161Sjhb	if (fmt2 != NULL) {
102181136Sjhb		argv[i++] = strdup("-f");
103181136Sjhb		argv[i++] = strdup(fmt2);
104181136Sjhb	}
105181136Sjhb	argv[i++] = strdup(file);
106181136Sjhb	argv[i++] = NULL;
107181136Sjhb
108181136Sjhb	switch (fork()) {
109181136Sjhb	case -1:
110181136Sjhb		err(1, "fork");
111181136Sjhb		break;
112181136Sjhb	case 0:
113181136Sjhb		execv(_PATH_LDD32, argv);
114181136Sjhb		warn("%s", _PATH_LDD32);
115181161Sjhb		_exit(127);
116181136Sjhb		break;
117181136Sjhb	default:
118181161Sjhb		if (wait(&status) < 0)
119181136Sjhb			rval = 1;
120181161Sjhb		else if (WIFSIGNALED(status))
121181136Sjhb			rval = 1;
122181161Sjhb		else if (WIFEXITED(status) && WEXITSTATUS(status) != 0)
123181136Sjhb			rval = 1;
124181136Sjhb		break;
125181136Sjhb	}
126181136Sjhb	while (i--)
127181136Sjhb		free(argv[i]);
128254018Smarkj	LDD_SETENV("TRACE_LOADED_OBJECTS", "yes", 1);
129181136Sjhb	return (rval);
130181136Sjhb}
131180236Sedwin#endif
132180235Sedwin
133696Spaulint
13495648Smarkmmain(int argc, char *argv[])
135696Spaul{
136180234Sedwin	char *fmt1, *fmt2;
137180234Sedwin	int rval, c, aflag, vflag;
138696Spaul
13990755Sobrien	aflag = vflag = 0;
140180234Sedwin	fmt1 = fmt2 = NULL;
14190755Sobrien
142180234Sedwin	while ((c = getopt(argc, argv, "af:v")) != -1) {
143696Spaul		switch (c) {
14490755Sobrien		case 'a':
14590755Sobrien			aflag++;
14690755Sobrien			break;
14718598Speter		case 'f':
148180234Sedwin			if (fmt1 != NULL) {
149180234Sedwin				if (fmt2 != NULL)
15069827Scharnier					errx(1, "too many formats");
15118598Speter				fmt2 = optarg;
15218598Speter			} else
15318598Speter				fmt1 = optarg;
15418598Speter			break;
155180234Sedwin		case 'v':
156180234Sedwin			vflag++;
157180234Sedwin			break;
158696Spaul		default:
159696Spaul			usage();
160180234Sedwin			/* NOTREACHED */
161696Spaul		}
162696Spaul	}
163696Spaul	argc -= optind;
164696Spaul	argv += optind;
165696Spaul
166180234Sedwin	if (vflag && fmt1 != NULL)
16718600Speter		errx(1, "-v may not be used with -f");
16818600Speter
169696Spaul	if (argc <= 0) {
170696Spaul		usage();
171180234Sedwin		/* NOTREACHED */
172696Spaul	}
173696Spaul
17439354Sdfr#ifdef __i386__
17518600Speter	if (vflag) {
17618600Speter		for (c = 0; c < argc; c++)
17718600Speter			dump_file(argv[c]);
17818600Speter		exit(error_count == 0 ? EXIT_SUCCESS : EXIT_FAILURE);
17918600Speter	}
18039354Sdfr#endif
18118600Speter
1821741Srich	rval = 0;
183180234Sedwin	for (; argc > 0; argc--, argv++) {
184180235Sedwin		int fd, status, is_shlib, rv, type;
185696Spaul
186696Spaul		if ((fd = open(*argv, O_RDONLY, 0)) < 0) {
1871741Srich			warn("%s", *argv);
188696Spaul			rval |= 1;
189696Spaul			continue;
190696Spaul		}
191180235Sedwin		rv = is_executable(*argv, fd, &is_shlib, &type);
192180235Sedwin		close(fd);
193180235Sedwin		if (rv == 0) {
194696Spaul			rval |= 1;
195696Spaul			continue;
196696Spaul		}
19735575Sdfr
198180236Sedwin		switch (type) {
199180236Sedwin		case TYPE_ELF:
200180236Sedwin		case TYPE_AOUT:
201180236Sedwin			break;
202180646Sedwin#if __ELF_WORD_SIZE > 32 && defined(ELF32_SUPPORTED)
203180236Sedwin		case TYPE_ELF32:
204181136Sjhb			rval |= execldd32(*argv, fmt1, fmt2, aflag, vflag);
205181136Sjhb			continue;
206180236Sedwin#endif
207180236Sedwin		case TYPE_UNKNOWN:
208180236Sedwin		default:
209180236Sedwin			/*
210180236Sedwin			 * This shouldn't happen unless is_executable()
211180236Sedwin			 * is broken.
212180236Sedwin			 */
213180236Sedwin			errx(EDOOFUS, "unknown executable type");
214180236Sedwin		}
215180236Sedwin
216180235Sedwin		/* ld.so magic */
217254018Smarkj		LDD_SETENV("TRACE_LOADED_OBJECTS", "yes", 1);
218180235Sedwin		if (fmt1 != NULL)
219254018Smarkj			LDD_SETENV("TRACE_LOADED_OBJECTS_FMT1", fmt1, 1);
220180235Sedwin		if (fmt2 != NULL)
221254018Smarkj			LDD_SETENV("TRACE_LOADED_OBJECTS_FMT2", fmt2, 1);
22235575Sdfr
223254018Smarkj		LDD_SETENV("TRACE_LOADED_OBJECTS_PROGNAME", *argv, 1);
224180234Sedwin		if (aflag)
225254018Smarkj			LDD_SETENV("TRACE_LOADED_OBJECTS_ALL", "1", 1);
22690755Sobrien		else if (fmt1 == NULL && fmt2 == NULL)
22718598Speter			/* Default formats */
22818598Speter			printf("%s:\n", *argv);
2291153Sjkh		fflush(stdout);
230696Spaul
231696Spaul		switch (fork()) {
232696Spaul		case -1:
2331741Srich			err(1, "fork");
234696Spaul			break;
235696Spaul		default:
236181161Sjhb			if (wait(&status) < 0) {
2371741Srich				warn("wait");
2381741Srich				rval |= 1;
2391741Srich			} else if (WIFSIGNALED(status)) {
240180234Sedwin				fprintf(stderr, "%s: signal %d\n", *argv,
241180234Sedwin				    WTERMSIG(status));
242696Spaul				rval |= 1;
243181161Sjhb			} else if (WIFEXITED(status) &&
244181161Sjhb			    WEXITSTATUS(status) != 0) {
245180234Sedwin				fprintf(stderr, "%s: exit status %d\n", *argv,
246180234Sedwin				    WEXITSTATUS(status));
247696Spaul				rval |= 1;
248696Spaul			}
249696Spaul			break;
250696Spaul		case 0:
251105439Ssobomax			if (is_shlib == 0) {
25290172Ssobomax				execl(*argv, *argv, (char *)NULL);
25390172Ssobomax				warn("%s", *argv);
254105439Ssobomax			} else {
255105439Ssobomax				dlopen(*argv, RTLD_TRACE);
256105439Ssobomax				warnx("%s: %s", *argv, dlerror());
25790172Ssobomax			}
258696Spaul			_exit(1);
259696Spaul		}
260696Spaul	}
261696Spaul
262696Spaul	return rval;
263696Spaul}
264180235Sedwin
265180235Sedwinstatic void
266180235Sedwinusage(void)
267180235Sedwin{
268180235Sedwin
269180235Sedwin	fprintf(stderr, "usage: ldd [-a] [-v] [-f format] program ...\n");
270180235Sedwin	exit(1);
271180235Sedwin}
272180235Sedwin
273180235Sedwinstatic int
274180235Sedwinis_executable(const char *fname, int fd, int *is_shlib, int *type)
275180235Sedwin{
276180235Sedwin	union {
277180235Sedwin		struct exec aout;
278180646Sedwin#if __ELF_WORD_SIZE > 32 && defined(ELF32_SUPPORTED)
279180236Sedwin		Elf32_Ehdr elf32;
280180646Sedwin#endif
281180235Sedwin		Elf_Ehdr elf;
282180235Sedwin	} hdr;
283180235Sedwin	int n;
284180235Sedwin
285180235Sedwin	*is_shlib = 0;
286180235Sedwin	*type = TYPE_UNKNOWN;
287180235Sedwin
288180235Sedwin	if ((n = read(fd, &hdr, sizeof(hdr))) == -1) {
289180235Sedwin		warn("%s: can't read program header", fname);
290180235Sedwin		return (0);
291180235Sedwin	}
292180235Sedwin
293180235Sedwin	if ((size_t)n >= sizeof(hdr.aout) && !N_BADMAG(hdr.aout)) {
294180235Sedwin		/* a.out file */
295180235Sedwin		if ((N_GETFLAG(hdr.aout) & EX_DPMASK) != EX_DYNAMIC
296180235Sedwin#if 1 /* Compatibility */
297180235Sedwin		    || hdr.aout.a_entry < __LDPGSZ
298180235Sedwin#endif
299180235Sedwin			) {
300180235Sedwin			warnx("%s: not a dynamic executable", fname);
301180235Sedwin			return (0);
302180235Sedwin		}
303180235Sedwin		*type = TYPE_AOUT;
304180235Sedwin		return (1);
305180235Sedwin	}
306180235Sedwin
307180646Sedwin#if __ELF_WORD_SIZE > 32 && defined(ELF32_SUPPORTED)
308180236Sedwin	if ((size_t)n >= sizeof(hdr.elf32) && IS_ELF(hdr.elf32) &&
309180236Sedwin	    hdr.elf32.e_ident[EI_CLASS] == ELFCLASS32) {
310180236Sedwin		/* Handle 32 bit ELF objects */
311180236Sedwin		Elf32_Phdr phdr;
312180236Sedwin		int dynamic, i;
313180236Sedwin
314180236Sedwin		dynamic = 0;
315180236Sedwin		*type = TYPE_ELF32;
316180236Sedwin
317180236Sedwin		if (lseek(fd, hdr.elf32.e_phoff, SEEK_SET) == -1) {
318180236Sedwin			warnx("%s: header too short", fname);
319180236Sedwin			return (0);
320180236Sedwin		}
321180236Sedwin		for (i = 0; i < hdr.elf32.e_phnum; i++) {
322180236Sedwin			if (read(fd, &phdr, hdr.elf32.e_phentsize) !=
323180236Sedwin			    sizeof(phdr)) {
324180236Sedwin				warnx("%s: can't read program header", fname);
325180236Sedwin				return (0);
326180236Sedwin			}
327180236Sedwin			if (phdr.p_type == PT_DYNAMIC) {
328180236Sedwin				dynamic = 1;
329180236Sedwin				break;
330180236Sedwin			}
331180236Sedwin		}
332180236Sedwin
333180236Sedwin		if (!dynamic) {
334180236Sedwin			warnx("%s: not a dynamic ELF executable", fname);
335180236Sedwin			return (0);
336180236Sedwin		}
337180236Sedwin		if (hdr.elf32.e_type == ET_DYN) {
338215705Sbrucec			if (hdr.elf32.e_ident[EI_OSABI] == ELFOSABI_FREEBSD) {
339180236Sedwin				*is_shlib = 1;
340180236Sedwin				return (1);
341180236Sedwin			}
342180236Sedwin			warnx("%s: not a FreeBSD ELF shared object", fname);
343180236Sedwin			return (0);
344180236Sedwin		}
345180236Sedwin
346180236Sedwin		return (1);
347180236Sedwin	}
348180236Sedwin#endif
349180236Sedwin
350180235Sedwin	if ((size_t)n >= sizeof(hdr.elf) && IS_ELF(hdr.elf) &&
351180235Sedwin	    hdr.elf.e_ident[EI_CLASS] == ELF_TARG_CLASS) {
352180235Sedwin		/* Handle default ELF objects on this architecture */
353180235Sedwin		Elf_Phdr phdr;
354180235Sedwin		int dynamic, i;
355180235Sedwin
356180235Sedwin		dynamic = 0;
357180235Sedwin		*type = TYPE_ELF;
358180235Sedwin
359180235Sedwin		if (lseek(fd, hdr.elf.e_phoff, SEEK_SET) == -1) {
360180235Sedwin			warnx("%s: header too short", fname);
361180235Sedwin			return (0);
362180235Sedwin		}
363180235Sedwin		for (i = 0; i < hdr.elf.e_phnum; i++) {
364180235Sedwin			if (read(fd, &phdr, hdr.elf.e_phentsize)
365180235Sedwin			   != sizeof(phdr)) {
366180235Sedwin				warnx("%s: can't read program header", fname);
367180235Sedwin				return (0);
368180235Sedwin			}
369180235Sedwin			if (phdr.p_type == PT_DYNAMIC) {
370180235Sedwin				dynamic = 1;
371180235Sedwin				break;
372180235Sedwin			}
373180235Sedwin		}
374180235Sedwin
375180235Sedwin		if (!dynamic) {
376180235Sedwin			warnx("%s: not a dynamic ELF executable", fname);
377180235Sedwin			return (0);
378180235Sedwin		}
379180235Sedwin		if (hdr.elf.e_type == ET_DYN) {
380291268Sjkim			switch (hdr.elf.e_ident[EI_OSABI]) {
381291268Sjkim			case ELFOSABI_FREEBSD:
382180235Sedwin				*is_shlib = 1;
383180235Sedwin				return (1);
384291268Sjkim#ifdef __ARM_EABI__
385291268Sjkim			case ELFOSABI_NONE:
386291268Sjkim				if (hdr.elf.e_machine != EM_ARM)
387291268Sjkim					break;
388291268Sjkim				if (((hdr.elf.e_flags & 0xff000000) >> 24) < 4)
389291268Sjkim					break;
390291268Sjkim				*is_shlib = 1;
391291268Sjkim				return (1);
392291268Sjkim#endif
393180235Sedwin			}
394180235Sedwin			warnx("%s: not a FreeBSD ELF shared object", fname);
395180235Sedwin			return (0);
396180235Sedwin		}
397180235Sedwin
398180235Sedwin		return (1);
399180235Sedwin	}
400180235Sedwin
401180235Sedwin	warnx("%s: not a dynamic executable", fname);
402180235Sedwin	return (0);
403180235Sedwin}
404