ldd.c revision 280220
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: head/usr.bin/ldd/ldd.c 280220 2015-03-18 13:59:04Z andrew $"); 3369827Scharnier 34696Spaul#include <sys/wait.h> 3595648Smarkm 3676224Sobrien#include <machine/elf.h> 3795648Smarkm 3895153Smike#include <arpa/inet.h> 3995648Smarkm 4090172Ssobomax#include <dlfcn.h> 411741Srich#include <err.h> 42180236Sedwin#include <errno.h> 431741Srich#include <fcntl.h> 441741Srich#include <stdio.h> 451741Srich#include <stdlib.h> 46181136Sjhb#include <string.h> 471741Srich#include <unistd.h> 48696Spaul 4995648Smarkm#include "extern.h" 5018600Speter 51280220Sandrew/* We don't support a.out executables on arm64 */ 52280220Sandrew#ifndef __aarch64__ 53280220Sandrew#include <a.out.h> 54280220Sandrew#define AOUT_SUPPORTED 55280220Sandrew#endif 56280220Sandrew 57180646Sedwin/* 58180877Sedwin * 32-bit ELF data structures can only be used if the system header[s] declare 59180877Sedwin * them. There is no official macro for determining whether they are declared, 60180877Sedwin * so check for the existence of one of the 32-macros defined in elf(5). 61180646Sedwin */ 62180877Sedwin#ifdef ELF32_R_TYPE 63180877Sedwin#define ELF32_SUPPORTED 64180646Sedwin#endif 65180646Sedwin 66254018Smarkj#define LDD_SETENV(name, value, overwrite) do { \ 67254018Smarkj setenv("LD_" name, value, overwrite); \ 68254018Smarkj setenv("LD_32_" name, value, overwrite); \ 69254018Smarkj} while (0) 70254018Smarkj 71254018Smarkj#define LDD_UNSETENV(name) do { \ 72254018Smarkj unsetenv("LD_" name); \ 73254018Smarkj unsetenv("LD_32_" name); \ 74254018Smarkj} while (0) 75254018Smarkj 76180235Sedwinstatic int is_executable(const char *fname, int fd, int *is_shlib, 77180235Sedwin int *type); 78180235Sedwinstatic void usage(void); 79180234Sedwin 80180235Sedwin#define TYPE_UNKNOWN 0 81180235Sedwin#define TYPE_AOUT 1 82180235Sedwin#define TYPE_ELF 2 /* Architecture default */ 83180646Sedwin#if __ELF_WORD_SIZE > 32 && defined(ELF32_SUPPORTED) 84180236Sedwin#define TYPE_ELF32 3 /* Explicit 32 bits on architectures >32 bits */ 85696Spaul 86181136Sjhb#define _PATH_LDD32 "/usr/bin/ldd32" 87180235Sedwin 88181136Sjhbstatic int 89181136Sjhbexecldd32(char *file, char *fmt1, char *fmt2, int aflag, int vflag) 90181136Sjhb{ 91181136Sjhb char *argv[8]; 92181136Sjhb int i, rval, status; 93181136Sjhb 94254018Smarkj LDD_UNSETENV("TRACE_LOADED_OBJECTS"); 95181136Sjhb rval = 0; 96181136Sjhb i = 0; 97181136Sjhb argv[i++] = strdup(_PATH_LDD32); 98181136Sjhb if (aflag) 99181136Sjhb argv[i++] = strdup("-a"); 100181136Sjhb if (vflag) 101181136Sjhb argv[i++] = strdup("-v"); 102181161Sjhb if (fmt1 != NULL) { 103181136Sjhb argv[i++] = strdup("-f"); 104181136Sjhb argv[i++] = strdup(fmt1); 105181136Sjhb } 106181161Sjhb if (fmt2 != NULL) { 107181136Sjhb argv[i++] = strdup("-f"); 108181136Sjhb argv[i++] = strdup(fmt2); 109181136Sjhb } 110181136Sjhb argv[i++] = strdup(file); 111181136Sjhb argv[i++] = NULL; 112181136Sjhb 113181136Sjhb switch (fork()) { 114181136Sjhb case -1: 115181136Sjhb err(1, "fork"); 116181136Sjhb break; 117181136Sjhb case 0: 118181136Sjhb execv(_PATH_LDD32, argv); 119181136Sjhb warn("%s", _PATH_LDD32); 120181161Sjhb _exit(127); 121181136Sjhb break; 122181136Sjhb default: 123181161Sjhb if (wait(&status) < 0) 124181136Sjhb rval = 1; 125181161Sjhb else if (WIFSIGNALED(status)) 126181136Sjhb rval = 1; 127181161Sjhb else if (WIFEXITED(status) && WEXITSTATUS(status) != 0) 128181136Sjhb rval = 1; 129181136Sjhb break; 130181136Sjhb } 131181136Sjhb while (i--) 132181136Sjhb free(argv[i]); 133254018Smarkj LDD_SETENV("TRACE_LOADED_OBJECTS", "yes", 1); 134181136Sjhb return (rval); 135181136Sjhb} 136180236Sedwin#endif 137180235Sedwin 138696Spaulint 13995648Smarkmmain(int argc, char *argv[]) 140696Spaul{ 141180234Sedwin char *fmt1, *fmt2; 142180234Sedwin int rval, c, aflag, vflag; 143696Spaul 14490755Sobrien aflag = vflag = 0; 145180234Sedwin fmt1 = fmt2 = NULL; 14690755Sobrien 147180234Sedwin while ((c = getopt(argc, argv, "af:v")) != -1) { 148696Spaul switch (c) { 14990755Sobrien case 'a': 15090755Sobrien aflag++; 15190755Sobrien break; 15218598Speter case 'f': 153180234Sedwin if (fmt1 != NULL) { 154180234Sedwin if (fmt2 != NULL) 15569827Scharnier errx(1, "too many formats"); 15618598Speter fmt2 = optarg; 15718598Speter } else 15818598Speter fmt1 = optarg; 15918598Speter break; 160180234Sedwin case 'v': 161180234Sedwin vflag++; 162180234Sedwin break; 163696Spaul default: 164696Spaul usage(); 165180234Sedwin /* NOTREACHED */ 166696Spaul } 167696Spaul } 168696Spaul argc -= optind; 169696Spaul argv += optind; 170696Spaul 171180234Sedwin if (vflag && fmt1 != NULL) 17218600Speter errx(1, "-v may not be used with -f"); 17318600Speter 174696Spaul if (argc <= 0) { 175696Spaul usage(); 176180234Sedwin /* NOTREACHED */ 177696Spaul } 178696Spaul 17939354Sdfr#ifdef __i386__ 18018600Speter if (vflag) { 18118600Speter for (c = 0; c < argc; c++) 18218600Speter dump_file(argv[c]); 18318600Speter exit(error_count == 0 ? EXIT_SUCCESS : EXIT_FAILURE); 18418600Speter } 18539354Sdfr#endif 18618600Speter 1871741Srich rval = 0; 188180234Sedwin for (; argc > 0; argc--, argv++) { 189180235Sedwin int fd, status, is_shlib, rv, type; 190696Spaul 191696Spaul if ((fd = open(*argv, O_RDONLY, 0)) < 0) { 1921741Srich warn("%s", *argv); 193696Spaul rval |= 1; 194696Spaul continue; 195696Spaul } 196180235Sedwin rv = is_executable(*argv, fd, &is_shlib, &type); 197180235Sedwin close(fd); 198180235Sedwin if (rv == 0) { 199696Spaul rval |= 1; 200696Spaul continue; 201696Spaul } 20235575Sdfr 203180236Sedwin switch (type) { 204180236Sedwin case TYPE_ELF: 205180236Sedwin case TYPE_AOUT: 206180236Sedwin break; 207180646Sedwin#if __ELF_WORD_SIZE > 32 && defined(ELF32_SUPPORTED) 208180236Sedwin case TYPE_ELF32: 209181136Sjhb rval |= execldd32(*argv, fmt1, fmt2, aflag, vflag); 210181136Sjhb continue; 211180236Sedwin#endif 212180236Sedwin case TYPE_UNKNOWN: 213180236Sedwin default: 214180236Sedwin /* 215180236Sedwin * This shouldn't happen unless is_executable() 216180236Sedwin * is broken. 217180236Sedwin */ 218180236Sedwin errx(EDOOFUS, "unknown executable type"); 219180236Sedwin } 220180236Sedwin 221180235Sedwin /* ld.so magic */ 222254018Smarkj LDD_SETENV("TRACE_LOADED_OBJECTS", "yes", 1); 223180235Sedwin if (fmt1 != NULL) 224254018Smarkj LDD_SETENV("TRACE_LOADED_OBJECTS_FMT1", fmt1, 1); 225180235Sedwin if (fmt2 != NULL) 226254018Smarkj LDD_SETENV("TRACE_LOADED_OBJECTS_FMT2", fmt2, 1); 22735575Sdfr 228254018Smarkj LDD_SETENV("TRACE_LOADED_OBJECTS_PROGNAME", *argv, 1); 229180234Sedwin if (aflag) 230254018Smarkj LDD_SETENV("TRACE_LOADED_OBJECTS_ALL", "1", 1); 23190755Sobrien else if (fmt1 == NULL && fmt2 == NULL) 23218598Speter /* Default formats */ 23318598Speter printf("%s:\n", *argv); 2341153Sjkh fflush(stdout); 235696Spaul 236696Spaul switch (fork()) { 237696Spaul case -1: 2381741Srich err(1, "fork"); 239696Spaul break; 240696Spaul default: 241181161Sjhb if (wait(&status) < 0) { 2421741Srich warn("wait"); 2431741Srich rval |= 1; 2441741Srich } else if (WIFSIGNALED(status)) { 245180234Sedwin fprintf(stderr, "%s: signal %d\n", *argv, 246180234Sedwin WTERMSIG(status)); 247696Spaul rval |= 1; 248181161Sjhb } else if (WIFEXITED(status) && 249181161Sjhb WEXITSTATUS(status) != 0) { 250180234Sedwin fprintf(stderr, "%s: exit status %d\n", *argv, 251180234Sedwin WEXITSTATUS(status)); 252696Spaul rval |= 1; 253696Spaul } 254696Spaul break; 255696Spaul case 0: 256105439Ssobomax if (is_shlib == 0) { 25790172Ssobomax execl(*argv, *argv, (char *)NULL); 25890172Ssobomax warn("%s", *argv); 259105439Ssobomax } else { 260105439Ssobomax dlopen(*argv, RTLD_TRACE); 261105439Ssobomax warnx("%s: %s", *argv, dlerror()); 26290172Ssobomax } 263696Spaul _exit(1); 264696Spaul } 265696Spaul } 266696Spaul 267696Spaul return rval; 268696Spaul} 269180235Sedwin 270180235Sedwinstatic void 271180235Sedwinusage(void) 272180235Sedwin{ 273180235Sedwin 274180235Sedwin fprintf(stderr, "usage: ldd [-a] [-v] [-f format] program ...\n"); 275180235Sedwin exit(1); 276180235Sedwin} 277180235Sedwin 278180235Sedwinstatic int 279180235Sedwinis_executable(const char *fname, int fd, int *is_shlib, int *type) 280180235Sedwin{ 281180235Sedwin union { 282280220Sandrew#ifdef AOUT_SUPPORTED 283180235Sedwin struct exec aout; 284280220Sandrew#endif 285180646Sedwin#if __ELF_WORD_SIZE > 32 && defined(ELF32_SUPPORTED) 286180236Sedwin Elf32_Ehdr elf32; 287180646Sedwin#endif 288180235Sedwin Elf_Ehdr elf; 289180235Sedwin } hdr; 290180235Sedwin int n; 291180235Sedwin 292180235Sedwin *is_shlib = 0; 293180235Sedwin *type = TYPE_UNKNOWN; 294180235Sedwin 295180235Sedwin if ((n = read(fd, &hdr, sizeof(hdr))) == -1) { 296180235Sedwin warn("%s: can't read program header", fname); 297180235Sedwin return (0); 298180235Sedwin } 299180235Sedwin 300280220Sandrew#ifdef AOUT_SUPPORTED 301180235Sedwin if ((size_t)n >= sizeof(hdr.aout) && !N_BADMAG(hdr.aout)) { 302180235Sedwin /* a.out file */ 303180235Sedwin if ((N_GETFLAG(hdr.aout) & EX_DPMASK) != EX_DYNAMIC 304180235Sedwin#if 1 /* Compatibility */ 305180235Sedwin || hdr.aout.a_entry < __LDPGSZ 306180235Sedwin#endif 307180235Sedwin ) { 308180235Sedwin warnx("%s: not a dynamic executable", fname); 309180235Sedwin return (0); 310180235Sedwin } 311180235Sedwin *type = TYPE_AOUT; 312180235Sedwin return (1); 313180235Sedwin } 314280220Sandrew#endif 315180235Sedwin 316180646Sedwin#if __ELF_WORD_SIZE > 32 && defined(ELF32_SUPPORTED) 317180236Sedwin if ((size_t)n >= sizeof(hdr.elf32) && IS_ELF(hdr.elf32) && 318180236Sedwin hdr.elf32.e_ident[EI_CLASS] == ELFCLASS32) { 319180236Sedwin /* Handle 32 bit ELF objects */ 320180236Sedwin Elf32_Phdr phdr; 321180236Sedwin int dynamic, i; 322180236Sedwin 323180236Sedwin dynamic = 0; 324180236Sedwin *type = TYPE_ELF32; 325180236Sedwin 326180236Sedwin if (lseek(fd, hdr.elf32.e_phoff, SEEK_SET) == -1) { 327180236Sedwin warnx("%s: header too short", fname); 328180236Sedwin return (0); 329180236Sedwin } 330180236Sedwin for (i = 0; i < hdr.elf32.e_phnum; i++) { 331180236Sedwin if (read(fd, &phdr, hdr.elf32.e_phentsize) != 332180236Sedwin sizeof(phdr)) { 333180236Sedwin warnx("%s: can't read program header", fname); 334180236Sedwin return (0); 335180236Sedwin } 336180236Sedwin if (phdr.p_type == PT_DYNAMIC) { 337180236Sedwin dynamic = 1; 338180236Sedwin break; 339180236Sedwin } 340180236Sedwin } 341180236Sedwin 342180236Sedwin if (!dynamic) { 343180236Sedwin warnx("%s: not a dynamic ELF executable", fname); 344180236Sedwin return (0); 345180236Sedwin } 346180236Sedwin if (hdr.elf32.e_type == ET_DYN) { 347215705Sbrucec if (hdr.elf32.e_ident[EI_OSABI] == ELFOSABI_FREEBSD) { 348180236Sedwin *is_shlib = 1; 349180236Sedwin return (1); 350180236Sedwin } 351180236Sedwin warnx("%s: not a FreeBSD ELF shared object", fname); 352180236Sedwin return (0); 353180236Sedwin } 354180236Sedwin 355180236Sedwin return (1); 356180236Sedwin } 357180236Sedwin#endif 358180236Sedwin 359180235Sedwin if ((size_t)n >= sizeof(hdr.elf) && IS_ELF(hdr.elf) && 360180235Sedwin hdr.elf.e_ident[EI_CLASS] == ELF_TARG_CLASS) { 361180235Sedwin /* Handle default ELF objects on this architecture */ 362180235Sedwin Elf_Phdr phdr; 363180235Sedwin int dynamic, i; 364180235Sedwin 365180235Sedwin dynamic = 0; 366180235Sedwin *type = TYPE_ELF; 367180235Sedwin 368180235Sedwin if (lseek(fd, hdr.elf.e_phoff, SEEK_SET) == -1) { 369180235Sedwin warnx("%s: header too short", fname); 370180235Sedwin return (0); 371180235Sedwin } 372180235Sedwin for (i = 0; i < hdr.elf.e_phnum; i++) { 373180235Sedwin if (read(fd, &phdr, hdr.elf.e_phentsize) 374180235Sedwin != sizeof(phdr)) { 375180235Sedwin warnx("%s: can't read program header", fname); 376180235Sedwin return (0); 377180235Sedwin } 378180235Sedwin if (phdr.p_type == PT_DYNAMIC) { 379180235Sedwin dynamic = 1; 380180235Sedwin break; 381180235Sedwin } 382180235Sedwin } 383180235Sedwin 384180235Sedwin if (!dynamic) { 385180235Sedwin warnx("%s: not a dynamic ELF executable", fname); 386180235Sedwin return (0); 387180235Sedwin } 388180235Sedwin if (hdr.elf.e_type == ET_DYN) { 389215705Sbrucec if (hdr.elf.e_ident[EI_OSABI] == ELFOSABI_FREEBSD) { 390180235Sedwin *is_shlib = 1; 391180235Sedwin return (1); 392180235Sedwin } 393180235Sedwin warnx("%s: not a FreeBSD ELF shared object", fname); 394180235Sedwin return (0); 395180235Sedwin } 396180235Sedwin 397180235Sedwin return (1); 398180235Sedwin } 399180235Sedwin 400180235Sedwin warnx("%s: not a dynamic executable", fname); 401180235Sedwin return (0); 402180235Sedwin} 403