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