Index: usr.bin/ldd/Makefile =================================================================== --- usr.bin/ldd/Makefile +++ usr.bin/ldd/Makefile @@ -3,4 +3,6 @@ PROG?= ldd SRCS= ldd.c +LIBADD= elf + .include Index: usr.bin/ldd/ldd.c =================================================================== --- usr.bin/ldd/ldd.c +++ usr.bin/ldd/ldd.c @@ -33,6 +33,7 @@ #include __FBSDID("$FreeBSD$"); +#include #include #include @@ -43,6 +44,9 @@ #include #include #include +#include +#include +#include #include #include #include @@ -259,179 +263,182 @@ exit(1); } +static bool +is_pie(const char *fname, Elf *elf) +{ + GElf_Shdr shdr; + GElf_Dyn dyn; + Elf_Scn *scn; + Elf_Data *d; + u_int i; + + scn = NULL; + while ((scn = elf_nextscn(elf, scn)) != NULL) { + if (gelf_getshdr(scn, &shdr) == NULL) { + warnx("%s: %s", fname, elf_errmsg(0)); + return (false); + } + + if (shdr.sh_type != SHT_DYNAMIC) + continue; + + d = elf_getdata(scn, NULL); + if (d == NULL) + continue; + + for (i = 0; i < shdr.sh_size / shdr.sh_entsize; i++) { + if (gelf_getdyn(d, i, &dyn) == NULL) { + warnx("%s: %s", fname, elf_errmsg(0)); + break; + } + + if (dyn.d_tag != DT_FLAGS_1) + continue; + + return ((dyn.d_un.d_val & DF_1_PIE) != 0); + } + } + + return (false); +} + +static bool +check_notes(const char *buf, size_t len) +{ + const Elf_Note *note; + const char *name; + size_t namesz, descsz; + + for (;;) { + if (len < sizeof(*note)) + return (false); + note = (const Elf_Note *)(uintptr_t)buf; + buf += sizeof(*note); + len -= sizeof(*note); + + namesz = roundup2(note->n_namesz, 4); + descsz = roundup2(note->n_descsz, 4); + if (len < namesz + descsz) + return (false); + + name = buf; + if (strncmp(name, "FreeBSD", namesz) == 0 && + note->n_type == NT_FREEBSD_ABI_TAG) + return (true); + + buf += namesz + descsz; + len -= namesz + descsz; + } + + return (false); +} + +static bool +is_freebsd_elf(const char *fname, Elf *elf, GElf_Ehdr *ehdr) +{ + GElf_Shdr shdr; + Elf_Scn *scn; + Elf_Data *d; + + switch (ehdr->e_ident[EI_OSABI]) { + case ELFOSABI_FREEBSD: + return (true); + case ELFOSABI_NONE: + break; + default: + return (false); + } + + scn = NULL; + while ((scn = elf_nextscn(elf, scn)) != NULL) { + if (gelf_getshdr(scn, &shdr) == NULL) { + warnx("%s: %s", fname, elf_errmsg(0)); + return (false); + } + + if (shdr.sh_type != SHT_NOTE) + continue; + + d = elf_getdata(scn, NULL); + if (d == NULL) + continue; + + if (check_notes(d->d_buf, d->d_size)) + return (true); + } + + return (false); +} + static int is_executable(const char *fname, int fd, int *is_shlib, int *type) { - union { -#if __ELF_WORD_SIZE > 32 && defined(ELF32_SUPPORTED) - Elf32_Ehdr elf32; -#endif - Elf_Ehdr elf; - } hdr; - Elf_Phdr phdr, dynphdr; - Elf_Dyn *dynp; - void *dyndata; -#if __ELF_WORD_SIZE > 32 && defined(ELF32_SUPPORTED) - Elf32_Phdr phdr32, dynphdr32; - Elf32_Dyn *dynp32; -#endif - int df1pie, dynamic, i, n; + Elf *elf; + GElf_Ehdr ehdr; + GElf_Phdr phdr; + bool dynamic; + int i; *is_shlib = 0; *type = TYPE_UNKNOWN; - df1pie = 0; + dynamic = false; - if ((n = read(fd, &hdr, sizeof(hdr))) == -1) { - warn("%s: can't read program header", fname); + if (elf_version(EV_CURRENT) == EV_NONE) { + warnx("unsupported libelf"); + return (0); + } + elf = elf_begin(fd, ELF_C_READ, NULL); + if (elf == NULL) { + warnx("%s: %s", fname, elf_errmsg(0)); + return (0); + } + if (elf_kind(elf) != ELF_K_ELF) { + elf_end(elf); + warnx("%s: not a dynamic ELF executable", fname); + return (0); + } + if (gelf_getehdr(elf, &ehdr) == NULL) { + warnx("%s: %s", fname, elf_errmsg(0)); + elf_end(elf); return (0); } + *type = TYPE_ELF; #if __ELF_WORD_SIZE > 32 && defined(ELF32_SUPPORTED) - if ((size_t)n >= sizeof(hdr.elf32) && IS_ELF(hdr.elf32) && - hdr.elf32.e_ident[EI_CLASS] == ELFCLASS32) { - /* Handle 32 bit ELF objects */ - - dynamic = 0; + if (gelf_getclass(elf) == ELFCLASS32) { *type = TYPE_ELF32; - - if (lseek(fd, hdr.elf32.e_phoff, SEEK_SET) == -1) { - warnx("%s: header too short", fname); - return (0); - } - for (i = 0; i < hdr.elf32.e_phnum; i++) { - if (read(fd, &phdr32, hdr.elf32.e_phentsize) != - sizeof(phdr32)) { - warnx("%s: can't read program header", fname); - return (0); - } - if (phdr32.p_type == PT_DYNAMIC) { - dynamic = 1; - dynphdr32 = phdr32; - break; - } - } - - if (!dynamic) { - warnx("%s: not a dynamic ELF executable", fname); - return (0); - } - - if (hdr.elf32.e_type == ET_DYN) { - if (lseek(fd, dynphdr32.p_offset, SEEK_SET) == -1) { - warnx("%s: dynamic segment out of range", - fname); - return (0); - } - dyndata = malloc(dynphdr32.p_filesz); - if (dyndata == NULL) { - warn("malloc"); - return (0); - } - if (read(fd, dyndata, dynphdr32.p_filesz) != - (ssize_t)dynphdr32.p_filesz) { - free(dyndata); - warnx("%s: can't read dynamic segment", fname); - return (0); - } - for (dynp32 = dyndata; dynp32->d_tag != DT_NULL; - dynp32++) { - if (dynp32->d_tag != DT_FLAGS_1) - continue; - df1pie = (dynp32->d_un.d_val & DF_1_PIE) != 0; - break; - } - free(dyndata); - - if (hdr.elf32.e_ident[EI_OSABI] == ELFOSABI_FREEBSD) { - if (!df1pie) - *is_shlib = 1; - return (1); - } - warnx("%s: not a FreeBSD ELF shared object", fname); - return (0); - } - - return (1); } #endif - if ((size_t)n >= sizeof(hdr.elf) && IS_ELF(hdr.elf) && - hdr.elf.e_ident[EI_CLASS] == ELF_TARG_CLASS) { - /* Handle default ELF objects on this architecture */ - - dynamic = 0; - *type = TYPE_ELF; - - if (lseek(fd, hdr.elf.e_phoff, SEEK_SET) == -1) { - warnx("%s: header too short", fname); + for (i = 0; i < ehdr.e_phnum; i++) { + if (gelf_getphdr(elf, i, &phdr) == NULL) { + warnx("%s: %s", fname, elf_errmsg(0)); + elf_end(elf); return (0); } - for (i = 0; i < hdr.elf.e_phnum; i++) { - if (read(fd, &phdr, hdr.elf.e_phentsize) - != sizeof(phdr)) { - warnx("%s: can't read program header", fname); - return (0); - } - if (phdr.p_type == PT_DYNAMIC) { - dynamic = 1; - dynphdr = phdr; - break; - } + switch (phdr.p_type) { + case PT_DYNAMIC: + dynamic = true; + break; } + } - if (!dynamic) { - warnx("%s: not a dynamic ELF executable", fname); - return (0); - } + if (!dynamic) { + elf_end(elf); + warnx("%s: not a dynamic ELF executable", fname); + return (0); + } - if (hdr.elf.e_type == ET_DYN) { - if (lseek(fd, dynphdr.p_offset, SEEK_SET) == -1) { - warnx("%s: dynamic segment out of range", - fname); - return (0); - } - dyndata = malloc(dynphdr.p_filesz); - if (dyndata == NULL) { - warn("malloc"); - return (0); - } - if (read(fd, dyndata, dynphdr.p_filesz) != - (ssize_t)dynphdr.p_filesz) { - free(dyndata); - warnx("%s: can't read dynamic segment", fname); - return (0); - } - for (dynp = dyndata; dynp->d_tag != DT_NULL; dynp++) { - if (dynp->d_tag != DT_FLAGS_1) - continue; - df1pie = (dynp->d_un.d_val & DF_1_PIE) != 0; - break; - } - free(dyndata); + if (ehdr.e_type == ET_DYN && !is_pie(fname, elf)) { + *is_shlib = 1; - switch (hdr.elf.e_ident[EI_OSABI]) { - case ELFOSABI_FREEBSD: - if (!df1pie) - *is_shlib = 1; - return (1); -#ifdef __ARM_EABI__ - case ELFOSABI_NONE: - if (hdr.elf.e_machine != EM_ARM) - break; - if (EF_ARM_EABI_VERSION(hdr.elf.e_flags) < - EF_ARM_EABI_FREEBSD_MIN) - break; - *is_shlib = 1; - return (1); -#endif - } + if (!is_freebsd_elf(fname, elf, &ehdr)) { + elf_end(elf); warnx("%s: not a FreeBSD ELF shared object", fname); return (0); } - - return (1); } - warnx("%s: not a dynamic executable", fname); - return (0); + elf_end(elf); + return (1); }